Fix ZHA climate hvac_action for Centralite thermostat (#35993)

* Centralite specific control seq of operation

* Remove Fan safeguards

* Split hvac_action property.

* Refactor hvac_action property.

Current hvac_action logic is Zen Within thermostat specific and differs
a bit from ZCL specs. Implement it as a separate class.

* Refactor hvac_action property for default thermostat

Follow more closely ZCL specs in parsing hvac state of the thermostat.
This commit is contained in:
Alexei Chetroi
2020-05-23 06:22:36 -04:00
committed by GitHub
parent 3d253fa52a
commit 04cfd36d06
3 changed files with 144 additions and 46 deletions
+78 -23
View File
@@ -89,7 +89,22 @@ CLIMATE_SINOPE = {
"profile_id": 260,
},
}
SINOPE = "Sinope Technologies"
CLIMATE_ZEN = {
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.Fan.cluster_id,
zigpy.zcl.clusters.hvac.Thermostat.cluster_id,
zigpy.zcl.clusters.hvac.UserInterface.cluster_id,
],
"out_clusters": [zigpy.zcl.clusters.general.Ota.cluster_id],
}
}
MANUF_SINOPE = "Sinope Technologies"
MANUF_ZEN = "Zen Within"
ZCL_ATTR_PLUG = {
"abs_min_heat_setpoint_limit": 800,
@@ -169,7 +184,14 @@ async def device_climate_fan(device_climate_mock):
async def device_climate_sinope(device_climate_mock):
"""Sinope thermostat."""
return await device_climate_mock(CLIMATE_SINOPE, manuf=SINOPE)
return await device_climate_mock(CLIMATE_SINOPE, manuf=MANUF_SINOPE)
@pytest.fixture
async def device_climate_zen(device_climate_mock):
"""Zen Within thermostat."""
return await device_climate_mock(CLIMATE_ZEN, manuf=MANUF_ZEN)
def test_sequence_mappings():
@@ -201,6 +223,52 @@ async def test_climate_hvac_action_running_state(hass, device_climate):
thrm_cluster = device_climate.device.endpoints[1].thermostat
entity_id = await find_entity_id(DOMAIN, device_climate, hass)
state = hass.states.get(entity_id)
assert state.attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_OFF
await send_attributes_report(
hass, thrm_cluster, {0x001E: Thermostat.RunningMode.Off}
)
state = hass.states.get(entity_id)
assert state.attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_OFF
await send_attributes_report(
hass, thrm_cluster, {0x001C: Thermostat.SystemMode.Auto}
)
state = hass.states.get(entity_id)
assert state.attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_IDLE
await send_attributes_report(
hass, thrm_cluster, {0x001E: Thermostat.RunningMode.Cool}
)
state = hass.states.get(entity_id)
assert state.attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_COOL
await send_attributes_report(
hass, thrm_cluster, {0x001E: Thermostat.RunningMode.Heat}
)
state = hass.states.get(entity_id)
assert state.attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_HEAT
await send_attributes_report(
hass, thrm_cluster, {0x001E: Thermostat.RunningMode.Off}
)
state = hass.states.get(entity_id)
assert state.attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_IDLE
await send_attributes_report(
hass, thrm_cluster, {0x0029: Thermostat.RunningState.Fan_State_On}
)
state = hass.states.get(entity_id)
assert state.attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_FAN
async def test_climate_hvac_action_running_state_zen(hass, device_climate_zen):
"""Test Zen hvac action via running state."""
thrm_cluster = device_climate_zen.device.endpoints[1].thermostat
entity_id = await find_entity_id(DOMAIN, device_climate_zen, hass)
state = hass.states.get(entity_id)
assert ATTR_HVAC_ACTION not in state.attributes
@@ -266,7 +334,7 @@ async def test_climate_hvac_action_pi_demand(hass, device_climate):
entity_id = await find_entity_id(DOMAIN, device_climate, hass)
state = hass.states.get(entity_id)
assert ATTR_HVAC_ACTION not in state.attributes
assert state.attributes[ATTR_HVAC_ACTION] == CURRENT_HVAC_OFF
await send_attributes_report(hass, thrm_cluster, {0x0007: 10})
state = hass.states.get(entity_id)
@@ -381,7 +449,7 @@ async def test_target_temperature(
"unoccupied_heating_setpoint": 1600,
"unoccupied_cooling_setpoint": 2700,
},
manuf=SINOPE,
manuf=MANUF_SINOPE,
)
entity_id = await find_entity_id(DOMAIN, device_climate, hass)
if preset:
@@ -417,7 +485,7 @@ async def test_target_temperature_high(
"system_mode": Thermostat.SystemMode.Auto,
"unoccupied_cooling_setpoint": unoccupied,
},
manuf=SINOPE,
manuf=MANUF_SINOPE,
)
entity_id = await find_entity_id(DOMAIN, device_climate, hass)
if preset:
@@ -453,7 +521,7 @@ async def test_target_temperature_low(
"system_mode": Thermostat.SystemMode.Auto,
"unoccupied_heating_setpoint": unoccupied,
},
manuf=SINOPE,
manuf=MANUF_SINOPE,
)
entity_id = await find_entity_id(DOMAIN, device_climate, hass)
if preset:
@@ -665,7 +733,7 @@ async def test_set_temperature_heat_cool(hass, device_climate_mock):
"unoccupied_heating_setpoint": 1600,
"unoccupied_cooling_setpoint": 2700,
},
manuf=SINOPE,
manuf=MANUF_SINOPE,
)
entity_id = await find_entity_id(DOMAIN, device_climate, hass)
thrm_cluster = device_climate.device.endpoints[1].thermostat
@@ -755,7 +823,7 @@ async def test_set_temperature_heat(hass, device_climate_mock):
"unoccupied_heating_setpoint": 1600,
"unoccupied_cooling_setpoint": 2700,
},
manuf=SINOPE,
manuf=MANUF_SINOPE,
)
entity_id = await find_entity_id(DOMAIN, device_climate, hass)
thrm_cluster = device_climate.device.endpoints[1].thermostat
@@ -838,7 +906,7 @@ async def test_set_temperature_cool(hass, device_climate_mock):
"unoccupied_cooling_setpoint": 1600,
"unoccupied_heating_setpoint": 2700,
},
manuf=SINOPE,
manuf=MANUF_SINOPE,
)
entity_id = await find_entity_id(DOMAIN, device_climate, hass)
thrm_cluster = device_climate.device.endpoints[1].thermostat
@@ -921,7 +989,7 @@ async def test_set_temperature_wrong_mode(hass, device_climate_mock):
"unoccupied_cooling_setpoint": 1600,
"unoccupied_heating_setpoint": 2700,
},
manuf=SINOPE,
manuf=MANUF_SINOPE,
)
entity_id = await find_entity_id(DOMAIN, device_climate, hass)
thrm_cluster = device_climate.device.endpoints[1].thermostat
@@ -1000,19 +1068,6 @@ async def test_fan_mode(hass, device_climate_fan):
assert state.attributes[ATTR_FAN_MODE] == FAN_ON
async def test_set_fan_mode_no_fan(hass, device_climate):
"""Test setting fan mode on fun less climate."""
entity_id = await find_entity_id(DOMAIN, device_climate, hass)
await hass.services.async_call(
DOMAIN,
SERVICE_SET_FAN_MODE,
{ATTR_ENTITY_ID: entity_id, ATTR_FAN_MODE: FAN_ON},
blocking=True,
)
async def test_set_fan_mode_not_supported(hass, device_climate_fan):
"""Test fan setting unsupported mode."""
+1 -1
View File
@@ -3349,7 +3349,7 @@ DEVICES = [
},
("climate", "00:11:22:33:44:55:66:77-1"): {
"channels": ["thermostat", "fan"],
"entity_class": "Thermostat",
"entity_class": "ZenWithinThermostat",
"entity_id": "climate.zen_within_zen_01_77665544_fan_thermostat",
},
},