Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[automation] Binding actions cannot be configured by UIs #1745

Open
cweitkamp opened this issue Oct 21, 2020 · 55 comments · May be fixed by #4392
Open

[automation] Binding actions cannot be configured by UIs #1745

cweitkamp opened this issue Oct 21, 2020 · 55 comments · May be fixed by #4392
Labels
automation UI User Interface

Comments

@cweitkamp
Copy link
Contributor

This is a follow-up issue of eclipse-archived/smarthome#6602 as no solution has been implemented yet and we need to track it for OH3 release.

While implementing and testing my port of the Pushover add-on (openhab/openhab-addons#8586) I again stumbled upon this missing feature. Currently it is not possible to configure inputs for Binding actions based on ThingActions. The annotated inputs are not visible in UIs.

image

    @RuleAction(label = "@text/sendHTMLMessageActionLabel", description = "@text/sendHTMLMessageActionDescription")
    public @ActionOutput(name = "sent", label = "@text/sendMessageActionOutputLabel", description = "@text/sendMessageActionOutputDescription", type = "java.lang.Boolean") Boolean sendHtmlMessage(
            @ActionInput(name = "message", label = "@text/sendMessageActionInputMessageLabel", description = "@text/sendMessageActionInputMessageDescription", type = "java.lang.String", required = true) String message,
            @ActionInput(name = "title", label = "@text/sendMessageActionInputTitleLabel", description = "@text/sendMessageActionInputTitleDescription", type = "java.lang.String") @Nullable String title) {
        logger.trace("ThingAction 'sendHtmlMessage' called with value(s): message='{}', title='{}'", message, title);
        return send(getDefaultPushoverMessageBuilder(message).withHtmlFormatting(), title);
    }

Internal automation actions like say or playSound define next to the inputs a list of configuration descriptions matching their inputs.

Conclusions of eclipse-archived/smarthome#6602 are:

  • No short term solution to add support for input in the UIs. Proposal is to hide all ThingActions.
  • The is uncertainty about when to use inputs and when to use configuration descriptions (e.g. currently all existing core actions for NGRE defined them twice, an input and a related configuration).

Question is how to proceed? Should we hide all ThingActions in the near future? Or would it be an option to introduce a way to add configuration descriptions to ThingActions (e.g. by adding a new annotation for it)?

A previous private discussion between @kaikreuzer , @ghys , @openhab-5iver and myself resulted in the following brainstorming:

  • Displaying inputs along with config parameters should be doable to configure an action instance in a rule.

  • Apparently inputs have a type which is referenced in the API as a Java type (like java.lang.String or org.eclipse.smarthome.core.types.Command or org.eclipse.smarthome.core.events.Event), that’s a little bit different to the config parameters whose type is “TEXT” etc. Supporting anything other than primitive types in the UI could be tricky.

  • Given a module type like:
    image

    and a rule like:
    image

    I’m not sure how inputs are to be configured here, I suppose in the simplest case it’s something like below, with what the user configured in input boxes. But what if you want to use an output from another module?

"inputs": {
  "topic": "some/topic",
  "value": "somevalue"
}
  • It is also IMO not exactly clear which inputs are to be presented to the user and which are “technical” (for example “event” in GenericEventCondition or “input” in CompareCondition).
  • The ItemCommandAction module type has both a “command” input and a “command” config parameter described as “the default command to be sent if none is passed as an input value” - this would be confusing for users if both were displayed.
@kaikreuzer
Copy link
Member

@cweitkamp While this is quite a nasty issue that needs to be tackled, I feel that we won't be able to solve it for the 3.0 release - would you agree to hence remove it from the OH3 issue tracking?

@cweitkamp
Copy link
Contributor Author

Yes, that is okay for me. But I would like to agree on a temporarily solution like suggested in #1878. We should maybe hide binding actions having an additional parameters in the UI to not tangle users.

I already tried a very rough approach (a22476c) which just maps all defined inputs to configuration parameters respectively. This makes them visible in the UI but does not work properly. And it does not feel right to do it that (simple) way.

@ghys
Copy link
Member

ghys commented Dec 6, 2020

We should maybe hide binding actions having an additional parameters in the UI to not tangle users.

Definitely, things that don't work should not be visible.

@cweitkamp
Copy link
Contributor Author

I agree. By default - as a temporarily solution in OHC - or by adding the visibility annotation in every add-on. I tend to the first solution because it will be less work and easier to revert after a final implementation.

@rkoshak
Copy link

rkoshak commented Dec 8, 2020

As you work on solving this issue going forward, I just want to mention that not all binding actions make sense and should never be in the list. For example, the Astro binding only provides actions that return a value. There is no way to actually use that value as an action so they shouldn't be there in the list.

@kaikreuzer
Copy link
Member

I wouldn't say so. Action results are put in the scope and should in theory be consumable by other actions in the rule that are later in the sequence.

@rkoshak
Copy link

rkoshak commented Dec 8, 2020

But, at least in my experimentation, the scope isn't shared between actions, even actions of the same rule. It's definitely not the case that the scope is shared between the conditions and the actions. I may have messed up my experiments though and came to the wrong conclusion.

Given the following rule that has one action that puts a variable into the context and another action that tries to log it out I always get "foo is undefined" in the logs. Is there some other way to put a variable into the scope?

triggers:
  - id: "2"
    configuration:
      itemName: aTestSwitch
    type: core.ItemCommandTrigger
conditions: []
actions:
  - inputs: {}
    id: "1"
    configuration:
      type: application/javascript
      script: this.foo = "Hello world!";
    type: script.ScriptAction
  - inputs: {}
    id: "3"
    configuration:
      type: application/javascript
      script: >-
        var logger =
        Java.type("org.slf4j.LoggerFactory").getLogger("org.openhab.model.script.Experiments");


        logger.info("foo is " + this.foo);
    type: script.ScriptAction
2020-12-08 14:48:21.199 [INFO ] [org.openhab.model.script.Experiments] - foo is undefined

@kaikreuzer
Copy link
Member

That's why I added "in theory" - haven't tried it myself yet and won't have to time to go deeper into that before the release...

@openhab-bot
Copy link
Collaborator

This issue has been mentioned on openHAB Community. There might be relevant details there:

https://community.openhab.org/t/openhab3-mail-rules-actions-not-same-as-openhab2/129377/2

@openhab-bot
Copy link
Collaborator

This issue has been mentioned on openHAB Community. There might be relevant details there:

https://community.openhab.org/t/ipcamera-new-ip-camera-binding/42771/2682

@openhab-bot
Copy link
Collaborator

This issue has been mentioned on openHAB Community. There might be relevant details there:

https://community.openhab.org/t/openhab-4-0-wishlist/142388/293

@boehan
Copy link
Contributor

boehan commented Aug 12, 2024

Given that #2810 has been merged, is the missing part only a matter of integration in UI, or is there still anything needed from core side?

@florian-h05
Copy link
Contributor

florian-h05 commented Sep 26, 2024

@kaikreuzer Don‘t move it - the core side is not finished yet.

@florian-h05
Copy link
Contributor

is there still anything needed from core side?

Yes, #2810 only gives access to the Thing actions of a Thing and allows to invoke them, but the input parameters are still difficult to handle.
The UI would rather need the input description in config parameter format, otherwise it is difficult to know which input type to show for what.

Example:
When I retrieve the Thing actions of my Fronius inverter, I get the following input definitions:

"inputs": [
      {
        "name": "from",
        "type": "java.time.LocalTime",
        "label": "Begin Timestamp",
        "description": "The beginning of the time range",
        "required": false,
        "tags": [],
        "reference": "",
        "defaultValue": ""
      },
      {
        "name": "until",
        "type": "java.time.LocalTime",
        "label": "End Timestamp",
        "description": "The (inclusive) end of the time range",
        "required": false,
        "tags": [],
        "reference": "",
        "defaultValue": ""
      }
    ],

To show input fields for this, I would need to an extra processing of these definitions inside the UI — meanwhile the rest is using config descriptions.

When comparing this with how config descriptions look (e.g. https:/openhab/openhab-webui/blob/217fc060ee0a976acc9e046a3dd8ed5f3d7470da/bundles/org.openhab.ui/web/src/assets/definitions/persistence.js#L23), there are many similarities, so it should be possible to modify the API to return config descriptions for the inputs.

@openhab-bot
Copy link
Collaborator

This issue has been mentioned on openHAB Community. There might be relevant details there:

https://community.openhab.org/t/incorporating-matter/127907/162

@lolodomo
Copy link
Contributor

lolodomo commented Sep 26, 2024

The UI would rather need the input description in config parameter format, otherwise it is difficult to know which input type to show for what.

The main difficulty would be the conversion of input parameter types. Are current input types only Java primitive data types?

And what to do with the output type ? Ignore it ? We could also decide to build two groups of config parameters, one for input parameters and one for the output parameter ?

Edit: we could keep by default the current format in the REST response and have a new one based on ConfigDescription triggered by a new API parameter. Like that we keep backward compatibility.

WDYT ?

@florian-h05
Copy link
Contributor

The main difficulty would be the conversion of input parameter types. Are current input types only Java primitive data types?

Yeah, that‘s the difficulty — current input types can in fact be defined by any string value and are specified in some cases, but usually they default to the canonical name of the input‘s class, see:

if (!"".equals(inp.type())) {
type = inp.type();
} else {
type = param.getType().getCanonicalName();
}

And what to do with the output type ? Ignore it ?

Yes — when executing an action, the output map will be serialised to a JSON object (basically key-value pairs), and I guess the values of the output map are just made toString().
So I don‘t think the output types really matter to the UI.

we could keep by default the current format in the REST response and have a new one based on ConfigDescription triggered by a new API parameter. Like that we keep backward compatibility.

I am not sure if there is actual need to keep backward compatibility compatibility here … I think HABApp might use this data, but I don‘t think anything in the official openHAB distro does use this.
But if you would prefer to keep backward compatibility, we could do it.
The more important question IMO is:

Where to get the config descriptions from? I have several options in mind:

  1. Map the Java types to config descriptions type and context. Could be really difficult but avoids changes to binding code.
  2. Modify the @ActionInput annotation to allows passing config descriptions or introduce a new @ActionInputConfigDescription. This would require to adjust 63 files in openhab-addons (see https:/search?q=repo%3Aopenhab%2Fopenhab-addons%20%40ActionInput&type=code)
  3. Store the config descriptions inside a xml file like thing-types etc.

WDYT?

@florian-h05
Copy link
Contributor

I am currently on vacation in 🇫🇷, will be back in action next week, so I will leave it to you.
When I’m back home I will take care of the UI part.

@lolodomo
Copy link
Contributor

lolodomo commented Sep 28, 2024

This is not exactly related to our current discussion but when I run this API (GET actions/{thingUID}), it looks like outputs field is always empty.
For example, one thing action from the astro binding is defined like that:

    @RuleAction(label = "get the elevation", description = "Get the elevation for a given time.")
    public @Nullable @ActionOutput(name = "getElevation", label = "Elevation", type = "org.openhab.core.library.types.QuantityType<javax.measure.quantity.Angle>") QuantityType<Angle> getElevation(
            @ActionInput(name = "date", label = "Date", required = false, description = "Considered date") @Nullable ZonedDateTime date) {
        logger.debug("Astro action 'getElevation' called");
        AstroThingHandler theHandler = this.handler;
        if (theHandler != null) {
            return theHandler.getElevation(date != null ? date : ZonedDateTime.now());
        } else {
            logger.info("Astro Action service ThingHandler is null!");
        }
        return null;
    }

Then the API returns

  {
    "actionUid": "astro.getElevation",
    "label": "get the elevation",
    "description": "Get the elevation for a given time.",
    "inputs": [
      {
        "name": "date",
        "type": "java.time.ZonedDateTime",
        "label": "Date",
        "description": "Considered date",
        "required": false,
        "tags": [],
        "reference": "",
        "defaultValue": ""
      }
    ],
    "outputs": []
  },

I guess we should have one output ?

@florian-h05
Copy link
Contributor

Yes, looks like a bug.

@lolodomo
Copy link
Contributor

The "required" parameter of the @ActionInput annotation is not clear for me. Does it mean the value provided should not be null ? It is what I guess but in this case why do we have this in certain bindings, that "required = true" but with @nullable ?

$ find */src -name "*.java" -exec grep -H "@ActionInput" {} \; | grep "required = true" | grep "@Nullable"
org.openhab.binding.avmfritz/src/main/java/org/openhab/binding/avmfritz/internal/actions/AVMFritzHeatingActions.java:            @ActionInput(name = "Duration", label = "@text/setBoostModeDurationInputLabel", description = "@text/setBoostModeDurationInputDescription", type = "java.lang.Long", required = true) @Nullable Long duration) {
org.openhab.binding.avmfritz/src/main/java/org/openhab/binding/avmfritz/internal/actions/AVMFritzHeatingActions.java:            @ActionInput(name = "Duration", label = "@text/setWindowOpenModeDurationInputLabel", description = "@text/setWindowOpenModeDurationInputDescription", type = "java.lang.Long", required = true) @Nullable Long duration) {
org.openhab.binding.flume/src/main/java/org/openhab/binding/flume/internal/actions/FlumeDeviceActions.java:            @ActionInput(name = "sinceDateTime", label = "Since Date/Time", required = true, description = "Restrict the query range to data samples since this datetime.") @Nullable LocalDateTime sinceDateTime,
org.openhab.binding.flume/src/main/java/org/openhab/binding/flume/internal/actions/FlumeDeviceActions.java:            @ActionInput(name = "untilDateTime", label = "Until Date/Time", required = true, description = "Restrict the query range to data samples until this datetime.") @Nullable LocalDateTime untilDateTime,
org.openhab.binding.flume/src/main/java/org/openhab/binding/flume/internal/actions/FlumeDeviceActions.java:            @ActionInput(name = "bucket", label = "Bucket size", required = true, description = "The bucket grouping of the data we are querying (MIN, HR, DAY, MON, YR).") @Nullable String bucket,
org.openhab.binding.flume/src/main/java/org/openhab/binding/flume/internal/actions/FlumeDeviceActions.java:            @ActionInput(name = "operation", label = "Operation", required = true, description = "The aggregate/accumulate operation to perform (SUM, AVG, MIN, MAX, CNT).") @Nullable String operation) {
org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnModuleActions.java:            @ActionInput(name = "table", required = true, type = "java.lang.String", label = "Table", description = "The key table (A-D)") @Nullable String table,
org.openhab.binding.lcn/src/main/java/org/openhab/binding/lcn/internal/LcnModuleActions.java:            @ActionInput(name = "action", required = true, type = "java.lang.String", label = "Action", description = "The action (HIT, MAKE, BREAK)") @Nullable String action) {
org.openhab.binding.pushover/src/main/java/org/openhab/binding/pushover/internal/actions/PushoverActions.java:            @ActionInput(name = "url", label = "@text/sendMessageActionInputURLLabel", description = "@text/sendMessageActionInputURLDescription", type = "java.lang.String", required = true) @Nullable String url,
org.openhab.binding.pushover/src/main/java/org/openhab/binding/pushover/internal/actions/PushoverActions.java:            @ActionInput(name = "url", label = "@text/sendMessageActionInputURLLabel", description = "@text/sendMessageActionInputURLDescription", type = "java.lang.String", required = true) @Nullable String url,
org.openhab.binding.pushover/src/main/java/org/openhab/binding/pushover/internal/actions/PushoverActions.java:            @ActionInput(name = "attachment", label = "@text/sendMessageActionInputAttachmentLabel", description = "@text/sendMessageActionInputAttachmentDescription", type = "java.lang.String", required = true) @Nullable String attachment,
org.openhab.binding.pushover/src/main/java/org/openhab/binding/pushover/internal/actions/PushoverActions.java:            @ActionInput(name = "attachment", label = "@text/sendMessageActionInputAttachmentLabel", description = "@text/sendMessageActionInputAttachmentDescription", type = "java.lang.String", required = true) @Nullable String attachment,
org.openhab.binding.pushover/src/main/java/org/openhab/binding/pushover/internal/actions/PushoverActions.java:            @ActionInput(name = "receipt", label = "@text/cancelPriorityMessageActionInputReceiptLabel", description = "@text/cancelPriorityMessageActionInputReceiptDescription", type = "java.lang.String", required = true) @Nullable String receipt) {
org.openhab.binding.pushover/src/main/java/org/openhab/binding/pushover/internal/actions/PushoverActions.java:            @ActionInput(name = "device", label = "@text/sendMessageActionInputDeviceLabel", description = "@text/sendMessageActionInputDeviceDescription", type = "java.lang.String", required = true) @Nullable String device,
org.openhab.binding.pushover/src/main/java/org/openhab/binding/pushover/internal/actions/PushoverActions.java:            @ActionInput(name = "device", label = "@text/sendMessageActionInputDeviceLabel", description = "@text/sendMessageActionInputDeviceDescription", type = "java.lang.String", required = true) @Nullable String device,
org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/FritzboxActions.java:            @ActionInput(name = "phonenumber", label = "@text/phonebookLookupActionInputPhoneNumberLabel", description = "@text/phonebookLookupActionInputPhoneNumberDescription", type = "java.lang.String", required = true) @Nullable String phonenumber,
org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/FritzboxActions.java:            @ActionInput(name = "phonenumber", label = "@text/phonebookLookupActionInputPhoneNumberLabel", description = "@text/phonebookLookupActionInputPhoneNumberDescription", type = "java.lang.String", required = true) @Nullable String phonenumber) {
org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/FritzboxActions.java:            @ActionInput(name = "phonenumber", label = "@text/phonebookLookupActionInputPhoneNumberLabel", description = "@text/phonebookLookupActionInputPhoneNumberDescription", type = "java.lang.String", required = true) @Nullable String phonenumber,
org.openhab.binding.tr064/src/main/java/org/openhab/binding/tr064/internal/FritzboxActions.java:            @ActionInput(name = "phonenumber", label = "@text/phonebookLookupActionInputPhoneNumberLabel", description = "@text/phonebookLookupActionInputPhoneNumberDescription", type = "java.lang.String", required = true) @Nullable String phonenumber,

@florian-h05 : if my understanding is correct, that means if you get a config description parameter with required set to false, then the user is not forced to fill the parameter but when calling the action, you will have to provide null as value.

@florian-h05
Copy link
Contributor

then the user is not forced to fill the parameter but when calling the action, you will have to provide null as value.

I don’t think that I have to actually pass null as value through the API, I would think null is passed if the param is missing. But that is something we can easily try out …

@lolodomo
Copy link
Contributor

lolodomo commented Sep 28, 2024

I am not succeeding to invoke an action with the API.
Here is my action:

    @RuleAction(label = "test", description = "Test action")
    public void testAction(
            @ActionInput(name = "booleanParam", label = "boolean parameter", required = true, description = "Descr boolean parameter") boolean booleanParam,
            @ActionInput(name = "booleanParam1", label = "Boolean param 1", required = false, description = "Descr Boolean param 1") @Nullable Boolean booleanParam1,
            @ActionInput(name = "booleanParam2", label = "Boolean param 2", required = true, description = "Descr Boolean param 2") Boolean booleanParam2,
            @ActionInput(name = "intParam", label = "int parameter", required = true, description = "Descr int parameter") int intParam,
            @ActionInput(name = "IntegerParam1", label = "Integer param 1", required = false, description = "Descr Integer param 1") @Nullable Integer IntegerParam1,
            @ActionInput(name = "IntegerParam2", label = "Integer param 2", required = true, description = "Descr Integer param 2") Integer IntegerParam2,
            @ActionInput(name = "longParam", label = "long parameter", required = true, description = "Descr long parameter") long longParam,
            @ActionInput(name = "longParam1", label = "Long param 1", required = false, description = "Descr Long param 1") @Nullable Long longParam1,
            @ActionInput(name = "longParam2", label = "Long param 2", required = true, description = "Descr Long param 2") Long longParam2,
            @ActionInput(name = "doubleParam", label = "double parameter", required = true, description = "Descr double parameter") double doubleParam,
            @ActionInput(name = "doubleParam1", label = "Double param 1", required = false, description = "Descr Double param 1") @Nullable Double doubleParam1,
            @ActionInput(name = "doubleParam2", label = "Double param 2", required = true, description = "Descr Double param 2") Double doubleParam2,
            @ActionInput(name = "stringParam1", label = "String param 1", required = false, description = "Descr String param 1") @Nullable String stringParam1,
            @ActionInput(name = "stringParam2", label = "String param 2", required = true, description = "Descr String param 2") String stringParam2,
            @ActionInput(name = "stringParam3", label = "String param 3", description = "Descr String param 3") @Nullable String stringParam3,
            @ActionInput(name = "stringParam4", label = "String param 4", description = "Descr String param 4") String stringParam4) {
    }

Here is what I provide when I invoke the action with the UI:
image
The API returns code 200 but I got this exception in logs:

18:09:31.835 [ERROR] [dule.handler.AnnotationActionHandler] - Could not call method 'public void org.openhab.binding.astro.internal.action.AstroActions.testAction(boolean,java.lang.Boolean,java.lang.Boolean,int,java.lang.Integer,java.lang.Integer,long,java.lang.Long,java.lang.Long,double,java.lang.Double,java.lang.Double,java.lang.String,java.lang.String,java.lang.String,java.lang.String)' from module type 'astro.testAction'.
java.lang.IllegalArgumentException: argument type mismatch
	at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:?]
	at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[?:?]
	at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:?]
	at java.lang.reflect.Method.invoke(Method.java:568) ~[?:?]
	at org.openhab.core.automation.internal.module.handler.AnnotationActionHandler.execute(AnnotationActionHandler.java:88) ~[?:?]
	at org.openhab.core.automation.rest.internal.ThingActionsResource.executeThingAction(ThingActionsResource.java:245) ~[?:?]
	at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:?]
	at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[?:?]
	at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:?]
	at java.lang.reflect.Method.invoke(Method.java:568) ~[?:?]
	at org.apache.cxf.service.invoker.AbstractInvoker.performInvocation(AbstractInvoker.java:179) ~[bundleFile:3.6.2]
	at org.apache.cxf.service.invoker.AbstractInvoker.invoke(AbstractInvoker.java:96) ~[bundleFile:3.6.2]
	at org.apache.cxf.jaxrs.JAXRSInvoker.invoke(JAXRSInvoker.java:201) ~[bundleFile:3.6.2]
	at org.apache.cxf.jaxrs.JAXRSInvoker.invoke(JAXRSInvoker.java:104) ~[bundleFile:3.6.2]
	at org.apache.cxf.interceptor.ServiceInvokerInterceptor$1.run(ServiceInvokerInterceptor.java:59) ~[bundleFile:3.6.2]
	at org.apache.cxf.interceptor.ServiceInvokerInterceptor.handleMessage(ServiceInvokerInterceptor.java:96) ~[bundleFile:3.6.2]
	at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:307) ~[bundleFile:3.6.2]
	at org.apache.cxf.transport.ChainInitiationObserver.onMessage(ChainInitiationObserver.java:121) ~[bundleFile:3.6.2]
	at org.apache.cxf.transport.http.AbstractHTTPDestination.invoke(AbstractHTTPDestination.java:265) ~[bundleFile:3.6.2]
	at org.apache.cxf.transport.servlet.ServletController.invokeDestination(ServletController.java:234) ~[bundleFile:3.6.2]
	at org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:208) ~[bundleFile:3.6.2]
	at org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:160) ~[bundleFile:3.6.2]
	at org.apache.cxf.transport.servlet.CXFNonSpringServlet.invoke(CXFNonSpringServlet.java:225) ~[bundleFile:3.6.2]
	at org.apache.cxf.transport.servlet.AbstractHTTPServlet.handleRequest(AbstractHTTPServlet.java:304) ~[bundleFile:3.6.2]
	at org.apache.cxf.transport.servlet.AbstractHTTPServlet.doPost(AbstractHTTPServlet.java:217) ~[bundleFile:3.6.2]
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:681) ~[bundleFile:?]
	at org.apache.cxf.transport.servlet.AbstractHTTPServlet.service(AbstractHTTPServlet.java:279) ~[bundleFile:3.6.2]
	at org.ops4j.pax.web.service.spi.servlet.OsgiInitializedServlet.service(OsgiInitializedServlet.java:102) ~[bundleFile:?]
	at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:799) ~[bundleFile:9.4.54.v20240208]
	at org.eclipse.jetty.servlet.ServletHandler$ChainEnd.doFilter(ServletHandler.java:1656) ~[bundleFile:9.4.54.v20240208]
	at org.ops4j.pax.web.service.spi.servlet.OsgiFilterChain.doFilter(OsgiFilterChain.java:113) ~[bundleFile:?]
	at org.ops4j.pax.web.service.jetty.internal.PaxWebServletHandler.doHandle(PaxWebServletHandler.java:334) ~[bundleFile:?]
	at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143) ~[bundleFile:9.4.54.v20240208]
	at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:600) ~[bundleFile:9.4.54.v20240208]
	at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127) ~[bundleFile:9.4.54.v20240208]
	at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:235) ~[bundleFile:9.4.54.v20240208]
	at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1624) ~[bundleFile:9.4.54.v20240208]
	at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:233) ~[bundleFile:9.4.54.v20240208]
	at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1440) ~[bundleFile:9.4.54.v20240208]
	at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:188) ~[bundleFile:9.4.54.v20240208]
	at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:505) ~[bundleFile:9.4.54.v20240208]
	at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1594) ~[bundleFile:9.4.54.v20240208]
	at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:186) ~[bundleFile:9.4.54.v20240208]
	at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1355) ~[bundleFile:9.4.54.v20240208]
	at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141) ~[bundleFile:9.4.54.v20240208]
	at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:234) ~[bundleFile:9.4.54.v20240208]
	at org.ops4j.pax.web.service.jetty.internal.PrioritizedHandlerCollection.handle(PrioritizedHandlerCollection.java:96) ~[bundleFile:?]
	at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127) ~[bundleFile:9.4.54.v20240208]
	at org.eclipse.jetty.server.Server.handle(Server.java:516) ~[bundleFile:9.4.54.v20240208]
	at org.eclipse.jetty.server.HttpChannel.lambda$handle$1(HttpChannel.java:487) ~[bundleFile:9.4.54.v20240208]
	at org.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:732) [bundleFile:9.4.54.v20240208]
	at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:479) [bundleFile:9.4.54.v20240208]
	at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:277) [bundleFile:9.4.54.v20240208]
	at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:311) [bundleFile:9.4.54.v20240208]
	at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:105) [bundleFile:9.4.54.v20240208]
	at org.eclipse.jetty.io.ChannelEndPoint$1.run(ChannelEndPoint.java:104) [bundleFile:9.4.54.v20240208]
	at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:883) [bundleFile:9.4.54.v20240208]
	at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1034) [bundleFile:9.4.54.v20240208]
	at java.lang.Thread.run(Thread.java:833) [?:?]

