Modernize nuheat for new climate platform (#32714)

* Modernize nuheat for new climate platform

* Home Assistant state now mirrors the
  state displayed at mynewheat.com

* Remove off mode as the device does not implement
  and setting was not implemented anyways

* Implement missing set_hvac_mode for nuheat

* Now shows as unavailable when offline

* Add a unique id (serial number)

* Fix hvac_mode as it was really implementing hvac_action

* Presets now map to the open api spec
  published at https://api.mynuheat.com/swagger/

* ThermostatModel: scheduleMode

* Empty commit to re-run ci

* Revert test cleanup as it leaves files behind.

Its going to be more invasive to modernize the tests so
it will have to come in a new pr
This commit is contained in:
J. Nick Koston 2020-03-20 13:01:51 -05:00 committed by GitHub
parent c00f04221f
commit 92d373055f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 72 additions and 44 deletions

View file

@ -2,14 +2,15 @@
from datetime import timedelta from datetime import timedelta
import logging import logging
from nuheat.config import SCHEDULE_HOLD, SCHEDULE_RUN, SCHEDULE_TEMPORARY_HOLD
import voluptuous as vol import voluptuous as vol
from homeassistant.components.climate import ClimateDevice from homeassistant.components.climate import ClimateDevice
from homeassistant.components.climate.const import ( from homeassistant.components.climate.const import (
CURRENT_HVAC_HEAT,
CURRENT_HVAC_IDLE,
HVAC_MODE_AUTO, HVAC_MODE_AUTO,
HVAC_MODE_HEAT, HVAC_MODE_HEAT,
HVAC_MODE_OFF,
PRESET_NONE,
SUPPORT_PRESET_MODE, SUPPORT_PRESET_MODE,
SUPPORT_TARGET_TEMPERATURE, SUPPORT_TARGET_TEMPERATURE,
) )
@ -28,16 +29,25 @@ _LOGGER = logging.getLogger(__name__)
MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=5) MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=5)
# Hold modes # The device does not have an off function.
MODE_AUTO = HVAC_MODE_AUTO # Run device schedule # To turn it off set to min_temp and PRESET_PERMANENT_HOLD
MODE_HOLD_TEMPERATURE = "temperature" OPERATION_LIST = [HVAC_MODE_AUTO, HVAC_MODE_HEAT]
MODE_TEMPORARY_HOLD = "temporary_temperature"
OPERATION_LIST = [HVAC_MODE_HEAT, HVAC_MODE_OFF] PRESET_RUN = "Run Schedule"
PRESET_TEMPORARY_HOLD = "Temporary Hold"
PRESET_PERMANENT_HOLD = "Permanent Hold"
SCHEDULE_HOLD = 3 PRESET_MODES = [PRESET_RUN, PRESET_TEMPORARY_HOLD, PRESET_PERMANENT_HOLD]
SCHEDULE_RUN = 1
SCHEDULE_TEMPORARY_HOLD = 2 PRESET_MODE_TO_SCHEDULE_MODE_MAP = {
PRESET_RUN: SCHEDULE_RUN,
PRESET_TEMPORARY_HOLD: SCHEDULE_TEMPORARY_HOLD,
PRESET_PERMANENT_HOLD: SCHEDULE_HOLD,
}
SCHEDULE_MODE_TO_PRESET_MODE_MAP = {
value: key for key, value in PRESET_MODE_TO_SCHEDULE_MODE_MAP.items()
}
SERVICE_RESUME_PROGRAM = "resume_program" SERVICE_RESUME_PROGRAM = "resume_program"
@ -118,12 +128,36 @@ class NuHeatThermostat(ClimateDevice):
return self._thermostat.fahrenheit return self._thermostat.fahrenheit
@property @property
def hvac_mode(self): def unique_id(self):
"""Return current operation. ie. heat, idle.""" """Return the unique id."""
if self._thermostat.heating: return self._thermostat.serial_number
return HVAC_MODE_HEAT
return HVAC_MODE_OFF @property
def available(self):
"""Return the unique id."""
return self._thermostat.online
def set_hvac_mode(self, hvac_mode):
"""Set the system mode."""
if hvac_mode == HVAC_MODE_AUTO:
self._thermostat.schedule_mode = SCHEDULE_RUN
elif hvac_mode == HVAC_MODE_HEAT:
self._thermostat.schedule_mode = SCHEDULE_HOLD
self._schedule_update()
@property
def hvac_mode(self):
"""Return current setting heat or auto."""
if self._thermostat.schedule_mode in (SCHEDULE_TEMPORARY_HOLD, SCHEDULE_HOLD):
return HVAC_MODE_HEAT
return HVAC_MODE_AUTO
@property
def hvac_action(self):
"""Return current operation heat or idle."""
return CURRENT_HVAC_HEAT if self._thermostat.heating else CURRENT_HVAC_IDLE
@property @property
def min_temp(self): def min_temp(self):
@ -153,21 +187,12 @@ class NuHeatThermostat(ClimateDevice):
def preset_mode(self): def preset_mode(self):
"""Return current preset mode.""" """Return current preset mode."""
schedule_mode = self._thermostat.schedule_mode schedule_mode = self._thermostat.schedule_mode
if schedule_mode == SCHEDULE_RUN: return SCHEDULE_MODE_TO_PRESET_MODE_MAP.get(schedule_mode, PRESET_RUN)
return MODE_AUTO
if schedule_mode == SCHEDULE_HOLD:
return MODE_HOLD_TEMPERATURE
if schedule_mode == SCHEDULE_TEMPORARY_HOLD:
return MODE_TEMPORARY_HOLD
return MODE_AUTO
@property @property
def preset_modes(self): def preset_modes(self):
"""Return available preset modes.""" """Return available preset modes."""
return [PRESET_NONE, MODE_HOLD_TEMPERATURE, MODE_TEMPORARY_HOLD] return PRESET_MODES
@property @property
def hvac_modes(self): def hvac_modes(self):
@ -177,37 +202,42 @@ class NuHeatThermostat(ClimateDevice):
def resume_program(self): def resume_program(self):
"""Resume the thermostat's programmed schedule.""" """Resume the thermostat's programmed schedule."""
self._thermostat.resume_schedule() self._thermostat.resume_schedule()
self._force_update = True self._schedule_update()
def set_preset_mode(self, preset_mode): def set_preset_mode(self, preset_mode):
"""Update the hold mode of the thermostat.""" """Update the hold mode of the thermostat."""
if preset_mode == PRESET_NONE:
schedule_mode = SCHEDULE_RUN
elif preset_mode == MODE_HOLD_TEMPERATURE: self._thermostat.schedule_mode = PRESET_MODE_TO_SCHEDULE_MODE_MAP.get(
schedule_mode = SCHEDULE_HOLD preset_mode, SCHEDULE_RUN
)
elif preset_mode == MODE_TEMPORARY_HOLD: self._schedule_update()
schedule_mode = SCHEDULE_TEMPORARY_HOLD
self._thermostat.schedule_mode = schedule_mode
self._force_update = True
def set_temperature(self, **kwargs): def set_temperature(self, **kwargs):
"""Set a new target temperature.""" """Set a new target temperature."""
temperature = kwargs.get(ATTR_TEMPERATURE) self._set_temperature(kwargs.get(ATTR_TEMPERATURE))
def _set_temperature(self, temperature):
if self._temperature_unit == "C": if self._temperature_unit == "C":
self._thermostat.target_celsius = temperature self._thermostat.target_celsius = temperature
else: else:
self._thermostat.target_fahrenheit = temperature self._thermostat.target_fahrenheit = temperature
# If they set a temperature without changing the mode
# to heat, we behave like the device does locally
# and set a temp hold.
if self._thermostat.schedule_mode == SCHEDULE_RUN:
self._thermostat.schedule_mode = SCHEDULE_TEMPORARY_HOLD
_LOGGER.debug( _LOGGER.debug(
"Setting NuHeat thermostat temperature to %s %s", "Setting NuHeat thermostat temperature to %s %s",
temperature, temperature,
self.temperature_unit, self.temperature_unit,
) )
self._schedule_update()
def _schedule_update(self):
self._force_update = True self._force_update = True
if self.hass:
self.schedule_update_ha_state(True)
def update(self): def update(self):
"""Get the latest state from the thermostat.""" """Get the latest state from the thermostat."""

