mirror of
https://github.com/home-assistant/core.git
synced 2025-04-24 01:08:12 +00:00
Add ozw support for single setpoint thermostat devices (#37713)
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
This commit is contained in:
parent
0297c9e612
commit
24ed932b01
@ -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
|
||||
|
||||
|
@ -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,),
|
||||
|
@ -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
|
||||
|
36
tests/fixtures/ozw/climate_network_dump.csv
vendored
36
tests/fixtures/ozw/climate_network_dump.csv
vendored
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user