How can I use OpenHab REST API with Blockly?

Hi,

some context:
I try to create dynamically some manutention alerts (i.e: battery of sensor whatever is empty please replace it, this device need water, (that will then be displayed as switches and be deleted when marked “off”)

What works?
When I use swagger and test the API it works:

It creates the Item properly and return a 201 and I can see the item created.

What doesn’t work?

The problem is when I try to do that through blockly:

It generates some error logs:

2024-11-01 15:32:02.896 [INFO ] [nhab.automation.script.ui.18c5421be6] - Create new item testItem
2024-11-01 15:32:02.897 [INFO ] [nhab.automation.script.ui.18c5421be6] - {
  "name": "testItem",
  "label": "Todo test item",
  "category": "Switch",
  "type": "Switch",
  "groupNames": [
    "ManutentionAlerts"
  ]
}
2024-11-01 15:32:02.898 [ERROR] [omation.script.javascript.18c5421be6] - Failed to execute script: TypeError: invokeMember (sendHttpPutRequest) on org.openhab.core.model.script.actions.HTTP failed due to: no applicable overload found (overloads: [Method[public static java.lang.String org.openhab.core.model.script.actions.HTTP.sendHttpPutRequest(java.lang.String,int)], Method[public static java.lang.String org.openhab.core.model.script.actions.HTTP.sendHttpPutRequest(java.lang.String,java.lang.String,java.lang.String,int)], Method[public static java.lang.String org.openhab.core.model.script.actions.HTTP.sendHttpPutRequest(java.lang.String,java.lang.String,java.lang.String)], Method[public static java.lang.String org.openhab.core.model.script.actions.HTTP.sendHttpPutRequest(java.lang.String,java.lang.String,java.lang.String,java.util.Map,int)], Method[public static java.lang.String org.openhab.core.model.script.actions.HTTP.sendHttpPutRequest(java.lang.String)]], arguments: [http://192.168.4.167:8087/rest/items/testItem (String), application/json (String), DynamicObject<JSOrdinary>@39a27c96 (DefaultLayout), 3000 (Integer)])
        at <js>.:program(<eval>:4)
        at org.graalvm.polyglot.Context.eval(Context.java:399)
        at com.oracle.truffle.js.scriptengine.GraalJSScriptEngine.eval(GraalJSScriptEngine.java:458)
        at com.oracle.truffle.js.scriptengine.GraalJSScriptEngine.eval(GraalJSScriptEngine.java:426)
        at java.scripting/javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:262)
        ... 74 more
2024-11-01 15:32:02.898 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID '18c5421be6' failed: org.graalvm.polyglot.PolyglotException: TypeError: invokeMember (sendHttpPutRequest) on org.openhab.core.model.script.actions.HTTP failed due to: no applicable overload found (overloads: [Method[public static java.lang.String org.openhab.core.model.script.actions.HTTP.sendHttpPutRequest(java.lang.String,int)], Method[public static java.lang.String org.openhab.core.model.script.actions.HTTP.sendHttpPutRequest(java.lang.String,java.lang.String,java.lang.String,int)], Method[public static java.lang.String org.openhab.core.model.script.actions.HTTP.sendHttpPutRequest(java.lang.String,java.lang.String,java.lang.String)], Method[public static java.lang.String org.openhab.core.model.script.actions.HTTP.sendHttpPutRequest(java.lang.String,java.lang.String,java.lang.String,java.util.Map,int)], Method[public static java.lang.String org.openhab.core.model.script.actions.HTTP.sendHttpPutRequest(java.lang.String)]], arguments: [http://192.168.4.167:8087/rest/items/testItem (String), application/json (String), DynamicObject<JSOrdinary>@39a27c96 (DefaultLayout), 3000 (Integer)])

I don’t really know what is going on here. I use openhab 4.2.2

Also is the bearer token automatically managed? Could that be the cause?

Any help is welcome.

Why do you need to create new Items for this? Just want to understand, cause we have many example to receice alerts when a battery drops low. No need for separate alert Items.

Hi @hmerk I would like to stick to my problem and avoid to diverge the topic too much so I answered your question in “hidden mode”.

More explanations about why I want to create OH Items from a rule

I found a lot of workarounds creating “predefined virtual switch items” and then changing their labels.

While it can work, this workaround is not dynamic (You just have as many switch available as what you created)

Another option “sending notification” could work for some users but me I want a toggle button that I, or my wife can “mark as done” via the UI.

Notification can be lost.

Aside of that:
I also currently parse some calendar events via nodered to generate some recurrent items automatically in openhab (same way) and it works pretty well. I just wanted to generate some items from openhab itself i.e: the humidifer tank is almost empty, etc.)

1 Like

As to my knowledge yes. You need some sort of authentication. Try to add the following line to your http put header:

 'Authorization': 'Bearer oh.rules.DhBqiiR…(token)

Make sense thank you, I added it but the issue persist:

Failed to execute script: TypeError: invokeMember (sendHttpPutRequest) on org.openhab.core.model.script.actions.HTTP failed due to: no applicable overload found (overloads: [Method[public static java.lang.String org.openhab.core.model.script.actions.HTTP.sendHttpPutRequest(java.lang.String,int)], Method[public static java.lang.String org.openhab.core.model.script.actions.HTTP.sendHttpPutRequest(java.lang.String,java.lang.String,java.lang.String,int)], Method[public static java.lang.String org.openhab.core.model.script.actions.HTTP.sendHttpPutRequest(java.lang.String,java.lang.String,java.lang.String)], Method[public static java.lang.String org.openhab.core.model.script.actions.HTTP.sendHttpPutRequest(java.lang.String,java.lang.String,java.lang.String,java.util.Map,int)], Method[public static java.lang.String org.openhab.core.model.script.actions.HTTP.sendHttpPutRequest(java.lang.String)]], arguments: [http://192.168.4.167:8087/rest/items/testItem (String),  application/json (String), DynamicObject<JSOrdinary>@e6ab4ab (DefaultLayout), DynamicObject<JSOrdinary>@117618f4 (DefaultLayout), 3000 (Integer)])
        at <js>.:program(<eval>:4)
        at org.graalvm.polyglot.Context.eval(Context.java:399)
        at com.oracle.truffle.js.scriptengine.GraalJSScriptEngine.eval(GraalJSScriptEngine.java:458)
        at com.oracle.truffle.js.scriptengine.GraalJSScriptEngine.eval(GraalJSScriptEngine.java:426)
        at java.scripting/javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:262)
        ... 74 more

Seems to be a mapping between “js → java” which is not allowed

This is a working js example of sending a command to an item. I am not familiar with blockly so you need to adopt it:


var url = 'http://192.168.178.61:8080/rest/items/testItem'
var contentType = "text/plain";
var content = "ON";
var headers = {
  'Accept': '*/*',
  'Authorization': 'Bearer oh.rules.DhBqiiRJiotlpI2…….'
};
var result = actions.HTTP.sendHttpPostRequest(url, contentType, content, headers, 10000);
console.log("Result:",result);
1 Like

Look very similar to the generated code of blockly:

if (true) {
  console.info('Create new item testItem');
  console.info({'name': 'testItem', 'label': 'Todo test item', 'category': 'Switch', 'type': 'Switch', 'groupNames': ['ManutentionAlerts']});
  console.info((actions.HTTP.sendHttpPutRequest('http://192.168.4.167:8087/rest/items/testItem',' application/json', {'name': 'testItem', 'label': 'Todo test item', 'category': 'Switch', 'type': 'Switch', 'groupNames': ['ManutentionAlerts']}, {'Accept': '*/*', 'Authorization': 'Bearer oh.OPENHABAPITOKEN.xzy'}, 3000)));
}

Which version of openhab are you using @Oliver2 ? might be the cause I guess?

EDIT:
seems that blockly add an extra space for the payload ???

' application/json'

And this is hardcoded:
image

How are your API security settings at
/settings/services/org.openhab.restauth

org.openhab.restauth looks like that (I didn’t touched it):

:org.apache.felix.configadmin.revision:=L"1"
_felix_.cm.newConfiguration=B"true"
service.pid="org.openhab.restauth"

Not sure if this makes a difference but delete the space character.

I am on 4.3.0

1 Like

same issue unfortunately, might be something fixed in 4.3 is it “safe” to upgrade ? :slight_smile:

The reason we ask is XY Problems are rampant. Far too often we answer the question exactly as asked and the asker is no closer to getting what they are after because the way they think their problem should be solved is suboptimal or doesn’t actually solve the root problem.

As a case in point, it’s a whole lot easier to create an Item using the inline script block with some custom JS code than it is to interact with the REST API.

items.replaceItem({
  type: 'String',
  name: 'Hallway_Light',
  label: 'Hallway Light',
  category: 'light',
  groups: ['Hallway', 'Light'],
  tags: ['Lightbulb'],
  channels: {
    'binding:thing:device:hallway#light': {},
    'binding:thing:device:livingroom#light': {
      profile: 'system:follow'
    }
  },
  metadata: {
    expire: '10m,command=1',
    stateDescription: {
      config: {
        pattern: '%d%%',
        options: '1=Red, 2=Green, 3=Blue'
      }
    }
  }
});

Obviously use the properties you need for this.

No authentication stuff is required. Because Blockly compiles to JS, you can insert any arbitrary code that is awkward or impossible to do using Blockly, using the inline script block.

It’s good to have some working examples of working with the REST API from a rule, but for the most part, there is very little you need to use the REST API to do in OH. Almost everything in the REST API is also available directly in the rules.

2 Likes

Hi @rlkoshak, thank you for your answer and your explanations, it make sense to try to find a proper solution.

Two things however:

  1. The HTTP calls are natively provided by OH in blockly and do not work in 4.2 apparently.

That’s probably a bug to address and not to ignore with “other solution”. (I mean we should tackle that :slight_smile: not put it under the carpet. And it might be an opportunity for me to publish a bugfix I’m willing to help on this)

I will try to upgrade OH and see if it fix the problem.
If yes then it has been fixed in the new versions fair enough.

EDIT: I confirm, 4.3.0-M2 release do not have the issue anymore:

2024-11-01 20:08:05.051 [INFO ] [nhab.automation.script.ui.18c5421be6] - Create new item testItem
2024-11-01 20:08:05.072 [INFO ] [nhab.automation.script.ui.18c5421be6] - {
  "name": "testItem",
  "label": "Todo test item",
  "category": "Switch",
  "type": "Switch",
  "groupNames": [
    "ManutentionAlerts"
  ]
}
2024-11-01 20:08:05.160 [INFO ] [nhab.automation.script.ui.18c5421be6] - {"link":"http://192.168.4.167:8087/rest/items/testItem","state":"NULL","type":"Switch","name":"testItem","label":"Todo test item","category":"Switch","tags":[],"groupNames":["ManutentionAlerts"]}

  1. with all my respect for this awesome project (and I mean it it has a huge potential), the REST API is well documented thanks to the swagger integration.

But the rest like the code that you share for JS rules is like very undocumented and very difficult to “generate”.

Would you mind sharing some resources to help me become more autonomous for writing such rules ? (link to the source code or documentation to understand what makes items.replaceItem available in a JS rule and so on ? I would like to be able to write rules one day without having to dig over and over in this discussion forum and be more autonomous)

I tested your suggestion to invoke JS directly it works.
My understanding is that:

it will do → js<->java → call the java “service layer”
and if I use rest it will do → js ↔ java → call the rest api layer → call the service layer so indeed it have a little overhead regarding optimisation.

I completely agree. Nothing I posted is meant to negate the fact that there might be a problem with the HTTP block nor the value of having some more working examples of the HTTP block on the forum.

But to solve your original problem, I think using the inline script block is the better approach overall.

The example I posted comes straight from the docs.

When you use Blockly:

Blockly -> "compiles" to JS Scripting -> openhab-js wraps OH's APIs with JS -> Java interactions with OH core (in this case the ItemRegistry).

When you use JS directly you get:

JS -> openhab-js wraps OH's APIs with JS -> Java interactions wtih OH core

When you use the REST API in Blockly you get:

Blockly -> "compiles" to JS Scripting -> openhab-js wraps the sendHttpXRequest  actions -> Java sendHttpXRequest actions -> Java HTTP requests -> Network -> REST API servlets -> Java interactions with OH core

When you use just JS:

JS -> openhab-js wraps the sendHttpXRequest actions -> Java sendHttpXRequest actions -> Java HTTP requests -> Network -> REST API servlets -> Java interactions with OH core

Not only do the REST API requests introduce a bunch of overhead (optimising the runtime of a rule like this is almost never worth the effort to correct), they also introduce a bunch of complexity (which is almost always worth correcting). For example, you don’t need to mess with API tokens or basic auth or anything like that with the non-HTTP approach because it ends up being just a function call within the OH process. The call never leaves OH.

When using the REST API, the OH rule has to act like it’s not even running in OH at all. It’s just another network client.

Blockly is great! It’s way more capable and complete than I ever thought it would become (all props go to devs). However, if you are going to head down a rarely used path or even employ an anti-pattern (most of the time creating Items dynamically is what I’d call an anti-pattern), Blockly is likely not going to be the best option. Blockly is always going to present the way the OH developers expect and want you to interact with OH. If you are going to go outside that (and there are plenty of good reasons to do so), you’re going to have to jump out of Blockly. Creating Items, Things, and Rules from a rule are examples of using OH in rare and unexpected/unsupported ways.

Thankfully, we have a way you can have your cake and eat it too with the inline script block. You don’t have to completly abandon Blockly. But you will have to get used to checking the JS Scripting docs and possibly even the code of the helper library (openhab-js, linked to in the JS Scripting docs) if you are after doing something really esoteric.

2 Likes