]> git.basschouten.com Git - openhab-addons.git/commitdiff
[shelly] Improved TRV profile handling (selectable list via dynamic state options...
authorMarkus Michels <markus7017@gmail.com>
Tue, 9 Aug 2022 08:04:07 +0000 (10:04 +0200)
committerGitHub <noreply@github.com>
Tue, 9 Aug 2022 08:04:07 +0000 (10:04 +0200)
provider); some improvements & fixes

Signed-off-by: Markus Michels <markus7017@gmail.com>
17 files changed:
bundles/org.openhab.binding.shelly/README.md
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/api1/Shelly1ApiJsonDTO.java
bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/api1/Shelly1CoIoTProtocol.java
bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/api1/Shelly1CoIoTVersion2.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/ShellyComponents.java
bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/handler/ShellyLightHandler.java
bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/handler/ShellyProtectedHandler.java
bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/handler/ShellyRelayHandler.java
bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/handler/ShellyThingInterface.java
bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/provider/ShellyChannelDefinitions.java
bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/provider/ShellyStateDescriptionProvider.java [new file with mode: 0644]
bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/util/ShellyUtils.java
bundles/org.openhab.binding.shelly/src/main/resources/OH-INF/i18n/shelly.properties

index 6de414e9d17e6a0fd4818a12992e85cad8de92ba..73cf2c7118d659047c3a8dae371a84b272448abd 100644 (file)
@@ -27,7 +27,7 @@ Also check out the [Shelly Manager](doc/ShellyManager.md), which
 
 ## Supported Devices
 
-### Generation 1:
+### Generation 1
 
 | thing-type         | Model                                                  | Vendor ID |
 |--------------------|--------------------------------------------------------|-----------|
