- 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:
- 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:
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
}
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) {
postDataBuilder.append(name);
postDataBuilder.append('=');
String value = "";
- if (name.equals("failedSignInCount")) {
+ if ("failedSignInCount".equals(name)) {
value = "ape:AA==";
} else {
String[] strings = map.get(name);
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) {
this.connectionToInitialize = connection;
}
- if (!uri.equals("/")) {
+ if (!"/".equals(uri)) {
String newUri = req.getServletPath() + "/";
resp.sendRedirect(newUri);
return;
}
logger.debug("doGet {}", uri);
- if (!uri.equals("/")) {
+ if (!"/".equals(uri)) {
String newUri = req.getServletPath() + "/";
resp.sendRedirect(newUri);
return;
*/
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;
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;
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;
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;
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()) {
}
}
}
- if (key.equalsIgnoreCase("Location")) {
+ if ("Location".equalsIgnoreCase(key)) {
// get redirect location
location = header.getValue().get(0);
if (!location.isEmpty()) {
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");
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();
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);
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;
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;
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);
@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;
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;
}
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;
}
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")) {
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";
}
}
if (shd.getCapabilities().stream().map(capability -> capability.interfaceName)
.anyMatch(SUPPORTED_INTERFACES::contains)) {
result.add(shd);
-
}
} else {
SmartHomeGroup shg = (SmartHomeGroup) baseDevice;
return applianceId;
}
+ @Override
+ public @Nullable String findEntityId() {
+ return entityId;
+ }
+
@Override
public boolean isGroup() {
return false;
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;
@Nullable
String findId();
+ @Nullable
+ String findEntityId();
+
boolean isGroup();
}
*/
@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";
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";
}
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;
*/
@NonNullByDefault
public abstract class HandlerBase {
+ // Logger
+ private final Logger logger = LoggerFactory.getLogger(HandlerBase.class);
+
protected SmartHomeDeviceHandler smartHomeDeviceHandler;
protected Map<String, ChannelInfo> channels = new HashMap<>();
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) {
--- /dev/null
+/**
+ * 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;
+ }
+}
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
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
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;
*/
@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);
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)) {
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
}
}
}
+ 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;
}
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
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
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
<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>