@lolodomo
Copy link
Contributor

lolodomo commented Sep 28, 2024

Looks like it works with Boolean and String types but not with Integer !

Another failing basic example:

    @RuleAction(label = "test 5", description = "Test action 5")
    public void testAction5(
            @ActionInput(name = "IntegerParam", label = "Integer param", required = true, description = "Descr Integer param") Integer IntegerParam) {
        logger.info("testAction5 IntegerParam = {}", IntegerParam);
    }

Called like that:
image

Leads ot exception

18:50:28.343 [ERROR] [dule.handler.AnnotationActionHandler] - Could not call method 'public void org.openhab.binding.astro.internal.action.AstroActions.testAction5(java.lang.Integer)' from module type 'astro.testAction5'.
java.lang.IllegalArgumentException: argument type mismatch
	at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:?]
	at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[?:?]
	at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:?]
	at java.lang.reflect.Method.invoke(Method.java:568) ~[?:?]
	at org.openhab.core.automation.internal.module.handler.AnnotationActionHandler.execute(AnnotationActionHandler.java:88) ~[?:?]
	at org.openhab.core.automation.rest.internal.ThingActionsResource.executeThingAction(ThingActionsResource.java:245) ~[?:?]
	at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:?]
	at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[?:?]
	at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:?]
	at java.lang.reflect.Method.invoke(Method.java:568) ~[?:?]
	at org.apache.cxf.service.invoker.AbstractInvoker.performInvocation(AbstractInvoker.java:179) ~[bundleFile:3.6.2]
	at org.apache.cxf.service.invoker.AbstractInvoker.invoke(AbstractInvoker.java:96) ~[bundleFile:3.6.2]
	at org.apache.cxf.jaxrs.JAXRSInvoker.invoke(JAXRSInvoker.java:201) ~[bundleFile:3.6.2]
	at org.apache.cxf.jaxrs.JAXRSInvoker.invoke(JAXRSInvoker.java:104) ~[bundleFile:3.6.2]
	at org.apache.cxf.interceptor.ServiceInvokerInterceptor$1.run(ServiceInvokerInterceptor.java:59) ~[bundleFile:3.6.2]
	at org.apache.cxf.interceptor.ServiceInvokerInterceptor.handleMessage(ServiceInvokerInterceptor.java:96) ~[bundleFile:3.6.2]
	at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:307) ~[bundleFile:3.6.2]
	at org.apache.cxf.transport.ChainInitiationObserver.onMessage(ChainInitiationObserver.java:121) ~[bundleFile:3.6.2]
	at org.apache.cxf.transport.http.AbstractHTTPDestination.invoke(AbstractHTTPDestination.java:265) ~[bundleFile:3.6.2]
	at org.apache.cxf.transport.servlet.ServletController.invokeDestination(ServletController.java:234) ~[bundleFile:3.6.2]
	at org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:208) ~[bundleFile:3.6.2]
	at org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:160) ~[bundleFile:3.6.2]
	at org.apache.cxf.transport.servlet.CXFNonSpringServlet.invoke(CXFNonSpringServlet.java:225) ~[bundleFile:3.6.2]
	at org.apache.cxf.transport.servlet.AbstractHTTPServlet.handleRequest(AbstractHTTPServlet.java:304) ~[bundleFile:3.6.2]
	at org.apache.cxf.transport.servlet.AbstractHTTPServlet.doPost(AbstractHTTPServlet.java:217) ~[bundleFile:3.6.2]
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:681) ~[bundleFile:?]
	at org.apache.cxf.transport.servlet.AbstractHTTPServlet.service(AbstractHTTPServlet.java:279) ~[bundleFile:3.6.2]
	at org.ops4j.pax.web.service.spi.servlet.OsgiInitializedServlet.service(OsgiInitializedServlet.java:102) ~[bundleFile:?]
	at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:799) ~[bundleFile:9.4.54.v20240208]
	at org.eclipse.jetty.servlet.ServletHandler$ChainEnd.doFilter(ServletHandler.java:1656) ~[bundleFile:9.4.54.v20240208]
	at org.ops4j.pax.web.service.spi.servlet.OsgiFilterChain.doFilter(OsgiFilterChain.java:113) ~[bundleFile:?]
	at org.ops4j.pax.web.service.jetty.internal.PaxWebServletHandler.doHandle(PaxWebServletHandler.java:334) ~[bundleFile:?]
	at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143) ~[bundleFile:9.4.54.v20240208]
	at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:600) ~[bundleFile:9.4.54.v20240208]
	at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127) ~[bundleFile:9.4.54.v20240208]
	at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:235) ~[bundleFile:9.4.54.v20240208]
	at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1624) ~[bundleFile:9.4.54.v20240208]
	at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:233) ~[bundleFile:9.4.54.v20240208]
	at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1440) ~[bundleFile:9.4.54.v20240208]
	at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:188) ~[bundleFile:9.4.54.v20240208]
	at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:505) ~[bundleFile:9.4.54.v20240208]
	at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1594) ~[bundleFile:9.4.54.v20240208]
	at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:186) ~[bundleFile:9.4.54.v20240208]
	at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1355) ~[bundleFile:9.4.54.v20240208]
	at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141) ~[bundleFile:9.4.54.v20240208]
	at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:234) ~[bundleFile:9.4.54.v20240208]
	at org.ops4j.pax.web.service.jetty.internal.PrioritizedHandlerCollection.handle(PrioritizedHandlerCollection.java:96) ~[bundleFile:?]
	at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127) ~[bundleFile:9.4.54.v20240208]
	at org.eclipse.jetty.server.Server.handle(Server.java:516) ~[bundleFile:9.4.54.v20240208]
	at org.eclipse.jetty.server.HttpChannel.lambda$handle$1(HttpChannel.java:487) ~[bundleFile:9.4.54.v20240208]
	at org.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:732) [bundleFile:9.4.54.v20240208]
	at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:479) [bundleFile:9.4.54.v20240208]
	at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:277) [bundleFile:9.4.54.v20240208]
	at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:311) [bundleFile:9.4.54.v20240208]
	at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:105) [bundleFile:9.4.54.v20240208]
	at org.eclipse.jetty.io.ChannelEndPoint$1.run(ChannelEndPoint.java:104) [bundleFile:9.4.54.v20240208]
	at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.runTask(EatWhatYouKill.java:338) [bundleFile:9.4.54.v20240208]
	at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:315) [bundleFile:9.4.54.v20240208]
	at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.tryProduce(EatWhatYouKill.java:173) [bundleFile:9.4.54.v20240208]
	at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:131) [bundleFile:9.4.54.v20240208]
	at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:409) [bundleFile:9.4.54.v20240208]
	at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:883) [bundleFile:9.4.54.v20240208]
	at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1034) [bundleFile:9.4.54.v20240208]
	at java.lang.Thread.run(Thread.java:833) [?:?]

