]> git.basschouten.com Git - openhab-addons.git/commitdiff
[amazonechocontrol] Add channels to thermostatController (#13067)
authorDaniel Campbell <djcampbell79@gmail.com>
Mon, 1 Aug 2022 10:48:37 +0000 (05:48 -0500)
committerGitHub <noreply@github.com>
Mon, 1 Aug 2022 10:48:37 +0000 (12:48 +0200)
* enhance: add thermostat channels to amazonechocontrol

Signed-off-by: Daniel Campbell <djcampbell79@gmail.com>
19 files changed:
bundles/org.openhab.binding.amazonechocontrol/README.md
bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/AccountServlet.java
bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/BindingServlet.java
bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/Connection.java
bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/WebSocketConnection.java
bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/discovery/AmazonEchoDiscovery.java
bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/discovery/SmartHomeDevicesDiscovery.java
bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/handler/EchoHandler.java
bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/handler/SmartHomeDeviceHandler.java
bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonSmartHomeDevices.java
bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/JsonSmartHomeGroups.java
bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/jsons/SmartHomeBaseDevice.java
bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/smarthome/Constants.java
bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/smarthome/HandlerBase.java
bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/smarthome/HandlerHumiditySensor.java [new file with mode: 0644]
bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/smarthome/HandlerTemperatureSensor.java
bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/smarthome/HandlerThermostatController.java
bundles/org.openhab.binding.amazonechocontrol/src/main/resources/OH-INF/i18n/amazonechocontrol.properties
bundles/org.openhab.binding.amazonechocontrol/src/main/resources/OH-INF/thing/thing-types.xml

index 6d1979d13580c60830faf03d89119902b02237f7..a2428e6202f8da308ca69afb78e147072bf052fa 100644 (file)
@@ -27,6 +27,7 @@ It provides features to control and view the current state of echo devices:
 - change the equalizer settings
 - get information about the next alarm, reminder and timer
 - send a message to the echo devices
+- control alexa smart thermostat
 
 It also provides features to control devices connected to your echo:
 
@@ -58,6 +59,7 @@ Some ideas what you can do in your home by using rules and other openHAB control
 - Change the equalizer settings depending on the bluetooth connection
 - Turn on a light on your alexa alarm time
 - Activate or deactivate the Alexa Guard with presence detection
+- Adjust thermostat setpoint and mode
 
 With the possibility to control your lights you could do:
 
@@ -438,20 +440,25 @@ The only possibility to find out the id is by using the discover function in the
 The channels of the smarthome devices will be generated at runtime. Check in the UI thing configurations, which channels are created.
 
 | Channel Type ID          | Item Type | Access Mode | Thing Type                    | Description
-|--------------------------|-----------|-------------|-------------------------------|------------------------------------------------------------------------------------------
-| powerState               | Switch    | R/W         | smartHomeDevice, smartHomeDeviceGroup | Shows and changes the state (ON/OFF) of your device
-| brightness               | Dimmer    | R/W         | smartHomeDevice, smartHomeDeviceGroup | Shows and changes the brightness of your lamp
-| color                    | Color     | R           | smartHomeDevice, smartHomeDeviceGroup | Shows the color of your light
-| colorName                | String    | R/W         | smartHomeDevice, smartHomeDeviceGroup | Shows and changes the color name of your light (groups are not able to show their color)
-| colorTemperatureName     | String    | R/W         | smartHomeDevice, smartHomeDeviceGroup | White temperatures name of your lights (groups are not able to show their color)
-| armState                 | String    | R/W         | smartHomeDevice, smartHomeDeviceGroup | State of your alarm guard. Options: ARMED_AWAY, ARMED_STAY, ARMED_NIGHT, DISARMED (groups are not able to show their state)
-| burglaryAlarm            | Contact   | R           | smartHomeDevice | Burglary alarm
-| carbonMonoxideAlarm      | Contact   | R           | smartHomeDevice | Carbon monoxide detection alarm
-| fireAlarm                | Contact   | R           | smartHomeDevice | Fire alarm
-| waterAlarm               | Contact   | R           | smartHomeDevice | Water alarm
-| glassBreakDetectionState | Contact   | R           | smartHomeDevice | Glass break detection alarm
-| smokeAlarmDetectionState | Contact   | R           | smartHomeDevice | Smoke detection alarm
-| temperature              | Number    | R           | smartHomeDevice | Temperature
+|--------------------------|----------------------|-------------|-------------------------------|------------------------------------------------------------------------------------------
+| powerState               | Switch               | R/W         | smartHomeDevice, smartHomeDeviceGroup | Shows and changes the state (ON/OFF) of your device
+| brightness               | Dimmer               | R/W         | smartHomeDevice, smartHomeDeviceGroup | Shows and changes the brightness of your lamp
+| color                    | Color                | R           | smartHomeDevice, smartHomeDeviceGroup | Shows the color of your light
+| colorName                | String               | R/W         | smartHomeDevice, smartHomeDeviceGroup | Shows and changes the color name of your light (groups are not able to show their color)
+| colorTemperatureName     | String               | R/W         | smartHomeDevice, smartHomeDeviceGroup | White temperatures name of your lights (groups are not able to show their color)
+| armState                 | String               | R/W         | smartHomeDevice, smartHomeDeviceGroup | State of your alarm guard. Options: ARMED_AWAY, ARMED_STAY, ARMED_NIGHT, DISARMED (groups are not able to show their state)
+| burglaryAlarm            | Contact              | R           | smartHomeDevice | Burglary alarm
+| carbonMonoxideAlarm      | Contact              | R           | smartHomeDevice | Carbon monoxide detection alarm
+| fireAlarm                | Contact              | R           | smartHomeDevice | Fire alarm
+| waterAlarm               | Contact              | R           | smartHomeDevice | Water alarm
+| glassBreakDetectionState | Contact              | R           | smartHomeDevice | Glass break detection alarm
+| smokeAlarmDetectionState | Contact              | R           | smartHomeDevice | Smoke detection alarm
+| temperature              | Number:Temperature   | R           | smartHomeDevice | Temperature
+| targetSetpoint           | Number:Temperature   | R/W         | smartHomeDevice | Thermostat target setpoint
+| upperSetpoint            | Number:Temperature   | R/W         | smartHomeDevice | Thermostat upper setpoint (AUTO)
+| lowerSetpoint            | Number:Temperature   | R/W         | smartHomeDevice | Thermostat lower setpoint (AUTO)
+| relativeHumidity         | Number:Dimensionless | R           | smartHomeDevice | Thermostat humidity
+| thermostatMode           | String               | R/W         | smartHomeDevice | Thermostat operation mode
 
 ### Example
 
index ce006aea1049728bfdae05f22ddde71b1e973423..cc2974ca0bd58262346ac87af328a06f5d3ee08f 100644 (file)
@@ -145,7 +145,7 @@ public class AccountServlet extends HttpServlet {
         }
 
         Connection connection = this.account.findConnection();
-        if (connection != null && uri.equals("/changedomain")) {
+        if (connection != null && "/changedomain".equals(uri)) {
             Map<String, String[]> map = req.getParameterMap();
             String[] domainArray = map.get("domain");
             if (domainArray == null) {
@@ -199,7 +199,7 @@ public class AccountServlet extends HttpServlet {
             postDataBuilder.append(name);
             postDataBuilder.append('=');
             String value = "";
-            if (name.equals("failedSignInCount")) {
+            if ("failedSignInCount".equals(name)) {
                 value = "ape:AA==";
             } else {
                 String[] strings = map.get(name);
@@ -277,29 +277,29 @@ public class AccountServlet extends HttpServlet {
 
             if (connection != null && connection.verifyLogin()) {
                 // handle commands
-                if (baseUrl.equals("/logout") || baseUrl.equals("/logout/")) {
+                if ("/logout".equals(baseUrl) || "/logout/".equals(baseUrl)) {
                     this.connectionToInitialize = reCreateConnection();
                     this.account.setConnection(null);
                     resp.sendRedirect(this.servletUrl);
                     return;
                 }
                 // handle commands
-                if (baseUrl.equals("/newdevice") || baseUrl.equals("/newdevice/")) {
+                if ("/newdevice".equals(baseUrl) || "/newdevice/".equals(baseUrl)) {
                     this.connectionToInitialize = new Connection(null, this.gson);
                     this.account.setConnection(null);
                     resp.sendRedirect(this.servletUrl);
                     return;
                 }
 
-                if (baseUrl.equals("/devices") || baseUrl.equals("/devices/")) {
+                if ("/devices".equals(baseUrl) || "/devices/".equals(baseUrl)) {
                     handleDevices(resp, connection);
                     return;
                 }
-                if (baseUrl.equals("/changeDomain") || baseUrl.equals("/changeDomain/")) {
+                if ("/changeDomain".equals(baseUrl) || "/changeDomain/".equals(baseUrl)) {
                     handleChangeDomain(resp, connection);
                     return;
                 }
-                if (baseUrl.equals("/ids") || baseUrl.equals("/ids/")) {
+                if ("/ids".equals(baseUrl) || "/ids/".equals(baseUrl)) {
                     String serialNumber = getQueryMap(queryString).get("serialNumber");
                     Device device = account.findDeviceJson(serialNumber);
                     if (device != null) {
@@ -318,7 +318,7 @@ public class AccountServlet extends HttpServlet {
                 this.connectionToInitialize = connection;
             }
 
-            if (!uri.equals("/")) {
+            if (!"/".equals(uri)) {
                 String newUri = req.getServletPath() + "/";
                 resp.sendRedirect(newUri);
                 return;
index 69a671d47a542d7daef0b60b975a61c65020adb6..6ec223e70799cefd251547087c07dacc6a00a0d0 100644 (file)
@@ -97,7 +97,7 @@ public class BindingServlet extends HttpServlet {
         }
         logger.debug("doGet {}", uri);
 
-        if (!uri.equals("/")) {
+        if (!"/".equals(uri)) {
             String newUri = req.getServletPath() + "/";
             resp.sendRedirect(newUri);
             return;
index b950df3706aefd0579bc0cbce29b6bd6fd46e3a1..e868e24cbaceb08e5d599d89838aaa47b644a3f6 100644 (file)
@@ -12,6 +12,8 @@
  */
 package org.openhab.binding.amazonechocontrol.internal;
 
+import static org.openhab.binding.amazonechocontrol.internal.smarthome.Constants.*;
+
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InterruptedIOException;
@@ -32,10 +34,12 @@ import java.util.Base64;
 import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Objects;
 import java.util.Random;
 import java.util.Scanner;
@@ -109,6 +113,7 @@ import org.openhab.binding.amazonechocontrol.internal.jsons.JsonWebSiteCookie;
 import org.openhab.binding.amazonechocontrol.internal.jsons.SmartHomeBaseDevice;
 import org.openhab.core.common.ThreadPoolManager;
 import org.openhab.core.library.types.QuantityType;
+import org.openhab.core.library.types.StringType;
 import org.openhab.core.library.unit.SIUnits;
 import org.openhab.core.util.HexUtils;
 import org.slf4j.Logger;
@@ -118,6 +123,7 @@ import com.google.gson.Gson;
 import com.google.gson.GsonBuilder;
 import com.google.gson.JsonArray;
 import com.google.gson.JsonElement;
+import com.google.gson.JsonNull;
 import com.google.gson.JsonObject;
 import com.google.gson.JsonParseException;
 import com.google.gson.JsonSyntaxException;
@@ -644,7 +650,7 @@ public class Connection {
                 for (Map.Entry<@Nullable String, List<String>> header : headerFields.entrySet()) {
                     String key = header.getKey();
                     if (key != null && !key.isEmpty()) {
-                        if (key.equalsIgnoreCase("Set-Cookie")) {
+                        if ("Set-Cookie".equalsIgnoreCase(key)) {
                             // store cookie
                             for (String cookieHeader : header.getValue()) {
                                 if (!cookieHeader.isEmpty()) {
@@ -655,7 +661,7 @@ public class Connection {
                                 }
                             }
                         }
-                        if (key.equalsIgnoreCase("Location")) {
+                        if ("Location".equalsIgnoreCase(key)) {
                             // get redirect location
                             location = header.getValue().get(0);
                             if (!location.isEmpty()) {
@@ -1074,7 +1080,7 @@ public class Connection {
         requestObject.add("stateRequests", stateRequests);
         String requestBody = requestObject.toString();
         String json = makeRequestAndReturnString("POST", alexaServer + "/api/phoenix/state", requestBody, true, null);
-        logger.trace("Requested {} and received {}", requestBody, json);
+        logger.debug("Requested {} and received {}", requestBody, json);
 
         JsonObject responseObject = Objects.requireNonNull(gson.fromJson(json, JsonObject.class));
         JsonArray deviceStates = (JsonArray) responseObject.get("deviceStates");
@@ -1164,6 +1170,8 @@ public class Connection {
     public void smartHomeCommand(String entityId, String action, @Nullable String property, @Nullable Object value)
             throws IOException, InterruptedException {
         String url = alexaServer + "/api/phoenix/state";
+        Float lowerSetpoint = null;
+        Float upperSetpoint = null;
 
         JsonObject json = new JsonObject();
         JsonArray controlRequests = new JsonArray();
@@ -1173,20 +1181,95 @@ public class Connection {
         JsonObject parameters = new JsonObject();
         parameters.addProperty("action", action);
         if (property != null) {
-            if (value instanceof QuantityType<?>) {
-                parameters.addProperty(property + ".value", ((QuantityType<?>) value).floatValue());
-                parameters.addProperty(property + ".scale",
-                        ((QuantityType<?>) value).getUnit().equals(SIUnits.CELSIUS) ? "celsius" : "fahrenheit");
-            } else if (value instanceof Boolean) {
-                parameters.addProperty(property, (boolean) value);
-            } else if (value instanceof String) {
-                parameters.addProperty(property, (String) value);
-            } else if (value instanceof Number) {
-                parameters.addProperty(property, (Number) value);
-            } else if (value instanceof Character) {
-                parameters.addProperty(property, (Character) value);
-            } else if (value instanceof JsonElement) {
-                parameters.add(property, (JsonElement) value);
+            if ("setThermostatMode".equals(action)) {
+                if (value instanceof StringType) {
+                    parameters.addProperty(property + ".value", value.toString());
+                }
+            } else if ("setTargetTemperature".equals(action)) {
+                if ("targetTemperature".equals(property)) {
+                    if (value instanceof QuantityType<?>) {
+                        parameters.addProperty(property + ".value", ((QuantityType<?>) value).floatValue());
+                        parameters.addProperty(property + ".scale",
+                                ((QuantityType<?>) value).getUnit().equals(SIUnits.CELSIUS) ? "celsius" : "fahrenheit");
+                    }
+                } else {
+                    // Get current upper and lower setpoints to build command syntax
+                    Map<String, JsonArray> devices = null;
+                    try {
+                        List<SmartHomeBaseDevice> deviceList = getSmarthomeDeviceList().stream()
+                                .filter(device -> entityId.equals(device.findEntityId())).collect(Collectors.toList());
+                        devices = getSmartHomeDeviceStatesJson(new HashSet<>(deviceList));
+                    } catch (URISyntaxException e) {
+                        logger.debug("{}", e.toString());
+                    }
+                    Entry<String, JsonArray> entry = devices.entrySet().iterator().next();
+                    JsonArray states = entry.getValue();
+                    for (JsonElement stateElement : states) {
+                        JsonObject stateValue = new JsonObject();
+                        String stateJson = stateElement.getAsString();
+                        if (stateJson.startsWith("{") && stateJson.endsWith("}")) {
+                            JsonObject state = Objects.requireNonNull(gson.fromJson(stateJson, JsonObject.class));
+                            String interfaceName = Objects.requireNonNullElse(state.get("namespace"), JsonNull.INSTANCE)
+                                    .getAsString();
+                            String name = Objects.requireNonNullElse(state.get("name"), JsonNull.INSTANCE)
+                                    .getAsString();
+                            if ("Alexa.ThermostatController".equals(interfaceName)) {
+                                if ("upperSetpoint".equals(name)) {
+                                    stateValue = Objects.requireNonNullElse(state.get("value"), JsonNull.INSTANCE)
+                                            .getAsJsonObject();
+                                    upperSetpoint = Objects
+                                            .requireNonNullElse(stateValue.get("value"), JsonNull.INSTANCE)
+                                            .getAsFloat();
+                                } else if ("lowerSetpoint".equals(name)) {
+                                    stateValue = Objects.requireNonNullElse(state.get("value"), JsonNull.INSTANCE)
+                                            .getAsJsonObject();
+                                    lowerSetpoint = Objects
+                                            .requireNonNullElse(stateValue.get("value"), JsonNull.INSTANCE)
+                                            .getAsFloat();
+                                }
+                            }
+                        }
+                    }
+                    if ("lowerSetTemperature".equals(property)) {
+                        if (value instanceof QuantityType<?>) {
+                            parameters.addProperty("upperSetTemperature.value", upperSetpoint);
+                            parameters.addProperty("upperSetTemperature.scale",
+                                    ((QuantityType<?>) value).getUnit().equals(SIUnits.CELSIUS) ? "celsius"
+                                            : "fahrenheit");
+                            parameters.addProperty(property + ".value", ((QuantityType<?>) value).floatValue());
+                            parameters.addProperty(property + ".scale",
+                                    ((QuantityType<?>) value).getUnit().equals(SIUnits.CELSIUS) ? "celsius"
+                                            : "fahrenheit");
+                        }
+                    } else if ("upperSetTemperature".equals(property)) {
+                        if (value instanceof QuantityType<?>) {
+                            parameters.addProperty(property + ".value", ((QuantityType<?>) value).floatValue());
+                            parameters.addProperty(property + ".scale",
+                                    ((QuantityType<?>) value).getUnit().equals(SIUnits.CELSIUS) ? "celsius"
+                                            : "fahrenheit");
+                            parameters.addProperty("lowerSetTemperature.value", lowerSetpoint);
+                            parameters.addProperty("lowerSetTemperature.scale",
+                                    ((QuantityType<?>) value).getUnit().equals(SIUnits.CELSIUS) ? "celsius"
+                                            : "fahrenheit");
+                        }
+                    }
+                }
+            } else {
+                if (value instanceof QuantityType<?>) {
+                    parameters.addProperty(property + ".value", ((QuantityType<?>) value).floatValue());
+                    parameters.addProperty(property + ".scale",
+                            ((QuantityType<?>) value).getUnit().equals(SIUnits.CELSIUS) ? "celsius" : "fahrenheit");
+                } else if (value instanceof Boolean) {
+                    parameters.addProperty(property, (boolean) value);
+                } else if (value instanceof String) {
+                    parameters.addProperty(property, (String) value);
+                } else if (value instanceof Number) {
+                    parameters.addProperty(property, (Number) value);
+                } else if (value instanceof Character) {
+                    parameters.addProperty(property, (Character) value);
+                } else if (value instanceof JsonElement) {
+                    parameters.add(property, (JsonElement) value);
+                }
             }
         }
         controlRequest.add("parameters", parameters);
index 399ea52a04d530268726fec9205986d2f4ad2bdf..872dc40fd48e2dae9f9dfb90075bb22687906f76 100644 (file)
@@ -30,6 +30,7 @@ import java.util.concurrent.ThreadLocalRandom;
 
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
+import org.eclipse.jetty.client.HttpClient;
 import org.eclipse.jetty.util.ssl.SslContextFactory;
 import org.eclipse.jetty.websocket.api.Session;
 import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
@@ -73,10 +74,11 @@ public class WebSocketConnection {
             IWebSocketCommandHandler webSocketCommandHandler) throws IOException {
         this.webSocketCommandHandler = webSocketCommandHandler;
         amazonEchoControlWebSocket = new AmazonEchoControlWebSocket();
-        webSocketClient = new WebSocketClient(new SslContextFactory.Client());
+        HttpClient httpClient = new HttpClient(new SslContextFactory.Client());
+        webSocketClient = new WebSocketClient(httpClient);
         try {
             String host;
-            if (amazonSite.equalsIgnoreCase("amazon.com")) {
+            if ("amazon.com".equalsIgnoreCase(amazonSite)) {
                 host = "dp-gw-na-js." + amazonSite;
             } else {
                 host = "dp-gw-na." + amazonSite;
index 13a2bceccec8f0236e50d0c5d3c73b9128635fbe..073a1061f1db90f38bde8e2c945138b3739a324a 100644 (file)
@@ -139,13 +139,13 @@ public class AmazonEchoDiscovery extends AbstractDiscoveryService {
                 String deviceFamily = device.deviceFamily;
                 if (deviceFamily != null) {
                     ThingTypeUID thingTypeId;
-                    if (deviceFamily.equals("ECHO")) {
+                    if ("ECHO".equals(deviceFamily)) {
                         thingTypeId = THING_TYPE_ECHO;
-                    } else if (deviceFamily.equals("ROOK")) {
+                    } else if ("ROOK".equals(deviceFamily)) {
                         thingTypeId = THING_TYPE_ECHO_SPOT;
-                    } else if (deviceFamily.equals("KNIGHT")) {
+                    } else if ("KNIGHT".equals(deviceFamily)) {
                         thingTypeId = THING_TYPE_ECHO_SHOW;
-                    } else if (deviceFamily.equals("WHA")) {
+                    } else if ("WHA".equals(deviceFamily)) {
                         thingTypeId = THING_TYPE_ECHO_WHA;
                     } else {
                         logger.debug("Unknown thing type '{}'", deviceFamily);
index 2ac945bc796f43dd1058cb526ecaccadf9c76924..2f7c8ef242888bc28783f8d97f399907655005d9 100644 (file)
@@ -49,7 +49,7 @@ import org.slf4j.LoggerFactory;
 @NonNullByDefault
 public class SmartHomeDevicesDiscovery extends AbstractDiscoveryService {
     private AccountHandler accountHandler;
-    private final Logger logger = LoggerFactory.getLogger(SmartHomeDevicesDiscovery.class);
+    private Logger logger = LoggerFactory.getLogger(SmartHomeDevicesDiscovery.class);
 
     private @Nullable ScheduledFuture<?> startScanStateJob;
     private @Nullable Long activateTimeStamp;
@@ -189,6 +189,8 @@ public class SmartHomeDevicesDiscovery extends AbstractDiscoveryService {
                         deviceName = "Alexa Color Controller on " + shd.friendlyName;
                     } else if (interfaces.contains("Alexa.PowerController")) {
                         deviceName = "Alexa Plug on " + shd.friendlyName;
+                    } else if (interfaces.contains("Alexa.ThermostatController")) {
+                        deviceName = "Alexa Smart " + shd.friendlyName;
                     } else {
                         deviceName = "Unknown Device on " + shd.friendlyName;
                     }
index 110a5b2cc760a00490aad8fa4deafe41f6fbe412..59952ace83b50850abd28815d4d657e6c0e80746 100644 (file)
@@ -783,11 +783,11 @@ public class EchoHandler extends BaseThingHandler implements IEchoThingHandler {
             if (currentNotification != null) {
                 String type = currentNotification.type;
                 if (type != null) {
-                    if (type.equals("Reminder")) {
+                    if ("Reminder".equals(type)) {
                         updateState(CHANNEL_REMIND, StringType.EMPTY);
                         updateRemind = false;
                     }
-                    if (type.equals("Alarm")) {
+                    if ("Alarm".equals(type)) {
                         updateState(CHANNEL_PLAY_ALARM_SOUND, StringType.EMPTY);
                         updateAlarm = false;
                     }
@@ -864,10 +864,10 @@ public class EchoHandler extends BaseThingHandler implements IEchoThingHandler {
                             if (musicProviderId != null) {
                                 musicProviderId = musicProviderId.toUpperCase();
 
-                                if (musicProviderId.equals("AMAZON MUSIC")) {
+                                if ("AMAZON MUSIC".equals(musicProviderId)) {
                                     musicProviderId = "AMAZON_MUSIC";
                                 }
-                                if (musicProviderId.equals("CLOUD_PLAYER")) {
+                                if ("CLOUD_PLAYER".equals(musicProviderId)) {
                                     musicProviderId = "AMAZON_MUSIC";
                                 }
                                 if (musicProviderId.startsWith("TUNEIN")) {
@@ -876,7 +876,7 @@ public class EchoHandler extends BaseThingHandler implements IEchoThingHandler {
                                 if (musicProviderId.startsWith("IHEARTRADIO")) {
                                     musicProviderId = "I_HEART_RADIO";
                                 }
-                                if (musicProviderId.equals("APPLE") && musicProviderId.contains("MUSIC")) {
+                                if ("APPLE".equals(musicProviderId) && musicProviderId.contains("MUSIC")) {
                                     musicProviderId = "APPLE_MUSIC";
                                 }
                             }
index 5cfda45db1bec86d335a6df91aace1361c16684a..6eb054499410eb99e8a6f3bf4042dad922662dcc 100644 (file)
@@ -347,7 +347,6 @@ public class SmartHomeDeviceHandler extends BaseThingHandler {
             if (shd.getCapabilities().stream().map(capability -> capability.interfaceName)
                     .anyMatch(SUPPORTED_INTERFACES::contains)) {
                 result.add(shd);
-
             }
         } else {
             SmartHomeGroup shg = (SmartHomeGroup) baseDevice;
index fcc53623d8fa659e9651e8b0fa143aca9314b7e0..a02ef015cc316978617a7dd42b0abc6cfbb19ff2 100644 (file)
@@ -55,6 +55,11 @@ public class JsonSmartHomeDevices {
             return applianceId;
         }
 
+        @Override
+        public @Nullable String findEntityId() {
+            return entityId;
+        }
+
         @Override
         public boolean isGroup() {
             return false;
index 4fa6dbea98479fa8764ab4fec56912d4bd6a611f..6ed6eada82944149a1a9adc4c76219a7cecce4af 100644 (file)
@@ -39,6 +39,19 @@ public class JsonSmartHomeGroups {
             return value;
         }
 
+        @Override
+        public @Nullable String findEntityId() {
+            SmartHomeGroupIdentifier applianceGroupIdentifier = this.applianceGroupIdentifier;
+            if (applianceGroupIdentifier == null) {
+                return null;
+            }
+            String value = applianceGroupIdentifier.value;
+            if (value == null) {
+                return null;
+            }
+            return value;
+        }
+
         @Override
         public boolean isGroup() {
             return true;
index 874495bf2f8145c3db3a16a29426e4137f2425a4..dacec9f873acd11e16547a18ce17ae6f7bf98e50 100644 (file)
@@ -26,23 +26,38 @@ import org.openhab.core.thing.type.ChannelTypeUID;
  */
 @NonNullByDefault
 public class Constants {
-    public static final Map<String, Function<SmartHomeDeviceHandler, HandlerBase>> HANDLER_FACTORY = Map.of(
-            HandlerPowerController.INTERFACE, HandlerPowerController::new, HandlerBrightnessController.INTERFACE,
-            HandlerBrightnessController::new, HandlerColorController.INTERFACE, HandlerColorController::new,
-            HandlerColorTemperatureController.INTERFACE, HandlerColorTemperatureController::new,
-            HandlerSecurityPanelController.INTERFACE, HandlerSecurityPanelController::new,
-            HandlerAcousticEventSensor.INTERFACE, HandlerAcousticEventSensor::new, HandlerTemperatureSensor.INTERFACE,
-            HandlerTemperatureSensor::new, HandlerThermostatController.INTERFACE, HandlerThermostatController::new,
-            HandlerPercentageController.INTERFACE, HandlerPercentageController::new,
-            HandlerPowerLevelController.INTERFACE, HandlerPowerLevelController::new);
+    public static final Map<String, Function<SmartHomeDeviceHandler, HandlerBase>> HANDLER_FACTORY = Map.ofEntries(
+            Map.entry(HandlerPowerController.INTERFACE, HandlerPowerController::new),
+            Map.entry(HandlerBrightnessController.INTERFACE, HandlerBrightnessController::new),
+            Map.entry(HandlerColorController.INTERFACE, HandlerColorController::new),
+            Map.entry(HandlerColorTemperatureController.INTERFACE, HandlerColorTemperatureController::new),
+            Map.entry(HandlerSecurityPanelController.INTERFACE, HandlerSecurityPanelController::new),
+            Map.entry(HandlerAcousticEventSensor.INTERFACE, HandlerAcousticEventSensor::new),
+            Map.entry(HandlerTemperatureSensor.INTERFACE, HandlerTemperatureSensor::new),
+            Map.entry(HandlerThermostatController.INTERFACE, HandlerThermostatController::new),
+            Map.entry(HandlerPercentageController.INTERFACE, HandlerPercentageController::new),
+            Map.entry(HandlerPowerLevelController.INTERFACE, HandlerPowerLevelController::new),
+            Map.entry(HandlerHumiditySensor.INTERFACE, HandlerHumiditySensor::new));
 
     public static final Set<String> SUPPORTED_INTERFACES = HANDLER_FACTORY.keySet();
 
     // channel types
     public static final ChannelTypeUID CHANNEL_TYPE_TEMPERATURE = new ChannelTypeUID(
             AmazonEchoControlBindingConstants.BINDING_ID, "temperature");
+    public static final ChannelTypeUID CHANNEL_TYPE_HUMIDITY = new ChannelTypeUID(
+            AmazonEchoControlBindingConstants.BINDING_ID, "relativeHumidity");
     public static final ChannelTypeUID CHANNEL_TYPE_TARGETSETPOINT = new ChannelTypeUID(
             AmazonEchoControlBindingConstants.BINDING_ID, "targetSetpoint");
+    public static final ChannelTypeUID CHANNEL_TYPE_LOWERSETPOINT = new ChannelTypeUID(
+            AmazonEchoControlBindingConstants.BINDING_ID, "lowerSetpoint");
+    public static final ChannelTypeUID CHANNEL_TYPE_UPPERSETPOINT = new ChannelTypeUID(
+            AmazonEchoControlBindingConstants.BINDING_ID, "upperSetpoint");
+    public static final ChannelTypeUID CHANNEL_TYPE_THERMOSTATMODE = new ChannelTypeUID(
+            AmazonEchoControlBindingConstants.BINDING_ID, "thermostatMode");
+    public static final ChannelTypeUID CHANNEL_TYPE_FAN_OPERATION = new ChannelTypeUID(
+            AmazonEchoControlBindingConstants.BINDING_ID, "fanOperation");
+    public static final ChannelTypeUID CHANNEL_TYPE_COOLER_OPERATION = new ChannelTypeUID(
+            AmazonEchoControlBindingConstants.BINDING_ID, "coolerOperation");
 
     // List of Item types
     public static final String ITEM_TYPE_SWITCH = "Switch";
@@ -50,6 +65,7 @@ public class Constants {
     public static final String ITEM_TYPE_STRING = "String";
     public static final String ITEM_TYPE_NUMBER = "Number";
     public static final String ITEM_TYPE_NUMBER_TEMPERATURE = "Number:Temperature";
+    public static final String ITEM_TYPE_HUMIDITY = "Number:Dimensionless";
     public static final String ITEM_TYPE_CONTACT = "Contact";
     public static final String ITEM_TYPE_COLOR = "Color";
 }
index e95b0a469b5ec291c43930c717c284c0c46b41d2..ddbc2b212838b527bdb3883a220c90336f301cd9 100644 (file)
@@ -33,6 +33,8 @@ import org.openhab.core.thing.type.ChannelTypeUID;
 import org.openhab.core.types.Command;
 import org.openhab.core.types.State;
 import org.openhab.core.types.StateDescription;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import com.google.gson.JsonObject;
 
@@ -41,6 +43,9 @@ import com.google.gson.JsonObject;
  */
 @NonNullByDefault
 public abstract class HandlerBase {
+    // Logger
+    private final Logger logger = LoggerFactory.getLogger(HandlerBase.class);
+
     protected SmartHomeDeviceHandler smartHomeDeviceHandler;
     protected Map<String, ChannelInfo> channels = new HashMap<>();
 
@@ -76,6 +81,7 @@ public abstract class HandlerBase {
             if (properties != null) {
                 List<JsonSmartHomeCapabilities.Property> supported = Objects.requireNonNullElse(properties.supported,
                         List.of());
+                logger.trace("{} | {}", capability.toString(), supported.toString());
                 for (Property property : supported) {
                     String name = property.name;
                     if (name != null) {
diff --git a/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/smarthome/HandlerHumiditySensor.java b/bundles/org.openhab.binding.amazonechocontrol/src/main/java/org/openhab/binding/amazonechocontrol/internal/smarthome/HandlerHumiditySensor.java
new file mode 100644 (file)
index 0000000..6576b55
--- /dev/null
@@ -0,0 +1,101 @@
+/**
+ * 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.amazonechocontrol.internal.smarthome;
+
+import static org.openhab.binding.amazonechocontrol.internal.smarthome.Constants.*;
+import static org.openhab.core.library.unit.Units.*;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Locale;
+
+import javax.measure.Unit;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.amazonechocontrol.internal.Connection;
+import org.openhab.binding.amazonechocontrol.internal.handler.SmartHomeDeviceHandler;
+import org.openhab.binding.amazonechocontrol.internal.jsons.JsonSmartHomeCapabilities.SmartHomeCapability;
+import org.openhab.binding.amazonechocontrol.internal.jsons.JsonSmartHomeDevices.SmartHomeDevice;
+import org.openhab.core.library.types.QuantityType;
+import org.openhab.core.types.Command;
+import org.openhab.core.types.State;
+import org.openhab.core.types.StateDescription;
+import org.openhab.core.types.UnDefType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.JsonObject;
+
+/**
+ * The {@link HandlerHumiditySensor} is responsible for the Alexa.HumiditySensorInterface
+ *
+ * @author Daniel Campbell - Initial contribution
+ */
+@NonNullByDefault
+public class HandlerHumiditySensor extends HandlerBase {
+    // Logger
+    private final Logger logger = LoggerFactory.getLogger(HandlerHumiditySensor.class);
+    // Interface
+    public static final String INTERFACE = "Alexa.HumiditySensor";
+    // Channel definitions
+    private static final ChannelInfo HUMIDITY = new ChannelInfo("relativeHumidity" /* propertyName */ ,
+            "relativeHumidity" /* ChannelId */, CHANNEL_TYPE_HUMIDITY /* Channel Type */ ,
+            ITEM_TYPE_HUMIDITY /* Item Type */);
+
+    public HandlerHumiditySensor(SmartHomeDeviceHandler smartHomeDeviceHandler) {
+        super(smartHomeDeviceHandler);
+    }
+
+    @Override
+    public String[] getSupportedInterface() {
+        return new String[] { INTERFACE };
+    }
+
+    @Override
+    protected ChannelInfo @Nullable [] findChannelInfos(SmartHomeCapability capability, String property) {
+        if (HUMIDITY.propertyName.equals(property)) {
+            return new ChannelInfo[] { HUMIDITY };
+        }
+        return null;
+    }
+
+    @Override
+    public void updateChannels(String interfaceName, List<JsonObject> stateList, UpdateChannelResult result) {
+        for (JsonObject state : stateList) {
+            State humidityValue = null;
+            logger.debug("Updating {} with state: {}", interfaceName, state.toString());
+            if (HUMIDITY.propertyName.equals(state.get("name").getAsString())) {
+                // For groups take the first
+                humidityValue = getQuantityTypeState(state.get("value").getAsInt(), PERCENT);
+                updateState(HUMIDITY.channelId, humidityValue == null ? UnDefType.UNDEF : humidityValue);
+            }
+        }
+    }
+
+    protected State getQuantityTypeState(@Nullable Number value, Unit<?> unit) {
+        return (value == null) ? UnDefType.UNDEF : new QuantityType<>(value, unit);
+    }
+
+    @Override
+    public boolean handleCommand(Connection connection, SmartHomeDevice shd, String entityId,
+            List<SmartHomeCapability> capabilities, String channelId, Command command) throws IOException {
+        return false;
+    }
+
+    @Override
+    public @Nullable StateDescription findStateDescription(String channelId, StateDescription originalStateDescription,
+            @Nullable Locale locale) {
+        return null;
+    }
+}
index 3f87071905b5f1177b129b360d7fc1e73816204c..a24c7115d1101a49bfc0b2b70e40c5b410235a4d 100644 (file)
@@ -32,17 +32,21 @@ import org.openhab.core.library.unit.SIUnits;
 import org.openhab.core.types.Command;
 import org.openhab.core.types.StateDescription;
 import org.openhab.core.types.UnDefType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import com.google.gson.JsonObject;
 
 /**
- * The {@link HandlerTemperatureSensor} is responsible for the Alexa.PowerControllerInterface
+ * The {@link HandlerTemperatureSensor} is responsible for the Alexa.TemperatureSensorInterface
  *
  * @author Lukas Knoeller - Initial contribution
  * @author Michael Geramb - Initial contribution
  */
 @NonNullByDefault
 public class HandlerTemperatureSensor extends HandlerBase {
+    // Logger
+    private final Logger logger = LoggerFactory.getLogger(HandlerTemperatureSensor.class);
     // Interface
     public static final String INTERFACE = "Alexa.TemperatureSensor";
     // Channel definitions
@@ -71,6 +75,7 @@ public class HandlerTemperatureSensor extends HandlerBase {
     public void updateChannels(String interfaceName, List<JsonObject> stateList, UpdateChannelResult result) {
         QuantityType<Temperature> temperatureValue = null;
         for (JsonObject state : stateList) {
+            logger.debug("Updating {} with state: {}", interfaceName, state.toString());
             if (TEMPERATURE.propertyName.equals(state.get("name").getAsString())) {
                 JsonObject value = state.get("value").getAsJsonObject();
                 // For groups take the first
index 44fba494bc00e6df28a12b6757af8752b6607a8a..033d7c07afd975d8a606967fa45cef2fe793cc6a 100644 (file)
@@ -27,11 +27,14 @@ import org.openhab.binding.amazonechocontrol.internal.handler.SmartHomeDeviceHan
 import org.openhab.binding.amazonechocontrol.internal.jsons.JsonSmartHomeCapabilities.SmartHomeCapability;
 import org.openhab.binding.amazonechocontrol.internal.jsons.JsonSmartHomeDevices.SmartHomeDevice;
 import org.openhab.core.library.types.QuantityType;
+import org.openhab.core.library.types.StringType;
 import org.openhab.core.library.unit.ImperialUnits;
 import org.openhab.core.library.unit.SIUnits;
 import org.openhab.core.types.Command;
 import org.openhab.core.types.StateDescription;
 import org.openhab.core.types.UnDefType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import com.google.gson.JsonObject;
 
@@ -42,12 +45,23 @@ import com.google.gson.JsonObject;
  */
 @NonNullByDefault
 public class HandlerThermostatController extends HandlerBase {
+    // Logger
+    private final Logger logger = LoggerFactory.getLogger(HandlerThermostatController.class);
     // Interface
     public static final String INTERFACE = "Alexa.ThermostatController";
     // Channel definitions
     private static final ChannelInfo TARGET_SETPOINT = new ChannelInfo("targetSetpoint" /* propertyName */ ,
             "targetSetpoint" /* ChannelId */, CHANNEL_TYPE_TARGETSETPOINT /* Channel Type */ ,
             ITEM_TYPE_NUMBER_TEMPERATURE /* Item Type */);
+    private static final ChannelInfo LOWER_SETPOINT = new ChannelInfo("lowerSetpoint" /* propertyName */ ,
+            "lowerSetpoint" /* ChannelId */, CHANNEL_TYPE_LOWERSETPOINT /* Channel Type */ ,
+            ITEM_TYPE_NUMBER_TEMPERATURE /* Item Type */);
+    private static final ChannelInfo UPPER_SETPOINT = new ChannelInfo("upperSetpoint" /* propertyName */ ,
+            "upperSetpoint" /* ChannelId */, CHANNEL_TYPE_UPPERSETPOINT /* Channel Type */ ,
+            ITEM_TYPE_NUMBER_TEMPERATURE /* Item Type */);
+    private static final ChannelInfo THERMOSTAT_MODE = new ChannelInfo("thermostatMode" /* propertyName */ ,
+            "thermostatMode" /* ChannelId */, CHANNEL_TYPE_THERMOSTATMODE /* Channel Type */ ,
+            ITEM_TYPE_STRING /* Item Type */);
 
     public HandlerThermostatController(SmartHomeDeviceHandler smartHomeDeviceHandler) {
         super(smartHomeDeviceHandler);
@@ -63,17 +77,47 @@ public class HandlerThermostatController extends HandlerBase {
         if (TARGET_SETPOINT.propertyName.equals(property)) {
             return new ChannelInfo[] { TARGET_SETPOINT };
         }
+        if (LOWER_SETPOINT.propertyName.equals(property)) {
+            return new ChannelInfo[] { LOWER_SETPOINT };
+        }
+        if (UPPER_SETPOINT.propertyName.equals(property)) {
+            return new ChannelInfo[] { UPPER_SETPOINT };
+        }
+        if (THERMOSTAT_MODE.propertyName.equals(property)) {
+            return new ChannelInfo[] { THERMOSTAT_MODE };
+        }
         return null;
     }
 
     @Override
     public void updateChannels(String interfaceName, List<JsonObject> stateList, UpdateChannelResult result) {
-        QuantityType<Temperature> temperatureValue = null;
         for (JsonObject state : stateList) {
+            QuantityType<Temperature> temperatureValue = null;
+            logger.debug("Updating {} with state: {}", interfaceName, state.toString());
             if (TARGET_SETPOINT.propertyName.equals(state.get("name").getAsString())) {
-                JsonObject value = state.get("value").getAsJsonObject();
                 // For groups take the first
                 if (temperatureValue == null) {
+                    JsonObject value = state.get("value").getAsJsonObject();
+                    float temperature = value.get("value").getAsFloat();
+                    String scale = value.get("scale").getAsString().toUpperCase();
+                    if ("CELSIUS".equals(scale)) {
+                        temperatureValue = new QuantityType<Temperature>(temperature, SIUnits.CELSIUS);
+                    } else {
+                        temperatureValue = new QuantityType<Temperature>(temperature, ImperialUnits.FAHRENHEIT);
+                    }
+                }
+                updateState(TARGET_SETPOINT.channelId, temperatureValue == null ? UnDefType.UNDEF : temperatureValue);
+            }
+            if (THERMOSTAT_MODE.propertyName.equals(state.get("name").getAsString())) {
+                // For groups take the first
+                String operation = state.get("value").getAsString().toUpperCase();
+                StringType operationValue = new StringType(operation);
+                updateState(THERMOSTAT_MODE.channelId, operationValue);
+            }
+            if (UPPER_SETPOINT.propertyName.equals(state.get("name").getAsString())) {
+                // For groups take the first
+                if (temperatureValue == null) {
+                    JsonObject value = state.get("value").getAsJsonObject();
                     float temperature = value.get("value").getAsFloat();
                     String scale = value.get("scale").getAsString().toUpperCase();
                     if ("CELSIUS".equals(scale)) {
@@ -82,9 +126,23 @@ public class HandlerThermostatController extends HandlerBase {
                         temperatureValue = new QuantityType<Temperature>(temperature, ImperialUnits.FAHRENHEIT);
                     }
                 }
+                updateState(UPPER_SETPOINT.channelId, temperatureValue == null ? UnDefType.UNDEF : temperatureValue);
+            }
+            if (LOWER_SETPOINT.propertyName.equals(state.get("name").getAsString())) {
+                // For groups take the first
+                if (temperatureValue == null) {
+                    JsonObject value = state.get("value").getAsJsonObject();
+                    float temperature = value.get("value").getAsFloat();
+                    String scale = value.get("scale").getAsString().toUpperCase();
+                    if ("CELSIUS".equals(scale)) {
+                        temperatureValue = new QuantityType<Temperature>(temperature, SIUnits.CELSIUS);
+                    } else {
+                        temperatureValue = new QuantityType<Temperature>(temperature, ImperialUnits.FAHRENHEIT);
+                    }
+                }
+                updateState(LOWER_SETPOINT.channelId, temperatureValue == null ? UnDefType.UNDEF : temperatureValue);
             }
         }
-        updateState(TARGET_SETPOINT.channelId, temperatureValue == null ? UnDefType.UNDEF : temperatureValue);
     }
 
     @Override
@@ -99,6 +157,30 @@ public class HandlerThermostatController extends HandlerBase {
                 }
             }
         }
+        if (channelId.equals(LOWER_SETPOINT.channelId)) {
+            if (containsCapabilityProperty(capabilities, LOWER_SETPOINT.propertyName)) {
+                if (command instanceof QuantityType) {
+                    connection.smartHomeCommand(entityId, "setTargetTemperature", "lowerSetTemperature", command);
+                    return true;
+                }
+            }
+        }
+        if (channelId.equals(UPPER_SETPOINT.channelId)) {
+            if (containsCapabilityProperty(capabilities, UPPER_SETPOINT.propertyName)) {
+                if (command instanceof QuantityType) {
+                    connection.smartHomeCommand(entityId, "setTargetTemperature", "upperSetTemperature", command);
+                    return true;
+                }
+            }
+        }
+        if (channelId.equals(THERMOSTAT_MODE.channelId)) {
+            if (containsCapabilityProperty(capabilities, THERMOSTAT_MODE.propertyName)) {
+                if (command instanceof StringType) {
+                    connection.smartHomeCommand(entityId, "setThermostatMode", "thermostatMode", command);
+                    return true;
+                }
+            }
+        }
         return false;
     }
 
index 1875688d70123ac5e2edbe4c2013af253f66b033..dba47615fedf6425eb9fb94ebf9356dd63f51f3e 100644 (file)
@@ -101,6 +101,8 @@ channel-type.amazonechocontrol.lastVoiceCommand.label = Last Voice Command
 channel-type.amazonechocontrol.lastVoiceCommand.description = Last voice command spoken to the device. Writing to the channel starts voice output.
 channel-type.amazonechocontrol.loop.label = Loop
 channel-type.amazonechocontrol.loop.description = Loop
+channel-type.amazonechocontrol.lowerSetpoint.label = Lower Setpoint
+channel-type.amazonechocontrol.lowerSetpoint.description = Lower Setpoint
 channel-type.amazonechocontrol.mediaLength.label = Media Length
 channel-type.amazonechocontrol.mediaLength.description = Media length
 channel-type.amazonechocontrol.mediaProgress.label = Media Progress
@@ -139,6 +141,8 @@ channel-type.amazonechocontrol.radio.label = TuneIn Radio
 channel-type.amazonechocontrol.radio.description = Radio turned on
 channel-type.amazonechocontrol.radioStationId.label = TuneIn Radio Station Id
 channel-type.amazonechocontrol.radioStationId.description = Id of the radio station
+channel-type.amazonechocontrol.relativeHumidity.label = Humidity
+channel-type.amazonechocontrol.relativeHumidity.description = Relative humidity measured by the thermostat.
 channel-type.amazonechocontrol.remind.label = Remind
 channel-type.amazonechocontrol.remind.description = Speak the reminder and send a notification to the Alexa app
 channel-type.amazonechocontrol.save.label = Save
@@ -173,8 +177,12 @@ channel-type.amazonechocontrol.textToSpeech.label = Speak
 channel-type.amazonechocontrol.textToSpeech.description = Speak the text (Write only). It is possible to use plain text or SSML: <speak>I want to tell you a secret.<amazon:effect name="whispered">I am not a real human.</amazon:effect>.Can you believe it?</speak>
 channel-type.amazonechocontrol.textToSpeechVolume.label = Speak Volume
 channel-type.amazonechocontrol.textToSpeechVolume.description = Volume of the Speak channel. If 0, the current volume will be used.
+channel-type.amazonechocontrol.thermostatMode.label = Thermostat Mode
+channel-type.amazonechocontrol.thermostatMode.description = Thermostat Mode
 channel-type.amazonechocontrol.title.label = Title
 channel-type.amazonechocontrol.title.description = Title
+channel-type.amazonechocontrol.upperSetpoint.label = Upper Setpoint
+channel-type.amazonechocontrol.upperSetpoint.description = Upper Setpoint
 channel-type.amazonechocontrol.volume.label = Volume
 channel-type.amazonechocontrol.volume.description = Volume of the sound
 channel-type.amazonechocontrol.waterAlarm.label = Water Alarm
index 20601205a7a24a1e08d5a86945fe988a16ea2913..8e3a374cafea64c88a50e3b61ceb36404af9094b 100644 (file)
                <description>Temperature</description>
                <state readOnly="true" pattern="%.1f %unit%"/>
        </channel-type>
+       <!-- Alexa.HumiditySensor -->
+       <channel-type id="relativeHumidity">
+               <item-type>Number:Dimensionless</item-type>
+               <label>Humidity</label>
+               <description>Relative humidity measured by the thermostat.</description>
+               <category>Humidity</category>
+               <tags>
+                       <tag>Measurement</tag>
+                       <tag>Temperature</tag>
+               </tags>
+               <state readOnly="true" pattern=":.1%"/>
+       </channel-type>
        <!-- Alexa.ThermostatController -->
        <channel-type id="targetSetpoint">
                <item-type>Number:Temperature</item-type>
                <description>Target Setpoint</description>
                <state pattern="%.1f %unit%"/>
        </channel-type>
+       <channel-type id="upperSetpoint">
+               <item-type>Number:Temperature</item-type>
+               <label>Upper Setpoint</label>
+               <description>Upper Setpoint</description>
+               <category>Temperature</category>
+               <tags>
+                       <tag>Setpoint</tag>
+                       <tag>Temperature</tag>
+               </tags>
+               <state pattern="%.1f %unit%"/>
+       </channel-type>
+       <channel-type id="lowerSetpoint">
+               <item-type>Number:Temperature</item-type>
+               <label>Lower Setpoint</label>
+               <description>Lower Setpoint</description>
+               <category>Temperature</category>
+               <tags>
+                       <tag>Setpoint</tag>
+                       <tag>Temperature</tag>
+               </tags>
+               <state pattern="%.1f %unit%"/>
+       </channel-type>
+       <channel-type id="thermostatMode">
+               <item-type>String</item-type>
+               <label>Thermostat Mode</label>
+               <description>Thermostat Mode</description>
+               <state pattern="%s"/>
+       </channel-type>
 </thing:thing-descriptions>