View file

@ -3,8 +3,8 @@ import unittest
from unittest.mock import Mock, patch from unittest.mock import Mock, patch
from homeassistant.components.climate.const import ( from homeassistant.components.climate.const import (
HVAC_MODE_AUTO,
HVAC_MODE_HEAT, HVAC_MODE_HEAT,
HVAC_MODE_OFF,
SUPPORT_PRESET_MODE, SUPPORT_PRESET_MODE,
SUPPORT_TARGET_TEMPERATURE, SUPPORT_TARGET_TEMPERATURE,
) )
@ -130,10 +130,8 @@ class TestNuHeat(unittest.TestCase):
assert self.thermostat.current_temperature == 22 assert self.thermostat.current_temperature == 22
def test_current_operation(self): def test_current_operation(self):
"""Test current operation.""" """Test requested mode."""
assert self.thermostat.hvac_mode == HVAC_MODE_HEAT assert self.thermostat.hvac_mode == HVAC_MODE_AUTO
self.thermostat._thermostat.heating = False
assert self.thermostat.hvac_mode == HVAC_MODE_OFF
def test_min_temp(self): def test_min_temp(self):
"""Test min temp.""" """Test min temp."""
@ -155,7 +153,7 @@ class TestNuHeat(unittest.TestCase):
def test_operation_list(self): def test_operation_list(self):
"""Test the operation list.""" """Test the operation list."""
assert self.thermostat.hvac_modes == [HVAC_MODE_HEAT, HVAC_MODE_OFF] assert self.thermostat.hvac_modes == [HVAC_MODE_AUTO, HVAC_MODE_HEAT]
def test_resume_program(self): def test_resume_program(self):
"""Test resume schedule.""" """Test resume schedule."""