Skip to content

Commit

Permalink
[venstarthermostat] Venstar thermostat away mode enhancement (openhab…
Browse files Browse the repository at this point in the history
…#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 <matthew.davies@skynet.be>
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
<matthew.davies@skynet.be>
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
<matthew.davies@skynet.be>
Signed-off-by: raveydavies <84205523+raveydavies@users.noreply.github.com>

* VENSTAR THERMOSTAT AWAY MODE INCLUSION - UPDATED README

Signed-off-by: Matthew Davies <matthew.davies@skynet.be>
Signed-off-by: raveydavies <84205523+raveydavies@users.noreply.github.com>
Signed-off-by: Luca Calcaterra <calcaterra.luca@gmail.com>
  • Loading branch information
raveydavies authored and lucacalcaterra committed Jul 26, 2021
1 parent cf2b110 commit b7ae994
Show file tree
Hide file tree
Showing 8 changed files with 207 additions and 17 deletions.
4 changes: 4 additions & 0 deletions bundles/org.openhab.binding.venstarthermostat/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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) | |
Expand Down Expand Up @@ -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
Expand All @@ -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
}
}
Expand Down
1 change: 1 addition & 0 deletions bundles/org.openhab.binding.venstarthermostat/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>org.openhab.addons.bundles</groupId>
<artifactId>org.openhab.addons.reactor.bundles</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
* 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 {
Expand All @@ -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";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@
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;
Expand Down Expand Up @@ -84,6 +86,7 @@
*
* @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 {
Expand All @@ -108,7 +111,8 @@ public VenstarThermostatHandler(Thing thing) {
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());
}
Expand Down Expand Up @@ -173,7 +177,17 @@ public void handleCommand(ChannelUID channelUID, Command command) {
}
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);
}
Expand Down Expand Up @@ -287,19 +301,23 @@ private State getOutdoorTemperature() {
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<Temperature> getCoolingSetpoint() {
Expand All @@ -318,26 +336,63 @@ private VenstarSystemMode getSystemMode() {
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<String, String> 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<String, String> 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<String, String> 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);
Expand All @@ -346,6 +401,7 @@ private void updateThermostat(int heat, int cool, VenstarSystemMode mode) {
} catch (VenstarAuthenticationException e) {
goOffline(ThingStatusDetail.CONFIGURATION_ERROR, "Authorization Failed");
}
return null;
}

private void updateData() {
Expand Down Expand Up @@ -373,6 +429,8 @@ private void updateData() {
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) {
Expand Down Expand Up @@ -438,7 +496,7 @@ private String sendRequest(Request request) throws VenstarAuthenticationExceptio

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);
Expand Down
Original file line number Diff line number Diff line change
@@ -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));
}
}
Original file line number Diff line number Diff line change
@@ -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<VenstarAwayMode> {
@Override
public VenstarAwayMode deserialize(JsonElement element, Type arg1, JsonDeserializationContext arg2)
throws JsonParseException {
int key = element.getAsInt();
return VenstarAwayMode.fromInt(key);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,29 @@
* 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;
double heattemp;

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() {
Expand Down Expand Up @@ -76,4 +80,12 @@ public int getTempunits() {
public void setTempunits(int tempunits) {
this.tempunits = tempunits;
}

public VenstarAwayMode getAway() {
return away;
}

public void setAwayMode(VenstarAwayMode away) {
this.away = away;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
<channel id="coolingSetpoint" typeId="coolingSetpoint"/>
<channel id="systemState" typeId="systemState"/>
<channel id="systemStateRaw" typeId="systemStateRaw"/>
<channel id="awayMode" typeId="awayMode"/>
<channel id="awayModeRaw" typeId="awayModeRaw"/>
</channels>

<properties>
Expand Down Expand Up @@ -68,6 +70,25 @@
<state readOnly="true"/>
</channel-type>

<channel-type id="awayMode">
<item-type>String</item-type>
<label>Away Mode</label>
<description>Current Away Mode</description>
<state readOnly="false">
<options>
<option value="home">Home</option>
<option value="away">Away</option>
</options>
</state>
</channel-type>

<channel-type id="awayModeRaw" advanced="true">
<item-type>Number</item-type>
<label>Away Mode (Raw)</label>
<description>Current Away Mode, as an integer number</description>
<state readOnly="true"/>
</channel-type>

<channel-type id="systemState">
<item-type>String</item-type>
<label>System State</label>
Expand Down

0 comments on commit b7ae994

Please sign in to comment.