I guess that 123 is put in an Object that is not java.lang.Integer.
I am going to log the table of parameters passed to the method.

lolodomo added a commit to lolodomo/openhab-core that referenced this issue Sep 28, 2024
@lolodomo lolodomo linked a pull request Sep 28, 2024 that will close this issue
@lolodomo
Copy link
Contributor

lolodomo commented Sep 28, 2024

I guess that 123 is put in an Object that is not java.lang.Integer.
I am going to log the table of parameters passed to the method.

I don't know why but my provided value 123 is stored in a java.lang.Double as 123.0.

19:23:47.248 [WARN ] [dule.handler.AnnotationActionHandler] - Argument 0: value 123.0 of type java.lang.Double
19:23:47.252 [ERROR] [dule.handler.AnnotationActionHandler] - Could not call method 'public void org.openhab.binding.astro.internal.action.AstroActions.testAction5(java.lang.Integer)' from module type 'astro.testAction5'.
java.lang.IllegalArgumentException: argument type mismatch

Our current code that build the method arguments before invoking the method should very probably apply type casting.

@lolodomo
Copy link
Contributor

lolodomo commented Sep 28, 2024

I don’t think that I have to actually pass null as value through the API, I would think null is passed if the param is missing. But that is something we can easily try out …

Tested, you're right. In case a parameter is not provided, null is passed as parameter to the method.

