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

Enhance ThingActions UI support #4392

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

lolodomo
Copy link
Contributor

@lolodomo lolodomo commented Sep 28, 2024

Fixes #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]

@lolodomo lolodomo requested a review from a team as a code owner September 28, 2024 17:04
@lolodomo lolodomo marked this pull request as draft September 28, 2024 17:04
@lolodomo
Copy link
Contributor Author

lolodomo commented Sep 28, 2024

For the following action

    @RuleAction(label = "test", description = "Test action")
    public @ActionOutput(name = "result", label = "Test result", type = "java.lang.Integer") int testAction(
            @ActionInput(name = "booleanParam1", label = "@text/rule.testAction.param.booleanParam1.label", required = true, description = "@text/rule.testAction.param.booleanParam1.description") boolean booleanParam1,
            @ActionInput(name = "booleanParam2", label = "Boolean parameter 2", required = false, description = "Descr Boolean parameter 1") @Nullable Boolean booleanParam2,
            @ActionInput(name = "booleanParam3", label = "Boolean parameter 3", required = true, description = "Descr Boolean parameter 2") Boolean booleanParam3,
            @ActionInput(name = "intParam", label = "int parameter", required = true, description = "Descr int parameter") int intParam,
            @ActionInput(name = "integerParam", label = "Integer parameter", description = "Descr Integer parameter") @Nullable Integer integerParam,
            @ActionInput(name = "longParam1", label = "long parameter", required = true, description = "Descr long parameter") long longParam1,
            @ActionInput(name = "longParam2", label = "Long parameter", description = "Descr Long parameter") @Nullable Long longParam2,
            @ActionInput(name = "doubleParam1", label = "double parameter", required = true, description = "Descr double parameter") double doubleParam1,
            @ActionInput(name = "doubleParam2", label = "Double parameter", description = "Descr Double parameter") @Nullable Double doubleParam2,
            @ActionInput(name = "numberParam", label = "Number parameter", description = "Descr Number parameter") @Nullable Number numberParam,
            @ActionInput(name = "stringParam", label = "String parameter", description = "Descr String parameter") @Nullable String stringParam,
            @ActionInput(name = "decimalTypeParam", label = "DecimalType parameter", description = "Descr DecimalType parameter") @Nullable DecimalType decimalTypeParam,
            @ActionInput(name = "localDateParam", label = "LocalDate parameter", description = "Descr LocalDate parameter") @Nullable LocalDate localDateParam,
            @ActionInput(name = "localTimeParam", label = "LocalTime parameter", description = "Descr LocalTime parameter") @Nullable LocalTime localTimeParam,
            @ActionInput(name = "localDateTimeParam", label = "LocalDateTime parameter", description = "Descr LocalDateTime parameter") @Nullable LocalDateTime localDateTimeParam,
            @ActionInput(name = "zonedDateTimeParam", label = "ZonedDateTime parameter", description = "Descr ZonedDateTime parameter") @Nullable ZonedDateTime zonedDateTimeParam,
            @ActionInput(name = "dateParam", label = "Date parameter", description = "Descr Date parameter") @Nullable Date dateParam,
            @ActionInput(name = "instantParam", label = "Instant parameter", description = "Descr Instant parameter") @Nullable Instant instantParam,
            @ActionInput(name = "durationParam", label = "Duration parameter", description = "Descr Duration parameter") @Nullable Duration durationParam,
            @ActionInput(name = "quantityTemperatureParam", label = "QuantityType<Temperature> parameter", description = "Descr QuantityType<Temperature> parameter", type = "QuantityType<Temperature>") @Nullable QuantityType<Temperature> quantityTemperatureParam,
            @ActionInput(name = "quantityPowerParam", label = "QuantityType<Power> parameter", description = "Descr QuantityType<Power> parameter", type = "org.openhab.core.library.types.QuantityType<javax.measure.quantity.Power>") @Nullable QuantityType<Power> quantityPowerParam) {
        logger.info(
                "testAction booleanParam1 = {}, booleanParam2 = {}, booleanParam3 = {}, intParam = {}, integerParam = {}, longParam1 = {}, longParam2 = {}, doubleParam1 = {}, doubleParam2 = {}, numberParam = {}, stringParam = {}, decimalTypeParam = {}, localDateParam = {}, localTimeParam = {}, localDateTimeParam = {}, zonedDateTimeParam = {}, dateParam = {}, instantParam = {}, durationParam = {}, quantityTemperatureParam = {}, quantityPowerParam = {}",
                booleanParam1, booleanParam2, booleanParam3, intParam, integerParam, longParam1, longParam2,
                doubleParam1, doubleParam2, numberParam, stringParam, decimalTypeParam, localDateParam, localTimeParam,
                localDateTimeParam, zonedDateTimeParam, dateParam, instantParam, durationParam,
                quantityTemperatureParam, quantityPowerParam);
        return 256;
    }

