JS Rule catch error

Hello all,

I have a rule to catch some values


rules.JSRule({
  name: "get  values",
  description: "get values",
  triggers: [triggers.GenericCronTrigger("0 * * ? * * *")],
  execute: (event) => {
                try {
                        var response = actions.HTTP.sendHttpGetRequest('http://192.168.178.28/status.html');
} catch (error) {       
                        console.error("Error", "Some bad stuff happened in \"get  values\": " + error);
                }
                finally {
                   }

        },
  tags: ["get  values"],
  id: "get  values"
});


how can I catch a

 Fatal transport error: java.util.concurrent.ExecutionException: java.net.NoRouteToHostException: No route to host

error? with the normal try and catch block i can’t catch it…

Can you please provide the whole log message? I want to see the logger name that logs that error.

[ERROR] [enhab.core.model.script.actions.HTTP] - Fatal transport error: java.util.concurrent.ExecutionException: java.net.ConnectException: Connection refused

You cannot catch this because it is not thrown to the scriptā€˜s layer … the exception is catched inside the HTTP actions and then logged, you will receive null as return value if anything went wrong.

See openhab-core/bundles/org.openhab.core.model.script/src/org/openhab/core/model/script/actions/HTTP.java at 873bb53cbc12bf7624499f66170bfc90a10c35d6 Ā· openhab/openhab-core Ā· GitHub.

Assuming this isn’t just a toy problem for demonstration purposes, the HTTP binding would probably be a more flexible and more straight forward way to implement a periodic poll of a web page.

What are you hoping to do by catching the exception? Maybe this is really na XY Problem?

I want to set an value to 0 if my inverter is not available…

Yeah you are right but I need to cut out the values from the response…that’s why I am using a rule here is the curl command

curl -s -u admin:admin  http://192.168.x.x/status.html | grep -E "\webdata_total_e(\s|$)" | cut -d'"' -f 2

grep and cut are not going to be available in your rule either so using a rule here over the binding doesn’t really give you anything. You could use executeCommandLine I suppose but as best as I can tell without seeing the actual page returned you should be able to easily use the HTTP binding and REGEX transform to achieve this. The XPath transform might also be useful here.

I can’t give you the REGEX without seeing the page returned but if would be something like .*webdata_total_e "(.*)".*.

Once you have that, if the HTTP GET request failed, the HTTP binding will set the Thing status from ONLINE to something else (I’m seeing UNKNOWN in my experiments but I know I’ve seen OFFLINE in the past too).

There is also a Last Success and Last Failure pair of Channels which get updated with a timestamp of the last success or failure.

You could trigger a rule with the Thing status change or a change to the Last Failure to do whatever you need to do in cases where the web server doesn’t respond.

Unfortunately, what I expected to happen didn’t. I expected/hoped that the linked Items would have been set to NULL or UNDEF when the page didn’t respond so the rule would still probably be required. But at least you won’t be doubling the network traffic and processing to detect when the server goes offline as you would with two rules polling the same URL, one to get the data and another to report when it goes offline. It also means that you don’t really have to do anything special like exception handling to detect when the device goes offline.

You are right but I want to set the value to 0 if it’s not reachable so I can handle it also in widget…

And I only need one polling if response is NULL then it’s not reachable…

I’m not sure I understand.

You’d have a rule that triggers when the Thing status changes from ONLINE or when the Item linked to the Last Failure Channel changes and update/command your Item to 0 in that rule. When it is online, the Thing will parse out and update any Items necessary, no rule is involved.

You don’t even need a Script Action here, you can do it with a simple UI rule since all it needs to do it trigger and update that one Item to 0.

As written, your rule queries the HTTP server once per minute whether it’s online or not independently from and in addition to any polling you do to with your curl to get the data. With the HTTP binding you’d just have the one polling period which handles both detecting offline and extracting the data.

Are you saying that you plan on bringing the logic from your curl/grep/cut into the rule? In that case then yes, you’d not have additional polling of the server.

Note that NULL is not the same thing as null. The sendHttpGetAction returns null on error, not NULL.

this is my Implementation

