diff --git a/homeassistant/components/nuheat/climate.py b/homeassistant/components/nuheat/climate.py index f675b6a90f4..f8d6bf1d8df 100644 --- a/homeassistant/components/nuheat/climate.py +++ b/homeassistant/components/nuheat/climate.py @@ -3,10 +3,16 @@ from datetime import timedelta import logging from nuheat.config import SCHEDULE_HOLD, SCHEDULE_RUN, SCHEDULE_TEMPORARY_HOLD -from nuheat.util import celsius_to_nuheat, fahrenheit_to_nuheat +from nuheat.util import ( + celsius_to_nuheat, + fahrenheit_to_nuheat, + nuheat_to_celsius, + nuheat_to_fahrenheit, +) from homeassistant.components.climate import ClimateDevice from homeassistant.components.climate.const import ( + ATTR_HVAC_MODE, CURRENT_HVAC_HEAT, CURRENT_HVAC_IDLE, HVAC_MODE_AUTO, @@ -15,9 +21,10 @@ from homeassistant.components.climate.const import ( SUPPORT_TARGET_TEMPERATURE, ) from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS, TEMP_FAHRENHEIT +from homeassistant.helpers import event as event_helper from homeassistant.util import Throttle -from .const import DOMAIN, MANUFACTURER +from .const import DOMAIN, MANUFACTURER, NUHEAT_API_STATE_SHIFT_DELAY _LOGGER = logging.getLogger(__name__) @@ -67,6 +74,8 @@ class NuHeatThermostat(ClimateDevice): """Initialize the thermostat.""" self._thermostat = thermostat self._temperature_unit = temperature_unit + self._schedule_mode = None + self._target_temperature = None self._force_update = False @property @@ -107,19 +116,15 @@ class NuHeatThermostat(ClimateDevice): def set_hvac_mode(self, hvac_mode): """Set the system mode.""" - - # This is the same as what res if hvac_mode == HVAC_MODE_AUTO: - self._thermostat.resume_schedule() + self._set_schedule_mode(SCHEDULE_RUN) elif hvac_mode == HVAC_MODE_HEAT: - self._thermostat.schedule_mode = SCHEDULE_HOLD - - self._schedule_update() + self._set_schedule_mode(SCHEDULE_HOLD) @property def hvac_mode(self): """Return current setting heat or auto.""" - if self._thermostat.schedule_mode in (SCHEDULE_TEMPORARY_HOLD, SCHEDULE_HOLD): + if self._schedule_mode in (SCHEDULE_TEMPORARY_HOLD, SCHEDULE_HOLD): return HVAC_MODE_HEAT return HVAC_MODE_AUTO @@ -148,15 +153,14 @@ class NuHeatThermostat(ClimateDevice): def target_temperature(self): """Return the currently programmed temperature.""" if self._temperature_unit == "C": - return self._thermostat.target_celsius + return nuheat_to_celsius(self._target_temperature) - return self._thermostat.target_fahrenheit + return nuheat_to_fahrenheit(self._target_temperature) @property def preset_mode(self): """Return current preset mode.""" - schedule_mode = self._thermostat.schedule_mode - return SCHEDULE_MODE_TO_PRESET_MODE_MAP.get(schedule_mode, PRESET_RUN) + return SCHEDULE_MODE_TO_PRESET_MODE_MAP.get(self._schedule_mode, PRESET_RUN) @property def preset_modes(self): @@ -168,35 +172,44 @@ class NuHeatThermostat(ClimateDevice): """Return list of possible operation modes.""" return OPERATION_LIST - def resume_program(self): - """Resume the thermostat's programmed schedule.""" - self._thermostat.resume_schedule() - self._schedule_update() - def set_preset_mode(self, preset_mode): """Update the hold mode of the thermostat.""" - - self._thermostat.schedule_mode = PRESET_MODE_TO_SCHEDULE_MODE_MAP.get( - preset_mode, SCHEDULE_RUN + self._set_schedule_mode( + PRESET_MODE_TO_SCHEDULE_MODE_MAP.get(preset_mode, SCHEDULE_RUN) ) + + def _set_schedule_mode(self, schedule_mode): + """Set a schedule mode.""" + self._schedule_mode = schedule_mode + # Changing the property here does the actual set + self._thermostat.schedule_mode = schedule_mode self._schedule_update() def set_temperature(self, **kwargs): """Set a new target temperature.""" - self._set_temperature(kwargs.get(ATTR_TEMPERATURE)) + self._set_temperature_and_mode( + kwargs.get(ATTR_TEMPERATURE), hvac_mode=kwargs.get(ATTR_HVAC_MODE) + ) - def _set_temperature(self, temperature): + def _set_temperature_and_mode(self, temperature, hvac_mode=None, preset_mode=None): + """Set temperature and hvac mode at the same time.""" if self._temperature_unit == "C": - target_temp = celsius_to_nuheat(temperature) + target_temperature = celsius_to_nuheat(temperature) else: - target_temp = fahrenheit_to_nuheat(temperature) + target_temperature = fahrenheit_to_nuheat(temperature) # If they set a temperature without changing the mode # to heat, we behave like the device does locally # and set a temp hold. - target_schedule_mode = SCHEDULE_HOLD - if self._thermostat.schedule_mode in (SCHEDULE_RUN, SCHEDULE_TEMPORARY_HOLD): - target_schedule_mode = SCHEDULE_TEMPORARY_HOLD + target_schedule_mode = SCHEDULE_TEMPORARY_HOLD + if preset_mode: + target_schedule_mode = PRESET_MODE_TO_SCHEDULE_MODE_MAP.get( + preset_mode, SCHEDULE_RUN + ) + elif self._schedule_mode == SCHEDULE_HOLD or ( + hvac_mode and hvac_mode == HVAC_MODE_HEAT + ): + target_schedule_mode = SCHEDULE_HOLD _LOGGER.debug( "Setting NuHeat thermostat temperature to %s %s and schedule mode: %s", @@ -204,15 +217,32 @@ class NuHeatThermostat(ClimateDevice): self.temperature_unit, target_schedule_mode, ) - # If we do not send schedule_mode we always get - # SCHEDULE_HOLD - self._thermostat.set_target_temperature(target_temp, target_schedule_mode) + + self._thermostat.set_target_temperature( + target_temperature, target_schedule_mode + ) + self._schedule_mode = target_schedule_mode + self._target_temperature = target_temperature self._schedule_update() def _schedule_update(self): + if not self.hass: + return + + # Update the new state + self.schedule_update_ha_state(False) + + # nuheat has a delay switching state + # so we schedule a poll of the api + # in the future to make sure the change actually + # took effect + event_helper.call_later( + self.hass, NUHEAT_API_STATE_SHIFT_DELAY, self._schedule_force_refresh + ) + + def _schedule_force_refresh(self, _): self._force_update = True - if self.hass: - self.schedule_update_ha_state(True) + self.schedule_update_ha_state(True) def update(self): """Get the latest state from the thermostat.""" @@ -226,6 +256,8 @@ class NuHeatThermostat(ClimateDevice): def _throttled_update(self, **kwargs): """Get the latest state from the thermostat with a throttle.""" self._thermostat.get_data() + self._schedule_mode = self._thermostat.schedule_mode + self._target_temperature = self._thermostat.target_temperature @property def device_info(self): @@ -233,5 +265,6 @@ class NuHeatThermostat(ClimateDevice): return { "identifiers": {(DOMAIN, self._thermostat.serial_number)}, "name": self._thermostat.room, + "model": "nVent Signature", "manufacturer": MANUFACTURER, } diff --git a/homeassistant/components/nuheat/const.py b/homeassistant/components/nuheat/const.py index e9465d69275..1bb6c3825e7 100644 --- a/homeassistant/components/nuheat/const.py +++ b/homeassistant/components/nuheat/const.py @@ -7,3 +7,5 @@ PLATFORMS = ["climate"] CONF_SERIAL_NUMBER = "serial_number" MANUFACTURER = "NuHeat" + +NUHEAT_API_STATE_SHIFT_DELAY = 4 diff --git a/tests/components/nuheat/mocks.py b/tests/components/nuheat/mocks.py index 7b7c9d1ac06..a9adfd3aa57 100644 --- a/tests/components/nuheat/mocks.py +++ b/tests/components/nuheat/mocks.py @@ -23,6 +23,7 @@ def _get_mock_thermostat_run(): schedule_mode=SCHEDULE_RUN, target_celsius=22, target_fahrenheit=72, + target_temperature=2217, ) thermostat.get_data = Mock() @@ -48,6 +49,7 @@ def _get_mock_thermostat_schedule_hold_unavailable(): schedule_mode=SCHEDULE_HOLD, target_celsius=23, target_fahrenheit=79, + target_temperature=2609, ) thermostat.get_data = Mock() @@ -73,6 +75,7 @@ def _get_mock_thermostat_schedule_hold_available(): schedule_mode=SCHEDULE_HOLD, target_celsius=23, target_fahrenheit=79, + target_temperature=2609, ) thermostat.get_data = Mock() @@ -98,6 +101,7 @@ def _get_mock_thermostat_schedule_temporary_hold(): schedule_mode=SCHEDULE_TEMPORARY_HOLD, target_celsius=43, target_fahrenheit=99, + target_temperature=3729, ) thermostat.get_data = Mock()