Add ozw support for single setpoint thermostat devices (#37713)

Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
This commit is contained in:
Marcel van der Veldt 2020-07-17 14:25:16 +02:00 committed by GitHub
parent 0297c9e612
commit 24ed932b01
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 142 additions and 17 deletions

View File

@ -174,7 +174,8 @@ class ZWaveClimateEntity(ZWaveDeviceEntity, ClimateEntity):
def hvac_mode(self):
"""Return hvac operation ie. heat, cool mode."""
if not self.values.mode:
return None
# Thermostat(valve) with no support for setting a mode is considered heating-only
return HVAC_MODE_HEAT
return ZW_HVAC_MODE_MAPPINGS.get(
self.values.mode.value[VALUE_SELECTED_ID], HVAC_MODE_HEAT_COOL
)
@ -197,7 +198,7 @@ class ZWaveClimateEntity(ZWaveDeviceEntity, ClimateEntity):
@property
def temperature_unit(self):
"""Return the unit of measurement."""
if self.values.temperature and self.values.temperature.units == "F":
if self.values.temperature is not None and self.values.temperature.units == "F":
return TEMP_FAHRENHEIT
return TEMP_CELSIUS
@ -220,6 +221,8 @@ class ZWaveClimateEntity(ZWaveDeviceEntity, ClimateEntity):
def preset_mode(self):
"""Return preset operation ie. eco, away."""
# A Zwave mode that can not be translated to a hass mode is considered a preset
if not self.values.mode:
return None
if self.values.mode.value[VALUE_SELECTED_ID] not in MODES_LIST:
return self.values.mode.value[VALUE_SELECTED_LABEL]
return PRESET_NONE
@ -274,8 +277,14 @@ class ZWaveClimateEntity(ZWaveDeviceEntity, ClimateEntity):
async def async_set_hvac_mode(self, hvac_mode):
"""Set new target hvac mode."""
if not self.values.mode:
# Thermostat(valve) with no support for setting a mode
_LOGGER.warning(
"Thermostat %s does not support setting a mode", self.entity_id
)
return
hvac_mode_value = self._hvac_modes.get(hvac_mode)
if not hvac_mode_value:
if hvac_mode_value is None:
_LOGGER.warning("Received an invalid hvac mode: %s", hvac_mode)
return
self.values.mode.send_value(hvac_mode_value)
@ -320,8 +329,11 @@ class ZWaveClimateEntity(ZWaveDeviceEntity, ClimateEntity):
def _get_current_mode_setpoint_values(self) -> Tuple:
"""Return a tuple of current setpoint Z-Wave value(s)."""
current_mode = self.values.mode.value[VALUE_SELECTED_ID]
setpoint_names = MODE_SETPOINT_MAPPINGS.get(current_mode, ())
if not self.values.mode:
setpoint_names = ("setpoint_heating",)
else:
current_mode = self.values.mode.value[VALUE_SELECTED_ID]
setpoint_names = MODE_SETPOINT_MAPPINGS.get(current_mode, ())
# we do not want None values in our tuple so check if the value exists
return tuple(
getattr(self.values, value_name)
@ -331,20 +343,21 @@ class ZWaveClimateEntity(ZWaveDeviceEntity, ClimateEntity):
def _set_modes_and_presets(self):
"""Convert Z-Wave Thermostat modes into Home Assistant modes and presets."""
if not self.values.mode:
return
all_modes = {}
all_presets = {PRESET_NONE: None}
# Z-Wave uses one list for both modes and presets.
# Iterate over all Z-Wave ThermostatModes and extract the hvac modes and presets.
for val in self.values.mode.value[VALUE_LIST]:
if val[VALUE_ID] in MODES_LIST:
# treat value as hvac mode
hass_mode = ZW_HVAC_MODE_MAPPINGS.get(val[VALUE_ID])
all_modes[hass_mode] = val[VALUE_ID]
else:
# treat value as hvac preset
all_presets[val[VALUE_LABEL]] = val[VALUE_ID]
if self.values.mode:
# Z-Wave uses one list for both modes and presets.
# Iterate over all Z-Wave ThermostatModes and extract the hvac modes and presets.
for val in self.values.mode.value[VALUE_LIST]:
if val[VALUE_ID] in MODES_LIST:
# treat value as hvac mode
hass_mode = ZW_HVAC_MODE_MAPPINGS.get(val[VALUE_ID])
all_modes[hass_mode] = val[VALUE_ID]
else:
# treat value as hvac preset
all_presets[val[VALUE_LABEL]] = val[VALUE_ID]
else:
all_modes[HVAC_MODE_HEAT] = None
self._hvac_modes = all_modes
self._hvac_presets = all_presets

View File

@ -131,6 +131,37 @@ DISCOVERY_SCHEMAS = (
},
},
},
{ # Z-Wave Thermostat device without mode support
const.DISC_COMPONENT: "climate",
const.DISC_GENERIC_DEVICE_CLASS: (const_ozw.GENERIC_TYPE_THERMOSTAT,),
const.DISC_SPECIFIC_DEVICE_CLASS: (
const_ozw.SPECIFIC_TYPE_SETPOINT_THERMOSTAT,
),
const.DISC_VALUES: {
const.DISC_PRIMARY: {
const.DISC_COMMAND_CLASS: (CommandClass.THERMOSTAT_SETPOINT,)
},
"temperature": {
const.DISC_COMMAND_CLASS: (CommandClass.SENSOR_MULTILEVEL,),
const.DISC_INDEX: (1,),
const.DISC_OPTIONAL: True,
},
"operating_state": {
const.DISC_COMMAND_CLASS: (CommandClass.THERMOSTAT_OPERATING_STATE,),
const.DISC_OPTIONAL: True,
},
"valve_position": {
const.DISC_COMMAND_CLASS: (CommandClass.SWITCH_MULTILEVEL,),
const.DISC_INDEX: (0,),
const.DISC_OPTIONAL: True,
},
"setpoint_heating": {
const.DISC_COMMAND_CLASS: (CommandClass.THERMOSTAT_SETPOINT,),
const.DISC_INDEX: (1,),
const.DISC_OPTIONAL: True,
},
},
},
{ # Rollershutter
const.DISC_COMPONENT: "cover",
const.DISC_GENERIC_DEVICE_CLASS: (const_ozw.GENERIC_TYPE_SWITCH_MULTILEVEL,),

View File

@ -6,6 +6,7 @@ from homeassistant.components.climate.const import (
ATTR_FAN_MODES,
ATTR_HVAC_ACTION,
ATTR_HVAC_MODES,
ATTR_PRESET_MODE,
ATTR_PRESET_MODES,
ATTR_TARGET_TEMP_HIGH,
ATTR_TARGET_TEMP_LOW,
@ -218,3 +219,47 @@ async def test_climate(hass, climate_data, sent_messages, climate_msg, caplog):
)
assert len(sent_messages) == 8
assert "Received an invalid preset mode: invalid preset mode" in caplog.text
# test thermostat device without a mode commandclass
state = hass.states.get("climate.danfoss_living_connect_z_v1_06_014g0013_heating_1")
assert state is not None
assert state.state == HVAC_MODE_HEAT
assert state.attributes[ATTR_HVAC_MODES] == [
HVAC_MODE_HEAT,
]
assert state.attributes.get(ATTR_CURRENT_TEMPERATURE) is None
assert round(state.attributes[ATTR_TEMPERATURE], 0) == 21
assert state.attributes.get(ATTR_TARGET_TEMP_LOW) is None
assert state.attributes.get(ATTR_TARGET_TEMP_HIGH) is None
assert state.attributes.get(ATTR_PRESET_MODE) is None
assert state.attributes.get(ATTR_PRESET_MODES) is None
# Test set target temperature
await hass.services.async_call(
"climate",
"set_temperature",
{
"entity_id": "climate.danfoss_living_connect_z_v1_06_014g0013_heating_1",
"temperature": 28.0,
},
blocking=True,
)
assert len(sent_messages) == 9
msg = sent_messages[-1]
assert msg["topic"] == "OpenZWave/1/command/setvalue/"
assert msg["payload"] == {
"Value": 28.0,
"ValueIDKey": 281475116220434,
}
await hass.services.async_call(
"climate",
"set_hvac_mode",
{
"entity_id": "climate.danfoss_living_connect_z_v1_06_014g0013_heating_1",
"hvac_mode": HVAC_MODE_HEAT,
},
blocking=True,
)
assert len(sent_messages) == 9
assert "does not support setting a mode" in caplog.text

File diff suppressed because one or more lines are too long