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 force-pushed the enriched_actions_api branch from 7bf450e to 2ca7f24 Compare September 29, 2024 09:22
@lolodomo lolodomo changed the title [WIP] Enhance API GET /actions/{thingUID} to return input params as config … Enhance actions REST API Sep 29, 2024
@lolodomo lolodomo force-pushed the enriched_actions_api branch from 2ca7f24 to 9e04605 Compare September 29, 2024 09:29
@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.

@lolodomo lolodomo force-pushed the enriched_actions_api branch from f582e92 to f1e4101 Compare September 30, 2024 19:29
@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 lolodomo force-pushed the enriched_actions_api branch from 47e8144 to c6d8c3c Compare October 3, 2024 06:35
@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>
@lolodomo lolodomo force-pushed the enriched_actions_api branch from 95ddb01 to b308cd7 Compare October 6, 2024 08:22
@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>
@Nadahar
Copy link

Nadahar commented Nov 24, 2024

I don't know if this is the right place to ask, but it's somewhat on topic: I'm working on a binding where one of the Actions is to set a new API key in the device. It enables encryption and, in short, you don't want to make a mistake when setting it. Thus, I have a need for a longer/more elaborate description for this Action. I erroneously thought that I could use the same limited set of HTML tags in the Action description as is possible in Configuration descriptions (I need some bold text and a couple of line breaks). But, it doesn't work as of now - the tags are just printed out, in MainUI at least. Is this intentional or is the "rendering" just not implemented yet?

If it's the latter, I prefer to just leave the description as it is, and it will start to work properly later. If it's not planned, I'll just have to remove the formatting.

A nice bonus would also be support for a verify property for actions, like exists for configuration paramters. This would be a prime candidate for an action where you'd need a "confirm dialog".

@florian-h05
Copy link
Contributor

The action description itself does not support HTML tags, but for parameter descriptions HTML can be used as for config descriptions (the parameter input is the same component as all other config input in the UI).

A nice bonus would also be support for a verify property for actions, like exists for configuration paramters. This would be a prime candidate for an action where you'd need a "confirm dialog".

@lolodomo WDYT?

@lolodomo
Copy link
Contributor Author

Sorry, I am not aware of this existing verification feature. So feel free to do what you think is good.

@florian-h05
Copy link
Contributor

florian-h05 commented Nov 30, 2024

@Nadahar

A nice bonus would also be support for a verify property for actions, like exists for configuration parameters. This would be a prime candidate for an action where you'd need a "confirm dialog".

What is your exact use case for that? I don't see a general use for this.

@Nadahar
Copy link

Nadahar commented Nov 30, 2024

What is your exact use case for that? I don't see a general use for this.

The thing I would want it for is for an Action that sets the API-key in the device. This should NOT be done by mistake, as you need to factory reset the device if you lost control over the key. Now that Actions are available from the UI, they are more accessible, which is good, but it also makes it easier to do something like this without intending to. I think that a confirmation dialog would be helpful to make sure that the user really intends to set a new API key in the device.

@lolodomo
Copy link
Contributor Author

I don't think we should implement actions that change thing configuration. It is not the purpose of a thing action.

@florian-h05
Copy link
Contributor

The user already needs to select the action from a list, which will open a popup where the user has to enter the action inputs and can see the action output. To execute the action, the user needs to click on execute action in that popup. I don’t think we need another dialog there to avoid accidentally invoking a Thing action.

@Nadahar
Copy link

Nadahar commented Nov 30, 2024

I don't think we should implement actions that change thing configuration. It is not the purpose of a thing action.

I had a rather long discussion with others on the forum about where to place this (channel, configuration, action), and the conclusion was that an action was "the best fit".

@florian-h05
Copy link
Contributor

@lolodomo:
In my opinion the change API key inside device Thing actions is type of comparable to the pairing mode Thing action for Matter devices, I don’t see a general reason to avoid having such actions.

@lolodomo
Copy link
Contributor Author

I had a rather long discussion with others on the forum about where to place this (channel, configuration, action), and the conclusion was that an action was "the best fit".