So, regarding invokation oif the action through the REST API, it works well for the following parameter types: boolean, Boolean, double, Double, String. Of course, for boolean and double, if you do not provide any value or if you provide null as value, there is an expected exception as null is considered.
It fails for parameter of types int, Integer, long, Long, ...

I have an idea how to fix that.

@florian-h05
Copy link
Contributor

Our current code that build the method arguments before invoking the method should very probably apply type casting.

It fails for parameter of types int, Integer, long, Long, ...

IMO we should cast the double received from the API to the required type then, theoretically this is a lossy conversion but since the double has an integer value we don’t have to worry about loosing precision.

lolodomo added a commit to lolodomo/openhab-core that referenced this issue Sep 29, 2024
API GET /actions/{thingUID} now returns the input parameters also as a list of configuration description parameters.
It is provided only when all input parameters have a type than can be mapped to the type of a configuration description parameter.
It will be used in particular by Main UI to expose actions.

Also enhance the POST API (execute a thing action) in order to be more flexible regarding the type of each provided argument value and to map the value to the expected data type.

Related to openhab#1745

Signed-off-by: Laurent Garnier <[email protected]>
lolodomo added a commit to lolodomo/openhab-core that referenced this issue Sep 29, 2024
API GET /actions/{thingUID} now returns the input parameters also as a list of configuration description parameters.
It is provided only when all input parameters have a type than can be mapped to the type of a configuration description parameter.
It will be used in particular by Main UI to expose actions.

