]> git.basschouten.com Git - openhab-addons.git/commitdiff
[openwebnet] add support for lights general/area/group events and Things (#15921)
authorM Valla <12682715+mvalla@users.noreply.github.com>
Mon, 13 May 2024 05:10:36 +0000 (07:10 +0200)
committerGitHub <noreply@github.com>
Mon, 13 May 2024 05:10:36 +0000 (07:10 +0200)
* [openwebnet] initial support for general/area light events

Signed-off-by: Massimo Valla <mvcode00@gmail.com>
20 files changed:
bundles/org.openhab.binding.openwebnet/README.md
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/discovery/OpenWebNetDeviceDiscoveryService.java
bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/handler/LightAutomHandlersMap.java [new file with mode: 0644]
bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/handler/OpenWebNetAlarmHandler.java
bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/handler/OpenWebNetAutomationHandler.java
bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/handler/OpenWebNetAuxiliaryHandler.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/OpenWebNetGenericHandler.java
bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/handler/OpenWebNetLightingGroupHandler.java [new file with mode: 0644]
bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/handler/OpenWebNetLightingHandler.java
bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/handler/OpenWebNetScenarioBasicHandler.java
bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/handler/OpenWebNetScenarioHandler.java
bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/handler/OpenWebNetThermoregulationHandler.java
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/i18n/openwebnet.properties
bundles/org.openhab.binding.openwebnet/src/main/resources/OH-INF/thing/BusLightGroup.xml [new file with mode: 0644]
bundles/org.openhab.binding.openwebnet/src/test/java/org/openhab/binding/openwebnet/internal/handler/OwnIdTest.java

index 36beca291093d396ee6ea5f889a14c6fa8b8ebf2..5c5a797fb142d79ffd8e1d18d99ad72eb6775ba6 100644 (file)
@@ -41,17 +41,17 @@ The following Things and OpenWebNet `WHOs` are supported:
 
 ### For MyHOME - BUS/SCS
 
-| Category                      |     WHO     |                       Thing Type IDs                       | Description                                                      | Status                                                                                                                                                                                                     |
-|-------------------------------|:-----------:|:----------------------------------------------------------:|------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| Gateway Management            |    `13`     |                       `bus_gateway`                        | Any IP gateway supporting OpenWebNet protocol should work        | Successfully tested: F452, F453, F453AV,F454, F455, MyHOMEServer1, MyHOME_Screen10, MyHOME_Screen3,5, MH201, MH202, MH200N. Some connection stability issues/gateway resets reported with MH202            |
-| 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`, `bus_thermo_cu`   | Thermo zones management and temperature sensors (probes)         | Successfully tested: Thermostats H/LN4691, HS4692, KG4691; sensors (probes): L/N/NT4577 + 3455; Central Units 4-zones (models L/N/NT/HD/HC/HS4695) and 99-zones (model 3550). See [Channels - Thermo](#configuring-thermo) for more details |
-| Alarm                         |     `5`     |  `bus_alarm_system`, `bus_alarm_zone`                      | BUS Alarm system and zones                                       | Successfully tested: Burglar-alarm Unit 3486  |
-| Auxiliary (AUX)               |     `9`     |  `bus_aux`                                                 | AUX commands                                                     | Successfully tested: AUX configured for Burglar-alarm Unit 3486. **Only sending AUX commands is supported**    |
-| Basic, CEN & CEN+ Scenarios   | `0`, `15`, `25` | `bus_scenario_control`, `bus_cen_scenario_control`, `bus_cenplus_scenario_control` | Basic and CEN/CEN+ Scenarios events and virtual activation                 | Successfully tested: CEN/CEN+ scenario control: HC/HD/HS/L/N/NT4680 and basic scenario modules F420/IR3456 + L4680 (WHO=0)      |
-| Dry Contact and IR Interfaces |    `25`     |                    `bus_dry_contact_ir`                    | Dry Contacts and IR Interfaces                                   | Successfully tested: contact interfaces F428 and 3477; IR sensors: HC/HD/HS/L/N/NT4610      |
-| Energy Management             |    `18`     |                     `bus_energy_meter`                     | Energy Management                                                | Successfully tested: F520, F521. Partially tested: F522, F523                               |
+| Category                      | WHO             | Thing Type IDs                                                                     | Description                                                      | Status                                                                                                                                                                                                                                      |
+|-------------------------------|-:-:-------------|-:-:--------------------------------------------------------------------------------|------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| Gateway Management            | `13`            | `bus_gateway`                                                                      | Any IP gateway supporting OpenWebNet protocol should work        | Successfully tested: F452, F453, F453AV,F454, F455, MyHOMEServer1, MyHOME_Screen10, MyHOME_Screen3,5, MH201, MH202, MH200N. Some connection stability issues/gateway resets reported with MH202                                             |
+| Lighting                      | `1`             | `bus_on_off_switch`, `bus_dimmer`, `bus_light_group`                               | BUS switches and dimmers and groups of them                      | 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`, `bus_thermo_cu`                            | Thermo zones management and temperature sensors (probes)         | Successfully tested: Thermostats H/LN4691, HS4692, KG4691; sensors (probes): L/N/NT4577 + 3455; Central Units 4-zones (models L/N/NT/HD/HC/HS4695) and 99-zones (model 3550). See [Channels - Thermo](#configuring-thermo) for more details |
+| Alarm                         | `5`             | `bus_alarm_system`, `bus_alarm_zone`                                               | BUS Alarm system and zones                                       | Successfully tested: Burglar-alarm Unit 3486                                                                                                                                                                                                |
+| Auxiliary (AUX)               | `9`             | `bus_aux`                                                                          | AUX commands                                                     | Successfully tested: AUX configured for Burglar-alarm Unit 3486. **Only sending AUX commands is supported**                                                                                                                                 |
+| Basic, CEN & CEN+ Scenarios   | `0`, `15`, `25` | `bus_scenario_control`, `bus_cen_scenario_control`, `bus_cenplus_scenario_control` | Basic and CEN/CEN+ Scenarios events and virtual activation       | Successfully tested: CEN/CEN+ scenario control: HC/HD/HS/L/N/NT4680 and basic scenario modules F420/IR3456 + L4680 (WHO=0)                                                                                                                  |
+| Dry Contact and IR Interfaces | `25`            | `bus_dry_contact_ir`                                                               | Dry Contacts and IR Interfaces                                   | Successfully tested: contact interfaces F428 and 3477; IR sensors: HC/HD/HS/L/N/NT4610                                                                                                                                                      |
+| Energy Management             | `18`            | `bus_energy_meter`                                                                 | Energy Management                                                | Successfully tested: F520, F521. Partially tested: F522, F523                                                                                                                                                                               |
 
 ### For MyHOME Radio - Zigbee
 
@@ -133,8 +133,9 @@ For any manually added device, you must configure:
 - the associated gateway Thing (`Parent Bridge` menu)
 - the `where` configuration parameter (`OpenWebNet Address`): this is the OpenWebNet address configured for the device in the BTicino/Legrand system. This address can be found either on the device itself (Physical configuration, using jumpers in case of BUS) or through the MyHOME_Suite software (Virtual configuration). The address can have several formats depending on the device/system:
   - example for MyHOME - BUS/SCS system:
-    - light device A=`2` (Area 2), PL=`4` (Light-point 4) --> `where="24"`
+    - light device A=`2` (room 2), PL=`4` (light-point 4) --> `where="24"`
     - light device A=`03`, PL=`11` on local bus `01` --> `where="0311#4#01"`
+    - lights in room 5 (A=`AMB`, PL=`5`) --> `where="5"`
     - scenario control module address `53` --> `where="53"`
     - CEN scenario A=`05`, PL=`12` --> `where="0512"`
     - CEN+ scenario `5`: add `2` before --> `where="25"`
@@ -210,22 +211,36 @@ Event: command OPEN = *9*1*5##
 OPEN command to execute: *5*8#134##
 ```
 
+#### Configuring Light Groups (Room/Group/General)
+
+The `bus_light_group` Thing type represents a set of devices belonging to a room or group in the BUS system:
+
+- `bus_light_group` with `where="5"` will be associated to all lights configured in room 5 (A=`5`)
+- `bus_light_group` with `where="#4"` will be associated to all lights belonging to group 4 (G=`4`)
+- `bus_light_group` with `where="0"` will be associated to all lights in the system (General).
+
+It's not mandatory to define `bus_light_group` Things: however adding them is useful to send commands to a set of lights or to track state of a room/all lights when one of the members changes state.
+It's also not mandatory to define `bus_light_group` Things to track state of a light when a room/group/general ON/OFF command is sent on the BUS (the light state will be updated in any case).
+
+**NOTE 1**
+For a `bus_light_group` Thing to be updated properly, at least one light Thing belonging to that room/group must also be configured.
+
 ## Channels
 
 ### Lighting, Automation, Basic/CEN/CEN+ Scenario Events, Dry Contact / IR Interfaces, Power and AUX 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     |
-| `scenario`                              | `bus_scenario_control`                                        | String        | Trigger channel for Basic scenario events [see possible values](#scenario-channels)                           | R (TRIGGER) |
-| `button#X`                              | `bus_cen_scenario_control`, `bus_cenplus_scenario_control`    | String        | Trigger channel for CEN/CEN+ scenario events [see possible values](#scenario-channels)                        | R (TRIGGER) |
-| `sensor`                                | `bus_dry_contact_ir`                                          | Switch        | If a Dry Contact Interface is `ON`/`OFF`, or if an IR Sensor is detecting movement (`ON`), or not  (`OFF`)    |      R      |
-| `power`                                 | `bus_energy_meter`                                            | Number:Power  | The current active power usage from Energy Meter                                                              |      R      |
-| `energyToday`                           | `bus_energy_meter`                                            | Number:Energy | Current day energy                                                                                            |      R      |
-| `energyThisMonth`                       | `bus_energy_meter`                                            | Number:Energy | Current month energy                                                                                          |      R      |
-| `aux`                                   | `bus_aux`                                                     | String        | Possible commands: `ON`, `OFF`, `TOGGLE`, `STOP`, `UP`, `DOWN`, `ENABLED`, `DISABLED`, `RESET_GEN`, `RESET_BI`, `RESET_TRI`. Only `ON` and `OFF` are supported for now |     R/W     |
+| 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`, `bus_light_group`, `zb_on_off_switch`, `zb_on_off_switch2u` | Switch        | To switch the device (or group) `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         |
+| `scenario`                              | `bus_scenario_control`                                                           | String        | Trigger channel for Basic scenario events [see possible values](#scenario-channels)                                                                                    | R (TRIGGER) |
+| `button#X`                              | `bus_cen_scenario_control`, `bus_cenplus_scenario_control`                       | String        | Trigger channel for CEN/CEN+ scenario events [see possible values](#scenario-channels)                                                                                 | R (TRIGGER) |
+| `sensor`                                | `bus_dry_contact_ir`                                                             | Switch        | If a Dry Contact Interface is `ON`/`OFF`, or if an IR Sensor is detecting movement (`ON`), or not  (`OFF`)                                                             | R           |
+| `power`                                 | `bus_energy_meter`                                                               | Number:Power  | The current active power usage from Energy Meter                                                                                                                       | R           |
+| `energyToday`                           | `bus_energy_meter`                                                               | Number:Energy | Current day energy                                                                                                                                                     | R           |
+| `energyThisMonth`                       | `bus_energy_meter`                                                               | Number:Energy | Current month energy                                                                                                                                                   | R           |
+| `aux`                                   | `bus_aux`                                                                        | String        | Possible commands: `ON`, `OFF`, `TOGGLE`, `STOP`, `UP`, `DOWN`, `ENABLED`, `DISABLED`, `RESET_GEN`, `RESET_BI`, `RESET_TRI`. Only `ON` and `OFF` are supported for now | R/W         |
 
 ### Alarm channels
 
@@ -375,6 +390,8 @@ MyHOME BUS/SCS gateway and Things configuration:
 
 ```java
 Bridge openwebnet:bus_gateway:mybridge "MyHOMEServer1" [ host="192.168.1.35", passwd="abcde", port=20000, discoveryByActivation=false ] {
+      bus_light_group               ALL_lights           "All lights"               [ where="0" ]
+      bus_light_group               LR_room              "Living Room"              [ where="5" ]
       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="20050"]
@@ -417,6 +434,8 @@ NOTE: lights, blinds and zones (thermostat) can be handled from personal assista
 In the following example some `Google Assistant` (`ga="..."`) and `HomeKit` (`homekit="..."`) metadata were added as examples according to the [documentation for Google Assistant integration on openHAB](https://www.openhab.org/docs/ecosystem/google-assistant) and [the openHAB HomeKit Add-on documentation](https://www.openhab.org/addons/integrations/homekit/): see the specific openHAB documentation for updated configurations and more metadata options.
 
 ```java
+Switch              iALL_lights                 "All Lights"                                    { channel="openwebnet:bus_light_group:mybridge:ALL_lights:switch", ga="Light", homekit="Lighting" }
+Switch              iLR_room                    "Living Room"                                   { channel="openwebnet:bus_light_group:mybridge:LR_room:switch", ga="Light", homekit="Lighting" }
 Switch              iLR_switch                  "Light"                       (gLivingRoom)     { channel="openwebnet:bus_on_off_switch:mybridge:LR_switch:switch", ga="Light", homekit="Lighting" }
 Dimmer              iLR_dimmer                  "Dimmer [%.0f %%]"            (gLivingRoom)     { channel="openwebnet:bus_dimmer:mybridge:LR_dimmer:brightness", ga="Light", homekit="Lighting" }
 
@@ -486,6 +505,8 @@ sitemap openwebnet label="OpenWebNet Binding Example Sitemap"
 {
     Frame label="Living Room"
     {
+          Default item=iALL_lights          icon="light"
+          Default item=iLR_room             icon="light"
           Default item=iLR_switch           icon="light"
           Default item=iLR_dimmer           icon="light"
           Default item=iLR_shutter
index 4fff9f74cc9cd77228bd339d86aa43d97a9cd934..add37a0c852b90df62a4a828a42b700ec53017eb 100644 (file)
@@ -25,10 +25,10 @@ import org.openhab.core.thing.ThingTypeUID;
  * The {@link OpenWebNetBindingConstants} class defines common constants, which
  * are used across the whole binding.
  *
- * @author Massimo Valla - Initial contribution, updates
+ * @author Massimo Valla - Initial contribution, updates. Light groups.
  * @author Gilberto Cocchi - Thermoregulation
  * @author Andrea Conte - Energy management, Thermoregulation
- * @author Giovanni Fabiani - Aux support
+ * @author Giovanni Fabiani - AUX support
  */
 
 @NonNullByDefault
@@ -53,6 +53,10 @@ public class OpenWebNetBindingConstants {
     public static final String THING_LABEL_BUS_ON_OFF_SWITCH = "Switch";
     public static final ThingTypeUID THING_TYPE_BUS_DIMMER = new ThingTypeUID(BINDING_ID, "bus_dimmer");
     public static final String THING_LABEL_BUS_DIMMER = "Dimmer";
+
+    public static final ThingTypeUID THING_TYPE_BUS_LIGHT_GROUP = new ThingTypeUID(BINDING_ID, "bus_light_group");
+    public static final String THING_LABEL_BUS_LIGHT_GROUP = "Light Group";
+
     public static final ThingTypeUID THING_TYPE_BUS_AUTOMATION = new ThingTypeUID(BINDING_ID, "bus_automation");
     public static final String THING_LABEL_BUS_AUTOMATION = "Automation";
     public static final ThingTypeUID THING_TYPE_BUS_ENERGY_METER = new ThingTypeUID(BINDING_ID, "bus_energy_meter");
@@ -97,6 +101,7 @@ public class OpenWebNetBindingConstants {
     public static final Set<ThingTypeUID> LIGHTING_SUPPORTED_THING_TYPES = Set.of(THING_TYPE_ZB_ON_OFF_SWITCH,
             THING_TYPE_ZB_ON_OFF_SWITCH_2UNITS, THING_TYPE_ZB_DIMMER, THING_TYPE_BUS_ON_OFF_SWITCH,
             THING_TYPE_BUS_DIMMER);
+    public static final Set<ThingTypeUID> LIGHTING_GROUP_SUPPORTED_THING_TYPES = Set.of(THING_TYPE_BUS_LIGHT_GROUP);
     // ## Automation
     public static final Set<ThingTypeUID> AUTOMATION_SUPPORTED_THING_TYPES = Set.of(THING_TYPE_ZB_AUTOMATION,
             THING_TYPE_BUS_AUTOMATION);
@@ -118,7 +123,7 @@ public class OpenWebNetBindingConstants {
 
     // ## Groups
     public static final Set<ThingTypeUID> DEVICE_SUPPORTED_THING_TYPES = Stream
-            .of(LIGHTING_SUPPORTED_THING_TYPES, AUTOMATION_SUPPORTED_THING_TYPES,
+            .of(LIGHTING_SUPPORTED_THING_TYPES, LIGHTING_GROUP_SUPPORTED_THING_TYPES, AUTOMATION_SUPPORTED_THING_TYPES,
                     THERMOREGULATION_SUPPORTED_THING_TYPES, ENERGY_MANAGEMENT_SUPPORTED_THING_TYPES,
                     SCENARIO_SUPPORTED_THING_TYPES, SCENARIO_BASIC_SUPPORTED_THING_TYPES, AUX_SUPPORTED_THING_TYPES,
                     ALARM_SUPPORTED_THING_TYPES, GENERIC_SUPPORTED_THING_TYPES)
index cc7aed40ecea068faa67b089f0e116650e14f2e7..bec7a46e5b6edaba5cfa9f25293bc9954c171363 100644 (file)
@@ -22,6 +22,7 @@ import org.openhab.binding.openwebnet.internal.handler.OpenWebNetAuxiliaryHandle
 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.OpenWebNetLightingGroupHandler;
 import org.openhab.binding.openwebnet.internal.handler.OpenWebNetLightingHandler;
 import org.openhab.binding.openwebnet.internal.handler.OpenWebNetScenarioBasicHandler;
 import org.openhab.binding.openwebnet.internal.handler.OpenWebNetScenarioHandler;
@@ -66,6 +67,9 @@ public class OpenWebNetHandlerFactory extends BaseThingHandlerFactory {
         } else if (OpenWebNetLightingHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) {
             logger.debug("creating NEW LIGHTING Handler --- {}", thing.getUID());
             return new OpenWebNetLightingHandler(thing);
+        } else if (OpenWebNetLightingGroupHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) {
+            logger.debug("creating NEW LIGHTING GROUP Handler --- {}", thing.getUID());
+            return new OpenWebNetLightingGroupHandler(thing);
         } else if (OpenWebNetAutomationHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) {
             logger.debug("creating NEW AUTOMATION Handler --- {}", thing.getUID());
             return new OpenWebNetAutomationHandler(thing);
index 553335011393720f4b706c5608c70e8481f469b0..6157d25fffee07e1654ff3848dd3428a67969139 100644 (file)
@@ -29,6 +29,7 @@ import org.openwebnet4j.OpenDeviceType;
 import org.openwebnet4j.message.BaseOpenMessage;
 import org.openwebnet4j.message.Where;
 import org.openwebnet4j.message.WhereAlarm;
+import org.openwebnet4j.message.WhereLightAutom;
 import org.openwebnet4j.message.WhereThermo;
 import org.openwebnet4j.message.WhereZigBee;
 import org.openwebnet4j.message.Who;
@@ -41,7 +42,7 @@ import org.slf4j.LoggerFactory;
  * The {@link OpenWebNetDeviceDiscoveryService} is responsible for discovering
  * OpenWebNet devices connected to a bridge/gateway
  *
- * @author Massimo Valla - Initial contribution
+ * @author Massimo Valla - Initial contribution. Discovery of BUS light Group
  * @author Andrea Conte - Energy management, Thermoregulation
  * @author Gilberto Cocchi - Thermoregulation
  * @author Giovanni Fabiani - Aux support
@@ -56,7 +57,8 @@ public class OpenWebNetDeviceDiscoveryService extends AbstractThingHandlerDiscov
 
     private @NonNullByDefault({}) ThingUID bridgeUID;
 
-    private boolean cuFound = false;
+    private boolean thermoCUFound = false;
+    private boolean lightFound = false;
 
     public OpenWebNetDeviceDiscoveryService() {
         super(OpenWebNetBridgeHandler.class, SUPPORTED_THING_TYPES, SEARCH_TIME_SEC);
@@ -71,7 +73,9 @@ public class OpenWebNetDeviceDiscoveryService extends AbstractThingHandlerDiscov
     protected void startScan() {
         logger.info("------ SEARCHING for DEVICES on bridge '{}' ({}) ...", thingHandler.getThing().getLabel(),
                 bridgeUID);
-        cuFound = false;
+
+        thermoCUFound = false;
+        lightFound = false;
         thingHandler.searchDevices();
     }
 
@@ -223,14 +227,20 @@ public class OpenWebNetDeviceDiscoveryService extends AbstractThingHandlerDiscov
         }
 
         String ownId = thingHandler.ownIdFromWhoWhere(deviceWho, w);
-        if (OpenWebNetBindingConstants.THING_TYPE_BUS_ON_OFF_SWITCH.equals(thingTypeUID)) {
+        if (OpenWebNetBindingConstants.THING_TYPE_BUS_ON_OFF_SWITCH.equals(thingTypeUID)
+                || OpenWebNetBindingConstants.THING_TYPE_BUS_DIMMER.equals(thingTypeUID)) {
+            WhereLightAutom wla = (WhereLightAutom) w;
+            discoverBUSGroups(deviceWho, wla);
+            if (wla.isGroup()) { // group thing has been already discovered in previous line
+                return;
+            }
             if (thingHandler.getRegisteredDevice(ownId) != null) {
                 logger.debug("dimmer/switch with WHERE={} already registered, skipping this discovery result", w);
                 return;
             }
         }
 
-        String tId = thingHandler.thingIdFromWhere(w);
+        String tId = thingHandler.thingIdFromWhoWhere(deviceWho, w);
         ThingUID thingUID = new ThingUID(thingTypeUID, bridgeUID, tId);
 
         DiscoveryResult discoveryResult = null;
@@ -246,7 +256,7 @@ public class OpenWebNetDeviceDiscoveryService extends AbstractThingHandlerDiscov
 
         // detect Thermo CU type
         if (OpenWebNetBindingConstants.THING_TYPE_BUS_THERMO_CU.equals(thingTypeUID)) {
-            cuFound = true;
+            thermoCUFound = true;
             logger.debug("CU found: {}", w);
             if (w.value().charAt(0) == '#') { // 99-zone CU
                 thingLabel += " 99-zone";
@@ -258,7 +268,7 @@ public class OpenWebNetDeviceDiscoveryService extends AbstractThingHandlerDiscov
                 logger.debug("@@@@ THERMO CU found 4-zone: where={}, ownId={}, whereConfig={}", w, ownId, whereConfig);
             }
         } else if (OpenWebNetBindingConstants.THING_TYPE_BUS_THERMO_ZONE.equals(thingTypeUID)) {
-            if (cuFound) {
+            if (thermoCUFound) {
                 // set param standalone = false for thermo zone
                 properties.put(OpenWebNetBindingConstants.CONFIG_PROPERTY_STANDALONE, false);
             }
@@ -292,6 +302,48 @@ public class OpenWebNetDeviceDiscoveryService extends AbstractThingHandlerDiscov
         thingDiscovered(discoveryResult);
     }
 
+    private void discoverBUSGroups(Who deviceWho, WhereLightAutom where) {
+        if (!lightFound) {
+            lightFound = true;
+            createGroupDiscoveryResult(deviceWho, (WhereLightAutom) WhereLightAutom.GENERAL);
+        }
+        if (where.isGroup()) {
+            createGroupDiscoveryResult(deviceWho, where);
+        } else {
+            createGroupDiscoveryResult(deviceWho, new WhereLightAutom(Integer.toString(where.getArea())));
+        }
+    }
+
+    private void createGroupDiscoveryResult(Who deviceWho, WhereLightAutom where) {
+        Map<String, Object> properties = new HashMap<>(2);
+        String ownId = thingHandler.ownIdFromWhoWhere(deviceWho, where);
+        String tId = thingHandler.thingIdFromWhoWhere(deviceWho, where);
+
+        String thingLabel = OpenWebNetBindingConstants.THING_LABEL_BUS_LIGHT_GROUP;
+
+        if (where.isGeneral()) {
+            thingLabel = "General " + thingLabel;
+        } else if (where.isArea()) {
+            thingLabel = "Area " + where.getArea() + " " + thingLabel;
+        } else {
+            thingLabel += " " + where.value();
+        }
+
+        ThingUID thingUID = new ThingUID(OpenWebNetBindingConstants.THING_TYPE_BUS_LIGHT_GROUP, bridgeUID, tId);
+        String whereConfig = where.value();
+
+        properties.put(OpenWebNetBindingConstants.CONFIG_PROPERTY_WHERE, whereConfig);
+        properties.put(OpenWebNetBindingConstants.PROPERTY_OWNID, ownId);
+
+        thingLabel = thingLabel + " (WHERE=" + whereConfig + ")";
+
+        DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID)
+                .withThingType(OpenWebNetBindingConstants.THING_TYPE_BUS_LIGHT_GROUP).withProperties(properties)
+                .withRepresentationProperty(OpenWebNetBindingConstants.PROPERTY_OWNID).withBridge(bridgeUID)
+                .withLabel(thingLabel).build();
+        thingDiscovered(discoveryResult);
+    }
+
     @Override
     public void initialize() {
         thingHandler.deviceDiscoveryService = this;
diff --git a/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/handler/LightAutomHandlersMap.java b/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/handler/LightAutomHandlersMap.java
new file mode 100644 (file)
index 0000000..09e0156
--- /dev/null
@@ -0,0 +1,148 @@
+/**
+ * Copyright (c) 2010-2024 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 java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openwebnet4j.message.WhereLightAutom;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A map to store handlers for lights and automations. The map is organised by
+ * Area.
+ *
+ * @author Massimo Valla - Initial contribution
+ */
+@NonNullByDefault
+public class LightAutomHandlersMap {
+
+    private final Logger logger = LoggerFactory.getLogger(LightAutomHandlersMap.class);
+
+    private Map<Integer, Map<String, OpenWebNetThingHandler>> hndlrsMap;
+    private @Nullable OpenWebNetThingHandler oneHandler = null;
+
+    protected LightAutomHandlersMap() {
+        hndlrsMap = new ConcurrentHashMap<>();
+    }
+
+    protected void add(int area, OpenWebNetThingHandler handler) {
+        if (!hndlrsMap.containsKey(area)) {
+            hndlrsMap.put(area, new ConcurrentHashMap<>());
+        }
+        Map<String, OpenWebNetThingHandler> areaHndlrs = hndlrsMap.get(Integer.valueOf(area));
+        final String handlerOwnId = handler.ownId;
+        if (areaHndlrs != null && handlerOwnId != null) {
+            areaHndlrs.put(handlerOwnId, handler);
+            if (oneHandler == null) {
+                oneHandler = handler;
+            }
+            logger.debug("MAP - Added handler {} to Area {}", handlerOwnId, area);
+            logger.debug("Map: {}", this.toString());
+        }
+    }
+
+    protected void remove(int area, OpenWebNetThingHandler handler) {
+        if (hndlrsMap.containsKey(area)) {
+            Map<String, OpenWebNetThingHandler> areaHndlrs = hndlrsMap.get(Integer.valueOf(area));
+            if (areaHndlrs != null) {
+                boolean removed = areaHndlrs.remove(handler.ownId, handler);
+                OpenWebNetThingHandler oneHandler = this.oneHandler;
+                // if the removed handler was linked by oneHandler, find another one
+                if (removed && oneHandler != null && oneHandler.equals(handler)) {
+                    this.oneHandler = getFirst();
+                }
+                logger.debug("MAP - Removed handler {} from Area {}", handler.ownId, area);
+                logger.debug("Map: {}", this.toString());
+            }
+        }
+    }
+
+    protected @Nullable List<OpenWebNetThingHandler> getAreaHandlers(int area) {
+        Map<String, OpenWebNetThingHandler> areaHndlrs = hndlrsMap.get(area);
+        if (areaHndlrs != null) {
+            List<OpenWebNetThingHandler> list = new ArrayList<OpenWebNetThingHandler>(areaHndlrs.values());
+            return list;
+        } else {
+            return null;
+        }
+    }
+
+    protected @Nullable List<OpenWebNetThingHandler> getAllHandlers() {
+        List<OpenWebNetThingHandler> list = new ArrayList<OpenWebNetThingHandler>();
+        for (Map.Entry<Integer, Map<String, OpenWebNetThingHandler>> entry : hndlrsMap.entrySet()) {
+            Map<String, OpenWebNetThingHandler> innerMap = entry.getValue();
+            for (Map.Entry<String, OpenWebNetThingHandler> innerEntry : innerMap.entrySet()) {
+                OpenWebNetThingHandler hndlr = innerEntry.getValue();
+                list.add(hndlr);
+            }
+        }
+        return list;
+    }
+
+    protected boolean isEmpty() {
+        return oneHandler == null;
+    }
+
+    protected @Nullable OpenWebNetThingHandler getOneHandler() {
+        if (oneHandler == null) {
+            oneHandler = getFirst();
+        }
+        return oneHandler;
+    }
+
+    private @Nullable OpenWebNetThingHandler getFirst() {
+        for (Map.Entry<Integer, Map<String, OpenWebNetThingHandler>> entry : hndlrsMap.entrySet()) {
+            Map<String, OpenWebNetThingHandler> innerMap = entry.getValue();
+            for (Map.Entry<String, OpenWebNetThingHandler> innerEntry : innerMap.entrySet()) {
+                OpenWebNetThingHandler thingHandler = innerEntry.getValue();
+                WhereLightAutom whereLightAutom = (WhereLightAutom) thingHandler.deviceWhere;
+                if (whereLightAutom != null && whereLightAutom.isAPL()) {
+                    return thingHandler;
+                }
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder log = new StringBuilder();
+        log.append("\n---- LightAutomHandlersMap ----");
+        for (Map.Entry<Integer, Map<String, OpenWebNetThingHandler>> entry : hndlrsMap.entrySet()) {
+            log.append("\n- Area: " + entry.getKey() + "\n   -");
+            Map<String, OpenWebNetThingHandler> innerMap = entry.getValue();
+            for (Map.Entry<String, OpenWebNetThingHandler> innerEntry : innerMap.entrySet()) {
+                OpenWebNetThingHandler thingHandler = innerEntry.getValue();
+                log.append(" " + thingHandler.ownId);
+            }
+        }
+        log.append("\n# getAllHandlers: ");
+        List<OpenWebNetThingHandler> allHandlers = getAllHandlers();
+        if (allHandlers != null) {
+            for (OpenWebNetThingHandler singleHandler : allHandlers) {
+                log.append(" " + singleHandler.ownId);
+            }
+        }
+        OpenWebNetThingHandler oneThingHandler = this.getOneHandler();
+        log.append("\n# getOneHandler() = " + (oneThingHandler == null ? "null" : oneThingHandler.ownId));
+        log.append("\n-------------------------------");
+
+        return log.toString();
+    }
+}
index 0bd59ad0dfc6ea50aca79533eb6660f539909455..c1383a8b34f5807a229a3b8ce6b879fa15b8656d 100644 (file)
@@ -272,8 +272,8 @@ public class OpenWebNetAlarmHandler extends OpenWebNetThingHandler {
     }
 
     @Override
-    protected String ownIdPrefix() {
-        return Who.BURGLAR_ALARM.value().toString();
+    protected Who getManagedWho() {
+        return Who.BURGLAR_ALARM;
     }
 
     @Override
index 75642ab5f2ce75866c853098e620ebcc5cdbec86..bff74f20e373ba8ffabf99a1d4d216413a80f70a 100644 (file)
@@ -309,8 +309,8 @@ public class OpenWebNetAutomationHandler extends OpenWebNetThingHandler {
     }
 
     @Override
-    protected String ownIdPrefix() {
-        return Who.AUTOMATION.value().toString();
+    protected Who getManagedWho() {
+        return Who.AUTOMATION;
     }
 
     @Override
index 002ceb4c378f5db6df402219b4d6da27093fbdc2..07c93d530e5f80c73db1a0eebc1823f454436ee9 100644 (file)
@@ -125,7 +125,7 @@ public class OpenWebNetAuxiliaryHandler extends OpenWebNetThingHandler {
     }
 
     @Override
-    protected String ownIdPrefix() {
-        return Who.AUX.value().toString();
+    protected Who getManagedWho() {
+        return Who.AUX;
     }
 }
index f64b8f85b7530b4d685a01927de2a5214863bb48..ac7abe69fd6e4b95a9f6bcc99c736e78ae09130d 100644 (file)
@@ -66,6 +66,7 @@ import org.openwebnet4j.message.Scenario;
 import org.openwebnet4j.message.Thermoregulation;
 import org.openwebnet4j.message.What;
 import org.openwebnet4j.message.Where;
+import org.openwebnet4j.message.WhereLightAutom;
 import org.openwebnet4j.message.WhereZigBee;
 import org.openwebnet4j.message.Who;
 import org.slf4j.Logger;
@@ -108,6 +109,9 @@ public class OpenWebNetBridgeHandler extends ConfigStatusBridgeHandler implement
     private Map<String, @Nullable OpenWebNetThingHandler> registeredDevices = new ConcurrentHashMap<>();
     private Map<String, Long> discoveringDevices = new ConcurrentHashMap<>();
 
+    protected @Nullable LightAutomHandlersMap lightsMap; // a LightAutomHandlersMap storing lights handlers organised by
+                                                         // the AREA they belong to
+
     protected @Nullable OpenGateway gateway;
     private boolean isBusGateway = false;
 
@@ -432,10 +436,52 @@ public class OpenWebNetBridgeHandler extends ConfigStatusBridgeHandler implement
      * @param ownId the device OpenWebNet id
      * @return the registered device Thing handler or null if the id cannot be found
      */
-    public @Nullable OpenWebNetThingHandler getRegisteredDevice(String ownId) {
+    public @Nullable OpenWebNetThingHandler getRegisteredDevice(@Nullable String ownId) {
         return registeredDevices.get(ownId);
     }
 
+    /**
+     * Adds a light handler to the light map for this bridge, grouped by Area
+     *
+     * @param area the light area
+     * @param lightHandler the light handler to be added
+     */
+    protected void addLight(int area, OpenWebNetThingHandler lightHandler) {
+        if (lightsMap == null) {
+            lightsMap = new LightAutomHandlersMap();
+        }
+        LightAutomHandlersMap lm = lightsMap;
+        if (lm != null) {
+            lm.add(area, lightHandler);
+            logger.debug("Added APL {} to lightsMap", lightHandler.ownId);
+        }
+    }
+
+    /**
+     * Remove a light handler to the light map for this bridge
+     *
+     * @param area the light area
+     * @param lightHandler the light handler to be removed
+     */
+    protected void removeLight(int area, OpenWebNetThingHandler lightHandler) {
+        LightAutomHandlersMap lightsMap = this.lightsMap;
+        if (lightsMap != null) {
+            lightsMap.remove(area, lightHandler);
+            logger.debug("Removed APL {} from lightsMap", lightHandler.ownId);
+        }
+    }
+
+    @Nullable
+    protected List<OpenWebNetThingHandler> getAllLights() {
+        LightAutomHandlersMap lightsMap = this.lightsMap;
+        return (lightsMap != null) ? lightsMap.getAllHandlers() : null;
+    }
+
+    @Nullable
+    public LightAutomHandlersMap getLightsMap() {
+        return this.lightsMap;
+    }
+
     private void refreshAllBridgeDevices() {
         logger.debug("--- --- ABOUT TO REFRESH ALL devices for bridge {}", thing.getUID());
         int howMany = 0;
@@ -459,8 +505,7 @@ public class OpenWebNetBridgeHandler extends ConfigStatusBridgeHandler implement
                 }
             } else if (System.currentTimeMillis() - lastRegisteredDeviceTS < REFRESH_ALL_DEVICES_DELAY_MSEC) {
                 // a device has been registered with the bridge just now, let's wait for other
-                // devices: re-schedule
-                // refreshAllDevices
+                // devices: re-schedule refreshAllDevices
                 logger.debug("--- REGISTER device just called... re-scheduling refreshAllBridgeDevices()");
                 refreshAllSchedule = scheduler.schedule(this::refreshAllBridgeDevices, REFRESH_ALL_DEVICES_DELAY_MSEC,
                         TimeUnit.MILLISECONDS);
@@ -525,7 +570,23 @@ public class OpenWebNetBridgeHandler extends ConfigStatusBridgeHandler implement
             return;
         }
 
+        // LIGHTING multiple messages for BUS
+        if (msg instanceof Lighting lmsg && isBusGateway) {
+            WhereLightAutom whereLightAutom = (WhereLightAutom) lmsg.getWhere();
+            if (whereLightAutom != null && (whereLightAutom.isGeneral() || whereLightAutom.isArea())) {
+                LightAutomHandlersMap lightsMap = this.lightsMap;
+                if (lightsMap != null && !lightsMap.isEmpty()) {
+                    OpenWebNetLightingHandler lightingHandler = (OpenWebNetLightingHandler) lightsMap.getOneHandler();
+                    if (lightingHandler != null) {
+                        lightingHandler.handleMultipleMessage(lmsg);
+                    }
+                }
+                return;
+            }
+        }
+
         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 CEN || baseMsg instanceof Auxiliary
@@ -697,7 +758,8 @@ public class OpenWebNetBridgeHandler extends ConfigStatusBridgeHandler implement
      * @return the ownId String
      */
     protected String ownIdFromDeviceWhere(Where where, OpenWebNetThingHandler handler) {
-        return handler.ownIdPrefix() + "." + normalizeWhere(where);
+        Who w = handler.getManagedWho();
+        return w.value().toString() + "." + normalizeWhere(w, where);
     }
 
     /**
@@ -708,7 +770,7 @@ public class OpenWebNetBridgeHandler extends ConfigStatusBridgeHandler implement
      * @return the ownId String
      */
     public String ownIdFromWhoWhere(Who who, Where where) {
-        return who.value() + "." + normalizeWhere(where);
+        return who.value() + "." + normalizeWhere(who, where);
     }
 
     /**
@@ -721,7 +783,7 @@ public class OpenWebNetBridgeHandler extends ConfigStatusBridgeHandler implement
         @Nullable
         Where w = baseMsg.getWhere();
         if (w != null) {
-            return baseMsg.getWho().value() + "." + normalizeWhere(w);
+            return baseMsg.getWho().value() + "." + normalizeWhere(baseMsg.getWho(), w);
         } else if (baseMsg instanceof Alarm) { // null and Alarm
             return baseMsg.getWho().value() + "." + "0"; // Alarm System --> where=0
         } else {
@@ -731,32 +793,39 @@ public class OpenWebNetBridgeHandler extends ConfigStatusBridgeHandler implement
     }
 
     /**
-     * Transform a Where address into a Thing id string
+     * Given a Who and a Where address, return a Thing id string
      *
+     * @param who the Who
      * @param where the Where address
      * @return the thing Id string
      */
-    public String thingIdFromWhere(Where where) {
-        return normalizeWhere(where); // '#' cannot be used in ThingUID;
+    public String thingIdFromWhoWhere(Who who, Where where) {
+        return normalizeWhere(who, where); // '#' cannot be used in ThingUID;
     }
 
     /**
-     * Normalize a Where address to generate ownId and Thing id
+     * Normalize, based on Who, a Where address. Used to generate ownId and Thing id
      *
+     * @param who the Who
      * @param where the Where address
      * @return the normalized address as String
      */
-    public String normalizeWhere(Where where) {
+    public String normalizeWhere(Who who, Where where) {
         String str = where.value();
         if (where instanceof WhereZigBee whereZigBee) {
             str = whereZigBee.valueWithUnit(WhereZigBee.UNIT_ALL); // 76543210X#9 --> 765432100#9
         } else {
             if (str.indexOf("#4#") == -1) { // skip APL#4#bus case
-                if (str.indexOf('#') == 0) { // Thermo central unit (#0) or zone via central unit (#Z, Z=[1-99]) --> Z,
-                                             // Alarm Zone (#Z) --> Z
-                    str = str.substring(1);
-                } else if (str.indexOf('#') > 0 && str.charAt(0) != '0') { // Thermo zone Z and actuator N (Z#N,
-                                                                           // Z=[1-99], N=[1-9]) --> Z
+                if (str.indexOf('#') == 0) {
+                    if (who.equals(Who.THERMOREGULATION) || who.equals(Who.THERMOREGULATION_DIAGNOSTIC)
+                            || who.equals(Who.BURGLAR_ALARM)) {
+                        // Thermo central unit (#0) or zone via central unit (#Z, Z=[1-99]) --> Z
+                        // or Alarm zone #Z --> Z
+                        str = str.substring(1);
+                    } // else leave the initial hash (for example for LightAutomWhere GROUPs #GR -->
+                      // hGR)
+                } else if (str.indexOf('#') > 0 && str.charAt(0) != '0') {
+                    // Thermo zone Z and actuator N (Z#N, Z=[1-99], N=[1-9]) --> Z)
                     str = str.substring(0, str.indexOf('#'));
                 }
             }
index 2b34b98a483fdbf3c5bba951df99acfb897c23db..3106fcda69ddbd273c4e79ce8a2c00880ae4d47c 100644 (file)
@@ -213,8 +213,8 @@ public class OpenWebNetEnergyHandler extends OpenWebNetThingHandler {
     }
 
     @Override
-    protected String ownIdPrefix() {
-        return Who.ENERGY_MANAGEMENT.value().toString();
+    protected Who getManagedWho() {
+        return Who.ENERGY_MANAGEMENT;
     }
 
     @Override
index 4c0a66742e28bb1f50fb24a46b2c217bd2870469..a0cecdb75bac22d96e8b71f1e6717d47229e774c 100644 (file)
@@ -23,6 +23,7 @@ import org.openhab.core.types.Command;
 import org.openwebnet4j.message.BaseOpenMessage;
 import org.openwebnet4j.message.Where;
 import org.openwebnet4j.message.WhereLightAutom;
+import org.openwebnet4j.message.Who;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -69,8 +70,8 @@ public class OpenWebNetGenericHandler extends OpenWebNetThingHandler {
     }
 
     @Override
-    protected String ownIdPrefix() {
-        return "G";
+    protected Who getManagedWho() {
+        return Who.UNKNOWN;
     }
 
     @Override
diff --git a/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/handler/OpenWebNetLightingGroupHandler.java b/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/handler/OpenWebNetLightingGroupHandler.java
new file mode 100644 (file)
index 0000000..0e34618
--- /dev/null
@@ -0,0 +1,208 @@
+/**
+ * Copyright (c) 2010-2024 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.HashSet;
+import java.util.Set;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.openwebnet.internal.OpenWebNetBindingConstants;
+import org.openhab.core.library.types.OnOffType;
+import org.openhab.core.thing.ChannelUID;
+import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingStatus;
+import org.openhab.core.thing.ThingStatusDetail;
+import org.openhab.core.thing.ThingTypeUID;
+import org.openhab.core.types.Command;
+import org.openwebnet4j.communication.OWNException;
+import org.openwebnet4j.message.BaseOpenMessage;
+import org.openwebnet4j.message.Lighting;
+import org.openwebnet4j.message.Where;
+import org.openwebnet4j.message.WhereLightAutom;
+import org.openwebnet4j.message.Who;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@link OpenWebNetLightingGroupHandler} is responsible for handling commands/messages for a Lighting OpenWebNet
+ * group.
+ * It extends the abstract {@link OpenWebNetThingHandler}.
+ *
+ * @author Massimo Valla - Initial contribution.
+ */
+@NonNullByDefault
+public class OpenWebNetLightingGroupHandler extends OpenWebNetThingHandler {
+
+    private final Logger logger = LoggerFactory.getLogger(OpenWebNetLightingGroupHandler.class);
+
+    public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = OpenWebNetBindingConstants.LIGHTING_GROUP_SUPPORTED_THING_TYPES;
+
+    protected Set<String> listOn = new HashSet<String>();
+
+    public OpenWebNetLightingGroupHandler(Thing thing) {
+        super(thing);
+    }
+
+    @Override
+    protected void requestChannelState(ChannelUID channel) {
+        super.requestChannelState(channel);
+        Where deviceWhere = this.deviceWhere;
+        if (deviceWhere != null) {
+            try {
+                send(Lighting.requestStatus(deviceWhere.value()));
+            } catch (OWNException e) {
+                updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
+            }
+        }
+    }
+
+    @Override
+    protected void refreshDevice(boolean refreshAll) {
+        if (refreshAll) {
+            logger.debug("--- refreshDevice() : refreshing GENERAL... ({})", thing.getUID());
+            try {
+                send(Lighting.requestStatus(WhereLightAutom.GENERAL.value()));
+            } catch (OWNException e) {
+                logger.warn("Excpetion while requesting all devices refresh: {}", e.getMessage());
+            }
+        } else {
+            logger.debug("--- refreshDevice() : refreshing SINGLE... ({})", thing.getUID());
+
+            requestChannelState(new ChannelUID(thing.getUID(), CHANNEL_SWITCH_01));
+        }
+    }
+
+    @Override
+    protected void handleChannelCommand(ChannelUID channel, Command command) {
+        switch (channel.getId()) {
+            case CHANNEL_SWITCH:
+                handleSwitchCommand(channel, command);
+                break;
+            default: {
+                logger.warn("Unsupported ChannelUID {}", channel);
+            }
+        }
+    }
+
+    /**
+     * Handles Lighting switch command for a channel
+     *
+     * @param channel the channel
+     * @param command the command
+     */
+    private void handleSwitchCommand(ChannelUID channel, Command command) {
+        logger.debug("handleSwitchCommand() (command={} - channel={})", command, channel);
+        Where deviceWhere = this.deviceWhere;
+        if (command instanceof OnOffType && deviceWhere != null) {
+            try {
+                if (OnOffType.ON.equals(command)) {
+                    send(Lighting.requestTurnOn(deviceWhere.value()));
+                } else if (OnOffType.OFF.equals(command)) {
+                    send(Lighting.requestTurnOff(deviceWhere.value()));
+                }
+            } catch (OWNException e) {
+                logger.warn("Exception while processing command {}: {}", command, e.getMessage());
+            }
+        } else {
+            logger.warn("Unsupported command: {}", command);
+        }
+    }
+
+    @Override
+    protected Who getManagedWho() {
+        return Who.LIGHTING;
+    }
+
+    protected void handlePropagatedMessage(Lighting lmsg, @Nullable String oId) {
+        logger.debug("handlePropagatedMessage({}) for thing: {}", lmsg, thing.getUID());
+
+        WhereLightAutom deviceWhere = (WhereLightAutom) this.deviceWhere;
+        if (deviceWhere != null && oId != null) {
+            int sizeBefore = listOn.size();
+            if (!lmsg.isOff()) {
+                if (listOn.add(oId)) {
+                    logger.debug("ADDED {} to listOn for {}", oId, deviceWhere);
+                }
+            } else {
+                if (listOn.remove(oId)) {
+                    logger.debug("REMOVED {} from listOn for {}", oId, deviceWhere);
+                }
+            }
+            logger.debug("listOn for {}: {}", deviceWhere, listOn);
+
+            boolean listOnChanged = false;
+
+            if (!listOn.isEmpty()) {
+                // some light still on
+                logger.debug("some light ON... switching group {} to ON", deviceWhere);
+                updateState(CHANNEL_SWITCH, OnOffType.ON);
+                listOnChanged = (sizeBefore == 0);
+            } else {
+                // no light is ON anymore
+                logger.debug("all lights OFF ... switching group {} to OFF ", deviceWhere);
+                updateState(CHANNEL_SWITCH, OnOffType.OFF);
+                listOnChanged = (sizeBefore > 0);
+            }
+            if (listOnChanged && !deviceWhere.isGeneral()) {
+                // Area has changed state, propagate APL msg to GEN handler, if exists
+                OpenWebNetBridgeHandler bridgeHandler = this.bridgeHandler;
+                if (bridgeHandler != null) {
+                    String genOwnId = this.getManagedWho().value() + ".0";
+                    OpenWebNetLightingGroupHandler genHandler = (OpenWebNetLightingGroupHandler) bridgeHandler
+                            .getRegisteredDevice(genOwnId);
+                    if (genHandler != null && this.ownId != null) {
+                        logger.debug("device {} is Propagating msg {} to GEN handler", deviceWhere, lmsg);
+                        genHandler.handlePropagatedMessage(lmsg, this.ownId);
+                    }
+                }
+            }
+
+            handleMessage(lmsg); // to make handler come online when a light of its group comes online
+        }
+    }
+
+    @Override
+    protected void handleMessage(BaseOpenMessage msg) {
+        logger.debug("handleMessage({}) for thing: {}", msg, thing.getUID());
+        super.handleMessage(msg);
+
+        WhereLightAutom w = (WhereLightAutom) deviceWhere;
+        if (w != null && w.isGroup()) {
+            if (((Lighting) msg).isOff()) {
+                updateState(CHANNEL_SWITCH, OnOffType.OFF);
+            } else {
+                updateState(CHANNEL_SWITCH, OnOffType.ON);
+            }
+        }
+    }
+
+    @Override
+    protected Where buildBusWhere(String wStr) throws IllegalArgumentException {
+        return new WhereLightAutom(wStr);
+    }
+
+    @Override
+    public void dispose() {
+        if (this.deviceWhere instanceof WhereLightAutom whereLightAutom) {
+            int area = whereLightAutom.getArea();
+            OpenWebNetBridgeHandler bridgeHandler = this.bridgeHandler;
+            if (bridgeHandler != null) {
+                bridgeHandler.removeLight(area, this);
+            }
+        }
+        super.dispose();
+    }
+}
index 97e3374e788be7350954ce86bc442de6b2e9723d..31f69ea0e8589adb6c3c414fa0266e3eb69d34b5 100644 (file)
@@ -14,6 +14,7 @@ package org.openhab.binding.openwebnet.internal.handler;
 
 import static org.openhab.binding.openwebnet.internal.OpenWebNetBindingConstants.*;
 
+import java.util.List;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
 
@@ -46,7 +47,7 @@ import org.slf4j.LoggerFactory;
  * commands/messages for a Lighting OpenWebNet device.
  * It extends the abstract {@link OpenWebNetThingHandler}.
  *
- * @author Massimo Valla - Initial contribution
+ * @author Massimo Valla - Initial contribution. Added LightAutomHandlersMap.
  */
 @NonNullByDefault
 public class OpenWebNetLightingHandler extends OpenWebNetThingHandler {
@@ -65,19 +66,33 @@ public class OpenWebNetLightingHandler extends OpenWebNetThingHandler {
     private static final int UNKNOWN_STATE = 1000;
 
     private long lastBrightnessChangeSentTS = 0; // timestamp when last brightness change was sent to the device
-
     private long lastStatusRequestSentTS = 0; // timestamp when last status request was sent to the device
-
     private static long lastAllDevicesRefreshTS = 0; // ts when last all device refresh was sent for this handler
-
     private int brightness = UNKNOWN_STATE; // current brightness percent value for this device
-
     private int brightnessBeforeOff = UNKNOWN_STATE; // latest brightness before device was set to off
 
+    @Nullable
+    String areaOwnId = null;
+    int area = -1;
+
     public OpenWebNetLightingHandler(Thing thing) {
         super(thing);
     }
 
+    @Override
+    public void initialize() {
+        super.initialize();
+        OpenWebNetBridgeHandler bridgeHandler = this.bridgeHandler;
+        if (this.deviceWhere instanceof WhereLightAutom whereLightAutom && bridgeHandler != null
+                && bridgeHandler.isBusGateway()) {
+            area = whereLightAutom.getArea();
+            if (area > 0) {
+                areaOwnId = this.getManagedWho().value() + "." + area;
+            }
+            bridgeHandler.addLight(area, this);
+        }
+    }
+
     @Override
     protected void requestChannelState(ChannelUID channel) {
         super.requestChannelState(channel);
@@ -236,20 +251,64 @@ public class OpenWebNetLightingHandler extends OpenWebNetThingHandler {
     }
 
     @Override
-    protected String ownIdPrefix() {
-        return Who.LIGHTING.value().toString();
+    protected Who getManagedWho() {
+        return Who.LIGHTING;
     }
 
     @Override
     protected void handleMessage(BaseOpenMessage msg) {
         logger.debug("handleMessage({}) for thing: {}", msg, thing.getUID());
         super.handleMessage(msg);
+
         ThingTypeUID thingType = thing.getThingTypeUID();
         if (THING_TYPE_ZB_DIMMER.equals(thingType) || THING_TYPE_BUS_DIMMER.equals(thingType)) {
             updateBrightness((Lighting) msg);
         } else {
             updateOnOffState((Lighting) msg);
         }
+
+        OpenWebNetBridgeHandler bridgeHandler = this.bridgeHandler;
+        if (bridgeHandler != null && bridgeHandler.isBusGateway()) {
+            if (deviceWhere instanceof WhereLightAutom whereLightAutom && ownId != null && whereLightAutom.isAPL()) {
+                // Propagate APL msg to AREA handler, if exists
+                OpenWebNetLightingGroupHandler areaHandler = (OpenWebNetLightingGroupHandler) bridgeHandler
+                        .getRegisteredDevice(areaOwnId);
+                if (areaHandler != null) {
+                    logger.debug("Light {} is propagating msg {} to AREA handler {}", whereLightAutom, msg, areaOwnId);
+                    areaHandler.handlePropagatedMessage((Lighting) msg, this.ownId);
+                } else {
+                    // Propagate APL msg to GEN handler, if exists
+                    String genOwnId = this.getManagedWho().value() + ".0";
+                    OpenWebNetLightingGroupHandler genHandler = (OpenWebNetLightingGroupHandler) bridgeHandler
+                            .getRegisteredDevice(genOwnId);
+                    if (genHandler != null) {
+                        logger.debug("Light {} is propagating msg {} to GEN handler", whereLightAutom, msg);
+                        genHandler.handlePropagatedMessage((Lighting) msg, this.ownId);
+                    }
+                }
+            }
+        }
+    }
+
+    protected void handleMultipleMessage(Lighting msg) {
+        logger.debug("handleMultipleMessage {}", msg);
+        WhereLightAutom whereLightAutom = (WhereLightAutom) msg.getWhere();
+        List<OpenWebNetThingHandler> allLightsHandlers = null;
+        OpenWebNetBridgeHandler bridgeHandler = this.bridgeHandler;
+        if (whereLightAutom.isGeneral() && bridgeHandler != null) {
+            allLightsHandlers = bridgeHandler.getAllLights();
+            if (allLightsHandlers != null) {
+                for (OpenWebNetThingHandler handler : allLightsHandlers) {
+                    handler.handleMessage(msg);
+                }
+            }
+        } else if (whereLightAutom.getArea() > 0) {
+            try {
+                send(Lighting.requestStatus(whereLightAutom.getArea() + ""));
+            } catch (OWNException e) {
+                logger.warn("Excpetion while requesting refresh for light area: {}", e.getMessage());
+            }
+        }
     }
 
     /**
@@ -401,4 +460,26 @@ public class OpenWebNetLightingHandler extends OpenWebNetThingHandler {
         }
         throw new OWNException("Cannot select channel from WHERE " + w);
     }
+
+    @Override
+    public void dispose() {
+        OpenWebNetBridgeHandler bridgeHandler = this.bridgeHandler;
+        if (deviceWhere instanceof WhereLightAutom whereLightAutom && bridgeHandler != null
+                && bridgeHandler.isBusGateway()) {
+            int area = whereLightAutom.getArea();
+            if (areaOwnId != null) {
+                // remove light from listOn for Area
+                OpenWebNetLightingGroupHandler areaHandler = (OpenWebNetLightingGroupHandler) bridgeHandler
+                        .getRegisteredDevice(areaOwnId);
+                if (areaHandler != null) {
+                    if (areaHandler.listOn.remove(ownId)) {
+                        logger.debug("Removed {} from listOn for {}", ownId, areaOwnId);
+                    }
+                }
+            }
+            // remove light from lightsMap
+            bridgeHandler.removeLight(area, this);
+        }
+        super.dispose();
+    }
 }
index fe3611ef4729506fe1e7d94efa09bfb64d6a6a37..334f1294aae9256830035cff3995544f1b1abd69 100644 (file)
@@ -48,8 +48,8 @@ public class OpenWebNetScenarioBasicHandler extends OpenWebNetThingHandler {
     }
 
     @Override
-    protected String ownIdPrefix() {
-        return Who.SCENARIO.value().toString();
+    protected Who getManagedWho() {
+        return Who.SCENARIO;
     }
 
     @Override
index fe412291c02a033976edaee9296c73971232f213..f105cb61e8ea1a09194ec7dc5996d972a35a2ea1 100644 (file)
@@ -165,11 +165,11 @@ public class OpenWebNetScenarioHandler extends OpenWebNetThingHandler {
     }
 
     @Override
-    protected String ownIdPrefix() {
+    protected Who getManagedWho() {
         if (isCENPlus || isDryContactIR) {
-            return Who.CEN_PLUS_SCENARIO_SCHEDULER.value().toString();
+            return Who.CEN_PLUS_SCENARIO_SCHEDULER;
         } else {
-            return Who.CEN_SCENARIO_SCHEDULER.value().toString();
+            return Who.CEN_SCENARIO_SCHEDULER;
         }
     }
 
index 2984d571831c01ed0709432731e879b982a107f3..c3552dce2efd8ae956ce6e492512866e3ab08abd 100644 (file)
@@ -164,8 +164,8 @@ public class OpenWebNetThermoregulationHandler extends OpenWebNetThingHandler {
     }
 
     @Override
-    protected String ownIdPrefix() {
-        return Who.THERMOREGULATION.value().toString();
+    protected Who getManagedWho() {
+        return Who.THERMOREGULATION;
     }
 
     private void handleSetFanSpeed(Command command) {
index 763097bf187c038c4d28ca4995fe1fb370e98c48..a4e821e4376ee301b2526def060e7c673fd58bc5 100644 (file)
@@ -42,6 +42,7 @@ import org.openwebnet4j.message.BaseOpenMessage;
 import org.openwebnet4j.message.OpenMessage;
 import org.openwebnet4j.message.Where;
 import org.openwebnet4j.message.WhereZigBee;
+import org.openwebnet4j.message.Who;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -175,15 +176,15 @@ public abstract class OpenWebNetThingHandler extends BaseThingHandler {
      *
      * @param msg the OpenMessage to be sent
      */
-    public @Nullable Response send(OpenMessage msg) throws OWNException {
+    public @Nullable Response send(@Nullable OpenMessage msg) throws OWNException {
         OpenWebNetBridgeHandler bh = bridgeHandler;
-        if (bh != null) {
+        if (msg != null && bh != null) {
             OpenGateway gw = bh.gateway;
             if (gw != null) {
                 return gw.send(msg);
             }
         }
-        logger.warn("Couldn't send message {}: handler or gateway is null", msg);
+        logger.warn("Couldn't send message {}: handler, gateway or message is null", msg);
         return null;
     }
 
@@ -330,10 +331,10 @@ public abstract class OpenWebNetThingHandler extends BaseThingHandler {
     }
 
     /**
-     * Returns a prefix String for ownId specific for each handler.
+     * Returns the Who managed by the handler.
      * To be implemented by sub-classes.
      *
-     * @return
+     * @return Who managed by the handler
      */
-    protected abstract String ownIdPrefix();
+    protected abstract Who getManagedWho();
 }
index 6f973f45dc3f767061655e6488d4f7dd1688ea7b..4a3279b5a61c135e8ee5a296f99c822d596fb657 100644 (file)
@@ -25,6 +25,8 @@ thing-type.openwebnet.bus_energy_meter.label = Energy Meter
 thing-type.openwebnet.bus_energy_meter.description = An OpenWebNet BUS/SCS Energy Meter. BTicino models: F52x
 thing-type.openwebnet.bus_gateway.label = BUS Gateway
 thing-type.openwebnet.bus_gateway.description = This thing allows to connect to an IP BUS/SCS gateway that supports the OpenWebNet protocol (models: F454, MyHOMEServer1, F455, MH200N, F453, etc.)
+thing-type.openwebnet.bus_light_group.label = Switch Group
+thing-type.openwebnet.bus_light_group.description = An OpenWebNet BUS/SCS group of lights/loads (General, Area or Group BUS addressing).
 thing-type.openwebnet.bus_on_off_switch.label = Switch
 thing-type.openwebnet.bus_on_off_switch.description = An OpenWebNet BUS/SCS switch for the control of 1 light/load. BTicino models: xxx/yyyy/etc.
 thing-type.openwebnet.bus_scenario_control.label = Scenario Control
@@ -86,6 +88,8 @@ thing-type.config.openwebnet.bus_gateway.passwd.label = Password
 thing-type.config.openwebnet.bus_gateway.passwd.description = OpenWebNet gateway password (default: 12345)
 thing-type.config.openwebnet.bus_gateway.port.label = Port
 thing-type.config.openwebnet.bus_gateway.port.description = OpenWebNet gateway port (default: 20000)
+thing-type.config.openwebnet.bus_light_group.where.label = OpenWebNet Address (where)
+thing-type.config.openwebnet.bus_light_group.where.description = Example: General (A=GEN) --> where="0", Room 5 (A=AMB PL=5) --> where="5", Group 25 (A=GR PL=25) --> where="#25"
 thing-type.config.openwebnet.bus_on_off_switch.where.label = OpenWebNet Address (where)
 thing-type.config.openwebnet.bus_on_off_switch.where.description = Example: A/PL address: A=1 PL=3 --> where="13". On local bus: where="13#4#01"
 thing-type.config.openwebnet.bus_scenario_control.where.label = OpenWebNet Address (where)
diff --git a/bundles/org.openhab.binding.openwebnet/src/main/resources/OH-INF/thing/BusLightGroup.xml b/bundles/org.openhab.binding.openwebnet/src/main/resources/OH-INF/thing/BusLightGroup.xml
new file mode 100644 (file)
index 0000000..155b5d5
--- /dev/null
@@ -0,0 +1,37 @@
+<?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 controlling a group of lights -->
+       <thing-type id="bus_light_group">
+               <supported-bridge-type-refs>
+                       <bridge-type-ref id="bus_gateway"/>
+               </supported-bridge-type-refs>
+
+               <label>Switch Group</label>
+               <description>An OpenWebNet BUS/SCS group of lights/loads (General, Area or Group BUS addressing).</description>
+
+               <channels>
+                       <channel id="switch" typeId="switch"/>
+               </channels>
+
+               <properties>
+                       <property name="vendor">BTicino/Legrand</property>
+                       <property name="model">BTI-xxxx/yyyy/etc.</property>
+                       <property name="ownDeviceType">1000</property>
+               </properties>
+
+               <representation-property>ownId</representation-property>
+
+               <config-description>
+                       <parameter name="where" type="text" required="true">
+                               <label>OpenWebNet Address (where)</label>
+                               <description>Example: General (A=GEN) --> where="0", Room 5 (A=AMB PL=5) --> where="5", Group 25 (A=GR PL=25) -->
+                                       where="#25"</description>
+                       </parameter>
+               </config-description>
+
+       </thing-type>
+</thing:thing-descriptions>
index 7c141ac864e5c8aede21606260819acfe26110dc..7f5f9393cc783177bded1af7e3f50bcec2bd0196 100644 (file)
@@ -57,6 +57,12 @@ public class OwnIdTest {
      * Zigbee Switch_2u u1  789301201#9     789301200h9         1.789309800h9   789309800h9
      * Zigbee Switch_2u u2  789301202#9     789301200h9         1.789309800h9   789309800h9
      * BUS Switch           51              51                  1.51            51
+     *
+     * BUS Switch Gen       0               0                   1.0             0
+     * BUS Switch Area      5               5                   1.5             5
+     * BUS Switch Group     #25             h25                 1.h25           h25
+     * BUS Autom Group      #25             h25                 2.h25           h25
+     *
      * BUS Local Bus        25#4#01         25h4h01             1.25h4h01       25h4h01
      * BUS Autom            93              93                  2.93            93
      * BUS Thermo zone      1               1                   4.1             1
@@ -71,7 +77,8 @@ public class OwnIdTest {
      * BUS DryContact       399             399                 25.399          399
      * BUS AUX              4               4                   9.4             4
      * BUS Scenario         05              05                  0.05            05
-     * BUS Alarm Zone       #2 or 2         2                   5.2             2
+     * BUS Alarm Zone CU    #2              2                   5.2             2
+     * BUS Alarm Zone       2               2                   5.2             2
      * BUS Alarm silent     0               0                   5.0             0
      * BUS Alarm system     null            0                   5.0             0
      */
@@ -84,8 +91,16 @@ public class OwnIdTest {
         zb_switch_2u_1("*1*1*789301201#9##", Who.fromValue(1), new WhereZigBee("789301201#9"), "789301200h9", "1.789301200h9", "789301200h9"),
         zb_switch_2u_2("*1*1*789301202#9##", Who.fromValue(1),    new WhereZigBee("789301202#9"), "789301200h9", "1.789301200h9", "789301200h9"),
         bus_switch("*1*1*51##", Who.fromValue(1), new WhereLightAutom("51"),"51", "1.51", "51"),
+
+        bus_switch_gen("*1*1*0##", Who.fromValue(1), new WhereLightAutom("0"),"0", "1.0", "0"),
+        bus_switch_area("*1*1*5##", Who.fromValue(1), new WhereLightAutom("5"),"5", "1.5", "5"),
+        bus_switch_group("*1*1*#25##", Who.fromValue(1), new WhereLightAutom("#25"),"h25", "1.h25", "h25"),
+
         bus_localbus("*1*1*25#4#01##",  Who.fromValue(1), new WhereLightAutom("25#4#01"), "25h4h01", "1.25h4h01", "25h4h01"),
         bus_autom("*2*0*93##",Who.fromValue(2),  new WhereLightAutom("93"), "93", "2.93", "93"),
+
+        bus_autom_group("*2*1*#25##", Who.fromValue(2), new WhereLightAutom("#25"),"h25", "2.h25", "h25"),
+
         bus_thermo_zone("*#4*1*0*0020##", Who.fromValue(4),  new WhereThermo("1"),  "1", "4.1", "1"),
         bus_thermo_zone_via_cu("*#4*#1*0*0020##",  Who.fromValue(4), new WhereThermo("#1"), "1", "4.1", "1"),
         bus_thermo_cu_99("*#4*#0##",   Who.fromValue(4), new WhereThermo("#0") ,"0", "4.0", "0"),
@@ -98,7 +113,8 @@ public class OwnIdTest {
         bus_drycontact("*25*32#1*399##", Who.fromValue(25),   new WhereCEN("399"), "399", "25.399", "399"),
         bus_aux( "*9*1*4##", Who.fromValue(9),  new WhereAuxiliary("4"),"4","9.4","4"),
         bus_scenario( "*0*2*05##", Who.fromValue(0), new WhereLightAutom("05"), "05","0.05","05"),
-        bus_alarm_zh("*#5*#2##",  Who.fromValue(5), new WhereAlarm("#2"),  "2", "5.2", "2"),
+        bus_alarm_zone_cu("*#5*#2##",  Who.fromValue(5), new WhereAlarm("#2"),  "2", "5.2", "2"),
+        bus_alarm_zone("*#5*2##",  Who.fromValue(5), new WhereAlarm("2"),  "2", "5.2", "2"),
         bus_alarm_silent("*5*2*0##",  Who.fromValue(5), new WhereAlarm("0"),  "0", "5.0", "0"),
         bus_alarm_system( "*5*7*##", Who.fromValue(5),new WhereAlarm("0"), "0", "5.0", "0");
 
@@ -136,13 +152,13 @@ public class OwnIdTest {
         for (int i = 0; i < TEST.values().length; i++) {
             TEST test = TEST.values()[i];
             logger.info("testing {} (who={} where={})", test.msg, test.who, test.where);
-            assertEquals(test.norm, brH.normalizeWhere(test.where));
+            assertEquals(test.norm, brH.normalizeWhere(test.who, test.where));
             assertEquals(test.ownId, brH.ownIdFromWhoWhere(test.who, test.where));
             bmsg = test.msg;
             if (bmsg != null) {
                 assertEquals(test.ownId, brH.ownIdFromMessage(bmsg));
             }
-            assertEquals(test.thingId, brH.thingIdFromWhere(test.where));
+            assertEquals(test.thingId, brH.thingIdFromWhoWhere(test.who, test.where));
         }
     }
 }