added migrated 2.x add-ons

Signed-off-by: Kai Kreuzer <kai@openhab.org>
This commit is contained in:
Kai Kreuzer
2020-09-21 01:58:32 +02:00
parent bbf1a7fd29
commit 6df6783b60
11662 changed files with 1302875 additions and 11 deletions

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/classes" path="src/main/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="target/classes"/>
</classpath>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>org.openhab.binding.ecobee</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
</projectDescription>

View File

@@ -0,0 +1,13 @@
This content is produced and maintained by the openHAB project.
* Project home: https://www.openhab.org
== Declared Project Licenses
This program and the accompanying materials are made available under the terms
of the Eclipse Public License 2.0 which is available at
https://www.eclipse.org/legal/epl-2.0/.
== Source Code
https://github.com/openhab/openhab-addons

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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>
<version>3.0.0-SNAPSHOT</version>
</parent>
<artifactId>org.openhab.binding.ecobee</artifactId>
<name>openHAB Add-ons :: Bundles :: Ecobee Binding</name>
</project>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<features name="org.openhab.binding.ecobee-${project.version}" xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
<repository>mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features</repository>
<feature name="openhab-binding-ecobee" description="Ecobee Binding" version="${project.version}">
<feature>openhab-runtime-base</feature>
<bundle start-level="80">mvn:org.openhab.addons.bundles/org.openhab.binding.ecobee/${project.version}</bundle>
</feature>
</features>

View File

@@ -0,0 +1,644 @@
/**
* Copyright (c) 2010-2020 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.ecobee.action;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import javax.measure.quantity.Temperature;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.ecobee.internal.dto.thermostat.EventDTO;
import org.openhab.binding.ecobee.internal.enums.AckType;
import org.openhab.binding.ecobee.internal.enums.FanMode;
import org.openhab.binding.ecobee.internal.enums.HoldType;
import org.openhab.binding.ecobee.internal.enums.PlugState;
import org.openhab.binding.ecobee.internal.enums.VentilatorMode;
import org.openhab.binding.ecobee.internal.function.AcknowledgeFunction;
import org.openhab.binding.ecobee.internal.function.ControlPlugFunction;
import org.openhab.binding.ecobee.internal.function.CreateVacationFunction;
import org.openhab.binding.ecobee.internal.function.DeleteVacationFunction;
import org.openhab.binding.ecobee.internal.function.ResetPreferencesFunction;
import org.openhab.binding.ecobee.internal.function.ResumeProgramFunction;
import org.openhab.binding.ecobee.internal.function.SendMessageFunction;
import org.openhab.binding.ecobee.internal.function.SetHoldFunction;
import org.openhab.binding.ecobee.internal.function.SetOccupiedFunction;
import org.openhab.binding.ecobee.internal.function.UpdateSensorFunction;
import org.openhab.binding.ecobee.internal.handler.EcobeeThermostatBridgeHandler;
import org.openhab.binding.ecobee.internal.handler.EcobeeUtils;
import org.openhab.core.automation.annotation.ActionInput;
import org.openhab.core.automation.annotation.ActionOutput;
import org.openhab.core.automation.annotation.RuleAction;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.thing.binding.ThingActions;
import org.openhab.core.thing.binding.ThingActionsScope;
import org.openhab.core.thing.binding.ThingHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link EcobeeActions} defines the thing actions for the Ecobee binding.
* <p>
* <b>Note:</b>The static method <b>invokeMethod</b> handles the case where
* the test <i>actions instanceof EcobeeActions</i> fails. This test can fail
* due to an issue in openHAB core v2.5.0 where the {@link EcobeeActions} class
* can be loaded by a different classloader than the <i>actions</i> instance.
*
* @author John Cocula - Initial contribution
* @author Mark Hilbush - Adapted for OH2/3
* @author Connor Petty - Proxy method for invoking actions
*/
@ThingActionsScope(name = "ecobee")
@NonNullByDefault
public class EcobeeActions implements ThingActions, IEcobeeActions {
private static final Logger LOGGER = LoggerFactory.getLogger(EcobeeActions.class);
private @Nullable EcobeeThermostatBridgeHandler handler;
public EcobeeActions() {
LOGGER.debug("EcobeeActions: EcobeeActions: Actions service created");
}
@Override
public void setThingHandler(@Nullable ThingHandler handler) {
if (handler instanceof EcobeeThermostatBridgeHandler) {
this.handler = (EcobeeThermostatBridgeHandler) handler;
}
}
@Override
public @Nullable ThingHandler getThingHandler() {
return this.handler;
}
private static IEcobeeActions invokeMethodOf(@Nullable ThingActions actions) {
if (actions == null) {
throw new IllegalArgumentException("actions cannot be null");
}
if (actions.getClass().getName().equals(EcobeeActions.class.getName())) {
if (actions instanceof IEcobeeActions) {
return (IEcobeeActions) actions;
} else {
return (IEcobeeActions) Proxy.newProxyInstance(IEcobeeActions.class.getClassLoader(),
new Class[] { IEcobeeActions.class }, (Object proxy, Method method, Object[] args) -> {
Method m = actions.getClass().getDeclaredMethod(method.getName(),
method.getParameterTypes());
return m.invoke(actions, args);
});
}
}
throw new IllegalArgumentException("Actions is not an instance of EcobeeActions");
}
/**
* The acknowledge function allows an alert to be acknowledged.
*
* @see <a
* href="https://www.ecobee.com/home/developer/api/documentation/v1/functions/Acknowledge.shtml">Acknowledge
* </a>
*/
@Override
@RuleAction(label = "Acknowledge", description = "Acknowledges an alert.")
public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean acknowledge(
@ActionInput(name = "ackRef", description = "The acknowledge ref of alert") @Nullable String ackRef,
@ActionInput(name = "ackType", description = "The type of acknowledgement. Valid values: accept, decline, defer, unacknowledged") @Nullable String ackType,
@ActionInput(name = "remindMeLater", description = "(opt) Whether to remind at a later date, if this is a defer acknowledgement") @Nullable Boolean remindMeLater) {
LOGGER.debug("EcobeeActions: Action 'Acknowledge' called");
EcobeeThermostatBridgeHandler localHandler = handler;
if (localHandler == null) {
LOGGER.info("EcobeeActions: Action service ThingHandler is null!");
return false;
}
AcknowledgeFunction function = new AcknowledgeFunction(localHandler.getThermostatId(), ackRef,
AckType.forValue(ackType), remindMeLater);
return localHandler.actionPerformFunction(function);
}
public static boolean acknowledge(@Nullable ThingActions actions, @Nullable String ackRef, @Nullable String ackType,
@Nullable Boolean remindMeLater) {
return invokeMethodOf(actions).acknowledge(ackRef, ackType, remindMeLater);
}
/**
* Control the on/off state of a plug by setting a hold on the plug.
*
* @see <a href="https://www.ecobee.com/home/developer/api/documentation/v1/functions/ControlPlug.shtml">Control
* Plug</a>
*/
@Override
@RuleAction(label = "Control Plug", description = "Control the on/off state of a plug by setting a hold on the plug.")
public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean controlPlug(
@ActionInput(name = "plugName", description = "The name of the plug. Ensure each plug has a unique name.") @Nullable String plugName,
@ActionInput(name = "plugState", description = "The state to put the plug into. Valid values: on, off, resume.") @Nullable String plugState,
@ActionInput(name = "startDateTime", description = "(opt) The start date/time in thermostat time.") @Nullable Date startDateTime,
@ActionInput(name = "endDateTime", description = "(opt) The end date/time in thermostat time.") @Nullable Date endDateTime,
@ActionInput(name = "holdType", description = "(opt) The hold duration type. Valid values: dateTime, nextTransition, indefinite, holdHours.") @Nullable String holdType,
@ActionInput(name = "holdHours", description = "(opt) The number of hours to hold for, used and required if holdType='holdHours'.") @Nullable Number holdHours) {
LOGGER.debug("EcobeeActions: Action 'Control Plug' called");
EcobeeThermostatBridgeHandler localHandler = handler;
if (localHandler == null) {
LOGGER.info("EcobeeActions: Action service ThingHandler is null!");
return false;
}
ControlPlugFunction function = (new ControlPlugFunction(plugName, PlugState.forValue(plugState), startDateTime,
endDateTime, (holdType == null) ? null : HoldType.forValue(holdType),
(holdHours == null) ? null : Integer.valueOf(holdHours.intValue())));
return localHandler.actionPerformFunction(function);
}
public static boolean controlPlug(@Nullable ThingActions actions, @Nullable String plugName,
@Nullable String plugState, @Nullable Date startDateTime, @Nullable Date endDateTime,
@Nullable String holdType, @Nullable Number holdHours) {
return invokeMethodOf(actions).controlPlug(plugName, plugState, startDateTime, endDateTime, holdType,
holdHours);
}
/**
* The create vacation function creates a vacation event on the thermostat.
*
* @see <a href="https://www.ecobee.com/home/developer/api/documentation/v1/functions/CreateVacation.shtml">Create
* Vacation</a>
*/
@Override
@RuleAction(label = "Create Vacation", description = "The create vacation function creates a vacation event on the thermostat.")
public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean createVacation(
@ActionInput(name = "name", description = "The vacation event name. It must be unique.") @Nullable String name,
@ActionInput(name = "coolHoldTemp", description = "The temperature at which to set the cool vacation hold.") @Nullable QuantityType<Temperature> coolHoldTemp,
@ActionInput(name = "heatHoldTemp", description = "The temperature at which to set the heat vacation hold.") @Nullable QuantityType<Temperature> heatHoldTemp,
@ActionInput(name = "startDateTime", description = "(opt) The start date/time in thermostat time.") @Nullable Date startDateTime,
@ActionInput(name = "endDateTime", description = "(opt) The end date in thermostat time.") @Nullable Date endDateTime,
@ActionInput(name = "fan", description = "(opt) The fan mode during the vacation. Values: auto, on Default: auto") @Nullable String fan,
@ActionInput(name = "fanMinOnTime", description = "(opt) The minimum number of minutes to run the fan each hour. Range: 0-60, Default: 0") @Nullable Number fanMinOnTime) {
LOGGER.debug("EcobeeActions: Action 'Create Vacation' called");
EcobeeThermostatBridgeHandler localHandler = handler;
if (localHandler == null) {
LOGGER.info("EcobeeActions: Action service ThingHandler is null!");
return false;
}
CreateVacationFunction function = new CreateVacationFunction(name, coolHoldTemp, heatHoldTemp, startDateTime,
endDateTime, (fan == null) ? null : FanMode.forValue(fan),
(fanMinOnTime == null) ? null : Integer.valueOf(fanMinOnTime.intValue()));
return localHandler.actionPerformFunction(function);
}
public static boolean createVacation(@Nullable ThingActions actions, @Nullable String name,
@Nullable QuantityType<Temperature> coolHoldTemp, @Nullable QuantityType<Temperature> heatHoldTemp,
@Nullable Date startDateTime, @Nullable Date endDateTime, @Nullable String fan,
@Nullable Number fanMinOnTime) {
return invokeMethodOf(actions).createVacation(name, coolHoldTemp, heatHoldTemp, startDateTime, endDateTime, fan,
fanMinOnTime);
}
/**
* The delete vacation function deletes a vacation event from a thermostat.
*
* @see <a href="https://www.ecobee.com/home/developer/api/documentation/v1/functions/DeleteVacation.shtml">Delete
* Vacation</a>
*/
@Override
@RuleAction(label = "Delete Vacation", description = "The delete vacation function deletes a vacation event from a thermostat.")
public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean deleteVacation(
@ActionInput(name = "name", description = "The vacation event name to delete.") @Nullable String name) {
LOGGER.debug("EcobeeActions: Action 'Delete Vacation' called");
EcobeeThermostatBridgeHandler localHandler = handler;
if (localHandler == null) {
LOGGER.info("EcobeeActions: Action service ThingHandler is null!");
return false;
}
DeleteVacationFunction function = new DeleteVacationFunction(name);
return localHandler.actionPerformFunction(function);
}
public static boolean deleteVacation(@Nullable ThingActions actions, @Nullable String name) {
return invokeMethodOf(actions).deleteVacation(name);
}
/**
* The reset preferences function sets all of the user configurable settings back to the factory default values.
*
* @see <a href="https://www.ecobee.com/home/developer/api/documentation/v1/functions/ResetPreferences.shtml">Reset
* Preferences</a>
*/
@Override
@RuleAction(label = "Reset Preferences", description = "The reset preferences function sets all of the user configurable settings back to the factory default values.")
public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean resetPreferences() {
LOGGER.debug("EcobeeActions: Action 'Reset Preferences' called");
EcobeeThermostatBridgeHandler localHandler = handler;
if (localHandler == null) {
LOGGER.info("EcobeeActions: Action service ThingHandler is null!");
return false;
}
ResetPreferencesFunction function = new ResetPreferencesFunction();
return localHandler.actionPerformFunction(function);
}
public static boolean resetPreferences(@Nullable ThingActions actions) {
return invokeMethodOf(actions).resetPreferences();
}
/**
* The resume program function removes the currently running event.
*
* @see <a href="https://www.ecobee.com/home/developer/api/documentation/v1/functions/ResumeProgram.shtml">Resume
* Program</a>
*/
@Override
@RuleAction(label = "Resume Program", description = "Removes the currently running event providing the event is not a mandatory demand response event")
public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean resumeProgram(
@ActionInput(name = "resumeAll", description = "(opt) Should the thermostat be resumed to next event (false) or to its program (true)") @Nullable Boolean resumeAll) {
LOGGER.debug("EcobeeActions: Action 'Resume Program' called");
EcobeeThermostatBridgeHandler localHandler = handler;
if (localHandler == null) {
LOGGER.info("EcobeeActions: Action service ThingHandler is null!");
return false;
}
ResumeProgramFunction function = new ResumeProgramFunction(resumeAll);
return localHandler.actionPerformFunction(function);
}
public static boolean resumeProgram(@Nullable ThingActions actions, @Nullable Boolean resumeAll) {
return invokeMethodOf(actions).resumeProgram(resumeAll);
}
/**
* The send message function allows an alert message to be sent to the thermostat.
*
* @see <a href="https://www.ecobee.com/home/developer/api/documentation/v1/functions/SendMessage.shtml">Send
* Message</a>
*/
@Override
@RuleAction(label = "Send Message", description = "The send message function allows an alert message to be sent to the thermostat.")
public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean sendMessage(
@ActionInput(name = "text", description = "The message text to send. Text will be truncated to 500 characters if longer") @Nullable String text) {
LOGGER.debug("EcobeeActions: Action 'SendMessage' called");
EcobeeThermostatBridgeHandler localHandler = handler;
if (localHandler == null) {
LOGGER.info("EcobeeActions: Action service ThingHandler is null!");
return false;
}
SendMessageFunction function = new SendMessageFunction(text);
return localHandler.actionPerformFunction(function);
}
public static boolean sendMessage(@Nullable ThingActions actions, @Nullable String text) {
return invokeMethodOf(actions).sendMessage(text);
}
/**
* Set an indefinite hold using the supplied the cool and heat hold temperatures
*
* @see <a href="https://www.ecobee.com/home/developer/api/documentation/v1/functions/SetHold.shtml">Set Hold</a>
*/
@Override
@RuleAction(label = "Set Hold", description = "The set hold function sets the thermostat into a hold with the specified temperatures.")
public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean setHold(
@ActionInput(name = "coolHoldTemp", description = "The temperature at which to set the cool hold.") @Nullable QuantityType<Temperature> coolHoldTemp,
@ActionInput(name = "heatHoldTemp", description = "The temperature at which to set the heat hold.") @Nullable QuantityType<Temperature> heatHoldTemp) {
if (coolHoldTemp == null || heatHoldTemp == null) {
throw new IllegalArgumentException("hold temperatures cannot be null");
}
Map<String, Object> params = new HashMap<String, Object>();
params.put("coolHoldTemp", coolHoldTemp);
params.put("heatHoldTemp", heatHoldTemp);
return setHold(params, null, null, null, null);
}
public static boolean setHold(@Nullable ThingActions actions, @Nullable QuantityType<Temperature> coolHoldTemp,
@Nullable QuantityType<Temperature> heatHoldTemp) {
return invokeMethodOf(actions).setHold(coolHoldTemp, heatHoldTemp);
}
/**
* Set a hold by providing the cool and heat temperatures and the number of hours.
*/
@Override
@RuleAction(label = "Set Hold", description = "The set hold function sets the thermostat into a hold for the specified number of hours.")
public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean setHold(
@ActionInput(name = "coolHoldTemp", description = "The temperature at which to set the cool hold.") @Nullable QuantityType<Temperature> coolHoldTemp,
@ActionInput(name = "heatHoldTemp", description = "The temperature at which to set the heat hold.") @Nullable QuantityType<Temperature> heatHoldTemp,
@ActionInput(name = "holdHours", description = "The number of hours for the hold.") @Nullable Number holdHours) {
if (coolHoldTemp == null || heatHoldTemp == null) {
throw new IllegalArgumentException("hold temperatures cannot be null");
}
if (holdHours == null) {
throw new IllegalArgumentException("number of hold hours is missing");
}
Map<String, Object> params = new HashMap<String, Object>();
params.put("coolHoldTemp", coolHoldTemp);
params.put("heatHoldTemp", heatHoldTemp);
params.put("holdType", HoldType.HOLD_HOURS);
params.put("holdHours", Integer.valueOf(holdHours.intValue()));
return setHold(params, null, null, null, null);
}
public static boolean setHold(@Nullable ThingActions actions, @Nullable QuantityType<Temperature> coolHoldTemp,
@Nullable QuantityType<Temperature> heatHoldTemp, @Nullable Number holdHours) {
return invokeMethodOf(actions).setHold(coolHoldTemp, heatHoldTemp, holdHours);
}
/**
* Set an indefinite hold using the supplied climateRef
*/
@Override
@RuleAction(label = "Set Hold", description = "The set hold function sets the thermostat into a hold with the specified climate ref.")
public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean setHold(
@ActionInput(name = "holdClimateRef", description = "The holdClimateRef used to set the hold.") @Nullable String holdClimateRef) {
EcobeeThermostatBridgeHandler localHandler = handler;
if (localHandler == null) {
LOGGER.info("EcobeeActions: Action service ThingHandler is null!");
return false;
}
if (holdClimateRef == null || !localHandler.isValidClimateRef(holdClimateRef)) {
throw new IllegalArgumentException("hold climate ref is missing or invalid");
}
Map<String, Object> params = new HashMap<String, Object>();
params.put("holdClimateRef", holdClimateRef);
return setHold(params, null, null, null, null);
}
public static boolean setHold(@Nullable ThingActions actions, @Nullable String holdClimateRef) {
return invokeMethodOf(actions).setHold(holdClimateRef);
}
/**
* Set a hold using the supplied climateRef for the supplied number of hours.
*/
@Override
@RuleAction(label = "Set Hold", description = "The set hold function sets the thermostat into a hold with the specified climate ref.")
public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean setHold(
@ActionInput(name = "holdClimateRef", description = "The holdClimateRef used to set the hold.") @Nullable String holdClimateRef,
@ActionInput(name = "holdHours", description = "The number of hours for the hold.") @Nullable Number holdHours) {
if (holdHours == null) {
throw new IllegalArgumentException("number of hold hours is missing");
}
EcobeeThermostatBridgeHandler localHandler = handler;
if (localHandler == null) {
LOGGER.info("EcobeeActions: Action service ThingHandler is null!");
return false;
}
if (holdClimateRef == null || !localHandler.isValidClimateRef(holdClimateRef)) {
throw new IllegalArgumentException("hold climate ref is missing or invalid");
}
Map<String, Object> params = new HashMap<String, Object>();
params.put("holdClimateRef", holdClimateRef);
params.put("holdType", HoldType.HOLD_HOURS);
params.put("holdHours", Integer.valueOf(holdHours.intValue()));
return setHold(params, null, null, null, null);
}
public static boolean setHold(@Nullable ThingActions actions, @Nullable String holdClimateRef,
@Nullable Number holdHours) {
return invokeMethodOf(actions).setHold(holdClimateRef, holdHours);
}
/**
* Set a hold
*/
@Override
@RuleAction(label = "Set Hold", description = "The set hold function sets the thermostat into a hold with the specified temperature or climate ref.")
public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean setHold(
@ActionInput(name = "coolHoldTemp", description = "(opt) The temperature at which to set the cool hold.") @Nullable QuantityType<Temperature> coolHoldTemp,
@ActionInput(name = "heatHoldTemp", description = "(opt) The temperature at which to set the heat hold.") @Nullable QuantityType<Temperature> heatHoldTemp,
@ActionInput(name = "holdClimateRef", description = "(opt) The Climate to use as reference for setting the coolHoldTemp, heatHoldTemp and fan settings for this hold. If this value is passed the coolHoldTemp and heatHoldTemp are not required.") @Nullable String holdClimateRef,
@ActionInput(name = "startDateTime", description = "(opt) The start date in thermostat time.") @Nullable Date startDateTime,
@ActionInput(name = "endDateTime", description = "(opt) The end date in thermostat time.") @Nullable Date endDateTime,
@ActionInput(name = "holdType", description = "(opt) The hold duration type. Valid values: dateTime, nextTransition, indefinite, holdHours.") @Nullable String holdType,
@ActionInput(name = "holdHours", description = "(opt) The number of hours to hold for, used and required if holdType='holdHours'.") @Nullable Number holdHours) {
Map<String, Object> params = new HashMap<String, Object>();
if (coolHoldTemp != null) {
params.put("coolHoldTemp", coolHoldTemp);
}
if (heatHoldTemp != null) {
params.put("heatHoldTemp", heatHoldTemp);
}
if (holdClimateRef != null) {
params.put("holdClimateRef", holdClimateRef);
}
return setHold(params, holdType, holdHours, startDateTime, endDateTime);
}
public static boolean setHold(@Nullable ThingActions actions, @Nullable QuantityType<Temperature> coolHoldTemp,
@Nullable QuantityType<Temperature> heatHoldTemp, @Nullable String holdClimateRef,
@Nullable Date startDateTime, @Nullable Date endDateTime, @Nullable String holdType,
@Nullable Number holdHours) {
return invokeMethodOf(actions).setHold(coolHoldTemp, heatHoldTemp, holdClimateRef, startDateTime, endDateTime,
holdType, holdHours);
}
/**
* Set a hold by providing a parameter map
*/
@Override
@RuleAction(label = "Set Hold", description = "The set hold function sets the thermostat into a hold with the specified event parameters.")
public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean setHold(
@ActionInput(name = "params", description = "The map of hold parameters.") @Nullable Map<String, Object> params,
@ActionInput(name = "holdType", description = "(opt) The hold duration type. Valid values: dateTime, nextTransition, indefinite, holdHours.") @Nullable String holdType,
@ActionInput(name = "holdHours", description = "(opt) The number of hours to hold for, used and required if holdType='holdHours'.") @Nullable Number holdHours,
@ActionInput(name = "startDateTime", description = "(opt) The start date in thermostat time.") @Nullable Date startDateTime,
@ActionInput(name = "endDateTime", description = "(opt) The end date in thermostat time.") @Nullable Date endDateTime) {
LOGGER.debug("EcobeeActions: Action 'SetHold' called");
if (params == null) {
throw new IllegalArgumentException("params cannot be null");
}
EventDTO event = new EventDTO();
for (String key : params.keySet()) {
Object value = params.get(key);
switch (key) {
case "isOccupied":
event.isOccupied = ((Boolean) value);
break;
case "isCoolOff":
event.isCoolOff = ((Boolean) value);
break;
case "isHeatOff":
event.isHeatOff = ((Boolean) value);
break;
case "coolHoldTemp":
event.coolHoldTemp = EcobeeUtils.convertQuantityTypeToEcobeeTemp(value);
break;
case "heatHoldTemp":
event.heatHoldTemp = EcobeeUtils.convertQuantityTypeToEcobeeTemp(value);
break;
case "fan":
event.fan = FanMode.forValue((String) value).toString();
break;
case "vent":
event.vent = VentilatorMode.forValue((String) value).toString();
break;
case "ventilatorMinOnTime":
event.ventilatorMinOnTime = ((Integer) value);
break;
case "isOptional":
event.isOptional = ((Boolean) value);
break;
case "isTemperatureRelative":
event.isTemperatureRelative = ((Boolean) value);
break;
case "coolRelativeTemp":
event.coolRelativeTemp = EcobeeUtils.convertQuantityTypeToEcobeeTemp(value);
break;
case "heatRelativeTemp":
event.heatRelativeTemp = EcobeeUtils.convertQuantityTypeToEcobeeTemp(value);
break;
case "isTemperatureAbsolute":
event.isTemperatureAbsolute = ((Boolean) value);
break;
case "fanMinOnTime":
event.fanMinOnTime = ((Integer) value);
break;
case "holdClimateRef":
event.holdClimateRef = ((String) value);
break;
default:
LOGGER.warn("Unrecognized event field '{}' with value '{}' ignored.", key, value);
break;
}
}
EcobeeThermostatBridgeHandler localHandler = handler;
if (localHandler == null) {
LOGGER.info("EcobeeActions: Action service ThingHandler is null!");
return false;
}
SetHoldFunction function = new SetHoldFunction(event, (holdType == null) ? null : HoldType.forValue(holdType),
(holdHours == null) ? null : holdHours.intValue(), startDateTime, endDateTime);
return localHandler.actionPerformFunction(function);
}
public static boolean setHold(@Nullable ThingActions actions, @Nullable Map<String, Object> params,
@Nullable String holdType, @Nullable Number holdHours, @Nullable Date startDateTime,
@Nullable Date endDateTime) {
return invokeMethodOf(actions).setHold(params, holdType, holdHours, startDateTime, endDateTime);
}
/**
* The set occupied function may only be used by EMS thermostats. The function switches a thermostat from occupied
* mode to unoccupied, or vice versa.
*
* @see <a href="https://www.ecobee.com/home/developer/api/documentation/v1/functions/SetOccupied.shtml">Set
* Occupied</a>
*/
@Override
@RuleAction(label = "Set Occupied", description = "The function switches a thermostat from occupied mode to unoccupied, or vice versa (EMS MODELS ONLY).")
public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean setOccupied(
@ActionInput(name = "occupied", description = "The climate to use for the temperature, occupied (true) or unoccupied (false).") @Nullable Boolean occupied,
@ActionInput(name = "startDateTime", description = "(opt) The start date in thermostat time.") @Nullable Date startDateTime,
@ActionInput(name = "endDateTime", description = "(opt) The end date in thermostat time.") @Nullable Date endDateTime,
@ActionInput(name = "holdType", description = "(opt) The hold duration type. Valid values: dateTime, nextTransition, indefinite, holdHours.") @Nullable String holdType,
@ActionInput(name = "holdHours", description = "(opt) The number of hours to hold for, used and required if holdType='holdHours'.") @Nullable Number holdHours) {
LOGGER.debug("EcobeeActions: Action 'Set Occupied' called");
EcobeeThermostatBridgeHandler localHandler = handler;
if (localHandler == null) {
LOGGER.info("EcobeeActions: Action service ThingHandler is null!");
return false;
}
SetOccupiedFunction function = new SetOccupiedFunction(occupied, startDateTime, endDateTime,
(holdType == null) ? null : HoldType.forValue(holdType),
(holdHours == null) ? null : Integer.valueOf(holdHours.intValue()));
return localHandler.actionPerformFunction(function);
}
public static boolean setOccupied(@Nullable ThingActions actions, @Nullable Boolean occupied,
@Nullable Date startDateTime, @Nullable Date endDateTime, @Nullable String holdType,
@Nullable Number holdHours) {
return invokeMethodOf(actions).setOccupied(occupied, startDateTime, endDateTime, holdType, holdHours);
}
/**
* The update sensor function allows the caller to update the name of an ecobee3 remote sensor.
*
* @see <a href="https://www.ecobee.com/home/developer/api/documentation/v1/functions/UpdateSensor.shtml">Update
* Sensor</a>
*/
@Override
@RuleAction(label = "Update Sensor", description = "The update sensor function allows the caller to update the name of an ecobee3 remote sensor.")
public @ActionOutput(name = "success", type = "java.lang.Boolean") Boolean updateSensor(
@ActionInput(name = "name", description = "The updated name to give the sensor. Has a max length of 32, but shorter is recommended.") @Nullable String name,
@ActionInput(name = "deviceId", description = "The deviceId for the sensor, typically this indicates the enclosure and corresponds to the ThermostatRemoteSensor.id field. For example: rs:100") @Nullable String deviceId,
@ActionInput(name = "sensorId", description = "The identifier for the sensor within the enclosure. Corresponds to the RemoteSensorCapability.id. For example: 1") @Nullable String sensorId) {
LOGGER.debug("EcobeeActions: Action 'UpdateSensor' called");
EcobeeThermostatBridgeHandler localHandler = handler;
if (localHandler == null) {
LOGGER.info("EcobeeActions: Action service ThingHandler is null!");
return false;
}
UpdateSensorFunction function = new UpdateSensorFunction(name, deviceId, sensorId);
return localHandler.actionPerformFunction(function);
}
public static boolean updateSensor(@Nullable ThingActions actions, @Nullable String name, @Nullable String deviceId,
@Nullable String sensorId) {
return invokeMethodOf(actions).updateSensor(name, deviceId, sensorId);
}
/**
* Get the alerts list. Returns a JSON string containing all the alerts.
*/
@Override
@RuleAction(label = "Get Alerts", description = "Get the alerts list")
public @ActionOutput(name = "alerts", type = "java.lang.String") @Nullable String getAlerts() {
LOGGER.debug("EcobeeActions: Action 'Get Alerts' called");
EcobeeThermostatBridgeHandler localHandler = handler;
if (localHandler == null) {
LOGGER.info("EcobeeActions: Action service ThingHandler is null!");
return null;
}
return localHandler.getAlerts();
}
public static @Nullable String getAlerts(@Nullable ThingActions actions) {
return invokeMethodOf(actions).getAlerts();
}
/**
* Get the events list. Returns a JSON string contains all events.
*/
@Override
@RuleAction(label = "Get Events", description = "Get the events list")
public @ActionOutput(name = "events", type = "java.lang.String") @Nullable String getEvents() {
LOGGER.debug("EcobeeActions: Action 'Get Events' called");
EcobeeThermostatBridgeHandler localHandler = handler;
if (localHandler == null) {
LOGGER.info("EcobeeActions: Action service ThingHandler is null!");
return null;
}
return localHandler.getEvents();
}
public static @Nullable String getEvents(@Nullable ThingActions actions) {
return invokeMethodOf(actions).getEvents();
}
/**
* Get a list of climates. Returns a JSON string contains all climates.
*/
@Override
@RuleAction(label = "Get Climates", description = "Get a list of climates")
public @ActionOutput(name = "climates", type = "java.lang.String") @Nullable String getClimates() {
LOGGER.debug("EcobeeActions: Action 'Get Climates' called");
EcobeeThermostatBridgeHandler localHandler = handler;
if (localHandler == null) {
LOGGER.info("EcobeeActions: Action service ThingHandler is null!");
return null;
}
return localHandler.getClimates();
}
public static @Nullable String getClimates(@Nullable ThingActions actions) {
return invokeMethodOf(actions).getClimates();
}
}

View File

@@ -0,0 +1,78 @@
/**
* Copyright (c) 2010-2020 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.ecobee.action;
import java.util.Date;
import java.util.Map;
import javax.measure.quantity.Temperature;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.library.types.QuantityType;
/**
* The {@link IEcobeeActions} defines the interface for all thing actions supported by the binding.
* These methods, parameters, and return types are explained in {@link EcobeeActions}.
*
* @author Mark Hilbush - Initial contribution
*/
@NonNullByDefault
public interface IEcobeeActions {
public Boolean acknowledge(@Nullable String ackRef, @Nullable String ackType, @Nullable Boolean remindMeLater);
public Boolean controlPlug(@Nullable String plugName, @Nullable String plugState, @Nullable Date startDateTime,
@Nullable Date endDateTime, @Nullable String holdType, @Nullable Number holdHours);
public Boolean sendMessage(@Nullable String text);
public Boolean createVacation(@Nullable String name, @Nullable QuantityType<Temperature> coolHoldTemp,
@Nullable QuantityType<Temperature> heatHoldTemp, @Nullable Date startDateTime, @Nullable Date endDateTime,
@Nullable String fan, @Nullable Number fanMinOnTime);
public Boolean deleteVacation(@Nullable String name);
public Boolean resetPreferences();
public Boolean resumeProgram(@Nullable Boolean resumeAll);
public Boolean setHold(@Nullable QuantityType<Temperature> coolHoldTemp,
@Nullable QuantityType<Temperature> heatHoldTemp);
public Boolean setHold(@Nullable QuantityType<Temperature> coolHoldTemp,
@Nullable QuantityType<Temperature> heatHoldTemp, @Nullable Number holdHours);
public Boolean setHold(@Nullable String holdClimateRef);
public Boolean setHold(@Nullable String holdClimateRef, @Nullable Number holdHours);
public Boolean setHold(@Nullable QuantityType<Temperature> coolHoldTemp,
@Nullable QuantityType<Temperature> heatHoldTemp, @Nullable String holdClimateRef,
@Nullable Date startDateTime, @Nullable Date endDateTime, @Nullable String holdType,
@Nullable Number holdHours);
public Boolean setHold(@Nullable Map<String, Object> params, @Nullable String holdType, @Nullable Number holdHours,
@Nullable Date startDateTime, @Nullable Date endDateTime);
public Boolean setOccupied(@Nullable Boolean occupied, @Nullable Date startDateTime, @Nullable Date endDateTime,
@Nullable String holdType, @Nullable Number holdHours);
public Boolean updateSensor(@Nullable String name, @Nullable String deviceId, @Nullable String sensorId);
public @Nullable String getAlerts();
public @Nullable String getEvents();
public @Nullable String getClimates();
}

View File