The GET API now returns:

[
  {
    "actionUid": "astro.testAction",
    "label": "test",
    "description": "Test action",
    "inputs": [
      {
        "name": "booleanParam1",
        "type": "boolean",
        "label": "Paramêtre boolean",
        "description": "Un paramêtre booléen",
        "required": true,
        "tags": [],
        "reference": "",
        "defaultValue": ""
      },
      {
        "name": "booleanParam2",
        "type": "java.lang.Boolean",
        "label": "Boolean parameter 2",
        "description": "Descr Boolean parameter 1",
        "required": false,
        "tags": [],
        "reference": "",
        "defaultValue": ""
      },
      {
        "name": "booleanParam3",
        "type": "java.lang.Boolean",
        "label": "Boolean parameter 3",
        "description": "Descr Boolean parameter 2",
        "required": true,
        "tags": [],
        "reference": "",
        "defaultValue": ""
      },
      {
        "name": "intParam",
        "type": "int",
        "label": "int parameter",
        "description": "Descr int parameter",
        "required": true,
        "tags": [],
        "reference": "",
        "defaultValue": ""
      },
      {
        "name": "integerParam",
        "type": "java.lang.Integer",
        "label": "Integer parameter",
        "description": "Descr Integer parameter",
        "required": false,
        "tags": [],
        "reference": "",
        "defaultValue": ""
      },
      {
        "name": "longParam1",
        "type": "long",
        "label": "long parameter",
        "description": "Descr long parameter",
        "required": true,
        "tags": [],
        "reference": "",
        "defaultValue": ""
      },
      {
        "name": "longParam2",
        "type": "java.lang.Long",
        "label": "Long parameter",
        "description": "Descr Long parameter",
        "required": false,
        "tags": [],
        "reference": "",
        "defaultValue": ""
      },
      {
        "name": "doubleParam1",
        "type": "double",
        "label": "double parameter",
        "description": "Descr double parameter",
        "required": true,
        "tags": [],
        "reference": "",
        "defaultValue": ""
      },
      {
        "name": "doubleParam2",
        "type": "java.lang.Double",
        "label": "Double parameter",
        "description": "Descr Double parameter",
        "required": false,
        "tags": [],
        "reference": "",
        "defaultValue": ""
      },
      {
        "name": "numberParam",
        "type": "java.lang.Number",
        "label": "Number parameter",
        "description": "Descr Number parameter",
        "required": false,
        "tags": [],
        "reference": "",
        "defaultValue": ""
      },
      {
        "name": "stringParam",
        "type": "java.lang.String",
        "label": "String parameter",
        "description": "Descr String parameter",
        "required": false,
        "tags": [],
        "reference": "",
        "defaultValue": ""
      },
      {
        "name": "decimalTypeParam",
        "type": "org.openhab.core.library.types.DecimalType",
        "label": "DecimalType parameter",
        "description": "Descr DecimalType parameter",
        "required": false,
        "tags": [],
        "reference": "",
        "defaultValue": ""
      },
      {
        "name": "localDateParam",
        "type": "java.time.LocalDate",
        "label": "LocalDate parameter",
        "description": "Descr LocalDate parameter",
        "required": false,
        "tags": [],
        "reference": "",
        "defaultValue": ""
      },
      {
        "name": "localTimeParam",
        "type": "java.time.LocalTime",
        "label": "LocalTime parameter",
        "description": "Descr LocalTime parameter",
        "required": false,
        "tags": [],
        "reference": "",
        "defaultValue": ""
      },
      {
        "name": "localDateTimeParam",
        "type": "java.time.LocalDateTime",
        "label": "LocalDateTime parameter",
        "description": "Descr LocalDateTime parameter",
        "required": false,
        "tags": [],
        "reference": "",
        "defaultValue": ""
      },
      {
        "name": "zonedDateTimeParam",
        "type": "java.time.ZonedDateTime",
        "label": "ZonedDateTime parameter",
        "description": "Descr ZonedDateTime parameter",
        "required": false,
        "tags": [],
        "reference": "",
        "defaultValue": ""
      },
      {
        "name": "dateParam",
        "type": "java.util.Date",
        "label": "Date parameter",
        "description": "Descr Date parameter",
        "required": false,
        "tags": [],
        "reference": "",
        "defaultValue": ""
      },
      {
        "name": "instantParam",
        "type": "java.time.Instant",
        "label": "Instant parameter",
        "description": "Descr Instant parameter",
        "required": false,
        "tags": [],
        "reference": "",
        "defaultValue": ""
      },
      {
        "name": "durationParam",
        "type": "java.time.Duration",
        "label": "Duration parameter",
        "description": "Descr Duration parameter",
        "required": false,
        "tags": [],
        "reference": "",
        "defaultValue": ""
      },
      {
        "name": "quantityTemperatureParam",
        "type": "QuantityType<Temperature>",
        "label": "QuantityType<Temperature> parameter",
        "description": "Descr QuantityType<Temperature> parameter",
        "required": false,
        "tags": [],
        "reference": "",
        "defaultValue": ""
      },
      {
        "name": "quantityPowerParam",
        "type": "org.openhab.core.library.types.QuantityType<javax.measure.quantity.Power>",
        "label": "QuantityType<Power> parameter",
        "description": "Descr QuantityType<Power> parameter",
        "required": false,
        "tags": [],
        "reference": "",
        "defaultValue": ""
      }
    ],
    "inputConfigDescriptions": [
      {
        "default": "false",
        "description": "Un paramêtre booléen",
        "label": "Paramêtre boolean",
        "name": "booleanParam1",
        "required": true,
        "type": "BOOLEAN",
        "readOnly": false,
        "multiple": false,
        "advanced": false,
        "verify": false,
        "limitToOptions": true,
        "options": [],
        "filterCriteria": []
      },
      {
        "description": "Descr Boolean parameter 1",
        "label": "Boolean parameter 2",
        "name": "booleanParam2",
        "required": false,
        "type": "BOOLEAN",
        "readOnly": false,
        "multiple": false,
        "advanced": false,
        "verify": false,
        "limitToOptions": true,
        "options": [],
        "filterCriteria": []
      },
      {
        "description": "Descr Boolean parameter 2",
        "label": "Boolean parameter 3",
        "name": "booleanParam3",
        "required": true,
        "type": "BOOLEAN",
        "readOnly": false,
        "multiple": false,
        "advanced": false,
        "verify": false,
        "limitToOptions": true,
        "options": [],
        "filterCriteria": []
      },
      {
        "default": "0",
        "description": "Descr int parameter",
        "label": "int parameter",
        "name": "intParam",
        "required": true,
        "type": "INTEGER",
        "readOnly": false,
        "multiple": false,
        "advanced": false,
        "verify": false,
        "limitToOptions": true,
        "options": [],
        "filterCriteria": []
      },
      {
        "description": "Descr Integer parameter",
        "label": "Integer parameter",
        "name": "integerParam",
        "required": false,
        "type": "INTEGER",
        "readOnly": false,
        "multiple": false,
        "advanced": false,
        "verify": false,
        "limitToOptions": true,
        "options": [],
        "filterCriteria": []
      },
      {
        "default": "0",
        "description": "Descr long parameter",
        "label": "long parameter",
        "name": "longParam1",
        "required": true,
        "type": "INTEGER",
        "readOnly": false,
        "multiple": false,
        "advanced": false,
        "verify": false,
        "limitToOptions": true,
        "options": [],
        "filterCriteria": []
      },
      {
        "description": "Descr Long parameter",
        "label": "Long parameter",
        "name": "longParam2",
        "required": false,
        "type": "INTEGER",
        "readOnly": false,
        "multiple": false,
        "advanced": false,
        "verify": false,
        "limitToOptions": true,
        "options": [],
        "filterCriteria": []
      },
      {
        "default": "0",
        "description": "Descr double parameter",
        "label": "double parameter",
        "name": "doubleParam1",
        "required": true,
        "type": "DECIMAL",
        "readOnly": false,
        "multiple": false,
        "advanced": false,
        "verify": false,
        "limitToOptions": true,
        "options": [],
        "filterCriteria": []
      },
      {
        "description": "Descr Double parameter",
        "label": "Double parameter",
        "name": "doubleParam2",
        "required": false,
        "type": "DECIMAL",
        "readOnly": false,
        "multiple": false,
        "advanced": false,
        "verify": false,
        "limitToOptions": true,
        "options": [],
        "filterCriteria": []
      },
      {
        "description": "Descr Number parameter",
        "label": "Number parameter",
        "name": "numberParam",
        "required": false,
        "type": "DECIMAL",
        "readOnly": false,
        "multiple": false,
        "advanced": false,
        "verify": false,
        "limitToOptions": true,
        "options": [],
        "filterCriteria": []
      },
      {
        "description": "Descr String parameter",
        "label": "String parameter",
        "name": "stringParam",
        "required": false,
        "type": "TEXT",
        "readOnly": false,
        "multiple": false,
        "advanced": false,
        "verify": false,
        "limitToOptions": true,
        "options": [],
        "filterCriteria": []
      },
      {
        "description": "Descr DecimalType parameter",
        "label": "DecimalType parameter",
        "name": "decimalTypeParam",
        "required": false,
        "type": "DECIMAL",
        "readOnly": false,
        "multiple": false,
        "advanced": false,
        "verify": false,
        "limitToOptions": true,
        "options": [],
        "filterCriteria": []
      },
      {
        "context": "date",
        "description": "Descr LocalDate parameter",
        "label": "LocalDate parameter",
        "name": "localDateParam",
        "required": false,
        "type": "TEXT",
        "readOnly": false,
        "multiple": false,
        "advanced": false,
        "verify": false,
        "limitToOptions": true,
        "options": [],
        "filterCriteria": []
      },
      {
        "context": "time",
        "description": "Descr LocalTime parameter",
        "label": "LocalTime parameter",
        "name": "localTimeParam",
        "required": false,
        "type": "TEXT",
        "readOnly": false,
        "multiple": false,
        "advanced": false,
        "verify": false,
        "limitToOptions": true,
        "options": [],
        "filterCriteria": []
      },
      {
        "context": "datetime",
        "description": "Descr LocalDateTime parameter",
        "label": "LocalDateTime parameter",
        "name": "localDateTimeParam",
        "required": false,
        "type": "TEXT",
        "readOnly": false,
        "multiple": false,
        "advanced": false,
        "verify": false,
        "limitToOptions": true,
        "options": [],
        "filterCriteria": []
      },
      {
        "description": "Descr ZonedDateTime parameter",
        "label": "ZonedDateTime parameter",
        "name": "zonedDateTimeParam",
        "required": false,
        "type": "TEXT",
        "readOnly": false,
        "multiple": false,
        "advanced": false,
        "verify": false,
        "limitToOptions": true,
        "options": [],
        "filterCriteria": []
      },
      {
        "context": "datetime",
        "description": "Descr Date parameter",
        "label": "Date parameter",
        "name": "dateParam",
        "required": false,
        "type": "TEXT",
        "readOnly": false,
        "multiple": false,
        "advanced": false,
        "verify": false,
        "limitToOptions": true,
        "options": [],
        "filterCriteria": []
      },
      {
        "description": "Descr Instant parameter",
        "label": "Instant parameter",
        "name": "instantParam",
        "required": false,
        "type": "TEXT",
        "readOnly": false,
        "multiple": false,
        "advanced": false,
        "verify": false,
        "limitToOptions": true,
        "options": [],
        "filterCriteria": []
      },
      {
        "description": "Descr Duration parameter",
        "label": "Duration parameter",
        "name": "durationParam",
        "required": false,
        "type": "TEXT",
        "readOnly": false,
        "multiple": false,
        "advanced": false,
        "verify": false,
        "limitToOptions": true,
        "options": [],
        "filterCriteria": []
      },
      {
        "description": "Descr QuantityType<Temperature> parameter",
        "label": "QuantityType<Temperature> parameter",
        "name": "quantityTemperatureParam",
        "required": false,
        "type": "DECIMAL",
        "readOnly": false,
        "multiple": false,
        "advanced": false,
        "verify": false,
        "limitToOptions": true,
        "unit": "℃",
        "options": [],
        "filterCriteria": []
      },
      {
        "description": "Descr QuantityType<Power> parameter",
        "label": "QuantityType<Power> parameter",
        "name": "quantityPowerParam",
        "required": false,
        "type": "DECIMAL",
        "readOnly": false,
        "multiple": false,
        "advanced": false,
        "verify": false,
        "limitToOptions": true,
        "unit": "W",
        "options": [],
        "filterCriteria": []
      }
    ],
    "outputs": []
  }
]

