[openwebnet] Fixed handling of ZigBee USB gateway and devices (fixes #8915 #8917 #8962) (#9076)

* [openwebnet] Fixed config, handling and discovery of ZigBee devices (fixes #8915 and fixes #8917). Added test for ownID. Now uses openwebnet4j v0.3.2
* [openwebnet] ZigBee: improved handling of wrong 'where' parameter. Updated README. Improved logging.
* [openwebnet] Improved discoveryByActivation to distinguish dimmers from lights. Now using openwebnet4j 0.3.2-1

Signed-off-by: Massimo Valla <mvcode00@gmail.com>
This commit is contained in:
M Valla
2020-11-23 19:21:55 +01:00
committed by GitHub
parent ba98ef30e9
commit 874962e00a
15 changed files with 264 additions and 125 deletions

View File

@@ -9,7 +9,7 @@ The binding supports:
- commands from openHAB and feedback (events) from BUS/SCS and wireless network - commands from openHAB and feedback (events) from BUS/SCS and wireless network
![F454 Gateway](doc/F454_gateway.png) ![F454 Gateway](doc/F454_gateway.png)
![USB ZigBee Gateway](doc/USB_gateway.jpg) ![ZigBee USB Gateway](doc/USB_gateway.jpg)
## Supported Things ## Supported Things
@@ -35,19 +35,19 @@ The following Things and OpenWebNet `WHOs` are supported:
### For BUS/SCS ### For BUS/SCS
| Category | WHO | Thing Type IDs | Description | Status | | Category | WHO | Thing Type IDs | Description | Status |
| -------------------- | :----------: | :---------------------------------: | ----------------------------------------------------------- | ---------------- | | -------------------- | :----------: | :------------------------------------------: | -------------------------------------------------------------- | ---------------- |
| Gateway Management | `13` | `bus_gateway` | Any IP gateway supporting OpenWebNet protocol should work (e.g. F454 / MyHOMEServer1 / MH202 / F455 / MH200N, ...) | Successfully tested: F454, MyHOMEServer1, MyHOME_Screen10, F455, F452, F453AV, MH201, MH202, MH200N. Some connection stability issues/gateway resets reported with MH202 | | Gateway Management | `13` | `bus_gateway` | Any IP gateway supporting OpenWebNet protocol should work (e.g. F454 / MyHOMEServer1 / MH202 / F455 / MH200N, ...) | Successfully tested: F454, MyHOMEServer1, MyHOME_Screen10, F455, F452, F453AV, 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) | | 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 | | Automation | `2` | `bus_automation` | BUS roller shutters, with position feedback and auto-calibration | Successfully tested: LN4672M2 |
### For ZigBee (Radio) ### For ZigBee (Radio)
| Category | WHO | Thing Type IDs | Description | Status | | Category | WHO | Thing Type IDs | Description | Status |
| ---------- | :---: | :-------------------------------: | :-------------------------------------------------------------------: | ------------------------------------ | | -------------------- | :----: | :-------------------------------: | :-------------------------------------------------------------------: | ------------------------------------ |
| Gateway | `13` | `zb_gateway` | Wireless ZigBee USB Gateway (models: BTI-3578 / LG 088328) | Tested: BTI-3578 and LG 088328 | | Gateway Management | `13` | `zb_gateway` | ZigBee USB Gateway (models: BTI-3578 / LG 088328) | Tested: BTI-3578 and LG 088328 |
| Lighting | `1` | `zb_dimmer`, `zb_on_off_switch`, `zb_on_off_switch2u` | ZigBee dimmers, switches and 2-unit switches | Tested: BTI-4591, BTI-3584, BTI-4585 | | Lighting | `1` | `zb_dimmer`, `zb_on_off_switch`, `zb_on_off_switch2u` | ZigBee dimmers, switches and 2-unit switches | Tested: BTI-4591, BTI-3584, BTI-4585 |
| Automation | `2` | `zb_automation` | ZigBee roller shutters | | | Automation | `2` | `zb_automation` | ZigBee roller shutters | |
## Discovery ## Discovery
@@ -64,15 +64,15 @@ For other gateways you can add them manually, see [Thing Configuration](#thing-c
#### Discovery by Activation #### Discovery by Activation
Devices can also be discovered if activated while an Inbox Scan is active: start a new Scan, wait 15-20 seconds and then _while the Scan is still active_ (spinning arrow in Inbox), activate the physical device (for example dim the dimmer) to have it discovered by the binding. BUS devices can also be discovered if activated while an Inbox Scan is active: start a new Scan, wait 15-20 seconds and then _while the Scan is still active_ (spinning arrow in Inbox), activate the physical device (for example dim the dimmer) to have it discovered by the binding.
If a device cannot be discovered automatically it's always possible to add it manually, see [Configuring Devices](#configuring-devices). If a device cannot be discovered automatically it's always possible to add it manually, see [Configuring Devices](#configuring-devices).
### ZigBee Discovery ### ZigBee Discovery
- Zigbee USB gateway discovery is *not supported* at the moment: the gateway thing must be added manually see [Thing Configuration](#thing-configuration) below - ZigBee USB gateway discovery is *not supported* at the moment: the gateway thing must be added manually see [Thing Configuration](#thing-configuration) below
- The ZigBee USB Gateway must be inserted in one of the USB ports of the openHAB computer before discovery is started - The ZigBee USB Gateway must be inserted in one of the USB ports of the openHAB computer before a discovery is started
- ***IMPORTANT NOTE:*** As for other OH2 bindings using the USB/serial ports, on Linux the `openhab` user must be member of the `dialout` group, to be able to use USB/serial port: set the group with the following command: - ***IMPORTANT NOTE:*** As for other openHAB bindings using the USB/serial ports, on Linux the `openhab` user must be member of the `dialout` group to be able to use USB/serial port; set the group with the following command:
``` ```
$ sudo usermod -a -G dialout openhab $ sudo usermod -a -G dialout openhab
@@ -105,12 +105,12 @@ Alternatively the BUS/SCS Gateway thing can be configured using the `.things` fi
### Configuring Wireless ZigBee USB Gateway ### Configuring Wireless ZigBee USB Gateway
To add a ZigBee USB gateway manually using PaperUI: go to *Inbox > "+" > OpenWebNet > click `ADD MANUALLY`* and then select `ZigBee USB Gateway`. To add a ZigBee USB Gateway manually using PaperUI: go to *Inbox > "+" > OpenWebNet > click `ADD MANUALLY`* and then select `ZigBee USB Gateway`.
Configuration parameters are: Configuration parameters are:
- `serialPort` : the serial port where the ZigBee USB Gateway is connected (`String`, *mandatory*) - `serialPort` : the serial port where the ZigBee USB Gateway is connected (`String`, *mandatory*)
- Example: `COM3` - Examples: `/dev/ttyUSB0` (Linux/RaPi), `COM3` (Windows)
### Configuring Devices ### Configuring Devices
@@ -119,20 +119,20 @@ Devices can be discovered automatically from Inbox after a gateway has been conf
Devices can be also added manually from PaperUI. For each device it must be configured: Devices can be also added manually from PaperUI. For each device it must be configured:
- the associated gateway (`Bridge Selection` menu) - the associated gateway (`Bridge Selection` menu)
- the `WHERE` config parameter (`OpenWebNet Device Address`): - the `where` config parameter (`OpenWebNet Device Address`):
- example for BUS/SCS: Point to Point `A=2 PL=4` --> `WHERE="24"` - example for BUS/SCS device with WHERE address Point to Point `A=2 PL=4` --> `where="24"`
- example for BUS/SCS: Point to Point `A=6 PL=4` on local bus --> `WHERE="64#4#01"` - 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: use decimal format address without the UNIT part and network: ZigBee `WHERE=414122201#9` --> `WHERE="4141222"` - 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`.
## Channels ## Channels
Devices support some of the following channels: Devices support some of the following channels:
| Channel Type ID (channel ID) | Item Type | Description | Read/Write | | Channel Type ID (channel ID) | Item Type | Description | Read/Write |
|--------------------------|---------------|-------------------------------------------------------------------------|:----------:| |------------------------------------------------|---------------|---------------------------------------------------------|:----------:|
| `switch` | Switch | To switch the device `ON` and `OFF` | R/W | | `switch` or `switch_01`/`02` for ZigBee | Switch | To switch the device `ON` and `OFF` | R/W |
| `brightness` | Dimmer | To adjust the brightness value (Percent, `ON`, `OFF`) | R/W | | `brightness` | Dimmer | To adjust the brightness value (Percent, `ON`, `OFF`) | R/W |
| `shutter` | Rollershutter | To activate roller shutters (`UP`, `DOWN`, `STOP`, Percent - [see Shutter position](#shutter-position)) | R/W | | `shutter` | Rollershutter | To activate roller shutters (`UP`, `DOWN`, `STOP`, Percent - [see Shutter position](#shutter-position)) | R/W |
### Notes on channels ### Notes on channels
@@ -151,36 +151,47 @@ It's possible to enter a value manually or set `shutterRun=AUTO` (default) to ca
### openwebnet.things: ### openwebnet.things:
BUS gateway and things configuration:
```xtend ```xtend
Bridge openwebnet:bus_gateway:mybridge "MyHOMEServer1" [ host="192.168.1.35", passwd="abcde", port=20000, discoveryByActivation=false ] { 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_on_off_switch LR_switch "Living Room Light" [ where="51" ]
bus_dimmer LR_dimmer "Living Room Dimmer" [ where="25#4#01" ] bus_dimmer LR_dimmer "Living Room Dimmer" [ where="0311#4#01" ]
bus_dimmer LR_dalidimmer "Living Room Dali-Dimmer" [ where="0311#4#01" ] bus_automation LR_shutter "Living Room Shutter" [ where="93", shutterRun="10050"]
bus_automation LR_shutter "Living Room Shutter" [ where="93", shutterRun="10050"]
} }
``` ```
ZigBee USB Gateway and things configuration - for radio devices:
```xtend ```xtend
// ZigBee USB Gateway configuration for radio devices
Bridge openwebnet:zb_gateway:myZBgateway [serialPort="COM3"] { Bridge openwebnet:zb_gateway:myZBgateway [serialPort="COM3"] {
zb_dimmer myzigbeedimmer [ where="123456700#9"] zb_dimmer myZB_dimmer [ where="765432101#9"]
zb_on_off_switch myzigbeeswitch [ where="765432200#9"] zb_on_off_switch myZB_switch [ where="765432201#9"]
zb_on_off_switch2u myZB_2U_switch [ where="765432300#9"]
} }
``` ```
### openwebnet.items: ### openwebnet.items:
Items (Light, Dimmer, etc.) will be discovered by Google Assistant/Alexa/HomeKit if their tags are configured like in the example. Items (Light, Dimmer, etc.) will be discovered by Google Assistant/Alexa/HomeKit if their tags are configured like in the example:
```xtend ```xtend
Switch iLR_switch "Light" <light> (gLivingRoom) [ "Lighting" ] { channel="openwebnet:bus_on_off_switch:mybridge:LR_switch:switch" } Switch iLR_switch "Light" <light> (gLivingRoom) [ "Lighting" ] { channel="openwebnet:bus_on_off_switch:mybridge:LR_switch:switch" }
Dimmer iLR_dimmer "Dimmer [%.0f %%]" <DimmableLight> (gLivingRoom) [ "Lighting" ] { channel="openwebnet:bus_dimmer:mybridge:LR_dimmer:brightness" } Dimmer iLR_dimmer "Dimmer [%.0f %%]" <DimmableLight> (gLivingRoom) [ "Lighting" ] { channel="openwebnet:bus_dimmer:mybridge:LR_dimmer:brightness" }
Dimmer iLR_dalidimmer "Dali-Dimmer [%.0f %%]" <DimmableLight> (gLivingRoom) [ "Lighting" ] { channel="openwebnet:bus_dimmer:mybridge:LR_dalidimmer:brightness" }
/* For Dimmers, use category DimmableLight to have Off/On switch in addition to the Percent slider in PaperUI */ /* For Dimmers, use category DimmableLight to have Off/On switch in addition to the Percent slider in PaperUI */
Rollershutter iLR_shutter "Shutter [%.0f %%]" <rollershutter> (gShutters, gLivingRoom) [ "Blinds" ] { channel="openwebnet:bus_automation:mybridge:LR_shutter:shutter" } Rollershutter iLR_shutter "Shutter [%.0f %%]" <rollershutter> (gShutters, gLivingRoom) [ "Blinds" ] { channel="openwebnet:bus_automation:mybridge:LR_shutter:shutter" }
``` ```
Example items linked to ZigBee devices:
```xtend
Dimmer iDimmer "Dimmer [%.0f %%]" <DimmableLight> (gKitchen) [ "Lighting" ] { channel="openwebnet:zb_dimmer:myZBgateway:myZB_dimmer:brightness" }
Switch iSimpleSwitch "Kitchen Switch" <light> (gKitchen) [ "Lighting" ] { channel="openwebnet:zb_on_off_switch:myZBgateway:myZB_switch:switch_01" }
Switch iSwitch_01 "2U first light" <light> (gKitchen) [ "Lighting" ] { channel="openwebnet:zb_on_off_switch2u:myZBgateway:myZB_2U_switch:switch_01" }
Switch iSwitch_02 "2U second light" <light> (gKitchen) [ "Lighting" ] { channel="openwebnet:zb_on_off_switch2u:myZBgateway:myZB_2U_switch:switch_02" }
```
### openwebnet.sitemap ### openwebnet.sitemap
```xtend ```xtend
@@ -190,7 +201,6 @@ sitemap openwebnet label="OpenWebNet Binding Example Sitemap"
{ {
Default item=iLR_switch icon="light" Default item=iLR_switch icon="light"
Default item=iLR_dimmer icon="light" Default item=iLR_dimmer icon="light"
Default item=iLR_dalidimmer icon="light"
Default item=iLR_shutter Default item=iLR_shutter
} }
} }

View File

@@ -23,7 +23,7 @@
<dependency> <dependency>
<groupId>com.github.openwebnet4j</groupId> <groupId>com.github.openwebnet4j</groupId>
<artifactId>openwebnet4j</artifactId> <artifactId>openwebnet4j</artifactId>
<version>0.3.1</version> <version>0.3.2-1</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>

View File

@@ -329,8 +329,9 @@ public class OpenWebNetAutomationHandler extends OpenWebNetThingHandler {
updateMovingState(MOVING_STATE_STOPPED); updateMovingState(MOVING_STATE_STOPPED);
logger.debug("& {} & CALIBRATION - reached UP, now sending DOWN command...", deviceWhere); logger.debug("& {} & CALIBRATION - reached UP, now sending DOWN command...", deviceWhere);
calibrating = CALIBRATION_ACTIVATED; calibrating = CALIBRATION_ACTIVATED;
if (deviceWhere != null) { Where dw = deviceWhere;
String w = deviceWhere.value(); if (dw != null) {
String w = dw.value();
try { try {
send(Automation.requestMoveDown(w)); send(Automation.requestMoveDown(w));
} catch (OWNException e) { } catch (OWNException e) {

View File

@@ -49,7 +49,9 @@ import org.openwebnet4j.message.FrameException;
import org.openwebnet4j.message.GatewayMgmt; import org.openwebnet4j.message.GatewayMgmt;
import org.openwebnet4j.message.Lighting; import org.openwebnet4j.message.Lighting;
import org.openwebnet4j.message.OpenMessage; import org.openwebnet4j.message.OpenMessage;
import org.openwebnet4j.message.What;
import org.openwebnet4j.message.Where; import org.openwebnet4j.message.Where;
import org.openwebnet4j.message.WhereZigBee;
import org.openwebnet4j.message.Who; import org.openwebnet4j.message.Who;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -70,7 +72,8 @@ public class OpenWebNetBridgeHandler extends ConfigStatusBridgeHandler implement
// ConcurrentHashMap of devices registered to this BridgeHandler // ConcurrentHashMap of devices registered to this BridgeHandler
// association is: ownId (String) -> OpenWebNetThingHandler, with ownId = WHO.WHERE // association is: ownId (String) -> OpenWebNetThingHandler, with ownId = WHO.WHERE
private Map<String, OpenWebNetThingHandler> registeredDevices = new ConcurrentHashMap<>(); private Map<String, @Nullable OpenWebNetThingHandler> registeredDevices = new ConcurrentHashMap<>();
private Map<String, Long> discoveringDevices = new ConcurrentHashMap<>();
protected @Nullable OpenGateway gateway; protected @Nullable OpenGateway gateway;
private boolean isBusGateway = false; private boolean isBusGateway = false;
@@ -119,6 +122,7 @@ public class OpenWebNetBridgeHandler extends ConfigStatusBridgeHandler implement
"Could not connect to gateway before " + GATEWAY_ONLINE_TIMEOUT_SEC + "s"); "Could not connect to gateway before " + GATEWAY_ONLINE_TIMEOUT_SEC + "s");
} }
}, GATEWAY_ONLINE_TIMEOUT_SEC, TimeUnit.SECONDS); }, GATEWAY_ONLINE_TIMEOUT_SEC, TimeUnit.SECONDS);
logger.debug("bridge {} initialization completed", thing.getUID());
} catch (OWNException e) { } catch (OWNException e) {
logger.debug("gw.connect() returned OWNException: {}", e.getMessage()); logger.debug("gw.connect() returned OWNException: {}", e.getMessage());
// status is updated by callback onConnectionError() // status is updated by callback onConnectionError()
@@ -131,11 +135,11 @@ public class OpenWebNetBridgeHandler extends ConfigStatusBridgeHandler implement
* Init a ZigBee gateway based on config * Init a ZigBee gateway based on config
*/ */
private @Nullable OpenGateway initZigBeeGateway() { private @Nullable OpenGateway initZigBeeGateway() {
logger.debug("Initializing ZigBee USB gateway"); logger.debug("Initializing ZigBee USB Gateway");
OpenWebNetZigBeeBridgeConfig zbBridgeConfig = getConfigAs(OpenWebNetZigBeeBridgeConfig.class); OpenWebNetZigBeeBridgeConfig zbBridgeConfig = getConfigAs(OpenWebNetZigBeeBridgeConfig.class);
String serialPort = zbBridgeConfig.getSerialPort(); String serialPort = zbBridgeConfig.getSerialPort();
if (serialPort == null || serialPort.isEmpty()) { if (serialPort == null || serialPort.isEmpty()) {
logger.warn("Cannot connect to gateway. No serial port has been provided in Bridge configuration."); logger.info("Cannot connect ZigBee USB Gateway. No serial port has been provided in Bridge configuration.");
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"@text/offline.conf-error-no-serial-port"); "@text/offline.conf-error-no-serial-port");
return null; return null;
@@ -152,7 +156,7 @@ public class OpenWebNetBridgeHandler extends ConfigStatusBridgeHandler implement
OpenWebNetBusBridgeConfig busBridgeConfig = getConfigAs(OpenWebNetBusBridgeConfig.class); OpenWebNetBusBridgeConfig busBridgeConfig = getConfigAs(OpenWebNetBusBridgeConfig.class);
String host = busBridgeConfig.getHost(); String host = busBridgeConfig.getHost();
if (host == null || host.isEmpty()) { if (host == null || host.isEmpty()) {
logger.warn("Cannot connect to gateway. No host/IP has been provided in Bridge configuration."); logger.info("Cannot connect to BUS Gateway. No host/IP has been provided in Bridge configuration.");
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"@text/offline.conf-error-no-ip-address"); "@text/offline.conf-error-no-ip-address");
return null; return null;
@@ -177,7 +181,7 @@ public class OpenWebNetBridgeHandler extends ConfigStatusBridgeHandler implement
logger.debug("handleCommand (command={} - channel={})", command, channelUID); logger.debug("handleCommand (command={} - channel={})", command, channelUID);
OpenGateway gw = gateway; OpenGateway gw = gateway;
if (gw != null && !gw.isConnected()) { if (gw != null && !gw.isConnected()) {
logger.warn("Gateway is NOT connected, skipping command"); logger.info("Gateway is NOT connected, skipping command");
return; return;
} else { } else {
logger.warn("Channel not supported: channel={}", channelUID); logger.warn("Channel not supported: channel={}", channelUID);
@@ -206,7 +210,7 @@ public class OpenWebNetBridgeHandler extends ConfigStatusBridgeHandler implement
if (gw != null) { if (gw != null) {
gw.closeConnection(); gw.closeConnection();
gw.unsubscribe(this); gw.unsubscribe(this);
logger.debug("gateway {} connection closed and unsubscribed", gw.toString()); logger.debug("Gateway {} connection closed and unsubscribed", gw.toString());
gateway = null; gateway = null;
} }
reconnecting = false; reconnecting = false;
@@ -276,23 +280,54 @@ public class OpenWebNetBridgeHandler extends ConfigStatusBridgeHandler implement
} }
private void discoverByActivation(BaseOpenMessage baseMsg) { private void discoverByActivation(BaseOpenMessage baseMsg) {
logger.debug("BridgeHandler.discoverByActivation() msg={}", baseMsg); logger.debug("discoverByActivation: msg={}", baseMsg);
OpenWebNetDeviceDiscoveryService discService = deviceDiscoveryService; OpenWebNetDeviceDiscoveryService discService = deviceDiscoveryService;
if (discService == null) { if (discService == null) {
logger.warn("discoverByActivation: null OpenWebNetDeviceDiscoveryService, ignoring msg={}", baseMsg); logger.warn("discoverByActivation: null OpenWebNetDeviceDiscoveryService, ignoring msg={}", baseMsg);
return; return;
} }
if (baseMsg instanceof Lighting) { if (baseMsg instanceof Lighting || baseMsg instanceof Automation) { // we support these types only
BaseOpenMessage bmsg = baseMsg;
if (baseMsg instanceof Lighting) {
What what = baseMsg.getWhat();
if (Lighting.WHAT.OFF.equals(what)) { // skipping OFF msg: cannot distinguish dimmer/switch
logger.debug("discoverByActivation: skipping OFF msg: cannot distinguish dimmer/switch");
return;
}
if (Lighting.WHAT.ON.equals(what)) { // if not already done just now, request light status to
// distinguish dimmer from switch
if (discoveringDevices.containsKey(ownIdFromMessage(baseMsg))) {
logger.debug(
"discoverByActivation: we just requested status for this device and it's ON -> it's a switch");
} else {
OpenGateway gw = gateway;
if (gw != null) {
try {
discoveringDevices.put(ownIdFromMessage(baseMsg),
Long.valueOf(System.currentTimeMillis()));
gw.send(Lighting.requestStatus(baseMsg.getWhere().value()));
return;
} catch (OWNException e) {
logger.warn("discoverByActivation: Exception while requesting light state: {}",
e.getMessage());
return;
}
}
}
}
discoveringDevices.remove(ownIdFromMessage(baseMsg));
}
OpenDeviceType type = null; OpenDeviceType type = null;
try { try {
type = baseMsg.detectDeviceType(); type = bmsg.detectDeviceType();
} catch (FrameException e) { } catch (FrameException e) {
logger.warn("Exception while detecting device type: {}", e.getMessage()); logger.warn("Exception while detecting device type: {}", e.getMessage());
} }
if (type != null) { if (type != null) {
discService.newDiscoveryResult(baseMsg.getWhere(), type, baseMsg); discService.newDiscoveryResult(bmsg.getWhere(), type, bmsg);
} else { } else {
logger.debug("discoverByActivation: no device type detected from msg: {}", baseMsg); logger.debug("discoverByActivation: no device type detected from msg: {}", bmsg);
} }
} }
} }
@@ -354,7 +389,7 @@ public class OpenWebNetBridgeHandler extends ConfigStatusBridgeHandler implement
// let's try to get the Thing associated with this message... // let's try to get the Thing associated with this message...
if (baseMsg instanceof Lighting || baseMsg instanceof Automation) { if (baseMsg instanceof Lighting || baseMsg instanceof Automation) {
String ownId = ownIdFromMessage(baseMsg); String ownId = ownIdFromMessage(baseMsg);
logger.debug("ownId={}", ownId); logger.debug("ownIdFromMessage({}) --> {}", baseMsg, ownId);
OpenWebNetThingHandler deviceHandler = registeredDevices.get(ownId); OpenWebNetThingHandler deviceHandler = registeredDevices.get(ownId);
if (deviceHandler == null) { if (deviceHandler == null) {
OpenGateway gw = gateway; OpenGateway gw = gateway;
@@ -384,7 +419,7 @@ public class OpenWebNetBridgeHandler extends ConfigStatusBridgeHandler implement
return; return;
} }
if (gw instanceof USBGateway) { if (gw instanceof USBGateway) {
logger.info("------------------- CONNECTED to USB (ZigBee) gateway - USB port: {}", logger.info("------------------- CONNECTED to ZigBee USB gateway - USB port: {}",
((USBGateway) gw).getSerialPortName()); ((USBGateway) gw).getSerialPortName());
} else { } else {
logger.info("------------------- CONNECTED to BUS gateway '{}' ({}:{})", thing.getUID(), logger.info("------------------- CONNECTED to BUS gateway '{}' ({}:{})", thing.getUID(),
@@ -482,25 +517,24 @@ public class OpenWebNetBridgeHandler extends ConfigStatusBridgeHandler implement
} }
/** /**
* Return a ownId string (=WHO.WHERE) from a deviceWhere thing config parameter (already normalized) and its * Return a ownId string (=WHO.WHERE) from the device Where address and handler
* handler.
* *
* @param deviceWhere the device WHERE config parameter * @param where the Where address (to be normalized)
* @param handler the thing handler * @param handler the device handler
* @return the ownId * @return the ownId String
*/ */
protected String ownIdFromDeviceWhere(@Nullable String deviceWhere, OpenWebNetThingHandler handler) { protected String ownIdFromDeviceWhere(Where where, OpenWebNetThingHandler handler) {
return handler.ownIdPrefix() + "." + deviceWhere; return handler.ownIdPrefix() + "." + normalizeWhere(where);
} }
/** /**
* Returns a ownId string (=WHO.WHERE) from a Where address and Who * Returns a ownId string (=WHO.WHERE) from a Who and Where address
* *
* @param where the Where address (to be normalized)
* @param who the Who * @param who the Who
* @return the ownId * @param where the Where address (to be normalized)
* @return the ownId String
*/ */
public String ownIdFromWhoWhere(Where where, Who who) { public String ownIdFromWhoWhere(Who who, Where where) {
return who.value() + "." + normalizeWhere(where); return who.value() + "." + normalizeWhere(where);
} }
@@ -510,45 +544,39 @@ public class OpenWebNetBridgeHandler extends ConfigStatusBridgeHandler implement
* @param baseMsg the BaseOpenMessage * @param baseMsg the BaseOpenMessage
* @return the ownId String * @return the ownId String
*/ */
private String ownIdFromMessage(BaseOpenMessage baseMsg) { public String ownIdFromMessage(BaseOpenMessage baseMsg) {
return baseMsg.getWho().value() + "." + normalizeWhere(baseMsg.getWhere()); return baseMsg.getWho().value() + "." + normalizeWhere(baseMsg.getWhere());
} }
/** /**
* Transform a Where address into a Thing id string based on bridge type (BUS/USB ZigBee). * Transform a Where address into a Thing id string
* '#' in WHERE are changed to 'h'
* *
* @param where the Where address * @param where the Where address
* @return the thing Id * @return the thing Id string
*/ */
public String thingIdFromWhere(Where where) { public String thingIdFromWhere(Where where) {
return normalizeWhere(where).replace('#', 'h'); // '#' cannot be used in ThingUID; return normalizeWhere(where); // '#' cannot be used in ThingUID;
} }
/** /**
* Normalize a Where address for Thermo and Zigbee devices * Normalize a Where address
* *
* @param where the Where address * @param where the Where address
* @return the normalized address * @return the normalized address as String
*/ */
public String normalizeWhere(Where where) { public String normalizeWhere(Where where) {
String str = ""; String str = where.value();
if (isBusGateway) { if (where instanceof WhereZigBee) {
if (where.value().indexOf('#') < 0) { // no hash present str = ((WhereZigBee) where).valueWithUnit(WhereZigBee.UNIT_ALL); // 76543210X#9 --> 765432100#9
str = where.value();
} else if (where.value().indexOf("#4#") > 0) { // local bus: APL#4#bus
str = where.value();
} else if (where.value().indexOf('#') == 0) { // thermo zone via central unit: #0 or #Z (Z=[1-99]) --> Z
str = where.value().substring(1);
} else if (where.value().indexOf('#') > 0) { // thermo zone and actuator N: Z#N (Z=[1-99], N=[1-9]) -- > Z
str = where.value().substring(0, where.value().indexOf('#'));
} else {
logger.warn("normalizeWhere() unexpected WHERE: {}", where);
str = where.value();
}
return str;
} else { } else {
return where.value(); if (str.indexOf("#4#") > 0) { // local bus: APL#4#bus
// no change needed
} else if (str.indexOf('#') == 0) { // Thermo zone via central unit: #0 or #Z (Z=[1-99]) --> Z
str = str.substring(1);
} else if (str.indexOf('#') > 0) { // Thermo zone and actuator N: Z#N (Z=[1-99], N=[1-9]) --> Z
str = str.substring(0, str.indexOf('#'));
}
} }
return str.replace('#', 'h');
} }
} }

View File

@@ -72,17 +72,22 @@ public abstract class OpenWebNetThingHandler extends BaseThingHandler {
if (!(deviceWhereConfig instanceof String)) { if (!(deviceWhereConfig instanceof String)) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"WHERE parameter in configuration is null or invalid"); "WHERE parameter in configuration is null or invalid");
return;
} else { } else {
String deviceWhereStr = (String) getConfig().get(CONFIG_PROPERTY_WHERE); String deviceWhereStr = (String) getConfig().get(CONFIG_PROPERTY_WHERE);
Where w; Where w;
if (brH.isBusGateway()) { try {
w = new WhereLightAutom(deviceWhereStr); if (brH.isBusGateway()) {
} else { w = new WhereLightAutom(deviceWhereStr);
w = new WhereZigBee(deviceWhereStr); } else {
w = new WhereZigBee(deviceWhereStr);
}
} catch (IllegalArgumentException ia) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"WHERE parameter in configuration is invalid");
return;
} }
deviceWhere = w; deviceWhere = w;
final String oid = brH.ownIdFromDeviceWhere(w.value(), this); final String oid = brH.ownIdFromDeviceWhere(w, this);
ownId = oid; ownId = oid;
Map<String, String> properties = editProperties(); Map<String, String> properties = editProperties();
properties.put(PROPERTY_OWNID, oid); properties.put(PROPERTY_OWNID, oid);
@@ -91,11 +96,11 @@ public abstract class OpenWebNetThingHandler extends BaseThingHandler {
logger.debug("associated thing to bridge with ownId={}", ownId); logger.debug("associated thing to bridge with ownId={}", ownId);
updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.NONE, "waiting state update..."); updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.NONE, "waiting state update...");
} }
return;
} }
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"No bridge associated, please assign a bridge in thing configuration.");
} }
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"No bridge associated, please assign a bridge in thing configuration.");
} }
@Override @Override
@@ -105,8 +110,12 @@ public abstract class OpenWebNetThingHandler extends BaseThingHandler {
if (handler != null) { if (handler != null) {
OpenGateway gw = handler.gateway; OpenGateway gw = handler.gateway;
if (gw != null && !gw.isConnected()) { if (gw != null && !gw.isConnected()) {
logger.info("Cannot handle command {}:{} for {}: gateway is not connected", channel, command, logger.info("Cannot handle {} command for {}: gateway is not connected", command, getThing().getUID());
getThing().getUID()); return;
}
if (deviceWhere == null) {
logger.info("Cannot handle {} command for {}: 'where' parameter is not configured or is invalid",
command, getThing().getUID());
return; return;
} }
if (command instanceof RefreshType) { if (command instanceof RefreshType) {

View File

@@ -139,7 +139,7 @@ public class OpenWebNetDeviceDiscoveryService extends AbstractDiscoveryService
} }
} }
String ownId = bridgeHandler.ownIdFromWhoWhere(where, deviceWho); String ownId = bridgeHandler.ownIdFromWhoWhere(deviceWho, where);
if (thingTypeUID == OpenWebNetBindingConstants.THING_TYPE_BUS_ON_OFF_SWITCH) { if (thingTypeUID == OpenWebNetBindingConstants.THING_TYPE_BUS_ON_OFF_SWITCH) {
if (bridgeHandler.getRegisteredDevice(ownId) != null) { if (bridgeHandler.getRegisteredDevice(ownId) != null) {
logger.debug("dimmer/switch with WHERE={} already registered, skipping this discovery result", where); logger.debug("dimmer/switch with WHERE={} already registered, skipping this discovery result", where);
@@ -152,27 +152,26 @@ public class OpenWebNetDeviceDiscoveryService extends AbstractDiscoveryService
DiscoveryResult discoveryResult = null; DiscoveryResult discoveryResult = null;
String whereLabel = where.value(); String whereConfig = where.value();
if (where instanceof WhereZigBee && WhereZigBee.UNIT_02.equals(((WhereZigBee) where).getUnit())) { if (where instanceof WhereZigBee && WhereZigBee.UNIT_02.equals(((WhereZigBee) where).getUnit())) {
logger.debug("UNIT=02 found (WHERE={})", where); logger.debug("UNIT=02 found (WHERE={}) -> will remove previous result if exists", where);
logger.debug("will remove previous result if exists");
thingRemoved(thingUID); // remove previously discovered thing thingRemoved(thingUID); // remove previously discovered thing
// re-create thingUID with new type // re-create thingUID with new type
thingTypeUID = OpenWebNetBindingConstants.THING_TYPE_ZB_ON_OFF_SWITCH_2UNITS; thingTypeUID = OpenWebNetBindingConstants.THING_TYPE_ZB_ON_OFF_SWITCH_2UNITS;
thingLabel = OpenWebNetBindingConstants.THING_LABEL_ZB_ON_OFF_SWITCH_2UNITS; thingLabel = OpenWebNetBindingConstants.THING_LABEL_ZB_ON_OFF_SWITCH_2UNITS;
thingUID = new ThingUID(thingTypeUID, bridgeUID, tId); thingUID = new ThingUID(thingTypeUID, bridgeUID, tId);
whereLabel = whereLabel.replace("02#", "00#"); // replace unit '02' with all unit '00' whereConfig = ((WhereZigBee) where).valueWithUnit(WhereZigBee.UNIT_ALL); // replace unit '02' with '00'
logger.debug("UNIT=02, switching type from {} to {}", logger.debug("UNIT=02, switching type from {} to {}",
OpenWebNetBindingConstants.THING_TYPE_ZB_ON_OFF_SWITCH, OpenWebNetBindingConstants.THING_TYPE_ZB_ON_OFF_SWITCH,
OpenWebNetBindingConstants.THING_TYPE_ZB_ON_OFF_SWITCH_2UNITS); OpenWebNetBindingConstants.THING_TYPE_ZB_ON_OFF_SWITCH_2UNITS);
} }
Map<String, Object> properties = new HashMap<>(2); Map<String, Object> properties = new HashMap<>(2);
properties.put(OpenWebNetBindingConstants.CONFIG_PROPERTY_WHERE, bridgeHandler.normalizeWhere(where)); properties.put(OpenWebNetBindingConstants.CONFIG_PROPERTY_WHERE, whereConfig);
properties.put(OpenWebNetBindingConstants.PROPERTY_OWNID, ownId); properties.put(OpenWebNetBindingConstants.PROPERTY_OWNID, ownId);
if (thingTypeUID == OpenWebNetBindingConstants.THING_TYPE_GENERIC_DEVICE) { if (thingTypeUID == OpenWebNetBindingConstants.THING_TYPE_GENERIC_DEVICE) {
thingLabel = thingLabel + " (WHO=" + deviceWho + ", WHERE=" + whereLabel + ")"; thingLabel = thingLabel + " (WHO=" + deviceWho + ", WHERE=" + whereConfig + ")";
} else { } else {
thingLabel = thingLabel + " (WHERE=" + whereLabel + ")"; thingLabel = thingLabel + " (WHERE=" + whereConfig + ")";
} }
discoveryResult = DiscoveryResultBuilder.create(thingUID).withThingType(thingTypeUID).withProperties(properties) discoveryResult = DiscoveryResultBuilder.create(thingUID).withThingType(thingTypeUID).withProperties(properties)
.withRepresentationProperty(OpenWebNetBindingConstants.PROPERTY_OWNID).withBridge(bridgeUID) .withRepresentationProperty(OpenWebNetBindingConstants.PROPERTY_OWNID).withBridge(bridgeUID)

View File

@@ -38,8 +38,8 @@
</parameter> </parameter>
<parameter name="where" type="text" required="true"> <parameter name="where" type="text" required="true">
<label>OpenWebNet Device Address (WHERE)</label> <label>OpenWebNet Device Address (where)</label>
<description>Example: A/PL address: A=1 PL=3 --> WHERE=13. On local bus: WHERE=13#4#01</description> <description>Example: A/PL address: A=1 PL=3 --> where=13. On local bus: where=13#4#01</description>
</parameter> </parameter>
</config-description> </config-description>
</thing-type> </thing-type>

View File

@@ -27,8 +27,8 @@
<config-description> <config-description>
<parameter name="where" type="text" required="true"> <parameter name="where" type="text" required="true">
<label>OpenWebNet Device Address (WHERE)</label> <label>OpenWebNet Device Address (where)</label>
<description>Example: A/PL address: A=1 PL=3 --> WHERE=13. On local bus: WHERE=13#4#01</description> <description>Example: A/PL address: A=1 PL=3 --> where=13. On local bus: where=13#4#01</description>
</parameter> </parameter>
</config-description> </config-description>

View File

@@ -27,8 +27,8 @@
<config-description> <config-description>
<parameter name="where" type="text" required="true"> <parameter name="where" type="text" required="true">
<label>OpenWebNet Device Address (WHERE)</label> <label>OpenWebNet Device Address (where)</label>
<description>Example: A/PL address: A=1 PL=3 --> WHERE=13. On local bus: WHERE=13#4#01</description> <description>Example: A/PL address: A=1 PL=3 --> where=13. On local bus: where=13#4#01</description>
</parameter> </parameter>
</config-description> </config-description>

View File

@@ -24,7 +24,7 @@
<config-description> <config-description>
<parameter name="where" type="text" required="true"> <parameter name="where" type="text" required="true">
<label>OpenWebNet Device Address (WHERE)</label> <label>OpenWebNet Device Address (where)</label>
<description>It identifies one OpenWebNet device</description> <description>It identifies one OpenWebNet device</description>
</parameter> </parameter>
</config-description> </config-description>

View File

@@ -38,9 +38,8 @@
<default>AUTO</default> <default>AUTO</default>
</parameter> </parameter>
<parameter name="where" type="text"> <parameter name="where" type="text">
<label>OpenWebNet Device Address</label> <label>OpenWebNet Device Address (where)</label>
<description>It identifies one ZigBee device. Use decimal format address without the UNIT part and network: ZigBee <description>It identifies one ZigBee device. Example: 765432101#9</description>
WHERE=414122201#9 -> OpenWebNet Device Address = 4141222</description>
<required>true</required> <required>true</required>
</parameter> </parameter>
</config-description> </config-description>

View File

@@ -27,9 +27,8 @@
<config-description> <config-description>
<parameter name="where" type="text" required="true"> <parameter name="where" type="text" required="true">
<label>OpenWebNet Device Address</label> <label>OpenWebNet Device Address (where)</label>
<description>It identifies one ZigBee device. Use decimal format address without the UNIT part and network: ZigBee <description>It identifies one ZigBee device. Example: 765432101#9</description>
WHERE=414122201#9 -> OpenWebNet Device Address = 4141222</description>
</parameter> </parameter>
</config-description> </config-description>

View File

@@ -27,9 +27,8 @@
<config-description> <config-description>
<parameter name="where" type="text" required="true"> <parameter name="where" type="text" required="true">
<label>OpenWebNet Device Address</label> <label>OpenWebNet Device Address (where)</label>
<description>It identifies one ZigBee device. Use decimal format address without the UNIT part and network: ZigBee <description>It identifies one ZigBee device. Example: 765432101#9</description>
WHERE=414122201#9 -> OpenWebNet Device Address = 4141222</description>
</parameter> </parameter>
</config-description> </config-description>

View File

@@ -28,9 +28,8 @@
<config-description> <config-description>
<parameter name="where" type="text" required="true"> <parameter name="where" type="text" required="true">
<label>OpenWebNet Device Address</label> <label>OpenWebNet Device Address (where)</label>
<description>It identifies one ZigBee device. Use decimal format address without the UNIT part and network: ZigBee <description>It identifies one ZigBee device. Example: 765432100#9 (use unit=00 at the end)</description>
WHERE=414122201#9 -> OpenWebNet Device Address = 4141222</description>
</parameter> </parameter>
</config-description> </config-description>

View File

@@ -0,0 +1,96 @@
/**
* Copyright (c) 2010-2020 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.handler;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.mock;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.junit.jupiter.api.Test;
import org.openhab.core.thing.Bridge;
import org.openwebnet4j.message.Lighting;
import org.openwebnet4j.message.Where;
import org.openwebnet4j.message.WhereLightAutom;
import org.openwebnet4j.message.WhereZigBee;
import org.openwebnet4j.message.Who;
/**
* Test class for {@link OpenWebNetBridgeHandler#ownID} and ThingID calculation using {@link OpenWebNetBridgeHandler}
* methods: normalizeWhere(), ownIdFromWhoWhere(), ownIdFromMessage(), thingIdFromWhere()
*
* @author Massimo Valla - Initial contribution
*/
@NonNullByDefault
public class OwnIdTest {
// @formatter:off
/**
*
* deviceWhere
* (DevAddrParam)
* TYPE WHERE = normalizeWhere() ownId ThingID
* ---------------------------------------------------------------------------------
* Zigbee Switch 789309801#9 789309800h9 1.789309800h9 789309800h9
* 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 Local Bus 25#4#01 25h4h01 1.25h4h01 25h4h01
* BUS Autom 93 93 2.93 93
* BUS Thermo #1 or 1 1 4.1 1
* BUS TempSensor 500 500 4.500 500
* BUS Energy 51 51 18.51 51
* BUS CEN 51 51 15.51 51
* BUS CEN+ 212 212 25.212 212
* BUS DryContact 399 399 25.399 399
*
*/
// @formatter:on
public enum TEST {
zb_switch(new WhereZigBee("789309801#9"), Who.fromValue(1), "789309800h9", "1.789309800h9", "789309800h9"),
zb_switch_2u_1(new WhereZigBee("789301201#9"), Who.fromValue(1), "789301200h9", "1.789301200h9", "789301200h9"),
zb_switch_2u_2(new WhereZigBee("789301202#9"), Who.fromValue(1), "789301200h9", "1.789301200h9", "789301200h9"),
bus_switch(new WhereLightAutom("51"), Who.fromValue(1), "51", "1.51", "51"),
bus_localbus(new WhereLightAutom("25#4#01"), Who.fromValue(1), "25h4h01", "1.25h4h01", "25h4h01");
// bus_thermo("#1", "4", "1", "4.1", "1"),
// bus_tempSensor("500", "4", "500", "4.500", "500"),
// bus_energy("51", "18", "51", "18.51", "51");
public final Where where;
public final Who who;
public final String norm, ownId, thingId;
private TEST(Where where, Who who, String norm, String ownId, String thingId) {
this.where = where;
this.who = who;
this.norm = norm;
this.ownId = ownId;
this.thingId = thingId;
}
}
@Test
public void testOwnId() {
Bridge mockBridge = mock(Bridge.class);
OpenWebNetBridgeHandler brH = new OpenWebNetBridgeHandler(mockBridge);
for (int i = 0; i < TEST.values().length; i++) {
TEST test = TEST.values()[i];
// System.out.println("testing where=" + test.where);
assertEquals(test.norm, brH.normalizeWhere(test.where));
assertEquals(test.ownId, brH.ownIdFromWhoWhere(test.who, test.where));
assertEquals(test.ownId, brH.ownIdFromMessage(Lighting.requestTurnOn(test.where.value())));
assertEquals(test.thingId, brH.thingIdFromWhere(test.where));
}
}
}