Also enhance the POST API (execute a thing action) in order to be more flexible regarding the type of each provided argument value and to map the value to the expected data type.

Related to openhab#1745

Signed-off-by: Laurent Garnier <[email protected]>
@lolodomo
Copy link
Contributor

lolodomo commented Sep 29, 2024

IMO we should cast the double received from the API to the required type

It is now done. String value is also accepted for numbers.
LocalDate / LocalTime / LocalDateTime / ZonedDateTime types can now also be used by providing a string value respecting the appropriate format. I will document that.
I will probably support few additional types to cover most of those currently used in existing thing actions.

@lolodomo
Copy link
Contributor

lolodomo commented Sep 30, 2024

@florian-h05 : for QuantityType, should I simply consider type DECIMAL but with unit and unitLabel provided ?
Do you know what is exactly "unitLabel" ?
In that case, the user can only set the decimal value but without ability to change unit ?
My idea would be to provide the default unit for the dimension.

@florian-h05
Copy link
Contributor

florian-h05 commented Sep 30, 2024

@lolodomo Can you please first merge my PR lolodomo#3 before doing further changes? I have just resolved conflicts.
I already have a basic QuantityType handling implemented, we can still change it.

@lolodomo
Copy link
Contributor

lolodomo commented Oct 1, 2024