An example with the POST API using these input values in API explorer:

{
         "booleanParam1": true, "booleanParam2": false, "booleanParam3": true,
         "intParam": 128, "integerParam": 256,
         "longParam1": 1000, "longParam2": 123456,
         "doubleParam1": 123.456, "doubleParam2": 456.789, "numberParam": 789.012,
         "stringParam": "Test Value",
         "decimalTypeParam": 10.25, "localDateParam": "2024-08-31", "localTimeParam": "08:30:55",
         "localDateTimeParam": "2024-07-01 20:30:45", "zonedDateTimeParam": "2007-12-03T10:15:30+01:00[Europe/Paris]",
         "dateParam": "2024-11-05 09:45:12", "instantParam": "2017-12-09T20:15:30.00Z",
         "durationParam": "P2DT17H25M30.5S",
         "quantityTemperatureParam": 19.7, "quantityPowerParam": 50.5
}

The logs:

19:25:20.244 [DEBUG] [dule.handler.AnnotationActionHandler] - Calling action method testAction with the following arguments:
19:25:20.245 [DEBUG] [dule.handler.AnnotationActionHandler] -   - Argument 1: type java.lang.Boolean value true
19:25:20.245 [DEBUG] [dule.handler.AnnotationActionHandler] -   - Argument 2: type java.lang.Boolean value false
19:25:20.245 [DEBUG] [dule.handler.AnnotationActionHandler] -   - Argument 3: type java.lang.Boolean value true
19:25:20.245 [DEBUG] [dule.handler.AnnotationActionHandler] -   - Argument 4: type java.lang.Integer value 128
19:25:20.245 [DEBUG] [dule.handler.AnnotationActionHandler] -   - Argument 5: type java.lang.Integer value 256
19:25:20.246 [DEBUG] [dule.handler.AnnotationActionHandler] -   - Argument 6: type java.lang.Long value 1000
19:25:20.246 [DEBUG] [dule.handler.AnnotationActionHandler] -   - Argument 7: type java.lang.Long value 123456
19:25:20.246 [DEBUG] [dule.handler.AnnotationActionHandler] -   - Argument 8: type java.lang.Double value 123.456
19:25:20.246 [DEBUG] [dule.handler.AnnotationActionHandler] -   - Argument 9: type java.lang.Double value 456.789
19:25:20.246 [DEBUG] [dule.handler.AnnotationActionHandler] -   - Argument 10: type java.lang.Double value 789.012
19:25:20.246 [DEBUG] [dule.handler.AnnotationActionHandler] -   - Argument 11: type java.lang.String value Test Value
19:25:20.246 [DEBUG] [dule.handler.AnnotationActionHandler] -   - Argument 12: type org.openhab.core.library.types.DecimalType value 10.25
19:25:20.247 [DEBUG] [dule.handler.AnnotationActionHandler] -   - Argument 13: type java.time.LocalDate value 2024-08-31
19:25:20.247 [DEBUG] [dule.handler.AnnotationActionHandler] -   - Argument 14: type java.time.LocalTime value 08:30:55
19:25:20.247 [DEBUG] [dule.handler.AnnotationActionHandler] -   - Argument 15: type java.time.LocalDateTime value 2024-07-01T20:30:45
19:25:20.247 [DEBUG] [dule.handler.AnnotationActionHandler] -   - Argument 16: type java.time.ZonedDateTime value 2007-12-03T10:15:30+01:00[Europe/Paris]
19:25:20.247 [DEBUG] [dule.handler.AnnotationActionHandler] -   - Argument 17: type java.util.Date value Tue Nov 05 09:45:12 CET 2024
19:25:20.247 [DEBUG] [dule.handler.AnnotationActionHandler] -   - Argument 18: type java.time.Instant value 2017-12-09T20:15:30Z
19:25:20.248 [DEBUG] [dule.handler.AnnotationActionHandler] -   - Argument 19: type java.time.Duration value PT65H25M30.5S
19:25:20.250 [DEBUG] [dule.handler.AnnotationActionHandler] -   - Argument 20: type org.openhab.core.library.types.QuantityType value 19.7 °C
19:25:20.250 [DEBUG] [dule.handler.AnnotationActionHandler] -   - Argument 21: type org.openhab.core.library.types.QuantityType value 50.5 W
19:25:20.251 [INFO ] [g.astro.internal.action.AstroActions] - testAction booleanParam1 = true, booleanParam2 = false, booleanParam3 = true, intParam = 128, integerParam = 256, longParam1 = 1000, longParam2 = 123456, doubleParam1 = 123.456, doubleParam2 = 456.789, numberParam = 789.012, stringParam = Test Value, decimalTypeParam = 10.25, localDateParam = 2024-08-31, localTimeParam = 08:30:55, localDateTimeParam = 2024-07-01T20:30:45, zonedDateTimeParam = 2007-12-03T10:15:30+01:00[Europe/Paris], dateParam = Tue Nov 05 09:45:12 CET 2024, instantParam = 2017-12-09T20:15:30Z, durationParam = PT65H25M30.5S, quantityTemperatureParam = 19.7 °C, quantityPowerParam = 50.5 W