@@ -0,0 +1,399 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.ecobee.internal.dto.thermostat.AlertDTO;
import org.openhab.binding.ecobee.internal.dto.thermostat.EventDTO;
import org.openhab.binding.ecobee.internal.dto.thermostat.HouseDetailsDTO;
import org.openhab.binding.ecobee.internal.dto.thermostat.LocationDTO;
import org.openhab.binding.ecobee.internal.dto.thermostat.ManagementDTO;
import org.openhab.binding.ecobee.internal.dto.thermostat.RemoteSensorDTO;
import org.openhab.binding.ecobee.internal.dto.thermostat.TechnicianDTO;
import org.openhab.binding.ecobee.internal.dto.thermostat.ThermostatDTO;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.type.ChannelTypeUID;
/**
* The {@link EcobeeBindingConstants} class defines common constants that are
* used across the whole binding.
*
* @author Mark Hilbush - Initial contribution
*/
@NonNullByDefault
public class EcobeeBindingConstants {
public static final String BINDING_ID = "ecobee";
// Account bridge
public static final String THING_TYPE_ACCOUNT = "account";
public static final ThingTypeUID UID_ACCOUNT_BRIDGE = new ThingTypeUID(BINDING_ID, THING_TYPE_ACCOUNT);
public static final Set<ThingTypeUID> SUPPORTED_ACCOUNT_BRIDGE_THING_TYPES_UIDS = Collections
.unmodifiableSet(Stream.of(UID_ACCOUNT_BRIDGE).collect(Collectors.toSet()));
// Thermostat bridge
public static final String THING_TYPE_THERMOSTAT = "thermostat";
public static final ThingTypeUID UID_THERMOSTAT_BRIDGE = new ThingTypeUID(BINDING_ID, THING_TYPE_THERMOSTAT);
public static final Set<ThingTypeUID> SUPPORTED_THERMOSTAT_BRIDGE_THING_TYPES_UIDS = Collections
.unmodifiableSet(Stream.of(UID_THERMOSTAT_BRIDGE).collect(Collectors.toSet()));
// Remote sensor thing
public static final String THING_TYPE_SENSOR = "sensor";
public static final ThingTypeUID UID_SENSOR_THING = new ThingTypeUID(BINDING_ID, THING_TYPE_SENSOR);
public static final Set<ThingTypeUID> SUPPORTED_SENSOR_THING_TYPES_UIDS = Collections
.unmodifiableSet(Stream.of(UID_SENSOR_THING).collect(Collectors.toSet()));
// Collection of all supported thing types
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections.unmodifiableSet(
Stream.of(UID_ACCOUNT_BRIDGE, UID_THERMOSTAT_BRIDGE, UID_SENSOR_THING).collect(Collectors.toSet()));
// Thermostat bridge and remote sensor thing config parameters
public static final String CONFIG_THERMOSTAT_ID = "thermostatId";
public static final String CONFIG_SENSOR_ID = "sensorId";
// Channel groups
public static final String CHGRP_ALERT = "alerts";
public static final String CHGRP_INFO = "info";
public static final String CHGRP_ELECTRICITY = "electricity";
public static final String CHGRP_EQUIPMENT_STATUS = "equipmentStatus";
public static final String CHGRP_EVENT = "events";
public static final String CHGRP_EXTENDED_RUNTIME = "extendedRuntime";
public static final String CHGRP_HOUSE_DETAILS = "houseDetails";
public static final String CHGRP_LOCATION = "location";
public static final String CHGRP_MANAGEMENT = "management";
public static final String CHGRP_NOTIFICATION_SETTINGS = "notificationSettings";
public static final String CHGRP_OEM_CFG = "oemCfg";
public static final String CHGRP_PRIVACY = "privacy";
public static final String CHGRP_PROGRAM = "program";
public static final String CHGRP_RUNTIME = "runtime";
public static final String CHGRP_SETTINGS = "settings";
public static final String CHGRP_TECHNICIAN = "technician";
public static final String CHGRP_UTILITY = "utility";
public static final String CHGRP_VERSION = "version";
public static final String CHGRP_WEATHER = "weather";
public static final String CHGRP_FORECAST = "forecast";
// Exclude CHGRP_INFO and CHGRP_FORECAST because they are not part of the selection object
public static final List<String> CHANNEL_GROUPS = Stream.of(CHGRP_ALERT, CHGRP_ELECTRICITY, CHGRP_EQUIPMENT_STATUS,
CHGRP_EVENT, CHGRP_EXTENDED_RUNTIME, CHGRP_HOUSE_DETAILS, CHGRP_LOCATION, CHGRP_MANAGEMENT,
CHGRP_NOTIFICATION_SETTINGS, CHGRP_OEM_CFG, CHGRP_PRIVACY, CHGRP_PROGRAM, CHGRP_RUNTIME, CHGRP_SETTINGS,
CHGRP_TECHNICIAN, CHGRP_UTILITY, CHGRP_VERSION, CHGRP_WEATHER).collect(Collectors.toList());
// Thermostat bridge info channels
public static final String CH_IDENTIFIER = "identifier";
public static final String CH_NAME = "name";
public static final String CH_THERMOSTAT_REV = "thermostatRev";
public static final String CH_IS_REGISTERED = "isRegistered";
public static final String CH_MODEL_NUMBER = "modelNumber";
public static final String CH_BRAND = "brand";
public static final String CH_FEATURES = "features";
public static final String CH_LAST_MODIFIED = "lastModified";
public static final String CH_THERMOSTAT_TIME = "thermostatTime";
public static final String CH_UTC_TIME = "utcTime";
// Thermostat bridge equipment status channels
public static final String CH_EQUIPMENT_STATUS = "equipmentStatus";
// Thermostat bridge ALERT channels
public static final String CH_ALERT_ACKNOWLEDGE_REF = "acknowledgeRef";
public static final String CH_ALERT_DATE = "date";
public static final String CH_ALERT_TIME = "time";
public static final String CH_ALERT_SEVERITY = "severity";
public static final String CH_ALERT_TEXT = "text";
public static final String CH_ALERT_ALERT_NUMBER = "number";
public static final String CH_ALERT_ALERT_TYPE = "type";
public static final String CH_ALERT_IS_OPERATOR_ALERT = "isOperatorAlert";
public static final String CH_ALERT_REMINDER = "reminder";
public static final String CH_ALERT_SHOW_IDT = "showIdt";
public static final String CH_ALERT_SHOW_WEB = "showWeb";
public static final String CH_ALERT_SEND_EMAIL = "sendEmail";
public static final String CH_ALERT_ACKNOWLEDGEMENT = "acknowledgement";
public static final String CH_ALERT_REMIND_ME_LATER = "remindMeLater";
public static final String CH_ALERT_THERMOSTAT_IDENTIFIER = "thermostatIdentifier";
public static final String CH_ALERT_NOTIFICATION_TYPE = "notificationType";
// Thermostat bridge EVENT channels
public static final String CH_EVENT_NAME = "name";
public static final String CH_EVENT_TYPE = "type";
public static final String CH_EVENT_RUNNING = "running";
public static final String CH_EVENT_START_DATE = "startDate";
public static final String CH_EVENT_START_TIME = "startTime";
public static final String CH_EVENT_END_DATE = "endDate";
public static final String CH_EVENT_END_TIME = "endTime";
public static final String CH_EVENT_IS_OCCUPIED = "isOccupied";
public static final String CH_EVENT_IS_COOL_OFF = "isCoolOff";
public static final String CH_EVENT_IS_HEAT_OFF = "isHeatOff";
public static final String CH_EVENT_COOL_HOLD_TEMP = "coolHoldTemp";
public static final String CH_EVENT_HEAT_HOLD_TEMP = "heatHoldTemp";
public static final String CH_EVENT_FAN = "fan";
public static final String CH_EVENT_VENT = "vent";
public static final String CH_EVENT_VENTILATOR_MIN_ON_TIME = "ventilatorMinOnTime";
public static final String CH_EVENT_IS_OPTIONAL = "isOptional";
public static final String CH_EVENT_IS_TEMPERATURE_RELATIVE = "isTemperatureRelative";
public static final String CH_EVENT_COOL_RELATIVE_TEMP = "coolRelativeTemp";
public static final String CH_EVENT_HEAT_RELATIVE_TEMP = "heatRelativeTemp";
public static final String CH_EVENT_IS_TEMPERATURE_ABSOLUTE = "isTemperatureAbsolute";
public static final String CH_EVENT_DUTY_CYCLE_PERCENTAGE = "dutyCyclePercentage";
public static final String CH_EVENT_FAN_MIN_ON_TIME = "fanMinOnTime";
public static final String CH_EVENT_OCCUPIED_SENSOR_ACTIVE = "occupiedSensorActive";
public static final String CH_EVENT_UNOCCUPIED_SENSOR_ACTIVE = "unoccupiedSensorActive";
public static final String CH_EVENT_DR_RAMP_UP_TEMP = "drRampUpTemp";
public static final String CH_EVENT_DR_RAMP_UP_TIME = "drRampUpTime";
public static final String CH_EVENT_LINK_REF = "linkRef";
public static final String CH_EVENT_HOLD_CLIMATE_REF = "holdClimateRef";
// Thermostat bridge HOUSE DETAILS channels
public static final String CH_HOUSEDETAILS_STYLE = "style";
public static final String CH_HOUSEDETAILS_SIZE = "size";
public static final String CH_HOUSEDETAILS_NUMBER_OF_FLOORS = "numberOfFloors";
public static final String CH_HOUSEDETAILS_NUMBER_OF_ROOMS = "numberOfRooms";
public static final String CH_HOUSEDETAILS_NUMBER_OF_OCCUPANTS = "numberOfOccupants";
public static final String CH_HOUSEDETAILS_AGE = "age";
public static final String CH_HOUSEDETAILS_WINDOW_EFFICIENCY = "windowEfficiency";
// Thermostat bridge LOCATION channels
public static final String CH_TIME_ZONE_OFFSET_MINUTES = "timeZoneOffsetMinutes";
public static final String CH_TIME_ZONE = "timeZone";
public static final String CH_IS_DAYLIGHT_SAVING = "isDaylightSaving";
public static final String CH_STREET_ADDRESS = "streetAddress";
public static final String CH_CITY = "city";
public static final String CH_PROVINCE_STATE = "provinceState";
public static final String CH_COUNTRY = "country";
public static final String CH_POSTAL_CODE = "postalCode";
public static final String CH_PHONE_NUMBER = "phoneNumber";
public static final String CH_MAP_COORDINATES = "mapCoordinates";
// Thermostat bridge MANAGEMENT channels
public static final String CH_MANAGEMENT_ADMIN_CONTACT = "administrativeContact";
public static final String CH_MANAGEMENT_BILLING_CONTACT = "billingContact";
public static final String CH_MANAGEMENT_NAME = "name";
public static final String CH_MANAGEMENT_PHONE = "phone";
public static final String CH_MANAGEMENT_EMAIL = "email";
public static final String CH_MANAGEMENT_WEB = "web";
public static final String CH_MANAGEMENT_SHOW_ALERT_IDT = "showAlertIdt";
public static final String CH_MANAGEMENT_SHOW_ALERT_WEB = "showAlertWeb";
// Thermostat bridge PROGRAM channels
public static final String CH_PROGRAM_CURRENT_CLIMATE_REF = "currentClimateRef";
// Thermostat bridge RUNTIME channels
public static final String CH_RUNTIME_REV = "runtimeRev";
public static final String CH_CONNECTED = "connected";
public static final String CH_FIRST_CONNECTED = "firstConnected";
public static final String CH_CONNECT_DATE_TIME = "connectDateTime";
public static final String CH_DISCONNECT_DATE_TIME = "disconnectDateTime";
public static final String CH_RT_LAST_MODIFIED = "lastModified";
public static final String CH_RT_LAST_STATUS_MODIFIED = "lastStatusModified";
public static final String CH_RUNTIME_DATE = "runtimeDate";
public static final String CH_RUNTIME_INTERVAL = "runtimeInterval";
public static final String CH_ACTUAL_TEMPERATURE = "actualTemperature";
public static final String CH_ACTUAL_HUMIDITY = "actualHumidity";
public static final String CH_RAW_TEMPERATURE = "rawTemperature";
public static final String CH_SHOW_ICON_MODE = "showIconMode";
public static final String CH_DESIRED_HEAT = "desiredHeat";
public static final String CH_DESIRED_COOL = "desiredCool";
public static final String CH_DESIRED_HUMIDITY = "desiredHumidity";
public static final String CH_DESIRED_DEHUMIDITY = "desiredDehumidity";
public static final String CH_DESIRED_FAN_MODE = "desiredFanMode";
public static final String CH_DESIRED_HEAT_RANGE_LOW = "desiredHeatRangeLow";
public static final String CH_DESIRED_HEAT_RANGE_HIGH = "desiredHeatRangeHigh";
public static final String CH_DESIRED_COOL_RANGE_LOW = "desiredCoolRangeLow";
public static final String CH_DESIRED_COOL_RANGE_HIGH = "desiredCoolRangeHigh";
// Thermostat bridge SETTINGS channels
public static final String CH_HVAC_MODE = "hvacMode";
public static final String CH_LAST_SERVICE_DATE = "lastServiceDate";
public static final String CH_SERVICE_REMIND_ME = "serviceRemindMe";
public static final String CH_MONTHS_BETWEEN_SERVICE = "monthsBetweenService";
public static final String CH_REMIND_ME_DATE = "remindMeDate";
public static final String CH_VENT = "vent";
public static final String CH_VENTILATOR_MIN_ON_TIME = "ventilatorMinOnTime";
public static final String CH_SERVICE_REMIND_TECHNICIAN = "serviceRemindTechnician";
public static final String CH_EI_LOCATION = "eiLocation";
public static final String CH_COLD_TEMP_ALERT = "coldTempAlert";
public static final String CH_COLD_TEMP_ALERT_ENABLED = "coldTempAlertEnabled";
public static final String CH_HOT_TEMP_ALERT = "hotTempAlert";
public static final String CH_HOT_TEMP_ALERT_ENABLED = "hotTempAlertEnabled";
public static final String CH_COOL_STAGES = "coolStages";
public static final String CH_HEAT_STAGES = "heatStages";
public static final String CH_MAX_SET_BACK = "maxSetBack";
public static final String CH_MAX_SET_FORWARD = "maxSetForward";
public static final String CH_QUICK_SAVE_SET_BACK = "quickSaveSetBack";
public static final String CH_QUICK_SAVE_SET_FORWARD = "quickSaveSetForward";
public static final String CH_HAS_HEAT_PUMP = "hasHeatPump";
public static final String CH_HAS_FORCED_AIR = "hasForcedAir";
public static final String CH_HAS_BOILER = "hasBoiler";
public static final String CH_HAS_HUMIDIFIER = "hasHumidifier";
public static final String CH_HAS_ERV = "hasErv";
public static final String CH_HAS_HRV = "hasHrv";
public static final String CH_CONDENSATION_AVOID = "condensationAvoid";
public static final String CH_USE_CELSIUS = "useCelsius";
public static final String CH_USE_TIME_FORMAT_12 = "useTimeFormat12";
public static final String CH_LOCALE = "locale";
public static final String CH_HUMIDITY = "humidity";
public static final String CH_HUMIDIFIER_MODE = "humidifierMode";
public static final String CH_BACKLIGHT_ON_INTENSITY = "backlightOnIntensity";
public static final String CH_BACKLIGHT_SLEEP_INTENSITY = "backlightSleepIntensity";
public static final String CH_BACKLIGHT_OFF_TIME = "backlightOffTime";
public static final String CH_SOUND_TICK_VOLUME = "soundTickVolume";
public static final String CH_SOUND_ALERT_VOLUME = "soundAlertVolume";
public static final String CH_COMPRESSOR_PROTECTION_MIN_TIME = "compressorProtectionMinTime";
public static final String CH_COMPRESSOR_PROTECTION_MIN_TEMP = "compressorProtectionMinTemp";
public static final String CH_STAGE1_HEATING_DIFFERENTIAL_TEMP = "stage1HeatingDifferentialTemp";
public static final String CH_STAGE1_COOLING_DIFFERENTIAL_TEMP = "stage1CoolingDifferentialTemp";
public static final String CH_STAGE1_HEATING_DISSIPATION_TIME = "stage1HeatingDissipationTime";
public static final String CH_STAGE1_COOLING_DISSIPATION_TIME = "stage1CoolingDissipationTime";
public static final String CH_HEAT_PUMP_REVERSAL_ON_COOL = "heatPumpReversalOnCool";
public static final String CH_FAN_CONTROLLER_REQUIRED = "fanControlRequired";
public static final String CH_FAN_MIN_ON_TIME = "fanMinOnTime";
public static final String CH_HEAT_COOL_MIN_DELTA = "heatCoolMinDelta";
public static final String CH_TEMP_CORRECTION = "tempCorrection";
public static final String CH_HOLD_ACTION = "holdAction";
public static final String CH_HEAT_PUMP_GROUND_WATER = "heatPumpGroundWater";
public static final String CH_HAS_ELECTRIC = "hasElectric";
public static final String CH_HAS_DEHUMIDIFIER = "hasDehumidifier";
public static final String CH_DEHUMIDIFIER_MODE = "dehumidifierMode";
public static final String CH_DEHUMIDIFIER_LEVEL = "dehumidifierLevel";
public static final String CH_DEHUMIDIFY_WITH_AC = "dehumidifyWithAC";
public static final String CH_DEHUMIDIFY_OVERCOOL_OFFSET = "dehumidifyOvercoolOffset";
public static final String CH_AUTO_HEAT_COOL_FEATURE_ENABLED = "autoHeatCoolFeatureEnabled";
public static final String CH_WIFI_OFFLINE_ALERT = "wifiOfflineAlert";
public static final String CH_HEAT_MIN_TEMP = "heatMinTemp";
public static final String CH_HEAT_MAX_TEMP = "heatMaxTemp";
public static final String CH_COOL_MIN_TEMP = "coolMinTemp";
public static final String CH_COOL_MAX_TEMP = "coolMaxTemp";
public static final String CH_HEAT_RANGE_HIGH = "heatRangeHigh";
public static final String CH_HEAT_RANGE_LOW = "heatRangeLow";
public static final String CH_COOL_RANGE_HIGH = "coolRangeHigh";
public static final String CH_COOL_RANGE_LOW = "coolRangeLow";
public static final String CH_USER_ACCESS_CODE = "userAccessCode";
public static final String CH_USER_ACCESS_SETTING = "userAccessSetting";
public static final String CH_AUX_RUNTIME_ALERT = "auxRuntimeAlert";
public static final String CH_AUX_OUTDOOR_TEMP_ALERT = "auxOutdoorTempAlert";
public static final String CH_AUX_MAX_OUTDOOR_TEMP = "auxMaxOutdoorTemp";
public static final String CH_AUX_RUNTIME_ALERT_NOTIFY = "auxRuntimeAlertNotify";
public static final String CH_AUX_OUTDOOR_TEMP_ALERT_NOTIFY = "auxOutdoorTempAlertNotify";
public static final String CH_AUX_RUNTIME_ALERT_NOTIFY_TECHNICIAN = "auxRuntimeAlertNotifyTechnician";
public static final String CH_AUX_OUTDOOR_TEMP_ALERT_NOTIFY_TECHNICIAN = "auxOutdoorTempAlertNotifyTechnician";
public static final String CH_DISABLE_PREHEATING = "disablePreHeating";
public static final String CH_DISABLE_PRECOOLING = "disablePreCooling";
public static final String CH_INSTALLER_CODE_REQUIRED = "installerCodeRequired";
public static final String CH_DR_ACCEPT = "drAccept";
public static final String CH_IS_RENTAL_PROPERTY = "isRentalProperty";
public static final String CH_USE_ZONE_CONTROLLER = "useZoneController";
public static final String CH_RANDOM_START_DELAY_COOL = "randomStartDelayCool";
public static final String CH_RANDOM_START_DELAY_HEAT = "randomStartDelayHeat";
public static final String CH_HUMIDITY_HIGH_ALERT = "humidityHighAlert";
public static final String CH_HUMIDITY_LOW_ALERT = "humidityLowAlert";
public static final String CH_DISABLE_HEAT_PUMP_ALERTS = "disableHeatPumpAlerts";
public static final String CH_DISABLE_ALERTS_ON_IDT = "disableAlertsOnIdt";
public static final String CH_HUMIDITY_ALERT_NOTIFY = "humidityAlertNotify";
public static final String CH_HUMIDITY_ALERT_NOTIFY_TECHNICIAN = "humidityAlertNotifyTechnician";
public static final String CH_TEMP_ALERT_NOTIFY = "tempAlertNotify";
public static final String CH_TEMP_ALERT_NOTIFY_TECHNICIAN = "tempAlertNotifyTechnician";
public static final String CH_MONTHLY_ELECTRICITY_BILL_LIMIT = "monthlyElectricityBillLimit";
public static final String CH_ENABLE_ELECTRICITY_BILL_ALERT = "enableElectricityBillAlert";
public static final String CH_ENABLE_PROJECTED_ELECTRICITY_BILL_ALERT = "enableProjectedElectricityBillAlert";
public static final String CH_ELECTRICITY_BILLING_DAY_OF_MONTH = "electricityBillingDayOfMonth";
public static final String CH_ELECTRICITY_BILL_CYCLE_MONTHS = "electricityBillCycleMonths";
public static final String CH_ELECTRICITY_BILL_START_MONTH = "electricityBillStartMonth";
public static final String CH_VENTILATOR_MIN_ON_TIME_HOME = "ventilatorMinOnTimeHome";
public static final String CH_VENTILATOR_MIN_ON_TIME_AWAY = "ventilatorMinOnTimeAway";
public static final String CH_BACKLIGHT_OFF_DURING_SLEEP = "backlightOffDuringSleep";
public static final String CH_AUTO_AWAY = "autoAway";
public static final String CH_SMART_CIRCULATION = "smartCirculation";
public static final String CH_FOLLOW_ME_COMFORT = "followMeComfort";
public static final String CH_VENTILATOR_TYPE = "ventilatorType";
public static final String CH_IS_VENTILATOR_TIMER_ON = "isVentilatorTimerOn";
public static final String CH_VENTILATOR_OFF_DATE_TIME = "ventilatorOffDateTime";
public static final String CH_HAS_UV_FILTER = "hasUVFilter";
public static final String CH_COOLING_LOCKOUT = "coolingLockout";
public static final String CH_VENTILATOR_FREE_COOLING = "ventilatorFreeCooling";
public static final String CH_DEHUMIDIFY_WHEN_HEATING = "dehumidifyWhenHeating";
public static final String CH_VENTILATOR_DEHUMIDIFY = "ventilatorDehumidify";
public static final String CH_GROUP_REF = "groupRef";
public static final String CH_GROUP_NAME = "groupName";
public static final String CH_GROUP_SETTING = "groupSetting";
// Thermostat bridge TECHNICIAN channels
public static final String CH_TECHNICIAN_CONTRACTOR_REF = "contractorRef";
public static final String CH_TECHNICIAN_NAME = "name";
public static final String CH_TECHNICIAN_PHONE = "phone";
public static final String CH_TECHNICIAN_STREET_ADDRESS = "streetAddress";
public static final String CH_TECHNICIAN_CITY = "city";
public static final String CH_TECHNICIAN_PROVINCE_STATE = "provinceState";
public static final String CH_TECHNICIAN_COUNTRY = "country";
public static final String CH_TECHNICIAN_POSTAL_CODE = "postalCode";
public static final String CH_TECHNICIAN_EMAIL = "email";
public static final String CH_TECHNICIAN_WEB = "web";
// Thermostat bridge VERSION channels
public static final String CH_THERMOSTAT_FIRMWARE_VERSION = "thermostatFirmwareVersion";
// Thermostat bridge WEATHER channels
public static final String CH_WEATHER_TIMESTAMP = "timestamp";
public static final String CH_WEATHER_WEATHER_STATION = "weatherStation";
// Thermostat bridge FORECAST channels
public static final String CH_FORECAST_WEATHER_SYMBOL = "weatherSymbol";
public static final String CH_FORECAST_WEATHER_SYMBOL_TEXT = "weatherSymbolText";
public static final String CH_FORECAST_DATE_TIME = "dateTime";
public static final String CH_FORECAST_CONDITION = "condition";
public static final String CH_FORECAST_TEMPERATURE = "temperature";
public static final String CH_FORECAST_PRESSURE = "pressure";
public static final String CH_FORECAST_RELATIVE_HUMIDITY = "relativeHumidity";
public static final String CH_FORECAST_DEWPOINT = "dewpoint";
public static final String CH_FORECAST_VISIBILITY = "visibility";
public static final String CH_FORECAST_WIND_SPEED = "windSpeed";
public static final String CH_FORECAST_WIND_GUST = "windGust";
public static final String CH_FORECAST_WIND_DIRECTION = "windDirection";
public static final String CH_FORECAST_WIND_BEARING = "windBearing";
public static final String CH_FORECAST_POP = "pop";
public static final String CH_FORECAST_TEMP_HIGH = "tempHigh";
public static final String CH_FORECAST_TEMP_LOW = "tempLow";
public static final String CH_FORECAST_SKY = "sky";
public static final String CH_FORECAST_SKY_TEXT = "skyText";
// Remote sensor thing channel IDs
public static final String CH_SENSOR_ID = "id";
public static final String CH_SENSOR_NAME = "name";
public static final String CH_SENSOR_TYPE = "type";
public static final String CH_SENSOR_CODE = "code";
public static final String CH_SENSOR_IN_USE = "inUse";
// Channel Type UIDs for dynamically created sensor channels
public static final ChannelTypeUID CHANNELTYPEUID_TEMPERATURE = new ChannelTypeUID(BINDING_ID, "sensorTemperature");
public static final ChannelTypeUID CHANNELTYPEUID_HUMIDITY = new ChannelTypeUID(BINDING_ID, "sensorHumidity");
public static final ChannelTypeUID CHANNELTYPEUID_OCCUPANCY = new ChannelTypeUID(BINDING_ID, "sensorOccupancy");
public static final ChannelTypeUID CHANNELTYPEUID_GENERIC = new ChannelTypeUID(BINDING_ID, "sensorGeneric");
public static final AlertDTO EMPTY_ALERT = new AlertDTO();
public static final EventDTO EMPTY_EVENT = new EventDTO();
public static final LocationDTO EMPTY_LOCATION = new LocationDTO();
public static final HouseDetailsDTO EMPTY_HOUSEDETAILS = new HouseDetailsDTO();
public static final ManagementDTO EMPTY_MANAGEMENT = new ManagementDTO();
public static final TechnicianDTO EMPTY_TECHNICIAN = new TechnicianDTO();
public static final List<RemoteSensorDTO> EMPTY_SENSORS = Collections.<RemoteSensorDTO> emptyList();
public static final List<ThermostatDTO> EMPTY_THERMOSTATS = Collections.<ThermostatDTO> emptyList();
public static final String ECOBEE_BASE_URL = "https://api.ecobee.com/";
public static final String ECOBEE_AUTHORIZE_URL = ECOBEE_BASE_URL + "authorize";
public static final String ECOBEE_TOKEN_URL = ECOBEE_BASE_URL + "token";
public static final String ECOBEE_SCOPE = "smartWrite";
}

View File

@@ -0,0 +1,82 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal;
import static org.openhab.binding.ecobee.internal.EcobeeBindingConstants.*;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.openhab.binding.ecobee.internal.handler.EcobeeAccountBridgeHandler;
import org.openhab.binding.ecobee.internal.handler.EcobeeSensorThingHandler;
import org.openhab.binding.ecobee.internal.handler.EcobeeThermostatBridgeHandler;
import org.openhab.core.auth.client.oauth2.OAuthFactory;
import org.openhab.core.i18n.TimeZoneProvider;
import org.openhab.core.io.net.http.HttpClientFactory;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.binding.BaseThingHandlerFactory;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerFactory;
import org.openhab.core.thing.type.ChannelTypeRegistry;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
/**
* The {@link EcobeeHandlerFactory} is responsible for creating thing handlers
* for the account bridge, thermostat bridge, and sensor thing.
*
* @author Mark Hilbush - Initial contribution
*/
@NonNullByDefault
@Component(configurationPid = "binding.ecobee", service = ThingHandlerFactory.class)
public class EcobeeHandlerFactory extends BaseThingHandlerFactory {
private final TimeZoneProvider timeZoneProvider;
private final ChannelTypeRegistry channelTypeRegistry;
private final OAuthFactory oAuthFactory;
private final HttpClient httpClient;
@Activate
public EcobeeHandlerFactory(@Reference TimeZoneProvider timeZoneProvider,
@Reference ChannelTypeRegistry channelTypeRegistry, @Reference OAuthFactory oAuthFactory,
@Reference HttpClientFactory httpClientFactory) {
this.timeZoneProvider = timeZoneProvider;
this.channelTypeRegistry = channelTypeRegistry;
this.oAuthFactory = oAuthFactory;
this.httpClient = httpClientFactory.getCommonHttpClient();
}
@Override
public boolean supportsThingType(ThingTypeUID thingTypeUID) {
return SUPPORTED_THING_TYPES_UIDS.contains(thingTypeUID);
}
@Override
protected @Nullable ThingHandler createHandler(Thing thing) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
if (SUPPORTED_ACCOUNT_BRIDGE_THING_TYPES_UIDS.contains(thingTypeUID)) {
return new EcobeeAccountBridgeHandler((Bridge) thing, oAuthFactory, httpClient);
}
if (SUPPORTED_THERMOSTAT_BRIDGE_THING_TYPES_UIDS.contains(thingTypeUID)) {
return new EcobeeThermostatBridgeHandler((Bridge) thing, timeZoneProvider, channelTypeRegistry);
}
if (SUPPORTED_SENSOR_THING_TYPES_UIDS.contains(thingTypeUID)) {
return new EcobeeSensorThingHandler(thing);
}
return null;
}
}

View File

