Add HVACMode.OFF to Plugwise Adam (#103360)
parent
e161bb9e41
commit
4700ad7817
|
@ -46,6 +46,8 @@ class PlugwiseClimateEntity(PlugwiseEntity, ClimateEntity):
|
|||
_attr_temperature_unit = UnitOfTemperature.CELSIUS
|
||||
_attr_translation_key = DOMAIN
|
||||
|
||||
_previous_mode: str = "heating"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: PlugwiseDataUpdateCoordinator,
|
||||
|
@ -55,10 +57,15 @@ class PlugwiseClimateEntity(PlugwiseEntity, ClimateEntity):
|
|||
super().__init__(coordinator, device_id)
|
||||
self._attr_extra_state_attributes = {}
|
||||
self._attr_unique_id = f"{device_id}-climate"
|
||||
|
||||
self.cdr_gateway = coordinator.data.gateway
|
||||
gateway_id: str = coordinator.data.gateway["gateway_id"]
|
||||
self.gateway_data = coordinator.data.devices[gateway_id]
|
||||
# Determine supported features
|
||||
self._attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE
|
||||
if self.coordinator.data.gateway["cooling_present"]:
|
||||
if (
|
||||
self.cdr_gateway["cooling_present"]
|
||||
and self.cdr_gateway["smile_name"] != "Adam"
|
||||
):
|
||||
self._attr_supported_features = (
|
||||
ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
|
||||
)
|
||||
|
@ -73,6 +80,20 @@ class PlugwiseClimateEntity(PlugwiseEntity, ClimateEntity):
|
|||
self.device["thermostat"]["resolution"], 0.1
|
||||
)
|
||||
|
||||
def _previous_action_mode(self, coordinator: PlugwiseDataUpdateCoordinator) -> None:
|
||||
"""Return the previous action-mode when the regulation-mode is not heating or cooling.
|
||||
|
||||
Helper for set_hvac_mode().
|
||||
"""
|
||||
# When no cooling available, _previous_mode is always heating
|
||||
if (
|
||||
"regulation_modes" in self.gateway_data
|
||||
and "cooling" in self.gateway_data["regulation_modes"]
|
||||
):
|
||||
mode = self.gateway_data["select_regulation_mode"]
|
||||
if mode in ("cooling", "heating"):
|
||||
self._previous_mode = mode
|
||||
|
||||
@property
|
||||
def current_temperature(self) -> float:
|
||||
"""Return the current temperature."""
|
||||
|
@ -105,33 +126,46 @@ class PlugwiseClimateEntity(PlugwiseEntity, ClimateEntity):
|
|||
|
||||
@property
|
||||
def hvac_mode(self) -> HVACMode:
|
||||
"""Return HVAC operation ie. auto, heat, or heat_cool mode."""
|
||||
"""Return HVAC operation ie. auto, cool, heat, heat_cool, or off mode."""
|
||||
if (mode := self.device.get("mode")) is None or mode not in self.hvac_modes:
|
||||
return HVACMode.HEAT
|
||||
return HVACMode(mode)
|
||||
|
||||
@property
|
||||
def hvac_modes(self) -> list[HVACMode]:
|
||||
"""Return the list of available HVACModes."""
|
||||
hvac_modes = [HVACMode.HEAT]
|
||||
if self.coordinator.data.gateway["cooling_present"]:
|
||||
hvac_modes = [HVACMode.HEAT_COOL]
|
||||
"""Return a list of available HVACModes."""
|
||||
hvac_modes: list[HVACMode] = []
|
||||
if "regulation_modes" in self.gateway_data:
|
||||
hvac_modes.append(HVACMode.OFF)
|
||||
|
||||
if self.device["available_schedules"] != ["None"]:
|
||||
hvac_modes.append(HVACMode.AUTO)
|
||||
|
||||
if self.cdr_gateway["cooling_present"]:
|
||||
if "regulation_modes" in self.gateway_data:
|
||||
if self.gateway_data["select_regulation_mode"] == "cooling":
|
||||
hvac_modes.append(HVACMode.COOL)
|
||||
if self.gateway_data["select_regulation_mode"] == "heating":
|
||||
hvac_modes.append(HVACMode.HEAT)
|
||||
else:
|
||||
hvac_modes.append(HVACMode.HEAT_COOL)
|
||||
else:
|
||||
hvac_modes.append(HVACMode.HEAT)
|
||||
|
||||
return hvac_modes
|
||||
|
||||
@property
|
||||
def hvac_action(self) -> HVACAction | None:
|
||||
def hvac_action(self) -> HVACAction:
|
||||
"""Return the current running hvac operation if supported."""
|
||||
heater: str | None = self.coordinator.data.gateway["heater_id"]
|
||||
if heater:
|
||||
heater_data = self.coordinator.data.devices[heater]
|
||||
if heater_data["binary_sensors"]["heating_state"]:
|
||||
return HVACAction.HEATING
|
||||
if heater_data["binary_sensors"].get("cooling_state"):
|
||||
return HVACAction.COOLING
|
||||
# Keep track of the previous action-mode
|
||||
self._previous_action_mode(self.coordinator)
|
||||
|
||||
heater: str = self.coordinator.data.gateway["heater_id"]
|
||||
heater_data = self.coordinator.data.devices[heater]
|
||||
if heater_data["binary_sensors"]["heating_state"]:
|
||||
return HVACAction.HEATING
|
||||
if heater_data["binary_sensors"].get("cooling_state", False):
|
||||
return HVACAction.COOLING
|
||||
|
||||
return HVACAction.IDLE
|
||||
|
||||
|
@ -168,9 +202,18 @@ class PlugwiseClimateEntity(PlugwiseEntity, ClimateEntity):
|
|||
if hvac_mode not in self.hvac_modes:
|
||||
raise HomeAssistantError("Unsupported hvac_mode")
|
||||
|
||||
await self.coordinator.api.set_schedule_state(
|
||||
self.device["location"], "on" if hvac_mode == HVACMode.AUTO else "off"
|
||||
)
|
||||
if hvac_mode == self.hvac_mode:
|
||||
return
|
||||
|
||||
if hvac_mode == HVACMode.OFF:
|
||||
await self.coordinator.api.set_regulation_mode(hvac_mode)
|
||||
else:
|
||||
await self.coordinator.api.set_schedule_state(
|
||||
self.device["location"],
|
||||
"on" if hvac_mode == HVACMode.AUTO else "off",
|
||||
)
|
||||
if self.hvac_mode == HVACMode.OFF:
|
||||
await self.coordinator.api.set_regulation_mode(self._previous_mode)
|
||||
|
||||
@plugwise_command
|
||||
async def async_set_preset_mode(self, preset_mode: str) -> None:
|
||||
|
|
|
@ -7,6 +7,6 @@
|
|||
"integration_type": "hub",
|
||||
"iot_class": "local_polling",
|
||||
"loggers": ["crcmod", "plugwise"],
|
||||
"requirements": ["plugwise==0.33.2"],
|
||||
"requirements": ["plugwise==0.34.0"],
|
||||
"zeroconf": ["_plugwise._tcp.local."]
|
||||
}
|
||||
|
|
|
@ -1477,7 +1477,7 @@ plexauth==0.0.6
|
|||
plexwebsocket==0.0.14
|
||||
|
||||
# homeassistant.components.plugwise
|
||||
plugwise==0.33.2
|
||||
plugwise==0.34.0
|
||||
|
||||
# homeassistant.components.plum_lightpad
|
||||
plumlightpad==0.0.11
|
||||
|
|
|
@ -1135,7 +1135,7 @@ plexauth==0.0.6
|
|||
plexwebsocket==0.0.14
|
||||
|
||||
# homeassistant.components.plugwise
|
||||
plugwise==0.33.2
|
||||
plugwise==0.34.0
|
||||
|
||||
# homeassistant.components.plum_lightpad
|
||||
plumlightpad==0.0.11
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
"firmware": "2016-10-27T02:00:00+02:00",
|
||||
"hardware": "255",
|
||||
"location": "06aecb3d00354375924f50c47af36bd2",
|
||||
"mode": "heat",
|
||||
"mode": "off",
|
||||
"model": "Lisa",
|
||||
"name": "Slaapkamer",
|
||||
"preset_modes": ["home", "asleep", "away", "vacation", "no_frost"],
|
||||
|
|
|
@ -55,22 +55,20 @@
|
|||
"available_schedules": ["Weekschema", "Badkamer", "Test"],
|
||||
"dev_class": "thermostat",
|
||||
"location": "f2bf9048bef64cc5b6d5110154e33c81",
|
||||
"mode": "heat_cool",
|
||||
"mode": "cool",
|
||||
"model": "ThermoTouch",
|
||||
"name": "Anna",
|
||||
"preset_modes": ["home", "asleep", "away", "vacation", "no_frost"],
|
||||
"select_schedule": "Weekschema",
|
||||
"selected_schedule": "None",
|
||||
"sensors": {
|
||||
"setpoint_high": 23.5,
|
||||
"setpoint_low": 4.0,
|
||||
"setpoint": 23.5,
|
||||
"temperature": 25.8
|
||||
},
|
||||
"thermostat": {
|
||||
"lower_bound": 1.0,
|
||||
"resolution": 0.01,
|
||||
"setpoint_high": 23.5,
|
||||
"setpoint_low": 4.0,
|
||||
"setpoint": 23.5,
|
||||
"upper_bound": 35.0
|
||||
},
|
||||
"vendor": "Plugwise"
|
||||
|
@ -115,9 +113,8 @@
|
|||
"select_schedule": "Badkamer",
|
||||
"sensors": {
|
||||
"battery": 56,
|
||||
"setpoint_high": 23.5,
|
||||
"setpoint_low": 20.0,
|
||||
"temperature": 239
|
||||
"setpoint": 23.5,
|
||||
"temperature": 23.9
|
||||
},
|
||||
"temperature_offset": {
|
||||
"lower_bound": -2.0,
|
||||
|
@ -128,8 +125,7 @@
|
|||
"thermostat": {
|
||||
"lower_bound": 0.0,
|
||||
"resolution": 0.01,
|
||||
"setpoint_high": 25.0,
|
||||
"setpoint_low": 19.0,
|
||||
"setpoint": 25.0,
|
||||
"upper_bound": 99.9
|
||||
},
|
||||
"vendor": "Plugwise",
|
||||
|
|
|
@ -13,6 +13,10 @@ from homeassistant.util.dt import utcnow
|
|||
|
||||
from tests.common import MockConfigEntry, async_fire_time_changed
|
||||
|
||||
HA_PLUGWISE_SMILE_ASYNC_UPDATE = (
|
||||
"homeassistant.components.plugwise.coordinator.Smile.async_update"
|
||||
)
|
||||
|
||||
|
||||
async def test_adam_climate_entity_attributes(
|
||||
hass: HomeAssistant, mock_smile_adam: MagicMock, init_integration: MockConfigEntry
|
||||
|
@ -21,7 +25,7 @@ async def test_adam_climate_entity_attributes(
|
|||
state = hass.states.get("climate.zone_lisa_wk")
|
||||
assert state
|
||||
assert state.state == HVACMode.AUTO
|
||||
assert state.attributes["hvac_modes"] == [HVACMode.HEAT, HVACMode.AUTO]
|
||||
assert state.attributes["hvac_modes"] == [HVACMode.AUTO, HVACMode.HEAT]
|
||||
# hvac_action is not asserted as the fixture is not in line with recent firmware functionality
|
||||
|
||||
assert "preset_modes" in state.attributes
|
||||
|
@ -39,7 +43,7 @@ async def test_adam_climate_entity_attributes(
|
|||
state = hass.states.get("climate.zone_thermostat_jessie")
|
||||
assert state
|
||||
assert state.state == HVACMode.AUTO
|
||||
assert state.attributes["hvac_modes"] == [HVACMode.HEAT, HVACMode.AUTO]
|
||||
assert state.attributes["hvac_modes"] == [HVACMode.AUTO, HVACMode.HEAT]
|
||||
# hvac_action is not asserted as the fixture is not in line with recent firmware functionality
|
||||
|
||||
assert "preset_modes" in state.attributes
|
||||
|
@ -62,13 +66,21 @@ async def test_adam_2_climate_entity_attributes(
|
|||
assert state
|
||||
assert state.state == HVACMode.HEAT
|
||||
assert state.attributes["hvac_action"] == "heating"
|
||||
assert state.attributes["hvac_modes"] == [HVACMode.HEAT, HVACMode.AUTO]
|
||||
assert state.attributes["hvac_modes"] == [
|
||||
HVACMode.OFF,
|
||||
HVACMode.AUTO,
|
||||
HVACMode.HEAT,
|
||||
]
|
||||
|
||||
state = hass.states.get("climate.lisa_badkamer")
|
||||
assert state
|
||||
assert state.state == HVACMode.AUTO
|
||||
assert state.attributes["hvac_action"] == "heating"
|
||||
assert state.attributes["hvac_modes"] == [HVACMode.HEAT, HVACMode.AUTO]
|
||||
assert state.attributes["hvac_modes"] == [
|
||||
HVACMode.OFF,
|
||||
HVACMode.AUTO,
|
||||
HVACMode.HEAT,
|
||||
]
|
||||
|
||||
|
||||
async def test_adam_3_climate_entity_attributes(
|
||||
|
@ -78,11 +90,58 @@ async def test_adam_3_climate_entity_attributes(
|
|||
state = hass.states.get("climate.anna")
|
||||
|
||||
assert state
|
||||
assert state.state == HVACMode.HEAT_COOL
|
||||
assert state.state == HVACMode.COOL
|
||||
assert state.attributes["hvac_action"] == "cooling"
|
||||
assert state.attributes["hvac_modes"] == [
|
||||
HVACMode.HEAT_COOL,
|
||||
HVACMode.OFF,
|
||||
HVACMode.AUTO,
|
||||
HVACMode.COOL,
|
||||
]
|
||||
data = mock_smile_adam_3.async_update.return_value
|
||||
data.devices["da224107914542988a88561b4452b0f6"][
|
||||
"select_regulation_mode"
|
||||
] = "heating"
|
||||
data.devices["ad4838d7d35c4d6ea796ee12ae5aedf8"]["mode"] = "heat"
|
||||
data.devices["056ee145a816487eaa69243c3280f8bf"]["binary_sensors"][
|
||||
"cooling_state"
|
||||
] = False
|
||||
data.devices["056ee145a816487eaa69243c3280f8bf"]["binary_sensors"][
|
||||
"heating_state"
|
||||
] = True
|
||||
with patch(HA_PLUGWISE_SMILE_ASYNC_UPDATE, return_value=data):
|
||||
async_fire_time_changed(hass, utcnow() + timedelta(minutes=1))
|
||||
await hass.async_block_till_done()
|
||||
state = hass.states.get("climate.anna")
|
||||
assert state
|
||||
assert state.state == HVACMode.HEAT
|
||||
assert state.attributes["hvac_action"] == "heating"
|
||||
assert state.attributes["hvac_modes"] == [
|
||||
HVACMode.OFF,
|
||||
HVACMode.AUTO,
|
||||
HVACMode.HEAT,
|
||||
]
|
||||
data = mock_smile_adam_3.async_update.return_value
|
||||
data.devices["da224107914542988a88561b4452b0f6"][
|
||||
"select_regulation_mode"
|
||||
] = "cooling"
|
||||
data.devices["ad4838d7d35c4d6ea796ee12ae5aedf8"]["mode"] = "cool"
|
||||
data.devices["056ee145a816487eaa69243c3280f8bf"]["binary_sensors"][
|
||||
"cooling_state"
|
||||
] = True
|
||||
data.devices["056ee145a816487eaa69243c3280f8bf"]["binary_sensors"][
|
||||
"heating_state"
|
||||
] = False
|
||||
with patch(HA_PLUGWISE_SMILE_ASYNC_UPDATE, return_value=data):
|
||||
async_fire_time_changed(hass, utcnow() + timedelta(minutes=1))
|
||||
await hass.async_block_till_done()
|
||||
state = hass.states.get("climate.anna")
|
||||
assert state
|
||||
assert state.state == HVACMode.COOL
|
||||
assert state.attributes["hvac_action"] == "cooling"
|
||||
assert state.attributes["hvac_modes"] == [
|
||||
HVACMode.OFF,
|
||||
HVACMode.AUTO,
|
||||
HVACMode.COOL,
|
||||
]
|
||||
|
||||
|
||||
|
@ -173,6 +232,60 @@ async def test_adam_climate_entity_climate_changes(
|
|||
)
|
||||
|
||||
|
||||
async def test_adam_climate_off_mode_change(
|
||||
hass: HomeAssistant,
|
||||
mock_smile_adam_4: MagicMock,
|
||||
init_integration: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test handling of user requests in adam climate device environment."""
|
||||
state = hass.states.get("climate.slaapkamer")
|
||||
assert state
|
||||
assert state.state == HVACMode.OFF
|
||||
await hass.services.async_call(
|
||||
"climate",
|
||||
"set_hvac_mode",
|
||||
{
|
||||
"entity_id": "climate.slaapkamer",
|
||||
"hvac_mode": "heat",
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
assert mock_smile_adam_4.set_schedule_state.call_count == 1
|
||||
assert mock_smile_adam_4.set_regulation_mode.call_count == 1
|
||||
mock_smile_adam_4.set_regulation_mode.assert_called_with("heating")
|
||||
|
||||
state = hass.states.get("climate.kinderkamer")
|
||||
assert state
|
||||
assert state.state == HVACMode.HEAT
|
||||
await hass.services.async_call(
|
||||
"climate",
|
||||
"set_hvac_mode",
|
||||
{
|
||||
"entity_id": "climate.kinderkamer",
|
||||
"hvac_mode": "off",
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
assert mock_smile_adam_4.set_schedule_state.call_count == 1
|
||||
assert mock_smile_adam_4.set_regulation_mode.call_count == 2
|
||||
mock_smile_adam_4.set_regulation_mode.assert_called_with("off")
|
||||
|
||||
state = hass.states.get("climate.logeerkamer")
|
||||
assert state
|
||||
assert state.state == HVACMode.HEAT
|
||||
await hass.services.async_call(
|
||||
"climate",
|
||||
"set_hvac_mode",
|
||||
{
|
||||
"entity_id": "climate.logeerkamer",
|
||||
"hvac_mode": "heat",
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
assert mock_smile_adam_4.set_schedule_state.call_count == 1
|
||||
assert mock_smile_adam_4.set_regulation_mode.call_count == 2
|
||||
|
||||
|
||||
async def test_anna_climate_entity_attributes(
|
||||
hass: HomeAssistant,
|
||||
mock_smile_anna: MagicMock,
|
||||
|
@ -183,10 +296,7 @@ async def test_anna_climate_entity_attributes(
|
|||
assert state
|
||||
assert state.state == HVACMode.AUTO
|
||||
assert state.attributes["hvac_action"] == "heating"
|
||||
assert state.attributes["hvac_modes"] == [
|
||||
HVACMode.HEAT,
|
||||
HVACMode.AUTO,
|
||||
]
|
||||
assert state.attributes["hvac_modes"] == [HVACMode.AUTO, HVACMode.HEAT]
|
||||
|
||||
assert "no_frost" in state.attributes["preset_modes"]
|
||||
assert "home" in state.attributes["preset_modes"]
|
||||
|
@ -211,8 +321,8 @@ async def test_anna_2_climate_entity_attributes(
|
|||
assert state.state == HVACMode.AUTO
|
||||
assert state.attributes["hvac_action"] == "cooling"
|
||||
assert state.attributes["hvac_modes"] == [
|
||||
HVACMode.HEAT_COOL,
|
||||
HVACMode.AUTO,
|
||||
HVACMode.HEAT_COOL,
|
||||
]
|
||||
assert state.attributes["supported_features"] == 18
|
||||
assert state.attributes["target_temp_high"] == 24.0
|
||||
|
@ -230,8 +340,8 @@ async def test_anna_3_climate_entity_attributes(
|
|||
assert state.state == HVACMode.AUTO
|
||||
assert state.attributes["hvac_action"] == "idle"
|
||||
assert state.attributes["hvac_modes"] == [
|
||||
HVACMode.HEAT_COOL,
|
||||
HVACMode.AUTO,
|
||||
HVACMode.HEAT_COOL,
|
||||
]
|
||||
|
||||
|
||||
|
@ -270,10 +380,8 @@ async def test_anna_climate_entity_climate_changes(
|
|||
{"entity_id": "climate.anna", "hvac_mode": "auto"},
|
||||
blocking=True,
|
||||
)
|
||||
assert mock_smile_anna.set_schedule_state.call_count == 1
|
||||
mock_smile_anna.set_schedule_state.assert_called_with(
|
||||
"c784ee9fdab44e1395b8dee7d7a497d5", "on"
|
||||
)
|
||||
# hvac_mode is already auto so not called.
|
||||
assert mock_smile_anna.set_schedule_state.call_count == 0
|
||||
|
||||
await hass.services.async_call(
|
||||
"climate",
|
||||
|
@ -281,16 +389,13 @@ async def test_anna_climate_entity_climate_changes(
|
|||
{"entity_id": "climate.anna", "hvac_mode": "heat"},
|
||||
blocking=True,
|
||||
)
|
||||
assert mock_smile_anna.set_schedule_state.call_count == 2
|
||||
assert mock_smile_anna.set_schedule_state.call_count == 1
|
||||
mock_smile_anna.set_schedule_state.assert_called_with(
|
||||
"c784ee9fdab44e1395b8dee7d7a497d5", "off"
|
||||
)
|
||||
data = mock_smile_anna.async_update.return_value
|
||||
data.devices["3cb70739631c4d17a86b8b12e8a5161b"]["available_schedules"] = ["None"]
|
||||
with patch(
|
||||
"homeassistant.components.plugwise.coordinator.Smile.async_update",
|
||||
return_value=data,
|
||||
):
|
||||
with patch(HA_PLUGWISE_SMILE_ASYNC_UPDATE, return_value=data):
|
||||
async_fire_time_changed(hass, utcnow() + timedelta(minutes=1))
|
||||
await hass.async_block_till_done()
|
||||
state = hass.states.get("climate.anna")
|
||||
|
|
|
@ -16,7 +16,7 @@ from tests.common import MockConfigEntry
|
|||
async def test_adam_select_entities(
|
||||
hass: HomeAssistant, mock_smile_adam: MagicMock, init_integration: MockConfigEntry
|
||||
) -> None:
|
||||
"""Test a select."""
|
||||
"""Test a thermostat Select."""
|
||||
|
||||
state = hass.states.get("select.zone_lisa_wk_thermostat_schedule")
|
||||
assert state
|
||||
|
@ -44,3 +44,27 @@ async def test_adam_change_select_entity(
|
|||
"on",
|
||||
"Badkamer Schema",
|
||||
)
|
||||
|
||||
|
||||
async def test_adam_select_regulation_mode(
|
||||
hass: HomeAssistant, mock_smile_adam_3: MagicMock, init_integration: MockConfigEntry
|
||||
) -> None:
|
||||
"""Test a regulation_mode select.
|
||||
|
||||
Also tests a change in climate _previous mode.
|
||||
"""
|
||||
|
||||
state = hass.states.get("select.adam_regulation_mode")
|
||||
assert state
|
||||
assert state.state == "cooling"
|
||||
await hass.services.async_call(
|
||||
SELECT_DOMAIN,
|
||||
SERVICE_SELECT_OPTION,
|
||||
{
|
||||
"entity_id": "select.adam_regulation_mode",
|
||||
"option": "heating",
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
assert mock_smile_adam_3.set_regulation_mode.call_count == 1
|
||||
mock_smile_adam_3.set_regulation_mode.assert_called_with("heating")
|
||||
|
|
Loading…
Reference in New Issue