index 7179eed182ce35fe4b6df9b69c75511c4c9fa8d1..c9ae998c362e03ab4a8d762de37d29e2f98340aa 100755 (executable)
@@ -138,10 +138,10 @@ public class ShellyBindingConstants {
     public static final String CHANNEL_CONTROL_SETTEMP = "targetTemp";
     public static final String CHANNEL_CONTROL_POSITION = "position";
     public static final String CHANNEL_CONTROL_MODE = "mode";
-    public static final String CHANNEL_CONTROL_PROFILE = "selectedProfile";
     public static final String CHANNEL_CONTROL_BCONTROL = "boost";
     public static final String CHANNEL_CONTROL_BTIMER = "boostTimer";
     public static final String CHANNEL_CONTROL_SCHEDULE = "schedule";
+    public static final String CHANNEL_CONTROL_PROFILE = "selectedProfile";
 
     // External sensors for Shelly1/1PM
     public static final String CHANNEL_ESENDOR_TEMP1 = CHANNEL_SENSOR_TEMP + "1";
index 2ec06f032556c3d80c19cd63b58d58dabe6c219b..ac4d88bd6742947e5c2ae4101b88d3f5d89302db 100755 (executable)
@@ -65,8 +65,6 @@ public class ShellyHandlerFactory extends BaseThingHandlerFactory {
     private final Shelly1CoapServer coapServer;
     private final ShellyThingTable thingTable;
     private ShellyBindingConfiguration bindingConfig = new ShellyBindingConfiguration();
-    private String localIP = "";
-    private int httpPort = -1;
 
     /**
      * Activate the bundle: save properties
@@ -85,7 +83,7 @@ public class ShellyHandlerFactory extends BaseThingHandlerFactory {
         this.thingTable = thingTable;
 
         bindingConfig.updateFromProperties(configProperties);
-        localIP = bindingConfig.localIP;
+        String localIP = bindingConfig.localIP;
         if (localIP.isEmpty()) {
             localIP = ShellyUtils.getString(networkAddressService.getPrimaryIpv4HostAddress());
         }
@@ -94,11 +92,13 @@ public class ShellyHandlerFactory extends BaseThingHandlerFactory {
         }
 
         this.httpClient = httpClientFactory.getCommonHttpClient();
-        httpPort = HttpServiceUtil.getHttpServicePort(componentContext.getBundleContext());
+        int httpPort = HttpServiceUtil.getHttpServicePort(componentContext.getBundleContext());
         if (httpPort == -1) {
             httpPort = 8080;
         }
         logger.debug("Using OH HTTP port {}", httpPort);
+        bindingConfig.localIP = localIP;
+        bindingConfig.httpPort = httpPort;
 
         this.coapServer = new Shelly1CoapServer();
     }
@@ -117,8 +117,7 @@ public class ShellyHandlerFactory extends BaseThingHandlerFactory {
         if (thingType.equals(THING_TYPE_SHELLYPROTECTED_STR)) {
             logger.debug("{}: Create new thing of type {} using ShellyProtectedHandler", thing.getLabel(),
                     thingTypeUID.toString());
-            handler = new ShellyProtectedHandler(thing, messages, bindingConfig, coapServer, localIP, httpPort,
-                    httpClient);
+            handler = new ShellyProtectedHandler(thing, messages, bindingConfig, thingTable, coapServer, httpClient);
         } else if (thingType.equals(THING_TYPE_SHELLYBULB_STR) || thingType.equals(THING_TYPE_SHELLYDUO_STR)
                 || thingType.equals(THING_TYPE_SHELLYRGBW2_COLOR_STR)
                 || thingType.equals(THING_TYPE_SHELLYRGBW2_WHITE_STR)
@@ -126,11 +125,11 @@ public class ShellyHandlerFactory extends BaseThingHandlerFactory {
                 || thingType.equals(THING_TYPE_SHELLYVINTAGE_STR)) {
             logger.debug("{}: Create new thing of type {} using ShellyLightHandler", thing.getLabel(),
                     thingTypeUID.toString());
-            handler = new ShellyLightHandler(thing, messages, bindingConfig, coapServer, localIP, httpPort, httpClient);
+            handler = new ShellyLightHandler(thing, messages, bindingConfig, thingTable, coapServer, httpClient);
         } else if (SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID)) {
             logger.debug("{}: Create new thing of type {} using ShellyRelayHandler", thing.getLabel(),
                     thingTypeUID.toString());
-            handler = new ShellyRelayHandler(thing, messages, bindingConfig, coapServer, localIP, httpPort, httpClient);
+            handler = new ShellyRelayHandler(thing, messages, bindingConfig, thingTable, coapServer, httpClient);
         }
 
         if (handler != null) {
index 8b0965ea37547f4da2254a59114d0a708b06f714..c42c42cf5fe4831b041bb8cc781e961868da2d8e 100644 (file)
@@ -330,17 +330,19 @@ public class ShellyDeviceProfile {
         return -1;
     }
 
-    public String getValueProfile(int profileId) {
-        int id = profileId;
-        if (settings.thermostats != null) {
-            ShellyThermnostat t = settings.thermostats.get(0);
-            id = profileId == 0 ? getInteger(t.profile) : profileId;
-            if (id <= 0) {
-                return "DISABLED";
-            }
-            return id <= t.profileNames.length ? getString(t.profileNames[id - 1]) : "" + id;
+    public String[] getValveProfileList(int valveId) {
+        if (isTRV && settings.thermostats != null && valveId <= settings.thermostats.size()) {
+            ShellyThermnostat t = settings.thermostats.get(valveId);
+            return t.profileNames;
         }
+        return new String[0];
+    }
 
+    public String getValueProfile(int valveId, int profileId) {
+        int id = profileId;
+        if (id <= 0 && settings.thermostats != null) {
+            id = settings.thermostats.get(0).profile;
+        }
         return "" + id;
     }
 
index ed3257bff786639d2fed6559abd699ed6be79433..d968ce50fc81ff423cecc1f70c8c732d9bb0d4e3 100644 (file)
@@ -1143,7 +1143,7 @@ public class Shelly1ApiJsonDTO {
 
     /**
      * Shelly Dimmer returns light[]. However, the structure doesn't match the lights[] of a Bulb/RGBW2.
-     * The tag lights[] will be replaced with dimmers[] so this could be mapped to a different Gson structure.
+     * The tag lights[] will be replaced with dimmers[] so this could be mapped to a different Gson structure.
      * The function requires that it's only called when the device is a dimmer - on get settings and get status
      *
      * @param json Input Json as received by the API
index f544616756d99e5f85412e4f657172384f6262d1..37ced711c8573d9f5152b8e80fcdf46f9d259f89 100644 (file)
@@ -58,7 +58,6 @@ public class Shelly1CoIoTProtocol {
 
     // Due to the fact that the device reports only the current/last status, but no real events, we need to distinguish
     // between a real update or just a repeated status on periodic updates
-    protected int lastCfgCount = -1;
     protected int[] lastEventCount = { -1, -1, -1, -1, -1, -1, -1, -1 }; // 4Pro has 4 relays, so 8 should be fine
     protected String[] inputEvent = { "", "", "", "", "", "", "", "" };
     protected String lastWakeup = "";
index cd84004b2942b664499d2ac43727e65fc090a719..2e937f8c27a348f0fd39e6422fdee69cc0f1d23f 100644 (file)
@@ -48,6 +48,7 @@ import org.slf4j.LoggerFactory;
 @NonNullByDefault
 public class Shelly1CoIoTVersion2 extends Shelly1CoIoTProtocol implements Shelly1CoIoTInterface {
     private final Logger logger = LoggerFactory.getLogger(Shelly1CoIoTVersion2.class);
+    private int lastCfgCount = -1;
 
     public Shelly1CoIoTVersion2(String thingName, ShellyThingInterface thingHandler, Map<String, CoIotDescrBlk> blkMap,
             Map<String, CoIotDescrSen> sensorMap) {
@@ -107,7 +108,7 @@ public class Shelly1CoIoTVersion2 extends Shelly1CoIoTProtocol implements Shelly
                 case "3117": // S, mode, 0-5 (0=disabled)
                     value = getDouble(s.value).intValue();
                     updateChannel(updates, CHANNEL_GROUP_CONTROL, CHANNEL_CONTROL_PROFILE,
-                            getStringType(profile.getValueProfile((int) value)));
+                            getStringType(profile.getValueProfile(0, (int) value)));
                     updateChannel(updates, CHANNEL_GROUP_CONTROL, CHANNEL_CONTROL_SCHEDULE, getOnOff(value > 0));
                     break;
                 case "3118": // Valve state
index 025c8f53e3b3bd198008a2ca7ac5425e287d4c13..e9d65b0913ebf6a53cdabeffb18749eda0c44b63 100755 (executable)
@@ -21,9 +21,13 @@ import static org.openhab.core.thing.Thing.*;
 
 import java.net.InetAddress;
 import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.TreeMap;
+import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.ScheduledFuture;
 import java.util.concurrent.TimeUnit;
 
@@ -39,7 +43,6 @@ import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyInputSta
 import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyOtaCheckResult;
 import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsDevice;
 import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsStatus;
-import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyThermnostat;
 import org.openhab.binding.shelly.internal.api1.Shelly1CoapHandler;
 import org.openhab.binding.shelly.internal.api1.Shelly1CoapJSonDTO;
 import org.openhab.binding.shelly.internal.api1.Shelly1CoapServer;
@@ -48,6 +51,7 @@ import org.openhab.binding.shelly.internal.config.ShellyBindingConfiguration;
 import org.openhab.binding.shelly.internal.config.ShellyThingConfiguration;
 import org.openhab.binding.shelly.internal.discovery.ShellyThingCreator;
 import org.openhab.binding.shelly.internal.provider.ShellyChannelDefinitions;
+import org.openhab.binding.shelly.internal.provider.ShellyStateDescriptionProvider;
 import org.openhab.binding.shelly.internal.provider.ShellyTranslationProvider;
 import org.openhab.binding.shelly.internal.util.ShellyChannelCache;
 import org.openhab.binding.shelly.internal.util.ShellyVersionDTO;
@@ -62,10 +66,13 @@ import org.openhab.core.thing.ThingStatus;
 import org.openhab.core.thing.ThingStatusDetail;
 import org.openhab.core.thing.ThingTypeUID;
 import org.openhab.core.thing.binding.BaseThingHandler;
+import org.openhab.core.thing.binding.ThingHandlerService;
 import org.openhab.core.thing.binding.builder.ThingBuilder;
+import org.openhab.core.thing.type.ChannelTypeUID;
 import org.openhab.core.types.Command;
 import org.openhab.core.types.RefreshType;
 import org.openhab.core.types.State;
+import org.openhab.core.types.StateOption;
 import org.openhab.core.types.UnDefType;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -79,8 +86,21 @@ import org.slf4j.LoggerFactory;
 @NonNullByDefault
 public class ShellyBaseHandler extends BaseThingHandler
         implements ShellyDeviceListener, ShellyManagerInterface, ShellyThingInterface {
+    private class OptionEntry {
+        public ChannelTypeUID uid;
+        public String key;
+        public String value;
+
+        public OptionEntry(ChannelTypeUID uid, String key, String value) {
+            this.uid = uid;
+            this.key = key;
+            this.value = value;
+        }
+    }
+
     protected final Logger logger = LoggerFactory.getLogger(ShellyBaseHandler.class);
     protected final ShellyChannelDefinitions channelDefinitions;
+    private final CopyOnWriteArrayList<OptionEntry> stateOptions = new CopyOnWriteArrayList<>();
 
     public String thingName = "";
     public String thingType = "";
@@ -111,9 +131,6 @@ public class ShellyBaseHandler extends BaseThingHandler
     private final int cacheCount = UPDATE_SETTINGS_INTERVAL_SECONDS / UPDATE_STATUS_INTERVAL_SECONDS;
     private final ShellyChannelCache cache;
 
-    private String localIP = "";
-    private String localPort = "";
-
     private String lastWakeupReason = "";
     private int vibrationFilter = 0;
 
@@ -128,8 +145,8 @@ public class ShellyBaseHandler extends BaseThingHandler
      * @param httpPort from httpService
      */
     public ShellyBaseHandler(final Thing thing, final ShellyTranslationProvider translationProvider,
-            final ShellyBindingConfiguration bindingConfig, final Shelly1CoapServer coapServer, final String localIP,
-            int httpPort, final HttpClient httpClient) {
+            final ShellyBindingConfiguration bindingConfig, ShellyThingTable thingTable,
+            final Shelly1CoapServer coapServer, final HttpClient httpClient) {
         super(thing);
 
         this.thingName = getString(thing.getLabel());
@@ -140,8 +157,6 @@ public class ShellyBaseHandler extends BaseThingHandler
         this.config = getConfigAs(ShellyThingConfiguration.class);
 
         this.httpClient = httpClient;
-        this.localIP = localIP;
-        this.localPort = String.valueOf(httpPort);
         this.api = new Shelly1HttpApi(thingName, config, httpClient);
 
         coap = new Shelly1CoapHandler(this, coapServer);
@@ -153,6 +168,11 @@ public class ShellyBaseHandler extends BaseThingHandler
                 || key.equalsIgnoreCase(config.serviceName) || key.equalsIgnoreCase(thing.getUID().getAsString());
     }
 
+    @Override
+    public Collection<Class<? extends ThingHandlerService>> getServices() {
+        return Set.of(ShellyStateDescriptionProvider.class);
+    }
+
     public String getUID() {
         return getThing().getUID().getAsString();
     }
@@ -213,7 +233,9 @@ public class ShellyBaseHandler extends BaseThingHandler
     public void handleConfigurationUpdate(Map<String, Object> configurationParameters) {
         super.handleConfigurationUpdate(configurationParameters);
         logger.debug("{}: Thing config updated, re-initialize", thingName);
-        coap.stop();
+        if (coap != null) {
+            coap.stop();
+        }
         requestUpdates(1, true);// force re-initialization
     }
 
@@ -231,8 +253,6 @@ public class ShellyBaseHandler extends BaseThingHandler
         stopping = false;
         refreshSettings = false;
         lastWakeupReason = "";
-        profile.initFromThingType(thingType);
-        api.setConfig(thingName, config);
         cache.setThingName(thingName);
         cache.clear();
 
@@ -260,6 +280,8 @@ public class ShellyBaseHandler extends BaseThingHandler
             config.serviceName = getString(profile.hostname).toLowerCase();
         }
 
+        api.setConfig(thingName, config);
+        api.initialize();
         ShellyDeviceProfile tmpPrf = api.getDeviceProfile(thingType);
         if (this.getThing().getThingTypeUID().equals(THING_TYPE_SHELLYPROTECTED)) {
             changeThingType(thingName, tmpPrf.mode);
@@ -293,8 +315,18 @@ public class ShellyBaseHandler extends BaseThingHandler
         tmpPrf.status = api.getStatus();
         tmpPrf.updateFromStatus(tmpPrf.status);
 
+        if (tmpPrf.isTRV) {
+            String[] profileNames = tmpPrf.getValveProfileList(0);
+            String channelId = mkChannelId(CHANNEL_GROUP_CONTROL, CHANNEL_CONTROL_PROFILE);
+            logger.debug("{}: Adding TRV profile names to channel description: {}", thingName, profileNames);
+            clearStateOptions(channelId);
+            addStateOption(channelId, "0", "DISABLED");
+            for (int i = 0; i < profileNames.length; i++) {
+                addStateOption(channelId, "" + (i + 1), profileNames[i]);
+            }
+        }
+
         showThingConfig(tmpPrf);
-        // update thing properties
         checkVersion(tmpPrf, tmpPrf.status);
 
         if (config.eventsCoIoT && (tmpPrf.settings.coiot != null) && (tmpPrf.settings.coiot.enabled != null)) {
@@ -411,24 +443,8 @@ public class ShellyBaseHandler extends BaseThingHandler
                     break;
                 case CHANNEL_CONTROL_PROFILE:
                     logger.debug("{}: Select profile {}", thingName, command);
-                    int id = -1;
-                    if (command instanceof Number) {
-                        id = (int) getNumber(command);
-                    } else {
-                        String cmd = command.toString();
-                        if (isDigit(cmd.charAt(0))) {
-                            id = Integer.parseInt(cmd);
-                        } else if (cmd.equalsIgnoreCase("DISABLED")) {
-                            id = 0;
-                        } else if (profile.settings.thermostats != null) {
-                            ShellyThermnostat t = profile.settings.thermostats.get(0);
-                            for (int i = 0; i < t.profileNames.length; i++) {
-                                if (t.profileNames[i].equalsIgnoreCase(cmd)) {
-                                    id = i + 1;
-                                }
-                            }
-                        }
-                    }
+                    String cmd = command.toString();
+                    int id = Integer.parseInt(cmd);
                     if (id < 0 || id > 5) {
                         logger.warn("{}: Invalid profile Id {} requested", thingName, profile);
                     } else {
@@ -463,7 +479,6 @@ public class ShellyBaseHandler extends BaseThingHandler
 
             restartWatchdog();
             if (update && !autoCoIoT && !isUpdateScheduled()) {
-                logger.debug("{}: Command processed, request status update", thingName);
                 requestUpdates(1, false);
             }
         } catch (ShellyApiException e) {
@@ -517,8 +532,6 @@ public class ShellyBaseHandler extends BaseThingHandler
                     initializeThing(); // may fire an exception if initialization failed
                 }
                 // Get profile, if refreshSettings == true reload settings from device
-                logger.trace("{}: Updating status (scheduledUpdates={}, refreshSettings={})", thingName,
-                        scheduledUpdates, refreshSettings);
                 ShellySettingsStatus status = api.getStatus();
                 boolean restarted = checkRestarted(status);
                 profile = getProfile(refreshSettings || restarted);
@@ -590,6 +603,17 @@ public class ShellyBaseHandler extends BaseThingHandler
         }
     }
 
+    @Override
+    public ThingStatus getThingStatus() {
+        return getThing().getStatus();
+    }
+
+    @Override
+    public ThingStatusDetail getThingStatusDetail() {
+        return getThing().getStatusInfo().getStatusDetail();
+    }
+
+    @Override
     public boolean isThingOnline() {
         return getThing().getStatus() == ThingStatus.ONLINE;
     }
@@ -699,9 +723,10 @@ public class ShellyBaseHandler extends BaseThingHandler
         if (status.uptime != null) {
             stats.lastUptime = getLong(status.uptime);
         }
-        stats.coiotMessages = coap.getMessageCount();
-        stats.coiotErrors = coap.getErrorCount();
-
+        if (coap != null) {
+            stats.coiotMessages = coap.getMessageCount();
+            stats.coiotErrors = coap.getErrorCount();
+        }
         if (!alarm.isEmpty()) {
             postEvent(alarm, false);
         }
@@ -911,7 +936,7 @@ public class ShellyBaseHandler extends BaseThingHandler
             InetAddress addr = InetAddress.getByName(config.deviceIp);
             String saddr = addr.getHostAddress();
             if (!config.deviceIp.equals(saddr)) {
-                logger.debug("{}: hostname {} resolved to IP address {}", thingName, config.deviceIp, saddr);
+                logger.debug("{}: hostname {} resolved to IP address {}", thingName, config.deviceIp, saddr);
                 config.deviceIp = saddr;
             }
         } catch (UnknownHostException e) {
@@ -919,8 +944,8 @@ public class ShellyBaseHandler extends BaseThingHandler
         }
 
         config.serviceName = getString(properties.get(PROPERTY_SERVICE_NAME));