@@ -0,0 +1,345 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.api;
import static org.openhab.binding.ecobee.internal.EcobeeBindingConstants.*;
import java.io.ByteArrayInputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.TimeoutException;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.openhab.binding.ecobee.internal.dto.AbstractResponseDTO;
import org.openhab.binding.ecobee.internal.dto.SelectionDTO;
import org.openhab.binding.ecobee.internal.dto.SelectionType;
import org.openhab.binding.ecobee.internal.dto.thermostat.ThermostatDTO;
import org.openhab.binding.ecobee.internal.dto.thermostat.ThermostatRequestDTO;
import org.openhab.binding.ecobee.internal.dto.thermostat.ThermostatResponseDTO;
import org.openhab.binding.ecobee.internal.dto.thermostat.ThermostatUpdateRequestDTO;
import org.openhab.binding.ecobee.internal.dto.thermostat.summary.RevisionDTO;
import org.openhab.binding.ecobee.internal.dto.thermostat.summary.RevisionDTODeserializer;
import org.openhab.binding.ecobee.internal.dto.thermostat.summary.RunningDTO;
import org.openhab.binding.ecobee.internal.dto.thermostat.summary.RunningDTODeserializer;
import org.openhab.binding.ecobee.internal.dto.thermostat.summary.SummaryResponseDTO;
import org.openhab.binding.ecobee.internal.function.FunctionRequest;
import org.openhab.binding.ecobee.internal.handler.EcobeeAccountBridgeHandler;
import org.openhab.core.auth.client.oauth2.AccessTokenRefreshListener;
import org.openhab.core.auth.client.oauth2.AccessTokenResponse;
import org.openhab.core.auth.client.oauth2.OAuthClientService;
import org.openhab.core.auth.client.oauth2.OAuthException;
import org.openhab.core.auth.client.oauth2.OAuthFactory;
import org.openhab.core.auth.client.oauth2.OAuthResponseException;
import org.openhab.core.io.net.http.HttpUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonSyntaxException;
/**
* The {@link EcobeeApi} is responsible for managing all communication with
* the Ecobee API service.
*
* @author Mark Hilbush - Initial contribution
*/
@NonNullByDefault
public class EcobeeApi implements AccessTokenRefreshListener {
private static final Gson GSON = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss")
.registerTypeAdapter(RevisionDTO.class, new RevisionDTODeserializer())
.registerTypeAdapter(RunningDTO.class, new RunningDTODeserializer()).create();
private static final String ECOBEE_THERMOSTAT_URL = ECOBEE_BASE_URL + "1/thermostat";
private static final String ECOBEE_THERMOSTAT_SUMMARY_URL = ECOBEE_BASE_URL + "1/thermostatSummary";
private static final String ECOBEE_THERMOSTAT_UPDATE_URL = ECOBEE_THERMOSTAT_URL + "?format=json";
// These errors from the API will require an Ecobee authorization
private static final int ECOBEE_TOKEN_EXPIRED = 14;
private static final int ECOBEE_DEAUTHORIZED_TOKEN = 16;
private static final int TOKEN_EXPIRES_IN_BUFFER_SECONDS = 120;
public static final Properties HTTP_HEADERS;
static {
HTTP_HEADERS = new Properties();
HTTP_HEADERS.put("Content-Type", "application/json;charset=UTF-8");
HTTP_HEADERS.put("User-Agent", "openhab-ecobee-api/2.0");
}
public static Gson getGson() {
return GSON;
}
private final Logger logger = LoggerFactory.getLogger(EcobeeApi.class);
private final EcobeeAccountBridgeHandler bridgeHandler;
private final String apiKey;
private int apiTimeout;
private final OAuthFactory oAuthFactory;
private final HttpClient httpClient;
private @NonNullByDefault({}) OAuthClientService oAuthClientService;
private @NonNullByDefault({}) EcobeeAuth ecobeeAuth;
private @Nullable AccessTokenResponse accessTokenResponse;
public EcobeeApi(final EcobeeAccountBridgeHandler bridgeHandler, final String apiKey, final int apiTimeout,
org.openhab.core.auth.client.oauth2.OAuthFactory oAuthFactory, HttpClient httpClient) {
this.bridgeHandler = bridgeHandler;
this.apiKey = apiKey;
this.apiTimeout = apiTimeout;
this.oAuthFactory = oAuthFactory;
this.httpClient = httpClient;
createOAuthClientService();
}
public void createOAuthClientService() {
logger.debug("API: Creating OAuth Client Service");
OAuthClientService service = oAuthFactory.createOAuthClientService(
bridgeHandler.getThing().getUID().getAsString(), ECOBEE_TOKEN_URL, null, apiKey, "", ECOBEE_SCOPE,
false);
service.addAccessTokenRefreshListener(this);
ecobeeAuth = new EcobeeAuth(bridgeHandler, apiKey, apiTimeout, service, httpClient);
oAuthClientService = service;
}
public void deleteOAuthClientService() {
logger.debug("API: Deleting OAuth Client Service");
oAuthClientService.removeAccessTokenRefreshListener(this);
oAuthFactory.deleteServiceAndAccessToken(bridgeHandler.getThing().getUID().getAsString());
}
public void closeOAuthClientService() {
logger.debug("API: Closing OAuth Client Service");
oAuthClientService.removeAccessTokenRefreshListener(this);
oAuthFactory.ungetOAuthService(bridgeHandler.getThing().getUID().getAsString());
}
/**
* Check to see if the Ecobee authorization process is complete. This will be determined
* by requesting an AccessTokenResponse from the OHC OAuth service. If we get a valid
* response, then assume that the Ecobee authorization process is complete. Otherwise,
* start the Ecobee authorization process.
*/
private boolean isAuthorized() {
boolean isAuthorized = false;
try {
AccessTokenResponse localAccessTokenResponse = oAuthClientService.getAccessTokenResponse();
if (localAccessTokenResponse != null) {
logger.trace("API: Got AccessTokenResponse from OAuth service: {}", localAccessTokenResponse);
if (localAccessTokenResponse.isExpired(LocalDateTime.now(), TOKEN_EXPIRES_IN_BUFFER_SECONDS)) {
logger.debug("API: Token is expiring soon. Refresh it now");
localAccessTokenResponse = oAuthClientService.refreshToken();
}
ecobeeAuth.setState(EcobeeAuthState.COMPLETE);
isAuthorized = true;
} else {
logger.debug("API: Didn't get an AccessTokenResponse from OAuth service - doEcobeeAuthorization!!!");
if (ecobeeAuth.isComplete()) {
ecobeeAuth.setState(EcobeeAuthState.NEED_PIN);
}
}
accessTokenResponse = localAccessTokenResponse;
ecobeeAuth.doAuthorization();
} catch (OAuthException | IOException | RuntimeException e) {
logger.info("API: Got exception trying to get access token from OAuth service", e);
} catch (EcobeeAuthException e) {
logger.info("API: The Ecobee authorization process threw an exception", e);
ecobeeAuth.setState(EcobeeAuthState.NEED_PIN);
} catch (OAuthResponseException e) {
logger.info("API: Exception getting access token: error='{}', description='{}'", e.getError(),
e.getErrorDescription());
// How to handle the possible error codes?
}
return isAuthorized;
}
@Override
public void onAccessTokenResponse(AccessTokenResponse accessTokenResponse) {
}
public @Nullable SummaryResponseDTO performThermostatSummaryQuery() {
logger.debug("API: Perform thermostat summary query");
if (!isAuthorized()) {
return null;
}
SelectionDTO selection = new SelectionDTO();
selection.selectionType = SelectionType.REGISTERED;
selection.includeEquipmentStatus = Boolean.TRUE;
String requestJson = GSON.toJson(new ThermostatRequestDTO(selection), ThermostatRequestDTO.class);
String response = executeGet(ECOBEE_THERMOSTAT_SUMMARY_URL, requestJson);
if (response != null) {
try {
SummaryResponseDTO summaryResponse = GSON.fromJson(response, SummaryResponseDTO.class);
if (isSuccess(summaryResponse)) {
return summaryResponse;
}
} catch (JsonSyntaxException e) {
logJSException(e, response);
}
}
return null;
}
public List<ThermostatDTO> queryRegisteredThermostats() {
return performThermostatQuery(null);
}
public List<ThermostatDTO> performThermostatQuery(final @Nullable Set<String> thermostatIds) {
logger.debug("API: Perform query on thermostat: '{}'", thermostatIds);
if (!isAuthorized()) {
return EMPTY_THERMOSTATS;
}
SelectionDTO selection = bridgeHandler.getSelection();
selection.setThermostats(thermostatIds);
String requestJson = GSON.toJson(new ThermostatRequestDTO(selection), ThermostatRequestDTO.class);
String response = executeGet(ECOBEE_THERMOSTAT_URL, requestJson);
if (response != null) {
try {
ThermostatResponseDTO thermostatsResponse = GSON.fromJson(response, ThermostatResponseDTO.class);
if (isSuccess(thermostatsResponse)) {
return thermostatsResponse.thermostatList;
}
} catch (JsonSyntaxException e) {
logJSException(e, response);
}
}
return EMPTY_THERMOSTATS;
}
public boolean performThermostatFunction(FunctionRequest request) {
logger.debug("API: Perform function on thermostat: '{}'", request.selection.selectionMatch);
if (!isAuthorized()) {
return false;
}
return executePost(ECOBEE_THERMOSTAT_URL, GSON.toJson(request, FunctionRequest.class));
}
public boolean performThermostatUpdate(ThermostatUpdateRequestDTO request) {
logger.debug("API: Perform update on thermostat: '{}'", request.selection.selectionMatch);
if (!isAuthorized()) {
return false;
}
return executePost(ECOBEE_THERMOSTAT_UPDATE_URL, GSON.toJson(request, ThermostatUpdateRequestDTO.class));
}
private String buildQueryUrl(String baseUrl, String requestJson) throws UnsupportedEncodingException {
final StringBuilder urlBuilder = new StringBuilder(baseUrl);
urlBuilder.append("?json=");
urlBuilder.append(URLEncoder.encode(requestJson, StandardCharsets.UTF_8.toString()));
return urlBuilder.toString();
}
private @Nullable String executeGet(String url, String json) {
String response = null;
try {
long startTime = System.currentTimeMillis();
logger.trace("API: Get Request json is '{}'", json);
response = HttpUtil.executeUrl("GET", buildQueryUrl(url, json), setHeaders(), null, null, apiTimeout);
logger.trace("API: Response took {} msec: {}", System.currentTimeMillis() - startTime, response);
} catch (IOException e) {
logIOException(e);
} catch (EcobeeAuthException e) {
logger.info("API: Unable to execute GET: {}", e.getMessage());
}
return response;
}
private boolean executePost(String url, String json) {
try {
logger.trace("API: Post request json is '{}'", json);
long startTime = System.currentTimeMillis();
String response = HttpUtil.executeUrl("POST", url, setHeaders(), new ByteArrayInputStream(json.getBytes()),
"application/json", apiTimeout);
logger.trace("API: Response took {} msec: {}", System.currentTimeMillis() - startTime, response);
try {
ThermostatResponseDTO thermostatsResponse = GSON.fromJson(response, ThermostatResponseDTO.class);
return isSuccess(thermostatsResponse);
} catch (JsonSyntaxException e) {
logJSException(e, response);
}
} catch (IOException e) {
logIOException(e);
} catch (EcobeeAuthException e) {
logger.info("API: Unable to execute POST: {}", e.getMessage());
}
return false;
}
private void logIOException(Exception e) {
Throwable rootCause = ExceptionUtils.getRootCause(e);
if (rootCause instanceof TimeoutException || rootCause instanceof EOFException) {
// These are "normal" errors and should be logged as DEBUG
logger.debug("API: Call to Ecobee API failed with exception: {}: {}", rootCause.getClass().getSimpleName(),
rootCause.getMessage());
} else {
// What's left are unexpected errors that should be logged as INFO with a full stack trace
logger.info("API: Call to Ecobee API failed", e);
}
}
private void logJSException(Exception e, String response) {
// The API sometimes returns an HTML page complaining of an SSL error
// Otherwise, this probably should be INFO level
logger.debug("API: JsonSyntaxException parsing response: {}", response, e);
}
private boolean isSuccess(@Nullable AbstractResponseDTO response) {
boolean success = true;
if (response == null) {
logger.info("API: Ecobee API returned null response");
success = false;
} else if (response.status.code.intValue() != 0) {
logger.info("API: Ecobee API returned unsuccessful status: code={}, message={}", response.status.code,
response.status.message);
if (response.status.code == ECOBEE_DEAUTHORIZED_TOKEN) {
// Token has been deauthorized, so restart the authorization process from the beginning
logger.warn("API: Reset OAuth Client Service due to deauthorized token");
deleteOAuthClientService();
createOAuthClientService();
} else if (response.status.code == ECOBEE_TOKEN_EXPIRED) {
// Check isAuthorized again to see if we can get a valid token
logger.info("API: Unable to complete API call because token is expired");
if (!isAuthorized()) {
logger.warn("API: isAuthorized was NOT successful on second try");
}
}
success = false;
}
return success;
}
private Properties setHeaders() throws EcobeeAuthException {
AccessTokenResponse atr = accessTokenResponse;
if (atr == null) {
throw new EcobeeAuthException("Can not set auth header because access token is null");
}
Properties headers = new Properties();
headers.putAll(HTTP_HEADERS);
headers.put("Authorization", "Bearer " + atr.getAccessToken());
return headers;
}
}

View File

@@ -0,0 +1,266 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.api;
import static org.openhab.binding.ecobee.internal.EcobeeBindingConstants.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.HttpResponseException;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.http.HttpStatus;
import org.openhab.binding.ecobee.internal.dto.oauth.AuthorizeResponseDTO;
import org.openhab.binding.ecobee.internal.dto.oauth.TokenResponseDTO;
import org.openhab.binding.ecobee.internal.handler.EcobeeAccountBridgeHandler;
import org.openhab.core.auth.client.oauth2.AccessTokenResponse;
import org.openhab.core.auth.client.oauth2.OAuthClientService;
import org.openhab.core.auth.client.oauth2.OAuthException;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.JsonSyntaxException;
/**
* The {@link EcobeeAuth} performs the initial OAuth authorization
* with the Ecobee authorization servers. Once this process is complete, the
* AccessTokenResponse will be imported into the OHC OAuth Client Service. At
* that point, the OHC OAuth service will be responsible for refreshing tokens.
*
* @author Mark Hilbush - Initial contribution
*/
@NonNullByDefault
public class EcobeeAuth {
private final Logger logger = LoggerFactory.getLogger(EcobeeAuth.class);
private final EcobeeAccountBridgeHandler bridgeHandler;
private final String apiKey;
private final int apiTimeout;
private final OAuthClientService oAuthClientService;
private final HttpClient httpClient;
private EcobeeAuthState state;
private @Nullable AuthorizeResponseDTO authResponse;
private long pinExpirationTime;
/**
* The authorization code needed to make the first-time request
* of the refresh and access tokens. Obtained from the call to {@code authorize()}.
*/
private @Nullable String code;
public EcobeeAuth(EcobeeAccountBridgeHandler bridgeHandler, String apiKey, int apiTimeout,
OAuthClientService oAuthClientService, HttpClient httpClient) {
this.apiKey = apiKey;
this.apiTimeout = apiTimeout;
this.oAuthClientService = oAuthClientService;
this.httpClient = httpClient;
this.bridgeHandler = bridgeHandler;
pinExpirationTime = 0;
state = EcobeeAuthState.NEED_PIN;
}
public void setState(EcobeeAuthState newState) {
if (newState != state) {
logger.debug("EcobeeAuth: Change state from {} to {}", state, newState);
state = newState;
}
}
public boolean isComplete() {
return state == EcobeeAuthState.COMPLETE;
}
public EcobeeAuthState doAuthorization() throws EcobeeAuthException {
switch (state) {
case NEED_PIN:
authorize();
break;
case NEED_TOKEN:
getTokens();
break;
case COMPLETE:
bridgeHandler.updateBridgeStatus(ThingStatus.ONLINE);
break;
}
return state;
}
/**
* Call the Ecobee authorize endpoint to get the authorization code and PIN
* that will be used a) validate the application in the the Ecobee user web portal,
* and b) make the first time request for the access and refresh tokens.
* Warnings are suppressed to avoid the Gson.fromJson warnings.
*/
@SuppressWarnings({ "null", "unused" })
private void authorize() throws EcobeeAuthException {
logger.debug("EcobeeAuth: State is {}: Executing step: 'authorize'", state);
StringBuilder url = new StringBuilder(ECOBEE_AUTHORIZE_URL);
url.append("?response_type=ecobeePin");
url.append("&client_id=").append(apiKey);
url.append("&scope=").append(ECOBEE_SCOPE);
logger.trace("EcobeeAuth: Getting authorize URL={}", url);
String response = executeUrl("GET", url.toString());
logger.trace("EcobeeAuth: Auth response: {}", response);
try {
authResponse = EcobeeApi.getGson().fromJson(response, AuthorizeResponseDTO.class);
if (authResponse == null) {
logger.debug("EcobeeAuth: Got null authorize response from Ecobee API");
setState(EcobeeAuthState.NEED_PIN);
} else {
if (StringUtils.isNotEmpty(authResponse.error)) {
throw new EcobeeAuthException(authResponse.error + ": " + authResponse.errorDescription);
}
code = authResponse.code;
writeLogMessage(authResponse.pin, authResponse.expiresIn);
setPinExpirationTime(authResponse.expiresIn.longValue());
updateBridgeStatus();
setState(EcobeeAuthState.NEED_TOKEN);
}
} catch (JsonSyntaxException e) {
logger.info("EcobeeAuth: Exception while parsing authorize response: {}", e.getMessage());
setState(EcobeeAuthState.NEED_PIN);
}
}
/**
* Call the Ecobee token endpoint to get the access and refresh tokens. Once successfully retrieved,
* the access and refresh tokens will be injected into the OHC OAuth service.
* Warnings are suppressed to avoid the Gson.fromJson warnings.
*/
@SuppressWarnings({ "null", "unused" })
private void getTokens() throws EcobeeAuthException {
logger.debug("EcobeeAuth: State is {}: Executing step: 'getToken'", state);
StringBuilder url = new StringBuilder(ECOBEE_TOKEN_URL);
url.append("?grant_type=ecobeePin");
url.append("&code=").append(code);
url.append("&client_id=").append(apiKey);
logger.trace("EcobeeAuth: Posting token URL={}", url);
String response = executeUrl("POST", url.toString());
logger.trace("EcobeeAuth: Got a valid token response: {}", response);
TokenResponseDTO tokenResponse = EcobeeApi.getGson().fromJson(response, TokenResponseDTO.class);
if (tokenResponse == null) {
logger.debug("EcobeeAuth: Got null token response from Ecobee API");
updateBridgeStatus();
setState(isPinExpired() ? EcobeeAuthState.NEED_PIN : EcobeeAuthState.NEED_TOKEN);
return;
}
if (StringUtils.isNotEmpty(tokenResponse.error)) {
throw new EcobeeAuthException(tokenResponse.error + ": " + tokenResponse.errorDescription);
}
AccessTokenResponse accessTokenResponse = new AccessTokenResponse();
accessTokenResponse.setRefreshToken(tokenResponse.refreshToken);
accessTokenResponse.setAccessToken(tokenResponse.accessToken);
accessTokenResponse.setScope(tokenResponse.scope);
accessTokenResponse.setTokenType(tokenResponse.tokenType);
accessTokenResponse.setExpiresIn(tokenResponse.expiresIn);
try {
logger.debug("EcobeeAuth: Importing AccessTokenResponse into oAuthClientService!!!");
oAuthClientService.importAccessTokenResponse(accessTokenResponse);
bridgeHandler.updateBridgeStatus(ThingStatus.ONLINE);
setState(EcobeeAuthState.COMPLETE);
return;
} catch (OAuthException e) {
logger.info("EcobeeAuth: Got OAuthException", e);
// No other processing needed here
}
updateBridgeStatus();
setState(isPinExpired() ? EcobeeAuthState.NEED_PIN : EcobeeAuthState.NEED_TOKEN);
}
private void writeLogMessage(String pin, Integer expiresIn) {
logger.info("#################################################################");
logger.info("# Ecobee: U S E R I N T E R A C T I O N R E Q U I R E D !!");
logger.info("# Go to the Ecobee web portal, then:");
logger.info("# Enter PIN '{}' in My Apps within {} minutes.", pin, expiresIn);
logger.info("# NOTE: All API attempts will fail in the meantime.");
logger.info("#################################################################");
}
private void updateBridgeStatus() {
AuthorizeResponseDTO response = authResponse;
if (response != null) {
bridgeHandler.updateBridgeStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_PENDING,
String.format("Enter PIN '%s' in MyApps. PIN expires in %d minutes", response.pin,
getMinutesUntilPinExpiration()));
}
}
private void setPinExpirationTime(long expiresIn) {
pinExpirationTime = expiresIn + TimeUnit.MILLISECONDS.toMinutes(System.currentTimeMillis());
}
private long getMinutesUntilPinExpiration() {
return pinExpirationTime - TimeUnit.MILLISECONDS.toMinutes(System.currentTimeMillis());
}
private boolean isPinExpired() {
return getMinutesUntilPinExpiration() <= 0;
}
private @Nullable String executeUrl(String method, String url) {
Request request = httpClient.newRequest(url);
request.timeout(apiTimeout, TimeUnit.MILLISECONDS);
request.method(method);
EcobeeApi.HTTP_HEADERS.forEach((k, v) -> request.header((String) k, (String) v));
try {
ContentResponse contentResponse = request.send();
switch (contentResponse.getStatus()) {
case HttpStatus.OK_200:
return contentResponse.getContentAsString();
case HttpStatus.BAD_REQUEST_400:
logger.debug("BAD REQUEST(400) response received: {}", contentResponse.getContentAsString());
return contentResponse.getContentAsString();
case HttpStatus.UNAUTHORIZED_401:
logger.debug("UNAUTHORIZED(401) response received: {}", contentResponse.getContentAsString());
return contentResponse.getContentAsString();
case HttpStatus.NO_CONTENT_204:
logger.debug("HTTP response 204: No content. Check configuration");
break;
default:
logger.debug("HTTP {} failed: {}, {}", method, contentResponse.getStatus(),
contentResponse.getReason());
break;
}
} catch (TimeoutException e) {
logger.debug("TimeoutException: Call to Ecobee API timed out");
} catch (ExecutionException e) {
if (ExceptionUtils.getRootCause(e) instanceof HttpResponseException) {
logger.info("Awaiting entry of PIN in Ecobee portal. Expires in {} minutes",
getMinutesUntilPinExpiration());
} else {
logger.debug("ExecutionException on call to Ecobee authorization API", e);
}
} catch (InterruptedException e) {
logger.debug("InterruptedException on call to Ecobee authorization API: {}", e.getMessage());
}
return null;
}
}

View File

@@ -0,0 +1,35 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.api;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link EcobeeAuthException} is thrown during the Ecobee PIN authorization process.
*
* @author Mark Hilbush - Initial contribution
*/
@NonNullByDefault
public class EcobeeAuthException extends Exception {
private static final long serialVersionUID = 1L;
/**
* Constructor.
*
* @param message Ecobee error message
*/
public EcobeeAuthException(String message) {
super(message);
}
}

View File

@@ -0,0 +1,43 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.api;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The {@link EcobeeAuthState} represents that steps in the Ecobee PIN
* authorization process.
*
* @author Mark Hilbush - Initial contribution
*/
@NonNullByDefault
enum EcobeeAuthState {
/*
* This is the initial state. It indicates that an "authorize" API call is needed to get
* the Ecobee PIN.
*/
NEED_PIN,
/*
* This state indicates that an Ecobee PIN request was successful, and that a "token" API
* call is needed to complete the authorization and get the refresh and access tokens. In
* order to get the tokens, the user must authorize the application by entering the PIN
* into the Ecobee web portal.
*/
NEED_TOKEN,
/*
* This state indicates that the "authorize" and "token" steps were successful.
*/
COMPLETE;
}

View File

@@ -0,0 +1,56 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.config;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
/**
* The {@link EcobeeAccountConfiguration} class contains fields mapping
* to the account thing configuration parameters.
*
* @author Mark Hilbush - Initial contribution
*/
@NonNullByDefault
public class EcobeeAccountConfiguration {
/**
* Ecobee API key
*/
public @Nullable String apiKey;
/**
* Time in seconds between information refresh
*/
public @Nullable Integer refreshIntervalNormal;
/**
* Time in seconds to wait after successful update, command or action before refresh
*/
public @Nullable Integer refreshIntervalQuick;
/**
* Time in seconds to allow API request to complete
*/
public @Nullable Integer apiTimeout;
/*
* Enable/disable automatic discovery
*/
public @Nullable Boolean discoveryEnabled;
/**
* Interval with which to run the thermostat discovery process
*/
public @Nullable Integer discoveryInterval;
}

View File

@@ -0,0 +1,31 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.config;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
/**
* The {@link EcobeeSensorConfiguration} class contains fields mapping
* to the sensor thing configuration parameters.
*
* @author Mark Hilbush - Initial contribution
*/
@NonNullByDefault
public class EcobeeSensorConfiguration {
/**
* Id of remote sensor
*/
public @Nullable String sensorId;
}

View File

@@ -0,0 +1,31 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.config;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
/**
* The {@link EcobeeThermostatConfiguration} class contains fields mapping
* to the thermostat thing configuration parameters.
*
* @author Mark Hilbush - Initial contribution
*/
@NonNullByDefault
public class EcobeeThermostatConfiguration {
/**
* Thermostat ID assigned by Ecobee
*/
public @Nullable String thermostatId;
}

View File

@@ -0,0 +1,124 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.discovery;
import static org.openhab.binding.ecobee.internal.EcobeeBindingConstants.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.ecobee.internal.dto.thermostat.RemoteSensorDTO;
import org.openhab.binding.ecobee.internal.handler.EcobeeThermostatBridgeHandler;
import org.openhab.core.config.discovery.AbstractDiscoveryService;
import org.openhab.core.config.discovery.DiscoveryResult;
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
import org.openhab.core.config.discovery.DiscoveryService;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.ThingUID;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link SensorDiscoveryService} is responsible for discovering the Ecobee
* sensors that are assigned to a thermostat.
*
* @author Mark Hilbush - Initial contribution
*/
@NonNullByDefault
public class SensorDiscoveryService extends AbstractDiscoveryService implements DiscoveryService, ThingHandlerService {
private final Logger logger = LoggerFactory.getLogger(SensorDiscoveryService.class);
private @NonNullByDefault({}) EcobeeThermostatBridgeHandler bridgeHandler;
public SensorDiscoveryService() {
super(30);
}
@Override
public void setThingHandler(@Nullable ThingHandler handler) {
if (handler instanceof EcobeeThermostatBridgeHandler) {
((EcobeeThermostatBridgeHandler) handler).setDiscoveryService(this);
bridgeHandler = (EcobeeThermostatBridgeHandler) handler;
}
}
@Override
public @Nullable ThingHandler getThingHandler() {
return bridgeHandler;
}
@Override
public void activate() {
logger.debug("SensorDiscovery: Activating Ecobee sensor discovery service");
}
@Override
public void deactivate() {
logger.debug("SensorDiscovery: Deactivating Ecobee sensor discovery service");
}
@Override
public Set<ThingTypeUID> getSupportedThingTypes() {
return SUPPORTED_SENSOR_THING_TYPES_UIDS;
}
@Override
public void startBackgroundDiscovery() {
logger.debug("SensorDiscovery: Performing background discovery scan for {}", bridgeHandler.getThing().getUID());
discoverSensors();
}
@Override
public void startScan() {
logger.debug("SensorDiscovery: Starting discovery scan for {}", bridgeHandler.getThing().getUID());
discoverSensors();
}
@Override
public synchronized void abortScan() {
super.abortScan();
}
@Override
protected synchronized void stopScan() {
super.stopScan();
}
private String buildLabel(String name) {
return String.format("Ecobee Sensor %s", name);
}
private synchronized void discoverSensors() {
for (RemoteSensorDTO sensor : bridgeHandler.getSensors()) {
ThingUID bridgeUID = bridgeHandler.getThing().getUID();
ThingUID sensorUID = new ThingUID(UID_SENSOR_THING, bridgeUID, sensor.id.replace(":", "-"));
thingDiscovered(createDiscoveryResult(sensorUID, bridgeUID, sensor));
logger.trace("SensorDiscovery: Sensor with id '{}' and name '{}' added to Inbox with UID '{}'", sensor.id,
sensor.name, sensorUID);
}
}
private DiscoveryResult createDiscoveryResult(ThingUID sensorUID, ThingUID bridgeUID, RemoteSensorDTO sensor) {
Map<String, Object> properties = new HashMap<>(2);
properties.put(CONFIG_SENSOR_ID, sensor.id);
return DiscoveryResultBuilder.create(sensorUID).withProperties(properties)
.withRepresentationProperty(CONFIG_SENSOR_ID).withBridge(bridgeUID).withLabel(buildLabel(sensor.name))
.build();
}
}

View File

@@ -0,0 +1,132 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.discovery;
import static org.openhab.binding.ecobee.internal.EcobeeBindingConstants.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.ecobee.internal.dto.thermostat.ThermostatDTO;
import org.openhab.binding.ecobee.internal.handler.EcobeeAccountBridgeHandler;
import org.openhab.core.config.discovery.AbstractDiscoveryService;
import org.openhab.core.config.discovery.DiscoveryResult;
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
import org.openhab.core.config.discovery.DiscoveryService;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.ThingUID;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link ThermostatDiscoveryService} is responsible for discovering the Ecobee
* thermostats that are associated with the Ecobee Account.
*
* @author Mark Hilbush - Initial contribution
*/
@NonNullByDefault
public class ThermostatDiscoveryService extends AbstractDiscoveryService
implements DiscoveryService, ThingHandlerService {
private final Logger logger = LoggerFactory.getLogger(ThermostatDiscoveryService.class);
private @NonNullByDefault({}) EcobeeAccountBridgeHandler bridgeHandler;
public ThermostatDiscoveryService() {
super(30);
}
@Override
public void setThingHandler(@Nullable ThingHandler handler) {
if (handler instanceof EcobeeAccountBridgeHandler) {
this.bridgeHandler = (EcobeeAccountBridgeHandler) handler;
this.bridgeHandler.setDiscoveryService(this);
}
}
@Override
public @Nullable ThingHandler getThingHandler() {
return bridgeHandler;
}
@Override
public void activate() {
logger.debug("ThermostatDiscovery: Activating Ecobee thermostat discovery service for {}",
bridgeHandler.getThing().getUID());
}
@Override
public void deactivate() {
logger.debug("ThermostatDiscovery: Deactivating Ecobee thermostat discovery service for {}",
bridgeHandler.getThing().getUID());
}
@Override
public Set<ThingTypeUID> getSupportedThingTypes() {
return SUPPORTED_THERMOSTAT_BRIDGE_THING_TYPES_UIDS;
}
@Override
public void startBackgroundDiscovery() {
logger.trace("ThermostatDiscovery: Performing background discovery scan for {}",
bridgeHandler.getThing().getUID());
discoverThermostats();
}
@Override
public void startScan() {
logger.debug("ThermostatDiscovery: Starting discovery scan for {}", bridgeHandler.getThing().getUID());
discoverThermostats();
}
@Override
public synchronized void abortScan() {
super.abortScan();
}
@Override
protected synchronized void stopScan() {
super.stopScan();
}
private String buildLabel(String name) {
return String.format("Ecobee Thermostat %s", name);
}
private synchronized void discoverThermostats() {
for (ThermostatDTO thermostat : bridgeHandler.getRegisteredThermostats()) {
String name = thermostat.name;
String identifier = thermostat.identifier;
if (identifier != null && name != null) {
ThingUID thingUID = new ThingUID(UID_THERMOSTAT_BRIDGE, bridgeHandler.getThing().getUID(),
thermostat.identifier);
thingDiscovered(createDiscoveryResult(thingUID, identifier, name));
logger.trace("ThermostatDiscovery: Thermostat with id '{}' and name '{}' added to Inbox with UID '{}'",
thermostat.identifier, thermostat.name, thingUID);
}
}
}
private DiscoveryResult createDiscoveryResult(ThingUID thermostatUID, String identifier, String name) {
Map<String, Object> properties = new HashMap<>(2);
properties.put(CONFIG_THERMOSTAT_ID, identifier);
return DiscoveryResultBuilder.create(thermostatUID).withProperties(properties)
.withRepresentationProperty(CONFIG_THERMOSTAT_ID).withBridge(bridgeHandler.getThing().getUID())
.withLabel(buildLabel(name)).build();
}
}

View File

@@ -0,0 +1,36 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.dto;
/**
* The {@link AbstractResponseDTO} represents the common objects included in
* all responses.
*
* @author Mark Hilbush - Initial contribution
*/
public abstract class AbstractResponseDTO {
/*
* The Page object is optional and will only appear for responses which
* can be paged. It will not appear for responses which do not contain pageable content.
*/
public PageDTO page;
/*
* The Status object contains the response code for the request. It will also contain
* an appropriate message when an error occurs. The status is always returned from all
* GET and POST calls. A non-zero code means that an error occurred. Refer to the Response
* Codes section for details of each error which may be returned.
*/
public StatusDTO status;
}

View File

@@ -0,0 +1,33 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.dto;
/**
* The {@link AbstractSelectionDTO} represents the common objects included in
* all requests.
*
* @author Mark Hilbush - Initial contribution
*/
public class AbstractSelectionDTO {
/*
* The type of match data supplied: Values: thermostats, registered, managementSet.
*/
public String selectionType;
/*
* The match data based on selectionType (e.g. a comma-separated list of thermostat
* idendifiers in the case of a selectionType of thermostats)
*/
public String selectionMatch;
}

View File

@@ -0,0 +1,42 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.dto;
/**
* The {@link PageDTO} is optional and will only appear for responses which
* can be paged. It will not appear for responses which do not contain pageable content.
*
* @author Mark Hilbush - Initial contribution
*/
public class PageDTO {
/*
* The page retrieved or, in the case of a request parameter, the specific page requested.
*/
public Integer page;
/*
* The total pages available.
*/
public Integer totalPages;
/*
* The number of objects on this page.
*/
public Integer pageSize;
/*
* The total number of objects available.
*/
public Integer total;
}

View File

@@ -0,0 +1,246 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.dto;
import java.util.Set;
/**
* The {@link SelectionDTO} defines the resources and information to return
* as part of a response. The selection is required in all requests however
* meaning of some selection fields is only meaningful to certain types of requests.
*
* The selectionType parameter defines the type of selection to perform. The selectionMatch
* specifies the matching criteria for the type specified.
*
* @author Mark Hilbush - Initial contribution
*/
public class SelectionDTO {
/*
* The type of match data supplied: Values: thermostats, registered, managementSet.
*/
public SelectionType selectionType;
/*
* The match data based on selectionType (e.g. a comma-separated list of thermostat
* idendifiers in the case of a selectionType of thermostats)
*/
public String selectionMatch;
/*
* Include the thermostat's unacknowledged alert objects. If not specified, defaults to false.
*/
public Boolean includeAlerts;
/*
* Include the audio configuration for the selected Thermostat(s). If not specified, defaults to false.
*/
public Boolean includeAudio;
/*
* Include the thermostat device configuration objects. If not specified, defaults to false.
*/
public Boolean includeDevice;
/*
* Include the electricity readings object. If not specified, defaults to false.
*/
public Boolean includeElectricity;
/*
* Include the energy configuration for the selected Thermostat(s). If not specified, defaults to false.
*/
public Boolean includeEnergy;
/*
* Include the current thermostat equipment status information. If not specified, defaults to false.
*/
public Boolean includeEquipmentStatus;
/*
* Include the thermostat calendar events objects. If not specified, defaults to false.
*/
public Boolean includeEvents;
/*
* Include the extended thermostat runtime object. If not specified, defaults to false.
*/
public Boolean includeExtendedRuntime;
/*
* Include the current thermostat house details object. If not specified, defaults to false.
*/
public Boolean includeHouseDetails;
/*
* Include the thermostat location object. If not specified, defaults to false.
*/
public Boolean includeLocation;
/*
* Include the thermostat management company object. If not specified, defaults to false.
*/
public Boolean includeManagement;
/*
* Include the current thermostat alert and reminders settings. If not specified, defaults to false.
*/
public Boolean includeNotificationSettings;
/*
* Include the current thermostat OemCfg object. If not specified, defaults to false.
*/
public Boolean includeOemCfg;
/*
* Include the current thermostat privacy settings. Note: access to this object is restricted
* to callers with implict authentication, setting this value to true without proper
* credentials will result in an authentication exception.
*/
public Boolean includePrivacy;
/*
* Include the thermostat program object. If not specified, defaults to false.
*/
public Boolean includeProgram;
/*
* Include the thermostat reminder object. If not specified, defaults to false.
*/
public Boolean includeReminders;
/*
* Include the thermostat runtime object. If not specified, defaults to false.
*/
public Boolean includeRuntime;
/*
* Include the current securitySettings object for the selected Thermostat(s). If not specified, defaults to false.
*/
public Boolean includeSecuritySettings;
/*
* Include the list of current thermostatRemoteSensor objects for the selected Thermostat(s).
* If not specified, defaults to false.
*/
public Boolean includeSensors;
/*
* Include the thermostat settings object. If not specified, defaults to false.
*/
public Boolean includeSettings;
/*
* Include the thermostat technician object. If not specified, defaults to false.
*/
public Boolean includeTechnician;
/*
* Include the thermostat utility company object. If not specified, defaults to false.
*/
public Boolean includeUtility;
/*
* Include the current firmware version the Thermostat is running. If not specified, defaults to false.
*/
public Boolean includeVersion;
/*
* Include the current thermostat weather forecast object. If not specified, defaults to false.
*/
public Boolean includeWeather;
public SelectionDTO() {
selectionType = SelectionType.REGISTERED;
}
public void setThermostats(Set<String> thermostatIds) {
boolean isRegistered = thermostatIds == null || thermostatIds.isEmpty();
selectionType = isRegistered ? SelectionType.REGISTERED : SelectionType.THERMOSTATS;
selectionMatch = isRegistered ? "" : String.join(",", thermostatIds);
}
public void setSelectionType(SelectionType selectionType) {
this.selectionType = selectionType;
}
/**
* Merge this selection object with the one passed in as a parameter.
*
* @param selection
* @return A SelectionDTO object representing the merged selection objects
*/
public SelectionDTO mergeSelection(SelectionDTO selection) {
// Always get alerts, equipmentStatus, events, program, runtime, and sensors
this.includeAlerts = Boolean.TRUE;
this.includeEquipmentStatus = Boolean.TRUE;
this.includeEvents = Boolean.TRUE;
this.includeProgram = Boolean.TRUE;
this.includeRuntime = Boolean.TRUE;
this.includeSensors = Boolean.TRUE;
this.includeAudio = selection.includeAudio == Boolean.TRUE ? Boolean.TRUE : includeAudio;
this.includeDevice = selection.includeDevice == Boolean.TRUE ? Boolean.TRUE : includeDevice;
this.includeElectricity = selection.includeElectricity == Boolean.TRUE ? Boolean.TRUE : includeElectricity;
this.includeEnergy = selection.includeEnergy == Boolean.TRUE ? Boolean.TRUE : includeEnergy;
this.includeExtendedRuntime = selection.includeExtendedRuntime == Boolean.TRUE ? Boolean.TRUE
: includeExtendedRuntime;
this.includeHouseDetails = selection.includeHouseDetails == Boolean.TRUE ? Boolean.TRUE : includeHouseDetails;
this.includeLocation = selection.includeLocation == Boolean.TRUE ? Boolean.TRUE : includeLocation;
this.includeManagement = selection.includeManagement == Boolean.TRUE ? Boolean.TRUE : includeManagement;
this.includeNotificationSettings = selection.includeNotificationSettings == Boolean.TRUE ? Boolean.TRUE
: includeNotificationSettings;
this.includeOemCfg = selection.includeOemCfg == Boolean.TRUE ? Boolean.TRUE : includeOemCfg;
this.includePrivacy = selection.includePrivacy == Boolean.TRUE ? Boolean.TRUE : includePrivacy;
this.includeReminders = selection.includeReminders == Boolean.TRUE ? Boolean.TRUE : includeReminders;
this.includeSecuritySettings = selection.includeSecuritySettings == Boolean.TRUE ? Boolean.TRUE
: includeSecuritySettings;
this.includeSettings = selection.includeSettings == Boolean.TRUE ? Boolean.TRUE : includeSettings;
this.includeTechnician = selection.includeTechnician == Boolean.TRUE ? Boolean.TRUE : includeTechnician;
this.includeUtility = selection.includeUtility == Boolean.TRUE ? Boolean.TRUE : includeUtility;
this.includeVersion = selection.includeVersion == Boolean.TRUE ? Boolean.TRUE : includeVersion;
this.includeWeather = selection.includeWeather == Boolean.TRUE ? Boolean.TRUE : includeWeather;
return this;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("selectionType=").append(selectionType).append(",");
sb.append("selectionMatch=").append(selectionMatch).append(",");
sb.append("includeAlerts=").append(includeAlerts).append(",");
sb.append("includeAudio=").append(includeAudio).append(",");
sb.append("includeDevice=").append(includeDevice).append(",");
sb.append("includeElectricity=").append(includeElectricity).append(",");
sb.append("includeEnergy=").append(includeEnergy).append(",");
sb.append("includeEquipmentStatus=").append(includeEquipmentStatus).append(",");
sb.append("includeExtendedRuntime=").append(includeExtendedRuntime).append(",");
sb.append("includeHouseDetails=").append(includeHouseDetails).append(",");
sb.append("includeLocation=").append(includeLocation).append(",");
sb.append("includeManagement=").append(includeManagement).append(",");
sb.append("includeNotificationSettings=").append(includeNotificationSettings).append(",");
sb.append("includeOemCfg=").append(includeOemCfg).append(",");
sb.append("includePrivacy=").append(includePrivacy).append(",");
sb.append("includeProgram=").append(includeProgram).append(",");
sb.append("includeReminders=").append(includeReminders).append(",");
sb.append("includeRuntime=").append(includeRuntime).append(",");
sb.append("includeSecuritySettings=").append(includeSecuritySettings).append(",");
sb.append("includeSensors=").append(includeSensors).append(",");
sb.append("includeSettings=").append(includeSettings).append(",");
sb.append("includeTechnician=").append(includeTechnician).append(",");
sb.append("includeUtility=").append(includeUtility).append(",");
sb.append("includeVersion=").append(includeVersion).append(",");
sb.append("includeWeather=").append(includeWeather);
return sb.toString();
}
}

