type: array
items:
$ref: "#/definitions/ACFanLevel"
- horizontalSwing:
- description: Cooling system horizontal swing modes. (Tado confusingly names this array without an 's')
- type: array
- items:
- $ref: "#/definitions/ACHorizontalSwing"
verticalSwing:
description: Cooling system vertical swing modes. (Tado confusingly names this array without an 's')
type: array
items:
$ref: "#/definitions/ACVerticalSwing"
+ horizontalSwing:
+ description: Cooling system horizontal swing modes. (Tado confusingly names this array without an 's')
+ type: array
+ items:
+ $ref: "#/definitions/ACHorizontalSwing"
HeatingCapabilities:
x-discriminator-value: HEATING
import java.io.IOException;
+import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.tado.internal.TadoBindingConstants.FanLevel;
import org.openhab.binding.tado.internal.TadoBindingConstants.FanSpeed;
import org.openhab.binding.tado.internal.TadoBindingConstants.HorizontalSwing;
import org.openhab.binding.tado.internal.builder.ZoneStateProvider;
import org.openhab.binding.tado.internal.handler.TadoZoneHandler;
import org.openhab.core.thing.Thing;
+import org.openhab.core.thing.binding.ThingHandler;
/**
* Builder for incremental creation of zone overlays.
*
* @author Dennis Frommknecht - Initial contribution
*/
+@NonNullByDefault
public class TadoHvacChange {
- private TadoZoneHandler zoneHandler;
+
+ private final TadoZoneHandler zoneHandler;
+ private final TerminationConditionBuilder terminationConditionBuilder;
+ private final ZoneSettingsBuilder settingsBuilder;
private boolean followSchedule = false;
- private TerminationConditionBuilder terminationConditionBuilder;
- private ZoneSettingsBuilder settingsBuilder;
public TadoHvacChange(Thing zoneThing) {
- if (!(zoneThing.getHandler() instanceof TadoZoneHandler)) {
+ ThingHandler handler = zoneThing.getHandler();
+ if (!(handler instanceof TadoZoneHandler)) {
throw new IllegalArgumentException("TadoZoneThing expected, but instead got " + zoneThing);
}
-
- this.zoneHandler = (TadoZoneHandler) zoneThing.getHandler();
- this.terminationConditionBuilder = TerminationConditionBuilder.of(zoneHandler);
- this.settingsBuilder = ZoneSettingsBuilder.of(zoneHandler);
+ zoneHandler = (TadoZoneHandler) handler;
+ terminationConditionBuilder = TerminationConditionBuilder.of(zoneHandler);
+ settingsBuilder = ZoneSettingsBuilder.of(zoneHandler);
}
public TadoHvacChange withOperationMode(OperationMode operationMode) {
case MANUAL:
return activeForever();
case TIMER:
- return activeFor(null);
+ return activeForMinutes(0);
case UNTIL_CHANGE:
return activeUntilChange();
- default:
- return this;
}
+ return this;
}
public TadoHvacChange followSchedule() {
return this;
}
- public TadoHvacChange activeFor(Integer minutes) {
+ public TadoHvacChange activeForMinutes(int minutes) {
terminationConditionBuilder.withTerminationType(OverlayTerminationConditionType.TIMER);
- terminationConditionBuilder.withTimerDurationInSeconds(minutes != null ? minutes * 60 : null);
+ terminationConditionBuilder.withTimerDurationInSeconds(minutes * 60);
return this;
}
import java.math.RoundingMode;
import java.time.OffsetDateTime;
+import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.tado.internal.TadoBindingConstants.HvacMode;
import org.openhab.binding.tado.internal.TadoBindingConstants.OperationMode;
import org.openhab.binding.tado.internal.TadoBindingConstants.TemperatureUnit;
import org.openhab.binding.tado.internal.api.model.HotWaterZoneSetting;
import org.openhab.binding.tado.internal.api.model.Overlay;
import org.openhab.binding.tado.internal.api.model.OverlayTerminationConditionType;
+import org.openhab.binding.tado.internal.api.model.PercentageDataPoint;
import org.openhab.binding.tado.internal.api.model.Power;
import org.openhab.binding.tado.internal.api.model.SensorDataPoints;
import org.openhab.binding.tado.internal.api.model.TadoSystemType;
* @author Andrew Fiddian-Green - Added Low Battery Alarm, A/C Power and Open Window channels
*
*/
+@NonNullByDefault
public class TadoZoneStateAdapter {
- private ZoneState zoneState;
- private TemperatureUnit temperatureUnit;
+ private final ZoneState zoneState;
+ private final TemperatureUnit temperatureUnit;
public TadoZoneStateAdapter(ZoneState zoneState, TemperatureUnit temperatureUnit) {
this.zoneState = zoneState;
return toTemperatureState(sensorDataPoints.getInsideTemperature(), temperatureUnit);
}
- public DecimalType getHumidity() {
- SensorDataPoints sensorDataPoints = zoneState.getSensorDataPoints();
- return sensorDataPoints.getHumidity() != null ? toDecimalType(sensorDataPoints.getHumidity().getPercentage())
- : null;
+ public State getHumidity() {
+ PercentageDataPoint humidity = zoneState.getSensorDataPoints().getHumidity();
+ return humidity != null ? toDecimalType(humidity.getPercentage()) : UnDefType.UNDEF;
}
public DecimalType getHeatingPower() {
: DecimalType.ZERO;
}
- public OnOffType getAcPower() {
+ public State getAcPower() {
ActivityDataPoints dataPoints = zoneState.getActivityDataPoints();
AcPowerDataPoint acPower = dataPoints.getAcPower();
if (acPower != null) {
return OnOffType.from(acPowerValue);
}
}
- return null;
+ return UnDefType.UNDEF;
}
public StringType getMode() {
}
public State getTargetTemperature() {
+ if (!isPowerOn()) {
+ return UnDefType.UNDEF;
+ }
switch (zoneState.getSetting().getType()) {
case HEATING:
return toTemperatureState(((HeatingZoneSetting) zoneState.getSetting()).getTemperature(),
}
private static State toTemperatureState(TemperatureObject temperature, TemperatureUnit temperatureUnit) {
- if (temperature == null) {
- return UnDefType.NULL;
- }
-
return temperatureUnit == TemperatureUnit.FAHRENHEIT
? new QuantityType<>(temperature.getFahrenheit(), ImperialUnits.FAHRENHEIT)
: new QuantityType<>(temperature.getCelsius(), SIUnits.CELSIUS);
}
private static State toTemperatureState(TemperatureDataPoint temperature, TemperatureUnit temperatureUnit) {
- if (temperature == null) {
- return UnDefType.NULL;
- }
-
return temperatureUnit == TemperatureUnit.FAHRENHEIT
? new QuantityType<>(temperature.getFahrenheit(), ImperialUnits.FAHRENHEIT)
: new QuantityType<>(temperature.getCelsius(), SIUnits.CELSIUS);
*/
package org.openhab.binding.tado.internal.api;
+import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.tado.internal.api.auth.Authorizer;
import org.openhab.binding.tado.internal.api.auth.OAuthAuthorizer;
import org.openhab.binding.tado.internal.api.client.HomeApi;
*
* @author Dennis Frommknecht - Initial contribution
*/
+@NonNullByDefault
public class HomeApiFactory {
private static final String OAUTH_SCOPE = "home.user";
private static final String OAUTH_CLIENT_ID = "public-api-preview";
*/
package org.openhab.binding.tado.internal.api;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.tado.internal.TadoBindingConstants.FanLevel;
import org.openhab.binding.tado.internal.TadoBindingConstants.FanSpeed;
import org.openhab.binding.tado.internal.TadoBindingConstants.HorizontalSwing;
import org.openhab.binding.tado.internal.api.model.AcMode;
import org.openhab.binding.tado.internal.api.model.AcModeCapabilities;
import org.openhab.binding.tado.internal.api.model.AirConditioningCapabilities;
+import org.openhab.binding.tado.internal.api.model.GenericZoneCapabilities;
import org.openhab.binding.tado.internal.api.model.ManualTerminationCondition;
import org.openhab.binding.tado.internal.api.model.OverlayTerminationCondition;
import org.openhab.binding.tado.internal.api.model.OverlayTerminationConditionTemplate;
*
* @author Dennis Frommknecht - Initial contribution
*/
+@NonNullByDefault
public class TadoApiTypeUtils {
+
public static OverlayTerminationCondition getTerminationCondition(OverlayTerminationConditionType type,
- Integer timerDurationInSeconds) {
+ int timerDurationInSeconds) {
switch (type) {
case TIMER:
return timerTermination(timerDurationInSeconds);
case TADO_MODE:
return tadoModeTermination();
default:
- return null;
+ throw new IllegalArgumentException("Unexpected OverlayTerminationConditionType " + type);
}
}
public static OverlayTerminationCondition cleanTerminationCondition(
OverlayTerminationCondition terminationCondition) {
- Integer timerDuration = terminationCondition.getType() == OverlayTerminationConditionType.TIMER
- ? ((TimerTerminationCondition) terminationCondition).getRemainingTimeInSeconds()
- : null;
+ OverlayTerminationConditionType conditionType = terminationCondition.getType();
- return getTerminationCondition(terminationCondition.getType(), timerDuration);
+ int timerDuration = 0;
+ if (conditionType == OverlayTerminationConditionType.TIMER) {
+ Integer duration = ((TimerTerminationCondition) terminationCondition).getRemainingTimeInSeconds();
+ if (duration != null) {
+ timerDuration = duration.intValue();
+ }
+ }
+
+ return getTerminationCondition(conditionType, timerDuration);
}
public static OverlayTerminationCondition terminationConditionTemplateToTerminationCondition(
OverlayTerminationConditionTemplate template) {
- Integer timerDuration = template.getType() == OverlayTerminationConditionType.TIMER
- ? ((TimerTerminationConditionTemplate) template).getDurationInSeconds()
- : null;
+ OverlayTerminationConditionType conditionType = template.getType();
- return getTerminationCondition(template.getType(), timerDuration);
+ int timerDuration = 0;
+ if (conditionType == OverlayTerminationConditionType.TIMER) {
+ Integer duration = ((TimerTerminationConditionTemplate) template).getDurationInSeconds();
+ if (duration != null) {
+ timerDuration = duration.intValue();
+ }
+ }
+
+ return getTerminationCondition(conditionType, timerDuration);
}
public static TimerTerminationCondition timerTermination(int durationInSeconds) {
}
public static Float getTemperatureInUnit(TemperatureObject temperature, TemperatureUnit temperatureUnit) {
- if (temperature == null) {
- return null;
- }
-
return temperatureUnit == TemperatureUnit.FAHRENHEIT ? temperature.getFahrenheit() : temperature.getCelsius();
}
public static AcMode getAcMode(HvacMode mode) {
- if (mode == null) {
- return null;
- }
-
switch (mode) {
case HEAT:
return AcMode.HEAT;
case AUTO:
return AcMode.AUTO;
default:
- return null;
+ throw new IllegalArgumentException("Unexpected AcMode " + mode);
}
}
public static AcFanSpeed getAcFanSpeed(FanSpeed fanSpeed) {
- if (fanSpeed == null) {
- return null;
- }
-
switch (fanSpeed) {
case AUTO:
return AcFanSpeed.AUTO;
return AcFanSpeed.MIDDLE;
case LOW:
return AcFanSpeed.LOW;
+ default:
+ throw new IllegalArgumentException("Unexpected AcFanSpeed " + fanSpeed);
}
-
- return null;
}
public static ACFanLevel getFanLevel(FanLevel fanLevel) {
- if (fanLevel == null) {
- return null;
- }
-
switch (fanLevel) {
case AUTO:
return ACFanLevel.AUTO;
return ACFanLevel.LEVEL5;
case SILENT:
return ACFanLevel.SILENT;
+ default:
+ throw new IllegalArgumentException("Unexpected FanLevel " + fanLevel);
}
-
- return null;
}
public static ACHorizontalSwing getHorizontalSwing(HorizontalSwing horizontalSwing) {
- if (horizontalSwing == null) {
- return null;
- }
-
switch (horizontalSwing) {
case LEFT:
return ACHorizontalSwing.LEFT;
return ACHorizontalSwing.OFF;
case AUTO:
return ACHorizontalSwing.AUTO;
+ default:
+ throw new IllegalArgumentException("Unexpected HorizontalSwing " + horizontalSwing);
}
-
- return null;
}
public static ACVerticalSwing getVerticalSwing(VerticalSwing verticalSwing) {
- if (verticalSwing == null) {
- return null;
- }
-
switch (verticalSwing) {
case AUTO:
return ACVerticalSwing.AUTO;
return ACVerticalSwing.ON;
case OFF:
return ACVerticalSwing.OFF;
+ default:
+ throw new IllegalArgumentException("Unexpected VerticalSwing " + verticalSwing);
}
-
- return null;
}
- public static AcModeCapabilities getModeCapabilities(AirConditioningCapabilities capabilities, AcMode mode) {
- AcModeCapabilities modeCapabilities = null;
+ public static AcModeCapabilities getModeCapabilities(AcMode acMode,
+ @Nullable GenericZoneCapabilities capabilities) {
+ AirConditioningCapabilities acCapabilities;
- if (mode != null) {
- switch (mode) {
- case COOL:
- modeCapabilities = capabilities.getCOOL();
- break;
- case HEAT:
- modeCapabilities = capabilities.getHEAT();
- break;
- case DRY:
- modeCapabilities = capabilities.getDRY();
- break;
- case AUTO:
- modeCapabilities = capabilities.getAUTO();
- break;
- case FAN:
- modeCapabilities = capabilities.getFAN();
- break;
- }
+ if (capabilities instanceof AirConditioningCapabilities) {
+ acCapabilities = (AirConditioningCapabilities) capabilities;
+ } else {
+ acCapabilities = new AirConditioningCapabilities();
}
- return modeCapabilities != null ? modeCapabilities : new AcModeCapabilities();
+ switch (acMode) {
+ case COOL:
+ return acCapabilities.getCOOL();
+ case HEAT:
+ return acCapabilities.getHEAT();
+ case DRY:
+ return acCapabilities.getDRY();
+ case AUTO:
+ return acCapabilities.getAUTO();
+ case FAN:
+ return acCapabilities.getFAN();
+ default:
+ throw new IllegalArgumentException("Unexpected AcMode " + acMode);
+ }
}
}
import java.io.IOException;
import java.util.List;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.openhab.binding.tado.internal.TadoBindingConstants.FanLevel;
+import org.openhab.binding.tado.internal.TadoBindingConstants.FanSpeed;
+import org.openhab.binding.tado.internal.TadoBindingConstants.HorizontalSwing;
import org.openhab.binding.tado.internal.TadoBindingConstants.HvacMode;
import org.openhab.binding.tado.internal.TadoBindingConstants.TemperatureUnit;
+import org.openhab.binding.tado.internal.TadoBindingConstants.VerticalSwing;
import org.openhab.binding.tado.internal.api.ApiException;
+import org.openhab.binding.tado.internal.api.TadoApiTypeUtils;
import org.openhab.binding.tado.internal.api.model.ACFanLevel;
import org.openhab.binding.tado.internal.api.model.ACHorizontalSwing;
import org.openhab.binding.tado.internal.api.model.ACVerticalSwing;
import org.openhab.binding.tado.internal.api.model.AcFanSpeed;
import org.openhab.binding.tado.internal.api.model.AcMode;
import org.openhab.binding.tado.internal.api.model.AcModeCapabilities;
-import org.openhab.binding.tado.internal.api.model.AirConditioningCapabilities;
import org.openhab.binding.tado.internal.api.model.CoolingZoneSetting;
import org.openhab.binding.tado.internal.api.model.GenericZoneCapabilities;
import org.openhab.binding.tado.internal.api.model.GenericZoneSetting;
import org.openhab.binding.tado.internal.api.model.TadoSystemType;
import org.openhab.binding.tado.internal.api.model.TemperatureObject;
import org.openhab.binding.tado.internal.api.model.TemperatureRange;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
*
*
* @author Dennis Frommknecht - Initial contribution
*/
+@NonNullByDefault
public class AirConditioningZoneSettingsBuilder extends ZoneSettingsBuilder {
private static final AcMode DEFAULT_MODE = AcMode.COOL;
private static final float DEFAULT_TEMPERATURE_C = 20.0f;
private static final float DEFAULT_TEMPERATURE_F = 68.0f;
+ private static final String STATE_VALUE_NOT_SUPPORTED = "Your a/c unit does not support '{}:{}' when in state '{}:{}', (supported values: [{}]).";
+ private Logger logger = LoggerFactory.getLogger(AirConditioningZoneSettingsBuilder.class);
+
@Override
public GenericZoneSetting build(ZoneStateProvider zoneStateProvider, GenericZoneCapabilities genericCapabilities)
throws IOException, ApiException {
return coolingSetting(false);
}
- CoolingZoneSetting setting = coolingSetting(true);
- setting.setMode(getAcMode(mode));
+ CoolingZoneSetting newSetting = coolingSetting(true);
+
+ AcMode targetMode;
+ HvacMode mode = this.mode;
+ if (mode != null) {
+ targetMode = getAcMode(mode);
+ newSetting.setMode(targetMode);
+ } else {
+ // if mode not changing, so the reference is the current (or default) mode
+ targetMode = getCurrentOrDefaultAcMode(zoneStateProvider);
+ }
+
+ Float temperature = this.temperature;
if (temperature != null) {
- setting.setTemperature(temperature(temperature, temperatureUnit));
+ newSetting.setTemperature(temperature(temperature, temperatureUnit));
}
+ Boolean swing = this.swing;
if (swing != null) {
- setting.setSwing(swing ? Power.ON : Power.OFF);
+ newSetting.setSwing(swing.booleanValue() ? Power.ON : Power.OFF);
}
+ Boolean light = this.light;
if (light != null) {
- setting.setLight(light ? Power.ON : Power.OFF);
+ newSetting.setLight(light.booleanValue() ? Power.ON : Power.OFF);
}
+ FanSpeed fanSpeed = this.fanSpeed;
if (fanSpeed != null) {
- setting.setFanSpeed(getAcFanSpeed(fanSpeed));
+ newSetting.setFanSpeed(getAcFanSpeed(fanSpeed));
}
+ /*
+ * In the latest API release Tado introduced extra AC settings that have an open ended list of possible
+ * supported state values. And for any particular device, its specific list of supported values is available
+ * via its 'capabilities' structure. So before setting a new value, we check if the respective new value is in
+ * the capabilities list that corresponds to the target AC mode. And if not, a warning message is logged.
+ */
+ AcModeCapabilities targetModeCapabilities = TadoApiTypeUtils.getModeCapabilities(targetMode,
+ genericCapabilities);
+
+ FanLevel fanLevel = this.fanLevel;
if (fanLevel != null) {
- setting.setFanLevel(getFanLevel(fanLevel));
+ ACFanLevel targetFanLevel = getFanLevel(fanLevel);
+ List<ACFanLevel> targetFanLevels = targetModeCapabilities.getFanLevel();
+ if (targetFanLevels != null && targetFanLevels.contains(targetFanLevel)) {
+ newSetting.setFanLevel(targetFanLevel);
+ } else {
+ logger.warn(STATE_VALUE_NOT_SUPPORTED, targetFanLevel.getClass().getSimpleName(), targetFanLevel,
+ targetMode.getClass().getSimpleName(), targetMode, targetFanLevels);
+ }
}
+ HorizontalSwing horizontalSwing = this.horizontalSwing;
if (horizontalSwing != null) {
- setting.setHorizontalSwing(getHorizontalSwing(horizontalSwing));
+ ACHorizontalSwing targetHorizontalSwing = getHorizontalSwing(horizontalSwing);
+ List<ACHorizontalSwing> targetHorizontalSwings = targetModeCapabilities.getHorizontalSwing();
+ if (targetHorizontalSwings != null && targetHorizontalSwings.contains(targetHorizontalSwing)) {
+ newSetting.setHorizontalSwing(targetHorizontalSwing);
+ } else {
+ logger.warn(STATE_VALUE_NOT_SUPPORTED, targetHorizontalSwing.getClass().getSimpleName(),
+ targetHorizontalSwing, targetMode.getClass().getSimpleName(), targetMode,
+ targetHorizontalSwings);
+ }
}
+ VerticalSwing verticalSwing = this.verticalSwing;
if (verticalSwing != null) {
- setting.setVerticalSwing(getVerticalSwing(verticalSwing));
+ ACVerticalSwing targetVerticalSwing = getVerticalSwing(verticalSwing);
+ List<ACVerticalSwing> targetVerticalSwings = targetModeCapabilities.getVerticalSwing();
+ if (targetVerticalSwings != null && targetVerticalSwings.contains(targetVerticalSwing)) {
+ newSetting.setVerticalSwing(targetVerticalSwing);
+ } else {
+ logger.warn(STATE_VALUE_NOT_SUPPORTED, targetVerticalSwing.getClass().getSimpleName(),
+ targetVerticalSwing, targetMode.getClass().getSimpleName(), targetMode, targetVerticalSwings);
+ }
}
- addMissingSettingParts(zoneStateProvider, genericCapabilities, setting);
+ addMissingSettingParts(zoneStateProvider, genericCapabilities, newSetting);
- return setting;
+ return newSetting;
}
private void addMissingSettingParts(ZoneStateProvider zoneStateProvider,
- GenericZoneCapabilities genericCapabilities, CoolingZoneSetting setting) throws IOException, ApiException {
- if (setting.getMode() == null) {
+ GenericZoneCapabilities genericCapabilities, CoolingZoneSetting newSetting)
+ throws IOException, ApiException {
+ if (newSetting.getMode() == null) {
AcMode targetMode = getCurrentOrDefaultAcMode(zoneStateProvider);
- setting.setMode(targetMode);
+ newSetting.setMode(targetMode);
}
- AcModeCapabilities capabilities = getModeCapabilities((AirConditioningCapabilities) genericCapabilities,
- setting.getMode());
+ AcModeCapabilities targetCapabilities = getModeCapabilities(newSetting.getMode(), genericCapabilities);
- TemperatureRange temperatures = capabilities.getTemperatures();
- if (temperatures != null && setting.getTemperature() == null) {
- setting.setTemperature(getCurrentOrDefaultTemperature(zoneStateProvider, temperatures));
+ TemperatureRange temperatures = targetCapabilities.getTemperatures();
+ if (temperatures != null && newSetting.getTemperature() == null) {
+ newSetting.setTemperature(getCurrentOrDefaultTemperature(zoneStateProvider, temperatures));
}
- List<AcFanSpeed> fanSpeeds = capabilities.getFanSpeeds();
- if (fanSpeeds != null && !fanSpeeds.isEmpty() && setting.getFanSpeed() == null) {
- setting.setFanSpeed(getCurrentOrDefaultFanSpeed(zoneStateProvider, fanSpeeds));
+ List<AcFanSpeed> fanSpeeds = targetCapabilities.getFanSpeeds();
+ if (fanSpeeds != null && !fanSpeeds.isEmpty() && newSetting.getFanSpeed() == null) {
+ newSetting.setFanSpeed(getCurrentOrDefaultFanSpeed(zoneStateProvider, fanSpeeds));
}
- List<Power> swings = capabilities.getSwings();
- if (swings != null && !swings.isEmpty() && setting.getSwing() == null) {
- setting.setSwing(getCurrentOrDefaultSwing(zoneStateProvider, swings));
+ List<Power> swings = targetCapabilities.getSwings();
+ if (swings != null && !swings.isEmpty() && newSetting.getSwing() == null) {
+ newSetting.setSwing(getCurrentOrDefaultSwing(zoneStateProvider, swings));
}
- // Tado confusingly calls the List / getter method 'fanLevel' / 'getFanLevel()' without 's'
- List<ACFanLevel> fanLevels = capabilities.getFanLevel();
- if (fanLevels != null && !fanLevels.isEmpty() && setting.getFanLevel() == null) {
- setting.setFanLevel(getCurrentOrDefaultFanLevel(zoneStateProvider, fanLevels));
+ List<ACFanLevel> fanLevels = targetCapabilities.getFanLevel();
+ if (fanLevels != null && !fanLevels.isEmpty() && newSetting.getFanLevel() == null) {
+ newSetting.setFanLevel(getCurrentOrDefaultFanLevel(zoneStateProvider, fanLevels));
}
- // Tado confusingly calls the List / getter method 'horizontalSwing' / 'getHorizontalSwing()' without 's'
- List<ACHorizontalSwing> horizontalSwings = capabilities.getHorizontalSwing();
- if (horizontalSwings != null && !horizontalSwings.isEmpty() && setting.getHorizontalSwing() == null) {
- setting.setHorizontalSwing(getCurrentOrDefaultHorizontalSwing(zoneStateProvider, horizontalSwings));
+ List<ACHorizontalSwing> horizontalSwings = targetCapabilities.getHorizontalSwing();
+ if (horizontalSwings != null && !horizontalSwings.isEmpty() && newSetting.getHorizontalSwing() == null) {
+ newSetting.setHorizontalSwing(getCurrentOrDefaultHorizontalSwing(zoneStateProvider, horizontalSwings));
}
- // Tado confusingly calls the List / getter method 'verticalSwing' / 'getVerticalSwing()' without 's'
- List<ACVerticalSwing> verticalSwings = capabilities.getVerticalSwing();
- if (verticalSwings != null && !verticalSwings.isEmpty() && setting.getVerticalSwing() == null) {
- setting.setVerticalSwing(getCurrentOrDefaultVerticalSwing(zoneStateProvider, verticalSwings));
+ List<ACVerticalSwing> verticalSwings = targetCapabilities.getVerticalSwing();
+ if (verticalSwings != null && !verticalSwings.isEmpty() && newSetting.getVerticalSwing() == null) {
+ newSetting.setVerticalSwing(getCurrentOrDefaultVerticalSwing(zoneStateProvider, verticalSwings));
}
- // Tado confusingly calls the List / getter method 'light' / 'getLight()' without 's'
- List<Power> lights = capabilities.getLight();
- if (lights != null && !lights.isEmpty() && setting.getLight() == null) {
- setting.setLight(getCurrentOrDefaultLight(zoneStateProvider, lights));
+ List<Power> lights = targetCapabilities.getLight();
+ if (lights != null && !lights.isEmpty() && newSetting.getLight() == null) {
+ newSetting.setLight(getCurrentOrDefaultLight(zoneStateProvider, lights));
}
}
private AcMode getCurrentOrDefaultAcMode(ZoneStateProvider zoneStateProvider) throws IOException, ApiException {
- CoolingZoneSetting zoneSetting = (CoolingZoneSetting) zoneStateProvider.getZoneState().getSetting();
-
- return zoneSetting.getMode() != null ? zoneSetting.getMode() : DEFAULT_MODE;
+ AcMode acMode = ((CoolingZoneSetting) zoneStateProvider.getZoneState().getSetting()).getMode();
+ return acMode != null ? acMode : DEFAULT_MODE;
}
private TemperatureObject getCurrentOrDefaultTemperature(ZoneStateProvider zoneStateProvider,
private AcFanSpeed getCurrentOrDefaultFanSpeed(ZoneStateProvider zoneStateProvider, List<AcFanSpeed> fanSpeeds)
throws IOException, ApiException {
- CoolingZoneSetting zoneSetting = (CoolingZoneSetting) zoneStateProvider.getZoneState().getSetting();
-
- if (zoneSetting.getFanSpeed() != null && fanSpeeds.contains(zoneSetting.getFanSpeed())) {
- return zoneSetting.getFanSpeed();
- }
-
- return fanSpeeds.get(0);
+ AcFanSpeed fanSpeed = ((CoolingZoneSetting) zoneStateProvider.getZoneState().getSetting()).getFanSpeed();
+ return (fanSpeed != null) && fanSpeeds.contains(fanSpeed) ? fanSpeed : fanSpeeds.get(0);
}
private Power getCurrentOrDefaultSwing(ZoneStateProvider zoneStateProvider, List<Power> swings)
throws IOException, ApiException {
- CoolingZoneSetting zoneSetting = (CoolingZoneSetting) zoneStateProvider.getZoneState().getSetting();
-
- if (zoneSetting.getSwing() != null && swings.contains(zoneSetting.getSwing())) {
- return zoneSetting.getSwing();
- }
-
- return swings.get(0);
- }
-
- private Power getCurrentOrDefaultLight(ZoneStateProvider zoneStateProvider, List<Power> lights)
- throws IOException, ApiException {
- CoolingZoneSetting zoneSetting = (CoolingZoneSetting) zoneStateProvider.getZoneState().getSetting();
-
- if (zoneSetting.getLight() != null && lights.contains(zoneSetting.getLight())) {
- return zoneSetting.getLight();
- }
-
- return lights.get(0);
+ Power swing = ((CoolingZoneSetting) zoneStateProvider.getZoneState().getSetting()).getSwing();
+ return (swing != null) && swings.contains(swing) ? swing : swings.get(0);
}
private ACFanLevel getCurrentOrDefaultFanLevel(ZoneStateProvider zoneStateProvider, List<ACFanLevel> fanLevels)
throws IOException, ApiException {
- CoolingZoneSetting zoneSetting = (CoolingZoneSetting) zoneStateProvider.getZoneState().getSetting();
-
- if (zoneSetting.getFanLevel() != null && fanLevels.contains(zoneSetting.getFanLevel())) {
- return zoneSetting.getFanLevel();
- }
+ ACFanLevel fanLevel = ((CoolingZoneSetting) zoneStateProvider.getZoneState().getSetting()).getFanLevel();
+ return (fanLevel != null) && fanLevels.contains(fanLevel) ? fanLevel : fanLevels.get(0);
+ }
- return fanLevels.get(0);
+ private ACVerticalSwing getCurrentOrDefaultVerticalSwing(ZoneStateProvider zoneStateProvider,
+ List<ACVerticalSwing> vertSwings) throws IOException, ApiException {
+ ACVerticalSwing vertSwing = ((CoolingZoneSetting) zoneStateProvider.getZoneState().getSetting())
+ .getVerticalSwing();
+ return (vertSwing != null) && vertSwings.contains(vertSwing) ? vertSwing : vertSwings.get(0);
}
private ACHorizontalSwing getCurrentOrDefaultHorizontalSwing(ZoneStateProvider zoneStateProvider,
- List<ACHorizontalSwing> horizontalSwings) throws IOException, ApiException {
- CoolingZoneSetting zoneSetting = (CoolingZoneSetting) zoneStateProvider.getZoneState().getSetting();
-
- if (zoneSetting.getHorizontalSwing() != null && horizontalSwings.contains(zoneSetting.getHorizontalSwing())) {
- return zoneSetting.getHorizontalSwing();
- }
-
- return horizontalSwings.get(0);
+ List<ACHorizontalSwing> horzSwings) throws IOException, ApiException {
+ ACHorizontalSwing horzSwing = ((CoolingZoneSetting) zoneStateProvider.getZoneState().getSetting())
+ .getHorizontalSwing();
+ return (horzSwing != null) && horzSwings.contains(horzSwing) ? horzSwing : horzSwings.get(0);
}
- private ACVerticalSwing getCurrentOrDefaultVerticalSwing(ZoneStateProvider zoneStateProvider,
- List<ACVerticalSwing> verticalSwings) throws IOException, ApiException {
- CoolingZoneSetting zoneSetting = (CoolingZoneSetting) zoneStateProvider.getZoneState().getSetting();
-
- if (zoneSetting.getVerticalSwing() != null && verticalSwings.contains(zoneSetting.getVerticalSwing())) {
- return zoneSetting.getVerticalSwing();
- }
-
- return verticalSwings.get(0);
+ private Power getCurrentOrDefaultLight(ZoneStateProvider zoneStateProvider, List<Power> lights)
+ throws IOException, ApiException {
+ Power light = ((CoolingZoneSetting) zoneStateProvider.getZoneState().getSetting()).getLight();
+ return (light != null) && lights.contains(light) ? light : lights.get(0);
}
private CoolingZoneSetting coolingSetting(boolean powerOn) {
import java.io.IOException;
+import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.tado.internal.TadoBindingConstants.FanLevel;
import org.openhab.binding.tado.internal.TadoBindingConstants.FanSpeed;
import org.openhab.binding.tado.internal.TadoBindingConstants.HorizontalSwing;
*
* @author Dennis Frommknecht - Initial contribution
*/
+@NonNullByDefault
public class HeatingZoneSettingsBuilder extends ZoneSettingsBuilder {
private static final float DEFAULT_TEMPERATURE_C = 22.0f;
private static final float DEFAULT_TEMPERATURE_F = 72.0f;
HeatingZoneSetting setting = heatingSetting(true);
+ Float temperature = this.temperature;
if (temperature != null) {
setting.setTemperature(temperature(temperature, temperatureUnit));
}
import java.io.IOException;
+import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.tado.internal.TadoBindingConstants.FanLevel;
import org.openhab.binding.tado.internal.TadoBindingConstants.FanSpeed;
import org.openhab.binding.tado.internal.TadoBindingConstants.HorizontalSwing;
*
* @author Dennis Frommknecht - Initial contribution
*/
+@NonNullByDefault
public class HotWaterZoneSettingsBuilder extends ZoneSettingsBuilder {
private static final float DEFAULT_TEMPERATURE_C = 50.0f;
private static final float DEFAULT_TEMPERATURE_F = 122.0f;
HotWaterZoneSetting setting = hotWaterSetting(true);
+ Float temperature = this.temperature;
if (temperature != null) {
setting.setTemperature(temperature(temperature, temperatureUnit));
}
import java.io.IOException;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.tado.internal.api.ApiException;
import org.openhab.binding.tado.internal.api.model.OverlayTerminationCondition;
import org.openhab.binding.tado.internal.api.model.OverlayTerminationConditionType;
*
* @author Dennis Frommknecht - Initial contribution
*/
+@NonNullByDefault
public class TerminationConditionBuilder {
- private TadoZoneHandler zoneHandler;
- private OverlayTerminationConditionType terminationType = null;
- private Integer timerDurationInSeconds = null;
+ private final TadoZoneHandler zoneHandler;
+
+ private @Nullable OverlayTerminationConditionType terminationType;
+ private int timerDurationInSeconds = 0;
protected TerminationConditionBuilder(TadoZoneHandler zoneHandler) {
this.zoneHandler = zoneHandler;
public TerminationConditionBuilder withTerminationType(OverlayTerminationConditionType terminationType) {
this.terminationType = terminationType;
if (terminationType != OverlayTerminationConditionType.TIMER) {
- timerDurationInSeconds = null;
+ timerDurationInSeconds = 0;
}
-
return this;
}
- public TerminationConditionBuilder withTimerDurationInSeconds(Integer timerDurationInSeconds) {
+ public TerminationConditionBuilder withTimerDurationInSeconds(int timerDurationInSeconds) {
this.terminationType = OverlayTerminationConditionType.TIMER;
this.timerDurationInSeconds = timerDurationInSeconds;
return this;
}
public OverlayTerminationCondition build(ZoneStateProvider zoneStateProvider) throws IOException, ApiException {
- OverlayTerminationCondition terminationCondition = null;
+ OverlayTerminationCondition terminationCondition;
+ OverlayTerminationConditionType terminationType = this.terminationType;
if (terminationType != null) {
- if (terminationType != OverlayTerminationConditionType.TIMER || timerDurationInSeconds != null) {
+ if (terminationType != OverlayTerminationConditionType.TIMER || timerDurationInSeconds > 0) {
terminationCondition = getTerminationCondition(terminationType, timerDurationInSeconds);
} else {
terminationCondition = getCurrentOrDefaultTimerTermination(zoneStateProvider);
terminationCondition = getDefaultTerminationCondition();
}
}
+
return terminationCondition;
}
private OverlayTerminationCondition getDefaultTerminationCondition() throws IOException, ApiException {
OverlayTerminationCondition defaultTerminationCondition = zoneHandler.getDefaultTerminationCondition();
- return defaultTerminationCondition != null ? defaultTerminationCondition : manualTermination();
+ return defaultTerminationCondition;
}
private TimerTerminationCondition getCurrentOrDefaultTimerTermination(ZoneStateProvider zoneStateProvider)
throws IOException, ApiException {
// Timer without duration
- int duration = zoneHandler.getFallbackTimerDuration() * 60;
+ Integer duration = zoneHandler.getFallbackTimerDuration() * 60;
ZoneState zoneState = zoneStateProvider.getZoneState();
import java.io.IOException;
-import org.openhab.binding.tado.internal.TadoBindingConstants;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.tado.internal.TadoBindingConstants.FanLevel;
import org.openhab.binding.tado.internal.TadoBindingConstants.FanSpeed;
import org.openhab.binding.tado.internal.TadoBindingConstants.HorizontalSwing;
import org.openhab.binding.tado.internal.TadoBindingConstants.HvacMode;
import org.openhab.binding.tado.internal.TadoBindingConstants.TemperatureUnit;
import org.openhab.binding.tado.internal.TadoBindingConstants.VerticalSwing;
+import org.openhab.binding.tado.internal.TadoBindingConstants.ZoneType;
import org.openhab.binding.tado.internal.api.ApiException;
import org.openhab.binding.tado.internal.api.model.GenericZoneCapabilities;
import org.openhab.binding.tado.internal.api.model.GenericZoneSetting;
*
* @author Dennis Frommknecht - Initial contribution
*/
+@NonNullByDefault
public abstract class ZoneSettingsBuilder {
+
public static ZoneSettingsBuilder of(TadoZoneHandler zoneHandler) {
- TadoBindingConstants.ZoneType zoneType = zoneHandler.getZoneType();
- if (zoneType == null) {
- throw new IllegalArgumentException("Zone type is null");
- }
+ ZoneType zoneType = zoneHandler.getZoneType();
+
switch (zoneType) {
case HEATING:
return new HeatingZoneSettingsBuilder();
case HOT_WATER:
return new HotWaterZoneSettingsBuilder();
default:
- throw new IllegalArgumentException("Zone type " + zoneHandler.getZoneType() + " unknown");
+ throw new IllegalArgumentException("Zone type " + zoneType + " unknown");
}
}
- protected HvacMode mode = null;
- protected Float temperature = null;
protected TemperatureUnit temperatureUnit = TemperatureUnit.CELSIUS;
- protected Boolean swing = null;
- protected Boolean light = null;
- protected FanSpeed fanSpeed = null;
- protected FanLevel fanLevel = null;
- protected HorizontalSwing horizontalSwing = null;
- protected VerticalSwing verticalSwing = null;
+ protected @Nullable Float temperature;
+ protected @Nullable HvacMode mode;
+ protected @Nullable Boolean swing;
+ protected @Nullable Boolean light;
+ protected @Nullable FanSpeed fanSpeed;
+ protected @Nullable FanLevel fanLevel;
+ protected @Nullable HorizontalSwing horizontalSwing;
+ protected @Nullable VerticalSwing verticalSwing;
public ZoneSettingsBuilder withMode(HvacMode mode) {
this.mode = mode;
throws IOException, ApiException;
protected TemperatureObject truncateTemperature(TemperatureObject temperature) {
- if (temperature == null) {
- return null;
- }
-
TemperatureObject temperatureObject = new TemperatureObject();
if (temperatureUnit == TemperatureUnit.FAHRENHEIT) {
temperatureObject.setFahrenheit(temperature.getFahrenheit());
import java.io.IOException;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.tado.internal.api.ApiException;
import org.openhab.binding.tado.internal.api.model.ZoneState;
import org.openhab.binding.tado.internal.handler.TadoZoneHandler;
*
* @author Dennis Frommknecht - Initial contribution
*/
+@NonNullByDefault
public class ZoneStateProvider {
- private TadoZoneHandler zoneHandler;
- private ZoneState zoneState;
+ private final TadoZoneHandler zoneHandler;
+ private @Nullable ZoneState zoneState;
public ZoneStateProvider(TadoZoneHandler zoneHandler) {
this.zoneHandler = zoneHandler;
}
- ZoneState getZoneState() throws IOException, ApiException {
- if (this.zoneState == null) {
- ZoneState retrievedZoneState = zoneHandler.getZoneState();
- // empty zone state behaves like a NULL object
- this.zoneState = retrievedZoneState != null ? retrievedZoneState : new ZoneState();
+ public synchronized ZoneState getZoneState() throws IOException, ApiException {
+ ZoneState zoneState = this.zoneState;
+ if (zoneState == null) {
+ zoneState = this.zoneState = zoneHandler.getZoneState();
}
-
- return this.zoneState;
+ return zoneState;
}
}
*/
package org.openhab.binding.tado.internal.config;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
/**
* Holder-object for home configuration
*
* @author Dennis Frommknecht - Initial contribution
*/
+@NonNullByDefault
public class TadoHomeConfig {
- public String username;
- public String password;
+ public String username = "";
+ public String password = "";
}
*/
package org.openhab.binding.tado.internal.config;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
/**
* Holder-object for mobile device configuration
*
* @author Dennis Frommknecht - Initial contribution
*/
+@NonNullByDefault
public class TadoMobileDeviceConfig {
public int id;
public int refreshInterval;
*/
package org.openhab.binding.tado.internal.config;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+
/**
* Holder-object for zone configuration
*
* @author Dennis Frommknecht - Initial contribution
*/
+@NonNullByDefault
public class TadoZoneConfig {
public long id;
public int refreshInterval;
import java.util.stream.Collectors;
import java.util.stream.Stream;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.tado.internal.TadoBindingConstants;
import org.openhab.binding.tado.internal.api.ApiException;
import org.openhab.binding.tado.internal.api.model.MobileDevice;
*
* @author Dennis Frommknecht - Initial contribution
*/
+@NonNullByDefault
public class TadoDiscoveryService extends AbstractDiscoveryService {
private static final int TIMEOUT = 5;
private static final long REFRESH = 600;
private final Logger logger = LoggerFactory.getLogger(TadoDiscoveryService.class);
- private ScheduledFuture<?> discoveryFuture;
+ private @Nullable ScheduledFuture<?> discoveryFuture;
public static final Set<ThingTypeUID> DISCOVERABLE_THING_TYPES_UIDS = Collections
.unmodifiableSet(Stream.of(THING_TYPE_ZONE, THING_TYPE_MOBILE_DEVICE).collect(Collectors.toSet()));
@Override
protected void startBackgroundDiscovery() {
logger.debug("Start Tado background discovery");
+ ScheduledFuture<?> discoveryFuture = this.discoveryFuture;
if (discoveryFuture == null || discoveryFuture.isCancelled()) {
logger.debug("Start Scan");
- discoveryFuture = scheduler.scheduleWithFixedDelay(this::startScan, 30, REFRESH, TimeUnit.SECONDS);
+ this.discoveryFuture = scheduler.scheduleWithFixedDelay(this::startScan, 30, REFRESH, TimeUnit.SECONDS);
}
}
@Override
protected void stopBackgroundDiscovery() {
logger.debug("Stop Tado background discovery");
+ ScheduledFuture<?> discoveryFuture = this.discoveryFuture;
if (discoveryFuture != null && !discoveryFuture.isCancelled()) {
discoveryFuture.cancel(true);
- discoveryFuture = null;
}
}
private void discoverZones() {
Long homeId = homeHandler.getHomeId();
+
+ if (homeId == null) {
+ logger.debug("Could not discover tado zones: Missing home id");
+ return;
+ }
+
try {
List<Zone> zoneList = homeHandler.getApi().listZones(homeId);
private void discoverMobileDevices() {
Long homeId = homeHandler.getHomeId();
+
+ if (homeId == null) {
+ logger.debug("Could not discover mobile devices: Missing home id");
+ return;
+ }
+
try {
List<MobileDevice> mobileDeviceList = homeHandler.getApi().listMobileDevices(homeId);
}
}
} catch (IOException | ApiException e) {
- logger.debug("Could not discover tado zones: {}", e.getMessage(), e);
+ logger.debug("Could not discover mobile devices: {}", e.getMessage(), e);
}
}
*/
package org.openhab.binding.tado.internal.handler;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.tado.internal.api.client.HomeApi;
import org.openhab.core.thing.Bridge;
import org.openhab.core.thing.Thing;
import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.binding.BaseThingHandler;
+import org.openhab.core.thing.binding.BridgeHandler;
/**
* Common base class for home-based thing-handler.
*
* @author Dennis Frommknecht - Initial contribution
*/
+@NonNullByDefault
public abstract class BaseHomeThingHandler extends BaseThingHandler {
public BaseHomeThingHandler(Thing thing) {
super(thing);
}
- public Long getHomeId() {
+ public @Nullable Long getHomeId() {
TadoHomeHandler handler = getHomeHandler();
- return handler != null ? handler.getHomeId() : Long.valueOf(0);
+ return handler.getHomeId();
}
protected TadoHomeHandler getHomeHandler() {
Bridge bridge = getBridge();
- return bridge != null ? (TadoHomeHandler) bridge.getHandler() : null;
+ if (bridge == null) {
+ throw new IllegalStateException("Bridge not initialized");
+ }
+ BridgeHandler handler = bridge.getHandler();
+ if (!(handler instanceof TadoHomeHandler)) {
+ throw new IllegalStateException("Handler not initialized");
+ }
+ return (TadoHomeHandler) handler;
}
protected HomeApi getApi() {
TadoHomeHandler handler = getHomeHandler();
- return handler != null ? handler.getApi() : null;
+ return handler.getApi();
}
protected void onSuccessfulOperation() {
import java.util.Map;
import java.util.Objects;
+import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.tado.internal.api.ApiException;
import org.openhab.binding.tado.internal.api.model.ControlDevice;
import org.openhab.core.library.types.OnOffType;
* devices.
*
* @author Andrew Fiddian-Green - Initial contribution
- *
+ *
*/
+@NonNullByDefault
public class TadoBatteryChecker {
private final Logger logger = LoggerFactory.getLogger(TadoBatteryChecker.class);
- private Map<Long, State> zoneList = new HashMap<>();
+ private final Map<Long, State> zoneList = new HashMap<>();
+ private final TadoHomeHandler homeHandler;
+
private Date refreshTime = new Date();
- private TadoHomeHandler homeHandler;
public TadoBatteryChecker(TadoHomeHandler homeHandler) {
this.homeHandler = homeHandler;
private synchronized void refreshZoneList() {
Date now = new Date();
- if (homeHandler != null && (now.after(refreshTime) || zoneList.isEmpty())) {
+ if (now.after(refreshTime) || zoneList.isEmpty()) {
// be frugal, we only need to refresh the battery state hourly
Calendar calendar = Calendar.getInstance();
calendar.setTime(now);
import java.util.Map;
import java.util.Set;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.tado.internal.discovery.TadoDiscoveryService;
import org.openhab.core.config.discovery.DiscoveryService;
import org.openhab.core.thing.Bridge;
*
* @author Dennis Frommknecht - Initial contribution
*/
+@NonNullByDefault
@Component(configurationPid = "binding.tado", service = ThingHandlerFactory.class)
public class TadoHandlerFactory extends BaseThingHandlerFactory {
}
@Override
- protected ThingHandler createHandler(Thing thing) {
+ protected @Nullable ThingHandler createHandler(Thing thing) {
ThingTypeUID thingTypeUID = thing.getThingTypeUID();
if (thingTypeUID.equals(THING_TYPE_HOME)) {
package org.openhab.binding.tado.internal.handler;
import java.io.IOException;
+import java.util.List;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.tado.internal.TadoBindingConstants;
import org.openhab.binding.tado.internal.TadoBindingConstants.TemperatureUnit;
import org.openhab.binding.tado.internal.api.ApiException;
import org.openhab.binding.tado.internal.api.model.HomeState;
import org.openhab.binding.tado.internal.api.model.PresenceState;
import org.openhab.binding.tado.internal.api.model.User;
+import org.openhab.binding.tado.internal.api.model.UserHomes;
import org.openhab.binding.tado.internal.config.TadoHomeConfig;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.thing.Bridge;
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;
*
* @author Dennis Frommknecht - Initial contribution
*/
+@NonNullByDefault
public class TadoHomeHandler extends BaseBridgeHandler {
private Logger logger = LoggerFactory.getLogger(TadoHomeHandler.class);
private TadoHomeConfig configuration;
- private HomeApi api;
- private Long homeId;
+ private final HomeApi api;
- private TadoBatteryChecker batteryChecker;
-
- private ScheduledFuture<?> initializationFuture;
+ private @Nullable Long homeId;
+ private @Nullable TadoBatteryChecker batteryChecker;
+ private @Nullable ScheduledFuture<?> initializationFuture;
public TadoHomeHandler(Bridge bridge) {
super(bridge);
batteryChecker = new TadoBatteryChecker(this);
+ configuration = getConfigAs(TadoHomeConfig.class);
+ api = new HomeApiFactory().create(configuration.username, configuration.password);
}
public TemperatureUnit getTemperatureUnit() {
@Override
public void initialize() {
configuration = getConfigAs(TadoHomeConfig.class);
- api = new HomeApiFactory().create(configuration.username, configuration.password);
-
- if (this.initializationFuture == null || this.initializationFuture.isDone()) {
- initializationFuture = scheduler.scheduleWithFixedDelay(this::initializeBridgeStatusAndPropertiesIfOffline,
- 0, 300, TimeUnit.SECONDS);
+ ScheduledFuture<?> initializationFuture = this.initializationFuture;
+ if (initializationFuture == null || initializationFuture.isDone()) {
+ this.initializationFuture = scheduler.scheduleWithFixedDelay(
+ this::initializeBridgeStatusAndPropertiesIfOffline, 0, 300, TimeUnit.SECONDS);
}
}
return;
}
- if (user.getHomes().isEmpty()) {
+ List<UserHomes> homes = user.getHomes();
+ if (homes == null || homes.isEmpty()) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"User does not have access to any home");
return;
}
- homeId = user.getHomes().get(0).getId().longValue();
+ Integer firstHomeId = homes.get(0).getId();
+ if (firstHomeId == null) {
+ updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Missing Home Id");
+ return;
+ }
+
+ homeId = firstHomeId.longValue();
HomeInfo homeInfo = api.showHome(homeId);
TemperatureUnit temperatureUnit = org.openhab.binding.tado.internal.api.model.TemperatureUnit.FAHRENHEIT == homeInfo
@Override
public void dispose() {
super.dispose();
- if (this.initializationFuture != null || !this.initializationFuture.isDone()) {
- this.initializationFuture.cancel(true);
- this.initializationFuture = null;
+ ScheduledFuture<?> initializationFuture = this.initializationFuture;
+ if (initializationFuture != null && !initializationFuture.isCancelled()) {
+ initializationFuture.cancel(true);
}
}
return api;
}
- public Long getHomeId() {
+ public @Nullable Long getHomeId() {
return homeId;
}
public HomeState getHomeState() throws IOException, ApiException {
- HomeApi api = getApi();
- return api != null ? api.homeState(getHomeId()) : null;
+ return api.homeState(getHomeId());
}
public void updateHomeState() {
}
public State getBatteryLowAlarm(long zoneId) {
- return batteryChecker.getBatteryLowAlarm(zoneId);
+ TadoBatteryChecker batteryChecker = this.batteryChecker;
+ return batteryChecker != null ? batteryChecker.getBatteryLowAlarm(zoneId) : UnDefType.UNDEF;
}
}
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.tado.internal.TadoBindingConstants;
import org.openhab.binding.tado.internal.api.ApiException;
import org.openhab.binding.tado.internal.api.model.MobileDevice;
*
* @author Dennis Frommknecht - Initial contribution
*/
+@NonNullByDefault
public class TadoMobileDeviceHandler extends BaseHomeThingHandler {
private Logger logger = LoggerFactory.getLogger(TadoMobileDeviceHandler.class);
private TadoMobileDeviceConfig configuration;
- private ScheduledFuture<?> refreshTimer;
+ private @Nullable ScheduledFuture<?> refreshTimer;
public TadoMobileDeviceHandler(Thing thing) {
super(thing);
+ configuration = getConfigAs(TadoMobileDeviceConfig.class);
}
@Override
@Override
public void initialize() {
configuration = getConfigAs(TadoMobileDeviceConfig.class);
-
if (configuration.refreshInterval <= 0) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Refresh interval of zone "
+ configuration.id + " of home " + getHomeId() + " must be greater than zero");
}
private void scheduleZoneStateUpdate() {
+ ScheduledFuture<?> refreshTimer = this.refreshTimer;
if (refreshTimer == null || refreshTimer.isCancelled()) {
- refreshTimer = scheduler.scheduleWithFixedDelay(this::updateState, 5, configuration.refreshInterval,
+ this.refreshTimer = scheduler.scheduleWithFixedDelay(this::updateState, 5, configuration.refreshInterval,
TimeUnit.SECONDS);
}
}
private void cancelScheduledStateUpdate() {
+ ScheduledFuture<?> refreshTimer = this.refreshTimer;
if (refreshTimer != null) {
refreshTimer.cancel(false);
}
import javax.measure.quantity.Temperature;
+import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.tado.internal.TadoBindingConstants;
import org.openhab.binding.tado.internal.TadoBindingConstants.FanLevel;
import org.openhab.binding.tado.internal.TadoHvacChange;
import org.openhab.binding.tado.internal.adapter.TadoZoneStateAdapter;
import org.openhab.binding.tado.internal.api.ApiException;
+import org.openhab.binding.tado.internal.api.GsonBuilderFactory;
import org.openhab.binding.tado.internal.api.TadoApiTypeUtils;
-import org.openhab.binding.tado.internal.api.client.HomeApi;
import org.openhab.binding.tado.internal.api.model.ACFanLevel;
import org.openhab.binding.tado.internal.api.model.ACHorizontalSwing;
import org.openhab.binding.tado.internal.api.model.ACVerticalSwing;
+import org.openhab.binding.tado.internal.api.model.AcMode;
import org.openhab.binding.tado.internal.api.model.AcModeCapabilities;
-import org.openhab.binding.tado.internal.api.model.AirConditioningCapabilities;
import org.openhab.binding.tado.internal.api.model.CoolingZoneSetting;
import org.openhab.binding.tado.internal.api.model.GenericZoneCapabilities;
import org.openhab.binding.tado.internal.api.model.GenericZoneSetting;
import org.openhab.core.thing.ThingStatusInfo;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
-import org.openhab.core.types.State;
import org.openhab.core.types.StateOption;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.gson.Gson;
+
/**
* The {@link TadoZoneHandler} is responsible for handling commands of zones and update their state.
*
* @author Andrew Fiddian-Green - Added Low Battery Alarm, A/C Power and Open Window channels
*
*/
+@NonNullByDefault
public class TadoZoneHandler extends BaseHomeThingHandler {
private Logger logger = LoggerFactory.getLogger(TadoZoneHandler.class);
private final TadoStateDescriptionProvider stateDescriptionProvider;
-
private TadoZoneConfig configuration;
- private ScheduledFuture<?> refreshTimer;
- private ScheduledFuture<?> scheduledHvacChange;
- private GenericZoneCapabilities capabilities;
- TadoHvacChange pendingHvacChange;
+
+ private @Nullable ScheduledFuture<?> refreshTimer;
+ private @Nullable ScheduledFuture<?> scheduledHvacChange;
+ private @Nullable GenericZoneCapabilities capabilities;
+ private @Nullable TadoHvacChange pendingHvacChange;
+
+ private boolean disposing = false;
+ private @Nullable Gson gson;
public TadoZoneHandler(Thing thing, TadoStateDescriptionProvider stateDescriptionProvider) {
super(thing);
this.stateDescriptionProvider = stateDescriptionProvider;
+ configuration = getConfigAs(TadoZoneConfig.class);
}
public long getZoneId() {
- return this.configuration.id;
+ return configuration.id;
}
public int getFallbackTimerDuration() {
- return this.configuration.fallbackTimerDuration;
+ return configuration.fallbackTimerDuration;
}
- public @Nullable ZoneType getZoneType() {
- String zoneTypeStr = this.thing.getProperties().get(TadoBindingConstants.PROPERTY_ZONE_TYPE);
- return zoneTypeStr != null ? ZoneType.valueOf(zoneTypeStr) : null;
+ public ZoneType getZoneType() {
+ String zoneTypeStr = thing.getProperties().get(TadoBindingConstants.PROPERTY_ZONE_TYPE);
+ if (zoneTypeStr == null) {
+ throw new IllegalStateException("Zone type not initialized");
+ }
+ return ZoneType.valueOf(zoneTypeStr);
}
public OverlayTerminationCondition getDefaultTerminationCondition() throws IOException, ApiException {
OverlayTemplate overlayTemplate = getApi().showZoneDefaultOverlay(getHomeId(), getZoneId());
+ logApiTransaction(overlayTemplate, false);
return terminationConditionTemplateToTerminationCondition(overlayTemplate.getTerminationCondition());
}
public ZoneState getZoneState() throws IOException, ApiException {
- HomeApi api = getApi();
- return api != null ? api.showZoneState(getHomeId(), getZoneId()) : null;
+ ZoneState zoneState = getApi().showZoneState(getHomeId(), getZoneId());
+ logApiTransaction(zoneState, false);
+ return zoneState;
}
public GenericZoneCapabilities getZoneCapabilities() {
- return this.capabilities;
+ GenericZoneCapabilities capabilities = this.capabilities;
+ if (capabilities == null) {
+ throw new IllegalStateException("Zone capabilities not initialized");
+ }
+ return capabilities;
}
public TemperatureUnit getTemperatureUnit() {
}
public Overlay setOverlay(Overlay overlay) throws IOException, ApiException {
- logger.debug("Setting overlay of home {} and zone {} with overlay: {}", getHomeId(), getZoneId(),
- overlay.toString());
- return getApi().updateZoneOverlay(getHomeId(), getZoneId(), overlay);
+ try {
+ logApiTransaction(overlay, true);
+ Overlay newOverlay = getApi().updateZoneOverlay(getHomeId(), getZoneId(), overlay);
+ logApiTransaction(newOverlay, false);
+ return newOverlay;
+ } catch (ApiException e) {
+ if (!logger.isTraceEnabled()) {
+ logger.warn("ApiException sending JSON content:\n{}", convertToJsonString(overlay));
+ }
+ throw e;
+ }
}
public void removeOverlay() throws IOException, ApiException {
return;
}
- switch (id) {
- case TadoBindingConstants.CHANNEL_ZONE_HVAC_MODE:
- pendingHvacChange.withHvacMode(((StringType) command).toFullString());
- scheduleHvacChange();
- break;
- case TadoBindingConstants.CHANNEL_ZONE_TARGET_TEMPERATURE:
- QuantityType<Temperature> state = (QuantityType<Temperature>) command;
- QuantityType<Temperature> stateInTargetUnit = getTemperatureUnit() == TemperatureUnit.FAHRENHEIT
- ? state.toUnit(ImperialUnits.FAHRENHEIT)
- : state.toUnit(SIUnits.CELSIUS);
-
- if (stateInTargetUnit != null) {
- pendingHvacChange.withTemperature(stateInTargetUnit.floatValue());
- scheduleHvacChange();
- }
+ synchronized (this) {
+ TadoHvacChange pendingHvacChange = this.pendingHvacChange;
+ if (pendingHvacChange == null) {
+ throw new IllegalStateException("Zone pendingHvacChange not initialized");
+ }
- break;
- case TadoBindingConstants.CHANNEL_ZONE_SWING:
- pendingHvacChange.withSwing(((OnOffType) command) == OnOffType.ON);
- scheduleHvacChange();
- break;
- case TadoBindingConstants.CHANNEL_ZONE_LIGHT:
- pendingHvacChange.withLight(((OnOffType) command) == OnOffType.ON);
- scheduleHvacChange();
- break;
- case TadoBindingConstants.CHANNEL_ZONE_FAN_SPEED:
- pendingHvacChange.withFanSpeed(((StringType) command).toFullString());
- scheduleHvacChange();
- break;
- case TadoBindingConstants.CHANNEL_ZONE_FAN_LEVEL:
- String fanLevelString = ((StringType) command).toFullString();
- pendingHvacChange.withFanLevel(FanLevel.valueOf(fanLevelString.toUpperCase()));
- break;
- case TadoBindingConstants.CHANNEL_ZONE_HORIZONTAL_SWING:
- String horizontalSwingString = ((StringType) command).toFullString();
- pendingHvacChange.withHorizontalSwing(HorizontalSwing.valueOf(horizontalSwingString.toUpperCase()));
- scheduleHvacChange();
- break;
- case TadoBindingConstants.CHANNEL_ZONE_VERTICAL_SWING:
- String verticalSwingString = ((StringType) command).toFullString();
- pendingHvacChange.withVerticalSwing(VerticalSwing.valueOf(verticalSwingString.toUpperCase()));
- scheduleHvacChange();
- break;
- case TadoBindingConstants.CHANNEL_ZONE_OPERATION_MODE:
- String operationMode = ((StringType) command).toFullString();
- pendingHvacChange.withOperationMode(OperationMode.valueOf(operationMode));
- scheduleHvacChange();
- break;
- case TadoBindingConstants.CHANNEL_ZONE_TIMER_DURATION:
- pendingHvacChange.activeFor(((DecimalType) command).intValue());
- scheduleHvacChange();
- break;
+ switch (id) {
+ case TadoBindingConstants.CHANNEL_ZONE_HVAC_MODE:
+ pendingHvacChange.withHvacMode(((StringType) command).toFullString());
+ scheduleHvacChange();
+ break;
+ case TadoBindingConstants.CHANNEL_ZONE_TARGET_TEMPERATURE:
+ if (command instanceof QuantityType<?>) {
+ @SuppressWarnings("unchecked")
+ QuantityType<Temperature> state = (QuantityType<Temperature>) command;
+ QuantityType<Temperature> stateInTargetUnit = getTemperatureUnit() == TemperatureUnit.FAHRENHEIT
+ ? state.toUnit(ImperialUnits.FAHRENHEIT)
+ : state.toUnit(SIUnits.CELSIUS);
+
+ if (stateInTargetUnit != null) {
+ pendingHvacChange.withTemperature(stateInTargetUnit.floatValue());
+ scheduleHvacChange();
+ }
+ }
+ break;
+ case TadoBindingConstants.CHANNEL_ZONE_SWING:
+ pendingHvacChange.withSwing(((OnOffType) command) == OnOffType.ON);
+ scheduleHvacChange();
+ break;
+ case TadoBindingConstants.CHANNEL_ZONE_LIGHT:
+ pendingHvacChange.withLight(((OnOffType) command) == OnOffType.ON);
+ scheduleHvacChange();
+ break;
+ case TadoBindingConstants.CHANNEL_ZONE_FAN_SPEED:
+ pendingHvacChange.withFanSpeed(((StringType) command).toFullString());
+ scheduleHvacChange();
+ break;
+ case TadoBindingConstants.CHANNEL_ZONE_FAN_LEVEL:
+ String fanLevelString = ((StringType) command).toFullString();
+ pendingHvacChange.withFanLevel(FanLevel.valueOf(fanLevelString.toUpperCase()));
+ scheduleHvacChange();
+ break;
+ case TadoBindingConstants.CHANNEL_ZONE_HORIZONTAL_SWING:
+ String horizontalSwingString = ((StringType) command).toFullString();
+ pendingHvacChange.withHorizontalSwing(HorizontalSwing.valueOf(horizontalSwingString.toUpperCase()));
+ scheduleHvacChange();
+ break;
+ case TadoBindingConstants.CHANNEL_ZONE_VERTICAL_SWING:
+ String verticalSwingString = ((StringType) command).toFullString();
+ pendingHvacChange.withVerticalSwing(VerticalSwing.valueOf(verticalSwingString.toUpperCase()));
+ scheduleHvacChange();
+ break;
+ case TadoBindingConstants.CHANNEL_ZONE_OPERATION_MODE:
+ String operationMode = ((StringType) command).toFullString();
+ pendingHvacChange.withOperationMode(OperationMode.valueOf(operationMode));
+ scheduleHvacChange();
+ break;
+ case TadoBindingConstants.CHANNEL_ZONE_TIMER_DURATION:
+ pendingHvacChange.activeForMinutes(((DecimalType) command).intValue());
+ scheduleHvacChange();
+ break;
+ }
}
}
@Override
public void initialize() {
+ disposing = false;
configuration = getConfigAs(TadoZoneConfig.class);
-
if (configuration.refreshInterval <= 0) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Refresh interval of zone "
+ getZoneId() + " of home " + getHomeId() + " must be greater than zero");
@Override
public void dispose() {
+ disposing = true;
cancelScheduledZoneStateUpdate();
}
if (bridgeStatusInfo.getStatus() == ThingStatus.ONLINE) {
try {
Zone zoneDetails = getApi().showZoneDetails(getHomeId(), getZoneId());
+ logApiTransaction(zoneDetails, false);
+
GenericZoneCapabilities capabilities = getApi().showZoneCapabilities(getHomeId(), getZoneId());
+ logApiTransaction(capabilities, false);
if (zoneDetails == null || capabilities == null) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
updateProperty(TadoBindingConstants.PROPERTY_ZONE_NAME, zoneDetails.getName());
updateProperty(TadoBindingConstants.PROPERTY_ZONE_TYPE, zoneDetails.getType().name());
this.capabilities = capabilities;
- logger.debug("Got capabilities: {}", capabilities.toString());
} catch (IOException | ApiException e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
"Could not connect to server due to " + e.getMessage());
}
private void updateZoneState(boolean forceUpdate) {
- TadoHomeHandler home = getHomeHandler();
- if (home != null) {
- home.updateHomeState();
+ if ((thing.getStatus() != ThingStatus.ONLINE) || disposing) {
+ return;
}
+ getHomeHandler().updateHomeState();
+
// No update during HVAC change debounce
+ ScheduledFuture<?> scheduledHvacChange = this.scheduledHvacChange;
if (!forceUpdate && scheduledHvacChange != null && !scheduledHvacChange.isDone()) {
return;
}
try {
ZoneState zoneState = getZoneState();
- if (zoneState == null) {
- return;
- }
-
logger.debug("Updating state of home {} and zone {}", getHomeId(), getZoneId());
TadoZoneStateAdapter state = new TadoZoneStateAdapter(zoneState, getTemperatureUnit());
- updateStateIfNotNull(TadoBindingConstants.CHANNEL_ZONE_CURRENT_TEMPERATURE, state.getInsideTemperature());
- updateStateIfNotNull(TadoBindingConstants.CHANNEL_ZONE_HUMIDITY, state.getHumidity());
+ updateState(TadoBindingConstants.CHANNEL_ZONE_CURRENT_TEMPERATURE, state.getInsideTemperature());
+ updateState(TadoBindingConstants.CHANNEL_ZONE_HUMIDITY, state.getHumidity());
- updateStateIfNotNull(TadoBindingConstants.CHANNEL_ZONE_HEATING_POWER, state.getHeatingPower());
- updateStateIfNotNull(TadoBindingConstants.CHANNEL_ZONE_AC_POWER, state.getAcPower());
+ updateState(TadoBindingConstants.CHANNEL_ZONE_HEATING_POWER, state.getHeatingPower());
+ updateState(TadoBindingConstants.CHANNEL_ZONE_AC_POWER, state.getAcPower());
updateState(TadoBindingConstants.CHANNEL_ZONE_OPERATION_MODE, state.getOperationMode());
"Could not connect to server due to " + e.getMessage());
}
- if (home != null) {
- updateState(TadoBindingConstants.CHANNEL_ZONE_BATTERY_LOW_ALARM, home.getBatteryLowAlarm(getZoneId()));
- }
+ updateState(TadoBindingConstants.CHANNEL_ZONE_BATTERY_LOW_ALARM,
+ getHomeHandler().getBatteryLowAlarm(getZoneId()));
}
/**
return;
}
- AcModeCapabilities acCapabilities = TadoApiTypeUtils.getModeCapabilities(
- (AirConditioningCapabilities) capabilities, ((CoolingZoneSetting) setting).getMode());
+ AcMode acMode = ((CoolingZoneSetting) setting).getMode();
+ AcModeCapabilities acModeCapabilities = acMode == null ? new AcModeCapabilities()
+ : TadoApiTypeUtils.getModeCapabilities(acMode, capabilities);
- if (acCapabilities != null) {
- Channel channel;
-
- // update the options list of supported fan levels
- channel = thing.getChannel(TadoBindingConstants.CHANNEL_ZONE_FAN_LEVEL);
- List<ACFanLevel> fanLevels = acCapabilities.getFanLevel();
- if (channel != null && fanLevels != null) {
+ // update the options list of supported fan levels
+ Channel channel = thing.getChannel(TadoBindingConstants.CHANNEL_ZONE_FAN_LEVEL);
+ if (channel != null) {
+ List<ACFanLevel> fanLevels = acModeCapabilities.getFanLevel();
+ if (fanLevels != null) {
stateDescriptionProvider.setStateOptions(channel.getUID(),
fanLevels.stream().map(u -> new StateOption(u.name(), u.name())).collect(Collectors.toList()));
}
+ }
- // update the options list of supported horizontal swing settings
- channel = thing.getChannel(TadoBindingConstants.CHANNEL_ZONE_HORIZONTAL_SWING);
- List<ACHorizontalSwing> horizontalSwings = acCapabilities.getHorizontalSwing();
- if (channel != null && horizontalSwings != null) {
+ // update the options list of supported horizontal swing settings
+ channel = thing.getChannel(TadoBindingConstants.CHANNEL_ZONE_HORIZONTAL_SWING);
+ if (channel != null) {
+ List<ACHorizontalSwing> horizontalSwings = acModeCapabilities.getHorizontalSwing();
+ if (horizontalSwings != null) {
stateDescriptionProvider.setStateOptions(channel.getUID(), horizontalSwings.stream()
.map(u -> new StateOption(u.name(), u.name())).collect(Collectors.toList()));
}
+ }
- // update the options list of supported vertical swing settings
- channel = thing.getChannel(TadoBindingConstants.CHANNEL_ZONE_VERTICAL_SWING);
- List<ACVerticalSwing> verticalSwings = acCapabilities.getVerticalSwing();
- if (channel != null && verticalSwings != null) {
+ // update the options list of supported vertical swing settings
+ channel = thing.getChannel(TadoBindingConstants.CHANNEL_ZONE_VERTICAL_SWING);
+ if (channel != null) {
+ List<ACVerticalSwing> verticalSwings = acModeCapabilities.getVerticalSwing();
+ if (verticalSwings != null) {
stateDescriptionProvider.setStateOptions(channel.getUID(), verticalSwings.stream()
.map(u -> new StateOption(u.name(), u.name())).collect(Collectors.toList()));
}
}
private void scheduleZoneStateUpdate() {
+ ScheduledFuture<?> refreshTimer = this.refreshTimer;
if (refreshTimer == null || refreshTimer.isCancelled()) {
- refreshTimer = scheduler.scheduleWithFixedDelay(new Runnable() {
+ this.refreshTimer = scheduler.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
updateZoneState(false);
}
private void cancelScheduledZoneStateUpdate() {
+ ScheduledFuture<?> refreshTimer = this.refreshTimer;
if (refreshTimer != null) {
refreshTimer.cancel(false);
}
}
private void scheduleHvacChange() {
+ ScheduledFuture<?> scheduledHvacChange = this.scheduledHvacChange;
if (scheduledHvacChange != null) {
scheduledHvacChange.cancel(false);
}
-
- scheduledHvacChange = scheduler.schedule(() -> {
+ this.scheduledHvacChange = scheduler.schedule(() -> {
try {
- TadoHvacChange change = this.pendingHvacChange;
- this.pendingHvacChange = new TadoHvacChange(getThing());
- change.apply();
+ synchronized (this) {
+ TadoHvacChange pendingHvacChange = this.pendingHvacChange;
+ this.pendingHvacChange = new TadoHvacChange(getThing());
+ if (pendingHvacChange != null) {
+ pendingHvacChange.apply();
+ }
+ }
} catch (IOException e) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
} catch (ApiException e) {
}, configuration.hvacChangeDebounce, TimeUnit.SECONDS);
}
- private void updateStateIfNotNull(String channelID, State state) {
- if (state != null) {
- updateState(channelID, state);
+ /**
+ * Helper method to log an API transaction on the given object.
+ * If the logger level is 'debug', the transaction is simply logged.
+ * If the logger level is 'trace, the object's JSON serial contents are included.
+ *
+ * @param object the object to be logged.
+ * @param isCommand marks whether the transaction is a command to, or a response from, the server.
+ */
+ private void logApiTransaction(Object object, boolean isCommand) {
+ if (logger.isDebugEnabled() || logger.isTraceEnabled()) {
+ String logType = isCommand ? "command" : "response";
+ if (logger.isTraceEnabled()) {
+ logger.trace("Api {}: homeId:{}, zoneId:{}, objectId:{}, content:\n{}", logType, getHomeId(),
+ getZoneId(), object.getClass().getSimpleName(), convertToJsonString(object));
+ } else if (logger.isDebugEnabled()) {
+ logger.debug("Api {}: homeId:{}, zoneId:{}, objectId:{}", logType, getHomeId(), getZoneId(),
+ object.getClass().getSimpleName());
+ }
+ }
+ }
+
+ private synchronized String convertToJsonString(Object object) {
+ Gson gson = this.gson;
+ if (gson == null) {
+ gson = this.gson = GsonBuilderFactory.defaultGsonBuilder().setPrettyPrinting().create();
}
+ return gson.toJson(object);
}
}