@florian-h05 : do not forget you will have to display the action result in UI. Is the current output fine for you? We could imagine a new parameter to request the output as a simple string. WDYT?

PS: I will merge your changes this evening.

@florian-h05
Copy link
Contributor

Is the current output fine for you? We could imagine a new parameter to request the output as a simple string. WDYT?

I would think it already is a JS object (this is what a Map gets serialised to) of stringified Java objects. Is this not the case?
I would just display what comes back from the API in a text field, no special display as the output might be very different and a text field is enough to read it. WDYT?

PS: I will merge your changes this evening.

Let me know if there are new conflicts that have to be resolved then.

@florian-h05
Copy link
Contributor

florian-h05 commented Oct 1, 2024

for QuantityType, should I simply consider type DECIMAL but with unit and unitLabel provided ?
Do you know what is exactly "unitLabel" ?

I would rather provide the Dimension and introduce a new context „quantity“ for TEXT input. The UI already includes a list of dimensions with their units to be used by the unit field of Items, so the data is already there …

@lolodomo
Copy link
Contributor

lolodomo commented Oct 1, 2024

I would think it already is a JS object (this is what a Map gets serialised to) of stringified Java objects. Is this not the case?
I would just display what comes back from the API in a text field, no special display as the output might be very different and a text field is enough to read it. WDYT?