View File

@@ -0,0 +1,72 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.dto;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import com.google.gson.annotations.SerializedName;
/**
* The {@link SelectionType} represents the valid selection types that can be passed in
* a SelectionDTO object.
*
* @author Mark Hilbush - Initial contribution
*/
@NonNullByDefault
public enum SelectionType {
/*
* Select only those thermostats listed in the CSV match criteria. No spaces in the CSV string. There is a limit
* of 25 identifiers per request.
*/
@SerializedName("thermostats")
THERMOSTATS("thermostats"),
/*
* When this is set the thermostats registered to the current user will be returned. This is only usable with
* Smart thermostats registered to a user. It does not work on EMS thermostats and may not be used by a Utility
* who is not the owner of thermostats.
*/
@SerializedName("registered")
REGISTERED("registered"),
/*
* Selects all thermostats for a given management set defined by the Management/Utility account. This is only
* available to Management/Utility accounts. "/" is the root, represented by the "My Sets" set.
*/
@SerializedName("managementSet")
MANAGEMENT_SET("managementSet");
private final String type;
private SelectionType(final String type) {
this.type = type;
}
public static SelectionType forValue(@Nullable String v) {
if (v != null) {
for (SelectionType at : SelectionType.values()) {
if (at.type.equals(v)) {
return at;
}
}
}
throw new IllegalArgumentException("Invalid or null selection type: " + v);
}
@Override
public String toString() {
return this.type;
}
}

View File

@@ -0,0 +1,34 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.dto;
/**
* The {@link StatusDTO} The StatusDTO object contains the processing status of
* the request. It will contain any relevant error information should an error
* occur. The status object is returned with every response regardless of success or failure status. It is suitable for
* logging request failures.
*
* @author Mark Hilbush - Initial contribution
*/
public class StatusDTO {
/*
* The status code for this status. Success is indicated by a code of 0.
*/
public Integer code;
/*
* The detailed message for this status.
*/
public String message;
}

View File

@@ -0,0 +1,41 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.dto.oauth;
import com.google.gson.annotations.SerializedName;
/**
* The {@link AbstractAuthResponseDTO} represents the common fields returned in all auth responses.
*
* @author Mark Hilbush - Initial contribution
*/
public abstract class AbstractAuthResponseDTO {
/*
* Error code.
*/
@SerializedName("error")
public String error;
/*
* Textual description of error.
*/
@SerializedName("error_description")
public String errorDescription;
/*
* URI referencing OAuth documentation.
*/
@SerializedName("error_uri")
public String errorURI;
}

View File

@@ -0,0 +1,53 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.dto.oauth;
import com.google.gson.annotations.SerializedName;
/**
* The {@link AuthorizeResponseDTO} is responsible for
*
* @author Mark Hilbush - Initial contribution
*/
public class AuthorizeResponseDTO extends AbstractAuthResponseDTO {
/*
* The PIN a user enters in the web portal.
*/
@SerializedName("ecobeePin")
public String pin;
/*
* The authorization token needed to request the access and refresh tokens.
*/
@SerializedName("code")
public String code;
/*
* The requested Scope from the original request. This must match the original request.
*/
@SerializedName("scope")
public String scope;
/*
* The number of minutes until the PIN expires. Ensure you inform the user how much time they have.
*/
@SerializedName("expires_in")
public Integer expiresIn;
/*
* The minimum amount of seconds which must pass between polling attempts for a token.
*/
@SerializedName("interval")
public Integer interval;
}

View File

@@ -0,0 +1,53 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.dto.oauth;
import com.google.gson.annotations.SerializedName;
/**
* The {@link TokenResponseDTO} is responsible for
*
* @author Mark Hilbush - Initial contribution
*/
public class TokenResponseDTO extends AbstractAuthResponseDTO {
/*
* Access token to be used in future API requests.
*/
@SerializedName("access_token")
public String accessToken;
/*
* Contains the string "Bearer"
*/
@SerializedName("token_type")
public String tokenType;
/*
* Number of seconds until the access token will expire.
*/
@SerializedName("expires_in")
public Integer expiresIn;
/*
* Token used to request a new access token.
*/
@SerializedName("refresh_token")
public String refreshToken;
/*
* Matches the scope included in the token request.
*/
@SerializedName("scope")
public String scope;
}

View File

@@ -0,0 +1,104 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.dto.thermostat;
/**
* The {@link AlertDTO} The Alert object represents an alert generated either
* by a thermostat or user which requires user attention. It may be an error,
* or a reminder for a filter change. Alerts may not be modified directly but
* rather they must be acknowledged using the Acknowledge Function.
*
* @author Mark Hilbush - Initial contribution
*/
public class AlertDTO {
/*
*
*/
public String acknowledgeRef;
/*
*
*/
public String date;
/*
*
*/
public String time;
/*
*
*/
public String severity;
/*
*
*/
public String text;
/*
*
*/
public Integer alertNumber;
/*
*
*/
public String alertType;
/*
*
*/
public Boolean isOperatorAlert;
/*
*
*/
public String reminder;
/*
*
*/
public Boolean showIdt;
/*
*
*/
public Boolean showWeb;
/*
*
*/
public Boolean sendEmail;
/*
*
*/
public String acknowledgement;
/*
*
*/
public Boolean remindMeLater;
/*
*
*/
public String thermostatIdentifier;
/*
*
*/
public String notificationType;
}

View File

@@ -0,0 +1,53 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.dto.thermostat;
import java.util.List;
/**
* The {@link AudioDTO} contains all the audio properties of the thermostat
* (only applicable to ecobee4).
*
* All read-only except for voiceEngines.
*
* @author Mark Hilbush - Initial contribution
*/
public class AudioDTO {
/*
* The volume level for audio playback. This includes volume of the voice assistant. A value between 0 and 100.
*/
public Integer playbackVolume;
/*
* Turn microphone (privacy mode) on and off.
*/
public Boolean microphoneEnabled;
/*
* The volume level for alerts on the thermostat. A value between 0 and 10, with 0 meaning 'off' - the zero
* value may not be honored by all ecobee versions.
*/
public Integer soundAlertVolume;
/*
* The volume level for key presses on the thermostat. A value between 0 and 10, with 0 meaning 'off' - the
* zero value may not be honored by all ecobee versions.
*/
public Integer soundTickVolume;
/*
* The list of voice engines compatible with the selected thermostat.
*/
public List<VoiceEngineDTO> voiceEngines;
}

View File

@@ -0,0 +1,104 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.dto.thermostat;
import java.util.List;
/**
* The {@link ClimateDTO} maps to the thermostat's Climate object.
*
* @see <a href="https://www.ecobee.com/home/developer/api/documentation/v1/objects/Climate.shtml">Climate</a>
*
* @author Mark Hilbush - Initial contribution
*/
public class ClimateDTO {
/*
* The unique climate name. The name may be changed without affecting the
* program integrity so long as uniqueness is maintained.
*/
public String name;
/*
* The unique climate identifier. Changing the identifier is not possible
* and it is generated on the server for each climate. If this value is
* not supplied a new climate will be created. For the default climates
* and existing user created climates the climateRef should be
* supplied - see note above.
*/
public String climateRef;
/*
* A flag indicating whether the property is occupied by persons during this climate
*/
public Boolean isOccupied;
/*
* A flag indicating whether ecobee optimized climate settings are used by this climate.
*/
public Boolean isOptimized;
/*
* The cooling fan mode. Default: on. Values: auto, on.
*/
public String coolFan;
/*
* The heating fan mode. Default: on. Values: auto, on.
*/
public String heatFan;
/*
* The ventilator mode. Default: off. Values: auto, minontime, on, off.
*/
public String vent;
/*
* The minimum time, in minutes, to run the ventilator each hour.
*/
public Integer ventilatorMinOnTime;
/*
* The climate owner. Default: system. Values: adHoc, demandResponse, quickSave,
* sensorAction, switchOccupancy, system, template, user.
*/
public String owner;
/*
* The type of climate. Default: program. Values: calendarEvent, program.
*/
public String type;
/*
* The integer conversion of the HEX color value used to display this
* climate on the thermostat and on the web portal.
*/
public Integer colour;
/*
* The cool temperature for this climate.
*/
public Integer coolTemp;
/*
* The heat temperature for this climate.
*/
public Integer heatTemp;
/*
* The list of sensors in use for the specific climate. The sensors listed here
* are used for temperature averaging within that climate. Only the sensorId
* and name are listed in the climate.
*/
public List<RemoteSensorDTO> sensors;
}

View File

@@ -0,0 +1,44 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.dto.thermostat;
import java.util.List;
/**
* The {@link DeviceDTO} represents a device attached to the thermostat. Devices may
* not be modified remotely, all changes must occur on the thermostat.
*
* @author Mark Hilbush - Initial contribution
*/
public class DeviceDTO {
/*
* A unique ID for the device.
*/
public Integer deviceId;
/*
* The user supplied device name.
*/
public String name;
/*
* The list of Sensor Objects associated with the device.
*/
public List<Object> sensors;
/*
* The list of Output Objects associated with the device.
*/
public List<Object> outputs;
}

View File

@@ -0,0 +1,31 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.dto.thermostat;
import java.util.List;
/**
* The {@link ElectricityDTO} contains the last collected electricity usage
* measurements for the thermostat. An electricity object is composed of
* Electricity Devices, each of which contains readings from an Electricity Tier.
*
* @author Mark Hilbush - Initial contribution
*/
public class ElectricityDTO {
/*
* The list of ElectricityDevice objects associated with the thermostat, each representing
* a device such as an electric meter or remote load control.
*/
public List<ElectricityDeviceDTO> electricityDevices;
}

View File

@@ -0,0 +1,52 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.dto.thermostat;
import java.util.List;
/**
* The {@link ElectricityDeviceDTO} represents an energy recording device. At this time,
* only meters are supported by the API.
*
* @author Mark Hilbush - Initial contribution
*/
public class ElectricityDeviceDTO {
/*
* The name of the device.
*/
public String name;
/*
* The list of Electricity Tiers containing the break down of daily electricity
* consumption of the device for the day, broken down per pricing tier.
*/
public List<ElectricityTierDTO> tiers;
/*
* The last date/time the reading was updated in UTC time.
*/
public String lastUpdate;
/*
* The last three daily electricity cost reads from the device in cents with a
* three decimal place precision.
*/
public List<String> cost;
/*
* The last three daily electricity consumption reads from the device in KWh
* with a three decimal place precision.
*/
public List<String> consumption;
}

View File

@@ -0,0 +1,39 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.dto.thermostat;
/**
* The {@link ElectricityTierDTO} is responsible for
*
* @author Mark Hilbush - Initial contribution
*/
public class ElectricityTierDTO {
/*
* The tier name as defined by the Utility. May be an empty string if
* the tier is undefined or the usage falls outside the defined tiers.
*/
public String name;
/*
* The last daily consumption reading collected. The reading format and precision
* is to three decimal places in kWh.
*/
public String consumption;
/*
* The daily cumulative tier cost in dollars if defined by the Utility. May
* be an empty string if undefined.
*/
public String cost;
}

View File

@@ -0,0 +1,22 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.dto.thermostat;
/**
* The {@link EnergyDTO} is undefined in the Ecobee API spec.
*
* @author Mark Hilbush - Initial contribution
*/
public class EnergyDTO {
}

View File

@@ -0,0 +1,70 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.dto.thermostat;
import org.openhab.binding.ecobee.internal.enums.EquipmentNotificationType;
/**
* The {@link EquipmentSettingDTO} represents the alert/reminder type which is associated
* with and dependent upon specific equipment controlled by the Thermostat. It is used
* when getting/setting the Thermostat NotificationSettings object. Note: Only the notification
* settings for the equipment/devices currently controlled by the Thermostat are returned during
* GET request, and only those same settings can be updated using the POST request. The type
* corresponds to the Alert.notificationType returned when alerts are also included in the
* selection. See Alert for more information.
*
* @author Mark Hilbush - Initial contribution
*/
public class EquipmentSettingDTO {
/*
* The date the filter was last changed for this equipment. String format: YYYY-MM-DD
*/
public String filterLastChanged;
/*
* The value representing the life of the filter. This value is expressed in month or hour,
* which is specified in the the filterLifeUnits property.
*/
public Integer filterLife;
/*
* The units the filterLife field is measured in. Possible values are: month, hour. month
* has a range of 1 - 12. hour has a range of 100 - 10000.
*/
public String filterLifeUnits;
/*
* The date the reminder will be triggered. This is a read-only field and cannot be modified
* through the API. The value is calculated and set by the thermostat.
*/
public String remindMeDate;
/*
* Boolean value representing whether or not alerts/reminders are enabled for this
* notification type or not.
*/
public Boolean enabled;
/*
* The type of notification. Possible values are: hvac, furnaceFilter, humidifierFilter,
* dehumidifierFilter, ventilator, ac, airFilter, airCleaner, uvLamp
*/
public EquipmentNotificationType type;
/*
* Boolean value representing whether or not alerts/reminders should be sent to the
* technician/contractor assoicated with the thermostat.
*/
public Boolean remindTechnician;
}

View File

@@ -0,0 +1,172 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.dto.thermostat;
/**
* The {@link EventDTO} represents a scheduled thermostat
* program change. All events have a start and end time during which the
* thermostat runtime settings will be modified. Events may not be directly
* modified, various Functions provide the capability to modify the calendar
* events and to modify the program. The event list is sorted with events
* ordered by whether they are currently running and the internal priority
* of each event. It is safe to take the first event which is running and
* show it as the currently running event. When the resume function is used,
* events are removed in the order they are listed here.
*
* @author Mark Hilbush - Initial contribution
*/
public class EventDTO {
/*
* The type of event. Values: hold, demandResponse, sensor, switchOccupancy,
* vacation, quickSave, today, autoAway, autoHome
*/
public String type;
/*
* The unique event name.
*/
public String name;
/*
* Whether the event is currently active or not.
*/
public Boolean running;
/*
* The event start date in thermostat local time.
*/
public String startDate;
/*
* The event start time in thermostat local time.
*/
public String startTime;
/*
* The event end date in thermostat local time.
*/
public String endDate;
/*
* The event end time in thermostat local time.
*/
public String endTime;
/*
* Whether there are persons occupying the property during the event.
*/
public Boolean isOccupied;
/*
* Whether cooling will be turned off during the event.
*/
public Boolean isCoolOff;
/*
* Whether heating will be turned off during the event.
*/
public Boolean isHeatOff;
/*
* The cooling absolute temperature to set.
*/
public Integer coolHoldTemp;
/*
* The heating absolute temperature to set.
*/
public Integer heatHoldTemp;
/*
* The fan mode during the event. Values: auto, on Default: based on current climate and hvac mode.
*/
public String fan;
/*
* The ventilator mode during the vent. Values: auto, minontime, on, off.
*/
public String vent;
/*
* The minimum amount of time the ventilator equipment must stay on on each duty cycle.
*/
public Integer ventilatorMinOnTime;
/*
* Whether this event is mandatory or the end user can cancel it.
*/
public Boolean isOptional;
/*
* Whether the event is using a relative temperature setting to the currently
* active program climate. See the Note at the bottom of this page for more information.
*/
public Boolean isTemperatureRelative;
/*
* The relative cool temperature adjustment.
*/
public Integer coolRelativeTemp;
/*
* The relative heat temperature adjustment.
*/
public Integer heatRelativeTemp;
/*
* Whether the event uses absolute temperatures to set the values. Default:
* true for DRs. See the Note at the bottom of this page for more information.
*/
public Boolean isTemperatureAbsolute;
/*
* Indicates the % scheduled runtime during a Demand Response event. Valid range
* is 0 - 100%. Default = 100, indicates no change to schedule.
*/
public Integer dutyCyclePercentage;
/*
* The minimum number of minutes to run the fan each hour. Range: 0-60, Default: 0
*/
public Integer fanMinOnTime;
/*
* True if this calendar event was created because of the occupied sensor.
*/
public Boolean occupiedSensorActive;
/*
* True if this calendar event was created because of the occupied sensor.
*/
public Boolean unoccupiedSensorActive;
/*
* Unsupported. Future feature.
*/
public Integer drRampUpTemp;
/*
* Unsupported. Future feature.
*/
public Integer drRampUpTime;
/*
* Unique identifier set by the server to link one or more events and alerts together.
*/
public String linkRef;
/*
* Used for display purposes to indicate what climate (if any) is being used for the hold.
*/
public String holdClimateRef;
}

View File

@@ -0,0 +1,184 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.dto.thermostat;
import java.util.List;
/**
* The {@link ExtendedRuntimeDTO} contains the last three 5 minute interval values
* sent by the thermostat for the past 15 minutes of runtime. The interval values
* are valuable when you are interested in analyzing the runtime data in a more
* granular fashion, at 5 minute increments rather than the more general 15 minute
* value from the Runtime Object. For the runtime values (i.e. heatPump, auxHeat,
* cool, etc.) refer to the Thermostat.Settings values (hasHeatPump, heatStages,
* coolStages) to determine whether a heat pump exists and how many stages the
* thermostat supports. The actual temperature and humidity will also be updated
* when the equipment state changes by the thermostat, this may occur at a frequency
* of 3 minutes, however it is only transmitted when there is an equipment state
* change on the thermostat.
*
* @author Mark Hilbush - Initial contribution
*/
public class ExtendedRuntimeDTO {
/*
* The UTC timestamp of the last value read. This timestamp is updated at a 15 min
* interval by the thermostat. For the 1st value, it is timestamp - 10 mins, for
* the 2nd value it is timestamp - 5 mins. Consider day boundaries being straddled
* when using these values.
*/
public String lastReadingTimestamp;
/*
* The UTC date of the last runtime reading. Format: YYYY-MM-DD
*/
public String runtimeDate;
/*
* The last 5 minute interval which was updated by the thermostat telemetry update.
* Subtract 2 from this interval to obtain the beginning interval for the last 3
* readings. Multiply by 5 mins to obtain the minutes of the day. Range: 0-287
*/
public Integer runtimeInterval;
/*
* The last three 5 minute actual temperature readings
*/
public List<Integer> actualTemperature;
/*
* The last three 5 minute actual humidity readings.
*/
public List<Integer> actualHumidity;
/*
* The last three 5 minute desired heat temperature readings.
*/
public List<Integer> desiredHeat;
/*
* The last three 5 minute desired cool temperature readings.
*/
public List<Integer> desiredCool;
/*
* The last three 5 minute desired humidity readings.
*/
public List<Integer> desiredHumidity;
/*
* The last three 5 minute desired de-humidification readings.
*/
public List<Integer> desiredDehumidity;
/*
* The last three 5 minute desired Demand Management temeprature offsets.
* This value is Demand Management adjustment value which was applied by
* the thermostat. If the thermostat decided not to honour the adjustment,
* it will send 0 for the interval. Compare these values with the values
* sent in the DM message to determine whether the thermostat applied
* the adjustment.
*/
public List<Integer> dmOffset;
/*
* The last three 5 minute HVAC Mode reading. These values indicate which
* stage was energized in the 5 minute interval. Values: heatStage10n,
* heatStage20n, heatStage30n, heatOff, compressorCoolStage10n,
* compressorCoolStage20n, compressorCoolOff, compressorHeatStage10n,
* compressorHeatStage20n, compressorHeatOff, economyCycle.
*/
public List<Integer> hvacMode;
/*
* The last three 5 minute HVAC Runtime values in seconds (0-300 seconds)
* per interval. This value corresponds to the heat pump stage 1 runtime.
*/
public List<Integer> heatPump1;
/*
* The last three 5 minute HVAC Runtime values in seconds (0-300 seconds)
* per interval. This value corresponds to the heat pump stage 2 runtime.
*/
public List<Integer> heatPump2;
/*
* The last three 5 minute HVAC Runtime values in seconds (0-300 seconds)
* per interval. This value corresponds to the auxiliary heat stage 1. If
* the thermostat does not have a heat pump, this is heat stage 1.
*/
public List<Integer> auxHeat1;
/*
* The last three 5 minute HVAC Runtime values in seconds (0-300 seconds)
* per interval. This value corresponds to the auxiliary heat stage 2. If
* the thermostat does not have a heat pump, this is heat stage 2.
*/
public List<Integer> auxHeat2;
/*
* The last three 5 minute HVAC Runtime values in seconds (0-300 seconds)
* per interval. This value corresponds to the heat stage 3 if the thermostat
* does not have a heat pump. Auxiliary stage 3 is not supported.
*/
public List<Integer> auxHeat3;
/*
* The last three 5 minute HVAC Runtime values in seconds (0-300 seconds)
* per interval. This value corresponds to the cooling stage 1.
*/
public List<Integer> cool1;
/*
* The last three 5 minute HVAC Runtime values in seconds (0-300 seconds)
* per interval. This value corresponds to the cooling stage 2.
*/
public List<Integer> cool2;
/*
* The last three 5 minute fan Runtime values in seconds (0-300 seconds) per interval.
*/
public List<Integer> fan;
/*
* The last three 5 minute humidifier Runtime values in seconds (0-300 seconds) per interval.
*/
public List<Integer> humidifier;
/*
* The last three 5 minute de-humidifier Runtime values in seconds (0-300 seconds) per interval.
*/
public List<Integer> dehumidifier;
/*
* The last three 5 minute economizer Runtime values in seconds (0-300 seconds) per interval.
*/
public List<Integer> economizer;
/*
* The last three 5 minute ventilator Runtime values in seconds (0-300 seconds) per interval.
*/
public List<Integer> ventilator;
/*
* The latest value of the current electricity bill as interpolated from the
* thermostat's readings from a paired electricity meter.
*/
public List<Integer> currentElectricityBill;
/*
* The latest estimate of the projected electricity bill as interpolated from the
* thermostat's readings from a paired electricity meter.
*/
public List<Integer> projectedElectricityBill;
}

View File

@@ -0,0 +1,40 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.dto.thermostat;
/**
* The {@link GeneralSettingDTO} represent the General alert/reminder type. It is
* used when getting/setting the Thermostat NotificationSettings object. The type
* corresponds to the Alert.notificationType returned when alerts are included in
* the selection. See Alert for more information.
*
* @author Mark Hilbush - Initial contribution
*/
public class GeneralSettingDTO {
/*
* Boolean value representing whether or not alerts/reminders are enabled for this notification type or not.
*/
public Boolean enabled;
/*
* The type of notification. Possible values are: temp
*/
public String type;
/*
* Boolean value representing whether or not alerts/reminders should be sent to the
* technician/contractor associated with the thermostat.
*/
public Boolean remindTechnician;
}

View File

@@ -0,0 +1,60 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.dto.thermostat;
/**
* The {@link HouseDetailsDTO} contains contains the information about the
* house the thermostat is installed in.
*
* @author Mark Hilbush - Initial contribution
*/
public class HouseDetailsDTO {
/*
* The style of house. Values: other, apartment, condominium, detached,
* loft, multiPlex, rowHouse, semiDetached, townhouse, and 0 for unknown.
*/
public String style;
/*
* The size of the house in square feet.
*/
public Integer size;
/*
* The number of floors or levels in the house.
*/
public Integer numberOfFloors;
/*
* The number of rooms in the house.
*/
public Integer numberOfRooms;
/*
* The number of occupants living in the house.
*/
public Integer numberOfOccupants;
/*
* The age of house in years.
*/
public Integer age;
/*
* This field defines the window efficiency of the house. Valid values
* are in the range 1 - 7. Changing the value of this field alters the
* settings the thermostat uses for the humidifier when in 'frost Control'
* mode. See the NOTE above before updating this value.
*/
public Integer windowEfficiency;
}

View File

@@ -0,0 +1,53 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.dto.thermostat;
import org.openhab.binding.ecobee.internal.enums.LimitNotificationType;
/**
* The {@link LimitSettingDTO} represents the alert/reminder type which is associated
* specific values, such as highHeat or lowHumidity. It is used when getting/setting
* the Thermostat NotificationSettings object. The type corresponds to the
* Alert.notificationType returned when alerts are also included in the selection.
* See Alert for more information.
*
* @author Mark Hilbush - Initial contribution
*/
public class LimitSettingDTO {
/*
* The value of the limit to set. For temperatures the value is expressed as
* degrees Fahrenheit, multipled by 10. For humidity values are expressed as
* a percentage from 5 to 95. See here for more information.
*/
public Integer limit;
/*
* Boolean value representing whether or not alerts/reminders are enabled for
* this notification type or not.
*/
public Boolean enabled;
/*
* The type of notification. Possible values are: lowTemp, highTemp, lowHumidity,
* highHumidity, auxHeat, auxOutdoor
*/
public LimitNotificationType type;
/*
* Boolean value representing whether or not alerts/reminders should be sent to
* the technician/contractor associated with the thermostat.
*/
public Boolean remindTechnician;
}

View File

@@ -0,0 +1,73 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.dto.thermostat;
/**
* The {@link LocationDTO} describes the physical location and coordinates of the
* thermostat as entered by the thermostat owner. The address information is used
* in a geocode look up to obtain the thermostat coordinates. The coordinates
* are used to obtain accurate weather information.
*
* @author Mark Hilbush - Initial contribution
*/
public class LocationDTO {
/*
* The timezone offset in minutes from UTC.
*/
public Integer timeZoneOffsetMinutes;
/*
* The Olson timezone the thermostat resides in (e.g America/Toronto).
*/
public String timeZone;
/*
* Whether the thermostat should factor in daylight savings when displaying the date and time.
*/
public Boolean isDaylightSaving;
/*
* The thermostat location street address.
*/
public String streetAddress;
/*
* The thermostat location city.
*/
public String city;
/*
* The thermostat location State or Province
*/
public String provinceState;
/*
* The thermostat location country.
*/
public String country;
/*
* The thermostat location ZIP or Postal code.
*/
public String postalCode;
/*
* The thermostat owner's phone number.
*/
public String phoneNumber;
/*
* The lat/long geographic coordinates of the thermostat location.
*/
public String mapCoordinates;
}

View File

@@ -0,0 +1,62 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.dto.thermostat;
/**
* The {@link ManagementDTO} contains information about the management company
* the thermostat belongs to. The Management object is read-only, it may be
* modified in the web portal.
*
* @author Mark Hilbush - Initial contribution
*/
public class ManagementDTO {
/*
* The administrative contact name.
*/
public String administrativeContact;
/*
* The billing contact name.
*/
public String billingContact;
/*
* The company name.
*/
public String name;
/*
* The phone number.
*/
public String phone;
/*
* The contact email address.
*/
public String email;
/*
* The company web site.
*/
public String web;
/*
* Whether to show management alerts on the thermostat.
*/
public Boolean showAlertIdt;
/*
* Whether to show management alerts in the web portal.
*/
public Boolean showAlertWeb;
}

View File

@@ -0,0 +1,55 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.dto.thermostat;
import java.util.List;
/**
* The {@link NotificationSettingsDTO} contains the configuration of the possible alerts and
* reminders which can be generated by the Thermostat. The NotificationsSettings supports
* retrieval through a Thermostat GET call, setting the includeNotificationSettings to
* true in the Selection.
*
* @author Mark Hilbush - Initial contribution
*/
public class NotificationSettingsDTO {
/*
* The list of email addresses alerts and reminders will be sent to.
* The full list of email addresses must be sent in any update request.
* If any are missing from that list they will be deleted. If an empty
* list is sent, any email addresses will be deleted.
*/
public List<String> emailAddresses;
/*
* Boolean values representing whether or not alerts and reminders
* will be sent to the email addresses listed above when triggered.
*/
public Boolean emailNotificationsEnabled;
/*
* The list of equipment specific alert and reminder settings.
*/
public List<EquipmentSettingDTO> equipment;
/*
* The list of general alert and reminder settings.
*/
public List<GeneralSettingDTO> general;
/*
* The list of limit specific alert and reminder settings.
*/
public List<LimitSettingDTO> limit;
}

View File

@@ -0,0 +1,25 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.dto.thermostat;
/**
* The {@link OemCfgDTO} is undefined in the Ecobee API spec.
*
* @author Mark Hilbush - Initial contribution
*/
public class OemCfgDTO {
public String serialNumber;
public Object cCfg;
}

View File

@@ -0,0 +1,22 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.dto.thermostat;
/**
* The {@link PrivacyDTO} is undefined in the Ecobee API spec.
*
* @author Mark Hilbush - Initial contribution
*/
public class PrivacyDTO {
}

View File

@@ -0,0 +1,40 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.dto.thermostat;
import java.util.List;
/**
* The {@link ProgramDTO} is a container for the Schedule and its Climates. See Core Concepts for
* details on how the program is structured. The schedule property is a two dimensional array
* containing the climate names.
*
* @author Mark Hilbush - Initial contribution
*/
public class ProgramDTO {
/*
* The Schedule object defining the program schedule.
*/
public List<List<String>> schedule;
/*
* The list of Climate objects defining all the climates in the program schedule.
*/
public List<ClimateDTO> climates;
/*
* The currently active climate, identified by its ClimateRef.
*/
public String currentClimateRef;
}

View File

@@ -0,0 +1,22 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.dto.thermostat;
/**
* The {@link ReminderDTO} is undefined in the Ecobee API spec.
*
* @author Mark Hilbush - Initial contribution
*/
public class ReminderDTO {
}

View File

@@ -0,0 +1,42 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.dto.thermostat;
/**
* The {@link RemoteSensorCapabilityDTO}represents the specific capability of a
* sensor connected to the thermostat. For the occupancy type capability the
* data will only show computed occupancy, as does the thermostat.
*
* @author Mark Hilbush - Initial contribution
*/
public class RemoteSensorCapabilityDTO {
/*
* The unique sensor capability identifier. For example: 1
*/
public String id;
/*
* The type of sensor capability. Values: adc, co2, dryContact,
* humidity, temperature, occupancy, unknown.
*/
public String type;
/*
* The data value for this capability, always a String. Temperature
* values are expressed as degrees Fahrenheit, multiplied by 10. For
* example, a temperature of 72F would be returned as the value "720".
* Occupancy values are "true" or "false". Humidity is expressed as
* a % value such as "45". Unknown values are returned as "unknown".
*/
public String value;
}

View File

@@ -0,0 +1,62 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.dto.thermostat;
import java.util.List;
/**
* The {@link RemoteSensorDTO} represents a sensor connected to the thermostat.
* The remote sensor data will only show computed occupancy, as does the thermostat.
* Definition - For a given sensor, computed occupancy means a sensor is occupied
* if any motion was detected in the past 30 minutes. RemoteSensor data changes
* trigger the runtimeRevision to be updated. The data updates are sent at an
* interval of 3 mins maximum. This means that you should not poll quicker
* than once every 3 mins for revision changes.
*
* @author Mark Hilbush - Initial contribution
*/
public class RemoteSensorDTO {
/*
* The unique sensor identifier. It is composed of deviceName + deviceId
* separated by colons, for example: rs:100
*/
public String id;
/*
* The user assigned sensor name.
*/
public String name;
/*
* The type of sensor. Values: thermostat, ecobee3_remote_sensor,
* monitor_sensor, control_sensor.
*/
public String type;
/*
* The unique 4-digit alphanumeric sensor code. For ecobee3 remote
* sensors this corresponds to the code found on the back of the physical sensor.
*/
public String code;
/*
* This flag indicates whether the remote sensor is currently in use
* by a comfort setting. See Climate for more information.
*/
public Boolean inUse;
/*
* The list of remoteSensorCapability objects for the remote sensor.
*/
public List<RemoteSensorCapabilityDTO> capability;
}

View File

