/**
* Copyright (c) 2010-2016 by the respective copyright holders.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.openhab.binding.ecobee.messages;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.codehaus.jackson.annotate.JsonCreator;
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
import org.codehaus.jackson.annotate.JsonProperty;
/**
* This response contains a list of thermostat configuration and state revisions. This request is a light-weight polling
* method which will only return the revision numbers for the significant portions of the thermostat data. It is the
* responsibility of the caller to store these revisions for future determination whether changes occurred at the next
* poll interval.
*
* <p>
* The intent is to permit the caller to determine whether a thermostat has changed since the last poll. Retrieval of a
* whole thermostat including runtime data is expensive and impractical for large amounts of thermostat such as a
* management set hierarchy, especially if nothing has changed. By storing the retrieved revisions, the caller may
* determine whether to get a thermostat and which sections of the thermostat should be retrieved.
*
* @see <a href="https://www.ecobee.com/home/developer/api/documentation/v1/operations/get-thermostat-summary.shtml">GET
* Thermostat Summary</a>
* @author John Cocula
* @since 1.7.0
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class ThermostatSummaryResponse extends ApiResponse {
private List<Revision> revisionList;
private List<Status> statusList;
/**
* Objects of this class, when compared to previously returned instances, allow you to determine if changes have
* occurred in the thermostat's program, HVAC mode, settings, configuration, alerts, telemetry, or running state of
* connected equipment, and if the thermostat is currently connected to ecobee's servers.
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public static class Revision extends AbstractMessagePart {
private String thermostatIdentifier;
private String thermostatName;
private boolean connected;
private String thermostatRevision;
private String alertsRevision;
private String runtimeRevision;
private String internalRevision;
/**
* Construct a Revision object.
*
* @param csv
* a colon separated list of values (in order):
*
* <table>
* <tr>
* <td>Value</td>
* <td>Type</td>
* <td>Description</td>
* </tr>
* <td>
* <td>Thermostat Identifier</td>
* <td>String</td>
* <td>The thermostat identifier.</td>
* </tr>
* <tr>
* <td>Thermostat Name</td>
* <td>String</td>
* <td>The thermostat name, otherwise an empty field if one is not set.</td>
* </tr>
* <tr>
* <td>Connected</td>
* <td>Boolean</td>
* <td>Whether the thermostat is currently connected to the ecobee servers.</td>
* </tr>
* <tr>
* <td>Thermostat Revision</td>
* <td>String</td>
* <td>Current thermostat revision. This revision is incremented whenever the thermostat program,
* hvac mode, settings or configuration change.</td>
* </tr>
* <tr>
* <td>Alerts Revision</td>
* <td>String</td>
* <td>Current revision of the thermostat alerts. This revision is incremented whenever a new alert
* is issued or an alert is modified (acknowledged or deferred).</td>
* </tr>
* <tr>
* <td>Runtime Revision</td>
* <td>String</td>
* <td>The current revision of the thermostat runtime settings. This revision is incremented whenever
* the thermostat transmits a new status message, or updates the equipment state. The shortest
* interval this revision may change is 3 minutes.</td>
* </tr>
* <tr>
* <td>Interval Revision</td>
* <td>String</td>
* <td>The current revision of the thermostat interval runtime settings. This revision is incremented
* whenever the thermostat transmits a new status message. The thermostat does this on a 15 minute
* interval.</td>
* </tr>
* </table>
*
* <p>
* It is the responsibility of the caller to ensure that the sizes of the revisionList and statusList
* are parsed in a flexible manner, as additional revisions and statuses will be added with new
* features and functionality.
*/
@JsonCreator
Revision(final String csv) {
String[] fields = csv.split(":");
assert fields.length >= 7 : "unable to parse revision";
thermostatIdentifier = fields[0];
thermostatName = fields[1];
connected = fields[2].equals("true");
thermostatRevision = fields[3];
alertsRevision = fields[4];
runtimeRevision = fields[5];
internalRevision = fields[6];
}
/**
* @return the thermostat identifier
*/
public String getThermostatIdentifier() {
return this.thermostatIdentifier;
}
/**
* @return the thermostat name, otherwise an empty string if one is not set.
*/
public String getThermostatName() {
return this.thermostatName;
}
/**
* Return <code>true</code> if the thermostat is currently connected to the ecobee servers.
*
* @return whether the thermostat is currently connected to the ecobee servers.
*/
public boolean isConnected() {
return this.connected;
}
/*
* Revisions are UTC date/time stamps in the format: YYMMDDHHMMSS. However, due to possible time drift between
* the API consumer, the server and thermostat, it is recommended that they are treated as a string, rather than
* as a date/time stamp. The recommended method to test for revision changes is to simply do a string comparison
* on the previous and current revision. If the strings match, nothing changed. Otherwise request the thermostat
* including the relevant information which changed.
*/
/**
* Return <code>true</code> if the thermostat program, HVAC mode, settings or configuration has changed since
* the some previous revision.
*
* @param previous
* the previous status for this thermostat. May be <code>null</code>, in which case this method
* returns <code>true</code>.
* @return <code>true</code> if the thermostat program, HVAC mode, settings or configuration has changed since
* the given revision.
* @throws IllegalArgumentException
* if you are attempting to compare different thermostats.
*/
public boolean hasThermostatChanged(final Revision previous) {
if (previous == null) {
return true;
}
if (!this.thermostatIdentifier.equals(previous.thermostatIdentifier)) {
throw new IllegalArgumentException("comparing different thermostats.");
} else {
return !this.thermostatRevision.equals(previous.thermostatRevision);
}
}
/**
* Return <code>true</code> if a new alert has been issued or an alert has been modified (acknowledged or
* deferred) since some previous Revision.
*
* @param previous
* the previous revision for this thermostat. May be <code>null</code>, in which case this method
* returns <code>true</code>.
* @return <code>true</code> if a new alert has been issued or an alert has been modified (acknowledged or
* deferred) since the given revision.
* @throws IllegalArgumentException
* if you are attempting to compare different thermostats.
*/
public boolean hasAlertsChanged(final Revision previous) {
if (previous == null) {
return true;
}
if (!this.thermostatIdentifier.equals(previous.thermostatIdentifier)) {
throw new IllegalArgumentException("comparing different thermostats.");
} else {
return !this.alertsRevision.equals(previous.alertsRevision);
}
}
/**
* Return <code>true</code> if the thermostat has transmitted a new status message, or has updated the equipment
* state since some previous revision. The shortest interval this revision may change is 3 minutes.
*
* <p>
* {@link #hasTransmittedNewStatus(ThermostatSummaryResponse.Revision)} differs from this method in that this
* method returns <code>true</code> when any thermostat runtime information has changed, whereas
* {@link #hasTransmittedNewStatus(ThermostatSummaryResponse.Revision)} only returns <code>true</code> when the
* thermostat has transmitted its 15 minute interval status message to the server. This method returns
* <code>true</code> when the thermostat has sent the interval status message every 15 minutes as well as
* whenever the equipment state changes on the thermostat, and it has transmitted that information. The
* equipment message can come at a frequency of 3 minutes. When expecting only updates to the thermostat
* telemetry data, use {@link #hasTransmittedNewStatus(ThermostatSummaryResponse.Revision)}.
*
* @param previous
* the previous revision for this thermostat. May be <code>null</code>, in which case this method
* returns <code>true</code>.
* @return <code>true</code> if the thermostat has transmitted a new status message, or has updated the
* equipment state since some previous Revision. The shortest interval this revision may change is 3
* minutes.
* @throws IllegalArgumentException
* if you are attempting to compare different thermostats.
* @see #hasTransmittedNewStatus(ThermostatSummaryResponse.Revision)
*/
public boolean hasRuntimeChanged(final Revision previous) {
if (previous == null) {
return true;
}
if (!this.thermostatIdentifier.equals(previous.thermostatIdentifier)) {
throw new IllegalArgumentException("comparing different thermostats.");
} else {
return !this.runtimeRevision.equals(previous.runtimeRevision);
}
}
/**
* Return <code>true</code> if the thermostat has transmitted a new status message since some previous Revision.
* The thermostat does this on a 15 minute interval.
*
* <p>
* This method differs from {@link #hasRuntimeChanged(ThermostatSummaryResponse.Revision)} in that
* {@link #hasRuntimeChanged(ThermostatSummaryResponse.Revision)} returns <code>true</code> when any thermostat
* runtime information has changed, whereas This method is only <code>true</code> when the thermostat has
* transmitted its 15-minute interval status message to the server.
*
* {@link #hasRuntimeChanged(ThermostatSummaryResponse.Revision)} returns <code>true</code> when the thermostat
* has transmitted its interval status message every 15 mins as well as whenever the equipment state changes on
* and it has transmitted that information. The equipment message can come at a frequency of 3 minutes. When
* expecting only updates to the thermostat telemetry data, use this method to detect if changes have occurred.
*
* @param previous
* the previous revision for this thermostat. May be <code>null</code>, in which case this method
* returns <code>true</code>.
* @return <code>true</code> if the thermostat has transmitted a new status message since some previous
* revision. The thermostat does this on a 15 minute interval.
* @throws IllegalArgumentException
* if you are attempting to compare different thermostats.
* @throws IllegalArgumentException
* if you are attempting to compare different thermostats.
* @see #hasRuntimeChanged(ThermostatSummaryResponse.Revision)
*/
public boolean hasTransmittedNewStatus(final Revision previous) {
if (previous == null) {
return true;
}
if (!this.thermostatIdentifier.equals(previous.thermostatIdentifier)) {
throw new IllegalArgumentException("comparing different thermostats.");
} else {
return !this.internalRevision.equals(previous.internalRevision);
}
}
@Override
public String toString() {
final ToStringBuilder builder = createToStringBuilder();
builder.appendSuper(super.toString());
builder.append("thermostatIdentifier", this.thermostatIdentifier);
builder.append("thermostatName", this.thermostatName);
builder.append("connected", this.connected ? "true" : "false");
builder.append("thermostatRevision", this.thermostatRevision);
builder.append("alertsRevision", this.alertsRevision);
builder.append("runtimeRevision", this.runtimeRevision);
builder.append("internalRevision", this.internalRevision);
return builder.toString();
}
}
/**
* A class version of the data returned in each status in the statusList.
*
* <p>
* The Thermostat Summary can also return the status of the equipment controlled by the Thermostat. The
* {@link #getRunningEquipment()} lists all equipment which is currently running. If a specific equipment type is
* not present in the set then it is not running, or "OFF". To retrieve this data the {@link Selection} object
* specified in the request should have the {@link Selection#setIncludeEquipmentStatus(Boolean)} set to
* <code>true</code>. The default is <code>false</code>.
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public static class Status extends AbstractMessagePart {
private String thermostatIdentifier;
private Set<String> runningEquipment;
/**
* Construct a Status object.
*
* @param csv
* contains a colon separated list of values (in order):
*
* <table>
* <tr>
* <td>Value</td>
* <td>Type</td>
* <td>Description</td>
* </tr>
* <tr>
* <td>Thermostat Identifier</td>
* <td>String</td>
* <td>The thermostat identifier.</td>
* </tr>
* <tr>
* <td>Equipment Status</td>
* <td>String</td>
* <td>If no equipment is currently running no data is returned. Possible values are: heatPump,
* heatPump2, heatPump3, compCool1, compCool2, auxHeat1, auxHeat2, auxHeat3, fan, humidifier,
* dehumidifier, ventilator, economizer, compHotWater, auxHotWater.</td>
* </tr>
* </table>
*
*/
@JsonCreator
Status(final String csv) {
String[] fields = csv.split(":");
assert fields.length >= 1 : "unable to parse status";
thermostatIdentifier = fields[0];
runningEquipment = new HashSet<String>();
if (fields.length >= 2) {
for (String equip : fields[1].split(",")) {
if (equip.length() == 0) {
continue;
}
runningEquipment.add(equip);
}
}
}
/**
* Return <code>true</code> if no equipment is currently running.
*
* @return <code>true</code> if no equipment is currently running.
*/
public boolean isIdle() {
return runningEquipment.isEmpty();
}
/**
* Return <code>true</code> if any heating equipment is currently running.
*
* @return <code>true</code> if any heating equipment is running.
*/
public boolean isHeating() {
return runningEquipment.contains("heatPump") || runningEquipment.contains("heatPump2")
|| runningEquipment.contains("heatPump3") || runningEquipment.contains("auxHeat1")
|| runningEquipment.contains("auxHeat2") || runningEquipment.contains("auxHeat3");
}
/**
* Return <code>true</code> if any cooling equipment is currently running.
*
* @return <code>true</code> if any cooling equipment is currently running.
*/
public boolean isCooling() {
return runningEquipment.contains("compCool1") || runningEquipment.contains("compCool2");
}
/**
* Return <code>true</code> if the named equipment is running.
*
* @param equipment
* the name of the equipment to check. Possible names are: heatPump, heatPump2, heatPump3, compCool1,
* compCool2, auxHeat1, auxHeat2, auxHeat3, fan, humidifier, dehumidifier, ventilator, economizer,
* compHotWater, auxHotWater.
* @return <code>true</code> if the named equipment is running
*/
public boolean isRunning(final String equipment) {
return runningEquipment.contains(equipment);
}
/**
* @return the thermostat identifier
*/
public String getThermostatIdentifier() {
return this.thermostatIdentifier;
}
/**
* @return A set of the running equipment. Possible contents are: heatPump, heatPump2, heatPump3, compCool1,
* compCool2, auxHeat1, auxHeat2, auxHeat3, fan, humidifier, dehumidifier, ventilator, economizer,
* compHotWater, auxHotWater.
*/
public Set<String> getRunningEquipment() {
return this.runningEquipment;
}
@Override
public String toString() {
final ToStringBuilder builder = createToStringBuilder();
builder.appendSuper(super.toString());
builder.append("thermostatIdentifier", this.thermostatIdentifier);
builder.append("runningEquipment", this.runningEquipment);
return builder.toString();
}
}
/**
* @return the list of CSV revision values
*/
@JsonProperty("revisionList")
public List<Revision> getRevisionList() {
return this.revisionList;
}
/**
* @return the list of CSV status values
*
* The statusList is only returned when the request {@link Selection} object has the
* {@link Selection#setIncludeEquipmentStatus(Boolean)} called with <code>true</code>. The default is
* <code>false</code>.
*/
@JsonProperty("statusList")
public List<Status> getStatusList() {
return this.statusList;
}
@Override
public String toString() {
final ToStringBuilder builder = createToStringBuilder();
builder.appendSuper(super.toString());
builder.append("revisionList", this.revisionList);
builder.append("statusList", this.statusList);
return builder.toString();
}
}