[openwebnet] Fix Things synchronization at boot / reconnect ()

* [openwebnet] Fixes : shutters are not refreshed at boot
* [openwebnet] added refreshDelay property
* [openwebnet] updated openwebnet4j to 0.7.1
* [openwebnet] improved where parameter description
* [openwebnet] updated to own4j 0.7.1 release
* [openwebnet] improved synch at boot
* [openwebnet] cleaned up refreshChannelState() and refreshDevice(), moved code to base class.
Added supportsRefreshAllDevices()
* [openwebnet] moved lastAllDevicesRefreshTS to sub-classes.
Set CEN/CEN+ things to ONLINE automatically.
* [openwebnet] improved comments/javadocs. Added connectSchedule to dispose()

Signed-off-by: Massimo Valla <mvcode00@gmail.com>
This commit is contained in:
M Valla 2022-02-26 19:38:49 +01:00 committed by GitHub
parent 9fd87f52ef
commit d2d6ce5782
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 318 additions and 194 deletions

@ -123,13 +123,13 @@ Devices can be discovered automatically using an Inbox Scan after a gateway has
For any manually added device, you must configure:
- the associated gateway (`Parent Bridge` menu)
- the `where` configuration parameter (`OpenWebNet Address`):
- example for BUS/SCS:
- light device with WHERE address Point to Point `A=2 PL=4` --> `where="24"`
- light device with WHERE address Point to Point `A=03 PL=11` on local bus --> `where="0311#4#01"`
- CEN scenario with WHERE address Point to Point `A=05 PL=12` --> `where="0512"`
- CEN+ configured scenario `5`: add a `2` before --> `where="25"`
- the associated gateway Thing (`Parent Bridge` menu)
- the `where` configuration parameter (`OpenWebNet Address`): this is the OpenWebNet address configured for the device in the BTicino/Legrand system. This address can be found either on the device itself (Physical configuration, using jumpers in case of BUS) or through the MyHOME_Suite software (Virtual configuration). The address can have several formats depending on the device/system:
- example for BUS/SCS system, address Point-to-point with Area (A) and Light-point (PL):
- light device A=`2` (Area 2), PL=`4` (Light-point 4) --> `where="24"`
- light device A=`03`, PL=`11` on local bus --> `where="0311#4#01"`
- CEN scenario A=`05`, PL=`12` --> `where="0512"`
- CEN+ scenario `5`: add a `2` before --> `where="25"`
- Dry Contact or IR Interface `99`: add a `3` before --> `where="399"`
- 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`.
@ -141,7 +141,7 @@ In BTicino MyHOME Thermoregulation (WHO=4) each **zone** has associated a thermo
Thermo zones can be configured defining a `bus_thermo_zone` Thing for each zone with the following parameters:
- the `where` configuration parameter (`OpenWebNet Address`):
- example BUS/SCS Thermo zone `1` --> `where="1"`
- example BUS/SCS zone `1` --> `where="1"`
- the `standAlone` configuration parameter (`boolean`, default: `true`): identifies if the zone is managed or not by a Central Unit (4 or 99 zones). `standAlone=true` means no Central Unit is present in the system.
Temperature sensors can be configured defining a `bus_thermo_sensor` Thing with the following parameters:

@ -23,7 +23,7 @@
<dependency>
<groupId>io.github.openwebnet4j</groupId>
<artifactId>openwebnet4j</artifactId>
<version>0.6.0</version>
<version>0.7.1</version>
<scope>compile</scope>
</dependency>

@ -138,7 +138,7 @@ public class OpenWebNetBindingConstants {
public static final String CONFIG_PROPERTY_WHERE = "where";
public static final String CONFIG_PROPERTY_SHUTTER_RUN = "shutterRun";
public static final String CONFIG_PROPERTY_SCENARIO_BUTTONS = "buttons";
// BUS gw config properties
// gw config properties
public static final String CONFIG_PROPERTY_HOST = "host";
public static final String CONFIG_PROPERTY_SERIAL_PORT = "serialPort";
// properties

@ -54,25 +54,25 @@ public class OpenWebNetHandlerFactory extends BaseThingHandlerFactory {
@Override
protected @Nullable ThingHandler createHandler(Thing thing) {
if (OpenWebNetBridgeHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) {
logger.debug("creating NEW BRIDGE Handler");
logger.debug("creating NEW BRIDGE Handler --- {}", thing.getUID());
return new OpenWebNetBridgeHandler((Bridge) thing);
} else if (OpenWebNetGenericHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) {
logger.debug("creating NEW GENERIC Handler");
logger.debug("creating NEW GENERIC Handler --- {}", thing.getUID());
return new OpenWebNetGenericHandler(thing);
} else if (OpenWebNetLightingHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) {
logger.debug("creating NEW LIGHTING Handler");
logger.debug("creating NEW LIGHTING Handler --- {}", thing.getUID());
return new OpenWebNetLightingHandler(thing);
} else if (OpenWebNetAutomationHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) {
logger.debug("creating NEW AUTOMATION Handler");
logger.debug("creating NEW AUTOMATION Handler --- {}", thing.getUID());
return new OpenWebNetAutomationHandler(thing);
} else if (OpenWebNetEnergyHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) {
logger.debug("creating NEW ENERGY Handler");
logger.debug("creating NEW ENERGY Handler --- {}", thing.getUID());
return new OpenWebNetEnergyHandler(thing);
} else if (OpenWebNetThermoregulationHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) {
logger.debug("creating NEW THERMO Handler");
logger.debug("creating NEW THERMO Handler --- {}", thing.getUID());
return new OpenWebNetThermoregulationHandler(thing);
} else if (OpenWebNetScenarioHandler.SUPPORTED_THING_TYPES.contains(thing.getThingTypeUID())) {
logger.debug("creating NEW SCENARIO Handler");
logger.debug("creating NEW SCENARIO Handler --- {}", thing.getUID());
return new OpenWebNetScenarioHandler(thing);
}
logger.warn("ThingType {} is not supported by this binding", thing.getThingTypeUID());

@ -59,12 +59,10 @@ public class OpenWebNetAutomationHandler extends OpenWebNetThingHandler {
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 = 60000; // interval in msec before sending another all
// devices refresh request
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = OpenWebNetBindingConstants.AUTOMATION_SUPPORTED_THING_TYPES;
private static long lastAllDevicesRefreshTS = 0; // ts when last all device refresh was sent for this handler
// moving states
public static final int MOVING_STATE_STOPPED = 0;
public static final int MOVING_STATE_MOVING_UP = 1;
@ -141,38 +139,36 @@ public class OpenWebNetAutomationHandler extends OpenWebNetThingHandler {
@Override
protected void requestChannelState(ChannelUID channel) {
logger.debug("requestChannelState() thingUID={} channel={}", thing.getUID(), channel.getId());
super.requestChannelState(channel);
Where w = deviceWhere;
if (w != null) {
try {
send(Automation.requestStatus(w.value()));
} catch (OWNException e) {
logger.debug("Exception while requesting channel {} state: {}", channel, e.getMessage(), e);
logger.debug("Exception while requesting state for channel {}: {} ", channel, e.getMessage());
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
}
} else {
logger.warn("Could not requestChannelState(): deviceWhere is null");
}
}
@Override
protected long getRefreshAllLastTS() {
return lastAllDevicesRefreshTS;
};
@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
if (refreshAll) {
logger.debug("--- refreshDevice() : refreshing GENERAL... ({})", thing.getUID());
try {
send(Automation.requestStatus(WhereLightAutom.GENERAL.value()));
lastAllDevicesRefreshTS = System.currentTimeMillis();
} catch (OWNException e) {
logger.warn("Excpetion while requesting all devices refresh: {}", e.getMessage());
}
} else {
logger.debug("--- refreshDevice() : refreshing SINGLE... ({})", thing.getUID());
requestChannelState(new ChannelUID(thing.getUID(), CHANNEL_SHUTTER));
}
}

@ -16,6 +16,7 @@ import static org.openhab.binding.openwebnet.internal.OpenWebNetBindingConstants
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
@ -79,6 +80,9 @@ public class OpenWebNetBridgeHandler extends ConfigStatusBridgeHandler implement
private static final int REFRESH_ALL_DEVICES_DELAY_MSEC = 500; // Delay to wait before sending all devices refresh
// request after a connect/reconnect
private static final int REFRESH_ALL_CHECK_DELAY_SEC = 20;
private static long lastRegisteredDeviceTS = -1; // timestamp when the last device has been associated to the bridge
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = OpenWebNetBindingConstants.BRIDGE_SUPPORTED_THING_TYPES;
@ -94,7 +98,8 @@ public class OpenWebNetBridgeHandler extends ConfigStatusBridgeHandler implement
public @Nullable OpenWebNetDeviceDiscoveryService deviceDiscoveryService;
private boolean reconnecting = false; // we are trying to reconnect to gateway
private @Nullable ScheduledFuture<?> refreshSchedule;
private @Nullable ScheduledFuture<?> refreshAllSchedule;
private @Nullable ScheduledFuture<?> connectSchedule;
private boolean scanIsActive = false; // a device scan has been activated by OpenWebNetDeviceDiscoveryService;
private boolean discoveryByActivation;
@ -128,8 +133,8 @@ public class OpenWebNetBridgeHandler extends ConfigStatusBridgeHandler implement
logger.debug("Trying to connect gateway {}... ", gw);
try {
gw.connect();
scheduler.schedule(() -> {
// if status is still UNKNOWN after timer ends, set the device as OFFLINE
connectSchedule = scheduler.schedule(() -> {
// if status is still UNKNOWN after timer ends, set the device OFFLINE
if (thing.getStatus().equals(ThingStatus.UNKNOWN)) {
logger.info("status still UNKNOWN. Setting device={} to OFFLINE", thing.getUID());
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
@ -167,6 +172,7 @@ public class OpenWebNetBridgeHandler extends ConfigStatusBridgeHandler implement
*/
private @Nullable OpenGateway initBusGateway() {
logger.debug("Initializing BUS gateway");
OpenWebNetBusBridgeConfig busBridgeConfig = getConfigAs(OpenWebNetBusBridgeConfig.class);
String host = busBridgeConfig.getHost();
if (host == null || host.isEmpty()) {
@ -199,7 +205,7 @@ public class OpenWebNetBridgeHandler extends ConfigStatusBridgeHandler implement
return;
} else {
if (command instanceof RefreshType) {
refreshAllDevices();
refreshAllBridgeDevices();
} else {
logger.warn("Command or channel not supported: channel={} command={}", channelUID, command);
}
@ -219,10 +225,14 @@ public class OpenWebNetBridgeHandler extends ConfigStatusBridgeHandler implement
@Override
public void dispose() {
ScheduledFuture<?> rSc = refreshSchedule;
ScheduledFuture<?> rSc = refreshAllSchedule;
if (rSc != null) {
rSc.cancel(true);
}
ScheduledFuture<?> cs = connectSchedule;
if (cs != null) {
cs.cancel(true);
}
disconnectGateway();
super.dispose();
}
@ -366,6 +376,7 @@ public class OpenWebNetBridgeHandler extends ConfigStatusBridgeHandler implement
logger.warn("registering device with an existing ownId={}", ownId);
}
registeredDevices.put(ownId, thingHandler);
lastRegisteredDeviceTS = System.currentTimeMillis();
logger.debug("registered device ownId={}, thing={}", ownId, thingHandler.getThing().getUID());
}
@ -392,13 +403,62 @@ public class OpenWebNetBridgeHandler extends ConfigStatusBridgeHandler implement
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);
private void refreshAllBridgeDevices() {
logger.debug("--- --- ABOUT TO REFRESH ALL devices for bridge {}", thing.getUID());
int howMany = 0;
final List<Thing> things = getThing().getThings();
int total = things.size();
logger.debug("--- FOUND {} things by getThings()", total);
if (total > 0) {
if (registeredDevices.isEmpty()
|| (System.currentTimeMillis() - lastRegisteredDeviceTS < REFRESH_ALL_DEVICES_DELAY_MSEC)) {
// if a device has been registered with the bridge just now, let's wait for other devices: re-schedule
// refreshAllDevices
logger.debug(
"--- REGISTER device not started or just called... re-scheduling refreshAllBridgeDevices()");
refreshAllSchedule = scheduler.schedule(this::refreshAllBridgeDevices, REFRESH_ALL_DEVICES_DELAY_MSEC,
TimeUnit.MILLISECONDS);
} else {
for (Thing ownThing : things) {
OpenWebNetThingHandler hndlr = (OpenWebNetThingHandler) ownThing.getHandler();
if (hndlr != null) {
howMany++;
logger.debug("--- REFRESHING ALL DEVICES FOR thing #{}/{}: {}", howMany, total,
ownThing.getUID());
hndlr.refreshAllDevices();
} else {
logger.warn("--- No handler for thing {}", ownThing.getUID());
}
}
logger.debug("--- --- COMPLETED REFRESH all devices for bridge {}", thing.getUID());
// set a check that all things are Online
refreshAllSchedule = scheduler.schedule(() -> checkAllRefreshed(things), REFRESH_ALL_CHECK_DELAY_SEC,
TimeUnit.SECONDS);
}
} else {
logger.debug("--- --- NO CHILD DEVICE to REFRESH for bridge {}", thing.getUID());
}
}
private void checkAllRefreshed(List<Thing> things) {
int howMany = 0;
int total = things.size();
boolean allOnline = true;
for (Thing ownThing : things) {
howMany++;
ThingStatus ts = ownThing.getStatus();
if (ThingStatus.ONLINE == ts) {
logger.debug("--- CHECKED ONLINE thing #{}/{}: {}", howMany, total, ownThing.getUID());
} else {
logger.debug("--- CHECKED ^^^OFFLINE^^^ thing #{}/{}: {}", howMany, total, ownThing.getUID());
allOnline = false;
}
}
if (allOnline) {
logger.debug("--- --- REFRESH CHECK COMPLETED: all things ONLINE for bridge {}", thing.getUID());
} else {
logger.debug("--- --- REFRESH CHECK COMPLETED: NOT all things ONLINE for bridge {}", thing.getUID());
}
}
@ -476,7 +536,7 @@ public class OpenWebNetBridgeHandler extends ConfigStatusBridgeHandler implement
}
updateStatus(ThingStatus.ONLINE);
// schedule a refresh for all devices
refreshSchedule = scheduler.schedule(this::refreshAllDevices, REFRESH_ALL_DEVICES_DELAY_MSEC,
refreshAllSchedule = scheduler.schedule(this::refreshAllBridgeDevices, REFRESH_ALL_DEVICES_DELAY_MSEC,
TimeUnit.MILLISECONDS);
}
@ -532,7 +592,7 @@ public class OpenWebNetBridgeHandler extends ConfigStatusBridgeHandler implement
"@text/offline.conf-error-auth" + " (" + e + ")");
}
} else {
logger.debug("---- reconnecting=true");
logger.debug("---- already reconnecting");
}
} else {
logger.warn("---- cannot start RECONNECT, gateway is null");
@ -550,9 +610,8 @@ public class OpenWebNetBridgeHandler extends ConfigStatusBridgeHandler implement
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,
refreshAllSchedule = scheduler.schedule(this::refreshAllBridgeDevices, REFRESH_ALL_DEVICES_DELAY_MSEC,
TimeUnit.MILLISECONDS);
}
}

@ -28,6 +28,7 @@ import org.openhab.core.library.unit.Units;
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.ThingStatusInfo;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.types.Command;
@ -146,22 +147,22 @@ public class OpenWebNetEnergyHandler extends OpenWebNetThingHandler {
@Override
protected void requestChannelState(ChannelUID channel) {
logger.debug("requestChannelState() thingUID={} channel={}", thing.getUID(), channel.getId());
super.requestChannelState(channel);
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);
logger.debug("Exception while requesting state for channel {}: {} ", channel, e.getMessage());
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
}
} else {
logger.warn("Could not requestChannelState(): deviceWhere is null");
}
}
@Override
protected void refreshDevice(boolean refreshAll) {
requestChannelState(new ChannelUID("any:any:any:any"));
logger.debug("--- refreshDevice() : refreshing SINGLE... ({})", thing.getUID());
requestChannelState(new ChannelUID(thing.getUID(), CHANNEL_POWER));
}
@Override

@ -26,10 +26,10 @@ import org.openhab.core.library.types.PercentType;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.types.Command;
import org.openwebnet4j.communication.OWNException;
import org.openwebnet4j.communication.Response;
import org.openwebnet4j.message.BaseOpenMessage;
import org.openwebnet4j.message.FrameException;
import org.openwebnet4j.message.Lighting;
@ -63,16 +63,12 @@ public class OpenWebNetLightingHandler extends OpenWebNetThingHandler {
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 = 60000; // 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
private static long lastAllDevicesRefreshTS = 0; // ts when last all device refresh was sent for this handler
private int brightness = UNKNOWN_STATE; // current brightness percent value for this device
private int brightnessBeforeOff = UNKNOWN_STATE; // latest brightness before device was set to off
@ -85,58 +81,43 @@ public class OpenWebNetLightingHandler extends OpenWebNetThingHandler {
@Override
protected void requestChannelState(ChannelUID channel) {
logger.debug("requestChannelState() thingUID={} channel={}", thing.getUID(), channel.getId());
requestStatus(channel.getId());
}
/** helper method to request light status based on channel */
private void requestStatus(String channelId) {
Where w = deviceWhere;
if (w != null) {
super.requestChannelState(channel);
if (deviceWhere != null) {
try {
lastStatusRequestSentTS = System.currentTimeMillis();
Response res = send(Lighting.requestStatus(toWhere(channelId)));
if (res != null && res.isSuccess()) {
// set thing online, if not already
ThingStatus ts = getThing().getStatus();
if (ThingStatus.ONLINE != ts && ThingStatus.REMOVING != ts && ThingStatus.REMOVED != ts) {
updateStatus(ThingStatus.ONLINE);
}
}
send(Lighting.requestStatus(toWhere(channel.getId())));
} catch (OWNException e) {
logger.warn("requestStatus() Exception while requesting light state: {}", e.getMessage());
logger.debug("Exception while requesting state for channel {}: {} ", channel, e.getMessage());
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
}
} else {
logger.warn("Could not requestStatus(): deviceWhere is null");
}
}
@Override
protected long getRefreshAllLastTS() {
return lastAllDevicesRefreshTS;
};
@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()}
if (refreshAll) {
logger.debug("--- refreshDevice() : refreshing GENERAL... ({})", thing.getUID());
try {
send(Lighting.requestStatus(WhereLightAutom.GENERAL.value()));
lastAllDevicesRefreshTS = System.currentTimeMillis();
} catch (OWNException e) {
logger.warn("Excpetion while requesting all devices refresh: {}", e.getMessage());
}
} else {
logger.debug("--- refreshDevice() : refreshing SINGLE... ({})", thing.getUID());
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
requestChannelState(new ChannelUID(thing.getUID(), CHANNEL_SWITCH_02));
}
requestChannelState(new ChannelUID(thing.getUID(), CHANNEL_SWITCH_01));
}
}
@ -291,7 +272,7 @@ public class OpenWebNetLightingHandler extends OpenWebNetThingHandler {
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);
requestChannelState(new ChannelUID(thing.getUID(), CHANNEL_BRIGHTNESS));
}, BRIGHTNESS_STATUS_REQUEST_DELAY_MSEC, TimeUnit.MILLISECONDS);
return;
} else {

@ -30,6 +30,8 @@ import org.openhab.core.library.types.OnOffType;
import org.openhab.core.thing.Channel;
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.ThingHandlerService;
import org.openhab.core.thing.binding.builder.ChannelBuilder;
@ -117,10 +119,8 @@ public class OpenWebNetScenarioHandler extends OpenWebNetThingHandler {
private boolean isDryContactIR = false;
private boolean isCENPlus = false;
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 = 10000; // interval in msec before sending another all
// devices refresh request
private static long lastAllDevicesRefreshTS = 0; // ts when last all device refresh was sent for this handler
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = OpenWebNetBindingConstants.SCENARIO_SUPPORTED_THING_TYPES;
@ -361,49 +361,50 @@ public class OpenWebNetScenarioHandler extends OpenWebNetThingHandler {
}
@Override
protected void refreshDevice(boolean refreshAll) {
protected void requestChannelState(ChannelUID channel) {
if (isDryContactIR) {
if (refreshAll) {
long now = System.currentTimeMillis();
if (now - lastAllDevicesRefreshTS > ALL_DEVICES_REFRESH_INTERVAL_MSEC) {
try {
send(CENPlusScenario.requestStatus("30"));
lastAllDevicesRefreshTS = now;
} catch (OWNException e) {
logger.warn("Excpetion while requesting all DryContact/IR devices refresh: {}", e.getMessage());
}
} else {
logger.debug("Refresh all devices just sent...");
super.requestChannelState(channel);
Where w = deviceWhere;
if (w != null) {
try {
send(CENPlusScenario.requestStatus(w.value()));
} catch (OWNException e) {
logger.debug("Exception while requesting state for channel {}: {} ", channel, e.getMessage());
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
}
} else {
requestState();
}
} else {
logger.debug("CEN/CEN+ channels are trigger channels and do not have state");
logger.debug("requestChannelState() CEN/CEN+ channels are trigger channels and do not have state.");
}
}
@Override
protected void requestChannelState(ChannelUID channel) {
if (isDryContactIR) {
requestState();
} else {
logger.debug("CEN/CEN+ channels are trigger channels and do not have state");
}
}
protected long getRefreshAllLastTS() {
return lastAllDevicesRefreshTS;
};
/* helper method to request DryContact/IR device state */
private void requestState() {
Where w = deviceWhere;
if (w != null) {
try {
send(CENPlusScenario.requestStatus(w.value()));
} catch (OWNException e) {
logger.warn("requestState() Exception while requesting device state: {} for thing {}", e.getMessage(),
thing.getUID());
@Override
protected void refreshDevice(boolean refreshAll) {
if (isDryContactIR) {
if (refreshAll) {
logger.debug("--- refreshDevice() : refreshing GENERAL... ({})", thing.getUID());
try {
send(CENPlusScenario.requestStatus("30"));
lastAllDevicesRefreshTS = System.currentTimeMillis();
} catch (OWNException e) {
logger.warn("Excpetion while requesting all devices refresh: {}", e.getMessage());
}
} else {
logger.debug("--- refreshDevice() : refreshing SINGLE... ({})", thing.getUID());
requestChannelState(new ChannelUID(thing.getUID(), CHANNEL_DRY_CONTACT_IR));
}
} else {
logger.warn("Could not requestState(): deviceWhere is null");
logger.debug("CEN/CEN+ channels are trigger channels and do not have state. Setting it ONLINE");
// put CEN/CEN+ scenario things to ONLINE automatically as they do not have state
ThingStatus ts = getThing().getStatus();
if (ThingStatus.ONLINE != ts && ThingStatus.REMOVING != ts && ThingStatus.REMOVED != ts) {
updateStatus(ThingStatus.ONLINE);
}
}
}

@ -97,9 +97,31 @@ public class OpenWebNetThermoregulationHandler extends OpenWebNetThingHandler {
@Override
protected void requestChannelState(ChannelUID channel) {
super.requestChannelState(channel);
refreshDevice(false);
}
@Override
protected void refreshDevice(boolean refreshAll) {
logger.debug("--- refreshDevice() : refreshing SINGLE... ({})", thing.getUID());
if (deviceWhere != null) {
String w = deviceWhere.value();
try {
send(Thermoregulation.requestTemperature(w));
if (!this.isTempSensor) {
// for bus_thermo_zone request also other single channels updates
send(Thermoregulation.requestSetPointTemperature(w));
send(Thermoregulation.requestFanCoilSpeed(w));
send(Thermoregulation.requestMode(w));
send(Thermoregulation.requestValvesStatus(w));
send(Thermoregulation.requestActuatorsStatus(w));
}
} catch (OWNException e) {
logger.warn("refreshDevice() where='{}' returned OWNException {}", w, e.getMessage());
}
}
}
@Override
protected Where buildBusWhere(String wStr) throws IllegalArgumentException {
WhereThermo wt = new WhereThermo(wStr);
@ -233,17 +255,17 @@ public class OpenWebNetThermoregulationHandler extends OpenWebNetThingHandler {
Thermoregulation.WhatThermo w = Thermoregulation.WhatThermo.fromValue(tmsg.getWhat().value());
if (w.mode() == null) {
if (w.getMode() == null) {
logger.debug("updateModeAndFunction() Could not parse Mode from: {}", tmsg.getFrameValue());
return;
}
if (w.function() == null) {
if (w.getFunction() == null) {
logger.debug("updateModeAndFunction() Could not parse Function from: {}", tmsg.getFrameValue());
return;
}
Thermoregulation.OperationMode mode = w.mode();
Thermoregulation.Function function = w.function();
Thermoregulation.OperationMode mode = w.getMode();
Thermoregulation.Function function = w.getFunction();
if (w == Thermoregulation.WhatThermo.HEATING) {
function = Thermoregulation.Function.HEATING;
@ -314,24 +336,4 @@ public class OpenWebNetThermoregulationHandler extends OpenWebNetThingHandler {
updateState(CHANNEL_ACTUATORS, UnDefType.UNDEF);
}
}
@Override
protected void refreshDevice(boolean refreshAll) {
if (deviceWhere != null) {
String w = deviceWhere.value();
try {
send(Thermoregulation.requestTemperature(w));
if (!this.isTempSensor) {
// for bus_thermo_zone request also other single channels updates
send(Thermoregulation.requestSetPointTemperature(w));
send(Thermoregulation.requestFanCoilSpeed(w));
send(Thermoregulation.requestMode(w));
send(Thermoregulation.requestValvesStatus(w));
send(Thermoregulation.requestActuatorsStatus(w));
}
} catch (OWNException e) {
logger.warn("refreshDevice() where='{}' returned OWNException {}", w, e.getMessage());
}
}
}
}

@ -29,6 +29,7 @@ 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.ThingStatusInfo;
import org.openhab.core.thing.binding.BaseThingHandler;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
@ -58,10 +59,14 @@ public abstract class OpenWebNetThingHandler extends BaseThingHandler {
protected @Nullable OpenWebNetBridgeHandler bridgeHandler;
protected @Nullable String ownId; // OpenWebNet identifier for this device: WHO.WHERE
protected @Nullable Where deviceWhere; // this device Where address
protected @Nullable Where deviceWhere; // this device WHERE address
protected @Nullable ScheduledFuture<?> requestChannelStateTimeout;
protected @Nullable ScheduledFuture<?> refreshTimeout;
private static final int ALL_DEVICES_REFRESH_INTERVAL_MSEC = 60_000; // interval before sending another
// refreshAllDevices request
public OpenWebNetThingHandler(Thing thing) {
super(thing);
}
@ -108,6 +113,15 @@ public abstract class OpenWebNetThingHandler extends BaseThingHandler {
}
}
@Override
public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
if (bridgeStatusInfo.getStatus() == ThingStatus.ONLINE) {
updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.NONE, "@text/unknown.waiting-state");
} else if (bridgeStatusInfo.getStatus() == ThingStatus.OFFLINE) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
}
}
@Override
public void handleCommand(ChannelUID channel, Command command) {
logger.debug("handleCommand() (command={} - channel={})", command, channel);
@ -115,23 +129,16 @@ 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", command, getThing().getUID());
logger.info("Cannot handle {} command for {}: gateway is not connected", command, thing.getUID());
return;
}
if (deviceWhere == null) {
logger.info("Cannot handle {} command for {}: 'where' parameter is not configured or is invalid",
command, getThing().getUID());
command, thing.getUID());
return;
}
if (command instanceof RefreshType) {
requestChannelState(channel);
// set a schedule to put device OFFLINE if no answer is received after THING_STATE_REQ_TIMEOUT_SEC
refreshTimeout = scheduler.schedule(() -> {
if (thing.getStatus().equals(ThingStatus.UNKNOWN)) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"Could not get channel state (timer expired)");
}
}, THING_STATE_REQ_TIMEOUT_SEC, TimeUnit.SECONDS);
} else {
handleChannelCommand(channel, command);
}
@ -142,16 +149,16 @@ public abstract class OpenWebNetThingHandler extends BaseThingHandler {
/**
* Handles a command for the specific channel for this thing.
* It must be implemented by each specific OpenWebNet category of device (WHO), based on channel
* It must be further implemented by each specific device handler.
*
* @param channel specific ChannleUID
* @param channel the {@link ChannelUID}
* @param command the Command to be executed
*/
protected abstract void handleChannelCommand(ChannelUID channel, Command command);
/**
* Handle incoming message from OWN network via bridge Thing, directed to this device. It should be further
* implemented by each specific device handler.
* Handle incoming message from OWN network via bridge Thing, directed to this device.
* It should be further implemented by each specific device handler.
*
* @param msg the message to handle
*/
@ -163,7 +170,9 @@ public abstract class OpenWebNetThingHandler extends BaseThingHandler {
}
/**
* Helper method to send OWN messages from ThingHandlers
* Helper method to send OWN messages from handler.
*
* @param msg the OpenMessage to be sent
*/
public @Nullable Response send(OpenMessage msg) throws OWNException {
OpenWebNetBridgeHandler bh = bridgeHandler;
@ -178,7 +187,9 @@ public abstract class OpenWebNetThingHandler extends BaseThingHandler {
}
/**
* Helper method to send with high priority OWN messages from ThingsHandlers
* Helper method to send with high priority OWN messages from handler.
*
* @param msg the OpenMessage to be sent
*/
protected @Nullable Response sendHighPriority(OpenMessage msg) throws OWNException {
OpenWebNetBridgeHandler handler = bridgeHandler;
@ -192,19 +203,87 @@ public abstract class OpenWebNetThingHandler extends BaseThingHandler {
}
/**
* Request the state for the specified channel
* Request the state for the specified channel. If no answer is received within THING_STATE_REQ_TIMEOUT_SEC, it is
* put OFFLINE.
* The method must be further implemented by each specific handler.
*
* @param channel the {@link ChannelUID} to request the state for
*/
protected abstract void requestChannelState(ChannelUID channel);
protected void requestChannelState(ChannelUID channel) {
logger.debug("requestChannelState() {}", channel);
Where w = deviceWhere;
if (w == null) {
logger.warn("Could not requestChannelState(): deviceWhere is null for thing {}", thing.getUID());
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "@text/offline.conf-error-where");
return;
}
// set a schedule to put device OFFLINE if no answer is received after THING_STATE_REQ_TIMEOUT_SEC
requestChannelStateTimeout = scheduler.schedule(() -> {
if (thing.getStatus().equals(ThingStatus.UNKNOWN)) {
logger.debug("requestChannelState() TIMEOUT for thing {}", thing.getUID());
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"@text/offline.comm-error-state");
}
}, THING_STATE_REQ_TIMEOUT_SEC, TimeUnit.SECONDS);
}
/**
* Refresh the device
* Refresh a device, possibly using a single OWN command if refreshAll=true and if supported.
* The method must be further implemented by each specific handler.
*
* @param refreshAll set true if all devices of the binding should be refreshed with one command, if possible
* @param refreshAll true if all devices for this handler must be refreshed with a single OWN command, if supported,
* otherwise just refresh the single device.
*/
protected abstract void refreshDevice(boolean refreshAll);
/**
* If the subclass supports refreshing all devices with a single OWN command, returns the last TS when a refreshAll
* was requested, or 0 if not requested yet. If not supported return -1 (default).
* It must be implemented by each subclass that supports all devices refresh.
*
* @return timestamp when last refreshAll command was sent, 0 if not requested yet, or -1 if it's not supported by
* subclass.
*/
protected long getRefreshAllLastTS() {
return -1;
};
/**
* Refresh all devices for this handler
*/
protected void refreshAllDevices() {
logger.debug("--- refreshAllDevices() for device {}", thing.getUID());
OpenWebNetBridgeHandler brH = bridgeHandler;
if (brH != null) {
long refAllTS = getRefreshAllLastTS();
logger.debug("{} support = {}", thing.getUID(), refAllTS >= 0);
if (brH.isBusGateway() && refAllTS >= 0) {
long now = System.currentTimeMillis();
if (now - refAllTS > ALL_DEVICES_REFRESH_INTERVAL_MSEC) {
logger.debug("--- refreshAllDevices() : refreshing ALL devices... ({})", thing.getUID());
refreshDevice(true);
} else {
logger.debug("--- refreshAllDevices() : refresh all devices just sent... ({})", thing.getUID());
}
// sometimes GENERAL (e.g. #*1*0##) refresh requests do not return state for all devices, so let's
// schedule another single refresh device, just in case
refreshTimeout = scheduler.schedule(() -> {
if (thing.getStatus().equals(ThingStatus.UNKNOWN)) {
logger.debug(
"--- refreshAllDevices() : schedule expired: --UNKNOWN-- status for {}. Refreshing it...",
thing.getUID());
refreshDevice(false);
} else {
logger.debug("--- refreshAllDevices() : schedule expired: ONLINE status for {}",
thing.getUID());
}
}, THING_STATE_REQ_TIMEOUT_SEC, TimeUnit.SECONDS);
} else { // USB device or AllDevicesRefresh not supported
refreshDevice(false);
}
}
}
/**
* 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).
@ -220,9 +299,13 @@ public abstract class OpenWebNetThingHandler extends BaseThingHandler {
if (bh != null && oid != null) {
bh.unregisterDevice(oid);
}
ScheduledFuture<?> sc = refreshTimeout;
if (sc != null) {
sc.cancel(true);
ScheduledFuture<?> rcst = requestChannelStateTimeout;
if (rcst != null) {
rcst.cancel(true);
}
ScheduledFuture<?> rt = refreshTimeout;
if (rt != null) {
rt.cancel(true);
}
super.dispose();
}

@ -14,5 +14,6 @@ offline.conf-error-auth = Authentication failed. Check gateway password in confi
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.
offline.comm-error-state = Could not get channel state.
unknown.waiting-state = Waiting state update...