@@ -0,0 +1,155 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.dto.thermostat;
import java.util.Date;
import java.util.List;
/**
* The {@link RuntimeDTO} represents the last known thermostat running state. This state
* is composed from the last interval status message received from a thermostat. It is
* also updated each time the thermostat posts configuration changes to the server.
* The runtime object contains the last 5 minute interval value sent by the thermostat
* for the past 15 minutes of runtime. The thermostat updates the server every 15 minutes
* with the last three 5 minute readings. The actual temperature and humidity will also
* be updated when the equipment state changes by the thermostat, this may occur at a
* frequency of 3 minutes, however it is only transmitted when there is an equipment
* state change on the thermostat. The runtime object contains two fields, desiredHeatRange
* and desiredCoolRange, which can be queried and used to determine that any holds being
* set through the API will not be adjusted. The API caller should check these ranges
* before calling the setHold function to mitigate against the new set points being
* adjusted by the server if the values are outside the acceptable ranges.
*
* @author Mark Hilbush - Initial contribution
*/
public class RuntimeDTO {
/*
* The current runtime revision. Equivalent in meaning to the runtime
* revision number in the thermostat summary call.
*/
public String runtimeRev;
/*
* Whether the thermostat is currently connected to the server.
*/
public Boolean connected;
/*
* The UTC date/time stamp of when the thermostat first connected
* to the ecobee server.
*/
public Date firstConnected;
/*
* The last recorded connection date and time.
*/
public Date connectDateTime;
/*
* The last recorded disconnection date and time.
*/
public Date disconnectDateTime;
/*
* The UTC date/time stamp of when the thermostat was updated.
* Format: YYYY-MM-DD HH:MM:SS
*/
public Date lastModified;
/*
* The UTC date/time stamp of when the thermostat last posted its
* runtime information. Format: YYYY-MM-DD HH:MM:SS
*/
public Date lastStatusModified;
/*
* The UTC date of the last runtime reading. Format: YYYY-MM-DD
*/
public String runtimeDate;
/*
* The last 5 minute interval which was updated by the thermostat
* telemetry update. Subtract 2 from this interval to obtain the
* beginning interval for the last 3 readings. Multiply by 5 mins
* to obtain the minutes of the day. Range: 0-287
*/
public Integer runtimeInterval;
/*
* The current temperature displayed on the thermostat.
*/
public Integer actualTemperature;
/*
* The current humidity % shown on the thermostat.
*/
public Integer actualHumidity;
/*
* The dry-bulb temperature recorded by the thermostat. When
* Energy.FeelsLikeMode is set to humidex, Runtime.actualTemperature
* will report a "feels like" temperature.
*/
public Integer rawTemperature;
/*
* The currently displayed icon on the thermostat.
*/
public Integer showIconMode;
/*
* The desired heat temperature as per the current running
* program or active event.
*/
public Integer desiredHeat;
/*
* The desired cool temperature as per the current running
* program or active event.
*/
public Integer desiredCool;
/*
* The desired humidity set point.
*/
public Integer desiredHumidity;
/*
* The desired dehumidification set point.
*/
public Integer desiredDehumidity;
/*
* The desired fan mode. Values: auto, on or null if the HVAC
* system is off and the thermostat is not controlling a fan independently.
*/
public String desiredFanMode;
/*
* This field provides the possible valid range for which a desiredHeat
* setpoint can be set to. This value takes into account the thermostat
* heat temperature limits as well the running program or active events.
* Values are returned as an Integer array representing the canonical
* minimum and maximim, e.g. [450,790].
*/
public List<Integer> desiredHeatRange;
/*
* This field provides the possible valid range for which a desiredCool
* setpoint can be set to. This value takes into account the thermostat
* cool temperature limits as well the running program or active events.
* Values are returned as an Integer array representing the canonical
* minimum and maximim, e.g. [650,920].
*/
public List<Integer> desiredCoolRange;
}

View File

@@ -0,0 +1,66 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.dto.thermostat;
/**
* The {@link SecuritySettingsDTO} defines the security settings which a thermostat
* may have. Currently this object stores data specific to access control. If any of
* the XXXAccess fields are not supplied they will default to false. So to set all
* to false where previously some were set to true the caller can either pass all
* the XXXAccess fields explicitly, or pass none and the default will be set for each.
*
* @author Mark Hilbush - Initial contribution
*/
public class SecuritySettingsDTO {
/*
* The 4-digit user access code for the thermostat. The code must be set when
* enabling access control. See the callout above for more information.
*/
public String userAccessCode;
/*
* The flag for determing whether there are any restrictions on the thermostat
* regarding access control. Default value is false. If all other values are
* true this value will default to true.
*/
public Boolean allUserAccess;
/*
* The flag for determing whether there are any restrictions on the thermostat
* regarding access control to the Thermostat Program. Default value is false,
* unless allUserAccess is true.
*/
public Boolean programAccess;
/*
* The flag for determing whether there are any restrictions on the thermostat
* regarding access control to the Thermostat system and settings. Default value
* is false, unless allUserAccess is true.
*/
public Boolean detailsAccess;
/*
* The flag for determing whether there are any restrictions on the thermostat
* regarding access control to the Thermostat quick save functionality. Default
* value is false, unless allUserAccess is true.
*/
public Boolean quickSaveAccess;
/*
* The flag for determing whether there are any restrictions on the thermostat
* regarding access control to the Thermostat vacation functionality. Default
* value is false, unless allUserAccess is true.
*/
public Boolean vacationAccess;
}

View File

@@ -0,0 +1,620 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.dto.thermostat;
/**
* The {@link SettingsDTO} is responsible for
*
* @author Mark Hilbush - Initial contribution
*/
public class SettingsDTO {
/*
* The current HVAC mode the thermostat is in. Values: auto, auxHeatOnly, cool, heat, off.
*/
public String hvacMode;
/*
* The last service date of the HVAC equipment.
*/
public String lastServiceDate;
/*
* Whether to send an alert when service is required again.
*/
public Boolean serviceRemindMe;
/*
* The user configured monthly interval between HVAC service reminders
*/
public Integer monthsBetweenService;
/*
* Date to be reminded about the next HVAC service date.
*/
public String remindMeDate;
/*
* The ventilator mode. Values: auto, minontime, on, off.
*/
public String vent;
/*
* The minimum time in minutes the ventilator is configured to run. The thermostat will always
* guarantee that the ventilator runs for this minimum duration whenever engaged.
*/
public Integer ventilatorMinOnTime;
/*
* Whether the technician associated with this thermostat should receive the HVAC service reminders as well.
*/
public Boolean serviceRemindTechnician;
/*
* A note about the physical location where the SMART or EMS Equipment Interface module is located.
*/
public String eiLocation;
/*
* The temperature at which a cold temp alert is triggered.
*/
public Integer coldTempAlert;
/*
* Whether cold temperature alerts are enabled.
*/
public Boolean coldTempAlertEnabled;
/*
* The temperature at which a hot temp alert is triggered.
*/
public Integer hotTempAlert;
/*
* Whether hot temperature alerts are enabled.
*/
public Boolean hotTempAlertEnabled;
/*
* The number of cool stages the connected HVAC equipment supports.
*/
public Integer coolStages;
/*
* The number of heat stages the connected HVAC equipment supports.
*/
public Integer heatStages;
/*
* The maximum automated set point set back offset allowed in degrees.
*/
public Integer maxSetBack;
/*
* The maximum automated set point set forward offset allowed in degrees.
*/
public Integer maxSetForward;
/*
* The set point set back offset, in degrees, configured for a quick save event.
*/
public Integer quickSaveSetBack;
/*
* The set point set forward offset, in degrees, configured for a quick save event.
*/
public Integer quickSaveSetForward;
/*
* Whether the thermostat is controlling a heat pump.
*/
public Boolean hasHeatPump;
/*
* Whether the thermostat is controlling a forced air furnace.
*/
public Boolean hasForcedAir;
/*
* Whether the thermostat is controlling a boiler.
*/
public Boolean hasBoiler;
/*
* Whether the thermostat is controlling a humidifier.
*/
public Boolean hasHumidifier;
/*
* Whether the thermostat is controlling an energy recovery ventilator.
*/
public Boolean hasErv;
/*
* Whether the thermostat is controlling a heat recovery ventilator.
*/
public Boolean hasHrv;
/*
* Whether the thermostat is in frost control mode.
*/
public Boolean condensationAvoid;
/*
* Whether the thermostat is configured to report in degrees Celsius.
*/
public Boolean useCelsius;
/*
* Whether the thermostat is using 12hr time format.
*/
public Boolean useTimeFormat12;
/*
* Multilanguage support, currently only "en" - english is supported. In future others
* locales can be supported.
*/
public String locale;
/*
* The minimum humidity level (in percent) set point for the humidifier
*/
public String humidity;
/*
* The humidifier mode. Values: auto, manual, off.
*/
public String humidifierMode;
/*
* The thermostat backlight intensity when on. A value between 0 and 10, with 0
* meaning 'off' - the zero value may not be honored by all ecobee versions.
*/
public Integer backlightOnIntensity;
/*
* The thermostat backlight intensity when asleep. A value between 0 and 10, with 0
* meaning 'off' - the zero value may not be honored by all ecobee versions.
*/
public Integer backlightSleepIntensity;
/*
* The time in seconds before the thermostat screen goes into sleep mode.
*/
public Integer backlightOffTime;
/*
* The field is deprecated. Please use Audio.soundTickVolume.
*/
public Integer soundTickVolume;
/*
* The field is deprecated. Please use Audio.soundAlertVolume.
*/
public Integer soundAlertVolume;
/*
* The minimum time the compressor must be off for in order to prevent short-cycling.
*/
public Integer compressorProtectionMinTime;
/*
* The minimum outdoor temperature that the compressor can operate at - applies
* more to air source heat pumps than geothermal.
*/
public Integer compressorProtectionMinTemp;
/*
* The difference between current temperature and set-point that will trigger stage 2 heating.
*/
public Integer stage1HeatingDifferentialTemp;
/*
* The difference between current temperature and set-point that will trigger stage 2 cooling.
*/
public Integer stage1CoolingDifferentialTemp;
/*
* The time after a heating cycle that the fan will run for to extract any heating left
* in the system - 30 second default.
*/
public Integer stage1HeatingDissipationTime;
/*
* The time after a cooling cycle that the fan will run for to extract any cooling left
* in the system - 30 second default.
*/
public Integer stage1CoolingDissipationTime;
/*
* The flag to tell if the heat pump is in heating mode or in cooling when the relay
* is engaged. If set to zero it's heating when the reversing valve is open, cooling
* when closed and if it's one - it's the opposite.
*/
public Boolean heatPumpReversalOnCool;
/*
* Whether fan control by the Thermostat is required in auxiliary heating (gas/electric/boiler),
* otherwise controlled by furnace.
*/
public Boolean fanControlRequired;
/*
* The minimum time, in minutes, to run the fan each hour. Value from 1 to 60.
*/
public Integer fanMinOnTime;
/*
* The minimum temperature difference between the heat and cool values. Used to ensure that when
* thermostat is in auto mode, the heat and cool values are separated by at least this value.
*/
public Integer heatCoolMinDelta;
/*
* The amount to adjust the temperature reading in degrees F - this value is subtracted from
* the temperature read from the sensor.
*/
public Integer tempCorrection;
/*
* The default end time setting the thermostat applies to user temperature holds. Values useEndTime4hour,
* useEndTime2hour (EMS Only), nextPeriod, indefinite, askMe
*/
public String holdAction;
/*
* Whether the Thermostat uses a geothermal / ground source heat pump.
*/
public Boolean heatPumpGroundWater;
/*
* Whether the thermostat is connected to an electric HVAC system.
*/
public Boolean hasElectric;
/*
* Whether the thermostat is connected to a dehumidifier. If true or dehumidifyOvercoolOffset > 0 then
* allow setting dehumidifierMode and dehumidifierLevel.
*/
public Boolean hasDehumidifier;
/*
* The dehumidifier mode. Values: on, off. If set to off then the dehumidifier will not run,
* nor will the AC overcool run.
*/
public String dehumidifierMode;
/*
* The dehumidification set point in percentage.
*/
public Integer dehumidifierLevel;
/*
* Whether the thermostat should use AC overcool to dehumidify. When set to true a postive integer value
* must be supplied for dehumidifyOvercoolOffset otherwise an API validation exception will be thrown.
*/
public Boolean dehumidifyWithAC;
/*
* Whether the thermostat should use AC overcool to dehumidify and what that temperature offset
* should be. A value of 0 means this feature is disabled and dehumidifyWithAC will be set to false.
* Value represents the value in F to subract from the current set point. Values should be in the
* range 0 - 50 and be divisible by 5.
*/
public Integer dehumidifyOvercoolOffset;
/*
* If enabled, allows the Thermostat to be put in HVACAuto mode.
*/
public Boolean autoHeatCoolFeatureEnabled;
/*
* Whether the alert for when wifi is offline is enabled.
*/
public Boolean wifiOfflineAlert;
/*
* The minimum heat set point allowed by the thermostat firmware.
*/
public Integer heatMinTemp;
/*
* The maximum heat set point allowed by the thermostat firmware.
*/
public Integer heatMaxTemp;
/*
* The minimum cool set point allowed by the thermostat firmware.
*/
public Integer coolMinTemp;
/*
* The maximum cool set point allowed by the thermostat firmware.
*/
public Integer coolMaxTemp;
/*
* The maximum heat set point configured by the user's preferences.
*/
public Integer heatRangeHigh;
/*
* The minimum heat set point configured by the user's preferences.
*/
public Integer heatRangeLow;
/*
* The maximum cool set point configured by the user's preferences.
*/
public Integer coolRangeHigh;
/*
* The minimum heat set point configured by the user's preferences.
*/
public Integer coolRangeLow;
/*
* The user access code value for this thermostat. See the SecuritySettings object for more information.
*/
public String userAccessCode;
/*
* The integer representation of the user access settings. See the SecuritySettings object for more information.
*/
public Integer userAccessSetting;
/*
* The temperature at which an auxHeat temperature alert is triggered.
*/
public Integer auxRuntimeAlert;
/*
* The temperature at which an auxOutdoor temperature alert is triggered.
*/
public Integer auxOutdoorTempAlert;
/*
* The maximum outdoor temperature above which aux heat will not run.
*/
public Integer auxMaxOutdoorTemp;
/*
* Whether the auxHeat temperature alerts are enabled.
*/
public Boolean auxRuntimeAlertNotify;
/*
* Whether the auxOutdoor temperature alerts are enabled.
*/
public Boolean auxOutdoorTempAlertNotify;
/*
* Whether the auxHeat temperature alerts for the technician are enabled.
*/
public Boolean auxRuntimeAlertNotifyTechnician;
/*
* Whether the auxOutdoor temperature alerts for the technician are enabled.
*/
public Boolean auxOutdoorTempAlertNotifyTechnician;
/*
* Whether the thermostat should use pre heating to reach the set point on time.
*/
public Boolean disablePreHeating;
/*
* Whether the thermostat should use pre cooling to reach the set point on time.
*/
public Boolean disablePreCooling;
/*
* Whether an installer code is required.
*/
public Boolean installerCodeRequired;
/*
* Whether Demand Response requests are accepted by this thermostat. Possible values
* are: always, askMe, customerSelect, defaultAccept, defaultDecline, never.
*/
public String drAccept;
/*
* Whether the property is a rental, or not.
*/
public Boolean isRentalProperty;
/*
* Whether to use a zone controller or not.
*/
public Boolean useZoneController;
/*
* Whether random start delay is enabled for cooling.
*/
public Integer randomStartDelayCool;
/*
* Whether random start delay is enabled for heating.
*/
public Integer randomStartDelayHeat;
/*
* The humidity level to trigger a high humidity alert.
*/
public Integer humidityHighAlert;
/*
* The humidity level to trigger a low humidity alert.
*/
public Integer humidityLowAlert;
/*
* Whether heat pump alerts are disabled.
*/
public Boolean disableHeatPumpAlerts;
/*
* Whether alerts are disabled from showing on the thermostat.
*/
public Boolean disableAlertsOnIdt;
/*
* Whether humidification alerts are enabled to the thermsotat owner.
*/
public Boolean humidityAlertNotify;
/*
* Whether humidification alerts are enabled to the technician associated with the thermsotat.
*/
public Boolean humidityAlertNotifyTechnician;
/*
* Whether temperature alerts are enabled to the thermsotat owner.
*/
public Boolean tempAlertNotify;
/*
* Whether temperature alerts are enabled to the technician associated with the thermostat.
*/
public Boolean tempAlertNotifyTechnician;
/*
* The dollar amount the owner specifies for their desired maximum electricy bill.
*/
public Integer monthlyElectricityBillLimit;
/*
* Whether electricity bill alerts are enabled.
*/
public Boolean enableElectricityBillAlert;
/*
* Whether electricity bill projection alerts are enabled
*/
public Boolean enableProjectedElectricityBillAlert;
/*
* The day of the month the owner's electricty usage is billed.
*/
public Integer electricityBillingDayOfMonth;
/*
* The owners billing cycle duration in months.
*/
public Integer electricityBillCycleMonths;
/*
* The annual start month of the owners billing cycle.
*/
public Integer electricityBillStartMonth;
/*
* The number of minutes to run ventilator per hour when home.
*/
public Integer ventilatorMinOnTimeHome;
/*
* The number of minutes to run ventilator per hour when away.
*/
public Integer ventilatorMinOnTimeAway;
/*
* Determines whether or not to turn the backlight off during sleep.
*/
public Boolean backlightOffDuringSleep;
/*
* When set to true if no occupancy motion detected thermostat will go into indefinite away
* hold, until either the user presses resume schedule or motion is detected.
*/
public Boolean autoAway;
/*
* When set to true if a larger than normal delta is found between sensors the fan
* will be engaged for 15min/hour.
*/
public Boolean smartCirculation;
/*
* When set to true if a sensor has detected presense for more than 10 minutes then
* include that sensor in temp average. If no activity has been seen on a sensor for
* more than 1 hour then remove this sensor from temperature average.
*/
public Boolean followMeComfort;
/*
* This read-only field represents the type of ventilator present for the Thermostat.
* The possible values are none, ventilator, hrv, and erv.
*/
public String ventilatorType;
/*
* This Boolean field represents whether the ventilator timer is on or off. The default
* value is false. If set to true the ventilatorOffDateTime is set to now() + 20 minutes.
* If set to false the ventilatorOffDateTime is set to it's default value.
*/
public Boolean isVentilatorTimerOn;
/*
* This read-only field represents the Date and Time the ventilator will run until.
* The default value is 2014-01-01 00:00:00.
*/
public String ventilatorOffDateTime;
/*
* This Boolean field represents whether the HVAC system has a UV filter. The default value is true.
*/
public Boolean hasUVFilter;
/*
* This field represents whether to permit the cooling to operate when the Outdoor temeperature
* is under a specific threshold, currently 55F. The default value is false.
*/
public Boolean coolingLockout;
/*
* Whether to use the ventilator to dehumidify when climate or calendar event indicates the owner
* is home. The default value is false.
*/
public Boolean ventilatorFreeCooling;
/*
* This field represents whether to permit dehumidifer to operate when the heating is
* running. The default value is false.
*/
public Boolean dehumidifyWhenHeating;
/*
* This field represents whether or not to allow dehumification when cooling. The default value is true.
*/
public Boolean ventilatorDehumidify;
/*
* The unique reference to the group this thermostat belongs to, if any. See GET Group request and POST
* Group request for more information.
*/
public String groupRef;
/*
* The name of the the group this thermostat belongs to, if any. See GET Group request and POST Group
* request for more information.
*/
public String groupName;
/*
* The setting value for the group this thermostat belongs to, if any. See GET Group request and POST
* Group request for more information.
*/
public Integer groupSetting;
}

View File

@@ -0,0 +1,71 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.dto.thermostat;
/**
* The {@link TechnicianDTO} contains information pertaining to the technician
* associated with a thermostat. The technician may not be modified through the API.
*
* @author Mark Hilbush - Initial contribution
*/
public class TechnicianDTO {
/*
* The internal ecobee unique identifier for this contractor.
*/
public String contractorRef;
/*
* The company name of the technician.
*/
public String name;
/*
* The technician's contact phone number.
*/
public String phone;
/*
* The technician's street address.
*/
public String streetAddress;
/*
* The technician's city.
*/
public String city;
/*
* The technician's State or Province.
*/
public String provinceState;
/*
* The technician's country.
*/
public String country;
/*
* The technician's ZIP or Postal Code.
*/
public String postalCode;
/*
* The technician's email address.
*/
public String email;
/*
* The technician's web site.
*/
public String web;
}

View File

@@ -0,0 +1,141 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.dto.thermostat;
import java.util.Date;
import java.util.List;
/**
* The {@link ThermostatDTO} is the central piece of the ecobee API. All objects
* relate in one way or another to a real thermostat. The thermostat object
* and its component objects define the real thermostat device.
*
* @author Mark Hilbush - Initial contribution
*/
public class ThermostatDTO {
/*
* The unique thermostat serial number.
*/
public String identifier;
/*
* A user defined name for a thermostat.
*/
public String name;
/*
* The current thermostat configuration revision.
*/
public String thermostatRev;
/*
* Whether the user registered the thermostat.
*/
public Boolean isRegistered;
/*
* The thermostat model number.
*
* Values: apolloSmart, apolloEms, idtSmart, idtEms, siSmart, siEms,
* athenaSmart, athenaEms, corSmart, nikeSmart, nikeEms
*/
public String modelNumber;
/*
* The thermostat brand.
*/
public String brand;
/*
* The comma-separated list of the thermostat's additional features, if any.
*/
public String features;
/*
* The last modified date time for the thermostat configuration.
*/
public Date lastModified;
/*
* The current time in the thermostat's time zone.
*/
public Date thermostatTime;
/*
* The current time in UTC.
*/
public String utcTime;
/*
* The status of all equipment controlled by this Thermostat.
* Only running equipment is listed in the CSV String.
*
* Values: heatPump, heatPump2, heatPump3, compCool1, compCool2,
* auxHeat1, auxHeat2, auxHeat3, fan, humidifier, dehumidifier,
* ventilator, economizer, compHotWater, auxHotWater.
*
* Note: If no equipment is currently running an empty String is returned.
* If Settings.hasHeatPump is true, heatPump value will be returned for
* heating, compCool for cooling, and auxHeat for aux heat.
* If Settings.hasForcedAir or Settings.hasBoiler is true, auxHeat value
* will be returned for heating and compCool for cooling (heatPump will
* not show up for heating).
*/
public String equipmentStatus;
public List<AlertDTO> alerts;
public AudioDTO audio;
public List<DeviceDTO> devices;
public ElectricityDTO electricity;
public EnergyDTO energy;
public List<EventDTO> events;
public ExtendedRuntimeDTO extendedRuntime;
public HouseDetailsDTO houseDetails;
public LocationDTO location;
public ManagementDTO management;
public NotificationSettingsDTO notificationSettings;
public OemCfgDTO oemCfg;
public PrivacyDTO privacy;
public ProgramDTO program;
public List<ReminderDTO> reminders;
public RuntimeDTO runtime;
public SecuritySettingsDTO securitySettings;
public List<RemoteSensorDTO> remoteSensors;
public SettingsDTO settings;
public TechnicianDTO technician;
public UtilityDTO utility;
public VersionDTO version;
public WeatherDTO weather;
}

View File

@@ -0,0 +1,32 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.dto.thermostat;
import org.openhab.binding.ecobee.internal.dto.SelectionDTO;
/**
* The {@link ThermostatRequestDTO} contains the information needed to make a thermostat request.
*
* @author Mark Hilbush - Initial contribution
*/
public class ThermostatRequestDTO {
public ThermostatRequestDTO(SelectionDTO selection) {
this.selection = selection;
}
/*
* Specifies which thermostats will be returned in the response.
*/
public SelectionDTO selection;
}

View File

@@ -0,0 +1,31 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.dto.thermostat;
import java.util.List;
import org.openhab.binding.ecobee.internal.dto.AbstractResponseDTO;
/**
* The {@link ThermostatResponseDTO} contains the list of thermostats in response to a
* thermostat request.
*
* @author Mark Hilbush - Initial contribution
*/
public class ThermostatResponseDTO extends AbstractResponseDTO {
/*
* List of thermostats matching the selection criteria in the thermostat request.
*/
public List<ThermostatDTO> thermostatList;
}

View File

@@ -0,0 +1,38 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.dto.thermostat;
import org.openhab.binding.ecobee.internal.dto.SelectionDTO;
/**
* The {@link ThermostatUpdateRequestDTO} contains the information needed to make a
* request to update a thermostat.
*
* @author Mark Hilbush - Initial contribution
*/
public class ThermostatUpdateRequestDTO {
public ThermostatUpdateRequestDTO(SelectionDTO selection) {
this.selection = selection;
}
/*
* Thermostat object containing changes.
*/
public ThermostatDTO thermostat;
/*
* Specifies the thermostat to be updated.
*/
public SelectionDTO selection;
}

View File

@@ -0,0 +1,41 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.dto.thermostat;
/**
* The {@link UtilityDTO} contains the Utility information the Thermostat belongs to.
* The utility may not be modified through the API.
*
* @author Mark Hilbush - Initial contribution
*/
public class UtilityDTO {
/*
* The Utility company name.
*/
public String name;
/*
* The Utility company contact phone number.
*/
public String phone;
/*
* The Utility company email address.
*/
public String email;
/*
* The Utility company web site.
*/
public String web;
}

View File

@@ -0,0 +1,26 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.dto.thermostat;
/**
* The {@link VersionDTO} contains version information about the thermostat.
*
* @author Mark Hilbush - Initial contribution
*/
public class VersionDTO {
/*
* The thermostat firmware version number.
*/
public String thermostatFirmwareVersion;
}

View File

@@ -0,0 +1,34 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.dto.thermostat;
/**
* The {@link VoiceEngineDTO} contains information about the voice assistant
* that the selected thermostat supports.
*
* @author Mark Hilbush - Initial contribution
*/
public class VoiceEngineDTO {
/*
* The name of the voice engine.
*/
public String name;
/*
* True if the voice engine is currently enabled (paired) for the ecobee account of
* selected thermostat. You can change the flag value by using UnlinkVoiceEngine
* thermostat function.
*/
public Boolean enabled;
}

View File

@@ -0,0 +1,39 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.dto.thermostat;
import java.util.Date;
import java.util.List;
/**
* The {@link WeatherDTO} contains the weather and forecast information for the thermostat's location.
*
* @author Mark Hilbush - Initial contribution
*/
public class WeatherDTO {
/*
* The time stamp in UTC of the weather forecast
*/
public Date timestamp;
/*
* The weather station identifier
*/
public String weatherStation;
/*
* The list of latest weather station forecasts
*/
public List<WeatherForecastDTO> forecasts;
}

View File

@@ -0,0 +1,107 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.dto.thermostat;
import java.util.Date;
/**
* The {@link WeatherForecastDTO} contains the weather forecast information for
* the thermostat. The first forecast is the most accurate, later forecasts
* become less accurate in distance and time.
*
* Weather Symbol Mappings
*
* @author Mark Hilbush - Initial contribution
*/
public class WeatherForecastDTO {
/*
* The Integer value used to map to a weatherSymbol.
*/
public Integer weatherSymbol;
/*
* The time stamp of the weather forecast.
*/
public Date dateTime;
/*
* A text value representing the current weather condition.
*/
public String condition;
/*
* The current temperature.
*/
public Integer temperature;
/*
* The current barometric pressure.
*/
public Integer pressure;
/*
* The current humidity.
*/
public Integer relativeHumidity;
/*
* he dewpoint.
*/
public Integer dewpoint;
/*
* The visibility in meters; 0 - 70,000.
*/
public Integer visibility;
/*
* The wind speed as an integer in mph * 1000.
*/
public Integer windSpeed;
/*
* The wind gust speed.
*/
public Integer windGust;
/*
* The wind direction.
*/
public String windDirection;
/*
* The wind bearing.
*/
public Integer windBearing;
/*
* The probability of precipitation.
*/
public Integer pop;
/*
* The predicted high temperature for the day.
*/
public Integer tempHigh;
/*
* The predicted low temperature for the day.
*/
public Integer tempLow;
/*
* The cloud cover condition.
*/
public Integer sky;
}

View File

@@ -0,0 +1,106 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.dto.thermostat.summary;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
/**
* The {@link RevisionDTO} contains information indicating what data
* has changed on the thermostat(s).
*
* @author Mark Hilbush - Initial contribution
*/
@NonNullByDefault
public class RevisionDTO {
/*
* The thermostat identifier.
*/
public String identifier;
/*
* The thermostat name, otherwise an empty field if one is not set.
*/
public @Nullable String name;
/*
* Whether the thermostat is currently connected to the ecobee servers.
*/
public @Nullable Boolean connected;
/*
* Current thermostat revision. This revision is incremented whenever
* the thermostat program, hvac mode, settings or configuration change.
* Changes to the following objects will update the thermostat revision:
* Settings, Program, Event, Device.
*/
public String thermostatRevision;
/*
* Current revision of the thermostat alerts. This revision is incremented
* whenever a new Alert is issued or an Alert is modified (acknowledged or deferred).
*/
public String alertsRevision;
/*
* 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 or Remote Sensor readings. The shortest interval
* this revision may change is 3 minutes.
*/
public String runtimeRevision;
/*
* The current revision of the thermostat interval runtime settings. This
* revision is incremented whenever the thermostat transmits a new status message
* in the form of a Runtime object. The thermostat updates this on a 15 minute interval.
*/
public String intervalRevision;
public RevisionDTO() {
identifier = "";
thermostatRevision = "";
alertsRevision = "";
runtimeRevision = "";
intervalRevision = "";
}
public String getId() {
return identifier;
}
public RevisionDTO getThis() {
return this;
}
public boolean hasChanged(@Nullable RevisionDTO previous) {
if (previous == null) {
return true;
}
return !(thermostatRevision.equals(previous.thermostatRevision)
&& alertsRevision.equals(previous.alertsRevision) && runtimeRevision.equals(previous.runtimeRevision)
&& intervalRevision.equals(previous.intervalRevision));
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("id:").append(identifier);
sb.append(" tstat:").append(thermostatRevision);
sb.append(" alert:").append(alertsRevision);
sb.append(" rntim:").append(runtimeRevision);
sb.append(" intrv:").append(intervalRevision);
return sb.toString();
}
}

View File

@@ -0,0 +1,53 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.dto.thermostat.summary;
import java.lang.reflect.Type;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
/**
* The {@link RevisionDTODeserializer} is responsible for
*
* @author Mark Hilbush - Initial contribution
*/
@NonNullByDefault
public class RevisionDTODeserializer implements JsonDeserializer<@Nullable RevisionDTO> {
@Override
public @Nullable RevisionDTO deserialize(@Nullable JsonElement json, @Nullable Type typeOfT,
@Nullable JsonDeserializationContext context) throws JsonParseException {
if (json == null || typeOfT == null || context == null) {
return null;
}
String[] fields = json.getAsString().split(":");
if (fields.length < 7) {
throw new JsonParseException("unable to parse RevisionList");
}
RevisionDTO revisionList = new RevisionDTO();
revisionList.identifier = fields[0];
revisionList.name = fields[1];
revisionList.connected = "true".equals(fields[2]);
revisionList.thermostatRevision = fields[3];
revisionList.alertsRevision = fields[4];
revisionList.runtimeRevision = fields[5];
revisionList.intervalRevision = fields[6];
return revisionList;
}
}

View File

@@ -0,0 +1,81 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.dto.thermostat.summary;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
/**
* The {@link RunningDTO} contains information indicating what equipment
* is running.
*
* @author John Cocula - Initial contribution
* @author Mark Hilbush - Adapt for OH2/3
*/
@NonNullByDefault
public class RunningDTO {
/*
* The thermostat identifier.
*/
public String identifier;
/*
* 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.
*/
public final Set<String> runningEquipment;
public RunningDTO() {
identifier = "";
runningEquipment = new HashSet<>();
}
public String getId() {
return identifier;
}
public RunningDTO getThis() {
return this;
}
public boolean hasChanged(@Nullable RunningDTO previous) {
if (previous == null) {
return true;
}
return !runningEquipment.equals(previous.runningEquipment);
}
public boolean isIdle() {
return runningEquipment.isEmpty();
}
public boolean isHeating() {
return runningEquipment.contains("heatPump") || runningEquipment.contains("heatPump2")
|| runningEquipment.contains("heatPump3") || runningEquipment.contains("auxHeat1")
|| runningEquipment.contains("auxHeat2") || runningEquipment.contains("auxHeat3");
}
public boolean isCooling() {
return runningEquipment.contains("compCool1") || runningEquipment.contains("compCool2");
}
public boolean isRunning(final String equipment) {
return runningEquipment.contains(equipment);
}
}

View File

@@ -0,0 +1,55 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.dto.thermostat.summary;
import java.lang.reflect.Type;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
/**
* The {@link RunningDTODeserializer} is responsible for
*
* @author Mark Hilbush - Initial contribution
*/
@NonNullByDefault
public class RunningDTODeserializer implements JsonDeserializer<@Nullable RunningDTO> {
@Override
public @Nullable RunningDTO deserialize(@Nullable JsonElement json, @Nullable Type typeOfT,
@Nullable JsonDeserializationContext context) throws JsonParseException {
if (json == null || typeOfT == null || context == null) {
return null;
}
String[] fields = json.getAsString().split(":");
if (fields.length < 1) {
throw new JsonParseException("unable to parse StatusList");
}
RunningDTO statusList = new RunningDTO();
statusList.identifier = fields[0];
if (fields.length >= 2) {
for (String equip : fields[1].split(",")) {
if (equip.length() == 0) {
continue;
}
statusList.runningEquipment.add(equip);
}
}
return statusList;
}
}

View File

