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

Merged
merged 21 commits into from
Oct 20, 2024
Merged

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 lg.hc@free.fr
Signed-off-by: Florian Hotze florianh_dev@icloud.com

@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:

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 <lg.hc@free.fr>
Signed-off-by: Florian Hotze <florianh_dev@icloud.com>
Copy link
Member

@J-N-K J-N-K left a comment

Choose a reason for hiding this comment

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

Code looks good!

Signed-off-by: Laurent Garnier <lg.hc@free.fr>
@lolodomo
Copy link
Contributor Author

@J-N-K : I added unit tests but I will continue later to cover more cases.

Signed-off-by: Laurent Garnier <lg.hc@free.fr>
@lolodomo
Copy link
Contributor Author

@J-N-K : unit tests are now fully defined for class ActionInputHelper.

@lolodomo
Copy link
Contributor Author

lolodomo commented Oct 19, 2024

@florian-h05 : I hope you are in the starting blocks ;)

Signed-off-by: Laurent Garnier <lg.hc@free.fr>
Copy link
Member

@J-N-K J-N-K left a comment

Choose a reason for hiding this comment

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

Just some minor comments. Nice test coverage.


@Test
public void testMapActionInputToConfigDescriptionParameterWhenBoolean() {
checkParamter(helper.mapActionInputToConfigDescriptionParameter(buildInput("java.lang.Boolean")),
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
checkParamter(helper.mapActionInputToConfigDescriptionParameter(buildInput("java.lang.Boolean")),
checkParameter(helper.mapActionInputToConfigDescriptionParameter(buildInput("java.lang.Boolean")),

typo

Copy link
Member

Choose a reason for hiding this comment

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

In general: This looks like a perfect example for a ParametrizedTest:

@MethodSource
@ParameterizedTest
public void testMapToEntityIsComplete(Class<?> originalType, Class<?> mappedType, int allowedDelta) {
Class<?> clazz = originalType;
int originalTypeFieldCount = 0;
while (clazz != Object.class) {
originalTypeFieldCount += clazz.getDeclaredFields().length;
clazz = clazz.getSuperclass();
}
int mappedEntityFieldCount = mappedType.getDeclaredFields().length;
assertThat(originalType.getName() + " not properly mapped", mappedEntityFieldCount,
is(originalTypeFieldCount + allowedDelta));
}
private static Stream<Arguments> testMapToEntityIsComplete() {
return Stream.of( //
// isBridge is an extra field for storage and not present in ThingType
Arguments.of(ThingType.class, AbstractStorageBasedTypeProvider.ThingTypeEntity.class, 1),
Arguments.of(ChannelType.class, AbstractStorageBasedTypeProvider.ChannelTypeEntity.class, 0),
Arguments.of(ChannelDefinition.class, AbstractStorageBasedTypeProvider.ChannelDefinitionEntity.class,
0),
// configDescriptionURI is not available for ChannelGroupType
Arguments.of(ChannelGroupType.class, AbstractStorageBasedTypeProvider.ChannelGroupTypeEntity.class, -1),
Arguments.of(ChannelGroupDefinition.class,
AbstractStorageBasedTypeProvider.ChannelGroupDefinitionEntity.class, 0),
Arguments.of(StateDescriptionFragmentImpl.class,
AbstractStorageBasedTypeProvider.StateDescriptionFragmentEntity.class, 0));
}

If you prefer keeping it the way it is now, that is also fine. Just let me know.

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 already spend too much of my free time on these unit tests!

Signed-off-by: Laurent Garnier <lg.hc@free.fr>
Signed-off-by: Laurent Garnier <lg.hc@free.fr>
Copy link
Member

@J-N-K J-N-K left a comment

Choose a reason for hiding this comment

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

Thanks!

@J-N-K J-N-K added the enhancement An enhancement or new feature of the Core label Oct 20, 2024
@J-N-K J-N-K added this to the 4.3 milestone Oct 20, 2024
@J-N-K J-N-K merged commit d431013 into openhab:main Oct 20, 2024
5 checks passed
@lolodomo lolodomo deleted the enriched_actions_api branch October 20, 2024 10:41
florian-h05 added a commit to florian-h05/openhab-webui that referenced this pull request Oct 21, 2024
…utput

Refs openhab/openhab-core#4392.
Closes openhab#2817.

This adds a new section "Actions" to the Thing tab of the Thing page,
which provides a button for each UI-supported Thing action.
Clicking on that button will open a popup,
where action input can be configured and action output can be viewed.

Currently, action output is displayed for actions with a single output,
as well as for the `result`, `qrPairingCode` and `manualPairingCode` keys of the response object.

Signed-off-by: Florian Hotze <dev@florianhotze.com>
florian-h05 added a commit to florian-h05/openhab-webui that referenced this pull request Oct 21, 2024
…utput

Refs openhab/openhab-core#4392.
Closes openhab#2817.

This adds a new section "Actions" to the Thing tab of the Thing page,
which provides a button for each UI-supported Thing action.
Clicking on that button will open a popup,
where action input can be configured and action output can be viewed.

Currently, action output is displayed pretty for the `result`, `qrPairingCode` and `manualPairingCode` keys of the response object.
In addition to that, the raw output can be viewed.

Signed-off-by: Florian Hotze <dev@florianhotze.com>
florian-h05 added a commit to openhab/openhab-webui that referenced this pull request Oct 22, 2024
)

Refs openhab/openhab-core#4392. 
Closes #2817.

This adds a new section "Actions" to the Thing tab of the Thing page,
which provides a button for each UI-supported Thing action.
Clicking on that button will open a popup, where action input can be
configured and action output can be viewed.

All keys of the action output response object from REST are rendered as
list Items, the labels are taken from the action output definitions and fallback to the key.
If the key is `qrCode` or its output type is defined as `qrCode`, its value is rendered as QR code.

For actions without inputs or without outputs, messages are shown indicating that there is no such.

---------

Signed-off-by: Florian Hotze <dev@florianhotze.com>
florian-h05 added a commit to florian-h05/openhab-core that referenced this pull request Oct 26, 2024
…te context docs

This changes the default format for the datetime context to the ISO standard.
This context is not used by add-ons and supported by the UI, so it should be possible to change it (again after openhab#4392.)

Also update the context docs from https://next.openhab.org/docs/developer/addons/config-xml.html#supported-contexts and the UI code.

Signed-off-by: Florian Hotze <dev@florianhotze.com>
J-N-K pushed a commit that referenced this pull request Oct 26, 2024
…te context docs (#4428)

* ConfigDescriptionParameter: Change default format for datetime & Update context docs

This changes the default format for the datetime context to the ISO standard.
This context is not used by add-ons and supported by the UI, so it should be possible to change it (again after #4392.)

Also update the context docs from https://next.openhab.org/docs/developer/addons/config-xml.html#supported-contexts and the UI code.

Signed-off-by: Florian Hotze <dev@florianhotze.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement An enhancement or new feature of the Core
Projects
None yet
Development

Successfully merging this pull request may close these issues.

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