[openwebnet] Improvements for ZigBee USB and Dimmers (#9662)
* [openwebnet] Log messages cleanup Signed-off-by: Massimo Valla <mvcode00@gmail.com> * [openwebnet] Fixed normalizeWhere * added builder for Bus Where addresses * added messages to OwnIdTest * depend on own4j 0.3.3-SNAPSHOT Signed-off-by: Massimo Valla <mvcode00@gmail.com> * [openwebnet] Discovery & reconnect for ZigBee USB gateways * added mgmt of disconnect/reconnect of ZigBee USB Gateway from serial (#9170) * added ZigBee USB Gateway auto discovery (#9171) Signed-off-by: Massimo Valla <mvcode00@gmail.com> * [openwebnet] fixed up/down automation for old fw (#9651) * now using openwebnet4j 0.3.3 (nrjavaserial 5.2.1) Signed-off-by: Massimo Valla <mvcode00@gmail.com> * [openwebnet] Fixes lowering dimmers from 20 to 10% does not change device level #9317 - bump openwebnet4j to 0.3.4 Signed-off-by: Massimo Valla <mvcode00@gmail.com> * [openwebnet] changes after PR review Signed-off-by: Massimo Valla <mvcode00@gmail.com>
This commit is contained in:
@@ -103,6 +103,8 @@ public class OpenWebNetBindingConstants {
|
||||
|
||||
// BUS gw config properties
|
||||
public static final String CONFIG_PROPERTY_HOST = "host";
|
||||
public static final String CONFIG_PROPERTY_SERIAL_PORT = "serialPort";
|
||||
|
||||
// properties
|
||||
public static final String PROPERTY_OWNID = "ownId";
|
||||
public static final String PROPERTY_ZIGBEEID = "zigbeeid";
|
||||
|
||||
@@ -41,6 +41,7 @@ import org.openwebnet4j.message.BaseOpenMessage;
|
||||
import org.openwebnet4j.message.FrameException;
|
||||
import org.openwebnet4j.message.GatewayMgmt;
|
||||
import org.openwebnet4j.message.Where;
|
||||
import org.openwebnet4j.message.WhereLightAutom;
|
||||
import org.openwebnet4j.message.Who;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -129,6 +130,11 @@ public class OpenWebNetAutomationHandler extends OpenWebNetThingHandler {
|
||||
positionEstimation = POSITION_UNKNOWN;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Where buildBusWhere(String wStr) throws IllegalArgumentException {
|
||||
return new WhereLightAutom(wStr);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void requestChannelState(ChannelUID channel) {
|
||||
logger.debug("requestChannelState() thingUID={} channel={}", thing.getUID(), channel.getId());
|
||||
@@ -280,8 +286,9 @@ public class OpenWebNetAutomationHandler extends OpenWebNetThingHandler {
|
||||
|
||||
@Override
|
||||
protected void handleMessage(BaseOpenMessage msg) {
|
||||
logger.debug("handleMessage({}) for thing: {}", msg, thing.getUID());
|
||||
updateAutomationState((Automation) msg);
|
||||
// REMINDER: update state, then update thing status in the super method, to avoid delays
|
||||
// REMINDER: update automation state, and only after update thing status using the super method, to avoid delays
|
||||
super.handleMessage(msg);
|
||||
}
|
||||
|
||||
|
||||
@@ -111,7 +111,7 @@ public class OpenWebNetBridgeHandler extends ConfigStatusBridgeHandler implement
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
} else {
|
||||
updateStatus(ThingStatus.UNKNOWN);
|
||||
logger.debug("Trying to connect gateway...");
|
||||
logger.debug("Trying to connect gateway {}... ", gw);
|
||||
try {
|
||||
gw.connect();
|
||||
scheduler.schedule(() -> {
|
||||
@@ -233,22 +233,22 @@ public class OpenWebNetBridgeHandler extends ConfigStatusBridgeHandler implement
|
||||
if (gw != null) {
|
||||
if (!gw.isDiscovering()) {
|
||||
if (!gw.isConnected()) {
|
||||
logger.debug("------$$ Gateway is NOT connected, cannot search for devices");
|
||||
logger.debug("------$$ Gateway '{}' is NOT connected, cannot search for devices", gw);
|
||||
return;
|
||||
}
|
||||
logger.info("------$$ STARTED active SEARCH for devices on gateway '{}'", this.getThing().getUID());
|
||||
logger.info("------$$ STARTED active SEARCH for devices on bridge '{}'", thing.getUID());
|
||||
try {
|
||||
gw.discoverDevices();
|
||||
} catch (OWNException e) {
|
||||
logger.warn("------$$ OWNException while discovering devices on gateway {}: {}",
|
||||
this.getThing().getUID(), e.getMessage());
|
||||
logger.warn("------$$ OWNException while discovering devices on bridge '{}': {}", thing.getUID(),
|
||||
e.getMessage());
|
||||
}
|
||||
} else {
|
||||
logger.debug("------$$ Searching devices on gateway {} already activated", this.getThing().getUID());
|
||||
logger.debug("------$$ Searching devices on bridge '{}' already activated", thing.getUID());
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
logger.debug("------$$ Cannot search devices: no gateway associated to this handler");
|
||||
logger.warn("------$$ Cannot search devices: no gateway associated to this handler");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -268,7 +268,7 @@ public class OpenWebNetBridgeHandler extends ConfigStatusBridgeHandler implement
|
||||
|
||||
@Override
|
||||
public void onDiscoveryCompleted() {
|
||||
logger.info("------$$ FINISHED active SEARCH for devices on gateway '{}'", this.getThing().getUID());
|
||||
logger.info("------$$ FINISHED active SEARCH for devices on bridge '{}'", thing.getUID());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -418,10 +418,10 @@ public class OpenWebNetBridgeHandler extends ConfigStatusBridgeHandler implement
|
||||
return;
|
||||
}
|
||||
if (gw instanceof USBGateway) {
|
||||
logger.info("------------------- CONNECTED to ZigBee USB gateway - USB port: {}",
|
||||
logger.info("---- CONNECTED to ZigBee USB gateway bridge '{}' (serialPort: {})", thing.getUID(),
|
||||
((USBGateway) gw).getSerialPortName());
|
||||
} else {
|
||||
logger.info("------------------- CONNECTED to BUS gateway '{}' ({}:{})", thing.getUID(),
|
||||
logger.info("---- CONNECTED to BUS gateway bridge '{}' ({}:{})", thing.getUID(),
|
||||
((BUSGateway) gw).getHost(), ((BUSGateway) gw).getPort());
|
||||
// update serial number property (with MAC address)
|
||||
if (properties.get(PROPERTY_SERIAL_NO) != gw.getMACAddr().toUpperCase()) {
|
||||
@@ -437,7 +437,7 @@ public class OpenWebNetBridgeHandler extends ConfigStatusBridgeHandler implement
|
||||
}
|
||||
if (propertiesChanged) {
|
||||
updateProperties(properties);
|
||||
logger.info("properties updated for '{}'", thing.getUID());
|
||||
logger.info("properties updated for bridge '{}'", thing.getUID());
|
||||
}
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
}
|
||||
@@ -450,7 +450,7 @@ public class OpenWebNetBridgeHandler extends ConfigStatusBridgeHandler implement
|
||||
} else {
|
||||
errMsg = error.getMessage();
|
||||
}
|
||||
logger.info("------------------- ON CONNECTION ERROR: {}", errMsg);
|
||||
logger.info("---- ON CONNECTION ERROR for gateway {}: {}", gateway, errMsg);
|
||||
isGatewayConnected = false;
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, errMsg);
|
||||
tryReconnectGateway();
|
||||
@@ -472,7 +472,7 @@ public class OpenWebNetBridgeHandler extends ConfigStatusBridgeHandler implement
|
||||
} else {
|
||||
errMsg = e.getMessage();
|
||||
}
|
||||
logger.info("------------------- DISCONNECTED from gateway. OWNException={}", errMsg);
|
||||
logger.info("---- DISCONNECTED from gateway {}. OWNException: {}", gateway, errMsg);
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
|
||||
"Disconnected from gateway (onDisconnected - " + errMsg + ")");
|
||||
tryReconnectGateway();
|
||||
@@ -483,29 +483,29 @@ public class OpenWebNetBridgeHandler extends ConfigStatusBridgeHandler implement
|
||||
if (gw != null) {
|
||||
if (!reconnecting) {
|
||||
reconnecting = true;
|
||||
logger.info("------------------- Starting RECONNECT cycle to gateway");
|
||||
logger.info("---- Starting RECONNECT cycle to gateway {}", gw);
|
||||
try {
|
||||
gw.reconnect();
|
||||
} catch (OWNAuthException e) {
|
||||
logger.info("------------------- AUTH error from gateway. Stopping reconnect");
|
||||
logger.info("---- AUTH error from gateway. Stopping re-connect");
|
||||
reconnecting = false;
|
||||
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR,
|
||||
"Authentication error. Check gateway password in Thing Configuration Parameters (" + e
|
||||
+ ")");
|
||||
}
|
||||
} else {
|
||||
logger.debug("------------------- reconnecting=true, do nothing");
|
||||
logger.debug("---- reconnecting=true, do nothing");
|
||||
}
|
||||
} else {
|
||||
logger.debug("------------------- cannot start RECONNECT, gateway is null");
|
||||
logger.warn("---- cannot start RECONNECT, gateway is null");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReconnected() {
|
||||
reconnecting = false;
|
||||
logger.info("------------------- RE-CONNECTED to gateway!");
|
||||
OpenGateway gw = gateway;
|
||||
logger.info("---- RE-CONNECTED to bridge {}", thing.getUID());
|
||||
if (gw != null) {
|
||||
updateStatus(ThingStatus.ONLINE);
|
||||
if (gw.getFirmwareVersion() != null) {
|
||||
@@ -568,10 +568,10 @@ public class OpenWebNetBridgeHandler extends ConfigStatusBridgeHandler implement
|
||||
if (where instanceof WhereZigBee) {
|
||||
str = ((WhereZigBee) where).valueWithUnit(WhereZigBee.UNIT_ALL); // 76543210X#9 --> 765432100#9
|
||||
} else {
|
||||
if (str.indexOf("#4#") == 0) { // no changes needed for local bus: APL#4#bus
|
||||
if (str.indexOf('#') == 0) { // Thermo zone via central unit: #0 or #Z (Z=[1-99]) --> Z
|
||||
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
|
||||
str = str.substring(1);
|
||||
} else if (str.indexOf('#') > 0) { // Thermo zone and actuator N: Z#N (Z=[1-99], N=[1-9]) --> Z
|
||||
} else if (str.indexOf('#') > 0) { // Thermo zone Z and actuator N (Z#N, Z=[1-99], N=[1-9]) --> Z
|
||||
str = str.substring(0, str.indexOf('#'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@ import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.types.Command;
|
||||
import org.openwebnet4j.message.BaseOpenMessage;
|
||||
import org.openwebnet4j.message.Where;
|
||||
import org.openwebnet4j.message.WhereLightAutom;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -65,10 +67,15 @@ public class OpenWebNetGenericHandler extends OpenWebNetThingHandler {
|
||||
return "G";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Where buildBusWhere(String wStr) throws IllegalArgumentException {
|
||||
return new WhereLightAutom(wStr);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleMessage(BaseOpenMessage msg) {
|
||||
super.handleMessage(msg);
|
||||
// do nothing
|
||||
logger.warn("handleMessage(): Nothing to do!");
|
||||
}
|
||||
} // class
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ import org.openwebnet4j.message.FrameException;
|
||||
import org.openwebnet4j.message.Lighting;
|
||||
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;
|
||||
@@ -53,15 +54,19 @@ public class OpenWebNetLightingHandler extends OpenWebNetThingHandler {
|
||||
|
||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = OpenWebNetBindingConstants.LIGHTING_SUPPORTED_THING_TYPES;
|
||||
|
||||
private static final int BRIGHTNESS_CHANGE_DELAY_MSEC = 1500; // delay before sending another brightness status
|
||||
// request
|
||||
private static final int BRIGHTNESS_STATUS_REQUEST_DELAY_MSEC = 900; // we must wait some time to be sure dimmer has
|
||||
// reached final level before requesting its
|
||||
// status
|
||||
// interval to interpret ON as response to requestStatus
|
||||
private static final int BRIGHTNESS_STATUS_REQUEST_INTERVAL_MSEC = 250;
|
||||
|
||||
// time to wait before sending a statusRequest, to avoid repeated requests and ensure dimmer has reached its final
|
||||
// level
|
||||
private static final int BRIGHTNESS_STATUS_REQUEST_DELAY_MSEC = 900;
|
||||
|
||||
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 int brightness = UNKNOWN_STATE; // current brightness percent value for this device
|
||||
|
||||
private int brightnessBeforeOff = UNKNOWN_STATE; // latest brightness before device was set to off
|
||||
@@ -83,6 +88,7 @@ public class OpenWebNetLightingHandler extends OpenWebNetThingHandler {
|
||||
Where w = deviceWhere;
|
||||
if (w != null) {
|
||||
try {
|
||||
lastStatusRequestSentTS = System.currentTimeMillis();
|
||||
Response res = send(Lighting.requestStatus(toWhere(channelId)));
|
||||
if (res != null && res.isSuccess()) {
|
||||
// set thing online if not already
|
||||
@@ -214,8 +220,8 @@ public class OpenWebNetLightingHandler extends OpenWebNetThingHandler {
|
||||
|
||||
@Override
|
||||
protected void handleMessage(BaseOpenMessage msg) {
|
||||
logger.debug("handleMessage({}) for thing: {}", msg, thing.getUID());
|
||||
super.handleMessage(msg);
|
||||
logger.debug("handleMessage() for thing: {}", thing.getUID());
|
||||
ThingTypeUID thingType = thing.getThingTypeUID();
|
||||
if (THING_TYPE_ZB_DIMMER.equals(thingType) || THING_TYPE_BUS_DIMMER.equals(thingType)) {
|
||||
updateBrightness((Lighting) msg);
|
||||
@@ -230,47 +236,54 @@ public class OpenWebNetLightingHandler extends OpenWebNetThingHandler {
|
||||
* @param msg the Lighting message received
|
||||
*/
|
||||
private synchronized void updateBrightness(Lighting msg) {
|
||||
long now = System.currentTimeMillis();
|
||||
logger.debug(" $BRI updateBrightness({}) || bri={} briBeforeOff={}", msg, brightness,
|
||||
brightnessBeforeOff);
|
||||
long now = System.currentTimeMillis();
|
||||
long delta = now - lastBrightnessChangeSentTS;
|
||||
boolean belowThresh = delta < BRIGHTNESS_CHANGE_DELAY_MSEC;
|
||||
boolean belowThresh = delta < BRIGHTNESS_STATUS_REQUEST_DELAY_MSEC;
|
||||
logger.debug(" $BRI delta={}ms {}", delta, (belowThresh ? "< DELAY" : ""));
|
||||
if (belowThresh) {
|
||||
// we just sent a command from OH, so we can ignore this message from network
|
||||
logger.debug(" $BRI a request was sent {} < {} ms --> no action needed", delta,
|
||||
BRIGHTNESS_CHANGE_DELAY_MSEC);
|
||||
logger.debug(" $BRI a command was sent {} < {} ms --> no action needed", delta,
|
||||
BRIGHTNESS_STATUS_REQUEST_DELAY_MSEC);
|
||||
} else {
|
||||
if (msg.isOn()) {
|
||||
logger.debug(" $BRI \"ON\" notification from network, scheduling requestStatus...");
|
||||
// we must wait BRIGHTNESS_STATUS_REQUEST_DELAY_MSEC to be sure dimmer has reached final level
|
||||
scheduler.schedule(() -> {
|
||||
requestStatus(CHANNEL_BRIGHTNESS);
|
||||
}, BRIGHTNESS_STATUS_REQUEST_DELAY_MSEC, TimeUnit.MILLISECONDS);
|
||||
} else {
|
||||
logger.debug(" $BRI update from network");
|
||||
if (msg.getWhat() != null) {
|
||||
updateBrightnessState(msg);
|
||||
} else { // dimension notification
|
||||
if (msg.getDim() == Lighting.DIM.DIMMER_LEVEL_100) {
|
||||
int newBrightness;
|
||||
try {
|
||||
newBrightness = msg.parseDimmerLevel100();
|
||||
} catch (FrameException fe) {
|
||||
logger.warn("updateBrightness() Wrong value for dimmerLevel100 in message: {}", msg);
|
||||
return;
|
||||
}
|
||||
logger.debug(" $BRI DIMMER_LEVEL_100 newBrightness={}", newBrightness);
|
||||
updateState(CHANNEL_BRIGHTNESS, new PercentType(newBrightness));
|
||||
if (newBrightness == 0) {
|
||||
brightnessBeforeOff = brightness;
|
||||
}
|
||||
brightness = newBrightness;
|
||||
} else {
|
||||
logger.warn("updateBrightness() Cannot handle message {} for thing {}", msg,
|
||||
getThing().getUID());
|
||||
// if we have not just sent a requestStatus, on ON event we send requestStatus to know current level
|
||||
long deltaStatusReq = now - lastStatusRequestSentTS;
|
||||
if (deltaStatusReq > BRIGHTNESS_STATUS_REQUEST_INTERVAL_MSEC) {
|
||||
logger.debug(" $BRI 'ON' is new notification from network, scheduling requestStatus...");
|
||||
// we must wait BRIGHTNESS_STATUS_REQUEST_DELAY_MSEC to be sure dimmer has reached its final level
|
||||
scheduler.schedule(() -> {
|
||||
requestStatus(CHANNEL_BRIGHTNESS);
|
||||
}, BRIGHTNESS_STATUS_REQUEST_DELAY_MSEC, TimeUnit.MILLISECONDS);
|
||||
return;
|
||||
} else {
|
||||
// otherwise we interpret this ON event as the requestStatus response event with level=1
|
||||
// so we proceed to call updateBrightnessState()
|
||||
logger.debug(" $BRI 'ON' is the requestStatus response level");
|
||||
}
|
||||
}
|
||||
logger.debug(" $BRI update from network");
|
||||
if (msg.getWhat() != null) {
|
||||
updateBrightnessState(msg);
|
||||
} else { // dimension notification
|
||||
if (msg.getDim() == Lighting.DIM.DIMMER_LEVEL_100) {
|
||||
int newBrightness;
|
||||
try {
|
||||
newBrightness = msg.parseDimmerLevel100();
|
||||
} catch (FrameException fe) {
|
||||
logger.warn("updateBrightness() Wrong value for dimmerLevel100 in message: {}", msg);
|
||||
return;
|
||||
}
|
||||
logger.debug(" $BRI DIMMER_LEVEL_100 newBrightness={}", newBrightness);
|
||||
updateState(CHANNEL_BRIGHTNESS, new PercentType(newBrightness));
|
||||
if (newBrightness == 0) {
|
||||
brightnessBeforeOff = brightness;
|
||||
}
|
||||
brightness = newBrightness;
|
||||
} else {
|
||||
logger.warn("updateBrightness() Cannot handle message {} for thing {}", msg, getThing().getUID());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -284,8 +297,12 @@ public class OpenWebNetLightingHandler extends OpenWebNetThingHandler {
|
||||
* @param msg the Lighting message received
|
||||
*/
|
||||
private void updateBrightnessState(Lighting msg) {
|
||||
if (msg.getWhat() != null) {
|
||||
int newBrightnessWhat = msg.getWhat().value();
|
||||
What w = msg.getWhat();
|
||||
if (w != null) {
|
||||
if (Lighting.WHAT.ON.equals(w)) {
|
||||
w = Lighting.WHAT.DIMMER_LEVEL_2; // levels start at 2
|
||||
}
|
||||
int newBrightnessWhat = w.value();
|
||||
int brightnessWhat = UNKNOWN_STATE;
|
||||
if (brightness != UNKNOWN_STATE) {
|
||||
brightnessWhat = Lighting.percentToWhat(brightness).value();
|
||||
@@ -344,6 +361,11 @@ public class OpenWebNetLightingHandler extends OpenWebNetThingHandler {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Where buildBusWhere(String wStr) throws IllegalArgumentException {
|
||||
return new WhereLightAutom(wStr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a WHERE address string based on channelId string
|
||||
*
|
||||
|
||||
@@ -34,7 +34,6 @@ import org.openwebnet4j.communication.Response;
|
||||
import org.openwebnet4j.message.BaseOpenMessage;
|
||||
import org.openwebnet4j.message.OpenMessage;
|
||||
import org.openwebnet4j.message.Where;
|
||||
import org.openwebnet4j.message.WhereLightAutom;
|
||||
import org.openwebnet4j.message.WhereZigBee;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -77,7 +76,7 @@ public abstract class OpenWebNetThingHandler extends BaseThingHandler {
|
||||
Where w;
|
||||
try {
|
||||
if (brH.isBusGateway()) {
|
||||
w = new WhereLightAutom(deviceWhereStr);
|
||||
w = buildBusWhere(deviceWhereStr);
|
||||
} else {
|
||||
w = new WhereZigBee(deviceWhereStr);
|
||||
}
|
||||
@@ -192,6 +191,14 @@ public abstract class OpenWebNetThingHandler extends BaseThingHandler {
|
||||
*/
|
||||
protected abstract void requestChannelState(ChannelUID channel);
|
||||
|
||||
/**
|
||||
* Abstract builder for device Where address, to be implemented by each subclass to choose the right Where subclass
|
||||
* (the method is used only if the Thing is associated to a BUS gateway).
|
||||
*
|
||||
* @param wStr the WHERE string
|
||||
*/
|
||||
protected abstract Where buildBusWhere(String wStr) throws IllegalArgumentException;
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
OpenWebNetBridgeHandler bh = bridgeHandler;
|
||||
|
||||
@@ -60,7 +60,7 @@ public class OpenWebNetDeviceDiscoveryService extends AbstractDiscoveryService
|
||||
|
||||
@Override
|
||||
public Set<ThingTypeUID> getSupportedThingTypes() {
|
||||
return OpenWebNetDeviceDiscoveryService.SUPPORTED_THING_TYPES;
|
||||
return SUPPORTED_THING_TYPES;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,237 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2021 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.openwebnet.internal.discovery;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.openwebnet.OpenWebNetBindingConstants;
|
||||
import org.openhab.core.config.discovery.AbstractDiscoveryService;
|
||||
import org.openhab.core.config.discovery.DiscoveryResult;
|
||||
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
|
||||
import org.openhab.core.config.discovery.DiscoveryService;
|
||||
import org.openhab.core.io.transport.serial.SerialPortIdentifier;
|
||||
import org.openhab.core.io.transport.serial.SerialPortManager;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
import org.openwebnet4j.GatewayListener;
|
||||
import org.openwebnet4j.OpenDeviceType;
|
||||
import org.openwebnet4j.USBGateway;
|
||||
import org.openwebnet4j.communication.OWNException;
|
||||
import org.openwebnet4j.message.BaseOpenMessage;
|
||||
import org.openwebnet4j.message.OpenMessage;
|
||||
import org.openwebnet4j.message.Where;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Reference;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link UsbGatewayDiscoveryService} extends {@link AbstractDiscoveryService} to detect ZigBee USB gateways
|
||||
* connected via serial port. The service will iterate over the available serial ports and open each one to test if a
|
||||
* OpenWebNet ZigBee USB gateway is connected. On successful connection, a new DiscoveryResult is created.
|
||||
*
|
||||
* @author Massimo Valla - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(service = DiscoveryService.class, configurationPid = "discovery.openwebnet")
|
||||
|
||||
public class UsbGatewayDiscoveryService extends AbstractDiscoveryService implements GatewayListener {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(UsbGatewayDiscoveryService.class);
|
||||
|
||||
private static final int DISCOVERY_TIMEOUT_SECONDS = 30;
|
||||
private static final int PORT_CHECK_TIMEOUT_MSEC = 1500;
|
||||
|
||||
private CountDownLatch portCheckLatch = new CountDownLatch(1);
|
||||
private @Nullable ScheduledFuture<?> connectTimeout;
|
||||
|
||||
private final SerialPortManager serialPortManager;
|
||||
private @Nullable USBGateway zbGateway;
|
||||
|
||||
private String currentScannedPortName = "";
|
||||
|
||||
/**
|
||||
* Keeps a boolean during time discovery process in progress.
|
||||
*/
|
||||
private boolean scanning;
|
||||
|
||||
/**
|
||||
* Constructs a new UsbGatewayDiscoveryService with the specified ZigBee USB Bridge ThingTypeUID
|
||||
*/
|
||||
@Activate
|
||||
public UsbGatewayDiscoveryService(final @Reference SerialPortManager spm) {
|
||||
super(Collections.singleton(OpenWebNetBindingConstants.THING_TYPE_ZB_GATEWAY), DISCOVERY_TIMEOUT_SECONDS,
|
||||
false);
|
||||
// Obtain the serial port manager service using an OSGi reference
|
||||
serialPortManager = spm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a new discovery scan. All available Serial Ports are scanned.
|
||||
*/
|
||||
@Override
|
||||
protected void startScan() {
|
||||
logger.debug("Started OpenWebNet ZigBee USB Gateway discovery scan");
|
||||
removeOlderResults(getTimestampOfLastScan());
|
||||
scanning = true;
|
||||
Stream<SerialPortIdentifier> portEnum = serialPortManager.getIdentifiers();
|
||||
// Check each available serial port
|
||||
try {
|
||||
for (SerialPortIdentifier portIdentifier : portEnum.toArray(SerialPortIdentifier[]::new)) {
|
||||
if (scanning) {
|
||||
currentScannedPortName = portIdentifier.getName();
|
||||
logger.debug("[{}] == checking serial port", currentScannedPortName);
|
||||
if (portIdentifier.isCurrentlyOwned()) {
|
||||
logger.debug("[{}] serial port is owned by: {}", currentScannedPortName,
|
||||
portIdentifier.getCurrentOwner());
|
||||
} else {
|
||||
logger.debug("[{}] trying to connect to a ZigBee USB Gateway...", currentScannedPortName);
|
||||
USBGateway gw = new USBGateway(currentScannedPortName);
|
||||
zbGateway = gw;
|
||||
gw.subscribe(this);
|
||||
portCheckLatch = new CountDownLatch(1);
|
||||
connectTimeout = scheduler.schedule(() -> {
|
||||
logger.debug("[{}] timeout expired", currentScannedPortName);
|
||||
endGwConnection();
|
||||
portCheckLatch.countDown();
|
||||
}, PORT_CHECK_TIMEOUT_MSEC, TimeUnit.MILLISECONDS);
|
||||
try {
|
||||
gw.connect();
|
||||
portCheckLatch.await();
|
||||
} catch (OWNException e) {
|
||||
logger.debug("[{}] OWNException while trying to connect to a ZigBee USB Gateway: {}",
|
||||
currentScannedPortName, e.getMessage());
|
||||
cancelConnectTimeout();
|
||||
endGwConnection();
|
||||
}
|
||||
}
|
||||
logger.debug("[{}] == finished checking port", currentScannedPortName);
|
||||
}
|
||||
}
|
||||
logger.debug("Finished checking all serial ports");
|
||||
} catch (InterruptedException ie) {
|
||||
logger.warn("[{}] interrupted: {}", currentScannedPortName, ie.getMessage());
|
||||
endGwConnection();
|
||||
logger.debug("Interrupted while checking serial ports");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected synchronized void stopScan() {
|
||||
scanning = false;
|
||||
cancelConnectTimeout();
|
||||
endGwConnection();
|
||||
portCheckLatch.countDown();
|
||||
super.stopScan();
|
||||
logger.debug("Stopped OpenWebNet ZigBee USB Gateway discovery scan");
|
||||
}
|
||||
|
||||
/**
|
||||
* Ends connection to the gateway
|
||||
*/
|
||||
private void endGwConnection() {
|
||||
USBGateway gw = zbGateway;
|
||||
if (gw != null) {
|
||||
gw.closeConnection();
|
||||
zbGateway = null;
|
||||
logger.debug("[{}] connection to gateway closed", currentScannedPortName);
|
||||
}
|
||||
}
|
||||
|
||||
private void cancelConnectTimeout() {
|
||||
ScheduledFuture<?> ct = connectTimeout;
|
||||
if (ct != null && !ct.isDone()) {
|
||||
ct.cancel(false);
|
||||
ct = null;
|
||||
logger.debug("[{}] timeout cancelled", currentScannedPortName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and notify a new ZigBee USB Gateway thing has been discovered
|
||||
*/
|
||||
private void bridgeDiscovered() {
|
||||
USBGateway gw = zbGateway;
|
||||
if (gw != null) {
|
||||
int gatewayZigBeeId = gw.getZigBeeIdAsDecimal();
|
||||
ThingUID gatewayUID = new ThingUID(OpenWebNetBindingConstants.THING_TYPE_ZB_GATEWAY,
|
||||
Integer.toString(gatewayZigBeeId));
|
||||
Map<String, Object> gwProperties = new HashMap<>(3);
|
||||
gwProperties.put(OpenWebNetBindingConstants.CONFIG_PROPERTY_SERIAL_PORT, gw.getSerialPortName());
|
||||
gwProperties.put(OpenWebNetBindingConstants.PROPERTY_FIRMWARE_VERSION, gw.getFirmwareVersion());
|
||||
gwProperties.put(OpenWebNetBindingConstants.PROPERTY_ZIGBEEID, String.valueOf(gatewayZigBeeId));
|
||||
|
||||
DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(gatewayUID).withProperties(gwProperties)
|
||||
.withLabel(OpenWebNetBindingConstants.THING_LABEL_ZB_GATEWAY + " (" + gw.getSerialPortName() + ")")
|
||||
.withRepresentationProperty(OpenWebNetBindingConstants.PROPERTY_ZIGBEEID).build();
|
||||
logger.debug("--- ZigBee USB Gateway thing discovered: {} fw: {}", discoveryResult.getLabel(),
|
||||
gw.getFirmwareVersion());
|
||||
thingDiscovered(discoveryResult);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnected() {
|
||||
logger.debug("[{}] found ZigBee USB Gateway", currentScannedPortName);
|
||||
cancelConnectTimeout();
|
||||
bridgeDiscovered();
|
||||
endGwConnection();
|
||||
portCheckLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectionError(@Nullable OWNException error) {
|
||||
OWNException e = error;
|
||||
String msg = (e != null ? e.getMessage() : "");
|
||||
logger.debug("[{}] onConnectionError(): {}", currentScannedPortName, msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectionClosed() {
|
||||
logger.debug("UsbGatewayDiscoveryService received onConnectionClosed()");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisconnected(@Nullable OWNException error) {
|
||||
logger.debug("UsbGatewayDiscoveryService received onDisconnected()");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReconnected() {
|
||||
logger.debug("UsbGatewayDiscoveryService received onReconnected()");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEventMessage(@Nullable OpenMessage msg) {
|
||||
logger.debug("UsbGatewayDiscoveryService received onEventMessage(): {}", msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewDevice(@Nullable Where where, @Nullable OpenDeviceType deviceType,
|
||||
@Nullable BaseOpenMessage message) {
|
||||
logger.debug("UsbGatewayDiscoveryService received onNewDevice()");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDiscoveryCompleted() {
|
||||
logger.debug("UsbGatewayDiscoveryService received onDiscoveryCompleted()");
|
||||
}
|
||||
}
|
||||
@@ -16,13 +16,17 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openwebnet4j.message.Lighting;
|
||||
import org.openwebnet4j.message.BaseOpenMessage;
|
||||
import org.openwebnet4j.message.FrameException;
|
||||
import org.openwebnet4j.message.Where;
|
||||
import org.openwebnet4j.message.WhereLightAutom;
|
||||
import org.openwebnet4j.message.WhereZigBee;
|
||||
import org.openwebnet4j.message.Who;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Test class for {@link OpenWebNetBridgeHandler#ownID} and ThingID calculation using {@link OpenWebNetBridgeHandler}
|
||||
@@ -33,6 +37,8 @@ import org.openwebnet4j.message.Who;
|
||||
@NonNullByDefault
|
||||
public class OwnIdTest {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(OwnIdTest.class);
|
||||
|
||||
// @formatter:off
|
||||
/**
|
||||
*
|
||||
@@ -57,22 +63,37 @@ public class OwnIdTest {
|
||||
// @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"),
|
||||
// @formatter:off
|
||||
zb_switch(new WhereZigBee("789309801#9"), Who.fromValue(1), "*1*1*789309801#9##", "789309800h9", "1.789309800h9", "789309800h9"),
|
||||
zb_switch_2u_1(new WhereZigBee("789301201#9"), Who.fromValue(1), "*1*1*789301201#9##", "789301200h9", "1.789301200h9", "789301200h9"),
|
||||
zb_switch_2u_2(new WhereZigBee("789301202#9"), Who.fromValue(1), "*1*1*789301202#9##", "789301200h9", "1.789301200h9", "789301200h9"),
|
||||
bus_switch(new WhereLightAutom("51"), Who.fromValue(1), "*1*1*51##", "51", "1.51", "51"),
|
||||
bus_localbus(new WhereLightAutom("25#4#01"), Who.fromValue(1), "*1*1*25#4#01##", "25h4h01", "1.25h4h01", "25h4h01");
|
||||
//bus_thermo_zone(new WhereThermo("1"), Who.fromValue(4),"*#4*1*0*0020##" , "1", "4.1", "1"),
|
||||
//bus_thermo_zone_act(new WhereThermo("2#1"), Who.fromValue(4),"*#4*2#1*20*0##" ,"2", "4.2", "2"),
|
||||
//bus_thermo_via_cu(new WhereThermo("#1"), Who.fromValue(4),"*#4*#1*0*0020##" ,"1", "4.1", "1"),
|
||||
// bus_tempSensor("500", "4", "500", "4.500", "500"),
|
||||
// bus_energy("51", "18", "51", "18.51", "51");
|
||||
|
||||
// @formatter:on
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(TEST.class);
|
||||
|
||||
public final Where where;
|
||||
public final Who who;
|
||||
public final @Nullable BaseOpenMessage msg;
|
||||
public final String norm, ownId, thingId;
|
||||
|
||||
private TEST(Where where, Who who, String norm, String ownId, String thingId) {
|
||||
private TEST(Where where, Who who, String msg, String norm, String ownId, String thingId) {
|
||||
this.where = where;
|
||||
this.who = who;
|
||||
BaseOpenMessage bmsg = null;
|
||||
try {
|
||||
bmsg = (BaseOpenMessage) BaseOpenMessage.parse(msg);
|
||||
} catch (FrameException e) {
|
||||
logger.warn("something is wrong in the test table. ownIdFromMessage test will be skipped");
|
||||
}
|
||||
this.msg = bmsg;
|
||||
this.norm = norm;
|
||||
this.ownId = ownId;
|
||||
this.thingId = thingId;
|
||||
@@ -83,13 +104,16 @@ public class OwnIdTest {
|
||||
public void testOwnId() {
|
||||
Bridge mockBridge = mock(Bridge.class);
|
||||
OpenWebNetBridgeHandler brH = new OpenWebNetBridgeHandler(mockBridge);
|
||||
|
||||
BaseOpenMessage bmsg;
|
||||
for (int i = 0; i < TEST.values().length; i++) {
|
||||
TEST test = TEST.values()[i];
|
||||
// System.out.println("testing where=" + test.where);
|
||||
logger.info("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())));
|
||||
bmsg = test.msg;
|
||||
if (bmsg != null) {
|
||||
assertEquals(test.ownId, brH.ownIdFromMessage(bmsg));
|
||||
}
|
||||
assertEquals(test.thingId, brH.thingIdFromWhere(test.where));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user