Skip to content

Commit

Permalink
[amazonechocontrol] Add channels to thermostatController (openhab#13067)
Browse files Browse the repository at this point in the history
* enhance: add thermostat channels to amazonechocontrol

Signed-off-by: Daniel Campbell <djcampbell79@gmail.com>
  • Loading branch information
djcampbell authored and psmedley committed Feb 23, 2023
1 parent 9c94bcf commit 64ac0a7
Show file tree
Hide file tree
Showing 19 changed files with 438 additions and 66 deletions.
35 changes: 21 additions & 14 deletions bundles/org.openhab.binding.amazonechocontrol/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down Expand Up @@ -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:

Expand Down Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ void doVerb(String verb, @Nullable HttpServletRequest req, @Nullable 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) {
Expand Down Expand Up @@ -199,7 +199,7 @@ void doVerb(String verb, @Nullable HttpServletRequest req, @Nullable 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);
Expand Down Expand Up @@ -277,29 +277,29 @@ protected void doGet(@Nullable HttpServletRequest req, @Nullable HttpServletResp

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) {
Expand All @@ -318,7 +318,7 @@ protected void doGet(@Nullable HttpServletRequest req, @Nullable HttpServletResp
this.connectionToInitialize = connection;
}

if (!uri.equals("/")) {
if (!"/".equals(uri)) {
String newUri = req.getServletPath() + "/";
resp.sendRedirect(newUri);
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ protected void doGet(@Nullable HttpServletRequest req, @Nullable HttpServletResp
}
logger.debug("doGet {}", uri);

if (!uri.equals("/")) {
if (!"/".equals(uri)) {
String newUri = req.getServletPath() + "/";
resp.sendRedirect(newUri);
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -32,10 +34,12 @@
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;
Expand Down Expand Up @@ -109,6 +113,7 @@
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;
Expand All @@ -118,6 +123,7 @@
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;
Expand Down Expand Up @@ -644,7 +650,7 @@ public HttpsURLConnection makeRequest(String verb, String url, @Nullable String
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()) {
Expand All @@ -655,7 +661,7 @@ public HttpsURLConnection makeRequest(String verb, String url, @Nullable String
}
}
}
if (key.equalsIgnoreCase("Location")) {
if ("Location".equalsIgnoreCase(key)) {
// get redirect location
location = header.getValue().get(0);
if (!location.isEmpty()) {
Expand Down Expand Up @@ -1074,7 +1080,7 @@ public Map<String, JsonArray> getSmartHomeDeviceStatesJson(Set<SmartHomeBaseDevi
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");
Expand Down Expand Up @@ -1164,6 +1170,8 @@ public void smartHomeCommand(String entityId, String action) throws IOException,
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();
Expand All @@ -1173,20 +1181,95 @@ public void smartHomeCommand(String entityId, String action, @Nullable String pr
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);
Expand Down
Loading

0 comments on commit 64ac0a7

Please sign in to comment.