Ok but if the map contains a "result" entry, just display its value (and not the full map).

@florian-h05
Copy link
Contributor

Can you please provide me an example output?

@lolodomo
Copy link
Contributor

lolodomo commented Oct 1, 2024

For an action returning an Integer, the result is the following:

{
  "result": 5544
}

The UI should just show: 5544
For an action returning a String:

{
  "result": "Here is the result."
}

The UI should just show: Here is the result.
For a void action:

{}

The UI should just show an empty string.

@lolodomo
Copy link
Contributor

lolodomo commented Oct 1, 2024

If I try to return a java.time.LocalDateTime, I get this log:

20:34:41.869 [WARN ] [dule.handler.AnnotationActionHandler] - Non compatible return type 'class java.time.LocalDateTime' on action method.

Current code only accepts Boolean, String, Integer, Float and Double as output types.

https:/openhab/openhab-core/blob/main/bundles/org.openhab.core.automation/src/main/java/org/openhab/core/automation/internal/module/handler/AnnotationActionHandler.java#L107

@florian-h05
Copy link
Contributor

Current code only accepts Boolean, String, Integer, Float and Double as output types.

That sounds good, because these are super easy to serialise for the API!

@lolodomo
Copy link
Contributor

lolodomo commented Oct 1, 2024

I have to check if all our actions return one of these 5 types.