-        config.localIp = localIP;
-        config.localPort = localPort;
+        config.localIp = bindingConfig.localIP;
+        config.localPort = String.valueOf(bindingConfig.httpPort);
         if (config.userId.isEmpty() && !bindingConfig.defaultUserId.isEmpty()) {
             config.userId = bindingConfig.defaultUserId;
             config.password = bindingConfig.defaultPassword;
@@ -1221,8 +1246,7 @@ public class ShellyBaseHandler extends BaseThingHandler
      * @param profile The device profile
      * @param status the /status result
      */
-    protected void updateProperties(ShellyDeviceProfile profile, ShellySettingsStatus status) {
-        logger.debug("{}: Update properties", thingName);
+    public void updateProperties(ShellyDeviceProfile profile, ShellySettingsStatus status) {
         Map<String, Object> properties = fillDeviceProperties(profile);
         String deviceName = getString(profile.settings.name);
         properties.put(PROPERTY_SERVICE_NAME, config.serviceName);
@@ -1347,6 +1371,35 @@ public class ShellyBaseHandler extends BaseThingHandler
         return profile;
     }
 
+    @Override
+    public List<StateOption> getStateOptions(ChannelTypeUID uid) {
+        List<StateOption> options = new ArrayList<>();
+        for (OptionEntry oe : stateOptions) {
+            if (oe.uid.equals(uid)) {
+                options.add(new StateOption(oe.key, oe.value));
+            }
+        }
+
+        if (!options.isEmpty()) {
+            logger.debug("{}: Return {} state options for channel uid {}", thingName, options.size(), uid.getId());
+        }
+        return options;
+    }
+
+    private void addStateOption(String channelId, String key, String value) {
+        ChannelTypeUID uid = channelDefinitions.getChannelTypeUID(channelId);
+        stateOptions.addIfAbsent(new OptionEntry(uid, key, value));
+    }
+
+    private void clearStateOptions(String channelId) {
+        ChannelTypeUID uid = channelDefinitions.getChannelTypeUID(channelId);
+        for (OptionEntry oe : stateOptions) {
+            if (oe.uid.equals(uid)) {
+                stateOptions.remove(oe);
+            }
+        }
+    }
+
     protected ShellyDeviceProfile getDeviceProfile() {
         return profile;
     }
@@ -1361,7 +1414,7 @@ public class ShellyBaseHandler extends BaseThingHandler
                 logger.debug("{}: Duplicate vibration events will be absorbed for the next {} sec", thingName,
                         vibrationFilter * UPDATE_STATUS_INTERVAL_SECONDS);
             } else {
-                logger.debug("{}: Vibration event absorbed, {} sec remaining", thingName,
+                logger.debug("{}: Vibration event absorbed, {} sec remaining", thingName,
                         vibrationFilter * UPDATE_STATUS_INTERVAL_SECONDS);
                 return;
             }
@@ -1379,7 +1432,9 @@ public class ShellyBaseHandler extends BaseThingHandler
             logger.debug("{}: Shelly statusJob stopped", thingName);
         }
 
-        coap.stop();
+        if (coap != null) {
+            coap.stop();
+        }
         profile.initialized = false;
     }
 