@@ -0,0 +1,132 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.dto.thermostat.summary;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.ecobee.internal.dto.AbstractResponseDTO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.annotations.SerializedName;
/**
* The {@link SummaryResponseDTO} contains the
*
* @author Mark Hilbush - Initial contribution
*/
@NonNullByDefault
public class SummaryResponseDTO extends AbstractResponseDTO {
private final transient Logger logger = LoggerFactory.getLogger(SummaryResponseDTO.class);
/*
* Number of thermostats listed in the Revision List.
*/
public @Nullable Integer thermostatCount;
/*
* The list of CSV revision values.
*/
public @Nullable List<RevisionDTO> revisionList;
/*
* The list of CSV status values.
*/
@SerializedName("statusList")
public @Nullable List<RunningDTO> runningList;
public boolean hasChanged(@Nullable SummaryResponseDTO previousSummary) {
if (previousSummary == null) {
logger.debug("SummaryResponse: No previous summary");
return true;
}
boolean changed = false;
if (revisionHasChanged(previousSummary.revisionList)) {
changed = true;
}
if (runningHasChanged(previousSummary.runningList)) {
changed = true;
}
return changed;
}
private boolean revisionHasChanged(@Nullable List<RevisionDTO> previousList) {
List<RevisionDTO> currentList = revisionList;
if (previousList == null || currentList == null) {
logger.debug("SummaryResponse: Previous and/or current revision list is null");
return true;
}
// Check to see if there are different thermostat Ids in current vs previous
Set<String> previousIds = previousList.stream().map(RevisionDTO::getId).collect(Collectors.toSet());
Set<String> currentIds = currentList.stream().map(RevisionDTO::getId).collect(Collectors.toSet());
if (!previousIds.equals(currentIds)) {
logger.debug("SummaryResponse: Thermostat id maps are different");
logger.trace(" : Curr: {}", Arrays.toString(currentIds.toArray()));
logger.trace(" : Prev: {}", Arrays.toString(previousIds.toArray()));
return true;
}
// Create a map of each thermostat id with its RevisionDTO object
Map<String, RevisionDTO> previousMap = previousList.stream()
.collect(Collectors.toMap(RevisionDTO::getId, RevisionDTO::getThis));
// Go through list of current RevisionDTOs to see if something has changed
for (RevisionDTO current : currentList) {
RevisionDTO previous = previousMap.get(current.getId());
if (current.hasChanged(previous)) {
logger.debug("SummaryResponse: Revisions has changed");
logger.trace(" : Curr: {}", current.toString());
logger.trace(" : Prev: {}", previous.toString());
return true;
}
}
return false;
}
private boolean runningHasChanged(@Nullable List<RunningDTO> previousList) {
List<RunningDTO> currentList = runningList;
if (previousList == null || currentList == null) {
logger.debug("SummaryResponse: Previous and/or current running list is null");
return true;
}
// Check to see if there are different thermostat Ids in current vs previous
Set<String> previousIds = previousList.stream().map(RunningDTO::getId).collect(Collectors.toSet());
Set<String> currentIds = currentList.stream().map(RunningDTO::getId).collect(Collectors.toSet());
if (!previousIds.equals(currentIds)) {
logger.debug("SummaryResponse: Thermostat id maps are different");
logger.trace(" : Curr: {}", Arrays.toString(currentIds.toArray()));
logger.trace(" : Prev: {}", Arrays.toString(previousIds.toArray()));
return true;
}
// Create a map of each thermostat id with its RunningDTO object
Map<String, RunningDTO> previousMap = previousList.stream()
.collect(Collectors.toMap(RunningDTO::getId, RunningDTO::getThis));
// Go through list of current RunningDTOs to see if something has changed
for (RunningDTO current : currentList) {
RunningDTO previous = previousMap.get(current.getId());
if (current.hasChanged(previous)) {
logger.debug("SummaryResponse: Running Equipment has changed");
logger.trace(" : Curr: {}", current.toString());
logger.trace(" : Prev: {}", previous.toString());
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,61 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.enums;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import com.google.gson.annotations.SerializedName;
/**
* The {@link AckType} represents the types of alert acknowledgements.
*
* @author Mark Hilbush - Initial contribution
*/
@NonNullByDefault
public enum AckType {
@SerializedName("accept")
ACCEPT("accept"),
@SerializedName("decline")
DECLINE("decline"),
@SerializedName("defer")
DEFER("defer"),
@SerializedName("unacknowledged")
UNACKNOWLEDGED("unacknowledged");
private final String type;
private AckType(String type) {
this.type = type;
}
public static AckType forValue(@Nullable String v) {
if (v != null) {
for (AckType at : AckType.values()) {
if (at.type.equals(v)) {
return at;
}
}
}
throw new IllegalArgumentException("Invalid or null ack type: " + v);
}
@Override
public String toString() {
return this.type;
}
}

View File

@@ -0,0 +1,80 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.enums;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import com.google.gson.annotations.SerializedName;
/**
* The {@link EquipmentNotificationType} represents the types of equipment notifications.
*
* @author Mark Hilbush - Initial contribution
*/
@NonNullByDefault
public enum EquipmentNotificationType {
@SerializedName("hvac")
HVAC("hvac"),
@SerializedName("furnaceFilter")
FURNACE_FILTER("furnaceFilter"),
@SerializedName("humidifierFilter")
HUMIDIFIER_FILTER("humidifierFilter"),
@SerializedName("dehumidifierFilter")
DEHUNIDIFIER_FILTER("dehumidifierFilter"),
@SerializedName("ventilator")
VENTILATOR("ventilator"),
@SerializedName("ac")
AC("ac"),
@SerializedName("airFilter")
AIR_FILTER("airFilter"),
@SerializedName("airCleaner")
AIR_CLEANER("airCleaner"),
@SerializedName("uvLamp")
UV_LAMP("uvLamp");
private final String mode;
private EquipmentNotificationType(String mode) {
this.mode = mode;
}
public String value() {
return mode;
}
public static EquipmentNotificationType forValue(@Nullable String v) {
if (v != null) {
for (EquipmentNotificationType vm : EquipmentNotificationType.values()) {
if (vm.mode.equals(v)) {
return vm;
}
}
}
throw new IllegalArgumentException("Invalid vent: " + v);
}
@Override
public String toString() {
return this.mode;
}
}

View File

@@ -0,0 +1,57 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.enums;
import org.eclipse.jdt.annotation.NonNullByDefault;
import com.google.gson.annotations.SerializedName;
/**
* The {@link FanMode} represents the possible fan modes.
*
* @author John Cocula - Initial contribution
* @author Mark Hilbush - Adapt for OH2/3
*/
@NonNullByDefault
public enum FanMode {
@SerializedName("auto")
AUTO("auto"),
@SerializedName("on")
ON("on");
private final String mode;
private FanMode(final String mode) {
this.mode = mode;
}
public String value() {
return mode;
}
public static FanMode forValue(String v) {
for (FanMode fm : FanMode.values()) {
if (fm.mode.equals(v)) {
return fm;
}
}
throw new IllegalArgumentException("Invalid fan mode: " + v);
}
@Override
public String toString() {
return this.mode;
}
}

View File

@@ -0,0 +1,73 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.enums;
import org.eclipse.jdt.annotation.NonNullByDefault;
import com.google.gson.annotations.SerializedName;
/**
* The {@link HoldType} represents the possible hold types.
*
* @author John Cocula - Initial contribution
* @author Mark Hilbush - Adapt for OH2/3
*/
@NonNullByDefault
public enum HoldType {
/**
* Use the provided startDate, startTime, endDate and endTime for the event.
* If start date/time is not provided, it will be assumed to be right now.
* End date/time is required.
*/
@SerializedName("dateTime")
DATE_TIME("dateTime"),
/**
* The end date/time will be set to the next climate transition in the program.
*/
@SerializedName("nextTransition")
NEXT_TRANSITION("nextTransition"),
/**
* The hold will not end and require to be cancelled explicitly.
*/
@SerializedName("indefinite")
INDEFINITE("indefinite"),
/**
* Use the value in the holdHours parameter to set the end date/time for the event.
*/
@SerializedName("holdHours")
HOLD_HOURS("holdHours");
private final String type;
private HoldType(final String type) {
this.type = type;
}
public static HoldType forValue(String v) {
for (HoldType ht : HoldType.values()) {
if (ht.type.equals(v)) {
return ht;
}
}
throw new IllegalArgumentException("Invalid hold type: " + v);
}
@Override
public String toString() {
return this.type;
}
}

View File

@@ -0,0 +1,71 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.enums;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import com.google.gson.annotations.SerializedName;
/**
* The {@link LimitNotificationType} represents the types of limit notifications.
*
* @author Mark Hilbush - Initial contribution
*/
@NonNullByDefault
public enum LimitNotificationType {
@SerializedName("lowTemp")
LOW_TEMP("lowTemp"),
@SerializedName("highTemp")
HIGH_TEMP("highTemp"),
@SerializedName("lowHumidity")
LOW_HUMIDITY("lowHumidity"),
@SerializedName("highHumidity")
HIGH_HUMIDITY("highHumidity"),
@SerializedName("auxHeat")
AUX_HEAT("auxHeat"),
@SerializedName("auxOutdoor")
AUX_OUTDOOR("auxOutdoor");
private final String mode;
private LimitNotificationType(String mode) {
this.mode = mode;
}
public String value() {
return mode;
}
public static LimitNotificationType forValue(@Nullable String v) {
if (v != null) {
for (LimitNotificationType vm : LimitNotificationType.values()) {
if (vm.mode.equals(v)) {
return vm;
}
}
}
throw new IllegalArgumentException("Invalid vent: " + v);
}
@Override
public String toString() {
return this.mode;
}
}

View File

@@ -0,0 +1,71 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.enums;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import com.google.gson.annotations.SerializedName;
/**
* The {@link PlugState} defines the possible plug states.
*
* @author Mark Hilbush - Initial contribution
*/
@NonNullByDefault
public enum PlugState {
/**
* Sets the plug into the on state for the start/end period specified.
* Creates a plug hold in the on state.
*/
@SerializedName("on")
ON("on"),
/**
* Sets the plug into the off state for the start/end period specified.
* Creates a plug hold in the off state.
*/
@SerializedName("off")
OFF("off"),
/**
* Causes the plug to resume its regular program and to follow it. Removes
* the currently active plug hold, if no hold is currently running, nothing
* happens. No other optional properties are used.
*/
@SerializedName("resume")
RESUME("resume");
private final String state;
private PlugState(String state) {
this.state = state;
}
public static PlugState forValue(@Nullable String v) {
if (v != null) {
for (PlugState ps : PlugState.values()) {
if (ps.state.equals(v)) {
return ps;
}
}
}
throw new IllegalArgumentException("Invalid plug state: " + v);
}
@Override
public String toString() {
return this.state;
}
}

View File

@@ -0,0 +1,65 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.enums;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import com.google.gson.annotations.SerializedName;
/**
* The {@link VentilatorMode} defines the possible ventilator modes.
*
* @author Mark Hilbush - Initial contribution
*/
@NonNullByDefault
public enum VentilatorMode {
@SerializedName("auto")
AUTO("auto"),
@SerializedName("minontime")
MIN_ON_TIME("minontime"),
@SerializedName("on")
ON("on"),
@SerializedName("off")
OFF("off");
private final String mode;
private VentilatorMode(String mode) {
this.mode = mode;
}
public String value() {
return mode;
}
public static VentilatorMode forValue(@Nullable String v) {
if (v != null) {
for (VentilatorMode vm : VentilatorMode.values()) {
if (vm.mode.equals(v)) {
return vm;
}
}
}
throw new IllegalArgumentException("Invalid vent: " + v);
}
@Override
public String toString() {
return this.mode;
}
}

View File

@@ -0,0 +1,47 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.function;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
import com.google.gson.annotations.Expose;
/**
* The {@link AbstractFunction} defines the base class used by all
* thermostat functions.
*
* @author Mark Hilbush - Initial contribution
*/
@NonNullByDefault
public abstract class AbstractFunction {
@Expose(serialize = false)
protected static final DateFormat YMD = new SimpleDateFormat("yyyy-MM-dd");
@Expose(serialize = false)
protected static final DateFormat HMS = new SimpleDateFormat("HH:mm:ss");
public String type;
public Map<String, Object> params;
public AbstractFunction(String type) {
this.type = type;
this.params = new HashMap<>();
}
}

View File

@@ -0,0 +1,42 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.function;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.ecobee.internal.enums.AckType;
/**
* The acknowledge function allows an alert to be acknowledged.
*
* @author John Cocula - Initial contribution
* @author Mark Hilbush - Adapt for OH2/3
*/
@NonNullByDefault
public final class AcknowledgeFunction extends AbstractFunction {
public AcknowledgeFunction(@Nullable String thermostatIdentifier, @Nullable String ackRef,
@Nullable AckType ackType, @Nullable Boolean remindMeLater) {
super("acknowledge");
if (thermostatIdentifier == null || ackRef == null || ackType == null) {
throw new IllegalArgumentException("thermostatIdentifier, ackRef and ackType are required.");
}
params.put("thermostatIdentifier", thermostatIdentifier);
params.put("ackRef", ackRef);
params.put("ackType", ackType);
if (remindMeLater != null) {
params.put("remindMeLater", remindMeLater);
}
}
}

View File

@@ -0,0 +1,63 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.function;
import java.util.Date;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.ecobee.internal.enums.HoldType;
import org.openhab.binding.ecobee.internal.enums.PlugState;
/**
* Control the on/off state of a plug by setting a hold on the plug. Creates a hold for the on or off state of the plug
* for the specified duration. Note that an event is created regardless of whether the program is in the same state as
* the requested state.
*
* @author John Cocula - Initial contribution
* @author Mark Hilbush - Adapt for OH2/3
*/
@NonNullByDefault
public final class ControlPlugFunction extends AbstractFunction {
public ControlPlugFunction(@Nullable String plugName, @Nullable PlugState plugState, @Nullable Date startDateTime,
@Nullable Date endDateTime, @Nullable HoldType holdType, @Nullable Integer holdHours) {
super("controlPlug");
if (plugName == null || plugState == null) {
throw new IllegalArgumentException("plugName and plugState arguments are required.");
}
if (holdType == HoldType.DATE_TIME && endDateTime == null) {
throw new IllegalArgumentException("End date/time is required for dateTime hold type.");
}
if (holdType == HoldType.HOLD_HOURS && holdHours == null) {
throw new IllegalArgumentException("holdHours must be specified when using holdHours hold type.");
}
params.put("plugName", plugName);
params.put("plugState", plugState);
if (startDateTime != null) {
params.put("startDate", YMD.format(startDateTime));
params.put("startTime", HMS.format(startDateTime));
}
if (endDateTime != null) {
params.put("endDate", YMD.format(endDateTime));
params.put("endTime", HMS.format(endDateTime));
}
if (holdType != null) {
params.put("holdType", holdType);
}
if (holdHours != null) {
params.put("holdHours", holdHours);
}
}
}

View File

@@ -0,0 +1,73 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.function;
import java.util.Date;
import javax.measure.quantity.Temperature;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.ecobee.internal.enums.FanMode;
import org.openhab.core.library.types.QuantityType;
import org.openhab.core.library.unit.ImperialUnits;
/**
* The create vacation function creates a vacation event on the thermostat. If the
* start/end date/times are not provided for the vacation event, the vacation event
* will begin immediately and last 14 days. If both the coolHoldTemp and heatHoldTemp
* parameters provided to this function have the same value, and the Thermostat is
* in auto mode, then the two values will be adjusted during processing to be
* separated by the value stored in Thermostat.Settings#heatCoolMinDelta.
*
* @author John Cocula - Initial contribution
* @author Mark Hilbush - Adapt for OH2/3
*/
@NonNullByDefault
public final class CreateVacationFunction extends AbstractFunction {
public CreateVacationFunction(@Nullable String name, @Nullable QuantityType<Temperature> coolHoldTemp,
@Nullable QuantityType<Temperature> heatHoldTemp, @Nullable Date startDateTime, @Nullable Date endDateTime,
@Nullable FanMode fan, @Nullable Integer fanMinOnTime) {
super("createVacation");
if (name == null || coolHoldTemp == null || heatHoldTemp == null) {
throw new IllegalArgumentException("name, coolHoldTemp and heatHoldTemp arguments are required.");
}
params.put("name", name);
QuantityType<Temperature> convertedCoolHoldTemp = coolHoldTemp.toUnit(ImperialUnits.FAHRENHEIT);
QuantityType<Temperature> convertedHeatHoldTemp = heatHoldTemp.toUnit(ImperialUnits.FAHRENHEIT);
if (convertedCoolHoldTemp == null || convertedHeatHoldTemp == null) {
throw new IllegalArgumentException("coolHoldTemp or heatHoldTemp are not proper QuantityTypes");
}
params.put("coolHoldTemp", Integer.valueOf(convertedCoolHoldTemp.intValue()));
params.put("heatHoldTemp", Integer.valueOf(convertedHeatHoldTemp.intValue()));
if (startDateTime != null) {
params.put("startDate", YMD.format(startDateTime));
params.put("startTime", HMS.format(startDateTime));
}
if (endDateTime != null) {
params.put("endDate", YMD.format(endDateTime));
params.put("endTime", HMS.format(endDateTime));
}
if (fan != null) {
params.put("fan", fan);
}
if (fanMinOnTime != null) {
// doc says String not Integer for fanMinOnTime parameter (@watou)
params.put("fanMinOnTime", fanMinOnTime.toString());
}
}
}

View File

@@ -0,0 +1,36 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.function;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
/**
* The delete vacation function deletes a vacation event from a thermostat. This is the
* only way to cancel a vacation event. This method is able to remove vacation
* events not yet started and scheduled in the future.
*
* @author John Cocula - Initial contribution
* @author Mark Hilbush - Adapt for OH2/3
*/
@NonNullByDefault
public final class DeleteVacationFunction extends AbstractFunction {
public DeleteVacationFunction(@Nullable String name) {
super("deleteVacation");
if (name == null) {
throw new IllegalArgumentException("name argument is required.");
}
params.put("name", name);
}
}

View File

@@ -0,0 +1,37 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.function;
import java.util.List;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.ecobee.internal.dto.SelectionDTO;
/**
* The {@link FunctionRequest} encapsulates functions that are to be sent to the
* Ecobee API.
*
* @author Mark Hilbush - Initial contribution
*/
@NonNullByDefault
public class FunctionRequest {
public FunctionRequest(SelectionDTO selection) {
this.selection = selection;
}
public SelectionDTO selection;
public @Nullable List<AbstractFunction> functions;
}

View File

@@ -0,0 +1,34 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.function;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* The reset preferences function sets all of the user configurable settings back to
* the factory default values. This function call will not only reset the top level
* thermostat settings such as hvacMode, lastServiceDate and vent, but also all of
* the user configurable fields of the thermostat.settings and thermostat.program
* objects. Note that this does not reset all values. For example, the installer
* settings and wifi details remain untouched.
*
* @author John Cocula - Initial contribution
* @author Mark Hilbush - Adapt for OH2/3
*/
@NonNullByDefault
public final class ResetPreferencesFunction extends AbstractFunction {
public ResetPreferencesFunction() {
super("resetPreferences");
}
}

View File

@@ -0,0 +1,39 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.function;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
/**
* The resume program function removes the currently running event providing the event
* is not a mandatory demand response event. The top active event is removed from the
* stack and the thermostat resumes its program, or enters the next event in the stack
* if one exists. This function may need to be called multiple times in order to resume all
* events. Sending 3 resume functions in a row will resume the thermostat to its program
* in all instances. Note that vacation events cannot be resumed, you must delete the
* vacation event using the DeleteVacationFunction.
*
* @author John Cocula - Initial contribution
* @author Mark Hilbush - Adapt for OH2/3
*/
@NonNullByDefault
public final class ResumeProgramFunction extends AbstractFunction {
public ResumeProgramFunction(@Nullable Boolean resumeAll) {
super("resumeProgram");
if (resumeAll != null) {
params.put("resumeAll", resumeAll);
}
}
}

View File

@@ -0,0 +1,35 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.function;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
/**
* The send message function allows an alert message to be sent to the thermostat. The
* message properties are same as those of the Alert object.
*
* @author John Cocula - Initial contribution
* @author Mark Hilbush - Adapt for OH2/3
*/
@NonNullByDefault
public final class SendMessageFunction extends AbstractFunction {
public SendMessageFunction(final @Nullable String text) {
super("sendMessage");
if (text == null) {
throw new IllegalArgumentException("text is required.");
}
params.put("text", text);
}
}

View File

@@ -0,0 +1,130 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.function;
import java.util.Date;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.ecobee.internal.dto.thermostat.EventDTO;
import org.openhab.binding.ecobee.internal.enums.HoldType;
/**
* The set hold function sets the thermostat into a hold with the specified temperature.
* Creates a hold for the specified duration. Note that an event is created regardless
* of whether the program is in the same state as the requested state. There is also
* support for creating a hold by passing a holdClimateRef request parameter/value pair
* to this function (See Event). When an existing and valid Thermostat.Climate#climateRef
* value is passed to this function, the coolHoldTemp, heatHoldTemp and fan mode from
* that Thermostat.Climate are used in the creation of the hold event. The values from
* that Climate will take precedence over any coolHoldTemp, heatHoldTemp, and fan
* mode parameters passed into this function separately. To resume from a hold and
* return to the program, use the ResumeProgramFunction.
*
* @author John Cocula - Initial contribution
* @author Mark Hilbush - Adapt for OH2/3
*/
@NonNullByDefault
public final class SetHoldFunction extends AbstractFunction {
public SetHoldFunction(@Nullable EventDTO event, @Nullable HoldType holdType, @Nullable Integer holdHours,
@Nullable Date startDateTime, @Nullable Date endDateTime) {
super("setHold");
if (event == null) {
throw new IllegalArgumentException("event must not be null");
}
if (holdType == HoldType.HOLD_HOURS && holdHours == null) {
throw new IllegalArgumentException("holdHours must be specified when holdType='holdHours'");
} else if (holdType == HoldType.DATE_TIME && endDateTime == null) {
throw new IllegalArgumentException("endDateTime must be specified when holdType='dateTime'");
}
if (event.holdClimateRef == null) {
if (Boolean.TRUE.equals(event.isTemperatureAbsolute) && Boolean.TRUE.equals(event.isTemperatureRelative)) {
throw new IllegalArgumentException("cannot set both absolute and relative temperatures");
}
if (Boolean.TRUE.equals(event.isTemperatureAbsolute)
&& (event.coolHoldTemp == null || event.heatHoldTemp == null)) {
throw new IllegalArgumentException(
"coolHoldTemp and heatHoldTemp must be specified when 'isTemperatureAbsolute' is true");
}
if (Boolean.TRUE.equals(event.isTemperatureRelative)
&& (event.coolRelativeTemp == null || event.heatRelativeTemp == null)) {
throw new IllegalArgumentException(
"coolRelativeTemp and heatRelativeTemp must be specified when 'isTemperatureRelative' is true");
}
}
// Make parameters from the input event
if (event.isOccupied != null) {
params.put("isOccupied", event.isOccupied);
}
if (event.isCoolOff != null) {
params.put("isCoolOff", event.isCoolOff);
}
if (event.isHeatOff != null) {
params.put("isHeatOff", event.isHeatOff);
}
if (event.coolHoldTemp != null) {
params.put("coolHoldTemp", event.coolHoldTemp);
}
if (event.heatHoldTemp != null) {
params.put("heatHoldTemp", event.heatHoldTemp);
}
if (event.fan != null) {
params.put("fan", event.fan);
}
if (event.vent != null) {
params.put("vent", event.vent);
}
if (event.ventilatorMinOnTime != null) {
params.put("ventilatorMinOnTime", event.ventilatorMinOnTime);
}
if (event.isOptional != null) {
params.put("isOptional", event.isOptional);
}
if (event.isTemperatureRelative != null) {
params.put("isTemperatureRelative", event.isTemperatureRelative);
}
if (event.coolRelativeTemp != null) {
params.put("coolRelativeTemp", event.coolRelativeTemp);
}
if (event.heatRelativeTemp != null) {
params.put("heatRelativeTemp", event.heatRelativeTemp);
}
if (event.isTemperatureAbsolute != null) {
params.put("isTemperatureAbsolute", event.isTemperatureAbsolute);
}
if (event.fanMinOnTime != null) {
params.put("fanMinOnTime", event.fanMinOnTime);
}
if (event.holdClimateRef != null) {
params.put("holdClimateRef", event.holdClimateRef);
}
// Make parameters from the holdType and hold options
if (holdType != null) {
params.put("holdType", holdType);
}
if (holdHours != null) {
params.put("holdHours", holdHours);
}
if (startDateTime != null) {
params.put("startDate", YMD.format(startDateTime));
params.put("startTime", HMS.format(startDateTime));
}
if (endDateTime != null) {
params.put("endDate", YMD.format(endDateTime));
params.put("endTime", HMS.format(endDateTime));
}
}
}

View File

@@ -0,0 +1,70 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.function;
import java.util.Date;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.ecobee.internal.enums.HoldType;
/**
* The set occupied function may only be used by EMS thermostats. The function switches a
* thermostat from occupied mode to unoccupied, or vice versa. If used on a Smart thermostat,
* the function will throw an error. Switch occupancy events are treated as Holds.
* There may only be one Switch Occupancy at one time, and the new event will replace any previous event.
* Note that an occupancy event is created regardless what the program on the thermostat is set
* to. For example, if the program is currently unoccupied and you set occupied=false, an
* occupancy event will be created using the heat/cool settings of the unoccupied program
* climate. If your intent is to go back to the program and remove the occupancy event,
* use ResumeProgramFunction instead.
*
* @author John Cocula - Initial contribution
* @author Mark Hilbush - Adapt for OH2/3
*/
@NonNullByDefault
public final class SetOccupiedFunction extends AbstractFunction {
public SetOccupiedFunction(@Nullable Boolean occupied, @Nullable Date startDateTime, @Nullable Date endDateTime,
@Nullable HoldType holdType, @Nullable Integer holdHours) {
super("setOccupied"); // not in doc; assuming
if (occupied == null) {
throw new IllegalArgumentException("occupied state is required.");
}
params.put("occupied", occupied);
if (startDateTime != null) {
params.put("startDate", YMD.format(startDateTime));
params.put("startTime", HMS.format(startDateTime));
}
if (endDateTime != null) {
params.put("endDate", YMD.format(endDateTime));
params.put("endTime", HMS.format(endDateTime));
}
if (holdType == HoldType.HOLD_HOURS && holdHours == null) {
throw new IllegalArgumentException("holdHours must be specified when holdType='holdHours'");
}
if (holdType == HoldType.DATE_TIME && endDateTime == null) {
throw new IllegalArgumentException("endDateTime must be specific when holdType='dateTime'");
}
if (holdType != null) {
params.put("holdType", holdType);
}
if (holdHours != null) {
params.put("holdHours", holdHours);
}
}
}

View File

@@ -0,0 +1,43 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.function;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
/**
* The update sensor function allows the caller to update the name of an ecobee3 remote sensor.
* Each ecobee3 remote sensor "enclosure" contains two distinct sensors types temperature
* and occupancy. Only one of the sensors is required in the request. Both of the sensors'
* names will be updated to ensure consistency as they are part of the same remote sensor
* enclosure. This also reflects accurately what happens on the Thermostat itself. Note: This
* function is restricted to the ecobee3 thermostat model only.
*
* @author John Cocula - Initial contribution
* @author Mark Hilbush - Adapt for OH2/3
*/
@NonNullByDefault
public final class UpdateSensorFunction extends AbstractFunction {
public UpdateSensorFunction(@Nullable final String name, @Nullable final String deviceId,
@Nullable final String sensorId) {
super("updateSensor");
if (name == null || deviceId == null || sensorId == null) {
throw new IllegalArgumentException("name, deviceId and sensorId arguments are required.");
}
params.put("name", name);
params.put("deviceId", deviceId);
params.put("sensorId", sensorId);
}
}

View File

@@ -0,0 +1,287 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.handler;
import static org.openhab.binding.ecobee.internal.EcobeeBindingConstants.CONFIG_THERMOSTAT_ID;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
import org.openhab.binding.ecobee.internal.api.EcobeeApi;
import org.openhab.binding.ecobee.internal.config.EcobeeAccountConfiguration;
import org.openhab.binding.ecobee.internal.discovery.ThermostatDiscoveryService;
import org.openhab.binding.ecobee.internal.dto.SelectionDTO;
import org.openhab.binding.ecobee.internal.dto.thermostat.ThermostatDTO;
import org.openhab.binding.ecobee.internal.dto.thermostat.ThermostatUpdateRequestDTO;
import org.openhab.binding.ecobee.internal.dto.thermostat.summary.SummaryResponseDTO;
import org.openhab.binding.ecobee.internal.function.FunctionRequest;
import org.openhab.core.auth.client.oauth2.OAuthFactory;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.binding.BaseBridgeHandler;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerService;
import org.openhab.core.types.Command;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link EcobeeAccountBridgeHandler} is responsible for managing
* communication with the Ecobee API.
*
* @author Mark Hilbush - Initial contribution
*/
@NonNullByDefault
public class EcobeeAccountBridgeHandler extends BaseBridgeHandler {
private static final int REFRESH_STARTUP_DELAY_SECONDS = 3;
private static final int REFRESH_INTERVAL_SECONDS = 1;
private static final int DISCOVERY_INTERVAL_SECONDS = 300;
private static final int DISCOVERY_INITIAL_DELAY_SECONDS = 10;
private static final int DEFAULT_REFRESH_INTERVAL_NORMAL_SECONDS = 20;
private static final int DEFAULT_REFRESH_INTERVAL_QUICK_SECONDS = 5;
private static final int DEFAULT_API_TIMEOUT_SECONDS = 20;
private final Logger logger = LoggerFactory.getLogger(EcobeeAccountBridgeHandler.class);
private final OAuthFactory oAuthFactory;
private final HttpClient httpClient;
private @NonNullByDefault({}) EcobeeApi api;
private @NonNullByDefault({}) String apiKey;
private int refreshIntervalNormal;
private int refreshIntervalQuick;
private int apiTimeout;
private boolean discoveryEnabled;
private int discoveryInterval;
private final Map<String, EcobeeThermostatBridgeHandler> thermostatHandlers = new ConcurrentHashMap<>();
private final Set<String> thermostatIds = new CopyOnWriteArraySet<>();
private @Nullable Future<?> refreshThermostatsJob;
private final AtomicInteger refreshThermostatsCounter = new AtomicInteger(REFRESH_STARTUP_DELAY_SECONDS);
private final AtomicInteger discoveryCounter = new AtomicInteger(DISCOVERY_INITIAL_DELAY_SECONDS);
private @Nullable ThermostatDiscoveryService discoveryService;
private @Nullable SummaryResponseDTO previousSummary;
public EcobeeAccountBridgeHandler(final Bridge bridge, OAuthFactory oAuthFactory, HttpClient httpClient) {
super(bridge);
this.oAuthFactory = oAuthFactory;
this.httpClient = httpClient;
}
@Override
public void initialize() {
logger.debug("AccountBridge: Initializing");
EcobeeAccountConfiguration config = getConfigAs(EcobeeAccountConfiguration.class);
apiKey = config.apiKey;
Integer value;
value = config.refreshIntervalNormal;
refreshIntervalNormal = value == null ? DEFAULT_REFRESH_INTERVAL_NORMAL_SECONDS : value;
value = config.refreshIntervalQuick;
refreshIntervalQuick = value == null ? DEFAULT_REFRESH_INTERVAL_QUICK_SECONDS : value;
value = config.apiTimeout;
apiTimeout = (value == null ? DEFAULT_API_TIMEOUT_SECONDS : value) * 1000;
Boolean booleanValue = config.discoveryEnabled;
discoveryEnabled = booleanValue == null ? false : booleanValue.booleanValue();
logger.debug("AccountBridge: Thermostat and sensor discovery is {}", discoveryEnabled ? "enabled" : "disabled");
value = config.discoveryInterval;
discoveryInterval = value == null ? DISCOVERY_INTERVAL_SECONDS : value;
api = new EcobeeApi(this, apiKey, apiTimeout, oAuthFactory, httpClient);
scheduleRefreshJob();
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_PENDING, "Checking authorization");
}
@Override
public void dispose() {
cancelRefreshJob();
api.closeOAuthClientService();
logger.debug("AccountBridge: Disposing");
}
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
}
@Override
public Collection<Class<? extends ThingHandlerService>> getServices() {
return Collections.singleton(ThermostatDiscoveryService.class);
}
@Override
public void childHandlerInitialized(ThingHandler thermostatHandler, Thing thermostatThing) {
String thermostatId = (String) thermostatThing.getConfiguration().get(CONFIG_THERMOSTAT_ID);
thermostatHandlers.put(thermostatId, (EcobeeThermostatBridgeHandler) thermostatHandler);
thermostatIds.add(thermostatId);
scheduleQuickPoll();
logger.debug("AccountBridge: Adding thermostat handler for {} with id {}", thermostatThing.getUID(),
thermostatId);
}
@Override
public void childHandlerDisposed(ThingHandler thermostatHandler, Thing thermostatThing) {
String thermostatId = (String) thermostatThing.getConfiguration().get(CONFIG_THERMOSTAT_ID);
thermostatHandlers.remove(thermostatId);
thermostatIds.remove(thermostatId);
logger.debug("AccountBridge: Removing thermostat handler for {} with id {}", thermostatThing.getUID(),
thermostatId);
}
public void setDiscoveryService(ThermostatDiscoveryService discoveryService) {
this.discoveryService = discoveryService;
}
public boolean isDiscoveryEnabled() {
return discoveryEnabled;
}
public void updateBridgeStatus(ThingStatus status) {
updateStatus(status);
}
public void updateBridgeStatus(ThingStatus status, ThingStatusDetail statusDetail, String statusMessage) {
updateStatus(status, statusDetail, statusMessage);
}
public boolean performThermostatFunction(FunctionRequest request) {
boolean success = api.performThermostatFunction(request);
if (success) {
scheduleQuickPoll();
}
return success;
}
public boolean performThermostatUpdate(ThermostatUpdateRequestDTO request) {
boolean success = api.performThermostatUpdate(request);
if (success) {
scheduleQuickPoll();
}
return success;
}
public SelectionDTO getSelection() {
SelectionDTO mergedSelection = new SelectionDTO();
for (EcobeeThermostatBridgeHandler handler : new ArrayList<EcobeeThermostatBridgeHandler>(
thermostatHandlers.values())) {
SelectionDTO selection = handler.getSelection();
logger.trace("AccountBridge: Thermostat {} selection: {}", handler.getThing().getUID(), selection);
mergedSelection.mergeSelection(selection);
}
return mergedSelection;
}
public void markOnline() {
updateStatus(ThingStatus.ONLINE);
}
public List<ThermostatDTO> getRegisteredThermostats() {
return api.queryRegisteredThermostats();
}
/*
* The refresh job
* - updates the thermostat channels on the refresh interval set in the thermostat thing config, and
* - runs the thermostat discovery on the refresh interval set in the thing config
*
* The thermostat update process involves first running a thermostat summary transaction to
* determine if any thermostat data has changed since the last summary. If any change is detected,
* a full query of the thermostats is performed.
*/
private void refresh() {
refreshThermostats();
discoverThermostats();
}
@SuppressWarnings("null")
private void refreshThermostats() {
if (refreshThermostatsCounter.getAndDecrement() == 0) {
refreshThermostatsCounter.set(refreshIntervalNormal);
SummaryResponseDTO summary = api.performThermostatSummaryQuery();
if (summary != null && summary.hasChanged(previousSummary) && !thermostatIds.isEmpty()) {
for (ThermostatDTO thermostat : api.performThermostatQuery(thermostatIds)) {
EcobeeThermostatBridgeHandler handler = thermostatHandlers.get(thermostat.identifier);
if (handler != null) {
handler.updateChannels(thermostat);
}
}
}
previousSummary = summary;
}
}
private void discoverThermostats() {
if (isDiscoveryEnabled()) {
if (discoveryCounter.getAndDecrement() == 0) {
discoveryCounter.set(discoveryInterval);
ThermostatDiscoveryService localDiscoveryService = discoveryService;
if (localDiscoveryService != null) {
logger.debug("AccountBridge: Running thermostat discovery");
localDiscoveryService.startBackgroundDiscovery();
}
}
}
}
private void scheduleQuickPoll() {
if (refreshThermostatsCounter.get() > refreshIntervalQuick) {
logger.debug("AccountBridge: Scheduling quick poll");
refreshThermostatsCounter.set(refreshIntervalQuick);
forceFullNextPoll();
}
}
private void scheduleRefreshJob() {
logger.debug("AccountBridge: Scheduling thermostat refresh job");
cancelRefreshJob();
refreshThermostatsCounter.set(0);
refreshThermostatsJob = scheduler.scheduleWithFixedDelay(this::refresh, REFRESH_STARTUP_DELAY_SECONDS,
REFRESH_INTERVAL_SECONDS, TimeUnit.SECONDS);
}
private void cancelRefreshJob() {
Future<?> localRefreshThermostatsJob = refreshThermostatsJob;
if (localRefreshThermostatsJob != null) {
forceFullNextPoll();
localRefreshThermostatsJob.cancel(true);
logger.debug("AccountBridge: Canceling thermostat refresh job");
}
}
private void forceFullNextPoll() {
previousSummary = null;
}
}

View File

@@ -0,0 +1,219 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.handler;
import static org.openhab.binding.ecobee.internal.EcobeeBindingConstants.*;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.lang.WordUtils;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.ecobee.internal.config.EcobeeSensorConfiguration;
import org.openhab.binding.ecobee.internal.dto.thermostat.RemoteSensorCapabilityDTO;
import org.openhab.binding.ecobee.internal.dto.thermostat.RemoteSensorDTO;
import org.openhab.core.library.unit.SmartHomeUnits;
import org.openhab.core.thing.Channel;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.ThingStatusInfo;
import org.openhab.core.thing.binding.BaseThingHandler;
import org.openhab.core.thing.binding.builder.ChannelBuilder;
import org.openhab.core.thing.binding.builder.ThingBuilder;
import org.openhab.core.thing.type.ChannelTypeUID;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link EcobeeSensorThingHandler} is responsible for updating the channels associated
* with an Ecobee remote sensor.
*
* @author Mark Hilbush - Initial contribution
*/
@NonNullByDefault
public class EcobeeSensorThingHandler extends BaseThingHandler {
public static final String CAPABILITY_ADC = "adc";
public static final String CAPABILITY_CO2 = "co2";
public static final String CAPABILITY_DRY_CONTACT = "dryContact";
public static final String CAPABILITY_HUMIDITY = "humidity";
public static final String CAPABILITY_OCCUPANCY = "occupancy";
public static final String CAPABILITY_TEMPERATURE = "temperature";
public static final String CAPABILITY_UNKNOWN = "unknown";
private final Logger logger = LoggerFactory.getLogger(EcobeeSensorThingHandler.class);
private @NonNullByDefault({}) String sensorId;
private Map<String, State> stateCache = new ConcurrentHashMap<>();
public EcobeeSensorThingHandler(Thing thing) {
super(thing);
}
@Override
public void initialize() {
sensorId = getConfigAs(EcobeeSensorConfiguration.class).sensorId;
logger.debug("SensorThing: Initializing sensor '{}'", sensorId);
clearSavedState();
updateStatus(EcobeeUtils.isBridgeOnline(getBridge()) ? ThingStatus.ONLINE : ThingStatus.OFFLINE);
}
@Override
public void dispose() {
logger.debug("SensorThing: Disposing sensor '{}'", sensorId);
}
@Override
public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
if (bridgeStatusInfo.getStatus() == ThingStatus.ONLINE) {
updateStatus(ThingStatus.ONLINE);
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
}
}
@SuppressWarnings("null")
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
if (command instanceof RefreshType) {
State state = stateCache.get(channelUID.getId());
if (state != null) {
updateState(channelUID.getId(), state);
}
return;
}
}
public void updateChannels(RemoteSensorDTO sensor) {
logger.debug("SensorThing: Updating channels for sensor '{}({})'", sensor.id, sensor.name);
updateChannel(CH_SENSOR_ID, EcobeeUtils.undefOrString(sensor.id));
updateChannel(CH_SENSOR_NAME, EcobeeUtils.undefOrString(sensor.name));
updateChannel(CH_SENSOR_TYPE, EcobeeUtils.undefOrString(sensor.type));
updateChannel(CH_SENSOR_CODE, EcobeeUtils.undefOrString(sensor.code));
updateChannel(CH_SENSOR_IN_USE, EcobeeUtils.undefOrOnOff(sensor.inUse));
for (RemoteSensorCapabilityDTO capability : sensor.capability) {
updateCapabilityChannels(capability);
}
}
private void updateCapabilityChannels(RemoteSensorCapabilityDTO capability) {
ChannelUID uid = new ChannelUID(thing.getUID(), capability.type);
Channel channel = thing.getChannel(uid);
if (channel == null) {
logger.debug("SensorThing: Create channel '{}'", uid);
ThingBuilder thingBuilder;
thingBuilder = editThing();
channel = ChannelBuilder.create(uid, getAcceptedItemType(capability.type))
.withLabel("Sensor " + WordUtils.capitalize(capability.type))
.withType(getChannelTypeUID(capability.type)).build();
thingBuilder.withChannel(channel);
updateThing(thingBuilder.build());
}
logger.trace("Capability '{}' has type '{}' with value '{}'", capability.id, capability.type, capability.value);
updateCapabilityState(capability.type, capability.value);
}
// adc, co2, dryContact, humidity, temperature, occupancy, unknown.
private String getAcceptedItemType(String capabilityType) {
String acceptedItemType;
switch (capabilityType) {
case CAPABILITY_TEMPERATURE:
acceptedItemType = "Number:Temperature";
break;
case CAPABILITY_HUMIDITY:
acceptedItemType = "Number:Dimensionless";
break;
case CAPABILITY_OCCUPANCY:
acceptedItemType = "Switch";
break;
case CAPABILITY_ADC:
case CAPABILITY_CO2:
case CAPABILITY_DRY_CONTACT:
case CAPABILITY_UNKNOWN:
default:
acceptedItemType = "String";
break;
}
return acceptedItemType;
}
private ChannelTypeUID getChannelTypeUID(String capabilityType) {
ChannelTypeUID channelTypeUID;
switch (capabilityType) {
case CAPABILITY_TEMPERATURE:
channelTypeUID = CHANNELTYPEUID_TEMPERATURE;
break;
case CAPABILITY_HUMIDITY:
channelTypeUID = CHANNELTYPEUID_HUMIDITY;
break;
case CAPABILITY_OCCUPANCY:
channelTypeUID = CHANNELTYPEUID_OCCUPANCY;
break;
case CAPABILITY_ADC:
case CAPABILITY_CO2:
case CAPABILITY_DRY_CONTACT:
case CAPABILITY_UNKNOWN:
default:
channelTypeUID = CHANNELTYPEUID_GENERIC;
break;
}
return channelTypeUID;
}
private void updateCapabilityState(String capabilityType, String value) {
State state;
switch (capabilityType) {
case CAPABILITY_TEMPERATURE:
try {
state = EcobeeUtils.undefOrTemperature(Integer.parseInt(value));
} catch (NumberFormatException e) {
state = UnDefType.UNDEF;
}
break;
case CAPABILITY_HUMIDITY:
try {
state = EcobeeUtils.undefOrQuantity(Integer.parseInt(value), SmartHomeUnits.PERCENT);
} catch (NumberFormatException e) {
state = UnDefType.UNDEF;
}
break;
case CAPABILITY_OCCUPANCY:
state = EcobeeUtils.undefOrOnOff("true".equals(value));
break;
case CAPABILITY_ADC:
case CAPABILITY_CO2:
case CAPABILITY_DRY_CONTACT:
case CAPABILITY_UNKNOWN:
default:
state = EcobeeUtils.undefOrString(value);
break;
}
updateChannel(capabilityType, state);
}
private void updateChannel(String channelId, State state) {
updateState(channelId, state);
stateCache.put(channelId, state);
}
private void clearSavedState() {
stateCache.clear();
}
}