I am afraid you were badly adviced.
Please link the discussion so that we can read it and understand exactly your need.

@Nadahar
Copy link

Nadahar commented Nov 30, 2024

The user already needs to select the action from a list, which will open a popup where the user has to enter the action inputs and can see the action output. To execute the action, the user needs to click on execute action in that popup. I don’t think we need another dialog there to avoid accidentally invoking a Thing action.

It was just a suggestion. In this case, doing a factory reset will result in losing all your device configuration and that you have to go through configuring the Wi-Fi of the device again, which includes setting it up as an AP, connecting to it, several reboots etc., so I just want to do whatever I can to make sure nobody does this when they don't intend to.

You do get a dialog when clicking the Action, but if you don't read the text closely because you think you have clicked a different action, so I would think that and extra "Are you sure?" would be helpful for "potentially destructive actions" like this.

@lolodomo
Copy link
Contributor Author

In my opinion the change API key inside device Thing actions is type of comparable to the pairing mode Thing action for Matter devices, I don’t see a general reason to avoid having such actions.

In case of Matter, the action is setting up nothing, it just creates a new pairing code.

@Nadahar
Copy link

Nadahar commented Nov 30, 2024

I am afraid you were badly adviced.
Please link the discussion so that we can read it and understand exactly your need.

The discussion isn't just about that, so it's quite a lot to read, but it kinda starts here:
https://community.openhab.org/t/millheat-local-api-development-questions/160139/18?u=nadahar

That said, one of the things that made me use Action for this was that it doesn't have the problem of being persisted. Both channels and configuration has the "problem" that I can't know that the input actually comes from the user, it could also come from storage. As there's no way to "read" the API key, I can't compare the value to see if it's different from the current one. With an action, at least I know that there's a user action on the other side of this. After setting the key, even if it's the same as the existing key, the device must be rebooted - so it's not something that "doesn't matter" if you just set the existing key.

@florian-h05
Copy link
Contributor

You do get a dialog when clicking the Action, but if you don't read the text closely because you think you have clicked a different action, so I would think that and extra "Are you sure?" would be helpful for "potentially destructive actions" like this.

If you really want a confirmation no one just clicks away, I would propose to add a text input where the user has to type something predefined (and explained in the input description) to confirm that the user wants to execute the action.
This is for example how GitHub asks for confirmation when changing something in the danger area of the repository settings: you need to type the repository name to confirm.

@lolodomo You have a good point with your last argument, anyway I don’t want to decide here if Thing actions should do that.

@Nadahar
Copy link

Nadahar commented Nov 30, 2024

If you really want a confirmation no one just clicks away, I would propose to add a text input where the user has to type something predefined (and explained in the input description) to confirm that the user wants to execute the action.
This is for example how GitHub asks for confirmation when changing something in the danger area of the repository settings: you need to type the repository name to confirm.

Yes, it has crossed my mind to do something like that. It's probably what I'll have to do, I was just explaining why I "wished" for a confirmation dialog.

@Nadahar
Copy link

Nadahar commented Nov 30, 2024

Just to elaborate, I agree that Actions aren't "ideal" for configuring devices. But, I don't think any of the available "means" are. I guess you could say that Channels is what is the intended way to do this, and for many things that is fine. But, when it comes to "one time setup" type of configuration options, I don't think they fit that well. The user will have to create and link an Item, just to be able to set it this one time. Also, I have situations where there are "more complex" configurations that require you to send e.g 5 parameters together when sending it to device, or the device will reject it. Doing that with Channels and Things quickly gets "complicated", trying to give the user feedback that it must configure and set all of them before it will work.

The persisting of Channel and configuration values are also a problem when it comes to device configuration. It's not easy to know where the "change" originates, and if it indeed is a real change. It's also confusing to users if a new value seems to have been set, but in reality something went wrong but OH persisted the new value.

I suggested on the forum that there should be a "new type" of configuration parameter, that isn't persisted. They would be populated with information from the device, and any changes would be persisted in the device. But, this suggestion was quickly shot down.

Regardless, the binding has to work the way OH is now, now how it might be in the future, so for now, I think an Action is what fits best.

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
4 participants