rules.JSRule({
  name: "get Deye values",
  description: "get Deye values",
  triggers: [triggers.GenericCronTrigger("0 * * ? * * *")],
  execute: (event) => {
                try {
                        var response = actions.HTTP.sendHttpGetRequest('status.html');
                        //console.error(response);
                        if (response != null) {
                                let position = response.search("404");
                                if (position == -1){
                                        var regex = "var webdata_total_e = \"[0-9]+[.][0-9]+\";";
                                        var energytotal = response.match(regex);
                                        regex = "[0-9]+[.][0-9]+";
                                        energytotal = energytotal.toString().match(regex);
                                        console.log(energytotal);
                                        regex = "var webdata_now_p = \"[0-9]+\";";
                                        var currentpower = response.match(regex);
                                        regex = "[0-9]+";
                                        currentpower = currentpower.toString().match(regex);
                                        console.log(currentpower);
                                        items.getItem("deye_actualpower").postUpdate(currentpower);
                                        if (energytotal > 0) {
                                                items.getItem("deye_totalenergy").postUpdate(energytotal);
                                        } else {
                                                console.info("get Deye values", "deye_totalenergy <=0");
                                        }
                                }else{
                                        //console.log("a items.getItem("deye_actualpower").postUpdate(currentpower);
                                        //items.getItem("deye_totalenergy").postUpdate(energytoday);l");
                                }
                        } else {
                                items.getItem("deye_actualpower").postUpdate(0);
                                //items.getItem("deye_totalenergy").postUpdate(0);
                        }
} catch (error) {       
                        console.error("Error", "Some bad stuff happened in \"get Deye values\": " + error);
                }
                finally {
                         //if (isNaN()) {
                        //      items.getItem("deye_actualpower").postUpdate(0);
                        //}
                }

        },
  tags: ["deye", "get Deye values"],
  id: "get Deye values"
});

Can you post an example of the data returned by the server? If it’s really html, it might be simpler to use XPath.

As written you could also simplify the processing some using the REGEX transform to find and extract the data all in one go.

    var energyTotal = actions.Transformation.transform('REGEX', '.*var webdata_total_e = \"([0-9]+[\.][0-9]+).*', response);