index b226667549e3d1e6ab46ca8b9979fb82c2ac6b26..7b5bcc0b37e3dbb20a00e60ec0e1624e9aefb493 100644 (file)
@@ -398,7 +398,7 @@ public class ShellyComponents {
                 updated |= thingHandler.updateChannel(CHANNEL_GROUP_CONTROL, CHANNEL_CONTROL_SCHEDULE,
                         getOnOff(t.schedule));
                 updated |= thingHandler.updateChannel(CHANNEL_GROUP_CONTROL, CHANNEL_CONTROL_PROFILE,
-                        getStringType(profile.getValueProfile(pid)));
+                        getStringType(profile.getValueProfile(0, pid)));
                 if (t.tmp != null) {
                     Double temp = convertToC(t.tmp.value, getString(t.tmp.units));
                     updated |= thingHandler.updateChannel(CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_TEMP,
index 6a1944338354572b111097e05ad90b869156825a..3ca5a34375408ab343158fc7cd1bae23b0ae4d75 100644 (file)
@@ -66,9 +66,9 @@ public class ShellyLightHandler extends ShellyBaseHandler {
      * @param httpPort port of the openHAB HTTP API
      */
     public ShellyLightHandler(final Thing thing, final ShellyTranslationProvider translationProvider,
-            final ShellyBindingConfiguration bindingConfig, final Shelly1CoapServer coapServer, final String localIP,
-            int httpPort, final HttpClient httpClient) {
-        super(thing, translationProvider, bindingConfig, coapServer, localIP, httpPort, httpClient);
+            final ShellyBindingConfiguration bindingConfig, final ShellyThingTable thingTable,
+            final Shelly1CoapServer coapServer, final HttpClient httpClient) {
+        super(thing, translationProvider, bindingConfig, thingTable, coapServer, httpClient);
         channelColors = new TreeMap<>();
     }
 
index f941638c5f521b3d2872975926e8598fb80f46b7..88f99133ae3b11b9e77d933386b1da0e78fcdc60 100644 (file)
@@ -36,9 +36,9 @@ public class ShellyProtectedHandler extends ShellyBaseHandler {
      * @param httpPort port of the openHAB HTTP API
      */
     public ShellyProtectedHandler(final Thing thing, final ShellyTranslationProvider translationProvider,
-            final ShellyBindingConfiguration bindingConfig, final Shelly1CoapServer coapServer, final String localIP,
-            int httpPort, final HttpClient httpClient) {
-        super(thing, translationProvider, bindingConfig, coapServer, localIP, httpPort, httpClient);
+            final ShellyBindingConfiguration bindingConfig, ShellyThingTable thingTable,
+            final Shelly1CoapServer coapService, final HttpClient httpClient) {
+        super(thing, translationProvider, bindingConfig, thingTable, coapService, httpClient);
     }
 
     @Override
index 174817a999bb5eddba39b474e3457a9917e6fcd4..bb5034aeaaf34841c455c99b1cc29e3b124c81fe 100644 (file)
@@ -64,9 +64,9 @@ public class ShellyRelayHandler extends ShellyBaseHandler {
      * @param httpPort port of the openHAB HTTP API
      */
     public ShellyRelayHandler(final Thing thing, final ShellyTranslationProvider translationProvider,
-            final ShellyBindingConfiguration bindingConfig, final Shelly1CoapServer coapServer, final String localIP,
-            int httpPort, final HttpClient httpClient) {
-        super(thing, translationProvider, bindingConfig, coapServer, localIP, httpPort, httpClient);
+            final ShellyBindingConfiguration bindingConfig, ShellyThingTable thingTable,
+            final Shelly1CoapServer coapServer, final HttpClient httpClient) {
+        super(thing, translationProvider, bindingConfig, thingTable, coapServer, httpClient);
     }
 
     @Override
@@ -329,7 +329,7 @@ public class ShellyRelayHandler extends ShellyBaseHandler {
      */
     public boolean updateRelays(ShellySettingsStatus status) throws ShellyApiException {
         boolean updated = false;
-        // Check for Relay in Standard Mode
+
         if (profile.hasRelays && !profile.isDimmer) {
             double voltage = -1;
             if (status.voltage == null && profile.settings.supplyVoltage != null) {
@@ -384,7 +384,7 @@ public class ShellyRelayHandler extends ShellyBaseHandler {
             ShellySettingsStatus dstatus = fromJson(gson, Shelly1ApiJsonDTO.fixDimmerJson(orgStatus.json),
                     ShellySettingsStatus.class);
 
-            logger.trace("{}: Updating {} dimmers(s)", thingName, dstatus.dimmers.size());
+            logger.trace("{}: Updating {} dimmers(s)", thingName, dstatus.dimmers.size());
             int l = 0;
             for (ShellyShortLightStatus dimmer : dstatus.dimmers) {
                 Integer r = l + 1;
index 0adb1f040290c5d7cc2f42ddab3ed8d4f8f5ae34..b7a28f9fbb2b504fa90192dd406adc55642403c5 100644 (file)
@@ -25,8 +25,11 @@ import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettings
 import org.openhab.binding.shelly.internal.config.ShellyThingConfiguration;
 import org.openhab.core.thing.Channel;
 import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.ThingStatus;
 import org.openhab.core.thing.ThingStatusDetail;
+import org.openhab.core.thing.type.ChannelTypeUID;
 import org.openhab.core.types.State;
+import org.openhab.core.types.StateOption;
 
 /**
  * The {@link ShellyThingInterface} implements the interface for Shelly Manager to access the thing handler
@@ -38,6 +41,8 @@ public interface ShellyThingInterface {
 
     public ShellyDeviceProfile getProfile(boolean forceRefresh) throws ShellyApiException;
 
+    public List<StateOption> getStateOptions(ChannelTypeUID uid);
+
     public double getChannelDouble(String group, String channel);
 
     public boolean updateChannel(String group, String channel, State value);
@@ -48,6 +53,12 @@ public interface ShellyThingInterface {
 
     public void setThingOffline(ThingStatusDetail detail, String messageKey);
 
+    public ThingStatus getThingStatus();
+
+    public ThingStatusDetail getThingStatusDetail();
+
+    public boolean isThingOnline();
+
     public boolean requestUpdates(int requestCount, boolean refreshSettings);
 
     public void triggerUpdateFromCoap();
index 51a0141ecab26a3ef919f66b7e3ad45cc2854b0e..46ed9b56006eb09bca8554264c64538dacaf33c7 100644 (file)
@@ -514,6 +514,11 @@ public class ShellyChannelDefinitions {
         return newChannels;
     }
 
+    public ChannelTypeUID getChannelTypeUID(String channelId) {
+        ShellyChannel channelDef = getDefinition(channelId);
+        return new ChannelTypeUID(BINDING_ID, channelDef.typeId);
+    }
+
     private static void addChannel(Thing thing, Map<String, Channel> newChannels, boolean supported, String group,
             String channelName) throws IllegalArgumentException {
         if (supported) {
diff --git a/bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/provider/ShellyStateDescriptionProvider.java b/bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/provider/ShellyStateDescriptionProvider.java
new file mode 100644 (file)
index 0000000..d16eaf7
--- /dev/null
@@ -0,0 +1,58 @@
+/**
+ * Copyright (c) 2010-2022 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+
+package org.openhab.binding.shelly.internal.provider;
+
+import java.util.Locale;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.shelly.internal.handler.ShellyThingInterface;
+import org.openhab.core.thing.Channel;
+import org.openhab.core.thing.binding.BaseDynamicStateDescriptionProvider;
+import org.openhab.core.thing.binding.ThingHandler;
+import org.openhab.core.thing.binding.ThingHandlerService;
+import org.openhab.core.thing.type.ChannelTypeUID;
+import org.openhab.core.types.StateDescription;
+
+/**
+ * This class provides the list of valid inputs for the input channel of a source.
+ *
+ * @author Markus Michels - Initial contribution
+ *
+ */
+@NonNullByDefault
+public class ShellyStateDescriptionProvider extends BaseDynamicStateDescriptionProvider implements ThingHandlerService {
+    private @Nullable ShellyThingInterface handler;
+
+    @Override
+    public void setThingHandler(ThingHandler handler) {
+        this.handler = (ShellyThingInterface) handler;
+    }
+
+    @Override
+    public @Nullable ThingHandler getThingHandler() {
+        return (ThingHandler) handler;
+    }
+
+    @SuppressWarnings("null")
+    @Override
+    public @Nullable StateDescription getStateDescription(Channel channel, @Nullable StateDescription original,
+            @Nullable Locale locale) {
+        ChannelTypeUID uid = channel.getChannelTypeUID();
+        if (uid != null && handler != null) {
+            setStateOptions(channel.getUID(), handler.getStateOptions(uid));
+        }
+        return super.getStateDescription(channel, original, locale);
+    }
+}
index e04bf160d59f76def5c6c4534e8dc2a2fe769b79..b5a0b4cfe7d677fca4679e0e85e9320318d8b637 100644 (file)
@@ -81,7 +81,7 @@ public class ShellyUtils {
         if (classOfT.isInstance(json)) {
             return wrap(classOfT).cast(json);
         } else if (json.isEmpty()) { // update GSON might return null
-            throw new ShellyApiException(PRE + className + "from empty JSON");
+            throw new ShellyApiException(PRE + className + " from empty JSON");
         } else {
             try {
                 @Nullable
@@ -92,7 +92,7 @@ public class ShellyUtils {
                 return obj;
             } catch (JsonSyntaxException e) {
                 throw new ShellyApiException(
-                        PRE + className + "from JSON (syntax/format error: " + e.getMessage() + "): " + json, e);
+                        PRE + className + " from JSON (syntax/format error: " + e.getMessage() + "): " + json, e);
             } catch (RuntimeException e) {
                 throw new ShellyApiException(PRE + className + " from JSON: " + json, e);
             }
index 317c5624557b7b2ad3c6abbcaa1e3aad4527eccc..8bb4f01edc2e3704c3e3e1c470331e64a8f976af 100644 (file)
@@ -388,10 +388,10 @@ channel-type.shelly.controlMode.label = Mode
 channel-type.shelly.controlMode.description = Sensor/Control Mode
 channel-type.shelly.controlMode.state.option.manual = Manual
 channel-type.shelly.controlMode.state.option.automatic = Automatic
-channel-type.shelly.controlSchedule.label = Schedule active
-channel-type.shelly.controlSchedule.description = ON: A scheduled program is active
 channel-type.shelly.controlProfile.label = Selected Profile
-channel-type.shelly.controlProfile.description = Selected Profile configured in the Shelly App
+channel-type.shelly.controlProfile.description = Id of the selected Profile configured in the Shelly App
+channel-type.shelly.controlSchedule.label = Schedule Active
+channel-type.shelly.controlSchedule.description = ON: A scheduled program is active
 channel-type.shelly.boostControl.label = Boost Mode
 channel-type.shelly.boostControl.description = ON: Boost mode is activated (overwrites automatic temperature mode)
 channel-type.shelly.boostTimer.label = Boost Timer