And the returned response body:

{
  "result": 256
}

@lolodomo lolodomo changed the title [WIP] Enhance API GET /actions/{thingUID} to return input params as config … Enhance actions REST API Sep 29, 2024
@florian-h05
Copy link
Contributor

florian-h05 commented Sep 30, 2024

@lolodomo The /rest/module-types endpoint also needs to provide these config descriptions for Thing action modules, so Thing actions can be invoked from rules.

@lolodomo
Copy link
Contributor Author

I don't understand, Thing actions can already be invoked from rules. ???

@florian-h05
Copy link
Contributor

After reading #1745 completely, yes they could in theory - the problem is however the same we attempt to fix with this PR: The Thing action module types only contain the inputs like the /actions endpoint, which makes it impossible for the UI to allow the user to provide the required inputs.

@florian-h05
Copy link
Contributor

@lolodomo Please do not force push from now on - I will start working on your PR as well and create a PR to your PR branch afterwards.

@florian-h05
Copy link
Contributor

florian-h05 commented Sep 30, 2024

@lolodomo I have created lolodomo#3, which adds support for invoking Thing actions through UI-based rules. Please merge it with rebase merge to keep commit history.
If you want me to do so I can also take over the remaining work on this topic.

@florian-h05
Copy link
Contributor

florian-h05 commented Oct 1, 2024