@lolodomo
Copy link
Contributor

lolodomo commented Oct 4, 2024

Here are the types found in existing thing action inputs that will be supported by UI:

  • String
  • Boolean
  • int
  • Integer
  • double
  • Double
  • long
  • Long
  • Number
  • DecimalType
  • QuantityType<Temperature>
  • QuantityType<Power>
  • QuantityType<Energy>
  • QuantityType<Time>
  • LocalDateTime
  • LocalDate
  • LocalTime
  • ZonedDateTime
  • Date
  • Instant
  • Duration

And those that will not be supported:

  • TimeUnit
  • UnitGroup
  • byte[]
  • List<String>
  • String...
  • List<Duration>
  • List<QuantityType<Power>>
  • Command
  • Map<String, @Nullable Object>
  • Object...

@lolodomo
Copy link
Contributor

lolodomo commented Oct 4, 2024

I am counting 21 existing thing actions that cannot be run properly due to an unsupported output type:

  • astro: 4 => 3 QuantityType<?> + 1 ZonedDateTime
  • energidataservice: 3 => 2 Map<Instant, BigDecimal> + 1 BigDecimal
  • flume: 1 => 1 QuantityType<?>
  • modbus/helioseasycontrols: 4 => 4 List<String>
  • solarforecast: 5 => 3 QuantityType<?> + 2 Instant
  • visualcrossing: 2 => 2 WeatherResponse
  • dbquery: 2 => 2 ActionQueryResult

They should fail here:
https:/openhab/openhab-core/blob/main/bundles/org.openhab.core.automation/src/main/java/org/openhab/core/automation/internal/module/handler/AnnotationActionHandler.java#L119

@jlaur: as you are concerned with energidataservice, does it mean that these actions can be used inside a rule but not through the REST API ?

@jlaur
Copy link
Contributor

jlaur commented Oct 4, 2024

as you are concerned with energidataservice, does it mean that these actions can be used inside a rule but not through the REST API ?

All actions are working correctly for both DSL and JavaScript, but I have not tested any of them through REST API since I have not been aware of this really.

lolodomo added a commit to lolodomo/openhab-core that referenced this issue Oct 6, 2024
Fixes openhab#1745

Return config description parameters for the ActionInputs of ThingActions for the REST GET /action/{thingUID} and REST GET /module-types endpoints.
The config description parameters are only provided if all input parameters have a type that can be mapped to a config description parameter (String, boolean, Boolean, byte, Byte, short, Short, int, Integer, long, Long, float, Float, double, Double, Number, DecimalType, QuantityType<?>, LocalDateTime, LocalDate, LocalTime, ZonedDateTime, Date, Instant and Duration).

Enhance the REST POST /actions/{thingUID}/{actionUid} endpoint (allows invoking Thing actions via REST) and the AnnotationActionHandler (allows invoking Thing actions from UI-rules) in order to be more flexible regarding the type of each provided argument value and to map the value to the expected data type. Number and string values will be accepted as inputs and the expected data type will be created from this value.

This will be used by the UI's Thing page and rule editor to allow invoking Thing actions through the UI or adding them to UI-bases rules.

Signed-off-by: Laurent Garnier <[email protected]>
Signed-off-by: Florian Hotze <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
automation UI User Interface
Projects
None yet
Development

Successfully merging a pull request may close this issue.

10 participants