View File

@@ -0,0 +1,957 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.handler;
import static org.openhab.binding.ecobee.internal.EcobeeBindingConstants.*;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.measure.Unit;
import org.apache.commons.lang.WordUtils;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.ecobee.action.EcobeeActions;
import org.openhab.binding.ecobee.internal.api.EcobeeApi;
import org.openhab.binding.ecobee.internal.config.EcobeeThermostatConfiguration;
import org.openhab.binding.ecobee.internal.discovery.SensorDiscoveryService;
import org.openhab.binding.ecobee.internal.dto.SelectionDTO;
import org.openhab.binding.ecobee.internal.dto.thermostat.AlertDTO;
import org.openhab.binding.ecobee.internal.dto.thermostat.ClimateDTO;
import org.openhab.binding.ecobee.internal.dto.thermostat.EventDTO;
import org.openhab.binding.ecobee.internal.dto.thermostat.HouseDetailsDTO;
import org.openhab.binding.ecobee.internal.dto.thermostat.LocationDTO;
import org.openhab.binding.ecobee.internal.dto.thermostat.ManagementDTO;
import org.openhab.binding.ecobee.internal.dto.thermostat.ProgramDTO;
import org.openhab.binding.ecobee.internal.dto.thermostat.RemoteSensorDTO;
import org.openhab.binding.ecobee.internal.dto.thermostat.RuntimeDTO;
import org.openhab.binding.ecobee.internal.dto.thermostat.SettingsDTO;
import org.openhab.binding.ecobee.internal.dto.thermostat.TechnicianDTO;
import org.openhab.binding.ecobee.internal.dto.thermostat.ThermostatDTO;
import org.openhab.binding.ecobee.internal.dto.thermostat.ThermostatUpdateRequestDTO;
import org.openhab.binding.ecobee.internal.dto.thermostat.VersionDTO;
import org.openhab.binding.ecobee.internal.dto.thermostat.WeatherDTO;
import org.openhab.binding.ecobee.internal.dto.thermostat.WeatherForecastDTO;
import org.openhab.binding.ecobee.internal.function.AbstractFunction;
import org.openhab.binding.ecobee.internal.function.FunctionRequest;
import org.openhab.core.i18n.TimeZoneProvider;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
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.library.unit.SmartHomeUnits;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Channel;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.ThingStatusInfo;
import org.openhab.core.thing.binding.BaseBridgeHandler;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.thing.binding.ThingHandlerService;
import org.openhab.core.thing.type.ChannelType;
import org.openhab.core.thing.type.ChannelTypeRegistry;
import org.openhab.core.thing.type.ChannelTypeUID;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
import org.openhab.core.types.State;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@link EcobeeThermostatBridgeHandler} is the handler for an Ecobee thermostat.
*
* @author Mark Hilbush - Initial contribution
*/
@NonNullByDefault
public class EcobeeThermostatBridgeHandler extends BaseBridgeHandler {
private static final int SENSOR_DISCOVERY_STARTUP_DELAY_SECONDS = 30;
private static final int SENSOR_DISCOVERY_INTERVAL_SECONDS = 300;
private final Logger logger = LoggerFactory.getLogger(EcobeeThermostatBridgeHandler.class);
private TimeZoneProvider timeZoneProvider;
private ChannelTypeRegistry channelTypeRegistry;
private @NonNullByDefault({}) String thermostatId;
private final Map<String, EcobeeSensorThingHandler> sensorHandlers = new ConcurrentHashMap<>();
private @Nullable Future<?> discoverSensorsJob;
private @Nullable SensorDiscoveryService discoveryService;
private @Nullable ThermostatDTO savedThermostat;
private @Nullable List<RemoteSensorDTO> savedSensors;
private List<String> validClimateRefs = new CopyOnWriteArrayList<>();
private Map<String, State> stateCache = new ConcurrentHashMap<>();
private Map<ChannelUID, Boolean> channelReadOnlyMap = new HashMap<>();
private Map<Integer, String> symbolMap = new HashMap<>();
private Map<Integer, String> skyMap = new HashMap<>();
public EcobeeThermostatBridgeHandler(Bridge bridge, TimeZoneProvider timeZoneProvider,
ChannelTypeRegistry channelTypeRegistry) {
super(bridge);
this.timeZoneProvider = timeZoneProvider;
this.channelTypeRegistry = channelTypeRegistry;
}
@Override
public void initialize() {
thermostatId = getConfigAs(EcobeeThermostatConfiguration.class).thermostatId;
logger.debug("ThermostatBridge: Initializing thermostat '{}'", thermostatId);
initializeWeatherMaps();
initializeReadOnlyChannels();
clearSavedState();
scheduleDiscoveryJob();
updateStatus(EcobeeUtils.isBridgeOnline(getBridge()) ? ThingStatus.ONLINE : ThingStatus.OFFLINE);
}
@Override
public void dispose() {
cancelDiscoveryJob();
logger.debug("ThermostatBridge: Disposing thermostat '{}'", thermostatId);
}
@Override
public void childHandlerInitialized(ThingHandler sensorHandler, Thing sensorThing) {
String sensorId = (String) sensorThing.getConfiguration().get(CONFIG_SENSOR_ID);
sensorHandlers.put(sensorId, (EcobeeSensorThingHandler) sensorHandler);
logger.debug("ThermostatBridge: Saving sensor handler for {} with id {}", sensorThing.getUID(), sensorId);
}
@Override
public void childHandlerDisposed(ThingHandler sensorHandler, Thing sensorThing) {
String sensorId = (String) sensorThing.getConfiguration().get(CONFIG_SENSOR_ID);
sensorHandlers.remove(sensorId);
logger.debug("ThermostatBridge: Removing sensor handler for {} with id {}", sensorThing.getUID(), sensorId);
}
@Override
public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
if (bridgeStatusInfo.getStatus() == ThingStatus.ONLINE) {
updateStatus(ThingStatus.ONLINE);
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
}
}
@SuppressWarnings("null")
@Override
public void handleCommand(ChannelUID channelUID, Command command) {
if (command instanceof RefreshType) {
State state = stateCache.get(channelUID.getId());
if (state != null) {
updateState(channelUID.getId(), state);
}
return;
}
if (isChannelReadOnly(channelUID)) {
logger.debug("Can't apply command '{}' to '{}' because channel is readonly", command, channelUID.getId());
return;
}
scheduler.execute(() -> {
handleThermostatCommand(channelUID, command);
});
}
public void setDiscoveryService(SensorDiscoveryService discoveryService) {
this.discoveryService = discoveryService;
}
/**
* Called by the AccountBridgeHandler to create a Selection that
* includes only the Ecobee objects for which there's at least one
* item linked to one of that object's channels.
*
* @return Selection
*/
public SelectionDTO getSelection() {
final SelectionDTO selection = new SelectionDTO();
for (String group : CHANNEL_GROUPS) {
for (Channel channel : thing.getChannelsOfGroup(group)) {
if (isLinked(channel.getUID())) {
try {
Field field = selection.getClass().getField("include" + WordUtils.capitalize(group));
logger.trace("ThermostatBridge: Thermostat thing '{}' including object '{}' in selection",
thing.getUID(), field.getName());
field.set(selection, Boolean.TRUE);
break;
} catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException
| SecurityException e) {
logger.debug("ThermostatBridge: Exception setting selection for group '{}'", group, e);
}
}
}
}
return selection;
}
public List<RemoteSensorDTO> getSensors() {
List<RemoteSensorDTO> localSavedSensors = savedSensors;
return localSavedSensors == null ? EMPTY_SENSORS : localSavedSensors;
}
public @Nullable String getAlerts() {
ThermostatDTO thermostat = savedThermostat;
if (thermostat != null && thermostat.alerts != null) {
return EcobeeApi.getGson().toJson(thermostat.alerts);
}
return null;
}
public @Nullable String getEvents() {
ThermostatDTO thermostat = savedThermostat;
if (thermostat != null && thermostat.events != null) {
return EcobeeApi.getGson().toJson(thermostat.events);
}
return null;
}
public @Nullable String getClimates() {
ThermostatDTO thermostat = savedThermostat;
if (thermostat != null && thermostat.program != null && thermostat.program.climates != null) {
return EcobeeApi.getGson().toJson(thermostat.program.climates);
}
return null;
}
public boolean isValidClimateRef(String climateRef) {
return validClimateRefs.contains(climateRef);
}
public String getThermostatId() {
return thermostatId;
}
/*
* Called by EcobeeActions to perform a thermostat function
*/
public boolean actionPerformFunction(AbstractFunction function) {
logger.debug("ThermostatBridge: Perform function '{}' on thermostat {}", function.type, thermostatId);
SelectionDTO selection = new SelectionDTO();
selection.setThermostats(Collections.singleton(thermostatId));
FunctionRequest request = new FunctionRequest(selection);
request.functions = Collections.singletonList(function);
EcobeeAccountBridgeHandler handler = getBridgeHandler();
if (handler != null) {
return handler.performThermostatFunction(request);
}
return false;
}
@Override
public Collection<Class<? extends ThingHandlerService>> getServices() {
return Collections.unmodifiableList(
Stream.of(EcobeeActions.class, SensorDiscoveryService.class).collect(Collectors.toList()));
}
public void updateChannels(ThermostatDTO thermostat) {
logger.debug("ThermostatBridge: Updating channels for thermostat id {}", thermostat.identifier);
savedThermostat = thermostat;
updateAlert(thermostat.alerts);
updateHouseDetails(thermostat.houseDetails);
updateInfo(thermostat);
updateEquipmentStatus(thermostat);
updateLocation(thermostat.location);
updateManagement(thermostat.management);
updateProgram(thermostat.program);
updateEvent(thermostat.events);
updateRemoteSensors(thermostat.remoteSensors);
updateRuntime(thermostat.runtime);
updateSettings(thermostat.settings);
updateTechnician(thermostat.technician);
updateVersion(thermostat.version);
updateWeather(thermostat.weather);
savedSensors = thermostat.remoteSensors;
}
private void handleThermostatCommand(ChannelUID channelUID, Command command) {
logger.debug("Got command '{}' for channel '{}' of thing '{}'", command, channelUID, getThing().getUID());
String channelId = channelUID.getIdWithoutGroup();
String groupId = channelUID.getGroupId();
if (groupId == null) {
logger.info("Can't handle command because channel's groupId is null");
return;
}
ThermostatDTO thermostat = new ThermostatDTO();
Field field;
try {
switch (groupId) {
case CHGRP_INFO:
field = thermostat.getClass().getField(channelId);
setField(field, thermostat, command);
break;
case CHGRP_SETTINGS:
SettingsDTO settings = new SettingsDTO();
field = settings.getClass().getField(channelId);
setField(field, settings, command);
thermostat.settings = settings;
break;
case CHGRP_LOCATION:
LocationDTO location = new LocationDTO();
field = location.getClass().getField(channelId);
setField(field, location, command);
thermostat.location = location;
break;
case CHGRP_HOUSE_DETAILS:
HouseDetailsDTO houseDetails = new HouseDetailsDTO();
field = houseDetails.getClass().getField(channelId);
setField(field, houseDetails, command);
thermostat.houseDetails = houseDetails;
break;
default:
// All other groups contain only read-only fields
return;
}
performThermostatUpdate(thermostat);
} catch (NoSuchFieldException | SecurityException e) {
logger.info("Unable to get field for '{}.{}'", groupId, channelId);
}
}
private void setField(Field field, Object object, Command command) {
logger.info("Setting field '{}.{}' to value '{}'", object.getClass().getSimpleName().toLowerCase(),
field.getName(), command);
Class<?> fieldClass = field.getType();
try {
boolean success = false;
if (String.class.isAssignableFrom(fieldClass)) {
if (command instanceof StringType) {
logger.debug("Set field of type String to value of StringType");
field.set(object, command.toString());
success = true;
}
} else if (Integer.class.isAssignableFrom(fieldClass)) {
if (command instanceof DecimalType) {
logger.debug("Set field of type Integer to value of DecimalType");
field.set(object, Integer.valueOf(((DecimalType) command).intValue()));
success = true;
} else if (command instanceof QuantityType) {
Unit<?> unit = ((QuantityType<?>) command).getUnit();
logger.debug("Set field of type Integer to value of QuantityType with unit {}", unit);
if (unit.equals(ImperialUnits.FAHRENHEIT) || unit.equals(SIUnits.CELSIUS)) {
QuantityType<?> quantity = ((QuantityType<?>) command).toUnit(ImperialUnits.FAHRENHEIT);
if (quantity != null) {
field.set(object, quantity.intValue() * 10);
success = true;
}
}
}
} else if (Boolean.class.isAssignableFrom(fieldClass)) {
if (command instanceof OnOffType) {
logger.debug("Set field of type Boolean to value of OnOffType");
field.set(object, command == OnOffType.ON);
success = true;
}
}
if (!success) {
logger.info("Don't know how to convert command of type '{}' to {}.{}",
command.getClass().getSimpleName(), object.getClass().getSimpleName(), field.getName());
}
} catch (IllegalArgumentException | IllegalAccessException e) {
logger.info("Unable to set field '{}.{}' to value '{}'", object.getClass().getSimpleName(), field.getName(),
command, e);
}
}
private void updateInfo(ThermostatDTO thermostat) {
final String grp = CHGRP_INFO + "#";
updateChannel(grp + CH_IDENTIFIER, EcobeeUtils.undefOrString(thermostat.identifier));
updateChannel(grp + CH_NAME, EcobeeUtils.undefOrString(thermostat.name));
updateChannel(grp + CH_THERMOSTAT_REV, EcobeeUtils.undefOrString(thermostat.thermostatRev));
updateChannel(grp + CH_IS_REGISTERED, EcobeeUtils.undefOrOnOff(thermostat.isRegistered));
updateChannel(grp + CH_MODEL_NUMBER, EcobeeUtils.undefOrString(thermostat.modelNumber));
updateChannel(grp + CH_BRAND, EcobeeUtils.undefOrString(thermostat.brand));
updateChannel(grp + CH_FEATURES, EcobeeUtils.undefOrString(thermostat.features));
updateChannel(grp + CH_LAST_MODIFIED, EcobeeUtils.undefOrDate(thermostat.lastModified, timeZoneProvider));
updateChannel(grp + CH_THERMOSTAT_TIME, EcobeeUtils.undefOrDate(thermostat.thermostatTime, timeZoneProvider));
}
private void updateEquipmentStatus(ThermostatDTO thermostat) {
final String grp = CHGRP_EQUIPMENT_STATUS + "#";
updateChannel(grp + CH_EQUIPMENT_STATUS, EcobeeUtils.undefOrString(thermostat.equipmentStatus));
}
private void updateRuntime(@Nullable RuntimeDTO runtime) {
if (runtime == null) {
return;
}
final String grp = CHGRP_RUNTIME + "#";
updateChannel(grp + CH_RUNTIME_REV, EcobeeUtils.undefOrString(runtime.runtimeRev));
updateChannel(grp + CH_CONNECTED, EcobeeUtils.undefOrOnOff(runtime.connected));
updateChannel(grp + CH_FIRST_CONNECTED, EcobeeUtils.undefOrDate(runtime.firstConnected, timeZoneProvider));
updateChannel(grp + CH_CONNECT_DATE_TIME, EcobeeUtils.undefOrDate(runtime.connectDateTime, timeZoneProvider));
updateChannel(grp + CH_DISCONNECT_DATE_TIME,
EcobeeUtils.undefOrDate(runtime.disconnectDateTime, timeZoneProvider));
updateChannel(grp + CH_RT_LAST_MODIFIED, EcobeeUtils.undefOrDate(runtime.lastModified, timeZoneProvider));
updateChannel(grp + CH_RT_LAST_STATUS_MODIFIED,
EcobeeUtils.undefOrDate(runtime.lastStatusModified, timeZoneProvider));
updateChannel(grp + CH_RUNTIME_DATE, EcobeeUtils.undefOrString(runtime.runtimeDate));
updateChannel(grp + CH_RUNTIME_INTERVAL, EcobeeUtils.undefOrDecimal(runtime.runtimeInterval));
updateChannel(grp + CH_ACTUAL_TEMPERATURE, EcobeeUtils.undefOrTemperature(runtime.actualTemperature));
updateChannel(grp + CH_ACTUAL_HUMIDITY,
EcobeeUtils.undefOrQuantity(runtime.actualHumidity, SmartHomeUnits.PERCENT));
updateChannel(grp + CH_RAW_TEMPERATURE, EcobeeUtils.undefOrTemperature(runtime.rawTemperature));
updateChannel(grp + CH_SHOW_ICON_MODE, EcobeeUtils.undefOrDecimal(runtime.showIconMode));
updateChannel(grp + CH_DESIRED_HEAT, EcobeeUtils.undefOrTemperature(runtime.desiredHeat));
updateChannel(grp + CH_DESIRED_COOL, EcobeeUtils.undefOrTemperature(runtime.desiredCool));
updateChannel(grp + CH_DESIRED_HUMIDITY,
EcobeeUtils.undefOrQuantity(runtime.desiredHumidity, SmartHomeUnits.PERCENT));
updateChannel(grp + CH_DESIRED_DEHUMIDITY,
EcobeeUtils.undefOrQuantity(runtime.desiredDehumidity, SmartHomeUnits.PERCENT));
updateChannel(grp + CH_DESIRED_FAN_MODE, EcobeeUtils.undefOrString(runtime.desiredFanMode));
if (runtime.desiredHeatRange != null && runtime.desiredHeatRange.size() == 2) {
updateChannel(grp + CH_DESIRED_HEAT_RANGE_LOW,
EcobeeUtils.undefOrTemperature(runtime.desiredHeatRange.get(0)));
updateChannel(grp + CH_DESIRED_HEAT_RANGE_HIGH,
EcobeeUtils.undefOrTemperature(runtime.desiredHeatRange.get(1)));
}
if (runtime.desiredCoolRange != null && runtime.desiredCoolRange.size() == 2) {
updateChannel(grp + CH_DESIRED_COOL_RANGE_LOW,
EcobeeUtils.undefOrTemperature(runtime.desiredCoolRange.get(0)));
updateChannel(grp + CH_DESIRED_COOL_RANGE_HIGH,
EcobeeUtils.undefOrTemperature(runtime.desiredCoolRange.get(1)));
}
}
private void updateSettings(@Nullable SettingsDTO settings) {
if (settings == null) {
return;
}
final String grp = CHGRP_SETTINGS + "#";
updateChannel(grp + CH_HVAC_MODE, EcobeeUtils.undefOrString(settings.hvacMode));
updateChannel(grp + CH_LAST_SERVICE_DATE, EcobeeUtils.undefOrString(settings.lastServiceDate));
updateChannel(grp + CH_SERVICE_REMIND_ME, EcobeeUtils.undefOrOnOff(settings.serviceRemindMe));
updateChannel(grp + CH_MONTHS_BETWEEN_SERVICE, EcobeeUtils.undefOrDecimal(settings.monthsBetweenService));
updateChannel(grp + CH_REMIND_ME_DATE, EcobeeUtils.undefOrString(settings.remindMeDate));
updateChannel(grp + CH_VENT, EcobeeUtils.undefOrString(settings.vent));
updateChannel(grp + CH_VENTILATOR_MIN_ON_TIME, EcobeeUtils.undefOrDecimal(settings.ventilatorMinOnTime));
updateChannel(grp + CH_SERVICE_REMIND_TECHNICIAN, EcobeeUtils.undefOrOnOff(settings.serviceRemindTechnician));
updateChannel(grp + CH_EI_LOCATION, EcobeeUtils.undefOrString(settings.eiLocation));
updateChannel(grp + CH_COLD_TEMP_ALERT, EcobeeUtils.undefOrTemperature(settings.coldTempAlert));
updateChannel(grp + CH_COLD_TEMP_ALERT_ENABLED, EcobeeUtils.undefOrOnOff(settings.coldTempAlertEnabled));
updateChannel(grp + CH_HOT_TEMP_ALERT, EcobeeUtils.undefOrTemperature(settings.hotTempAlert));
updateChannel(grp + CH_HOT_TEMP_ALERT_ENABLED, EcobeeUtils.undefOrOnOff(settings.hotTempAlertEnabled));
updateChannel(grp + CH_COOL_STAGES, EcobeeUtils.undefOrDecimal(settings.coolStages));
updateChannel(grp + CH_HEAT_STAGES, EcobeeUtils.undefOrDecimal(settings.heatStages));
updateChannel(grp + CH_MAX_SET_BACK, EcobeeUtils.undefOrDecimal(settings.maxSetBack));
updateChannel(grp + CH_MAX_SET_FORWARD, EcobeeUtils.undefOrDecimal(settings.maxSetForward));
updateChannel(grp + CH_QUICK_SAVE_SET_BACK, EcobeeUtils.undefOrDecimal(settings.quickSaveSetBack));
updateChannel(grp + CH_QUICK_SAVE_SET_FORWARD, EcobeeUtils.undefOrDecimal(settings.quickSaveSetForward));
updateChannel(grp + CH_HAS_HEAT_PUMP, EcobeeUtils.undefOrOnOff(settings.hasHeatPump));
updateChannel(grp + CH_HAS_FORCED_AIR, EcobeeUtils.undefOrOnOff(settings.hasForcedAir));
updateChannel(grp + CH_HAS_BOILER, EcobeeUtils.undefOrOnOff(settings.hasBoiler));
updateChannel(grp + CH_HAS_HUMIDIFIER, EcobeeUtils.undefOrOnOff(settings.hasHumidifier));
updateChannel(grp + CH_HAS_ERV, EcobeeUtils.undefOrOnOff(settings.hasErv));
updateChannel(grp + CH_HAS_HRV, EcobeeUtils.undefOrOnOff(settings.hasHrv));
updateChannel(grp + CH_CONDENSATION_AVOID, EcobeeUtils.undefOrOnOff(settings.condensationAvoid));
updateChannel(grp + CH_USE_CELSIUS, EcobeeUtils.undefOrOnOff(settings.useCelsius));
updateChannel(grp + CH_USE_TIME_FORMAT_12, EcobeeUtils.undefOrOnOff(settings.useTimeFormat12));
updateChannel(grp + CH_LOCALE, EcobeeUtils.undefOrString(settings.locale));
updateChannel(grp + CH_HUMIDITY, EcobeeUtils.undefOrString(settings.humidity));
updateChannel(grp + CH_HUMIDIFIER_MODE, EcobeeUtils.undefOrString(settings.humidifierMode));
updateChannel(grp + CH_BACKLIGHT_ON_INTENSITY, EcobeeUtils.undefOrDecimal(settings.backlightOnIntensity));
updateChannel(grp + CH_BACKLIGHT_SLEEP_INTENSITY, EcobeeUtils.undefOrDecimal(settings.backlightSleepIntensity));
updateChannel(grp + CH_BACKLIGHT_OFF_TIME, EcobeeUtils.undefOrDecimal(settings.backlightOffTime));
updateChannel(grp + CH_SOUND_TICK_VOLUME, EcobeeUtils.undefOrDecimal(settings.soundTickVolume));
updateChannel(grp + CH_SOUND_ALERT_VOLUME, EcobeeUtils.undefOrDecimal(settings.soundAlertVolume));
updateChannel(grp + CH_COMPRESSOR_PROTECTION_MIN_TIME,
EcobeeUtils.undefOrDecimal(settings.compressorProtectionMinTime));
updateChannel(grp + CH_COMPRESSOR_PROTECTION_MIN_TEMP,
EcobeeUtils.undefOrTemperature(settings.compressorProtectionMinTemp));
updateChannel(grp + CH_STAGE1_HEATING_DIFFERENTIAL_TEMP,
EcobeeUtils.undefOrDecimal(settings.stage1HeatingDifferentialTemp));
updateChannel(grp + CH_STAGE1_COOLING_DIFFERENTIAL_TEMP,
EcobeeUtils.undefOrDecimal(settings.stage1CoolingDifferentialTemp));
updateChannel(grp + CH_STAGE1_HEATING_DISSIPATION_TIME,
EcobeeUtils.undefOrDecimal(settings.stage1HeatingDissipationTime));
updateChannel(grp + CH_STAGE1_COOLING_DISSIPATION_TIME,
EcobeeUtils.undefOrDecimal(settings.stage1CoolingDissipationTime));
updateChannel(grp + CH_HEAT_PUMP_REVERSAL_ON_COOL, EcobeeUtils.undefOrOnOff(settings.heatPumpReversalOnCool));
updateChannel(grp + CH_FAN_CONTROLLER_REQUIRED, EcobeeUtils.undefOrOnOff(settings.fanControlRequired));
updateChannel(grp + CH_FAN_MIN_ON_TIME, EcobeeUtils.undefOrDecimal(settings.fanMinOnTime));
updateChannel(grp + CH_HEAT_COOL_MIN_DELTA, EcobeeUtils.undefOrDecimal(settings.heatCoolMinDelta));
updateChannel(grp + CH_TEMP_CORRECTION, EcobeeUtils.undefOrDecimal(settings.tempCorrection));
updateChannel(grp + CH_HOLD_ACTION, EcobeeUtils.undefOrString(settings.holdAction));
updateChannel(grp + CH_HEAT_PUMP_GROUND_WATER, EcobeeUtils.undefOrOnOff(settings.heatPumpGroundWater));
updateChannel(grp + CH_HAS_ELECTRIC, EcobeeUtils.undefOrOnOff(settings.hasElectric));
updateChannel(grp + CH_HAS_DEHUMIDIFIER, EcobeeUtils.undefOrOnOff(settings.hasDehumidifier));
updateChannel(grp + CH_DEHUMIDIFIER_MODE, EcobeeUtils.undefOrString(settings.dehumidifierMode));
updateChannel(grp + CH_DEHUMIDIFIER_LEVEL, EcobeeUtils.undefOrDecimal(settings.dehumidifierLevel));
updateChannel(grp + CH_DEHUMIDIFY_WITH_AC, EcobeeUtils.undefOrOnOff(settings.dehumidifyWithAC));
updateChannel(grp + CH_DEHUMIDIFY_OVERCOOL_OFFSET,
EcobeeUtils.undefOrDecimal(settings.dehumidifyOvercoolOffset));
updateChannel(grp + CH_AUTO_HEAT_COOL_FEATURE_ENABLED,
EcobeeUtils.undefOrOnOff(settings.autoHeatCoolFeatureEnabled));
updateChannel(grp + CH_WIFI_OFFLINE_ALERT, EcobeeUtils.undefOrOnOff(settings.wifiOfflineAlert));
updateChannel(grp + CH_HEAT_MIN_TEMP, EcobeeUtils.undefOrTemperature(settings.heatMinTemp));
updateChannel(grp + CH_HEAT_MAX_TEMP, EcobeeUtils.undefOrTemperature(settings.heatMaxTemp));
updateChannel(grp + CH_COOL_MIN_TEMP, EcobeeUtils.undefOrTemperature(settings.coolMinTemp));
updateChannel(grp + CH_COOL_MAX_TEMP, EcobeeUtils.undefOrTemperature(settings.coolMaxTemp));
updateChannel(grp + CH_HEAT_RANGE_HIGH, EcobeeUtils.undefOrTemperature(settings.heatRangeHigh));
updateChannel(grp + CH_HEAT_RANGE_LOW, EcobeeUtils.undefOrTemperature(settings.heatRangeLow));
updateChannel(grp + CH_COOL_RANGE_HIGH, EcobeeUtils.undefOrTemperature(settings.coolRangeHigh));
updateChannel(grp + CH_COOL_RANGE_LOW, EcobeeUtils.undefOrTemperature(settings.coolRangeLow));
updateChannel(grp + CH_USER_ACCESS_CODE, EcobeeUtils.undefOrString(settings.userAccessCode));
updateChannel(grp + CH_USER_ACCESS_SETTING, EcobeeUtils.undefOrDecimal(settings.userAccessSetting));
updateChannel(grp + CH_AUX_RUNTIME_ALERT, EcobeeUtils.undefOrDecimal(settings.auxRuntimeAlert));
updateChannel(grp + CH_AUX_OUTDOOR_TEMP_ALERT, EcobeeUtils.undefOrTemperature(settings.auxOutdoorTempAlert));
updateChannel(grp + CH_AUX_MAX_OUTDOOR_TEMP, EcobeeUtils.undefOrTemperature(settings.auxMaxOutdoorTemp));
updateChannel(grp + CH_AUX_RUNTIME_ALERT_NOTIFY, EcobeeUtils.undefOrOnOff(settings.auxRuntimeAlertNotify));
updateChannel(grp + CH_AUX_OUTDOOR_TEMP_ALERT_NOTIFY,
EcobeeUtils.undefOrOnOff(settings.auxOutdoorTempAlertNotify));
updateChannel(grp + CH_AUX_RUNTIME_ALERT_NOTIFY_TECHNICIAN,
EcobeeUtils.undefOrOnOff(settings.auxRuntimeAlertNotifyTechnician));
updateChannel(grp + CH_AUX_OUTDOOR_TEMP_ALERT_NOTIFY_TECHNICIAN,
EcobeeUtils.undefOrOnOff(settings.auxOutdoorTempAlertNotifyTechnician));
updateChannel(grp + CH_DISABLE_PREHEATING, EcobeeUtils.undefOrOnOff(settings.disablePreHeating));
updateChannel(grp + CH_DISABLE_PRECOOLING, EcobeeUtils.undefOrOnOff(settings.disablePreCooling));
updateChannel(grp + CH_INSTALLER_CODE_REQUIRED, EcobeeUtils.undefOrOnOff(settings.installerCodeRequired));
updateChannel(grp + CH_DR_ACCEPT, EcobeeUtils.undefOrString(settings.drAccept));
updateChannel(grp + CH_IS_RENTAL_PROPERTY, EcobeeUtils.undefOrOnOff(settings.isRentalProperty));
updateChannel(grp + CH_USE_ZONE_CONTROLLER, EcobeeUtils.undefOrOnOff(settings.useZoneController));
updateChannel(grp + CH_RANDOM_START_DELAY_COOL, EcobeeUtils.undefOrDecimal(settings.randomStartDelayCool));
updateChannel(grp + CH_RANDOM_START_DELAY_HEAT, EcobeeUtils.undefOrDecimal(settings.randomStartDelayHeat));
updateChannel(grp + CH_HUMIDITY_HIGH_ALERT,
EcobeeUtils.undefOrQuantity(settings.humidityHighAlert, SmartHomeUnits.PERCENT));
updateChannel(grp + CH_HUMIDITY_LOW_ALERT,
EcobeeUtils.undefOrQuantity(settings.humidityLowAlert, SmartHomeUnits.PERCENT));
updateChannel(grp + CH_DISABLE_HEAT_PUMP_ALERTS, EcobeeUtils.undefOrOnOff(settings.disableHeatPumpAlerts));
updateChannel(grp + CH_DISABLE_ALERTS_ON_IDT, EcobeeUtils.undefOrOnOff(settings.disableAlertsOnIdt));
updateChannel(grp + CH_HUMIDITY_ALERT_NOTIFY, EcobeeUtils.undefOrOnOff(settings.humidityAlertNotify));
updateChannel(grp + CH_HUMIDITY_ALERT_NOTIFY_TECHNICIAN,
EcobeeUtils.undefOrOnOff(settings.humidityAlertNotifyTechnician));
updateChannel(grp + CH_TEMP_ALERT_NOTIFY, EcobeeUtils.undefOrOnOff(settings.tempAlertNotify));
updateChannel(grp + CH_TEMP_ALERT_NOTIFY_TECHNICIAN,
EcobeeUtils.undefOrOnOff(settings.tempAlertNotifyTechnician));
updateChannel(grp + CH_MONTHLY_ELECTRICITY_BILL_LIMIT,
EcobeeUtils.undefOrDecimal(settings.monthlyElectricityBillLimit));
updateChannel(grp + CH_ENABLE_ELECTRICITY_BILL_ALERT,
EcobeeUtils.undefOrOnOff(settings.enableElectricityBillAlert));
updateChannel(grp + CH_ENABLE_PROJECTED_ELECTRICITY_BILL_ALERT,
EcobeeUtils.undefOrOnOff(settings.enableProjectedElectricityBillAlert));
updateChannel(grp + CH_ELECTRICITY_BILLING_DAY_OF_MONTH,
EcobeeUtils.undefOrDecimal(settings.electricityBillingDayOfMonth));
updateChannel(grp + CH_ELECTRICITY_BILL_CYCLE_MONTHS,
EcobeeUtils.undefOrDecimal(settings.electricityBillCycleMonths));
updateChannel(grp + CH_ELECTRICITY_BILL_START_MONTH,
EcobeeUtils.undefOrDecimal(settings.electricityBillStartMonth));
updateChannel(grp + CH_VENTILATOR_MIN_ON_TIME_HOME,
EcobeeUtils.undefOrDecimal(settings.ventilatorMinOnTimeHome));
updateChannel(grp + CH_VENTILATOR_MIN_ON_TIME_AWAY,
EcobeeUtils.undefOrDecimal(settings.ventilatorMinOnTimeAway));
updateChannel(grp + CH_BACKLIGHT_OFF_DURING_SLEEP, EcobeeUtils.undefOrOnOff(settings.backlightOffDuringSleep));
updateChannel(grp + CH_AUTO_AWAY, EcobeeUtils.undefOrOnOff(settings.autoAway));
updateChannel(grp + CH_SMART_CIRCULATION, EcobeeUtils.undefOrOnOff(settings.smartCirculation));
updateChannel(grp + CH_FOLLOW_ME_COMFORT, EcobeeUtils.undefOrOnOff(settings.followMeComfort));
updateChannel(grp + CH_VENTILATOR_TYPE, EcobeeUtils.undefOrString(settings.ventilatorType));
updateChannel(grp + CH_IS_VENTILATOR_TIMER_ON, EcobeeUtils.undefOrOnOff(settings.isVentilatorTimerOn));
updateChannel(grp + CH_VENTILATOR_OFF_DATE_TIME, EcobeeUtils.undefOrString(settings.ventilatorOffDateTime));
updateChannel(grp + CH_HAS_UV_FILTER, EcobeeUtils.undefOrOnOff(settings.hasUVFilter));
updateChannel(grp + CH_COOLING_LOCKOUT, EcobeeUtils.undefOrOnOff(settings.coolingLockout));
updateChannel(grp + CH_VENTILATOR_FREE_COOLING, EcobeeUtils.undefOrOnOff(settings.ventilatorFreeCooling));
updateChannel(grp + CH_DEHUMIDIFY_WHEN_HEATING, EcobeeUtils.undefOrOnOff(settings.dehumidifyWhenHeating));
updateChannel(grp + CH_VENTILATOR_DEHUMIDIFY, EcobeeUtils.undefOrOnOff(settings.ventilatorDehumidify));
updateChannel(grp + CH_GROUP_REF, EcobeeUtils.undefOrString(settings.groupRef));
updateChannel(grp + CH_GROUP_NAME, EcobeeUtils.undefOrString(settings.groupName));
updateChannel(grp + CH_GROUP_SETTING, EcobeeUtils.undefOrDecimal(settings.groupSetting));
}
private void updateProgram(@Nullable ProgramDTO program) {
if (program == null) {
return;
}
final String grp = CHGRP_PROGRAM + "#";
updateChannel(grp + CH_PROGRAM_CURRENT_CLIMATE_REF, EcobeeUtils.undefOrString(program.currentClimateRef));
if (program.climates != null) {
saveValidClimateRefs(program.climates);
}
}
private void saveValidClimateRefs(List<ClimateDTO> climates) {
validClimateRefs.clear();
for (ClimateDTO climate : climates) {
validClimateRefs.add(climate.climateRef);
}
}
private void updateAlert(@Nullable List<AlertDTO> alerts) {
AlertDTO firstAlert;
if (alerts == null || alerts.isEmpty()) {
firstAlert = EMPTY_ALERT;
} else {
firstAlert = alerts.get(0);
}
final String grp = CHGRP_ALERT + "#";
updateChannel(grp + CH_ALERT_ACKNOWLEDGE_REF, EcobeeUtils.undefOrString(firstAlert.acknowledgeRef));
updateChannel(grp + CH_ALERT_DATE, EcobeeUtils.undefOrString(firstAlert.date));
updateChannel(grp + CH_ALERT_TIME, EcobeeUtils.undefOrString(firstAlert.time));
updateChannel(grp + CH_ALERT_SEVERITY, EcobeeUtils.undefOrString(firstAlert.severity));
updateChannel(grp + CH_ALERT_TEXT, EcobeeUtils.undefOrString(firstAlert.text));
updateChannel(grp + CH_ALERT_ALERT_NUMBER, EcobeeUtils.undefOrDecimal(firstAlert.alertNumber));
updateChannel(grp + CH_ALERT_ALERT_TYPE, EcobeeUtils.undefOrString(firstAlert.alertType));
updateChannel(grp + CH_ALERT_IS_OPERATOR_ALERT, EcobeeUtils.undefOrOnOff(firstAlert.isOperatorAlert));
updateChannel(grp + CH_ALERT_REMINDER, EcobeeUtils.undefOrString(firstAlert.reminder));
updateChannel(grp + CH_ALERT_SHOW_IDT, EcobeeUtils.undefOrOnOff(firstAlert.showIdt));
updateChannel(grp + CH_ALERT_SHOW_WEB, EcobeeUtils.undefOrOnOff(firstAlert.showWeb));
updateChannel(grp + CH_ALERT_SEND_EMAIL, EcobeeUtils.undefOrOnOff(firstAlert.sendEmail));
updateChannel(grp + CH_ALERT_ACKNOWLEDGEMENT, EcobeeUtils.undefOrString(firstAlert.acknowledgement));
updateChannel(grp + CH_ALERT_REMIND_ME_LATER, EcobeeUtils.undefOrOnOff(firstAlert.remindMeLater));
updateChannel(grp + CH_ALERT_THERMOSTAT_IDENTIFIER, EcobeeUtils.undefOrString(firstAlert.thermostatIdentifier));
updateChannel(grp + CH_ALERT_NOTIFICATION_TYPE, EcobeeUtils.undefOrString(firstAlert.notificationType));
}
private void updateEvent(@Nullable List<EventDTO> events) {
EventDTO runningEvent = EMPTY_EVENT;
if (events != null && !events.isEmpty()) {
for (EventDTO event : events) {
if (event.running) {
runningEvent = event;
break;
}
}
}
final String grp = CHGRP_EVENT + "#";
updateChannel(grp + CH_EVENT_NAME, EcobeeUtils.undefOrString(runningEvent.name));
updateChannel(grp + CH_EVENT_TYPE, EcobeeUtils.undefOrString(runningEvent.type));
updateChannel(grp + CH_EVENT_RUNNING, EcobeeUtils.undefOrOnOff(runningEvent.running));
updateChannel(grp + CH_EVENT_START_DATE, EcobeeUtils.undefOrString(runningEvent.startDate));
updateChannel(grp + CH_EVENT_START_TIME, EcobeeUtils.undefOrString(runningEvent.startTime));
updateChannel(grp + CH_EVENT_END_DATE, EcobeeUtils.undefOrString(runningEvent.endDate));
updateChannel(grp + CH_EVENT_END_TIME, EcobeeUtils.undefOrString(runningEvent.endTime));
updateChannel(grp + CH_EVENT_IS_OCCUPIED, EcobeeUtils.undefOrOnOff(runningEvent.isOccupied));
updateChannel(grp + CH_EVENT_IS_COOL_OFF, EcobeeUtils.undefOrOnOff(runningEvent.isCoolOff));
updateChannel(grp + CH_EVENT_IS_HEAT_OFF, EcobeeUtils.undefOrOnOff(runningEvent.isHeatOff));
updateChannel(grp + CH_EVENT_COOL_HOLD_TEMP, EcobeeUtils.undefOrTemperature(runningEvent.coolHoldTemp));
updateChannel(grp + CH_EVENT_HEAT_HOLD_TEMP, EcobeeUtils.undefOrTemperature(runningEvent.heatHoldTemp));
updateChannel(grp + CH_EVENT_FAN, EcobeeUtils.undefOrString(runningEvent.fan));
updateChannel(grp + CH_EVENT_VENT, EcobeeUtils.undefOrString(runningEvent.vent));
updateChannel(grp + CH_EVENT_VENTILATOR_MIN_ON_TIME,
EcobeeUtils.undefOrDecimal(runningEvent.ventilatorMinOnTime));
updateChannel(grp + CH_EVENT_IS_OPTIONAL, EcobeeUtils.undefOrOnOff(runningEvent.isOptional));
updateChannel(grp + CH_EVENT_IS_TEMPERATURE_RELATIVE,
EcobeeUtils.undefOrOnOff(runningEvent.isTemperatureRelative));
updateChannel(grp + CH_EVENT_COOL_RELATIVE_TEMP, EcobeeUtils.undefOrDecimal(runningEvent.coolRelativeTemp));
updateChannel(grp + CH_EVENT_HEAT_RELATIVE_TEMP, EcobeeUtils.undefOrDecimal(runningEvent.heatRelativeTemp));
updateChannel(grp + CH_EVENT_IS_TEMPERATURE_ABSOLUTE,
EcobeeUtils.undefOrOnOff(runningEvent.isTemperatureAbsolute));
updateChannel(grp + CH_EVENT_DUTY_CYCLE_PERCENTAGE,
EcobeeUtils.undefOrDecimal(runningEvent.dutyCyclePercentage));
updateChannel(grp + CH_EVENT_FAN_MIN_ON_TIME, EcobeeUtils.undefOrDecimal(runningEvent.fanMinOnTime));
updateChannel(grp + CH_EVENT_OCCUPIED_SENSOR_ACTIVE,
EcobeeUtils.undefOrOnOff(runningEvent.occupiedSensorActive));
updateChannel(grp + CH_EVENT_UNOCCUPIED_SENSOR_ACTIVE,
EcobeeUtils.undefOrOnOff(runningEvent.unoccupiedSensorActive));
updateChannel(grp + CH_EVENT_DR_RAMP_UP_TEMP, EcobeeUtils.undefOrDecimal(runningEvent.drRampUpTemp));
updateChannel(grp + CH_EVENT_DR_RAMP_UP_TIME, EcobeeUtils.undefOrDecimal(runningEvent.drRampUpTime));
updateChannel(grp + CH_EVENT_LINK_REF, EcobeeUtils.undefOrString(runningEvent.linkRef));
updateChannel(grp + CH_EVENT_HOLD_CLIMATE_REF, EcobeeUtils.undefOrString(runningEvent.holdClimateRef));
}
private void updateWeather(@Nullable WeatherDTO weather) {
if (weather == null || weather.forecasts == null) {
return;
}
final String weatherGrp = CHGRP_WEATHER + "#";
updateChannel(weatherGrp + CH_WEATHER_TIMESTAMP, EcobeeUtils.undefOrDate(weather.timestamp, timeZoneProvider));
updateChannel(weatherGrp + CH_WEATHER_WEATHER_STATION, EcobeeUtils.undefOrString(weather.weatherStation));
for (int index = 0; index < weather.forecasts.size(); index++) {
final String grp = CHGRP_FORECAST + String.format("%d", index) + "#";
WeatherForecastDTO forecast = weather.forecasts.get(index);
if (forecast != null) {
updateChannel(grp + CH_FORECAST_WEATHER_SYMBOL, EcobeeUtils.undefOrDecimal(forecast.weatherSymbol));
updateChannel(grp + CH_FORECAST_WEATHER_SYMBOL_TEXT,
EcobeeUtils.undefOrString(symbolMap.get(forecast.weatherSymbol)));
updateChannel(grp + CH_FORECAST_DATE_TIME,
EcobeeUtils.undefOrDate(forecast.dateTime, timeZoneProvider));
updateChannel(grp + CH_FORECAST_CONDITION, EcobeeUtils.undefOrString(forecast.condition));
updateChannel(grp + CH_FORECAST_TEMPERATURE, EcobeeUtils.undefOrTemperature(forecast.temperature));
updateChannel(grp + CH_FORECAST_PRESSURE,
EcobeeUtils.undefOrQuantity(forecast.pressure, ImperialUnits.INCH_OF_MERCURY));
updateChannel(grp + CH_FORECAST_RELATIVE_HUMIDITY,
EcobeeUtils.undefOrQuantity(forecast.relativeHumidity, SmartHomeUnits.PERCENT));
updateChannel(grp + CH_FORECAST_DEWPOINT, EcobeeUtils.undefOrTemperature(forecast.dewpoint));
updateChannel(grp + CH_FORECAST_VISIBILITY,
EcobeeUtils.undefOrQuantity(forecast.visibility, SIUnits.METRE));
updateChannel(grp + CH_FORECAST_WIND_SPEED,
EcobeeUtils.undefOrQuantity(forecast.windSpeed, ImperialUnits.MILES_PER_HOUR));
updateChannel(grp + CH_FORECAST_WIND_GUST,
EcobeeUtils.undefOrQuantity(forecast.windGust, ImperialUnits.MILES_PER_HOUR));
updateChannel(grp + CH_FORECAST_WIND_DIRECTION, EcobeeUtils.undefOrString(forecast.windDirection));
updateChannel(grp + CH_FORECAST_WIND_BEARING,
EcobeeUtils.undefOrQuantity(forecast.windBearing, SmartHomeUnits.DEGREE_ANGLE));
updateChannel(grp + CH_FORECAST_POP, EcobeeUtils.undefOrQuantity(forecast.pop, SmartHomeUnits.PERCENT));
updateChannel(grp + CH_FORECAST_TEMP_HIGH, EcobeeUtils.undefOrTemperature(forecast.tempHigh));
updateChannel(grp + CH_FORECAST_TEMP_LOW, EcobeeUtils.undefOrTemperature(forecast.tempLow));
updateChannel(grp + CH_FORECAST_SKY, EcobeeUtils.undefOrDecimal(forecast.sky));
updateChannel(grp + CH_FORECAST_SKY_TEXT, EcobeeUtils.undefOrString(skyMap.get(forecast.sky)));
}
}
}
private void updateVersion(@Nullable VersionDTO version) {
if (version == null) {
return;
}
final String grp = CHGRP_VERSION + "#";
updateChannel(grp + CH_THERMOSTAT_FIRMWARE_VERSION,
EcobeeUtils.undefOrString(version.thermostatFirmwareVersion));
}
private void updateLocation(@Nullable LocationDTO loc) {
LocationDTO location = EMPTY_LOCATION;
if (loc != null) {
location = loc;
}
final String grp = CHGRP_LOCATION + "#";
updateChannel(grp + CH_TIME_ZONE_OFFSET_MINUTES, EcobeeUtils.undefOrDecimal(location.timeZoneOffsetMinutes));
updateChannel(grp + CH_TIME_ZONE, EcobeeUtils.undefOrString(location.timeZone));
updateChannel(grp + CH_IS_DAYLIGHT_SAVING, EcobeeUtils.undefOrOnOff(location.isDaylightSaving));
updateChannel(grp + CH_STREET_ADDRESS, EcobeeUtils.undefOrString(location.streetAddress));
updateChannel(grp + CH_CITY, EcobeeUtils.undefOrString(location.city));
updateChannel(grp + CH_PROVINCE_STATE, EcobeeUtils.undefOrString(location.provinceState));
updateChannel(grp + CH_COUNTRY, EcobeeUtils.undefOrString(location.country));
updateChannel(grp + CH_POSTAL_CODE, EcobeeUtils.undefOrString(location.postalCode));
updateChannel(grp + CH_PHONE_NUMBER, EcobeeUtils.undefOrString(location.phoneNumber));
updateChannel(grp + CH_MAP_COORDINATES, EcobeeUtils.undefOrPoint(location.mapCoordinates));
}
private void updateHouseDetails(@Nullable HouseDetailsDTO hd) {
HouseDetailsDTO houseDetails = EMPTY_HOUSEDETAILS;
if (hd != null) {
houseDetails = hd;
}
final String grp = CHGRP_HOUSE_DETAILS + "#";
updateChannel(grp + CH_HOUSEDETAILS_STYLE, EcobeeUtils.undefOrString(houseDetails.style));
updateChannel(grp + CH_HOUSEDETAILS_SIZE, EcobeeUtils.undefOrDecimal(houseDetails.size));
updateChannel(grp + CH_HOUSEDETAILS_NUMBER_OF_FLOORS, EcobeeUtils.undefOrDecimal(houseDetails.numberOfFloors));
updateChannel(grp + CH_HOUSEDETAILS_NUMBER_OF_ROOMS, EcobeeUtils.undefOrDecimal(houseDetails.numberOfRooms));
updateChannel(grp + CH_HOUSEDETAILS_NUMBER_OF_OCCUPANTS,
EcobeeUtils.undefOrDecimal(houseDetails.numberOfOccupants));
updateChannel(grp + CH_HOUSEDETAILS_AGE, EcobeeUtils.undefOrDecimal(houseDetails.age));
updateChannel(grp + CH_HOUSEDETAILS_WINDOW_EFFICIENCY,
EcobeeUtils.undefOrDecimal(houseDetails.windowEfficiency));
}
private void updateManagement(@Nullable ManagementDTO mgmt) {
ManagementDTO management = EMPTY_MANAGEMENT;
if (mgmt != null) {
management = mgmt;
}
final String grp = CHGRP_MANAGEMENT + "#";
updateChannel(grp + CH_MANAGEMENT_ADMIN_CONTACT, EcobeeUtils.undefOrString(management.administrativeContact));
updateChannel(grp + CH_MANAGEMENT_BILLING_CONTACT, EcobeeUtils.undefOrString(management.billingContact));
updateChannel(grp + CH_MANAGEMENT_NAME, EcobeeUtils.undefOrString(management.name));
updateChannel(grp + CH_MANAGEMENT_PHONE, EcobeeUtils.undefOrString(management.phone));
updateChannel(grp + CH_MANAGEMENT_EMAIL, EcobeeUtils.undefOrString(management.email));
updateChannel(grp + CH_MANAGEMENT_WEB, EcobeeUtils.undefOrString(management.web));
updateChannel(grp + CH_MANAGEMENT_SHOW_ALERT_IDT, EcobeeUtils.undefOrOnOff(management.showAlertIdt));
updateChannel(grp + CH_MANAGEMENT_SHOW_ALERT_WEB, EcobeeUtils.undefOrOnOff(management.showAlertWeb));
}
private void updateTechnician(@Nullable TechnicianDTO tech) {
TechnicianDTO technician = EMPTY_TECHNICIAN;
if (tech != null) {
technician = tech;
}
final String grp = CHGRP_TECHNICIAN + "#";
updateChannel(grp + CH_TECHNICIAN_CONTRACTOR_REF, EcobeeUtils.undefOrString(technician.contractorRef));
updateChannel(grp + CH_TECHNICIAN_NAME, EcobeeUtils.undefOrString(technician.name));
updateChannel(grp + CH_TECHNICIAN_PHONE, EcobeeUtils.undefOrString(technician.phone));
updateChannel(grp + CH_TECHNICIAN_STREET_ADDRESS, EcobeeUtils.undefOrString(technician.streetAddress));
updateChannel(grp + CH_TECHNICIAN_CITY, EcobeeUtils.undefOrString(technician.city));
updateChannel(grp + CH_TECHNICIAN_PROVINCE_STATE, EcobeeUtils.undefOrString(technician.provinceState));
updateChannel(grp + CH_TECHNICIAN_COUNTRY, EcobeeUtils.undefOrString(technician.country));
updateChannel(grp + CH_TECHNICIAN_POSTAL_CODE, EcobeeUtils.undefOrString(technician.postalCode));
updateChannel(grp + CH_TECHNICIAN_EMAIL, EcobeeUtils.undefOrString(technician.email));
updateChannel(grp + CH_TECHNICIAN_WEB, EcobeeUtils.undefOrString(technician.web));
}
private void updateChannel(String channelId, State state) {
updateState(channelId, state);
stateCache.put(channelId, state);
}
@SuppressWarnings("null")
private void updateRemoteSensors(@Nullable List<RemoteSensorDTO> remoteSensors) {
if (remoteSensors == null) {
return;
}
logger.debug("ThermostatBridge: Thermostat '{}' has {} remote sensors", thermostatId, remoteSensors.size());
for (RemoteSensorDTO sensor : remoteSensors) {
EcobeeSensorThingHandler handler = sensorHandlers.get(sensor.id);
if (handler != null) {
logger.debug("ThermostatBridge: Sending data to sensor handler '{}({})' of type '{}'", sensor.id,
sensor.name, sensor.type);
handler.updateChannels(sensor);
}
}
}
private void performThermostatUpdate(ThermostatDTO thermostat) {
SelectionDTO selection = new SelectionDTO();
selection.setThermostats(Collections.singleton(thermostatId));
ThermostatUpdateRequestDTO request = new ThermostatUpdateRequestDTO(selection);
request.thermostat = thermostat;
EcobeeAccountBridgeHandler handler = getBridgeHandler();
if (handler != null) {
handler.performThermostatUpdate(request);
}
}
private void scheduleDiscoveryJob() {
logger.debug("ThermostatBridge: Scheduling sensor discovery job");
cancelDiscoveryJob();
discoverSensorsJob = scheduler.scheduleWithFixedDelay(this::discoverSensors,
SENSOR_DISCOVERY_STARTUP_DELAY_SECONDS, SENSOR_DISCOVERY_INTERVAL_SECONDS, TimeUnit.SECONDS);
}
private void cancelDiscoveryJob() {
Future<?> localDiscoverSensorsJob = discoverSensorsJob;
if (localDiscoverSensorsJob != null) {
localDiscoverSensorsJob.cancel(true);
logger.debug("ThermostatBridge: Canceling sensor discovery job");
}
}
private void discoverSensors() {
EcobeeAccountBridgeHandler handler = getBridgeHandler();
if (handler != null && handler.isDiscoveryEnabled()) {
SensorDiscoveryService localDiscoveryService = discoveryService;
if (localDiscoveryService != null) {
logger.debug("ThermostatBridge: Running sensor discovery");
localDiscoveryService.startBackgroundDiscovery();
}
}
}
private @Nullable EcobeeAccountBridgeHandler getBridgeHandler() {
EcobeeAccountBridgeHandler handler = null;
Bridge bridge = getBridge();
if (bridge != null) {
handler = (EcobeeAccountBridgeHandler) bridge.getHandler();
}
return handler;
}
@SuppressWarnings("null")
private boolean isChannelReadOnly(ChannelUID channelUID) {
Boolean isReadOnly = channelReadOnlyMap.get(channelUID);
return isReadOnly != null ? isReadOnly : true;
}
private void clearSavedState() {
savedThermostat = null;
savedSensors = null;
stateCache.clear();
}
private void initializeReadOnlyChannels() {
channelReadOnlyMap.clear();
for (Channel channel : thing.getChannels()) {
ChannelTypeUID channelTypeUID = channel.getChannelTypeUID();
if (channelTypeUID != null) {
ChannelType channelType = channelTypeRegistry.getChannelType(channelTypeUID, null);
if (channelType != null) {
channelReadOnlyMap.putIfAbsent(channel.getUID(), channelType.getState().isReadOnly());
}
}
}
}
private void initializeWeatherMaps() {
initializeSymbolMap();
initializeSkyMap();
}
private void initializeSymbolMap() {
symbolMap.clear();
symbolMap.put(-2, "NO SYMBOL");
symbolMap.put(0, "SUNNY");
symbolMap.put(1, "FEW CLOUDS");
symbolMap.put(2, "PARTLY CLOUDY");
symbolMap.put(3, "MOSTLY CLOUDY");
symbolMap.put(4, "OVERCAST");
symbolMap.put(5, "DRIZZLE");
symbolMap.put(6, "RAIN");
symbolMap.put(7, "FREEZING RAIN");
symbolMap.put(8, "SHOWERS");
symbolMap.put(9, "HAIL");
symbolMap.put(10, "SNOW");
symbolMap.put(11, "FLURRIES");
symbolMap.put(12, "FREEZING SNOW");
symbolMap.put(13, "BLIZZARD");
symbolMap.put(14, "PELLETS");
symbolMap.put(15, "THUNDERSTORM");
symbolMap.put(16, "WINDY");
symbolMap.put(17, "TORNADO");
symbolMap.put(18, "FOG");
symbolMap.put(19, "HAZE");
symbolMap.put(20, "SMOKE");
symbolMap.put(21, "DUST");
}
private void initializeSkyMap() {
skyMap.clear();
skyMap.put(1, "SUNNY");
skyMap.put(2, "CLEAR");
skyMap.put(3, "MOSTLY SUNNY");
skyMap.put(4, "MOSTLY CLEAR");
skyMap.put(5, "HAZY SUNSHINE");
skyMap.put(6, "HAZE");
skyMap.put(7, "PASSING CLOUDS");
skyMap.put(8, "MORE SUN THAN CLOUDS");
skyMap.put(9, "SCATTERED CLOUDS");
skyMap.put(10, "PARTLY CLOUDY");
skyMap.put(11, "A MIXTURE OF SUN AND CLOUDS");
skyMap.put(12, "HIGH LEVEL CLOUDS");
skyMap.put(13, "MORE CLOUDS THAN SUN");
skyMap.put(14, "PARTLY SUNNY");
skyMap.put(15, "BROKEN CLOUDS");
skyMap.put(16, "MOSTLY CLOUDY");
skyMap.put(17, "CLOUDY");
skyMap.put(18, "OVERCAST");
skyMap.put(19, "LOW CLOUDS");
skyMap.put(20, "LIGHT FOG");
skyMap.put(21, "FOG");
skyMap.put(22, "DENSE FOG");
skyMap.put(23, "ICE FOG");
skyMap.put(24, "SANDSTORM");
skyMap.put(25, "DUSTSTORM");
skyMap.put(26, "INCREASING CLOUDINESS");
skyMap.put(27, "DECREASING CLOUDINESS");
skyMap.put(28, "CLEARING SKIES");
skyMap.put(29, "BREAKS OF SUN LATE");
skyMap.put(30, "EARLY FOG FOLLOWED BY SUNNY SKIES");
skyMap.put(31, "AFTERNOON CLOUDS");
skyMap.put(32, "MORNING CLOUDS");
skyMap.put(33, "SMOKE");
skyMap.put(34, "LOW LEVEL HAZE");
}
}