Thanks for integrating my changes ... now we have to update the PR description:

Enhance ThingActions UI support

Fixes #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.

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.

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.

@lolodomo
Copy link
Contributor Author

lolodomo commented Oct 1, 2024

When PR is ready for review, I will squash everything, provide a proper description and mention you as co-author.

@lolodomo
Copy link
Contributor Author

lolodomo commented Oct 5, 2024

@florian-h05 : I believe it is almost ready now. Can you please have a look to my last commits before I squash everything and update the 2 first messages ?

@lolodomo
Copy link
Contributor Author

lolodomo commented Oct 5, 2024

I am tempted to change the expected format for "datetime" context.
Documentation mentions "YYYY-MM-DD hh:mm". I would prefer "YYYY-MM-DD hh:mm:ss".
https://www.openhab.org/docs/developer/addons/config-xml.html#supported-context
As the "datetime"context is not yet used, this would not be a problem. WDYT ?

@florian-h05
Copy link
Contributor

Can you please have a look to my last commits before I squash everything and update the 2 first messages ?

I will do.

I am tempted to change the expected format for "datetime" context.

Agreed, having seconds is better than not having seconds - and as it is not yet used, there should be no problems.

@lolodomo
Copy link
Contributor Author

lolodomo commented Oct 5, 2024

