private static final SimpleDateFormat DATE_FORMATTER = new SimpleDateFormat("ss.SSS");
+ private static long lastAllDevicesRefreshTS = -1; // timestamp when the last request for all device refresh was sent
+ protected static final int ALL_DEVICES_REFRESH_INTERVAL_MSEC = 2000; // interval in msec before sending another all
+ // devices refresh request
+
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = OpenWebNetBindingConstants.AUTOMATION_SUPPORTED_THING_TYPES;
// moving states
} catch (OWNException e) {
logger.debug("Exception while requesting channel {} state: {}", channel, e.getMessage(), e);
}
+ } else {
+ logger.warn("Could not requestChannelState(): deviceWhere is null");
+ }
+ }
+
+ @Override
+ protected void refreshDevice(boolean refreshAll) {
+ OpenWebNetBridgeHandler brH = bridgeHandler;
+ if (brH != null) {
+ if (brH.isBusGateway() && refreshAll) {
+ long now = System.currentTimeMillis();
+ if (now - lastAllDevicesRefreshTS > ALL_DEVICES_REFRESH_INTERVAL_MSEC) {
+ try {
+ send(Automation.requestStatus(WhereLightAutom.GENERAL.value()));
+ lastAllDevicesRefreshTS = now;
+ } catch (OWNException e) {
+ logger.warn("Excpetion while requesting all devices refresh: {}", e.getMessage());
+ }
+ } else {
+ logger.debug("Refresh all devices just sent...");
+ }
+ } else {
+ requestChannelState(new ChannelUID("any")); // channel here does not make any difference
+ }
}
}
*/
package org.openhab.binding.openwebnet.handler;
-import static org.openhab.binding.openwebnet.OpenWebNetBindingConstants.PROPERTY_FIRMWARE_VERSION;
-import static org.openhab.binding.openwebnet.OpenWebNetBindingConstants.PROPERTY_SERIAL_NO;
-import static org.openhab.binding.openwebnet.OpenWebNetBindingConstants.THING_TYPE_ZB_GATEWAY;
+import static org.openhab.binding.openwebnet.OpenWebNetBindingConstants.*;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.config.core.status.ConfigStatusMessage;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.ChannelUID;
+import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.binding.ConfigStatusBridgeHandler;
import org.openhab.core.thing.binding.ThingHandlerService;
import org.openhab.core.types.Command;
+import org.openhab.core.types.RefreshType;
import org.openwebnet4j.BUSGateway;
import org.openwebnet4j.GatewayListener;
import org.openwebnet4j.OpenDeviceType;
private static final int GATEWAY_ONLINE_TIMEOUT_SEC = 20; // Time to wait for the gateway to become connected
+ private static final int REFRESH_ALL_DEVICES_DELAY_MSEC = 500; // Delay to wait before sending all devices refresh
+ // request after a connect/reconnect
+
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = OpenWebNetBindingConstants.BRIDGE_SUPPORTED_THING_TYPES;
// ConcurrentHashMap of devices registered to this BridgeHandler
public @Nullable OpenWebNetDeviceDiscoveryService deviceDiscoveryService;
private boolean reconnecting = false; // we are trying to reconnect to gateway
+ private @Nullable ScheduledFuture<?> refreshSchedule;
+
private boolean scanIsActive = false; // a device scan has been activated by OpenWebNetDeviceDiscoveryService;
private boolean discoveryByActivation;
if (thing.getStatus().equals(ThingStatus.UNKNOWN)) {
logger.info("status still UNKNOWN. Setting device={} to OFFLINE", thing.getUID());
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
- "Could not connect to gateway before " + GATEWAY_ONLINE_TIMEOUT_SEC + "s");
+ "@text/offline.comm-error-timeout");
}
}, GATEWAY_ONLINE_TIMEOUT_SEC, TimeUnit.SECONDS);
logger.debug("bridge {} initialization completed", thing.getUID());
public void handleCommand(ChannelUID channelUID, Command command) {
logger.debug("handleCommand (command={} - channel={})", command, channelUID);
OpenGateway gw = gateway;
- if (gw != null && !gw.isConnected()) {
+ if (gw == null || !gw.isConnected()) {
logger.warn("Gateway is NOT connected, skipping command");
return;
} else {
- logger.warn("Channel not supported: channel={}", channelUID);
+ if (command instanceof RefreshType) {
+ refreshAllDevices();
+ } else {
+ logger.warn("Command or channel not supported: channel={} command={}", channelUID, command);
+ }
}
}
@Override
public void dispose() {
+ ScheduledFuture<?> rSc = refreshSchedule;
+ if (rSc != null) {
+ rSc.cancel(true);
+ }
disconnectGateway();
super.dispose();
}
return registeredDevices.get(ownId);
}
+ private void refreshAllDevices() {
+ logger.debug("Refreshing all devices for bridge {}", thing.getUID());
+ for (Thing ownThing : getThing().getThings()) {
+ OpenWebNetThingHandler hndlr = (OpenWebNetThingHandler) ownThing.getHandler();
+ if (hndlr != null) {
+ hndlr.refreshDevice(true);
+ }
+ }
+ }
+
@Override
public void onEventMessage(@Nullable OpenMessage msg) {
logger.trace("RECEIVED <<<<< {}", msg);
logger.info("properties updated for bridge '{}'", thing.getUID());
}
updateStatus(ThingStatus.ONLINE);
+ // schedule a refresh for all devices
+ refreshSchedule = scheduler.schedule(this::refreshAllDevices, REFRESH_ALL_DEVICES_DELAY_MSEC,
+ TimeUnit.MILLISECONDS);
}
@Override
}
logger.info("---- ON CONNECTION ERROR for gateway {}: {}", gateway, errMsg);
isGatewayConnected = false;
- updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, errMsg);
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
+ "@text/offline.comm-error-connection" + " (onConnectionError - " + errMsg + ")");
tryReconnectGateway();
}
}
logger.info("---- DISCONNECTED from gateway {}. OWNException: {}", gateway, errMsg);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
- "Disconnected from gateway (onDisconnected - " + errMsg + ")");
+ "@text/offline.comm-error-disconnected" + " (onDisconnected - " + errMsg + ")");
tryReconnectGateway();
}
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
- + ")");
+ "@text/offline.conf-error-auth" + " (" + e + ")");
}
} else {
- logger.debug("---- reconnecting=true, do nothing");
+ logger.debug("---- reconnecting=true");
}
} else {
logger.warn("---- cannot start RECONNECT, gateway is null");
this.updateProperty(PROPERTY_FIRMWARE_VERSION, gw.getFirmwareVersion());
logger.debug("gw firmware version: {}", gw.getFirmwareVersion());
}
+
+ // schedule a refresh for all devices
+ refreshSchedule = scheduler.schedule(this::refreshAllDevices, REFRESH_ALL_DEVICES_DELAY_MSEC,
+ TimeUnit.MILLISECONDS);
}
}
private final Logger logger = LoggerFactory.getLogger(OpenWebNetEnergyHandler.class);
- public final static Set<ThingTypeUID> SUPPORTED_THING_TYPES = OpenWebNetBindingConstants.ENERGY_MANAGEMENT_SUPPORTED_THING_TYPES;
+ public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = OpenWebNetBindingConstants.ENERGY_MANAGEMENT_SUPPORTED_THING_TYPES;
public OpenWebNetEnergyHandler(Thing thing) {
super(thing);
@Override
protected void requestChannelState(ChannelUID channel) {
logger.debug("requestChannelState() thingUID={} channel={}", thing.getUID(), channel.getId());
- try {
- bridgeHandler.gateway.send(EnergyManagement.requestActivePower(deviceWhere.value()));
- } catch (OWNException e) {
- logger.warn("requestChannelState() OWNException thingUID={} channel={}: {}", thing.getUID(),
- channel.getId(), e.getMessage());
+ Where w = deviceWhere;
+ if (w != null) {
+ try {
+ send(EnergyManagement.requestActivePower(w.value()));
+ } catch (OWNException e) {
+ logger.debug("Exception while requesting channel {} state: {}", channel, e.getMessage(), e);
+ }
+ } else {
+ logger.warn("Could not requestChannelState(): deviceWhere is null");
}
}
+ @Override
+ protected void refreshDevice(boolean refreshAll) {
+ requestChannelState(new ChannelUID("any:any:any:any"));
+ }
+
@Override
protected void handleChannelCommand(ChannelUID channel, Command command) {
logger.warn("handleChannelCommand() Read only channel, unsupported command {}", command);
@Override
protected void requestChannelState(ChannelUID channel) {
// do nothing
- logger.warn("There are no channels");
+ logger.warn("Generic: there are no channels");
+ }
+
+ @Override
+ protected void refreshDevice(boolean refreshAll) {
+ // do nothing
+ logger.warn("Generic: nothing to refresh");
}
@Override
protected void handleChannelCommand(ChannelUID channel, Command command) {
// do nothing
- logger.warn("There are no channels");
+ logger.warn("Generic: there are no channels");
}
@Override
protected void handleMessage(BaseOpenMessage msg) {
super.handleMessage(msg);
// do nothing
- logger.warn("handleMessage(): Nothing to do!");
+ logger.warn("Generic: handleMessage() nothing to do!");
}
}
private static final int UNKNOWN_STATE = 1000;
+ private static long lastAllDevicesRefreshTS = -1; // timestamp when the last request for all device refresh was sent
+ // for this handler
+
+ protected static final int ALL_DEVICES_REFRESH_INTERVAL_MSEC = 2000; // interval in msec before sending another all
+ // devices refresh request
+
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
lastStatusRequestSentTS = System.currentTimeMillis();
Response res = send(Lighting.requestStatus(toWhere(channelId)));
if (res != null && res.isSuccess()) {
- // set thing online if not already
+ // set thing online, if not already
ThingStatus ts = getThing().getStatus();
if (ThingStatus.ONLINE != ts && ThingStatus.REMOVING != ts && ThingStatus.REMOVED != ts) {
updateStatus(ThingStatus.ONLINE);
} catch (OWNException e) {
logger.warn("requestStatus() Exception while requesting light state: {}", e.getMessage());
}
+ } else {
+ logger.warn("Could not requestStatus(): deviceWhere is null");
+ }
+ }
+
+ @Override
+ protected void refreshDevice(boolean refreshAll) {
+ OpenWebNetBridgeHandler brH = bridgeHandler;
+ if (brH != null) {
+ if (brH.isBusGateway() && refreshAll) {
+ long now = System.currentTimeMillis();
+ if (now - lastAllDevicesRefreshTS > ALL_DEVICES_REFRESH_INTERVAL_MSEC) {
+ try {
+ send(Lighting.requestStatus(WhereLightAutom.GENERAL.value()));
+ lastAllDevicesRefreshTS = now;
+ } catch (OWNException e) {
+ logger.warn("Excpetion while requesting all devices refresh: {}", e.getMessage());
+ }
+ } else {
+ logger.debug("Refresh all devices just sent...");
+ }
+ } else { // USB or BUS-single device
+ ThingTypeUID thingType = thing.getThingTypeUID();
+ if (THING_TYPE_ZB_ON_OFF_SWITCH_2UNITS.equals(thingType)) {
+ // Unfortunately using USB Gateway OpenWebNet both switch endpoints cannot be requested at the same
+ // time using UNIT 00 because USB stick returns NACK, so we need to send a request status for both
+ // endpoints
+ requestStatus(CHANNEL_SWITCH_02);
+ }
+ requestStatus(""); // channel here does not make any difference, see {@link #toWhere()}
+ }
}
}
* @param channelId the channelId string
**/
@Nullable
- protected String toWhere(String channelId) {
+ private String toWhere(String channelId) {
Where w = deviceWhere;
if (w != null) {
OpenWebNetBridgeHandler brH = bridgeHandler;
OpenWebNetBridgeHandler brH = (OpenWebNetBridgeHandler) bridge.getHandler();
if (brH != null) {
bridgeHandler = brH;
- Object deviceWhereConfig = getConfig().get(CONFIG_PROPERTY_WHERE);
- if (!(deviceWhereConfig instanceof String)) {
+
+ final String configDeviceWhere = (String) getConfig().get(CONFIG_PROPERTY_WHERE);
+ if (configDeviceWhere == null) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
- "WHERE parameter in configuration is null or invalid");
+ "@text/offline.conf-error-where");
} else {
- String deviceWhereStr = (String) getConfig().get(CONFIG_PROPERTY_WHERE);
Where w;
try {
if (brH.isBusGateway()) {
- w = buildBusWhere(deviceWhereStr);
+ w = buildBusWhere(configDeviceWhere);
} else {
- w = new WhereZigBee(deviceWhereStr);
+ w = new WhereZigBee(configDeviceWhere);
}
} catch (IllegalArgumentException ia) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
- "WHERE parameter in configuration is invalid");
+ "@text/offline.conf-error-where");
return;
}
deviceWhere = w;
updateProperties(properties);
brH.registerDevice(oid, this);
logger.debug("associated thing to bridge with ownId={}", ownId);
- updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.NONE, "waiting state update...");
+ updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.NONE, "@text/unknown.waiting-state");
}
}
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
- "No bridge associated, please assign a bridge in thing configuration.");
+ "@text/offline.conf-error-no-bridge");
}
}
}
/**
- * Helper method to send OWN messages from ThingsHandlers
+ * Helper method to send OWN messages from ThingHandlers
*/
protected @Nullable Response send(OpenMessage msg) throws OWNException {
- OpenWebNetBridgeHandler handler = bridgeHandler;
- if (handler != null) {
- OpenGateway gw = handler.gateway;
+ OpenWebNetBridgeHandler bh = bridgeHandler;
+ if (bh != null) {
+ OpenGateway gw = bh.gateway;
if (gw != null) {
return gw.send(msg);
}
}
+ logger.warn("Couldn't send message {}: handler or gateway is null", msg);
return null;
}
}
/**
- * Request to gateway state for thing channel. It must be implemented by each specific device handler.
+ * Request the state for the specified channel
*
- * @param channel the channel to request the state for
+ * @param channel the {@link ChannelUID} to request the state for
*/
protected abstract void requestChannelState(ChannelUID channel);
+ /**
+ * Refresh the device
+ *
+ * @param refreshAll set true if all devices of the binding should be refreshed with one command, if possible
+ */
+ protected abstract void refreshDevice(boolean refreshAll);
+
/**
* 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).
# Thing status descriptions
offline.conf-error-no-ip-address = Cannot connect to gateway. No host/IP has been provided in Bridge configuration.
offline.conf-error-no-serial-port = Cannot connect to gateway. No serial port has been provided in Bridge configuration.
-offline.wrong-configuration = Invalid configuration. Check Thing configuration parameters.
+offline.conf-error-where = WHERE parameter in Thing configuration is null or invalid
+offline.conf-error-no-bridge = No bridge associated, please assign a bridge in Thing configuration.
+offline.conf-error-auth = Authentication error. Check gateway password in Thing Configuration Parameters
+
+offline.comm-error-disconnected = Disconnected from gateway
+offline.comm-error-timeout = Connection to gateway timed out
+offline.comm-error-connection = Could not connect to gateway
+
+unknown.waiting-state = Waiting state update...
try {
bmsg = (BaseOpenMessage) BaseOpenMessage.parse(msg);
} catch (FrameException e) {
- logger.warn("something is wrong in the test table. ownIdFromMessage test will be skipped");
+ logger.warn("something is wrong in the test table ({}). ownIdFromMessage test will be skipped",
+ e.getMessage());
}
this.msg = bmsg;
this.norm = norm;