]> git.basschouten.com Git - openhab-addons.git/commitdiff
[shelly] Misc changes (small fixes, log improvements, hardened leak prevention on...
authorMarkus Michels <markus7017@gmail.com>
Sat, 25 Nov 2023 08:46:22 +0000 (09:46 +0100)
committerGitHub <noreply@github.com>
Sat, 25 Nov 2023 08:46:22 +0000 (09:46 +0100)
* Misc changes (same fixes, log improvements, hardened leak prevention on
exceptions)

---------

Signed-off-by: Markus Michels <markus7017@gmail.com>
18 files changed:
bundles/org.openhab.binding.shelly/README.md
bundles/org.openhab.binding.shelly/pom.xml
bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/ShellyBindingConstants.java
bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/ShellyHandlerFactory.java
bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/api/ShellyDeviceProfile.java
bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/api/ShellyHttpClient.java
bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/api1/Shelly1CoapHandler.java
bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/api2/Shelly2ApiClient.java
bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/api2/Shelly2ApiRpc.java
bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/api2/Shelly2RpcSocket.java
bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/api2/ShellyBluApi.java
bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/discovery/ShellyDiscoveryParticipant.java
bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/handler/ShellyBaseHandler.java
bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/handler/ShellyBluSensorHandler.java
bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/handler/ShellyComponents.java
bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/manager/ShellyManagerOverviewPage.java
bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/provider/ShellyChannelDefinitions.java
bundles/org.openhab.binding.shelly/src/main/resources/OH-INF/i18n/shelly.properties

index 406cffc19b72b3b72b2b651d0b7a29a2e24db4b6..fde35de2e69ee01ce8bd58ce9a1327689d2ed130 100644 (file)
@@ -1451,8 +1451,6 @@ See notes on discovery of Shelly BLU devices above.
 |         | lowBattery    | Switch   | yes       | Low battery alert (< 20%)                                                           |
 | device  | gatewayDevice | String   | yes       | Shelly forwarded last status update (BLU gateway), could vary from packet to packet |
 
-
-
 ### Shelly BLU Door/Window Sensor (thing-type: shellybludw)
 
 See notes on discovery of Shelly BLU devices above.
@@ -1467,7 +1465,7 @@ See notes on discovery of Shelly BLU devices above.
 |         | lowBattery    | Switch   | yes       | Low battery alert (< 20%)                                                           |
 | device  | gatewayDevice | String   | yes       | Shelly forwarded last status update (BLU gateway), could vary from packet to packet |
 
-## Shelly BLU Motion Sensor (thing-type: shellyblumotion)
+### Shelly BLU Motion Sensor (thing-type: shellyblumotion)
 
 See notes on discovery of Shelly BLU devices above.
 
index 61c8dbf6cf88a9d061345ca4e658bfc98de6745b..429dc71bd521d9761a1dc9d75810558bab4d402f 100644 (file)
   <artifactId>org.openhab.binding.shelly</artifactId>
   <name>openHAB Add-ons :: Bundles :: Shelly Binding Gen1+2</name>
 
-  <dependencies>
-    <dependency>
-      <groupId>org.eclipse.jetty.websocket</groupId>
-      <artifactId>websocket-server</artifactId>
-      <version>9.4.46.v20220331</version>
-      <scope>compile</scope>
-    </dependency>
-  </dependencies>
-
 </project>
index 34957a2b44cd6c8ec0a15732fd49a8badb72b05f..1a8732b0ac3869abeafa89884d07e3f14974ce77 100755 (executable)
@@ -120,6 +120,7 @@ public class ShellyBindingConstants {
     public static final String PROPERTY_DEV_TYPE = "deviceType";
     public static final String PROPERTY_DEV_MODE = "deviceMode";
     public static final String PROPERTY_DEV_GEN = "deviceGeneration";
+    public static final String PROPERTY_DEV_AUTH = "deviceAuth";
     public static final String PROPERTY_GW_DEVICE = "gatewayDevice";
     public static final String PROPERTY_HWREV = "deviceHwRev";
     public static final String PROPERTY_HWBATCH = "deviceHwBatch";
index f0cf33be8bb1d4b68b13f58c9e526e31f31fedbe..111f60e4af099d816b99847f0f78a41d496b0b15 100755 (executable)
@@ -119,7 +119,7 @@ public class ShellyHandlerFactory extends BaseThingHandlerFactory {
         ShellyBaseHandler handler = null;
 
         if (thingType.equals(THING_TYPE_SHELLYPROTECTED_STR)) {
-            logger.debug("{}: Create new thing of type {} using ShellyProtectedHandler", thing.getLabel(),
+            logger.debug("{}: Create new thing of type {} using ShellyProtectedHandler", thing.getLabel(),
                     thingTypeUID.toString());
             handler = new ShellyProtectedHandler(thing, messages, bindingConfig, thingTable, coapServer, httpClient);
         } else if (thingType.equals(THING_TYPE_SHELLYBULB_STR) || thingType.equals(THING_TYPE_SHELLYDUO_STR)
index 23cce5cff1a9396eb8ab684bd41637de16e2c49f..e82314ae99109678180a0a79308c08c2ba19c371 100644 (file)
@@ -145,6 +145,7 @@ public class ShellyDeviceProfile {
             device.hostname = device.mac.length() >= 12 ? "shelly-" + device.mac.toUpperCase().substring(6, 11)
                     : "unknown";
         }
+        device.mode = getString(settings.mode).toLowerCase();
         name = getString(settings.name);
         hwRev = settings.hwinfo != null ? getString(settings.hwinfo.hwRevision) : "";
         hwBatchId = settings.hwinfo != null ? getString(settings.hwinfo.batchId.toString()) : "";
@@ -418,4 +419,18 @@ public class ShellyDeviceProfile {
         // If device is not yet intialized or the enabled property is missing we assume that CoIoT is enabled
         return true;
     }
+
+    public static String buildBluServiceName(String name, String mac) throws IllegalArgumentException {
+        String model = name.contains("-") ? substringBefore(name, "-") : name; // e.g. SBBT-02C or just SBDW
+        switch (model) {
+            case SHELLYDT_BLUBUTTON:
+                return (THING_TYPE_SHELLYBLUBUTTON_STR + "-" + mac).toLowerCase();
+            case SHELLYDT_BLUDW:
+                return (THING_TYPE_SHELLYBLUDW_STR + "-" + mac).toLowerCase();
+            case SHELLYDT_BLUMOTION:
+                return (THING_TYPE_SHELLYBLUMOTION_STR + "-" + mac).toLowerCase();
+            default:
+                throw new IllegalArgumentException("Unsupported BLU device model " + model);
+        }
+    }
 }
index 7ed4363450bc8b1778e92004bf82f2ae541d2fee..41fb0181342ebfb4578f64dcfa18acb580042c75 100644 (file)
@@ -120,6 +120,12 @@ public class ShellyHttpClient {
                 }
                 return apiResult.response; // successful
             } catch (ShellyApiException e) {
+                if (e.isHttpAccessUnauthorized() && !profile.isGen2 && !basicAuth && !config.password.isEmpty()) {
+                    logger.debug("{}: Access is unauthorized, auto-activate basic auth", thingName);
+                    basicAuth = true;
+                    apiResult = innerRequest(HttpMethod.GET, uri, null, "");
+                }
+
                 if (e.isConnectionError()
                         || (!e.isTimeout() && !apiResult.isHttpServerError()) && !apiResult.isNotFound()
                         || profile.hasBattery || (retries == 0)) {
@@ -129,9 +135,10 @@ public class ShellyHttpClient {
 
                 timeout = true;
                 timeoutErrors++; // count the retries
-                logger.debug("{}: API Timeout, retry #{} ({})", thingName, timeoutErrors, e.toString());
-
                 retries--;
+                if (profile.alwaysOn) {
+                    logger.debug("{}: API Timeout, retry #{} ({})", thingName, timeoutErrors, e.toString());
+                }
             }
         }
         throw new ShellyApiException("API Timeout or inconsistent result"); // successful
index 3de692e224034d5dc6a44f1dc14ff62fe6fe1d86..48eab5fb5c91304c77bf962461cad613ab229a43 100644 (file)
@@ -49,6 +49,7 @@ import org.openhab.binding.shelly.internal.config.ShellyThingConfiguration;
 import org.openhab.binding.shelly.internal.handler.ShellyColorUtils;
 import org.openhab.binding.shelly.internal.handler.ShellyThingInterface;
 import org.openhab.core.library.unit.Units;
+import org.openhab.core.thing.ThingStatusDetail;
 import org.openhab.core.types.State;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -241,7 +242,7 @@ public class Shelly1CoapHandler implements Shelly1CoapListener {
                         }
                         if (!coiotBound) {
                             thingHandler.updateProperties(PROPERTY_COAP_VERSION, sVersion);
-                            logger.debug("{}: CoIoT Version {} detected", thingName, iVersion);
+                            logger.debug("{}: CoIoT Version {} detected", thingName, iVersion);
                             if (iVersion == COIOT_VERSION_1) {
                                 coiot = new Shelly1CoIoTVersion1(thingName, thingHandler, blkMap, sensorMap);
                             } else if (iVersion == COIOT_VERSION_2) {
@@ -265,6 +266,13 @@ public class Shelly1CoapHandler implements Shelly1CoapListener {
                 }
             }
 
+            // Don't change state to online when thing is in status config error
+            // (e.g. auth failed, but device sends COAP packets via multicast)
+            if (thingHandler.getThingStatusDetail() == ThingStatusDetail.CONFIGURATION_ERROR) {
+                logger.debug("{}: The device is not configuired correctly, skip Coap packet", thingName);
+                return;
+            }
+
             // If we received a CoAP message successful the thing must be online
             thingHandler.setThingOnline();
 
@@ -441,7 +449,7 @@ public class Shelly1CoapHandler implements Shelly1CoapListener {
 
         List<CoIotSensor> sensorUpdates = list.generic;
         Map<String, State> updates = new TreeMap<String, State>();
-        logger.debug("{}: {} CoAP sensor updates received", thingName, sensorUpdates.size());
+        logger.debug("{}: {} CoAP sensor updates received", thingName, sensorUpdates.size());
         int failed = 0;
         ShellyColorUtils col = new ShellyColorUtils();
         for (int i = 0; i < sensorUpdates.size(); i++) {
index 7ff8a699218e75f0046d8bca530cc583041c1843..0e687307b74d8d97181a313772212902afcbbe38 100644 (file)
@@ -507,7 +507,7 @@ public class Shelly2ApiClient extends ShellyHttpClient {
         rs.isValid = sm.isValid = emeter.isValid = true;
         if (cs.state != null) {
             if (!getString(rs.state).equals(cs.state)) {
-                logger.debug("{}: Roller status changed from {} to {}, updateChannels={}", thingName, rs.state,
+                logger.debug("{}: Roller status changed from {} to {}, updateChannels={}", thingName, rs.state,
                         mapValue(MAP_ROLLER_STATE, cs.state), updateChannels);
             }
             rs.state = mapValue(MAP_ROLLER_STATE, cs.state);
index 17efc1d84ebfab1f3e137c0ed67e2f1035ae168a..c37a9c854c28bde27817f814645ecf1cc8e1d010 100644 (file)
@@ -131,14 +131,13 @@ public class Shelly2ApiRpc extends Shelly2ApiClient implements ShellyApiInterfac
 
     @Override
     public void initialize() throws ShellyApiException {
-        if (!initialized) {
-            rpcSocket = new Shelly2RpcSocket(thingName, thingTable, config.deviceIp);
-            rpcSocket.addMessageHandler(this);
-            initialized = true;
-        } else {
+        if (initialized) {
             logger.debug("{}: Disconnect Rpc Socket on initialize", thingName);
             disconnect();
         }
+        rpcSocket = new Shelly2RpcSocket(thingName, thingTable, config.deviceIp);
+        rpcSocket.addMessageHandler(this);
+        initialized = true;
     }
 
     @Override
@@ -1211,6 +1210,9 @@ public class Shelly2ApiRpc extends Shelly2ApiClient implements ShellyApiInterfac
     }
 
     private void disconnect() {
+        if (rpcSocket.isConnected()) {
+            logger.debug("{}: Disconnect Rpc Socket", thingName);
+        }
         rpcSocket.disconnect();
     }
 
@@ -1220,8 +1222,10 @@ public class Shelly2ApiRpc extends Shelly2ApiClient implements ShellyApiInterfac
 
     @Override
     public void close() {
-        logger.debug("{}: Closing Rpc API (socket is {}, discovery={})", thingName,
-                rpcSocket.isConnected() ? "connected" : "disconnected", discovery);
+        if (initialized || rpcSocket.isConnected()) {
+            logger.debug("{}: Closing Rpc API (socket is {}, discovery={})", thingName,
+                    rpcSocket.isConnected() ? "connected" : "disconnected", discovery);
+        }
         disconnect();
         initialized = false;
     }
index faff2738ee27ef412aebf3579b567ea1eb3e4ec1..308ab06ed67cef03c75e65a109cc6b1637376aa4 100644 (file)
@@ -206,18 +206,24 @@ public class Shelly2RpcSocket {
                 if (s.isOpen()) {
                     logger.debug("{}: Disconnecting WebSocket ({} -> {})", thingName, s.getLocalAddress(),
                             s.getRemoteAddress());
-                    s.disconnect();
                 }
+                s.disconnect();
                 s.close(StatusCode.NORMAL, "Socket closed");
                 session = null;
             }
-            client.stop();
         } catch (Exception e) {
             if (e.getCause() instanceof InterruptedException) {
                 logger.debug("{}: Unable to close socket - interrupted", thingName); // e.g. device was rebooted
             } else {
                 logger.debug("{}: Unable to close socket", thingName, e);
             }
+        } finally {
+            // make sure client is stopped / thread terminates / socket resource is free up
+            try {
+                client.stop();
+            } catch (Exception e) {
+                logger.debug("{}: Unable to close Web Socket", thingName, e);
+            }
         }
     }
 
index 7869b6fd79a37ee1a27b4d6d2906ebcc552366a1..5dbb81609aad5dd7249f99bd2078a616894b67fe 100644 (file)
@@ -15,7 +15,6 @@ package org.openhab.binding.shelly.internal.api2;
 import static org.openhab.binding.shelly.internal.ShellyBindingConstants.*;
 import static org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.*;
 import static org.openhab.binding.shelly.internal.api2.Shelly2ApiJsonDTO.*;
-import static org.openhab.binding.shelly.internal.discovery.ShellyThingCreator.*;
 import static org.openhab.binding.shelly.internal.util.ShellyUtils.*;
 
 import java.util.ArrayList;
@@ -112,11 +111,11 @@ public class ShellyBluApi extends Shelly2ApiRpc {
     public ShellySettingsDevice getDeviceInfo() throws ShellyApiException {
         ShellySettingsDevice info = new ShellySettingsDevice();
         info.hostname = !config.serviceName.isEmpty() ? config.serviceName : "";
-        info.fw = "1234";
-        info.type = "SBBT";
+        info.fw = "";
+        info.type = "BLU";
         info.mac = config.deviceAddress;
         info.auth = false;
-        info.gen = 99;
+        info.gen = 2;
         return info;
     }
 
@@ -136,13 +135,13 @@ public class ShellyBluApi extends Shelly2ApiRpc {
             profile.gateway = getThing().getProperty(PROPERTY_GW_DEVICE);
         }
 
-        ShellySettingsDevice device = getDeviceInfo();
+        profile.device = getDeviceInfo();
         if (config.serviceName.isEmpty()) {
             config.serviceName = getString(profile.device.hostname);
         }
-        profile.fwDate = substringBefore(device.fw, "/");
-        profile.fwVersion = substringBefore(ShellyDeviceProfile.extractFwVersion(device.fw.replace("/", "/v")), "-");
-        profile.status.update.oldVersion = profile.fwVersion;
+
+        // for now we have no API to get this information
+        profile.fwDate = profile.fwVersion = profile.status.update.oldVersion = "";
         profile.status.hasUpdate = profile.status.update.hasUpdate = false;
 
         if (profile.hasBattery) {
@@ -239,7 +238,7 @@ public class ShellyBluApi extends Shelly2ApiRpc {
                         }
                         logger.debug("{}: BLU Device discovered", thingName);
                         if (e.data.name != null) {
-                            profile.settings.name = buildBluServiceName(e.data.name, e.data.addr);
+                            profile.settings.name = ShellyDeviceProfile.buildBluServiceName(e.data.name, e.data.addr);
                         }
                         break;
                     case SHELLY2_EVENT_BLUDATA:
@@ -317,18 +316,4 @@ public class ShellyBluApi extends Shelly2ApiRpc {
         if (updated) {
         }
     }
-
-    public static String buildBluServiceName(String name, String mac) throws IllegalArgumentException {
-        String model = name.contains("-") ? substringBefore(name, "-") : name; // e.g. SBBT-02C or just SBDW
-        switch (model) {
-            case SHELLYDT_BLUBUTTON:
-                return (THING_TYPE_SHELLYBLUBUTTON_STR + "-" + mac).toLowerCase();
-            case SHELLYDT_BLUDW:
-                return (THING_TYPE_SHELLYBLUDW_STR + "-" + mac).toLowerCase();
-            case SHELLYDT_BLUMOTION:
-                return (THING_TYPE_SHELLYBLUMOTION_STR + "-" + mac).toLowerCase();
-            default:
-                throw new IllegalArgumentException("Unsupported BLU device model " + model);
-        }
-    }
 }
index 793b710f7db9e46274832217e2d2fe261ed42716..db5da2e1e5e3fb0ad55523e06e094f5969682e4e 100755 (executable)
@@ -145,15 +145,18 @@ public class ShellyDiscoveryParticipant implements MDNSDiscoveryParticipant {
 
             boolean gen2 = "2".equals(service.getPropertyString("gen"));
             ShellyApiInterface api = null;
+            boolean auth = false;
             ShellySettingsDevice devInfo;
             try {
                 api = gen2 ? new Shelly2ApiRpc(name, config, httpClient) : new Shelly1HttpApi(name, config, httpClient);
                 api.initialize();
                 devInfo = api.getDeviceInfo();
                 model = devInfo.type;
+                auth = devInfo.auth;
                 if (devInfo.name != null) {
                     deviceName = devInfo.name;
                 }
+
                 profile = api.getDeviceProfile(thingType, devInfo);
                 api.close();
                 logger.debug("{}: Shelly settings : {}", name, profile.settingsJson);
@@ -191,6 +194,7 @@ public class ShellyDiscoveryParticipant implements MDNSDiscoveryParticipant {
                 addProperty(properties, PROPERTY_DEV_TYPE, thingType);
                 addProperty(properties, PROPERTY_DEV_GEN, gen2 ? "2" : "1");
                 addProperty(properties, PROPERTY_DEV_MODE, mode);
+                addProperty(properties, PROPERTY_DEV_AUTH, auth ? "yes" : "no");
 
                 logger.debug("{}: Adding Shelly {}, UID={}", name, deviceName, thingUID.getAsString());
                 String thingLabel = deviceName.isEmpty() ? name + " - " + address
index 2871943edabb9544333f88d81eac066b8ea8574f..0daed7b48a0cdf7d8efbe78a5006c0f3cf1590f6 100755 (executable)
@@ -187,21 +187,7 @@ public abstract class ShellyBaseHandler extends BaseThingHandler
                         config.eventsSensorReport, config.eventsCoIoT, bindingConfig.autoCoIoT);
                 start = initializeThing();
             } catch (ShellyApiException e) {
-                ShellyApiResult res = e.getApiResult();
-                String mid = "";
-                if (e.isJsonError()) { // invalid JSON format
-                    mid = "offline.status-error-unexpected-error";
-                    start = false;
-                } else if (isAuthorizationFailed(res)) {
-                    mid = "offline.conf-error-access-denied";
-                    start = false;
-                } else if (profile.alwaysOn && e.isConnectionError()) {
-                    mid = "offline.status-error-connect";
-                }
-                if (!mid.isEmpty()) {
-                    setThingOffline(ThingStatusDetail.COMMUNICATION_ERROR, mid, e.toString());
-                }
-                logger.debug("{}: Unable to initialize: {}, retrying later", thingName, e.toString());
+                start = handleApiException(e);
             } catch (IllegalArgumentException e) {
                 logger.debug("{}: Unable to initialize, retrying later", thingName, e);
             } finally {
@@ -215,6 +201,43 @@ public abstract class ShellyBaseHandler extends BaseThingHandler
         }, 2, TimeUnit.SECONDS);
     }
 
+    private boolean handleApiException(ShellyApiException e) {
+        ShellyApiResult res = e.getApiResult();
+        ThingStatusDetail errorCode = ThingStatusDetail.COMMUNICATION_ERROR;
+        String status = "";
+        boolean retry = true;
+        if (e.isJsonError()) { // invalid JSON format
+            logger.debug("{}: Unable to parse API response: {}; json={}", thingName, res.getUrl(), res.response, e);
+            status = "offline.status-error-unexpected-error";
+            errorCode = ThingStatusDetail.CONFIGURATION_ERROR;
+            retry = false;
+        } else if (res.isHttpAccessUnauthorized()) {
+            status = "offline.conf-error-access-denied";
+            errorCode = ThingStatusDetail.CONFIGURATION_ERROR;
+            retry = false;
+        } else if (isWatchdogExpired()) {
+            status = "offline.status-error-watchdog";
+        } else if (res.httpCode >= 400) {
+            logger.debug("{}: Unexpected API result: {}/{}", thingName, res.httpCode, res.httpReason, e);
+            status = "offline.status-error-unexpected-api-result";
+            retry = false;
+        } else if (profile.alwaysOn && (e.isConnectionError() || res.isHttpTimeout())) {
+            status = "offline.status-error-connect";
+        }
+
+        if (!status.isEmpty()) {
+            setThingOffline(errorCode, status, e.toString());
+        } else {
+            logger.debug("{}: Unable to initialize: {}, retrying later", thingName, e.toString());
+        }
+
+        if (!retry) {
+            api.close();
+        }
+
+        return retry;
+    }
+
     @Override
     public ShellyThingConfiguration getThingConfig() {
         return config;
@@ -464,10 +487,11 @@ public abstract class ShellyBaseHandler extends BaseThingHandler
                 requestUpdates(1, false);
             }
         } catch (ShellyApiException e) {
-            ShellyApiResult res = e.getApiResult();
-            if (isAuthorizationFailed(res)) {
+            if (!handleApiException(e)) {
                 return;
             }
+
+            ShellyApiResult res = e.getApiResult();
             if (res.isNotCalibrtated()) {
                 logger.warn("{}: {}", thingName, messages.get("roller.calibrating"));
             } else {
@@ -554,35 +578,7 @@ public abstract class ShellyBaseHandler extends BaseThingHandler
         } catch (ShellyApiException e) {
             // http call failed: go offline except for battery devices, which might be in
             // sleep mode. Once the next update is successful the device goes back online
-            String status = "";
-            ShellyApiResult res = e.getApiResult();
-            if (profile.alwaysOn && e.isConnectionError()) {
-                status = "offline.status-error-connect";
-            } else if (res.isHttpAccessUnauthorized()) {
-                status = "offline.conf-error-access-denied";
-            } else if (isWatchdogStarted()) {
-                if (!isWatchdogExpired()) {
-                    logger.debug("{}: Ignore API Timeout on {} {}, retry later", thingName, res.method, res.url);
-                    if (profile.alwaysOn) { // suppress for battery powered sensors
-                        logger.debug("{}: Ignore API Timeout on {} {}, retry later", thingName, res.method, res.url);
-                    }
-                }
-            } else if (e.isJSONException()) {
-                status = "offline.status-error-unexpected-api-result";
-                logger.debug("{}: Unable to parse API response: {}; json={}", thingName, res.getUrl(), res.response, e);
-            } else if (res.isHttpTimeout()) {
-                // Watchdog not started, e.g. device in sleep mode
-                if (isThingOnline()) { // ignore when already offline
-                    status = "offline.status-error-watchdog";
-                }
-            } else {
-                status = "offline.status-error-unexpected-api-result";
-                logger.debug("{}: Unexpected API result: {}", thingName, res.response, e);
-            }
-
-            if (!status.isEmpty()) {
-                setThingOffline(ThingStatusDetail.COMMUNICATION_ERROR, status);
-            }
+            handleApiException(e);
         } catch (NullPointerException | IllegalArgumentException e) {
             logger.debug("{}: Unable to refresh status: {}", thingName, messages.get("statusupdate.failed"), e);
         } finally {
@@ -631,7 +627,7 @@ public abstract class ShellyBaseHandler extends BaseThingHandler
         }
         if (prf.isRoller && prf.settings.favorites != null) {
             String channelId = mkChannelId(CHANNEL_GROUP_ROL_CONTROL, CHANNEL_ROL_CONTROL_FAV);
-            logger.debug("{}: Adding {} roler favorite(s) to channel description", thingName,
+            logger.debug("{}: Adding {} roler favorite(s) to channel description", thingName,
                     prf.settings.favorites.size());
             channelDefinitions.clearStateOptions(channelId);
             int fid = 1;
@@ -1057,7 +1053,7 @@ public abstract class ShellyBaseHandler extends BaseThingHandler
                 String minVersion = !gen2 ? SHELLY_API_MIN_FWVERSION : SHELLY2_API_MIN_FWVERSION;
                 if (version.compare(prf.fwVersion, minVersion) < 0) {
                     logger.warn("{}: {}", prf.device.hostname,
-                            messages.get("versioncheck.beta", prf.fwVersion, prf.fwDate));
+                            messages.get("versioncheck.tooold", prf.fwVersion, prf.fwDate, minVersion));
                 }
             }
             if (!gen2 && bindingConfig.autoCoIoT && ((version.compare(prf.fwVersion, SHELLY_API_MIN_FWCOIOT)) >= 0)
@@ -1120,23 +1116,6 @@ public abstract class ShellyBaseHandler extends BaseThingHandler
         }
     }
 
-    /**
-     * Checks the http response for authorization error.
-     * If the authorization failed the binding can't access the device settings and determine the thing type. In this
-     * case the thing type shelly-unknown is set.
-     *
-     * @param result exception details including the http respone
-     * @return true if the authorization failed
-     */
-    protected boolean isAuthorizationFailed(ShellyApiResult result) {
-        if (result.isHttpAccessUnauthorized()) {
-            // If the device is password protected the API doesn't provide settings to the device settings
-            setThingOffline(ThingStatusDetail.CONFIGURATION_ERROR, "offline.conf-error-access-denied");
-            return true;
-        }
-        return false;
-    }
-
     /**
      * Change type of this thing.
      *
@@ -1363,11 +1342,11 @@ public abstract class ShellyBaseHandler extends BaseThingHandler
         properties.put(PROPERTY_SERVICE_NAME, config.serviceName);
         String deviceName = getString(profile.settings.name);
         properties.put(PROPERTY_SERVICE_NAME, config.serviceName);
-        properties.put(PROPERTY_DEV_GEN, "1");
+        properties.put(PROPERTY_DEV_GEN, !profile.isGen2 ? "1" : "2");
+        properties.put(PROPERTY_DEV_AUTH, getBool(profile.device.auth) ? "yes" : "no");
         if (!deviceName.isEmpty()) {
             properties.put(PROPERTY_DEV_NAME, deviceName);
         }
-        properties.put(PROPERTY_DEV_GEN, !profile.isGen2 ? "1" : "2");
 
         // add status properties
         if (status.wifiSta != null) {
index 0c392678dbc00cb85f964c3581550e341ea01b3d..8e097bf761959f2ce587a9b92c3badd662edd53a 100644 (file)
@@ -13,7 +13,6 @@
 package org.openhab.binding.shelly.internal.handler;
 
 import static org.openhab.binding.shelly.internal.ShellyBindingConstants.*;
-import static org.openhab.binding.shelly.internal.api2.ShellyBluApi.buildBluServiceName;
 import static org.openhab.binding.shelly.internal.discovery.ShellyThingCreator.*;
 import static org.openhab.binding.shelly.internal.util.ShellyUtils.*;
 import static org.openhab.core.thing.Thing.PROPERTY_MODEL_ID;
@@ -23,6 +22,7 @@ import java.util.TreeMap;
 
 import org.eclipse.jdt.annotation.Nullable;
 import org.eclipse.jetty.client.HttpClient;
+import org.openhab.binding.shelly.internal.api.ShellyDeviceProfile;
 import org.openhab.binding.shelly.internal.api1.Shelly1CoapServer;
 import org.openhab.binding.shelly.internal.api2.Shelly2ApiJsonDTO.Shelly2NotifyEvent;
 import org.openhab.binding.shelly.internal.config.ShellyBindingConfiguration;
@@ -54,7 +54,7 @@ public class ShellyBluSensorHandler extends ShellyBaseHandler {
 
     public static void addBluThing(String gateway, Shelly2NotifyEvent e, ShellyThingTable thingTable) {
         String model = substringBefore(getString(e.data.name), "-").toUpperCase();
-        String mac = e.data.addr.replace(":", "");
+        String mac = e.data.addr.replaceAll(":", "");
         String ttype = "";
         logger.debug("{}: Create thing for new BLU device {}: {} / {}", gateway, e.data.name, model, mac);
         ThingTypeUID tuid;
@@ -75,7 +75,7 @@ public class ShellyBluSensorHandler extends ShellyBaseHandler {
                 logger.debug("{}: Unsupported BLU device model {}, MAC={}", gateway, model, mac);
                 return;
         }
-        String serviceName = buildBluServiceName(model, mac);
+        String serviceName = ShellyDeviceProfile.buildBluServiceName(getString(e.data.name), mac);
 
         Map<String, Object> properties = new TreeMap<>();
         addProperty(properties, PROPERTY_MODEL_ID, model);
index cbac83ffc2aa15c44351efb78e9f7b70bc497cb0..fc648d2eb7dd51c99d246996a3827bc23bdb571d 100644 (file)
@@ -78,7 +78,7 @@ public class ShellyComponents {
         if (status.tmp != null && getBool(status.tmp.isValid) && !thingHandler.getProfile().isSensor
                 && status.tmp.tC != SHELLY_API_INVTEMP) {
             thingHandler.updateChannel(CHANNEL_GROUP_DEV_STATUS, CHANNEL_DEVST_ITEMP,
-                    toQuantityType(getDouble(status.tmp.tC), DIGITS_NONE, SIUnits.CELSIUS));
+                    toQuantityType(getDouble(status.tmp.tC), DIGITS_TEMP, SIUnits.CELSIUS));
         } else if (status.temperature != null && status.temperature != SHELLY_API_INVTEMP) {
             thingHandler.updateChannel(CHANNEL_GROUP_DEV_STATUS, CHANNEL_DEVST_ITEMP,
                     toQuantityType(getDouble(status.temperature), DIGITS_NONE, SIUnits.CELSIUS));
index ff4307699725465d3f465a1bf828a1dae02c3742..93c8bd1ee0720956a1896ed898985f0467c418b1 100644 (file)
@@ -64,7 +64,7 @@ public class ShellyManagerOverviewPage extends ShellyManagerPage {
         String action = getUrlParm(parameters, URLPARM_ACTION).toLowerCase();
         String uidParm = getUrlParm(parameters, URLPARM_UID).toLowerCase();
 
-        logger.debug("Generating overview for {} devices", getThingHandlers().size());
+        logger.debug("Generating overview for {} devices", getThingHandlers().size());
 
         String html = "";
         Map<String, String> properties = new HashMap<>();
index 6f03bfc79ba7ad52c9ccf4e20f66f489b6e01881..7ecaa6232df2b011a14cfb788eda5e25a866f77a 100644 (file)
@@ -315,7 +315,7 @@ public class ShellyChannelDefinitions {
         addChannel(thing, add, profile.settings.sleepTime != null, CHGR_SENSOR, CHANNEL_SENSOR_SLEEPTIME);
 
         // If device has more than 1 meter the channel accumulatedWatts receives the accumulated value
-        boolean accuChannel = profile.numMeters > 1 && !profile.isRoller && !profile.isRGBW2;
+        boolean accuChannel = profile.hasRelays && profile.numMeters > 1 && !profile.isRoller && !profile.isRGBW2;
         addChannel(thing, add, accuChannel, CHGR_DEVST, CHANNEL_DEVST_ACCUWATTS);
         addChannel(thing, add, accuChannel, CHGR_DEVST, CHANNEL_DEVST_ACCUTOTAL);
         addChannel(thing, add, accuChannel && (status.emeters != null), CHGR_DEVST, CHANNEL_DEVST_ACCURETURNED);
index caa4b42438ff0314fcbb12f8ec11486cbb488806..8ec3b279b7bafff7d76689c60519eba051224565 100644 (file)
@@ -34,7 +34,7 @@ message.versioncheck.update = INFO: New firmware available: current version: {0}
 message.versioncheck.autocoiot = INFO: Firmware is full-filling the minimum version to auto-enable CoIoT
 message.init.noipaddress = Unable to detect local IP address. Please make sure that IPv4 is enabled for this interface and check openHAB Network Configuration.
 message.command.failed = ERROR: Unable to process command {0} for channel {1}
-message.command.init = Thing not yet initialized, command {0} triggered initialization
+message.command.init = Thing not yet initialized, command {0} triggered initialization
 message.status.unknown.initializing = Initializing or device in sleep mode.
 message.statusupdate.failed = Unable to update status
 message.status.managerstarted = Shelly Manager started at http(s)://{0}:{1}/shelly/manager
@@ -120,7 +120,7 @@ thing-type.shelly.shellyproem50.description = Shelly Pro EM-50 - 2xPower Meter +
 thing-type.shelly.shellypro4pm.description = Shelly Pro 4PM - 4xRelay Switch with Power Meter
  
 # BLU devices
-thing-type.shelly.shellyblubutton.description = Shelly BLU Button
+thing-type.shelly.shellyblubutton.description = Shelly BLU Button 1
 thing-type.shelly.shellybludw.description = Shelly BLU Door/Window Sensor
 thing-type.shelly.shellyblumotion.description = Shelly BLU Motion Sensor
  
@@ -247,7 +247,7 @@ channel-type.shelly.temperature4.description = Temperature of external Sensor #4
 channel-type.shelly.temperature5.label = Temperature 5
 channel-type.shelly.temperature6.description = Temperature of external Sensor #5
 channel-type.shelly.targetTemp.label = Target Temperature
-channel-type.shelly.targetTemp.description = Target Temperature  in °C to be reached in auto-temperature mode
+channel-type.shelly.targetTemp.description = Target Temperature in °C to be reached in auto-temperature mode
 channel-type.shelly.humidity.label = Humidity
 channel-type.shelly.humidity.description = Relative humidity (0..100%)
 channel-type.shelly.rollerShutter.label = Roller Control (0=open, 100=closed)