mirror of
https://github.com/home-assistant/core.git
synced 2025-07-25 22:27:07 +00:00
Preset support for MOES thermostat valves (#48178)
This commit is contained in:
parent
7473f25afd
commit
9739707f62
@ -31,6 +31,9 @@ from homeassistant.components.climate.const import (
|
|||||||
HVAC_MODE_HEAT_COOL,
|
HVAC_MODE_HEAT_COOL,
|
||||||
HVAC_MODE_OFF,
|
HVAC_MODE_OFF,
|
||||||
PRESET_AWAY,
|
PRESET_AWAY,
|
||||||
|
PRESET_BOOST,
|
||||||
|
PRESET_COMFORT,
|
||||||
|
PRESET_ECO,
|
||||||
PRESET_NONE,
|
PRESET_NONE,
|
||||||
SUPPORT_FAN_MODE,
|
SUPPORT_FAN_MODE,
|
||||||
SUPPORT_PRESET_MODE,
|
SUPPORT_PRESET_MODE,
|
||||||
@ -49,6 +52,8 @@ from .core.const import (
|
|||||||
CHANNEL_THERMOSTAT,
|
CHANNEL_THERMOSTAT,
|
||||||
DATA_ZHA,
|
DATA_ZHA,
|
||||||
DATA_ZHA_DISPATCHERS,
|
DATA_ZHA_DISPATCHERS,
|
||||||
|
PRESET_COMPLEX,
|
||||||
|
PRESET_SCHEDULE,
|
||||||
SIGNAL_ADD_ENTITIES,
|
SIGNAL_ADD_ENTITIES,
|
||||||
SIGNAL_ATTR_UPDATED,
|
SIGNAL_ATTR_UPDATED,
|
||||||
)
|
)
|
||||||
@ -595,3 +600,88 @@ class ZenWithinThermostat(Thermostat):
|
|||||||
)
|
)
|
||||||
class CentralitePearl(ZenWithinThermostat):
|
class CentralitePearl(ZenWithinThermostat):
|
||||||
"""Centralite Pearl Thermostat implementation."""
|
"""Centralite Pearl Thermostat implementation."""
|
||||||
|
|
||||||
|
|
||||||
|
@STRICT_MATCH(
|
||||||
|
channel_names=CHANNEL_THERMOSTAT,
|
||||||
|
manufacturers={
|
||||||
|
"_TZE200_ckud7u2l",
|
||||||
|
"_TZE200_ywdxldoj",
|
||||||
|
"_TYST11_ckud7u2l",
|
||||||
|
"_TYST11_ywdxldoj",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
class MoesThermostat(Thermostat):
|
||||||
|
"""Moes Thermostat implementation."""
|
||||||
|
|
||||||
|
def __init__(self, unique_id, zha_device, channels, **kwargs):
|
||||||
|
"""Initialize ZHA Thermostat instance."""
|
||||||
|
super().__init__(unique_id, zha_device, channels, **kwargs)
|
||||||
|
self._presets = [
|
||||||
|
PRESET_NONE,
|
||||||
|
PRESET_AWAY,
|
||||||
|
PRESET_SCHEDULE,
|
||||||
|
PRESET_COMFORT,
|
||||||
|
PRESET_ECO,
|
||||||
|
PRESET_BOOST,
|
||||||
|
PRESET_COMPLEX,
|
||||||
|
]
|
||||||
|
self._supported_flags |= SUPPORT_PRESET_MODE
|
||||||
|
|
||||||
|
@property
|
||||||
|
def hvac_modes(self) -> tuple[str, ...]:
|
||||||
|
"""Return only the heat mode, because the device can't be turned off."""
|
||||||
|
return (HVAC_MODE_HEAT,)
|
||||||
|
|
||||||
|
async def async_attribute_updated(self, record):
|
||||||
|
"""Handle attribute update from device."""
|
||||||
|
if record.attr_name == "operation_preset":
|
||||||
|
if record.value == 0:
|
||||||
|
self._preset = PRESET_AWAY
|
||||||
|
if record.value == 1:
|
||||||
|
self._preset = PRESET_SCHEDULE
|
||||||
|
if record.value == 2:
|
||||||
|
self._preset = PRESET_NONE
|
||||||
|
if record.value == 3:
|
||||||
|
self._preset = PRESET_COMFORT
|
||||||
|
if record.value == 4:
|
||||||
|
self._preset = PRESET_ECO
|
||||||
|
if record.value == 5:
|
||||||
|
self._preset = PRESET_BOOST
|
||||||
|
if record.value == 6:
|
||||||
|
self._preset = PRESET_COMPLEX
|
||||||
|
await super().async_attribute_updated(record)
|
||||||
|
|
||||||
|
async def async_preset_handler(self, preset: str, enable: bool = False) -> bool:
|
||||||
|
"""Set the preset mode."""
|
||||||
|
mfg_code = self._zha_device.manufacturer_code
|
||||||
|
if not enable:
|
||||||
|
return await self._thrm.write_attributes(
|
||||||
|
{"operation_preset": 2}, manufacturer=mfg_code
|
||||||
|
)
|
||||||
|
if preset == PRESET_AWAY:
|
||||||
|
return await self._thrm.write_attributes(
|
||||||
|
{"operation_preset": 0}, manufacturer=mfg_code
|
||||||
|
)
|
||||||
|
if preset == PRESET_SCHEDULE:
|
||||||
|
return await self._thrm.write_attributes(
|
||||||
|
{"operation_preset": 1}, manufacturer=mfg_code
|
||||||
|
)
|
||||||
|
if preset == PRESET_COMFORT:
|
||||||
|
return await self._thrm.write_attributes(
|
||||||
|
{"operation_preset": 3}, manufacturer=mfg_code
|
||||||
|
)
|
||||||
|
if preset == PRESET_ECO:
|
||||||
|
return await self._thrm.write_attributes(
|
||||||
|
{"operation_preset": 4}, manufacturer=mfg_code
|
||||||
|
)
|
||||||
|
if preset == PRESET_BOOST:
|
||||||
|
return await self._thrm.write_attributes(
|
||||||
|
{"operation_preset": 5}, manufacturer=mfg_code
|
||||||
|
)
|
||||||
|
if preset == PRESET_COMPLEX:
|
||||||
|
return await self._thrm.write_attributes(
|
||||||
|
{"operation_preset": 6}, manufacturer=mfg_code
|
||||||
|
)
|
||||||
|
|
||||||
|
return False
|
||||||
|
@ -173,6 +173,9 @@ MFG_CLUSTER_ID_START = 0xFC00
|
|||||||
POWER_MAINS_POWERED = "Mains"
|
POWER_MAINS_POWERED = "Mains"
|
||||||
POWER_BATTERY_OR_UNKNOWN = "Battery or Unknown"
|
POWER_BATTERY_OR_UNKNOWN = "Battery or Unknown"
|
||||||
|
|
||||||
|
PRESET_SCHEDULE = "schedule"
|
||||||
|
PRESET_COMPLEX = "complex"
|
||||||
|
|
||||||
|
|
||||||
class RadioType(enum.Enum):
|
class RadioType(enum.Enum):
|
||||||
# pylint: disable=invalid-name
|
# pylint: disable=invalid-name
|
||||||
|
@ -33,6 +33,9 @@ from homeassistant.components.climate.const import (
|
|||||||
HVAC_MODE_HEAT_COOL,
|
HVAC_MODE_HEAT_COOL,
|
||||||
HVAC_MODE_OFF,
|
HVAC_MODE_OFF,
|
||||||
PRESET_AWAY,
|
PRESET_AWAY,
|
||||||
|
PRESET_BOOST,
|
||||||
|
PRESET_COMFORT,
|
||||||
|
PRESET_ECO,
|
||||||
PRESET_NONE,
|
PRESET_NONE,
|
||||||
SERVICE_SET_FAN_MODE,
|
SERVICE_SET_FAN_MODE,
|
||||||
SERVICE_SET_HVAC_MODE,
|
SERVICE_SET_HVAC_MODE,
|
||||||
@ -44,6 +47,7 @@ from homeassistant.components.zha.climate import (
|
|||||||
HVAC_MODE_2_SYSTEM,
|
HVAC_MODE_2_SYSTEM,
|
||||||
SEQ_OF_OPERATION,
|
SEQ_OF_OPERATION,
|
||||||
)
|
)
|
||||||
|
from homeassistant.components.zha.core.const import PRESET_COMPLEX, PRESET_SCHEDULE
|
||||||
from homeassistant.const import ATTR_ENTITY_ID, ATTR_TEMPERATURE, STATE_UNKNOWN
|
from homeassistant.const import ATTR_ENTITY_ID, ATTR_TEMPERATURE, STATE_UNKNOWN
|
||||||
|
|
||||||
from .common import async_enable_traffic, find_entity_id, send_attributes_report
|
from .common import async_enable_traffic, find_entity_id, send_attributes_report
|
||||||
@ -103,8 +107,23 @@ CLIMATE_ZEN = {
|
|||||||
"out_clusters": [zigpy.zcl.clusters.general.Ota.cluster_id],
|
"out_clusters": [zigpy.zcl.clusters.general.Ota.cluster_id],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CLIMATE_MOES = {
|
||||||
|
1: {
|
||||||
|
"device_type": zigpy.profiles.zha.DeviceType.THERMOSTAT,
|
||||||
|
"in_clusters": [
|
||||||
|
zigpy.zcl.clusters.general.Basic.cluster_id,
|
||||||
|
zigpy.zcl.clusters.general.Identify.cluster_id,
|
||||||
|
zigpy.zcl.clusters.hvac.Thermostat.cluster_id,
|
||||||
|
zigpy.zcl.clusters.hvac.UserInterface.cluster_id,
|
||||||
|
61148,
|
||||||
|
],
|
||||||
|
"out_clusters": [zigpy.zcl.clusters.general.Ota.cluster_id],
|
||||||
|
}
|
||||||
|
}
|
||||||
MANUF_SINOPE = "Sinope Technologies"
|
MANUF_SINOPE = "Sinope Technologies"
|
||||||
MANUF_ZEN = "Zen Within"
|
MANUF_ZEN = "Zen Within"
|
||||||
|
MANUF_MOES = "_TZE200_ckud7u2l"
|
||||||
|
|
||||||
ZCL_ATTR_PLUG = {
|
ZCL_ATTR_PLUG = {
|
||||||
"abs_min_heat_setpoint_limit": 800,
|
"abs_min_heat_setpoint_limit": 800,
|
||||||
@ -183,6 +202,13 @@ async def device_climate_zen(device_climate_mock):
|
|||||||
return await device_climate_mock(CLIMATE_ZEN, manuf=MANUF_ZEN)
|
return await device_climate_mock(CLIMATE_ZEN, manuf=MANUF_ZEN)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
async def device_climate_moes(device_climate_mock):
|
||||||
|
"""MOES thermostat."""
|
||||||
|
|
||||||
|
return await device_climate_mock(CLIMATE_MOES, manuf=MANUF_MOES)
|
||||||
|
|
||||||
|
|
||||||
def test_sequence_mappings():
|
def test_sequence_mappings():
|
||||||
"""Test correct mapping between control sequence -> HVAC Mode -> Sysmode."""
|
"""Test correct mapping between control sequence -> HVAC Mode -> Sysmode."""
|
||||||
|
|
||||||
@ -1106,3 +1132,160 @@ async def test_set_fan_mode(hass, device_climate_fan):
|
|||||||
)
|
)
|
||||||
assert fan_cluster.write_attributes.await_count == 1
|
assert fan_cluster.write_attributes.await_count == 1
|
||||||
assert fan_cluster.write_attributes.call_args[0][0] == {"fan_mode": 5}
|
assert fan_cluster.write_attributes.call_args[0][0] == {"fan_mode": 5}
|
||||||
|
|
||||||
|
|
||||||
|
async def test_set_moes_preset(hass, device_climate_moes):
|
||||||
|
"""Test setting preset for moes trv."""
|
||||||
|
|
||||||
|
entity_id = await find_entity_id(DOMAIN, device_climate_moes, hass)
|
||||||
|
thrm_cluster = device_climate_moes.device.endpoints[1].thermostat
|
||||||
|
|
||||||
|
state = hass.states.get(entity_id)
|
||||||
|
assert state.attributes[ATTR_PRESET_MODE] == PRESET_NONE
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
DOMAIN,
|
||||||
|
SERVICE_SET_PRESET_MODE,
|
||||||
|
{ATTR_ENTITY_ID: entity_id, ATTR_PRESET_MODE: PRESET_AWAY},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert thrm_cluster.write_attributes.await_count == 1
|
||||||
|
assert thrm_cluster.write_attributes.call_args_list[0][0][0] == {
|
||||||
|
"operation_preset": 0
|
||||||
|
}
|
||||||
|
|
||||||
|
thrm_cluster.write_attributes.reset_mock()
|
||||||
|
await hass.services.async_call(
|
||||||
|
DOMAIN,
|
||||||
|
SERVICE_SET_PRESET_MODE,
|
||||||
|
{ATTR_ENTITY_ID: entity_id, ATTR_PRESET_MODE: PRESET_SCHEDULE},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert thrm_cluster.write_attributes.await_count == 2
|
||||||
|
assert thrm_cluster.write_attributes.call_args_list[0][0][0] == {
|
||||||
|
"operation_preset": 2
|
||||||
|
}
|
||||||
|
assert thrm_cluster.write_attributes.call_args_list[1][0][0] == {
|
||||||
|
"operation_preset": 1
|
||||||
|
}
|
||||||
|
|
||||||
|
thrm_cluster.write_attributes.reset_mock()
|
||||||
|
await hass.services.async_call(
|
||||||
|
DOMAIN,
|
||||||
|
SERVICE_SET_PRESET_MODE,
|
||||||
|
{ATTR_ENTITY_ID: entity_id, ATTR_PRESET_MODE: PRESET_COMFORT},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert thrm_cluster.write_attributes.await_count == 2
|
||||||
|
assert thrm_cluster.write_attributes.call_args_list[0][0][0] == {
|
||||||
|
"operation_preset": 2
|
||||||
|
}
|
||||||
|
assert thrm_cluster.write_attributes.call_args_list[1][0][0] == {
|
||||||
|
"operation_preset": 3
|
||||||
|
}
|
||||||
|
|
||||||
|
thrm_cluster.write_attributes.reset_mock()
|
||||||
|
await hass.services.async_call(
|
||||||
|
DOMAIN,
|
||||||
|
SERVICE_SET_PRESET_MODE,
|
||||||
|
{ATTR_ENTITY_ID: entity_id, ATTR_PRESET_MODE: PRESET_ECO},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert thrm_cluster.write_attributes.await_count == 2
|
||||||
|
assert thrm_cluster.write_attributes.call_args_list[0][0][0] == {
|
||||||
|
"operation_preset": 2
|
||||||
|
}
|
||||||
|
assert thrm_cluster.write_attributes.call_args_list[1][0][0] == {
|
||||||
|
"operation_preset": 4
|
||||||
|
}
|
||||||
|
|
||||||
|
thrm_cluster.write_attributes.reset_mock()
|
||||||
|
await hass.services.async_call(
|
||||||
|
DOMAIN,
|
||||||
|
SERVICE_SET_PRESET_MODE,
|
||||||
|
{ATTR_ENTITY_ID: entity_id, ATTR_PRESET_MODE: PRESET_BOOST},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert thrm_cluster.write_attributes.await_count == 2
|
||||||
|
assert thrm_cluster.write_attributes.call_args_list[0][0][0] == {
|
||||||
|
"operation_preset": 2
|
||||||
|
}
|
||||||
|
assert thrm_cluster.write_attributes.call_args_list[1][0][0] == {
|
||||||
|
"operation_preset": 5
|
||||||
|
}
|
||||||
|
|
||||||
|
thrm_cluster.write_attributes.reset_mock()
|
||||||
|
await hass.services.async_call(
|
||||||
|
DOMAIN,
|
||||||
|
SERVICE_SET_PRESET_MODE,
|
||||||
|
{ATTR_ENTITY_ID: entity_id, ATTR_PRESET_MODE: PRESET_COMPLEX},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert thrm_cluster.write_attributes.await_count == 2
|
||||||
|
assert thrm_cluster.write_attributes.call_args_list[0][0][0] == {
|
||||||
|
"operation_preset": 2
|
||||||
|
}
|
||||||
|
assert thrm_cluster.write_attributes.call_args_list[1][0][0] == {
|
||||||
|
"operation_preset": 6
|
||||||
|
}
|
||||||
|
|
||||||
|
thrm_cluster.write_attributes.reset_mock()
|
||||||
|
await hass.services.async_call(
|
||||||
|
DOMAIN,
|
||||||
|
SERVICE_SET_PRESET_MODE,
|
||||||
|
{ATTR_ENTITY_ID: entity_id, ATTR_PRESET_MODE: PRESET_NONE},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert thrm_cluster.write_attributes.await_count == 1
|
||||||
|
assert thrm_cluster.write_attributes.call_args_list[0][0][0] == {
|
||||||
|
"operation_preset": 2
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async def test_set_moes_operation_mode(hass, device_climate_moes):
|
||||||
|
"""Test setting preset for moes trv."""
|
||||||
|
|
||||||
|
entity_id = await find_entity_id(DOMAIN, device_climate_moes, hass)
|
||||||
|
thrm_cluster = device_climate_moes.device.endpoints[1].thermostat
|
||||||
|
|
||||||
|
await send_attributes_report(hass, thrm_cluster, {"operation_preset": 0})
|
||||||
|
|
||||||
|
state = hass.states.get(entity_id)
|
||||||
|
assert state.attributes[ATTR_PRESET_MODE] == PRESET_AWAY
|
||||||
|
|
||||||
|
await send_attributes_report(hass, thrm_cluster, {"operation_preset": 1})
|
||||||
|
|
||||||
|
state = hass.states.get(entity_id)
|
||||||
|
assert state.attributes[ATTR_PRESET_MODE] == PRESET_SCHEDULE
|
||||||
|
|
||||||
|
await send_attributes_report(hass, thrm_cluster, {"operation_preset": 2})
|
||||||
|
|
||||||
|
state = hass.states.get(entity_id)
|
||||||
|
assert state.attributes[ATTR_PRESET_MODE] == PRESET_NONE
|
||||||
|
|
||||||
|
await send_attributes_report(hass, thrm_cluster, {"operation_preset": 3})
|
||||||
|
|
||||||
|
state = hass.states.get(entity_id)
|
||||||
|
assert state.attributes[ATTR_PRESET_MODE] == PRESET_COMFORT
|
||||||
|
|
||||||
|
await send_attributes_report(hass, thrm_cluster, {"operation_preset": 4})
|
||||||
|
|
||||||
|
state = hass.states.get(entity_id)
|
||||||
|
assert state.attributes[ATTR_PRESET_MODE] == PRESET_ECO
|
||||||
|
|
||||||
|
await send_attributes_report(hass, thrm_cluster, {"operation_preset": 5})
|
||||||
|
|
||||||
|
state = hass.states.get(entity_id)
|
||||||
|
assert state.attributes[ATTR_PRESET_MODE] == PRESET_BOOST
|
||||||
|
|
||||||
|
await send_attributes_report(hass, thrm_cluster, {"operation_preset": 6})
|
||||||
|
|
||||||
|
state = hass.states.get(entity_id)
|
||||||
|
assert state.attributes[ATTR_PRESET_MODE] == PRESET_COMPLEX
|
||||||
|
Loading…
x
Reference in New Issue
Block a user