Agreed, having seconds is better than not having seconds - and as it is not yet used, there should be no problems.

Done.

I also updated my second message with a full example when using GET and POST API.

@lolodomo lolodomo changed the title Enhance actions REST API Enhance ThingActions UI support Oct 5, 2024
Copy link
Contributor

@florian-h05 florian-h05 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks for your work as well as improving what I have contributed!

Please update the PR title and description, I have already proposed a new one in #4392 (comment),
and add a joint sign-off.

Just two comments:

*
* @return true if the unit of measurements of this parameter is amongst a list of predefined values or false if not
*/
public boolean isUnitRestricted() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For what is that unit restriction required?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe it is to check that a developer did not enter an invalid unit in a binding XML file. The XSD probably defines a particular list of accepted values for the unit attribute.

But I agree that removing this check would be another option.

Comment on lines +74 to +78
// fallback to configuration as this is where the UI stores the input values
if (value == null) {
value = SerialisedInputsToActionInputs.map(moduleType, moduleType.getInputs().get(i),
module.getConfiguration().get(inputAnnotation.name()), null);
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@florian-h05 : you added these lines but to be honnest with you I don't understand in which conditions this is used. As unit provider is not provided in this case, I would like to understand the potential impact.

PS: I am going to add a log to see if this is used when I use the REST API but in principle it should not be required for this usage.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UnitProvider should be used in this case as well.
This change is required to make Thing actions usable in UI-based rules.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I used null, that means UnitProvider was not available...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you change that? Or have you already changed it? I cannot have a look at the code as I’m currently away from keyboard …

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not yet.
I have to think how I could retrieve this service in that class...

Copy link
Contributor Author

@lolodomo lolodomo Oct 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a problem only for QuantityType inputs, you loose the ability to provide a simple number.

By the way, I have an improvement to apply, unitProvider is not required in case the input is a string containing the number AND a unit.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current workaround in Main UI is that you create a string containing the value and the unit when calling the action, instead of just providing a number.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current workaround in Main UI is that you create a string containing the value and the unit when calling the action, instead of just providing a number.

In that case let’s keep this PR as is and we will do a follow-up PR where we will introduce „real“ QuantityType input support in the UI.

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]>
@lolodomo lolodomo marked this pull request as ready for review October 6, 2024 08:24
@lolodomo
Copy link
Contributor Author

lolodomo commented Oct 6, 2024

Now ready for an "official" review.

Also factorize the log of conversion failing.

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

Successfully merging this pull request may close these issues.

[automation] Binding actions cannot be configured by UIs
2 participants