[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

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

View File

@@ -49,7 +49,9 @@ import org.openwebnet4j.message.FrameException;
import org.openwebnet4j.message.GatewayMgmt;
import org.openwebnet4j.message.Lighting;
import org.openwebnet4j.message.OpenMessage;
import org.openwebnet4j.message.What;
import org.openwebnet4j.message.Where;
import org.openwebnet4j.message.WhereZigBee;
import org.openwebnet4j.message.Who;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -70,7 +72,8 @@ public class OpenWebNetBridgeHandler extends ConfigStatusBridgeHandler implement
// ConcurrentHashMap of devices registered to this BridgeHandler
// 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;
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");
}
}, GATEWAY_ONLINE_TIMEOUT_SEC, TimeUnit.SECONDS);
logger.debug("bridge {} initialization completed", thing.getUID());
} catch (OWNException e) {
logger.debug("gw.connect() returned OWNException: {}", e.getMessage());
// status is updated by callback onConnectionError()
@@ -131,11 +135,11 @@ public class OpenWebNetBridgeHandler extends ConfigStatusBridgeHandler implement
* Init a ZigBee gateway based on config
*/
private @Nullable OpenGateway initZigBeeGateway() {
logger.debug("Initializing ZigBee USB gateway");
logger.debug("Initializing ZigBee USB Gateway");
OpenWebNetZigBeeBridgeConfig zbBridgeConfig = getConfigAs(OpenWebNetZigBeeBridgeConfig.class);
String serialPort = zbBridgeConfig.getSerialPort();
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,
"@text/offline.conf-error-no-serial-port");
return null;
@@ -152,7 +156,7 @@ public class OpenWebNetBridgeHandler extends ConfigStatusBridgeHandler implement
OpenWebNetBusBridgeConfig busBridgeConfig = getConfigAs(OpenWebNetBusBridgeConfig.class);
String host = busBridgeConfig.getHost();
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,
"@text/offline.conf-error-no-ip-address");
return null;
@@ -177,7 +181,7 @@ public class OpenWebNetBridgeHandler extends ConfigStatusBridgeHandler implement
logger.debug("handleCommand (command={} - channel={})", command, channelUID);
OpenGateway gw = gateway;
if (gw != null && !gw.isConnected()) {
logger.warn("Gateway is NOT connected, skipping command");
logger.info("Gateway is NOT connected, skipping command");
return;
} else {
logger.warn("Channel not supported: channel={}", channelUID);
@@ -206,7 +210,7 @@ public class OpenWebNetBridgeHandler extends ConfigStatusBridgeHandler implement
if (gw != null) {
gw.closeConnection();
gw.unsubscribe(this);
logger.debug("gateway {} connection closed and unsubscribed", gw.toString());
logger.debug("Gateway {} connection closed and unsubscribed", gw.toString());
gateway = null;
}
reconnecting = false;
@@ -276,23 +280,54 @@ public class OpenWebNetBridgeHandler extends ConfigStatusBridgeHandler implement
}
private void discoverByActivation(BaseOpenMessage baseMsg) {
logger.debug("BridgeHandler.discoverByActivation() msg={}", baseMsg);
logger.debug("discoverByActivation: msg={}", baseMsg);
OpenWebNetDeviceDiscoveryService discService = deviceDiscoveryService;
if (discService == null) {
logger.warn("discoverByActivation: null OpenWebNetDeviceDiscoveryService, ignoring msg={}", baseMsg);
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;
try {
type = baseMsg.detectDeviceType();
type = bmsg.detectDeviceType();
} catch (FrameException e) {
logger.warn("Exception while detecting device type: {}", e.getMessage());
}
if (type != null) {
discService.newDiscoveryResult(baseMsg.getWhere(), type, baseMsg);
discService.newDiscoveryResult(bmsg.getWhere(), type, bmsg);
} 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...
if (baseMsg instanceof Lighting || baseMsg instanceof Automation) {
String ownId = ownIdFromMessage(baseMsg);
logger.debug("ownId={}", ownId);
logger.debug("ownIdFromMessage({}) --> {}", baseMsg, ownId);
OpenWebNetThingHandler deviceHandler = registeredDevices.get(ownId);
if (deviceHandler == null) {
OpenGateway gw = gateway;
@@ -384,7 +419,7 @@ public class OpenWebNetBridgeHandler extends ConfigStatusBridgeHandler implement
return;
}
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());
} else {
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
* handler.
* Return a ownId string (=WHO.WHERE) from the device Where address and handler
*
* @param deviceWhere the device WHERE config parameter
* @param handler the thing handler
* @return the ownId
* @param where the Where address (to be normalized)
* @param handler the device handler
* @return the ownId String
*/
protected String ownIdFromDeviceWhere(@Nullable String deviceWhere, OpenWebNetThingHandler handler) {
return handler.ownIdPrefix() + "." + deviceWhere;
protected String ownIdFromDeviceWhere(Where where, OpenWebNetThingHandler handler) {
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
* @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);
}
@@ -510,45 +544,39 @@ public class OpenWebNetBridgeHandler extends ConfigStatusBridgeHandler implement
* @param baseMsg the BaseOpenMessage
* @return the ownId String
*/
private String ownIdFromMessage(BaseOpenMessage baseMsg) {
public String ownIdFromMessage(BaseOpenMessage baseMsg) {
return baseMsg.getWho().value() + "." + normalizeWhere(baseMsg.getWhere());
}
/**
* Transform a Where address into a Thing id string based on bridge type (BUS/USB ZigBee).
* '#' in WHERE are changed to 'h'
* Transform a Where address into a Thing id string
*
* @param where the Where address
* @return the thing Id
* @return the thing Id string
*/
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
* @return the normalized address
* @return the normalized address as String
*/
public String normalizeWhere(Where where) {
String str = "";
if (isBusGateway) {
if (where.value().indexOf('#') < 0) { // no hash present
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;
String str = where.value();
if (where instanceof WhereZigBee) {
str = ((WhereZigBee) where).valueWithUnit(WhereZigBee.UNIT_ALL); // 76543210X#9 --> 765432100#9
} 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)) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"WHERE parameter in configuration is null or invalid");
return;
} else {
String deviceWhereStr = (String) getConfig().get(CONFIG_PROPERTY_WHERE);
Where w;
if (brH.isBusGateway()) {
w = new WhereLightAutom(deviceWhereStr);
} else {
w = new WhereZigBee(deviceWhereStr);
try {
if (brH.isBusGateway()) {
w = new WhereLightAutom(deviceWhereStr);
} else {
w = new WhereZigBee(deviceWhereStr);
}
} catch (IllegalArgumentException ia) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"WHERE parameter in configuration is invalid");
return;
}
deviceWhere = w;
final String oid = brH.ownIdFromDeviceWhere(w.value(), this);
final String oid = brH.ownIdFromDeviceWhere(w, this);
ownId = oid;
Map<String, String> properties = editProperties();
properties.put(PROPERTY_OWNID, oid);
@@ -91,11 +96,11 @@ public abstract class OpenWebNetThingHandler extends BaseThingHandler {
logger.debug("associated thing to bridge with ownId={}", ownId);
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
@@ -105,8 +110,12 @@ public abstract class OpenWebNetThingHandler extends BaseThingHandler {
if (handler != null) {
OpenGateway gw = handler.gateway;
if (gw != null && !gw.isConnected()) {
logger.info("Cannot handle command {}:{} for {}: gateway is not connected", channel, command,
getThing().getUID());
logger.info("Cannot handle {} command for {}: gateway is not connected", command, getThing().getUID());
return;
}
if (deviceWhere == null) {
logger.info("Cannot handle {} command for {}: 'where' parameter is not configured or is invalid",
command, getThing().getUID());
return;
}
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 (bridgeHandler.getRegisteredDevice(ownId) != null) {
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;
String whereLabel = where.value();
String whereConfig = where.value();
if (where instanceof WhereZigBee && WhereZigBee.UNIT_02.equals(((WhereZigBee) where).getUnit())) {
logger.debug("UNIT=02 found (WHERE={})", where);
logger.debug("will remove previous result if exists");
logger.debug("UNIT=02 found (WHERE={}) -> will remove previous result if exists", where);
thingRemoved(thingUID); // remove previously discovered thing
// re-create thingUID with new type
thingTypeUID = OpenWebNetBindingConstants.THING_TYPE_ZB_ON_OFF_SWITCH_2UNITS;
thingLabel = OpenWebNetBindingConstants.THING_LABEL_ZB_ON_OFF_SWITCH_2UNITS;
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 {}",
OpenWebNetBindingConstants.THING_TYPE_ZB_ON_OFF_SWITCH,
OpenWebNetBindingConstants.THING_TYPE_ZB_ON_OFF_SWITCH_2UNITS);
}
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);
if (thingTypeUID == OpenWebNetBindingConstants.THING_TYPE_GENERIC_DEVICE) {
thingLabel = thingLabel + " (WHO=" + deviceWho + ", WHERE=" + whereLabel + ")";
thingLabel = thingLabel + " (WHO=" + deviceWho + ", WHERE=" + whereConfig + ")";
} else {
thingLabel = thingLabel + " (WHERE=" + whereLabel + ")";
thingLabel = thingLabel + " (WHERE=" + whereConfig + ")";
}
discoveryResult = DiscoveryResultBuilder.create(thingUID).withThingType(thingTypeUID).withProperties(properties)
.withRepresentationProperty(OpenWebNetBindingConstants.PROPERTY_OWNID).withBridge(bridgeUID)

View File

@@ -38,8 +38,8 @@
</parameter>
<parameter name="where" type="text" required="true">
<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>
<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>
</parameter>
</config-description>
</thing-type>

View File

@@ -27,8 +27,8 @@
<config-description>
<parameter name="where" type="text" required="true">
<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>
<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>
</parameter>
</config-description>

View File

@@ -27,8 +27,8 @@
<config-description>
<parameter name="where" type="text" required="true">
<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>
<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>
</parameter>
</config-description>

View File

@@ -24,7 +24,7 @@
<config-description>
<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>
</parameter>
</config-description>

View File

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

View File

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

View File

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

View File

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