]> git.basschouten.com Git - openhab-addons.git/commitdiff
[openwebnet] added support for CEN/CEN+ scenarios (WHO=15/25) (#11398)
authorM Valla <12682715+mvalla@users.noreply.github.com>
Sat, 30 Oct 2021 16:10:03 +0000 (18:10 +0200)
committerGitHub <noreply@github.com>
Sat, 30 Oct 2021 16:10:03 +0000 (18:10 +0200)
* [openwebnet] first support for CEN

Signed-off-by: Massimo Valla <mvcode00@gmail.com>
* [openwebnet] added CEN actions. OpenWebNetThingHandler.send() is now public

Signed-off-by: Massimo Valla <mvcode00@gmail.com>
* [openwebnet] added CEN+ support

Signed-off-by: Massimo Valla <mvcode00@gmail.com>
* [openwebnet] use WhereCEN, removed nullpointer warnings from EnergyHandler. Improved README

Signed-off-by: Massimo Valla <mvcode00@gmail.com>
* [openwebnet] completed support for CEN/CEN+

Signed-off-by: Massimo Valla <mvcode00@gmail.com>
* [openwebnet] improved log

Signed-off-by: Massimo Valla <mvcode00@gmail.com>
* [openwebnet] corrected "pressure" and renamed some labels

Signed-off-by: Massimo Valla <mvcode00@gmail.com>
23 files changed:
bundles/org.openhab.binding.openwebnet/README.md
bundles/org.openhab.binding.openwebnet/pom.xml
bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/OpenWebNetBindingConstants.java
bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/OpenWebNetHandlerFactory.java
bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/actions/OpenWebNetCENActions.java [new file with mode: 0644]
bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/discovery/OpenWebNetDeviceDiscoveryService.java
bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/handler/OpenWebNetBridgeHandler.java
bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/handler/OpenWebNetEnergyHandler.java
bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/handler/OpenWebNetScenarioHandler.java [new file with mode: 0644]
bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/handler/OpenWebNetThingHandler.java
bundles/org.openhab.binding.openwebnet/src/main/resources/OH-INF/thing/BusAutomation.xml
bundles/org.openhab.binding.openwebnet/src/main/resources/OH-INF/thing/BusCENPlusScenarioControl.xml [new file with mode: 0644]
bundles/org.openhab.binding.openwebnet/src/main/resources/OH-INF/thing/BusCENScenarioControl.xml [new file with mode: 0644]
bundles/org.openhab.binding.openwebnet/src/main/resources/OH-INF/thing/BusDimmer.xml
bundles/org.openhab.binding.openwebnet/src/main/resources/OH-INF/thing/BusEnergyMeter.xml
bundles/org.openhab.binding.openwebnet/src/main/resources/OH-INF/thing/BusOnOffSwitch.xml
bundles/org.openhab.binding.openwebnet/src/main/resources/OH-INF/thing/BusThermoSensor.xml
bundles/org.openhab.binding.openwebnet/src/main/resources/OH-INF/thing/BusThermoZone.xml
bundles/org.openhab.binding.openwebnet/src/main/resources/OH-INF/thing/ZBAutomation.xml
bundles/org.openhab.binding.openwebnet/src/main/resources/OH-INF/thing/ZBDimmer.xml
bundles/org.openhab.binding.openwebnet/src/main/resources/OH-INF/thing/ZBOnOffSwitch.xml
bundles/org.openhab.binding.openwebnet/src/main/resources/OH-INF/thing/ZBOnOffSwitch2Units.xml
bundles/org.openhab.binding.openwebnet/src/main/resources/OH-INF/thing/channels.xml

index da42c54067f145fc64325e5412025a067778a5b3..89e1b8665e350d5aab94375cafad737d7615d4f5 100644 (file)
@@ -44,6 +44,7 @@ The following Things and OpenWebNet `WHOs` are supported:
 | Lighting             | `1`          | `bus_on_off_switch`, `bus_dimmer`   | BUS switches and dimmers                                         | Successfully tested: F411/2, F411/4, F411U2, F422, F429. Some discovery issues reported with F429 (DALI Dimmers)  |
 | Automation           | `2`          | `bus_automation`                    | BUS roller shutters, with position feedback and auto-calibration | Successfully tested: LN4672M2  |
 | Temperature Control  | `4`          | `bus_thermo_zone`, `bus_thermo_sensor` | Thermo zones management and temperature sensors (probes). NOTE Central Units (4 or 99 zones) are not fully supported yet. See [Channels - Thermo](#configuring-thermo) for more details. | Successfully tested: H/LN4691, HS4692, KG4691; thermo sensors: L/N/NT4577 + 3455 |
+| CEN & CEN+ Scenarios  | `15` & `25`  | `bus_cen_scenario_control`, `bus_cenplus_scenario_control` | CEN/CEN+ scenarios events and virtual activation | Successfully tested: scenario buttons: HC/HD/HS/L/N/NT4680 |
 | Energy Management    | `18`         | `bus_energy_meter`                  | Energy Management                                                | Successfully tested: F520, F521 |
 
 ### For ZigBee (Radio)
@@ -66,6 +67,7 @@ For other gateways you can add them manually, see [Thing Configuration](#thing-c
 - Once the gateway is online, a second Inbox Scan will discover BUS devices
 - BUS/SCS Dimmers must be ON and dimmed (30%-100%) during a Scan, otherwise they will be discovered as simple On/Off switches
     - *KNOWN ISSUE*: In some cases dimmers connected to a F429 Dali-interface are not automatically discovered
+- CEN/CEN+ Scenario Control devices will be discovered by activation only. See [discovery by activation](#discovery-by-activation) for details. After confirming a discovered CEN/CEN+ device from Inbox, activate again its scenario buttons to add button channels automatically
 
 #### Discovery by Activation
 
@@ -117,13 +119,18 @@ Alternatively the ZigBee USB Gateway thing can be configured using the `.things`
 ### Configuring Devices
 
 Devices can be discovered automatically using an Inbox Scan after a gateway has been configured and connected.
+
 For any manually added device, you must configure:
 
 - the associated gateway (`Parent Bridge` menu)
 - the `where` config parameter (`OpenWebNet Device Address`):
-  - example for BUS/SCS device with WHERE address Point to Point `A=2 PL=4` --> `where="24"`
-  - example for BUS/SCS device with WHERE address Point to Point `A=03 PL=11` on local bus --> `where="0311#4#01"`
-  - example for ZigBee devices: `where=765432101#9`. The ID of the device (ADDR part) is usually written in hexadecimal on the device itself, for example `ID 0074CBB1`: convert to decimal (`7654321`) and add `01#9` at the end to obtain `where=765432101#9`. For 2-unit switch devices (`zb_on_off_switch2u`), last part should be `00#9`.
+    - example for BUS/SCS:
+        - light device with WHERE address Point to Point `A=2 PL=4` --> `where="24"`
+        - light device with WHERE address Point to Point `A=03 PL=11` on local bus --> `where="0311#4#01"`
+        - CEN scenario with WHERE address Point to Point `A=05 PL=12` --> `where="0512"`
+        - CEN+ configured scenario `5`: add a `2` before --> `where="25"`
+    - example for ZigBee devices: `where=765432101#9`. The ID of the device (ADDR part) is usually written in hexadecimal on the device itself, for example `ID 0074CBB1`: convert to decimal (`7654321`) and add `01#9` at the end to obtain `where=765432101#9`. For 2-unit switch devices (`zb_on_off_switch2u`), last part should be `00#9`.
 
 #### Configuring Thermo
 
@@ -132,14 +139,14 @@ In BTicino MyHOME Thermoregulation (WHO=4) each **zone** has associated a thermo
 Thermo zones can be configured defining a `bus_thermo_zone` Thing for each zone with the following parameters:
 
 - the `where` config parameter (`OpenWebNet Device Address`):
-  - example BUS/SCS Thermo zone `1` --> `where="1"` 
+    - example BUS/SCS Thermo zone `1` --> `where="1"` 
 - the `standAlone` config parameter (`boolean`, default: `true`): identifies if the zone is managed or not by a Central Unit (4 or 99 zones). `standAlone=true` means no Central Unit is present in the system.
 
 Temperature sensors can be configured defining a `bus_thermo_sensor` Thing with the following parameters:
 
 - the `where` config parameter (`OpenWebNet Device Address`):
-  - example sensor `5` of external zone `00` --> `where="500"`
-  - example: slave sensor `3` of zone `2` --> `where="302"`
+    - example sensor `5` of external zone `00` --> `where="500"`
+    - example: slave sensor `3` of zone `2` --> `where="302"`
 
 #### NOTE
 
@@ -148,13 +155,14 @@ Systems with Central Units (4 or 99 zones) are not fully supported yet.
 
 ## Channels 
 
-### Lighting, Automation and Power meter channels
+### Lighting, Automation, Power meter and CEN/CEN+ Scenario Events channels
 
 | Channel Type ID (channel ID)             | Applies to Thing Type IDs                                     | Item Type     | Description                                           | Read/Write |
 | ---------------------------------------- | ------------------------------------------------------------- | ------------- | ----------------------------------------------------- | :--------: |
 | `switch` or `switch_01`/`02` for ZigBee  | `bus_on_off_switch`, `zb_on_off_switch`, `zb_on_off_switch2u` | Switch        | To switch the device `ON` and `OFF`                   |    R/W     |
 | `brightness`                             | `bus_dimmer`, `zb_dimmer`                                     | Dimmer        | To adjust the brightness value (Percent, `ON`, `OFF`) |    R/W     |
 | `shutter`                                | `bus_automation`                                              | Rollershutter | To activate roller shutters (`UP`, `DOWN`, `STOP`, Percent - [see Shutter position](#shutter-position)) |    R/W     |
+| `button#X`         | `bus_cen_scenario_control`, `bus_cenplus_scenario_control` | String        | Trigger channel for CEN/CEN+ scenario events [see possible values](#cen-cen-channels)  |     R (TRIGGER)      |
 | `power`                                  | `bus_energy_meter`                                            | Number:Power  | The current active power usage from Energy Meter      |     R      |
 
 ### Thermo channels
@@ -183,6 +191,31 @@ It's possible to enter a value manually or set `shutterRun=AUTO` (default) to ca
 - if OH is restarted the binding does not know if a shutter position has changed in the meantime, so its position will be `UNDEF`. Move the shutter all `UP`/`DOWN` to synchronize again its position with the binding
 - the shutter position is estimated based on UP/DOWN timing: an error of ±2% is normal
 
+#### CEN/CEN+ channels
+
+CEN/CEN+ are [TRIGGER channels](https://www.openhab.org/docs/configuration/rules-dsl.html#channel-based-triggers]): they handle events and do not have a state.
+
+A powerful feature is to be able to assign CEN or CEN+ commands to your physical wall switches and use the events they generate to trigger rules in openHAB: this way openHAB becomes a very powerful scenario manager activated by physical BTicino switches.
+See [openwebnet.rules](#openwebnet-rules) for an example on how to define rules that trigger on CEN/CEN+ buttons events.
+
+It's also possible to send *virtual press* events on the BUS, for example to enable the activation of MH202 scenarios from openHAB.
+See [openwebnet.sitemap](#openwebnet-sitemap) & [openwebnet.rules](#openwebnet-rules) sections for an example on how to use the `virtualPress` action connected to a pushbutton on a sitemap.
+
+- channels are named `button#X` where `X` is the button number on the Scenario Control device
+- in the .thing file configuration you can specify the `buttons` parameter to define a comma-separated list of buttons numbers [0-31] configured for the scenario device, example: `buttons=1,2,4`
+- possible events are:
+    - for CEN:
+        - `START_PRESS` - sent when you start pressing the button
+        - `SHORT_PRESS` - sent if you pressed the button shorter than 0,5sec (sent at the moment when you release it)
+        - `EXTENDED_PRESS` - sent if you keep the button pressed longer than 0,5sec; will be sent again every 0,5sec as long as you hold pressed (good for dimming rules)
+        - `RELEASE_EXTENDED_PRESS` - sent once when you finally release the button after having it pressed longer than 0,5sec
+    - for CEN+:
+        - `SHORT_PRESS` - sent if you pressed the button shorter than 0,5sec (sent at the moment when you release it)
+        - `START_EXTENDED_PRESS` - sent once as soon as you keep the button pressed longer than 0,5sec
+        - `EXTENDED_PRESS` - sent after `START_EXTENDED_PRESS` if you keep the button pressed longer; will be sent again every 0,5sec as long as you hold pressed (good for dimming rules)
+        - `RELEASE_EXTENDED_PRESS` - sent once when you finally release the button after having it pressed longer than 0,5sec
+
+
 ## Full Example
 
 ### openwebnet.things:
@@ -191,13 +224,15 @@ BUS gateway and things configuration:
 
 ```
 Bridge openwebnet:bus_gateway:mybridge "MyHOMEServer1" [ host="192.168.1.35", passwd="abcde", port=20000, discoveryByActivation=false ] {
-      bus_on_off_switch        LR_switch        "Living Room Light"      [ where="51" ]
-      bus_dimmer               LR_dimmer        "Living Room Dimmer"     [ where="0311#4#01" ]
-      bus_automation           LR_shutter       "Living Room Shutter"    [ where="93", shutterRun="10050"]      
-      bus_energy_meter         CENTRAL_Ta       "Energy Meter Ta"        [ where="51" ]        
-      bus_energy_meter         CENTRAL_Tb       "Energy Meter Tb"        [ where="52" ]           
-      bus_thermo_zone          LR_zone          "Living Room Zone"       [ where="2"]
-      bus_thermo_sensor        EXT_tempsensor   "External Temperature"   [ where="500"]
+      bus_on_off_switch             LR_switch            "Living Room Light"        [ where="51" ]
+      bus_dimmer                    LR_dimmer            "Living Room Dimmer"       [ where="0311#4#01" ]
+      bus_automation                LR_shutter           "Living Room Shutter"      [ where="93", shutterRun="10050"]      
+      bus_energy_meter              CENTRAL_Ta           "Energy Meter Ta"          [ where="51" ]     
+      bus_energy_meter              CENTRAL_Tb           "Energy Meter Tb"          [ where="52" ]        
+      bus_thermo_zone               LR_zone              "Living Room Zone"         [ where="2"]
+      bus_thermo_sensor             EXT_tempsensor       "External Temperature"     [ where="500"]
+      bus_cen_scenario_control      LR_CEN_scenario      "Living Room CEN"          [ where="51", buttons="4,3,8"]
+      bus_cenplus_scenario_control  LR_CENplus_scenario  "Living Room CEN+"         [ where="212", buttons="1,5,18" ]
 }
 ```
 
@@ -240,6 +275,8 @@ String              iLR_zone_cv                 "Conditioning valves"         (g
 
 Number:Temperature  iEXT_temp                   "Temperature [%.1f %unit%]"   (gExternal) { channel="openwebnet:bus_thermo_sensor:mybridge:EXT_tempsensor:temperature" }
 
+String               iCENPlusProxyItem            "CEN+ Proxy Item"                                    
+
 
 ```
 
@@ -281,9 +318,47 @@ sitemap openwebnet label="OpenWebNet Binding Example Sitemap"
           Default   item=iLR_zone_hv        label="Heating valves status"
           Default   item=iLR_zone_cv        label="Conditioning valves status"
     }
+    
+    Frame label="CEN+ Scenario activation"
+    {
+          Switch    item=iCENPlusProxyItem  label="My CEN+ scenario" icon="movecontrol"  mappings=[ON="Activate"]
+    }
 }
 ```
 
+### openwebnet.rules
+
+```xtend
+rule "CEN+ virtual press from OH button"
+/* This rule triggers when the proxy item iCENPlusProxyItem is activated, for example from a button on WebUI/sitemap.
+When activated it sends a "virtual short press" event (where=212, button=5) on the BUS 
+*/
+when 
+    Item iCENPlusProxyItem received command
+then
+    val actions = getActions("openwebnet","openwebnet:bus_cenplus_scenario_control:mybridge:212")
+    actions.virtualPress("SHORT_PRESS", 5)
+end
+
+
+rule "CEN dimmer increase"
+// A "start press" event on CEN where=51, button=4 will increase dimmer%
+when
+    Channel "openwebnet:bus_cen_scenario_control:mybridge:51:button#4" triggered START_PRESS
+then
+    sendCommand(iLR_dimmer, INCREASE)  
+end
+
+
+rule "CEN dimmer decrease"
+// A "release extended press" event on CEN where=51, button=4 will decrease dimmer%
+when
+    Channel "openwebnet:bus_cen_scenario_control:mybridge:51:button#4" triggered RELEASE_EXTENDED_PRESS
+then
+    sendCommand(iLR_dimmer, DECREASE)  
+end
+```
+
 ## Notes
 
 - The OpenWebNet protocol is maintained and Copyright by BTicino/Legrand. The documentation of the protocol if freely accessible for developers on the [Legrand developer web site](https://developer.legrand.com/documentation/open-web-net-for-myhome/)
index a69d138d689339344906a344ad3865686f36c2bb..a6e25ffac9f501817955cea6caf90e6f829eb634 100644 (file)
@@ -23,7 +23,7 @@
     <dependency>
       <groupId>io.github.openwebnet4j</groupId>
       <artifactId>openwebnet4j</artifactId>
-      <version>0.5.3</version>
+      <version>0.6.0</version>
       <scope>compile</scope>
     </dependency>
 
index 1f039a06faa0ce6e71aa60b72b3c4f0714e91314..218aa20fe996e3e4ccfec8d188c2e3cf102b4df1 100644 (file)
@@ -59,6 +59,12 @@ public class OpenWebNetBindingConstants {
     public static final String THING_LABEL_BUS_THERMO_SENSOR = "Thermo Sensor";
     public static final ThingTypeUID THING_TYPE_BUS_THERMO_ZONE = new ThingTypeUID(BINDING_ID, "bus_thermo_zone");
     public static final String THING_LABEL_BUS_THERMO_ZONE = "Thermo Zone";
+    public static final ThingTypeUID THING_TYPE_BUS_CEN_SCENARIO_CONTROL = new ThingTypeUID(BINDING_ID,
+            "bus_cen_scenario_control");
+    public static final String THING_LABEL_BUS_CEN_SCENARIO_CONTROL = "CEN Control";
+    public static final ThingTypeUID THING_TYPE_BUS_CENPLUS_SCENARIO_CONTROL = new ThingTypeUID(BINDING_ID,
+            "bus_cenplus_scenario_control");
+    public static final String THING_LABEL_BUS_CENPLUS_SCENARIO_CONTROL = "CEN+ Control";
 
     // ZIGBEE
     public static final ThingTypeUID THING_TYPE_ZB_ON_OFF_SWITCH = new ThingTypeUID(BINDING_ID, "zb_on_off_switch");
@@ -81,24 +87,22 @@ public class OpenWebNetBindingConstants {
     // ## Automation
     public static final Set<ThingTypeUID> AUTOMATION_SUPPORTED_THING_TYPES = Set.of(THING_TYPE_ZB_AUTOMATION,
             THING_TYPE_BUS_AUTOMATION);
-
     // ## Thermoregulation
     public static final Set<ThingTypeUID> THERMOREGULATION_SUPPORTED_THING_TYPES = Set.of(THING_TYPE_BUS_THERMO_ZONE,
             THING_TYPE_BUS_THERMO_SENSOR);
-
     // ## Energy Management
     public static final Set<ThingTypeUID> ENERGY_MANAGEMENT_SUPPORTED_THING_TYPES = Set.of(THING_TYPE_BUS_ENERGY_METER);
-
+    // ## CEN/CEN+ Scenario
+    public static final Set<ThingTypeUID> SCENARIO_SUPPORTED_THING_TYPES = Set.of(THING_TYPE_BUS_CEN_SCENARIO_CONTROL,
+            THING_TYPE_BUS_CENPLUS_SCENARIO_CONTROL);
     // ## Groups
     public static final Set<ThingTypeUID> DEVICE_SUPPORTED_THING_TYPES = Stream
             .of(LIGHTING_SUPPORTED_THING_TYPES, AUTOMATION_SUPPORTED_THING_TYPES,
                     THERMOREGULATION_SUPPORTED_THING_TYPES, ENERGY_MANAGEMENT_SUPPORTED_THING_TYPES,
-                    GENERIC_SUPPORTED_THING_TYPES)
+                    SCENARIO_SUPPORTED_THING_TYPES, GENERIC_SUPPORTED_THING_TYPES)
             .flatMap(Collection::stream).collect(Collectors.toCollection(HashSet::new));
-
     public static final Set<ThingTypeUID> BRIDGE_SUPPORTED_THING_TYPES = Set.of(THING_TYPE_ZB_GATEWAY,
             THING_TYPE_BUS_GATEWAY);
-
     public static final Set<ThingTypeUID> ALL_SUPPORTED_THING_TYPES = Stream
             .of(DEVICE_SUPPORTED_THING_TYPES, BRIDGE_SUPPORTED_THING_TYPES).flatMap(Collection::stream)
             .collect(Collectors.toCollection(HashSet::new));
@@ -109,10 +113,8 @@ public class OpenWebNetBindingConstants {
     public static final String CHANNEL_SWITCH_01 = "switch_01";
     public static final String CHANNEL_SWITCH_02 = "switch_02";
     public static final String CHANNEL_BRIGHTNESS = "brightness";
-
     // automation
     public static final String CHANNEL_SHUTTER = "shutter";
-
     // thermo
     public static final String CHANNEL_TEMPERATURE = "temperature";
     public static final String CHANNEL_FUNCTION = "function";
@@ -122,18 +124,20 @@ public class OpenWebNetBindingConstants {
     public static final String CHANNEL_CONDITIONING_VALVES = "conditioningValves";
     public static final String CHANNEL_HEATING_VALVES = "heatingValves";
     public static final String CHANNEL_ACTUATORS = "actuators";
-
     // energy management
     public static final String CHANNEL_POWER = "power";
+    // scenario button channels
+    public static final String CHANNEL_SCENARIO_BUTTON = "button#";
+    public static final String CHANNEL_TYPE_CEN_BUTTON_EVENT = "cenButtonEvent";
+    public static final String CHANNEL_TYPE_CEN_PLUS_BUTTON_EVENT = "cenPlusButtonEvent";
 
     // devices config properties
     public static final String CONFIG_PROPERTY_WHERE = "where";
     public static final String CONFIG_PROPERTY_SHUTTER_RUN = "shutterRun";
-
+    public static final String CONFIG_PROPERTY_SCENARIO_BUTTONS = "buttons";
     // BUS gw config properties
     public static final String CONFIG_PROPERTY_HOST = "host";
     public static final String CONFIG_PROPERTY_SERIAL_PORT = "serialPort";
-
     // properties
     public static final String PROPERTY_OWNID = "ownId";
     public static final String PROPERTY_ZIGBEEID = "zigbeeid";
index 8b258df58c8d341494941770ee12c818375374b6..6a024db6afbaa511e0727f590cf5c9d10685e5f4 100644 (file)
@@ -21,6 +21,7 @@ import org.openhab.binding.openwebnet.internal.handler.OpenWebNetBridgeHandler;
 import org.openhab.binding.openwebnet.internal.handler.OpenWebNetEnergyHandler;
 import org.openhab.binding.openwebnet.internal.handler.OpenWebNetGenericHandler;
 import org.openhab.binding.openwebnet.internal.handler.OpenWebNetLightingHandler;
+import org.openhab.binding.openwebnet.internal.handler.OpenWebNetScenarioHandler;
 import org.openhab.binding.openwebnet.internal.handler.OpenWebNetThermoregulationHandler;
 import org.openhab.core.thing.Bridge;
 import org.openhab.core.thing.Thing;
@@ -70,6 +71,9 @@ public class OpenWebNetHandlerFactory extends BaseThingHandlerFactory {
         } else if (OpenWebNetThermoregulationHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) {
             logger.debug("creating NEW THERMO Handler");
             return new OpenWebNetThermoregulationHandler(thing);
+        } else if (OpenWebNetScenarioHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) {
+            logger.debug("creating NEW SCENARIO Handler");
+            return new OpenWebNetScenarioHandler(thing);
         }
         logger.warn("ThingType {} is not supported by this binding", thing.getThingTypeUID());
         return null;
diff --git a/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/actions/OpenWebNetCENActions.java b/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/actions/OpenWebNetCENActions.java
new file mode 100644 (file)
index 0000000..ce1b5e1
--- /dev/null
@@ -0,0 +1,93 @@
+/**
+ * Copyright (c) 2010-2021 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.openwebnet.internal.actions;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.openwebnet.internal.handler.OpenWebNetScenarioHandler;
+import org.openhab.core.automation.annotation.ActionInput;
+import org.openhab.core.automation.annotation.ActionOutput;
+import org.openhab.core.automation.annotation.RuleAction;
+import org.openhab.core.thing.binding.ThingActions;
+import org.openhab.core.thing.binding.ThingActionsScope;
+import org.openhab.core.thing.binding.ThingHandler;
+import org.openwebnet4j.communication.OWNException;
+import org.openwebnet4j.communication.Response;
+import org.openwebnet4j.message.CEN;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link OpenWebNetCENActions} defines CEN/CEN+ actions for the openwebnet binding.
+ *
+ * @author Massimo Valla - Initial contribution
+ */
+
+@ThingActionsScope(name = "openwebnet")
+@NonNullByDefault
+public class OpenWebNetCENActions implements ThingActions {
+
+    private final Logger logger = LoggerFactory.getLogger(OpenWebNetCENActions.class);
+    private @Nullable OpenWebNetScenarioHandler scenarioHandler;
+
+    @Override
+    public void setThingHandler(@Nullable ThingHandler handler) {
+        this.scenarioHandler = (OpenWebNetScenarioHandler) handler;
+    }
+
+    @Override
+    public @Nullable ThingHandler getThingHandler() {
+        return scenarioHandler;
+    }
+
+    @RuleAction(label = "virtualPress", description = "Virtual press of the push button")
+    public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean virtualPress(
+            @ActionInput(name = "press", label = "press", description = "Type of press") @Nullable String press,
+            @ActionInput(name = "button", label = "button", description = "Button number") int button) {
+        OpenWebNetScenarioHandler handler = scenarioHandler;
+        if (handler == null) {
+            logger.warn("openwebnet OpenWebNetCENActions: scenarioHandler is null!");
+            return false;
+        }
+        if (press == null) {
+            logger.warn("openwebnet OpenWebNetCENActions: press parameter is null!");
+            return false;
+        }
+        CEN msg = null;
+        try {
+            msg = handler.pressStrToMessage(press, button);
+            Response res = handler.send(msg);
+            if (res != null) {
+                logger.debug("Sent virtualPress '{}' to gateway. Response: {}", msg, res.getResponseMessages());
+                return res.isSuccess();
+            } else {
+                logger.debug("virtual press action returned null response");
+            }
+        } catch (IllegalArgumentException e) {
+            logger.warn("cannot execute virtual press action for thing {}: {}", handler.getThing().getUID(),
+                    e.getMessage());
+        } catch (OWNException e) {
+            logger.warn("exception while sending virtual press message '{}' to gateway: {}", msg, e.getMessage());
+        }
+        return false;
+    }
+
+    // legacy delegate methods
+    public static void virtualPress(@Nullable ThingActions actions, @Nullable String press, int button) {
+        if (actions instanceof OpenWebNetCENActions) {
+            ((OpenWebNetCENActions) actions).virtualPress(press, button);
+        } else {
+            throw new IllegalArgumentException("Instance is not an OpenWebNetCENActions class.");
+        }
+    }
+}
index 2a339cca5fea530281d075601eec2b98384ed4f6..e92e684833aaf9d4c266bd36eaa2d37045860908 100644 (file)
@@ -153,6 +153,18 @@ public class OpenWebNetDeviceDiscoveryService extends AbstractDiscoveryService
                 deviceWho = Who.ENERGY_MANAGEMENT;
                 break;
             }
+            case SCENARIO_CONTROL: {
+                thingTypeUID = OpenWebNetBindingConstants.THING_TYPE_BUS_CEN_SCENARIO_CONTROL;
+                thingLabel = OpenWebNetBindingConstants.THING_LABEL_BUS_CEN_SCENARIO_CONTROL;
+                deviceWho = Who.CEN_SCENARIO_SCHEDULER;
+                break;
+            }
+            case MULTIFUNCTION_SCENARIO_CONTROL: {
+                thingTypeUID = OpenWebNetBindingConstants.THING_TYPE_BUS_CENPLUS_SCENARIO_CONTROL;
+                thingLabel = OpenWebNetBindingConstants.THING_LABEL_BUS_CENPLUS_SCENARIO_CONTROL;
+                deviceWho = Who.CEN_PLUS_SCENARIO_SCHEDULER;
+                break;
+            }
             default:
                 logger.warn("Device type {} is not supported, default to GENERIC device (WHERE={})", deviceType, where);
                 if (where instanceof WhereZigBee) {
index 2b9c83fd881727e36d17fb1c764bf9dd874c350e..c2e21d82a377b13ef7ddaf42f175c96a5aa02062 100644 (file)
@@ -48,6 +48,7 @@ import org.openwebnet4j.communication.OWNAuthException;
 import org.openwebnet4j.communication.OWNException;
 import org.openwebnet4j.message.Automation;
 import org.openwebnet4j.message.BaseOpenMessage;
+import org.openwebnet4j.message.CEN;
 import org.openwebnet4j.message.EnergyManagement;
 import org.openwebnet4j.message.FrameException;
 import org.openwebnet4j.message.GatewayMgmt;
@@ -308,7 +309,7 @@ public class OpenWebNetBridgeHandler extends ConfigStatusBridgeHandler implement
         }
         // we support these types only
         if (baseMsg instanceof Lighting || baseMsg instanceof Automation || baseMsg instanceof EnergyManagement
-                || baseMsg instanceof Thermoregulation) {
+                || baseMsg instanceof Thermoregulation || baseMsg instanceof CEN) {
             BaseOpenMessage bmsg = baseMsg;
             if (baseMsg instanceof Lighting) {
                 What what = baseMsg.getWhat();
@@ -419,7 +420,7 @@ public class OpenWebNetBridgeHandler extends ConfigStatusBridgeHandler implement
         BaseOpenMessage baseMsg = (BaseOpenMessage) msg;
         // let's try to get the Thing associated with this message...
         if (baseMsg instanceof Lighting || baseMsg instanceof Automation || baseMsg instanceof EnergyManagement
-                || baseMsg instanceof Thermoregulation) {
+                || baseMsg instanceof Thermoregulation || baseMsg instanceof CEN) {
             String ownId = ownIdFromMessage(baseMsg);
             logger.debug("ownIdFromMessage({}) --> {}", baseMsg, ownId);
             OpenWebNetThingHandler deviceHandler = registeredDevices.get(ownId);
index 386e45126d671ce1da3f5ef4902cae7e25117499..8835ba2cd5b0b272d3d59b1b3f1be55742d55550 100644 (file)
@@ -107,20 +107,23 @@ public class OpenWebNetEnergyHandler extends OpenWebNetThingHandler {
                         "subscribeToActivePowerChanges() Refreshing subscription for the next {}min for WHERE={} to active power changes notification",
                         ENERGY_SUBSCRIPTION_PERIOD, deviceWhere);
             }
-
-            try {
-                bridgeHandler.gateway.send(EnergyManagement.setActivePowerNotificationsTime(deviceWhere.value(),
-                        ENERGY_SUBSCRIPTION_PERIOD));
-                isFirstSchedulerLaunch = false;
-            } catch (Exception e) {
-                if (isFirstSchedulerLaunch) {
-                    logger.warn(
-                            "subscribeToActivePowerChanges() For WHERE={} could not subscribe to active power changes notifications. Exception={}",
-                            deviceWhere, e.getMessage());
-                } else {
-                    logger.warn(
-                            "subscribeToActivePowerChanges() Unable to refresh subscription to active power changes notifications for WHERE={}. Exception={}",
-                            deviceWhere, e.getMessage());
+            Where w = deviceWhere;
+            if (w == null) {
+                logger.warn("subscribeToActivePowerChanges() WHERE=null. Skipping");
+            } else {
+                try {
+                    send(EnergyManagement.setActivePowerNotificationsTime(w.value(), ENERGY_SUBSCRIPTION_PERIOD));
+                    isFirstSchedulerLaunch = false;
+                } catch (Exception e) {
+                    if (isFirstSchedulerLaunch) {
+                        logger.warn(
+                                "subscribeToActivePowerChanges() For WHERE={} could not subscribe to active power changes notifications. Exception={}",
+                                w, e.getMessage());
+                    } else {
+                        logger.warn(
+                                "subscribeToActivePowerChanges() Unable to refresh subscription to active power changes notifications for WHERE={}. Exception={}",
+                                w, e.getMessage());
+                    }
                 }
             }
         }, 0, ENERGY_SUBSCRIPTION_PERIOD - 1, TimeUnit.MINUTES);
@@ -129,9 +132,9 @@ public class OpenWebNetEnergyHandler extends OpenWebNetThingHandler {
     @Override
     public void dispose() {
         if (notificationSchedule != null) {
+            ScheduledFuture<?> ns = notificationSchedule;
+            ns.cancel(false);
             logger.debug("dispose() scheduler stopped.");
-
-            notificationSchedule.cancel(false);
         }
         super.dispose();
     }
diff --git a/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/handler/OpenWebNetScenarioHandler.java b/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/handler/OpenWebNetScenarioHandler.java
new file mode 100644 (file)
index 0000000..3ae1a35
--- /dev/null
@@ -0,0 +1,346 @@
+/**
+ * Copyright (c) 2010-2021 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.openwebnet.internal.handler;
+
+import static org.openhab.binding.openwebnet.internal.OpenWebNetBindingConstants.*;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Optional;
+import java.util.Scanner;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.openwebnet.internal.OpenWebNetBindingConstants;
+import org.openhab.binding.openwebnet.internal.actions.OpenWebNetCENActions;
+import org.openhab.core.thing.Channel;
+import org.openhab.core.thing.ChannelUID;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingTypeUID;
+import org.openhab.core.thing.binding.ThingHandlerService;
+import org.openhab.core.thing.binding.builder.ChannelBuilder;
+import org.openhab.core.thing.binding.builder.ThingBuilder;
+import org.openhab.core.thing.type.ChannelKind;
+import org.openhab.core.thing.type.ChannelTypeUID;
+import org.openhab.core.types.Command;
+import org.openwebnet4j.message.BaseOpenMessage;
+import org.openwebnet4j.message.CEN;
+import org.openwebnet4j.message.CEN.Pressure;
+import org.openwebnet4j.message.CENPlusScenario;
+import org.openwebnet4j.message.CENPlusScenario.CENPlusPressure;
+import org.openwebnet4j.message.CENScenario;
+import org.openwebnet4j.message.CENScenario.CENPressure;
+import org.openwebnet4j.message.FrameException;
+import org.openwebnet4j.message.Where;
+import org.openwebnet4j.message.WhereCEN;
+import org.openwebnet4j.message.Who;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link OpenWebNetScenarioHandler} is responsible for handling commands/messages for CEN/CEN+ Scenarios. It
+ * extends the abstract {@link OpenWebNetThingHandler}.
+ *
+ * @author Massimo Valla - Initial contribution
+ */
+@NonNullByDefault
+public class OpenWebNetScenarioHandler extends OpenWebNetThingHandler {
+
+    private final Logger logger = LoggerFactory.getLogger(OpenWebNetScenarioHandler.class);
+
+    private interface PressEvent {
+        @Override
+        public String toString();
+    }
+
+    private enum CENPressEvent implements PressEvent {
+        CEN_EVENT_START_PRESS("START_PRESS"),
+        CEN_EVENT_SHORT_PRESS("SHORT_PRESS"),
+        CEN_EVENT_EXTENDED_PRESS("EXTENDED_PRESS"),
+        CEN_EVENT_RELEASE_EXTENDED_PRESS("RELEASE_EXTENDED_PRESS");
+
+        private final String press;
+
+        CENPressEvent(final String pr) {
+            this.press = pr;
+        }
+
+        public static @Nullable CENPressEvent fromValue(String s) {
+            Optional<CENPressEvent> event = Arrays.stream(values()).filter(val -> s.equals(val.press)).findFirst();
+            return event.orElse(null);
+        }
+
+        @Override
+        public String toString() {
+            return press;
+        }
+    }
+
+    private enum CENPlusPressEvent implements PressEvent {
+        CENPLUS_EVENT_SHORT_PRESS("SHORT_PRESS"),
+        CENPLUS_EVENT_START_EXTENDED_PRESS("START_EXTENDED_PRESS"),
+        CENPLUS_EVENT_EXTENDED_PRESS("EXTENDED_PRESS"),
+        CENPLUS_EVENT_RELEASE_EXTENDED_PRESS("RELEASE_EXTENDED_PRESS");
+
+        private final String press;
+
+        CENPlusPressEvent(final String pr) {
+            this.press = pr;
+        }
+
+        public static @Nullable CENPlusPressEvent fromValue(String s) {
+            Optional<CENPlusPressEvent> event = Arrays.stream(values()).filter(val -> s.equals(val.press)).findFirst();
+            return event.orElse(null);
+        }
+
+        @Override
+        public String toString() {
+            return press;
+        }
+    }
+
+    private boolean isCENPlus = false;
+
+    public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = OpenWebNetBindingConstants.SCENARIO_SUPPORTED_THING_TYPES;
+
+    public OpenWebNetScenarioHandler(Thing thing) {
+        super(thing);
+        if (OpenWebNetBindingConstants.THING_TYPE_BUS_CENPLUS_SCENARIO_CONTROL.equals(thing.getThingTypeUID())) {
+            isCENPlus = true;
+            logger.debug("created CEN+ device for thing: {}", getThing().getUID());
+        } else {
+            logger.debug("created CEN device for thing: {}", getThing().getUID());
+        }
+    }
+
+    @Override
+    public void initialize() {
+        super.initialize();
+        Object buttonsConfig = getConfig().get(CONFIG_PROPERTY_SCENARIO_BUTTONS);
+        if (buttonsConfig != null) {
+            Set<Integer> buttons = csvStringToSetInt((String) buttonsConfig);
+            if (!buttons.isEmpty()) {
+                ThingBuilder thingBuilder = editThing();
+                Channel ch;
+                for (Integer i : buttons) {
+                    ch = thing.getChannel(CHANNEL_SCENARIO_BUTTON + i);
+                    if (ch == null) {
+                        thingBuilder.withChannel(buttonToChannel(i));
+                        logger.debug("added channel {} to thing: {}", i, getThing().getUID());
+                    }
+                }
+                updateThing(thingBuilder.build());
+            } else {
+                logger.warn("invalid config parameter buttons='{}' for thing {}", buttonsConfig, thing.getUID());
+            }
+        }
+    }
+
+    @Override
+    public Collection<Class<? extends ThingHandlerService>> getServices() {
+        return Collections.singleton(OpenWebNetCENActions.class);
+    }
+
+    @Override
+    protected String ownIdPrefix() {
+        if (isCENPlus) {
+            return Who.CEN_PLUS_SCENARIO_SCHEDULER.value().toString();
+        } else {
+            return Who.CEN_SCENARIO_SCHEDULER.value().toString();
+        }
+    }
+
+    @Override
+    protected void handleMessage(BaseOpenMessage msg) {
+        super.handleMessage(msg);
+        if (msg.isCommand()) {
+            triggerChannel((CEN) msg);
+        } else {
+            logger.debug("handleMessage() Ignoring unsupported DIM for thing {}. Frame={}", getThing().getUID(), msg);
+        }
+    }
+
+    private void triggerChannel(CEN cenMsg) {
+        Integer buttonNumber;
+        try {
+            buttonNumber = cenMsg.getButtonNumber();
+        } catch (FrameException e) {
+            logger.warn("cannot read CEN/CEN+ button. Ignoring message {}", cenMsg);
+            return;
+        }
+        if (buttonNumber == null || buttonNumber < 0 || buttonNumber > 31) {
+            logger.warn("invalid CEN/CEN+ button number: {}. Ignoring message {}", buttonNumber, cenMsg);
+            return;
+        }
+        Channel ch = thing.getChannel(CHANNEL_SCENARIO_BUTTON + buttonNumber);
+        if (ch == null) { // we have found a new button for this device, let's add a new channel for the button
+            ThingBuilder thingBuilder = editThing();
+            ch = buttonToChannel(buttonNumber);
+            thingBuilder.withChannel(ch);
+            updateThing(thingBuilder.build());
+            logger.info("added new channel {} to thing {}", ch.getUID(), getThing().getUID());
+        }
+        final Channel channel = ch;
+        PressEvent pressEv = null;
+        Pressure press = null;
+        try {
+            press = cenMsg.getButtonPressure();
+        } catch (FrameException e) {
+            logger.warn("invalid CEN/CEN+ Press. Ignoring message {}", cenMsg);
+            return;
+        }
+        if (press == null) {
+            logger.warn("invalid CEN/CEN+ Press. Ignoring message {}", cenMsg);
+            return;
+        }
+
+        if (cenMsg instanceof CENScenario) {
+            switch ((CENPressure) press) {
+                case START_PRESSURE:
+                    pressEv = CENPressEvent.CEN_EVENT_START_PRESS;
+                    break;
+                case RELEASE_SHORT_PRESSURE:
+                    pressEv = CENPressEvent.CEN_EVENT_SHORT_PRESS;
+                    break;
+                case EXTENDED_PRESSURE:
+                    pressEv = CENPressEvent.CEN_EVENT_EXTENDED_PRESS;
+                    break;
+                case RELEASE_EXTENDED_PRESSURE:
+                    pressEv = CENPressEvent.CEN_EVENT_RELEASE_EXTENDED_PRESS;
+                    break;
+                default:
+                    logger.warn("unsupported CENPress. Ignoring message {}", cenMsg);
+                    return;
+            }
+        } else {
+            switch ((CENPlusPressure) press) {
+                case SHORT_PRESSURE:
+                    pressEv = CENPlusPressEvent.CENPLUS_EVENT_SHORT_PRESS;
+                    break;
+                case START_EXTENDED_PRESSURE:
+                    pressEv = CENPlusPressEvent.CENPLUS_EVENT_START_EXTENDED_PRESS;
+                    break;
+                case EXTENDED_PRESSURE:
+                    pressEv = CENPlusPressEvent.CENPLUS_EVENT_EXTENDED_PRESS;
+                    break;
+                case RELEASE_EXTENDED_PRESSURE:
+                    pressEv = CENPlusPressEvent.CENPLUS_EVENT_RELEASE_EXTENDED_PRESS;
+                    break;
+                default:
+                    logger.warn("unsupported CENPlusPress. Ignoring message {}", cenMsg);
+                    return;
+            }
+        }
+
+        triggerChannel(channel.getUID(), pressEv.toString());
+    }
+
+    private Channel buttonToChannel(int buttonNumber) {
+        ChannelTypeUID channelTypeUID;
+        if (isCENPlus) {
+            channelTypeUID = new ChannelTypeUID(BINDING_ID, CHANNEL_TYPE_CEN_PLUS_BUTTON_EVENT);
+        } else {
+            channelTypeUID = new ChannelTypeUID(BINDING_ID, CHANNEL_TYPE_CEN_BUTTON_EVENT);
+        }
+        return ChannelBuilder
+                .create(new ChannelUID(getThing().getUID(), CHANNEL_SCENARIO_BUTTON + buttonNumber), "String")
+                .withType(channelTypeUID).withKind(ChannelKind.TRIGGER).withLabel("Button " + buttonNumber).build();
+    }
+
+    /**
+     * Construct a CEN/CEN+ virtual press message for this device given a pressString and button number
+     *
+     * @param pressString one START_PRESS, SHORT_PRESS etc.
+     * @param button number [0-31]
+     * @return CEN message
+     * @throws IllegalArgumentException if button number or pressString are invalid
+     */
+    public CEN pressStrToMessage(String pressString, int button) throws IllegalArgumentException {
+        Where w = deviceWhere;
+        if (w == null) {
+            throw new IllegalArgumentException("pressStrToMessage: deviceWhere is null");
+        }
+        if (isCENPlus) {
+            CENPlusPressEvent prEvent = CENPlusPressEvent.fromValue(pressString);
+            if (prEvent != null) {
+                switch (prEvent) {
+                    case CENPLUS_EVENT_SHORT_PRESS:
+                        return CENPlusScenario.virtualShortPressure(w.value(), button);
+                    case CENPLUS_EVENT_START_EXTENDED_PRESS:
+                        return CENPlusScenario.virtualStartExtendedPressure(w.value(), button);
+                    case CENPLUS_EVENT_EXTENDED_PRESS:
+                        return CENPlusScenario.virtualExtendedPressure(w.value(), button);
+                    case CENPLUS_EVENT_RELEASE_EXTENDED_PRESS:
+                        return CENPlusScenario.virtualReleaseExtendedPressure(w.value(), button);
+                    default:
+                        throw new IllegalArgumentException("unsupported press type: " + pressString);
+                }
+            } else {
+                throw new IllegalArgumentException("unsupported press type: " + pressString);
+            }
+        } else {
+            CENPressEvent prEvent = CENPressEvent.fromValue(pressString);
+            if (prEvent != null) {
+                switch (prEvent) {
+                    case CEN_EVENT_START_PRESS:
+                        return CENScenario.virtualStartPressure(w.value(), button);
+                    case CEN_EVENT_SHORT_PRESS:
+                        return CENScenario.virtualReleaseShortPressure(w.value(), button);
+                    case CEN_EVENT_EXTENDED_PRESS:
+                        return CENScenario.virtualExtendedPressure(w.value(), button);
+                    case CEN_EVENT_RELEASE_EXTENDED_PRESS:
+                        return CENScenario.virtualReleaseExtendedPressure(w.value(), button);
+                    default:
+                        throw new IllegalArgumentException("unsupported press type: " + pressString);
+                }
+            } else {
+                throw new IllegalArgumentException("unsupported press type: " + pressString);
+            }
+        }
+    }
+
+    private static Set<Integer> csvStringToSetInt(String s) {
+        TreeSet<Integer> intSet = new TreeSet<Integer>();
+        String sNorm = s.replaceAll("\\s", "");
+        Scanner sc = new Scanner(sNorm);
+        sc.useDelimiter(",");
+        while (sc.hasNextInt()) {
+            intSet.add(sc.nextInt());
+        }
+        sc.close();
+        return intSet;
+    }
+
+    @Override
+    protected void handleChannelCommand(ChannelUID channel, Command command) {
+        logger.warn("CEN/CEN+ channels are trigger channels and do not handle commands");
+    }
+
+    @Override
+    protected void refreshDevice(boolean refreshAll) {
+        logger.debug("CEN/CEN+ channels are trigger channels and do not have state");
+    }
+
+    @Override
+    protected Where buildBusWhere(String wStr) throws IllegalArgumentException {
+        return new WhereCEN(wStr);
+    }
+
+    @Override
+    protected void requestChannelState(ChannelUID channel) {
+        logger.debug("CEN/CEN+ channels are trigger channels and do not have state");
+    }
+}
index 7e0ac4bfe32d681fb7f0b249e352f3d152c6d024..27be24da9b7bd795db9d0d5a59d88d6560b45472 100644 (file)
@@ -165,7 +165,7 @@ public abstract class OpenWebNetThingHandler extends BaseThingHandler {
     /**
      * Helper method to send OWN messages from ThingHandlers
      */
-    protected @Nullable Response send(OpenMessage msg) throws OWNException {
+    public @Nullable Response send(OpenMessage msg) throws OWNException {
         OpenWebNetBridgeHandler bh = bridgeHandler;
         if (bh != null) {
             OpenGateway gw = bh.gateway;
index 4ba3d5392ff7cf258b0b389708946719a75e7c87..e91aae14782dbef4840d573a822a78b2e5df4336 100644 (file)
@@ -38,7 +38,7 @@
                        </parameter>
 
                        <parameter name="where" type="text" required="true">
-                               <label>OpenWebNet Device Address (where)</label>
+                               <label>OpenWebNet Address (where)</label>
                                <description>Example: A/PL address: A=1 PL=3 --> where=13. On local bus: where=13#4#01</description>
                        </parameter>
                </config-description>
diff --git a/bundles/org.openhab.binding.openwebnet/src/main/resources/OH-INF/thing/BusCENPlusScenarioControl.xml b/bundles/org.openhab.binding.openwebnet/src/main/resources/OH-INF/thing/BusCENPlusScenarioControl.xml
new file mode 100644 (file)
index 0000000..dbb589b
--- /dev/null
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<thing:thing-descriptions bindingId="openwebnet"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
+       xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
+
+       <!-- Thing for BUS CEN+ Scenario Control (BTicino HC/HD/HS/L/N/NT4680) -->
+       <thing-type id="bus_cenplus_scenario_control">
+               <supported-bridge-type-refs>
+                       <bridge-type-ref id="bus_gateway"/>
+               </supported-bridge-type-refs>
+               <label>CEN+ Scenario Control</label>
+               <description>A OpenWebNet BUS/SCS CEN+ Scenario Control device. BTicino models: HC/HD/HS/L/N/NT4680</description>
+
+               <!-- channels are created dynamically based on configured buttons -->
+
+               <properties>
+                       <property name="vendor">BTicino/Legrand</property>
+                       <property name="model">BTI-HC/HD/HS/L/N/NT4680</property>
+                       <property name="ownDeviceType">273</property>
+               </properties>
+
+               <representation-property>ownId</representation-property>
+
+               <config-description>
+                       <parameter name="buttons" type="text">
+                               <label>Configured Buttons</label>
+                               <description>List (comma separated) of buttons numbers [0-31] configured for this scenario device, example:
+                                       buttons=1,2,4
+                               </description>
+                       </parameter>
+                       <parameter name="where" type="text" required="true">
+                               <label>OpenWebNet Address (where)</label>
+                               <description>Use 2+N[0-2047]. Example: scenario control 5 --> WHERE=25</description>
+                       </parameter>
+               </config-description>
+
+       </thing-type>
+</thing:thing-descriptions>
diff --git a/bundles/org.openhab.binding.openwebnet/src/main/resources/OH-INF/thing/BusCENScenarioControl.xml b/bundles/org.openhab.binding.openwebnet/src/main/resources/OH-INF/thing/BusCENScenarioControl.xml
new file mode 100644 (file)
index 0000000..5f5700d
--- /dev/null
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<thing:thing-descriptions bindingId="openwebnet"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
+       xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
+
+       <!-- Thing for BUS CEN Scenario Control (BTicino HC/HD/HS/L/N/NT4680) -->
+       <thing-type id="bus_cen_scenario_control">
+               <supported-bridge-type-refs>
+                       <bridge-type-ref id="bus_gateway"/>
+               </supported-bridge-type-refs>
+
+               <label>CEN Scenario Control</label>
+               <description>A OpenWebNet BUS/SCS CEN Scenario Control device. BTicino models: HC/HD/HS/L/N/NT4680</description>
+
+               <!-- channels are created dynamically based on configured buttons -->
+
+               <properties>
+                       <property name="vendor">BTicino/Legrand</property>
+                       <property name="model">BTI-HC/HD/HS/L/N/NT4680</property>
+                       <property name="ownDeviceType">2</property>
+               </properties>
+
+               <representation-property>ownId</representation-property>
+
+               <config-description>
+                       <parameter name="buttons" type="text">
+                               <label>Configured Buttons</label>
+                               <description>List (comma separated) of buttons numbers [0-31] configured for this scenario device. Example:
+                                       buttons=1,2,4</description>
+                       </parameter>
+                       <parameter name="where" type="text" required="true">
+                               <label>OpenWebNet Address (where)</label>
+                               <description>Example: A/PL address: A=1 PL=3 --> WHERE=13. On local bus: WHERE=13#4#01</description>
+                       </parameter>
+
+               </config-description>
+
+       </thing-type>
+</thing:thing-descriptions>
index 58f5013e98b5d59f49e8966d3703c8e8023b474c..5288e9b5a8f53b4abdaed56ea99810daf09e7fe7 100644 (file)
@@ -27,7 +27,7 @@
 
                <config-description>
                        <parameter name="where" type="text" required="true">
-                               <label>OpenWebNet Device Address (where)</label>
+                               <label>OpenWebNet Address (where)</label>
                                <description>Example: A/PL address: A=1 PL=3 --> where=13. On local bus: where=13#4#01</description>
                        </parameter>
                </config-description>
index 2fcd93bd888f9ca1a3652af49e4c0c2caa4e3686..e9990a2e858f54b5fc07098800bc496bd04cb8ca 100644 (file)
@@ -27,7 +27,7 @@
 
                <config-description>
                        <parameter name="where" type="text" required="true">
-                               <label>OpenWebNet Address</label>
+                               <label>OpenWebNet Address (where)</label>
                                <description>Example: 5N with N=[1-255]</description>
                        </parameter>
                </config-description>
index 805cc0dc6ba5d41145b15ed60979a0c641058b4a..f8e7398b9acaebcda0247e214823f515e55b7b81 100644 (file)
@@ -27,7 +27,7 @@
 
                <config-description>
                        <parameter name="where" type="text" required="true">
-                               <label>OpenWebNet Device Address (where)</label>
+                               <label>OpenWebNet Address (where)</label>
                                <description>Example: A/PL address: A=1 PL=3 --> where=13. On local bus: where=13#4#01</description>
                        </parameter>
                </config-description>
index aec78eb816342690030694bcc86e799cce052d4e..4f878a1fc1ef333493b68ff3e5d6f1437385ebc1 100644 (file)
@@ -27,7 +27,7 @@
 
                <config-description>
                        <parameter name="where" type="text" required="true">
-                               <label>OpenWebNet Device Address</label>
+                               <label>OpenWebNet Address (where)</label>
                                <description>Example: sensor 3 of zone 2 --> where=302. Sensor 5 of external zone 00 --> where=500</description>
                        </parameter>
                </config-description>
index 0bb8c8b01fc4866ed4b2aed5a3396c393adeae85..85032f752184a148312351470dff683e6268f00c 100644 (file)
@@ -36,7 +36,7 @@
 
                <config-description>
                        <parameter name="where" type="text" required="true">
-                               <label>OpenWebNet Device Address</label>
+                               <label>OpenWebNet Address (where)</label>
                                <description>Example: zone 2 --> where=2.</description>
                        </parameter>
 
index 87a60e4374ce91ba8c1bf6e7c71ee83fc3632890..340541a9a8c196683a6847d0ec3ac26c70f439f2 100644 (file)
@@ -37,7 +37,7 @@
                                <default>AUTO</default>
                        </parameter>
                        <parameter name="where" type="text" required="true">
-                               <label>OpenWebNet Device Address (where)</label>
+                               <label>OpenWebNet Address (where)</label>
                                <description>It identifies one ZigBee device. Example: 765432101#9</description>
                        </parameter>
                </config-description>
index 98b07e81a5f54aeabc771ac765aa3d445b616074..f27f8b85744fdc663e2d846a009053fdc6dcbe9f 100644 (file)
@@ -27,7 +27,7 @@
 
                <config-description>
                        <parameter name="where" type="text" required="true">
-                               <label>OpenWebNet Device Address (where)</label>
+                               <label>OpenWebNet Address (where)</label>
                                <description>It identifies one ZigBee device. Example: 765432101#9</description>
                        </parameter>
                </config-description>
index f741b72403d353bdd0179549ad7141d5cf871cda..6e0f3e1714fd58d074313ec7855d683ee4edcf53 100644 (file)
@@ -27,7 +27,7 @@
 
                <config-description>
                        <parameter name="where" type="text" required="true">
-                               <label>OpenWebNet Device Address (where)</label>
+                               <label>OpenWebNet Address (where)</label>
                                <description>It identifies one ZigBee device. Example: 765432101#9</description>
                        </parameter>
                </config-description>
index 6de61f7f7ff5b2af6351f86a4545dcb6305e01d0..8f1fd585881bea676b41bcdb99eacec82e1df80f 100644 (file)
@@ -28,7 +28,7 @@
 
                <config-description>
                        <parameter name="where" type="text" required="true">
-                               <label>OpenWebNet Device Address (where)</label>
+                               <label>OpenWebNet Address (where)</label>
                                <description>It identifies one ZigBee device. Example: 765432100#9 (use unit=00 at the end)</description>
                        </parameter>
                </config-description>
index 97ca3eedc69949ad6260f4ec0d46af0f720aad8b..ddba826d737e8ab51e556405d6752d89f1bdeb49 100644 (file)
                <category>Energy</category>
                <state readOnly="true" pattern="%.0f %unit%"></state>
        </channel-type>
+
+       <!-- CEN/CEN+ trigger channels -->
+       <channel-type id="cenButtonEvent">
+               <kind>trigger</kind>
+               <label>CEN Button Event</label>
+               <event>
+                       <options>
+                               <option value="START_PRESS">start press</option>
+                               <option value="SHORT_PRESS">release after short press</option>
+                               <option value="EXTENDED_PRESS">extended press (repeated until release)</option>
+                               <option value="RELEASE_EXTENDED_PRESS">release after extended press</option>
+                       </options>
+               </event>
+       </channel-type>
+
+       <channel-type id="cenPlusButtonEvent">
+               <kind>trigger</kind>
+               <label>CEN+ Button Event</label>
+               <event>
+                       <options>
+                               <option value="SHORT_PRESS">short press</option>
+                               <option value="START_EXTENDED_PRESS">start of extended press</option>
+                               <option value="EXTENDED_PRESS">extended press (repeated until release)</option>
+                               <option value="RELEASE_EXTENDED_PRESS">release after extended press</option>
+                       </options>
+               </event>
+       </channel-type>
 </thing:thing-descriptions>