From d2d6ce5782bfdae002d089d2e85565b3c4800666 Mon Sep 17 00:00:00 2001 From: M Valla <12682715+mvalla@users.noreply.github.com> Date: Sat, 26 Feb 2022 19:38:49 +0100 Subject: [PATCH] [openwebnet] Fix Things synchronization at boot / reconnect (#11975) * [openwebnet] Fixes #11972: 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 --- .../org.openhab.binding.openwebnet/README.md | 16 +-- .../org.openhab.binding.openwebnet/pom.xml | 2 +- .../internal/OpenWebNetBindingConstants.java | 2 +- .../internal/OpenWebNetHandlerFactory.java | 14 +- .../handler/OpenWebNetAutomationHandler.java | 44 +++--- .../handler/OpenWebNetBridgeHandler.java | 89 ++++++++++-- .../handler/OpenWebNetEnergyHandler.java | 11 +- .../handler/OpenWebNetLightingHandler.java | 81 +++++------ .../handler/OpenWebNetScenarioHandler.java | 73 +++++----- .../OpenWebNetThermoregulationHandler.java | 50 +++---- .../handler/OpenWebNetThingHandler.java | 129 ++++++++++++++---- .../OH-INF/i18n/openwebnet.properties | 1 + 12 files changed, 318 insertions(+), 194 deletions(-) diff --git a/bundles/org.openhab.binding.openwebnet/README.md b/bundles/org.openhab.binding.openwebnet/README.md index 734e86658..9587f3653 100644 --- a/bundles/org.openhab.binding.openwebnet/README.md +++ b/bundles/org.openhab.binding.openwebnet/README.md @@ -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: diff --git a/bundles/org.openhab.binding.openwebnet/pom.xml b/bundles/org.openhab.binding.openwebnet/pom.xml index a46c8481d..24b732b72 100644 --- a/bundles/org.openhab.binding.openwebnet/pom.xml +++ b/bundles/org.openhab.binding.openwebnet/pom.xml @@ -23,7 +23,7 @@ io.github.openwebnet4j openwebnet4j - 0.6.0 + 0.7.1 compile diff --git a/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/OpenWebNetBindingConstants.java b/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/OpenWebNetBindingConstants.java index f68924166..997f74318 100644 --- a/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/OpenWebNetBindingConstants.java +++ b/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/OpenWebNetBindingConstants.java @@ -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 diff --git a/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/OpenWebNetHandlerFactory.java b/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/OpenWebNetHandlerFactory.java index c92242d30..08077109b 100644 --- a/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/OpenWebNetHandlerFactory.java +++ b/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/OpenWebNetHandlerFactory.java @@ -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()); diff --git a/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/handler/OpenWebNetAutomationHandler.java b/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/handler/OpenWebNetAutomationHandler.java index ecb00fcb7..43437d580 100644 --- a/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/handler/OpenWebNetAutomationHandler.java +++ b/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/handler/OpenWebNetAutomationHandler.java @@ -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 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)); } } diff --git a/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/handler/OpenWebNetBridgeHandler.java b/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/handler/OpenWebNetBridgeHandler.java index a06b03d3e..e0b062916 100644 --- a/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/handler/OpenWebNetBridgeHandler.java +++ b/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/handler/OpenWebNetBridgeHandler.java @@ -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 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 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 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); } } diff --git a/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/handler/OpenWebNetEnergyHandler.java b/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/handler/OpenWebNetEnergyHandler.java index d0e1c6794..59c68e5d1 100644 --- a/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/handler/OpenWebNetEnergyHandler.java +++ b/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/handler/OpenWebNetEnergyHandler.java @@ -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 diff --git a/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/handler/OpenWebNetLightingHandler.java b/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/handler/OpenWebNetLightingHandler.java index 2236c123a..c8f2b61a5 100644 --- a/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/handler/OpenWebNetLightingHandler.java +++ b/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/handler/OpenWebNetLightingHandler.java @@ -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 { diff --git a/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/handler/OpenWebNetScenarioHandler.java b/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/handler/OpenWebNetScenarioHandler.java index 25547cbb9..33b61005f 100644 --- a/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/handler/OpenWebNetScenarioHandler.java +++ b/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/handler/OpenWebNetScenarioHandler.java @@ -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 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); + } } } diff --git a/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/handler/OpenWebNetThermoregulationHandler.java b/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/handler/OpenWebNetThermoregulationHandler.java index 42c7fd413..4dba0b938 100644 --- a/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/handler/OpenWebNetThermoregulationHandler.java +++ b/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/handler/OpenWebNetThermoregulationHandler.java @@ -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()); - } - } - } } diff --git a/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/handler/OpenWebNetThingHandler.java b/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/handler/OpenWebNetThingHandler.java index 1cc076890..3ab7523eb 100644 --- a/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/handler/OpenWebNetThingHandler.java +++ b/bundles/org.openhab.binding.openwebnet/src/main/java/org/openhab/binding/openwebnet/internal/handler/OpenWebNetThingHandler.java @@ -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(); } diff --git a/bundles/org.openhab.binding.openwebnet/src/main/resources/OH-INF/i18n/openwebnet.properties b/bundles/org.openhab.binding.openwebnet/src/main/resources/OH-INF/i18n/openwebnet.properties index bf3bac400..dd528e287 100644 --- a/bundles/org.openhab.binding.openwebnet/src/main/resources/OH-INF/i18n/openwebnet.properties +++ b/bundles/org.openhab.binding.openwebnet/src/main/resources/OH-INF/i18n/openwebnet.properties @@ -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...