When using the transform the expression needs to match the whole document (hence the .* on the front and back). But then you can add to the pattern to find the spot you want to extract (the var wendata_total_e = \" part) and the first matching pattern is what the transform returns (the ([0-9]+[\.][0-9]+) part). Note I escape the . because otherwise REGEX treats . as ā€œany characterā€ instead of a literal . character.

If you were to move this to the HTTP binding using these REGEX expressions the Thing would look something like this (I assumed not using UoM):

UID: http:url:testHTTPThing
label: Test HTTP Thing
thingTypeUID: http:url
configuration:
  authMode: BASIC
  ignoreSSLErrors: false
  baseURL: http://192.168.x.x/status.html
  delay: 0
  stateMethod: GET
  refresh: 60
  commandMethod: GET
  timeout: 3000
  bufferSize: 2048
channels:
  - id: last-failure
    channelTypeUID: http:request-date-time
    label: Last Failure
    configuration: {}
  - id: last-success
    channelTypeUID: http:request-date-time
    label: Last Success
    configuration: {}
  - id: energyTotal
    channelTypeUID: http:number
    label: Energy Total
    description: ""
    configuration:
      mode: READONLY
      stateTransformation: REGEX:.*var webdata_total_e = \"([0-9]+[\.][0-9]+).*
  - id: currentPower
    channelTypeUID: http:number
    label: Current Power
    description: ""
    configuration:
      mode: READONLY
      stateTransformation: REGEX:.*var webdata_now_p = \"([0-9]).*

Your rule would become:

rules.JSRule({
  name: 'reset Deye when offline',
  description: 'Triggered when the Thing goes offline',
  triggers: [triggers.ThingStatusChangeTrigger('some:thing:uuid','ONLINE')],
  execute: (event) => {
    console.info('Device is offline, resetting deye_actualpower');
    items.deye_actualpower.postUpdate(0);
  },
  tags: ['deye', 'reset Deye values'],
  id: 'reset Deye values'
});

You could add ∩JS: | (parseFloat(input) > 0) ? input : 0 to the transformation for the energyTotal Channel to exactly match the logic you have in your rule. But that’s not strictly needed because the way the REGEX pattern is implemented, you’d never get a negative value anyway.

@rlkoshak thanks…

Sent you a private message with the complete website…

Thing http:url:deye "Deye Inverter" [
    baseURL="http://192.168.x.x/status.html",
    headers="",
    authMode = "BASIC",
    ignoreSSLErrors = true,
    delay=0,
    stateMethod= "GET",
    refresh= 60,
    commandMethod= "GET",
    timeout= 3000,
    bufferSize= 2048,
    refresh=15] {
        Channels:
         Type number : energyTotal "Energy Total" [ stateTransformation="REGEX:.*var webdata_total_e = \"([0-9]+[\.][0-9]+).*" ]
}

But getting this error

[14,73]: mismatched character '.' expecting set null
1 Like

OK, looking at the webpage the format is indeed XHTML which means Xpath could work theoretically. The bad news is the data is actually embedded in some JS which renders Xpath not that useful here. We have to use the REGEX transformation after all.

Ultimately, its stuff like this which makes me no longer user nor support .things files (or other text file based OH configs). Somewhere you have a syntax error. Since I’m not a computer, I can’t say what the error is. But the ā€œ14,73ā€ is likely the line number and column number in the file.

But I have confirmed that the REGEX expressions I created above do work on that webpage.

I changed it now to this

 Type number : energyTotal "Energy Total" [ stateTransformation="REGEX:.*var webdata_total_e = \"([0-9]+[\\.][0-9]+).*" ]


But now I get

Conversion of value '' with type 'class java.lang.String' to 'class java.util.ArrayList' failed. Returning null

If you need to double escape the . you probably need to do it for the " too.

You don’t show the full log line telling where that error is coming from but I’d guess it’s still a syntax error of some sort.

I changed it to

Type number : energyTotal "Energy Total" [ stateTransformation="REGEX:.*var webdata_total_e = \\"([0-9]+[\\.][0-9]+).*" ]

now I get

Configuration model 'deye.things' has errors, therefore ignoring it: [14,107]: mismatched input '(' expecting ']'
[14,109]: missing '}' at '0-9'
[14,120]: no viable alternative at input '0-9'
[14,128]: mismatched character '<EOF>' expecting '"'

All I can really offer is the following:

  • the REGEX expressions work
  • syntax errors like these do no occur when using managed Things
  • one does not need to mess with double escaping and stuff like that with managed Things

Like I said, I don’t do .things files any more so all I can really say is there is a syntax error. What is the error? :man_shrugging: I don’t have recent experience with .things files/syntax. I’m not a computer so can’t parse the file myself. I prefer to spend my time solving home automation problems instead of syntax errors so I’m not inclined to spend the time to relearn the .things file syntax and mentally parse this .things file to find the error.

The best I can offer is if you stick with .things files is to pay attention to the errors and keep adjusting until you get it right. I can be of little further help with that part.

ok I have now this

Type number : energyTotal "Energy Total" [ stateTransformation="REGEX:.*var webdata_total_e = \"([0-9]+[\\.][0-9]+).*" ]

still getting this error

Conversion of value '' with type 'class java.lang.String' to 'class java.util.ArrayList' failed. Returning null

but it’s running

Hmmmm, does this web page always return the same thing? If the REGEX doesn’t match anything it returns the empty string and obviously an empty string can’t be converted to a number.

If this is indeed the problem, you can add a REGEX filter in front of the main one to only process if the search string is in the page. I’m not 100% sure that’s the problem nor am I certain that the REGEX filter trick works with REGEX. But it’s worth a shot.

If this is the cause of this log statement, it’s being logged at the WARN level so it’s not an error.

ā€œREGEX:(.webdata_total_e.)∩REGEX:.var webdata_total_e = "([0-9]+[\.][0-9]+).ā€

How can I type the ā€œāˆ©ā€ character…maybe this is an issue too…i am just copy paste it…