From 503abab1819e5db3da3678f780e8086b7734b72b Mon Sep 17 00:00:00 2001 From: Matthew Davies <84205523+raveydavies@users.noreply.github.com> Date: Sat, 5 Jun 2021 20:10:45 +0200 Subject: [PATCH] [venstarthermostat] Venstar thermostat away mode enhancement (#10736) * [VENSTAR THERMOSTAT BINDING] ADD AWAY MODE Signed-off-by: Matthew Davies matthew.davies@skynet.be Signed-off-by: raveydavies <84205523+raveydavies@users.noreply.github.com> * [VENSTAR THERMOSTAT] FIXED COMMAND AWAY MODE Signed-off-by: Matthew Davies matthew.davies@skynet.be Signed-off-by: raveydavies <84205523+raveydavies@users.noreply.github.com> * VENSTAR THERMOSTAT AWAY MODE AFTER INITIAL COMMIT FEEDBACK This code includes the Away mode of the Venstar thermostat. It is updated following initial feedback and suggestions on my first version from @digitaldan. Signed-off-by: Matthew Davies Signed-off-by: raveydavies <84205523+raveydavies@users.noreply.github.com> * VENSTAR THERMOSTAT BINDING - INCLUDE AWAY MODE Removed the updateThermostat function, now have updateSettings and updateControls corresponding to local API URLs. Signed-off-by: Matthew Davies Signed-off-by: raveydavies <84205523+raveydavies@users.noreply.github.com> * VENSTAR THERMOSTAT AWAY MODE - Modification updated as per feedback 1 June 2021 Signed-off-by: Matthew Davies Signed-off-by: raveydavies <84205523+raveydavies@users.noreply.github.com> * VENSTAR THERMOSTAT AWAY MODE INCLUSION - UPDATED README Signed-off-by: Matthew Davies Signed-off-by: raveydavies <84205523+raveydavies@users.noreply.github.com> --- .../README.md | 4 + .../pom.xml | 1 + .../VenstarThermostatBindingConstants.java | 3 + .../handler/VenstarThermostatHandler.java | 90 +++++++++++++++---- .../internal/model/VenstarAwayMode.java | 56 ++++++++++++ .../model/VenstarAwayModeSerializer.java | 35 ++++++++ .../internal/model/VenstarInfoData.java | 14 ++- .../resources/OH-INF/thing/thing-types.xml | 21 +++++ 8 files changed, 207 insertions(+), 17 deletions(-) create mode 100644 bundles/org.openhab.binding.venstarthermostat/src/main/java/org/openhab/binding/venstarthermostat/internal/model/VenstarAwayMode.java create mode 100644 bundles/org.openhab.binding.venstarthermostat/src/main/java/org/openhab/binding/venstarthermostat/internal/model/VenstarAwayModeSerializer.java diff --git a/bundles/org.openhab.binding.venstarthermostat/README.md b/bundles/org.openhab.binding.venstarthermostat/README.md index cd247eb721..1e82441aa1 100644 --- a/bundles/org.openhab.binding.venstarthermostat/README.md +++ b/bundles/org.openhab.binding.venstarthermostat/README.md @@ -43,6 +43,8 @@ After adding the Inbox item, enter the user name and password from the physical | Channel | Type | Description | Notes | |--------------------|--------------------|------------------------------|--------------------------------------------------------| +| awayMode | String | Home or Away Mode | | +| awayModeRaw | Number | Away Mode Raw (Read Only) | 0 (Home) 1 (Away) | | systemMode | String | System Mode | | | systemModeRaw | Number | System Mode Raw (Read Only) | 0 (Off) 1 (Heat) 2 (Cool) 3 (Auto) | | systemState | String | System State (Read Only) | | @@ -72,6 +74,7 @@ Number:Temperature Guest_HVAC_CoolSetpoint "Cool Setpoint [%d °F]" {channel="v Number Guest_HVAC_Mode "Mode [%s]" {channel="venstarthermostat:colorTouchThermostat:001122334455:systemMode"} Number Guest_HVAC_Humidity "Humidity [%d %%]" {channel="venstarthermostat:colorTouchThermostat:001122334455:humidity"} Number Guest_HVAC_State "State [%s]" {channel="venstarthermostat:colorTouchThermostat:001122334455:systemState"} +Number Guest_Away_Mode "Mode [%s]" {channel="venstarthermostat:colorTouchThermostat:001122334455:awayMode"} ``` ### thermostat.sitemap @@ -83,6 +86,7 @@ sitemap demo label="Venstar Color Thermostat Demo" Setpoint item=Guest_HVAC_HeatSetpoint minValue=50 maxValue=99 Setpoint item=Guest_HVAC_CoolSetpoint minValue=50 maxValue=99 Switch item=Guest_HVAC_Mode mappings=[off=Off,heat=Heat,cool=Cool,auto=Auto] + Switch item=Guest_Away_Mode mappings=[home=Home,away=Away] Text item=Guest_HVAC_State } } diff --git a/bundles/org.openhab.binding.venstarthermostat/pom.xml b/bundles/org.openhab.binding.venstarthermostat/pom.xml index 0678650932..36e56da305 100644 --- a/bundles/org.openhab.binding.venstarthermostat/pom.xml +++ b/bundles/org.openhab.binding.venstarthermostat/pom.xml @@ -3,6 +3,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 + org.openhab.addons.bundles org.openhab.addons.reactor.bundles diff --git a/bundles/org.openhab.binding.venstarthermostat/src/main/java/org/openhab/binding/venstarthermostat/internal/VenstarThermostatBindingConstants.java b/bundles/org.openhab.binding.venstarthermostat/src/main/java/org/openhab/binding/venstarthermostat/internal/VenstarThermostatBindingConstants.java index 34bbd4a625..0c15359419 100644 --- a/bundles/org.openhab.binding.venstarthermostat/src/main/java/org/openhab/binding/venstarthermostat/internal/VenstarThermostatBindingConstants.java +++ b/bundles/org.openhab.binding.venstarthermostat/src/main/java/org/openhab/binding/venstarthermostat/internal/VenstarThermostatBindingConstants.java @@ -23,6 +23,7 @@ import org.openhab.core.thing.ThingTypeUID; * used across the whole binding. * * @author William Welliver - Initial contribution + * @author Matthew Davies - added awayMode and awayModeRaw to include thermostat away mode in binding */ @NonNullByDefault public class VenstarThermostatBindingConstants { @@ -44,6 +45,8 @@ public class VenstarThermostatBindingConstants { public final static String CHANNEL_SYSTEM_MODE = "systemMode"; public final static String CHANNEL_SYSTEM_STATE_RAW = "systemStateRaw"; public final static String CHANNEL_SYSTEM_MODE_RAW = "systemModeRaw"; + public final static String CHANNEL_AWAY_MODE = "awayMode"; + public final static String CHANNEL_AWAY_MODE_RAW = "awayModeRaw"; public final static String CONFIG_USERNAME = "username"; public final static String CONFIG_PASSWORD = "password"; diff --git a/bundles/org.openhab.binding.venstarthermostat/src/main/java/org/openhab/binding/venstarthermostat/internal/handler/VenstarThermostatHandler.java b/bundles/org.openhab.binding.venstarthermostat/src/main/java/org/openhab/binding/venstarthermostat/internal/handler/VenstarThermostatHandler.java index 153ae2ba42..3e21f20f64 100644 --- a/bundles/org.openhab.binding.venstarthermostat/src/main/java/org/openhab/binding/venstarthermostat/internal/handler/VenstarThermostatHandler.java +++ b/bundles/org.openhab.binding.venstarthermostat/src/main/java/org/openhab/binding/venstarthermostat/internal/handler/VenstarThermostatHandler.java @@ -47,6 +47,8 @@ import org.eclipse.jetty.client.util.DigestAuthentication; import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.openhab.binding.venstarthermostat.internal.VenstarThermostatConfiguration; +import org.openhab.binding.venstarthermostat.internal.model.VenstarAwayMode; +import org.openhab.binding.venstarthermostat.internal.model.VenstarAwayModeSerializer; import org.openhab.binding.venstarthermostat.internal.model.VenstarInfoData; import org.openhab.binding.venstarthermostat.internal.model.VenstarResponse; import org.openhab.binding.venstarthermostat.internal.model.VenstarSensor; @@ -84,6 +86,7 @@ import com.google.gson.JsonSyntaxException; * * @author William Welliver - Initial contribution * @author Dan Cunningham - Migration to Jetty, annotations and various improvements + * @author Matthew Davies - added code to include away mode in binding */ @NonNullByDefault public class VenstarThermostatHandler extends ConfigStatusThingHandler { @@ -108,7 +111,8 @@ public class VenstarThermostatHandler extends ConfigStatusThingHandler { super(thing); httpClient = new HttpClient(new SslContextFactory.Client(true)); gson = new GsonBuilder().registerTypeAdapter(VenstarSystemState.class, new VenstarSystemStateSerializer()) - .registerTypeAdapter(VenstarSystemMode.class, new VenstarSystemModeSerializer()).create(); + .registerTypeAdapter(VenstarSystemMode.class, new VenstarSystemModeSerializer()) + .registerTypeAdapter(VenstarAwayMode.class, new VenstarAwayModeSerializer()).create(); log.trace("VenstarThermostatHandler for thing {}", getThing().getUID()); } @@ -173,7 +177,17 @@ public class VenstarThermostatHandler extends ConfigStatusThingHandler { } log.debug("Setting system mode to {}", value); setSystemMode(value); - updateIfChanged(CHANNEL_SYSTEM_MODE_RAW, new StringType("" + value)); + updateIfChanged(CHANNEL_SYSTEM_MODE_RAW, new StringType(value.toString())); + } else if (channelUID.getId().equals(CHANNEL_AWAY_MODE)) { + VenstarAwayMode value; + if (command instanceof StringType) { + value = VenstarAwayMode.valueOf(((StringType) command).toString().toUpperCase()); + } else { + value = VenstarAwayMode.fromInt(((DecimalType) command).intValue()); + } + log.debug("Setting away mode to {}", value); + setAwayMode(value); + updateIfChanged(CHANNEL_AWAY_MODE_RAW, new StringType(value.toString())); } startUpdatesTask(UPDATE_AFTER_COMMAND_SECONDS); } @@ -287,19 +301,23 @@ public class VenstarThermostatHandler extends ConfigStatusThingHandler { private void setCoolingSetpoint(int cool) { int heat = getHeatingSetpoint().intValue(); VenstarSystemMode mode = getSystemMode(); - updateThermostat(heat, cool, mode); + updateControls(heat, cool, mode); } private void setSystemMode(VenstarSystemMode mode) { int cool = getCoolingSetpoint().intValue(); int heat = getHeatingSetpoint().intValue(); - updateThermostat(heat, cool, mode); + updateControls(heat, cool, mode); } private void setHeatingSetpoint(int heat) { int cool = getCoolingSetpoint().intValue(); VenstarSystemMode mode = getSystemMode(); - updateThermostat(heat, cool, mode); + updateControls(heat, cool, mode); + } + + private void setAwayMode(VenstarAwayMode away) { + updateSettings(away); } private QuantityType getCoolingSetpoint() { @@ -318,26 +336,63 @@ public class VenstarThermostatHandler extends ConfigStatusThingHandler { return infoData.getMode(); } - private void updateThermostat(int heat, int cool, VenstarSystemMode mode) { + private VenstarAwayMode getAwayMode() { + return infoData.getAway(); + } + + private void updateSettings(VenstarAwayMode away) { + // this function corresponds to the thermostat local API POST /settings instruction + // the function can be expanded with other parameters which are changed via POST /settings + Map params = new HashMap<>(); + params.put("away", String.valueOf(away.mode())); + VenstarResponse res = updateThermostat("/settings", params); + if (res != null) { + log.debug("Updated thermostat"); + // update our local copy until the next refresh occurs + infoData.setAwayMode(away); + // add other parameters here in the same way + } + } + + private void updateControls(int heat, int cool, VenstarSystemMode mode) { + // this function corresponds to the thermostat local API POST /control instruction + // the function can be expanded with other parameters which are changed via POST /control Map params = new HashMap<>(); - log.debug("Updating thermostat {} heat:{} cool {} mode: {}", getThing().getLabel(), heat, cool, mode); if (heat > 0) { params.put("heattemp", String.valueOf(heat)); } if (cool > 0) { params.put("cooltemp", String.valueOf(cool)); } - params.put("mode", "" + mode.mode()); + params.put("mode", String.valueOf(mode.mode())); + VenstarResponse res = updateThermostat("/control", params); + if (res != null) { + log.debug("Updated thermostat"); + // update our local copy until the next refresh occurs + infoData.setCooltemp(cool); + infoData.setHeattemp(heat); + infoData.setMode(mode); + // add other parameters here in the same way + } + } + + /** + * Function to send data to the thermostat and update the Thing state if there is an error + * + * @param path + * @param params + * @return VenstarResponse object or null if there was an error + */ + private @Nullable VenstarResponse updateThermostat(String path, Map params) { try { - String result = postData("/control", params); + String result = postData(path, params); VenstarResponse res = gson.fromJson(result, VenstarResponse.class); - if (res.isSuccess()) { - log.debug("Updated thermostat"); - // update our local copy until the next refresh occurs - infoData = new VenstarInfoData(cool, heat, infoData.getState(), mode); + if (res != null && res.isSuccess()) { + return res; } else { - log.debug("Failed to update thermostat: {}", res.getReason()); - goOffline(ThingStatusDetail.COMMUNICATION_ERROR, "Thermostat update failed: " + res.getReason()); + String reason = res == null ? "invalid response" : res.getReason(); + log.debug("Failed to update thermostat: {}", reason); + goOffline(ThingStatusDetail.COMMUNICATION_ERROR, reason); } } catch (VenstarCommunicationException | JsonSyntaxException e) { log.debug("Unable to fetch info data", e); @@ -346,6 +401,7 @@ public class VenstarThermostatHandler extends ConfigStatusThingHandler { } catch (VenstarAuthenticationException e) { goOffline(ThingStatusDetail.CONFIGURATION_ERROR, "Authorization Failed"); } + return null; } private void updateData() { @@ -373,6 +429,8 @@ public class VenstarThermostatHandler extends ConfigStatusThingHandler { updateIfChanged(CHANNEL_SYSTEM_MODE, new StringType(getSystemMode().modeName())); updateIfChanged(CHANNEL_SYSTEM_STATE_RAW, new DecimalType(getSystemState().state())); updateIfChanged(CHANNEL_SYSTEM_MODE_RAW, new DecimalType(getSystemMode().mode())); + updateIfChanged(CHANNEL_AWAY_MODE, new StringType(getAwayMode().modeName())); + updateIfChanged(CHANNEL_AWAY_MODE_RAW, new DecimalType(getAwayMode().mode())); goOnline(); } catch (VenstarCommunicationException | JsonSyntaxException e) { @@ -438,7 +496,7 @@ public class VenstarThermostatHandler extends ConfigStatusThingHandler { if (response.getStatus() != 200) { throw new VenstarCommunicationException( - "Error communitcating with thermostat. Error Code: " + response.getStatus()); + "Error communicating with thermostat. Error Code: " + response.getStatus()); } String content = response.getContentAsString(); log.trace("sendRequest: response {}", content); diff --git a/bundles/org.openhab.binding.venstarthermostat/src/main/java/org/openhab/binding/venstarthermostat/internal/model/VenstarAwayMode.java b/bundles/org.openhab.binding.venstarthermostat/src/main/java/org/openhab/binding/venstarthermostat/internal/model/VenstarAwayMode.java new file mode 100644 index 0000000000..8a2c496da8 --- /dev/null +++ b/bundles/org.openhab.binding.venstarthermostat/src/main/java/org/openhab/binding/venstarthermostat/internal/model/VenstarAwayMode.java @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2010-2021 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.venstarthermostat.internal.model; + +/** + * The {@link VenstarSystemMode} represents the value of the system mode returned + * from the REST API. + * + * @author Matthew Davies - created new class to add away mode to binding + */ +public enum VenstarAwayMode { + HOME(0, "home", "Home"), + AWAY(1, "away", "Away"); + + private int mode; + private String name; + private String friendlyName; + + VenstarAwayMode(int mode, String name, String friendlyName) { + this.mode = mode; + this.name = name; + this.friendlyName = friendlyName; + } + + public int mode() { + return mode; + } + + public String modeName() { + return name; + } + + public String friendlyName() { + return friendlyName; + } + + public static VenstarAwayMode fromInt(int mode) { + for (VenstarAwayMode am : values()) { + if (am.mode == mode) { + return am; + } + } + + throw (new IllegalArgumentException("Invalid away mode " + mode)); + } +} diff --git a/bundles/org.openhab.binding.venstarthermostat/src/main/java/org/openhab/binding/venstarthermostat/internal/model/VenstarAwayModeSerializer.java b/bundles/org.openhab.binding.venstarthermostat/src/main/java/org/openhab/binding/venstarthermostat/internal/model/VenstarAwayModeSerializer.java new file mode 100644 index 0000000000..2130d4b779 --- /dev/null +++ b/bundles/org.openhab.binding.venstarthermostat/src/main/java/org/openhab/binding/venstarthermostat/internal/model/VenstarAwayModeSerializer.java @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2010-2021 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.venstarthermostat.internal.model; + +import java.lang.reflect.Type; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; + +/** + * The {@link VenstarSystemModeSerializer} parses system mode values + * from the REST API JSON. + * + * @author Matthew Davies - created new class to include away mode in binding + */ +public class VenstarAwayModeSerializer implements JsonDeserializer { + @Override + public VenstarAwayMode deserialize(JsonElement element, Type arg1, JsonDeserializationContext arg2) + throws JsonParseException { + int key = element.getAsInt(); + return VenstarAwayMode.fromInt(key); + } +} diff --git a/bundles/org.openhab.binding.venstarthermostat/src/main/java/org/openhab/binding/venstarthermostat/internal/model/VenstarInfoData.java b/bundles/org.openhab.binding.venstarthermostat/src/main/java/org/openhab/binding/venstarthermostat/internal/model/VenstarInfoData.java index 6c02b7ee8b..6d9aeaec8f 100644 --- a/bundles/org.openhab.binding.venstarthermostat/src/main/java/org/openhab/binding/venstarthermostat/internal/model/VenstarInfoData.java +++ b/bundles/org.openhab.binding.venstarthermostat/src/main/java/org/openhab/binding/venstarthermostat/internal/model/VenstarInfoData.java @@ -16,6 +16,7 @@ package org.openhab.binding.venstarthermostat.internal.model; * The {@link VenstarInfoData} represents a thermostat state from the REST API. * * @author William Welliver - Initial contribution + * @author Matthew Davies - added VenstarAwayMode to include away mode in binding */ public class VenstarInfoData { double cooltemp; @@ -23,18 +24,21 @@ public class VenstarInfoData { VenstarSystemState state; VenstarSystemMode mode; + VenstarAwayMode away; int tempunits; public VenstarInfoData() { super(); } - public VenstarInfoData(double cooltemp, double heattemp, VenstarSystemState state, VenstarSystemMode mode) { + public VenstarInfoData(double cooltemp, double heattemp, VenstarSystemState state, VenstarSystemMode mode, + VenstarAwayMode away) { super(); this.cooltemp = cooltemp; this.heattemp = heattemp; this.state = state; this.mode = mode; + this.away = away; } public double getCooltemp() { @@ -76,4 +80,12 @@ public class VenstarInfoData { public void setTempunits(int tempunits) { this.tempunits = tempunits; } + + public VenstarAwayMode getAway() { + return away; + } + + public void setAwayMode(VenstarAwayMode away) { + this.away = away; + } } diff --git a/bundles/org.openhab.binding.venstarthermostat/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.venstarthermostat/src/main/resources/OH-INF/thing/thing-types.xml index e3b3095b82..a9c6d5061c 100644 --- a/bundles/org.openhab.binding.venstarthermostat/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.venstarthermostat/src/main/resources/OH-INF/thing/thing-types.xml @@ -19,6 +19,8 @@ + + @@ -68,6 +70,25 @@ + + String + + Current Away Mode + + + + + + + + + + Number + + Current Away Mode, as an integer number + + + String -- 2.47.3