View File

@@ -0,0 +1,109 @@
/**
* Copyright (c) 2010-2020 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.ecobee.internal.handler;
import java.time.ZonedDateTime;
import java.util.Date;
import javax.measure.Unit;
import javax.measure.quantity.Temperature;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.i18n.TimeZoneProvider;
import org.openhab.core.library.types.DateTimeType;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.PointType;
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.thing.Bridge;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.types.State;
import org.openhab.core.types.UnDefType;
/**
* The {@link EcobeeUtils} contains utility methods used by the
* thing handler and the bridge handler.
*
* @author Mark Hilbush - Initial contribution
*/
@NonNullByDefault
public final class EcobeeUtils {
private static final int UNKNOWN_VALUE = -5002;
/*
* Checks to see if a bridge is online.
*/
public static boolean isBridgeOnline(@Nullable Bridge bridge) {
boolean bridgeStatus = false;
if (bridge != null && bridge.getStatus() == ThingStatus.ONLINE) {
bridgeStatus = true;
}
return bridgeStatus;
}
/*
* Set the state to the passed value. If value is null, set the state to UNDEF
*/
public static State undefOrOnOff(@Nullable Boolean value) {
return value == null ? UnDefType.UNDEF : (value.booleanValue() ? OnOffType.ON : OnOffType.OFF);
}
public static State undefOrString(@Nullable String value) {
return value == null ? UnDefType.UNDEF : new StringType(value);
}
public static State undefOrDecimal(@Nullable Number value) {
return (value == null || isUnknown(value)) ? UnDefType.UNDEF : new DecimalType(value.doubleValue());
}
public static State undefOrQuantity(@Nullable Number value, Unit<?> unit) {
return (value == null || isUnknown(value)) ? UnDefType.UNDEF : new QuantityType<>(value, unit);
}
public static State undefOrTemperature(@Nullable Number value) {
return (value == null || isUnknown(value)) ? UnDefType.UNDEF
: new QuantityType<>(value.doubleValue() / 10.0, ImperialUnits.FAHRENHEIT);
}
public static State undefOrPoint(@Nullable String value) {
return value == null ? UnDefType.UNDEF : new PointType(value);
}
public static State undefOrDate(@Nullable Date date, TimeZoneProvider timeZoneProvider) {
return date == null ? UnDefType.UNDEF
: new DateTimeType(ZonedDateTime.ofInstant(date.toInstant(), timeZoneProvider.getTimeZone()));
}
private static boolean isUnknown(Number value) {
return value.intValue() == UNKNOWN_VALUE;
}
/*
* Convert a QuantityType<Temperature> to the internal format used by the Ecobee API.
*/
@SuppressWarnings("unchecked")
public static Integer convertQuantityTypeToEcobeeTemp(Object value) {
if (value instanceof QuantityType<?>) {
QuantityType<Temperature> convertedTemp = ((QuantityType<Temperature>) value)
.toUnit(ImperialUnits.FAHRENHEIT);
if (convertedTemp != null) {
return Integer.valueOf(convertedTemp.intValue() * 10);
}
}
throw new IllegalArgumentException("temperature is not a QuantityType");
}
}

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<binding:binding id="ecobee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:binding="https://openhab.org/schemas/binding/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/binding/v1.0.0 https://openhab.org/schemas/binding-1.0.0.xsd">
<name>Ecobee Binding</name>
<description>This is the binding for Ecobee smart thermostats.</description>
<author>Mark Hilbush</author>
</binding:binding>

View File

@@ -0,0 +1,52 @@
<config-description:config-descriptions
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:config-description="https://openhab.org/schemas/config-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/config-description/v1.0.0 https://openhab.org/schemas/config-description-1.0.0.xsd">
<config-description uri="thing-type:ecobee:account">
<parameter name="apiKey" type="text" required="true">
<label>API Key</label>
<description>Enter the API key</description>
<context>password</context>
</parameter>
<parameter name="refreshIntervalNormal" type="integer" min="2" required="false" unit="s">
<label>Refresh Interval (Normal)</label>
<description>Specifies the refresh interval in seconds</description>
<default>30</default>
</parameter>
<parameter name="refreshIntervalQuick" type="integer" min="2" required="false" unit="s">
<label>Refresh Interval (Quick)</label>
<description>Specifies time in seconds to wait after successful update, command or action before refresh</description>
<default>5</default>
</parameter>
<parameter name="apiTimeout" type="integer" min="2" required="false" unit="s">
<label>API Timeout</label>
<description>Time in seconds to allow API request to complete</description>
<default>22</default>
</parameter>
<parameter name="discoveryEnabled" type="boolean" required="false">
<label>Discovery Enabled</label>
<description>Enable/disable automatic discovery</description>
<default>true</default>
</parameter>
<parameter name="discoveryInterval" type="integer" min="60" required="false" unit="s">
<label>Thermostat Discovery Interval</label>
<description>Specifies time in seconds in which the binding will attempt to discover thermostats</description>
</parameter>
</config-description>
<config-description uri="thing-type:ecobee:thermostat">
<parameter name="thermostatId" type="text" required="true">
<label>Thermostat ID</label>
<description>Thermostat ID assigned to this thermostat by Ecobee</description>
</parameter>
</config-description>
<config-description uri="thing-type:ecobee:sensor">
<parameter name="sensorId" type="text" required="true">
<label>Sensor Id</label>
<description>Id assigned to this sensor (e.g. rs:101)</description>
</parameter>
</config-description>
</config-description:config-descriptions>