Bump plugwise to v0.21.3, add related new features (#76610)

Co-authored-by: Franck Nijhof <frenck@frenck.nl>
This commit is contained in:
Bouwe Westerdijk 2022-09-26 15:55:29 +02:00 committed by GitHub
parent 75f6f9b5e2
commit 2667f0b792
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 828 additions and 132 deletions

View file

@ -13,9 +13,10 @@ from homeassistant.components.climate import (
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import DEFAULT_MAX_TEMP, DEFAULT_MIN_TEMP, DOMAIN, THERMOSTAT_CLASSES
from .const import DOMAIN, MASTER_THERMOSTATS
from .coordinator import PlugwiseDataUpdateCoordinator
from .entity import PlugwiseEntity
from .util import plugwise_command
@ -31,7 +32,7 @@ async def async_setup_entry(
async_add_entities(
PlugwiseClimateEntity(coordinator, device_id)
for device_id, device in coordinator.data.devices.items()
if device["dev_class"] in THERMOSTAT_CLASSES
if device["dev_class"] in MASTER_THERMOSTATS
)
@ -59,26 +60,27 @@ class PlugwiseClimateEntity(PlugwiseEntity, ClimateEntity):
# Determine hvac modes and current hvac mode
self._attr_hvac_modes = [HVACMode.HEAT]
if self.coordinator.data.gateway.get("cooling_present"):
if self.coordinator.data.gateway["cooling_present"]:
self._attr_hvac_modes.append(HVACMode.COOL)
if self.device.get("available_schedules") != ["None"]:
if self.device["available_schedules"] != ["None"]:
self._attr_hvac_modes.append(HVACMode.AUTO)
self._attr_min_temp = self.device.get("lower_bound", DEFAULT_MIN_TEMP)
self._attr_max_temp = self.device.get("upper_bound", DEFAULT_MAX_TEMP)
if resolution := self.device.get("resolution"):
# Ensure we don't drop below 0.1
self._attr_target_temperature_step = max(resolution, 0.1)
self._attr_min_temp = self.device["thermostat"]["lower_bound"]
self._attr_max_temp = self.device["thermostat"]["upper_bound"]
# Ensure we don't drop below 0.1
self._attr_target_temperature_step = max(
self.device["thermostat"]["resolution"], 0.1
)
@property
def current_temperature(self) -> float | None:
def current_temperature(self) -> float:
"""Return the current temperature."""
return self.device["sensors"].get("temperature")
return self.device["sensors"]["temperature"]
@property
def target_temperature(self) -> float | None:
def target_temperature(self) -> float:
"""Return the temperature we try to reach."""
return self.device["sensors"].get("setpoint")
return self.device["thermostat"]["setpoint"]
@property
def hvac_mode(self) -> HVACMode:
@ -88,23 +90,24 @@ class PlugwiseClimateEntity(PlugwiseEntity, ClimateEntity):
return HVACMode(mode)
@property
def hvac_action(self) -> HVACAction:
def hvac_action(self) -> HVACAction | None:
"""Return the current running hvac operation if supported."""
# When control_state is present, prefer this data
if "control_state" in self.device:
if self.device.get("control_state") == "cooling":
return HVACAction.COOLING
# Support preheating state as heating, until preheating is added as a separate state
if self.device.get("control_state") in ["heating", "preheating"]:
return HVACAction.HEATING
else:
heater_central_data = self.coordinator.data.devices[
self.coordinator.data.gateway["heater_id"]
]
if heater_central_data["binary_sensors"].get("heating_state"):
return HVACAction.HEATING
if heater_central_data["binary_sensors"].get("cooling_state"):
return HVACAction.COOLING
if (control_state := self.device.get("control_state")) == "cooling":
return HVACAction.COOLING
# Support preheating state as heating, until preheating is added as a separate state
if control_state in ["heating", "preheating"]:
return HVACAction.HEATING
if control_state == "off":
return HVACAction.IDLE
hc_data = self.coordinator.data.devices[
self.coordinator.data.gateway["heater_id"]
]
if hc_data["binary_sensors"]["heating_state"]:
return HVACAction.HEATING
if hc_data["binary_sensors"].get("cooling_state"):
return HVACAction.COOLING
return HVACAction.IDLE
@ -117,25 +120,29 @@ class PlugwiseClimateEntity(PlugwiseEntity, ClimateEntity):
def extra_state_attributes(self) -> Mapping[str, Any] | None:
"""Return entity specific state attributes."""
return {
"available_schemas": self.device.get("available_schedules"),
"selected_schema": self.device.get("selected_schedule"),
"available_schemas": self.device["available_schedules"],
"selected_schema": self.device["selected_schedule"],
}
@plugwise_command
async def async_set_temperature(self, **kwargs: Any) -> None:
"""Set new target temperature."""
if ((temperature := kwargs.get(ATTR_TEMPERATURE)) is None) or (
self._attr_max_temp < temperature < self._attr_min_temp
if ((temperature := kwargs.get(ATTR_TEMPERATURE)) is None) or not (
self._attr_min_temp <= temperature <= self._attr_max_temp
):
raise ValueError("Invalid temperature requested")
raise ValueError("Invalid temperature change requested")
await self.coordinator.api.set_temperature(self.device["location"], temperature)
@plugwise_command
async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
"""Set the hvac mode."""
if hvac_mode not in self.hvac_modes:
raise HomeAssistantError("Unsupported hvac_mode")
await self.coordinator.api.set_schedule_state(
self.device["location"],
self.device.get("last_used"),
self.device["last_used"],
"on" if hvac_mode == HVACMode.AUTO else "off",
)

View file

@ -48,7 +48,7 @@ DEFAULT_SCAN_INTERVAL: Final[dict[str, timedelta]] = {
}
DEFAULT_USERNAME: Final = "smile"
THERMOSTAT_CLASSES: Final[list[str]] = [
MASTER_THERMOSTATS: Final[list[str]] = [
"thermostat",
"thermostatic_radiator_valve",
"zone_thermometer",

View file

@ -2,7 +2,7 @@
"domain": "plugwise",
"name": "Plugwise",
"documentation": "https://www.home-assistant.io/integrations/plugwise",
"requirements": ["plugwise==0.18.7"],
"requirements": ["plugwise==0.21.3"],
"codeowners": ["@CoMPaTech", "@bouwew", "@brefra", "@frenck"],
"zeroconf": ["_plugwise._tcp.local."],
"config_flow": true,

View file

@ -27,7 +27,11 @@ from .entity import PlugwiseEntity
class PlugwiseEntityDescriptionMixin:
"""Mixin values for Plugwse entities."""
command: Callable[[Smile, float], Awaitable[None]]
command: Callable[[Smile, str, float], Awaitable[None]]
native_max_value_key: str
native_min_value_key: str
native_step_key: str
native_value_key: str
@dataclass
@ -40,11 +44,15 @@ class PlugwiseNumberEntityDescription(
NUMBER_TYPES = (
PlugwiseNumberEntityDescription(
key="maximum_boiler_temperature",
command=lambda api, value: api.set_max_boiler_temperature(value),
command=lambda api, number, value: api.set_number_setpoint(number, value),
device_class=NumberDeviceClass.TEMPERATURE,
name="Maximum boiler temperature setpoint",
entity_category=EntityCategory.CONFIG,
native_max_value_key="upper_bound",
native_min_value_key="lower_bound",
native_step_key="resolution",
native_unit_of_measurement=TEMP_CELSIUS,
native_value_key="setpoint",
),
)
@ -91,24 +99,37 @@ class PlugwiseNumberEntity(PlugwiseEntity, NumberEntity):
@property
def native_step(self) -> float:
"""Return the setpoint step value."""
return max(self.device["resolution"], 1)
return max(
self.device[self.entity_description.key][
self.entity_description.native_step_key
],
1,
)
@property
def native_value(self) -> float:
"""Return the present setpoint value."""
return self.device[self.entity_description.key]
return self.device[self.entity_description.key][
self.entity_description.native_value_key
]
@property
def native_min_value(self) -> float:
"""Return the setpoint min. value."""
return self.device["lower_bound"]
return self.device[self.entity_description.key][
self.entity_description.native_min_value_key
]
@property
def native_max_value(self) -> float:
"""Return the setpoint max. value."""
return self.device["upper_bound"]
return self.device[self.entity_description.key][
self.entity_description.native_max_value_key
]
async def async_set_native_value(self, value: float) -> None:
"""Change to the new setpoint value."""
await self.entity_description.command(self.coordinator.api, value)
await self.entity_description.command(
self.coordinator.api, self.entity_description.key, value
)
await self.coordinator.async_request_refresh()

View file

@ -1306,7 +1306,7 @@ plexauth==0.0.6
plexwebsocket==0.0.13
# homeassistant.components.plugwise
plugwise==0.18.7
plugwise==0.21.3
# homeassistant.components.plum_lightpad
plumlightpad==0.0.11

View file

@ -930,7 +930,7 @@ plexauth==0.0.6
plexwebsocket==0.0.13
# homeassistant.components.plugwise
plugwise==0.18.7
plugwise==0.21.3
# homeassistant.components.plum_lightpad
plumlightpad==0.0.11

View file

@ -93,10 +93,108 @@ def mock_smile_adam() -> Generator[None, MagicMock, None]:
yield smile
@pytest.fixture
def mock_smile_adam_2() -> Generator[None, MagicMock, None]:
"""Create a 2nd Mock Adam environment for testing exceptions."""
chosen_env = "m_adam_heating"
with patch(
"homeassistant.components.plugwise.gateway.Smile", autospec=True
) as smile_mock:
smile = smile_mock.return_value
smile.gateway_id = "da224107914542988a88561b4452b0f6"
smile.heater_id = "056ee145a816487eaa69243c3280f8bf"
smile.smile_version = "3.6.4"
smile.smile_type = "thermostat"
smile.smile_hostname = "smile98765"
smile.smile_name = "Adam"
smile.connect.return_value = True
smile.notifications = _read_json(chosen_env, "notifications")
smile.async_update.return_value = _read_json(chosen_env, "all_data")
yield smile
@pytest.fixture
def mock_smile_adam_3() -> Generator[None, MagicMock, None]:
"""Create a 3rd Mock Adam environment for testing exceptions."""
chosen_env = "m_adam_cooling"
with patch(
"homeassistant.components.plugwise.gateway.Smile", autospec=True
) as smile_mock:
smile = smile_mock.return_value
smile.gateway_id = "da224107914542988a88561b4452b0f6"
smile.heater_id = "056ee145a816487eaa69243c3280f8bf"
smile.smile_version = "3.6.4"
smile.smile_type = "thermostat"
smile.smile_hostname = "smile98765"
smile.smile_name = "Adam"
smile.connect.return_value = True
smile.notifications = _read_json(chosen_env, "notifications")
smile.async_update.return_value = _read_json(chosen_env, "all_data")
yield smile
@pytest.fixture
def mock_smile_anna() -> Generator[None, MagicMock, None]:
"""Create a Mock Anna environment for testing exceptions."""
chosen_env = "anna_heatpump"
chosen_env = "anna_heatpump_heating"
with patch(
"homeassistant.components.plugwise.gateway.Smile", autospec=True
) as smile_mock:
smile = smile_mock.return_value
smile.gateway_id = "015ae9ea3f964e668e490fa39da3870b"
smile.heater_id = "1cbf783bb11e4a7c8a6843dee3a86927"
smile.smile_version = "4.0.15"
smile.smile_type = "thermostat"
smile.smile_hostname = "smile98765"
smile.smile_name = "Anna"
smile.connect.return_value = True
smile.notifications = _read_json(chosen_env, "notifications")
smile.async_update.return_value = _read_json(chosen_env, "all_data")
yield smile
@pytest.fixture
def mock_smile_anna_2() -> Generator[None, MagicMock, None]:
"""Create a 2nd Mock Anna environment for testing exceptions."""
chosen_env = "m_anna_heatpump_cooling"
with patch(
"homeassistant.components.plugwise.gateway.Smile", autospec=True
) as smile_mock:
smile = smile_mock.return_value
smile.gateway_id = "015ae9ea3f964e668e490fa39da3870b"
smile.heater_id = "1cbf783bb11e4a7c8a6843dee3a86927"
smile.smile_version = "4.0.15"
smile.smile_type = "thermostat"
smile.smile_hostname = "smile98765"
smile.smile_name = "Anna"
smile.connect.return_value = True
smile.notifications = _read_json(chosen_env, "notifications")
smile.async_update.return_value = _read_json(chosen_env, "all_data")
yield smile
@pytest.fixture
def mock_smile_anna_3() -> Generator[None, MagicMock, None]:
"""Create a 3rd Mock Anna environment for testing exceptions."""
chosen_env = "m_anna_heatpump_idle"
with patch(
"homeassistant.components.plugwise.gateway.Smile", autospec=True
) as smile_mock:

View file

@ -20,9 +20,12 @@
"name": "Zone Lisa Bios",
"zigbee_mac_address": "ABCD012345670A06",
"vendor": "Plugwise",
"lower_bound": 0.0,
"upper_bound": 99.9,
"resolution": 0.01,
"thermostat": {
"setpoint": 13.0,
"lower_bound": 0.0,
"upper_bound": 99.9,
"resolution": 0.01
},
"preset_modes": ["home", "asleep", "away", "no_frost"],
"active_preset": "away",
"available_schedules": [
@ -50,9 +53,6 @@
"name": "Floor kraan",
"zigbee_mac_address": "ABCD012345670A02",
"vendor": "Plugwise",
"lower_bound": 0.0,
"upper_bound": 100.0,
"resolution": 0.01,
"sensors": {
"temperature": 26.0,
"setpoint": 21.5,
@ -69,9 +69,6 @@
"name": "Bios Cv Thermostatic Radiator ",
"zigbee_mac_address": "ABCD012345670A09",
"vendor": "Plugwise",
"lower_bound": 0.0,
"upper_bound": 100.0,
"resolution": 0.01,
"sensors": {
"temperature": 17.2,
"setpoint": 13.0,
@ -89,9 +86,12 @@
"name": "Zone Lisa WK",
"zigbee_mac_address": "ABCD012345670A07",
"vendor": "Plugwise",
"lower_bound": 0.0,
"upper_bound": 99.9,
"resolution": 0.01,
"thermostat": {
"setpoint": 21.5,
"lower_bound": 0.0,
"upper_bound": 99.9,
"resolution": 0.01
},
"preset_modes": ["home", "asleep", "away", "no_frost"],
"active_preset": "home",
"available_schedules": [
@ -138,9 +138,6 @@
"name": "Thermostatic Radiator Jessie",
"zigbee_mac_address": "ABCD012345670A10",
"vendor": "Plugwise",
"lower_bound": 0.0,
"upper_bound": 100.0,
"resolution": 0.01,
"sensors": {
"temperature": 17.1,
"setpoint": 15.0,
@ -285,9 +282,12 @@
"name": "Zone Thermostat Jessie",
"zigbee_mac_address": "ABCD012345670A03",
"vendor": "Plugwise",
"lower_bound": 0.0,
"upper_bound": 99.9,
"resolution": 0.01,
"thermostat": {
"setpoint": 15.0,
"lower_bound": 0.0,
"upper_bound": 99.9,
"resolution": 0.01
},
"preset_modes": ["home", "asleep", "away", "no_frost"],
"active_preset": "asleep",
"available_schedules": [
@ -315,9 +315,6 @@
"name": "Thermostatic Radiator Badkamer",
"zigbee_mac_address": "ABCD012345670A17",
"vendor": "Plugwise",
"lower_bound": 0.0,
"upper_bound": 100.0,
"resolution": 0.01,
"sensors": {
"temperature": 19.1,
"setpoint": 14.0,
@ -335,9 +332,12 @@
"name": "Zone Thermostat Badkamer",
"zigbee_mac_address": "ABCD012345670A08",
"vendor": "Plugwise",
"lower_bound": 0.0,
"upper_bound": 99.9,
"resolution": 0.01,
"thermostat": {
"setpoint": 14.0,
"lower_bound": 0.0,
"upper_bound": 99.9,
"resolution": 0.01
},
"preset_modes": ["home", "asleep", "away", "no_frost"],
"active_preset": "away",
"available_schedules": [
@ -384,9 +384,12 @@
"name": "CV Kraan Garage",
"zigbee_mac_address": "ABCD012345670A11",
"vendor": "Plugwise",
"lower_bound": 0.0,
"upper_bound": 100.0,
"resolution": 0.01,
"thermostat": {
"setpoint": 5.5,
"lower_bound": 0.0,
"upper_bound": 100.0,
"resolution": 0.01
},
"preset_modes": ["home", "asleep", "away", "no_frost"],
"active_preset": "no_frost",
"available_schedules": [

View file

@ -1,6 +1,6 @@
[
{
"smile_name": "Anna",
"smile_name": "Smile",
"gateway_id": "015ae9ea3f964e668e490fa39da3870b",
"heater_id": "1cbf783bb11e4a7c8a6843dee3a86927",
"cooling_present": true,
@ -10,13 +10,16 @@
"1cbf783bb11e4a7c8a6843dee3a86927": {
"dev_class": "heater_central",
"location": "a57efe5f145f498c9be62a9b63626fbf",
"model": "Generic heater",
"model": "Generic heater/cooler",
"name": "OpenTherm",
"vendor": "Techneco",
"lower_bound": 0.0,
"upper_bound": 100.0,
"resolution": 1.0,
"maximum_boiler_temperature": 60.0,
"maximum_boiler_temperature": {
"setpoint": 60.0,
"lower_bound": 0.0,
"upper_bound": 100.0,
"resolution": 1.0
},
"elga_cooling_enabled": true,
"binary_sensors": {
"dhw_state": false,
"heating_state": true,
@ -43,8 +46,8 @@
"hardware": "AME Smile 2.0 board",
"location": "a57efe5f145f498c9be62a9b63626fbf",
"mac_address": "012345670001",
"model": "Anna",
"name": "Anna",
"model": "Smile",
"name": "Smile",
"vendor": "Plugwise B.V.",
"binary_sensors": {
"plugwise_notification": false
@ -61,9 +64,12 @@
"model": "Anna",
"name": "Anna",
"vendor": "Plugwise",
"lower_bound": 4.0,
"upper_bound": 30.0,
"resolution": 0.1,
"thermostat": {
"setpoint": 20.5,
"lower_bound": 4.0,
"upper_bound": 30.0,
"resolution": 0.1
},
"preset_modes": ["no_frost", "home", "away", "asleep", "vacation"],
"active_preset": "home",
"available_schedules": ["standaard"],
@ -72,7 +78,7 @@
"mode": "auto",
"sensors": {
"temperature": 19.3,
"setpoint": 21.0,
"setpoint": 20.5,
"illuminance": 86.0,
"cooling_activation_outdoor_temperature": 21.0,
"cooling_deactivation_threshold": 4.0

View file

@ -0,0 +1,139 @@
[
{
"smile_name": "Adam",
"gateway_id": "da224107914542988a88561b4452b0f6",
"heater_id": "056ee145a816487eaa69243c3280f8bf",
"cooling_present": true,
"notifications": {}
},
{
"ad4838d7d35c4d6ea796ee12ae5aedf8": {
"dev_class": "thermostat",
"location": "f2bf9048bef64cc5b6d5110154e33c81",
"model": "Anna",
"name": "Anna",
"vendor": "Plugwise B.V.",
"thermostat": {
"setpoint": 18.5,
"lower_bound": 1.0,
"upper_bound": 35.0,
"resolution": 0.01
},
"preset_modes": ["home", "asleep", "away", "no_frost"],
"active_preset": "asleep",
"available_schedules": ["Weekschema", "Badkamer", "Test"],
"selected_schedule": "None",
"last_used": "Weekschema",
"control_state": "cooling",
"mode": "cool",
"sensors": { "temperature": 18.1, "setpoint": 18.5 }
},
"1772a4ea304041adb83f357b751341ff": {
"dev_class": "thermo_sensor",
"firmware": "2020-11-04T01:00:00+01:00",
"hardware": "1",
"location": "f871b8c4d63549319221e294e4f88074",
"model": "Tom/Floor",
"name": "Tom Badkamer",
"zigbee_mac_address": "ABCD012345670A01",
"vendor": "Plugwise",
"sensors": {
"temperature": 21.6,
"battery": 99,
"temperature_difference": 2.3,
"valve_position": 0.0
}
},
"e2f4322d57924fa090fbbc48b3a140dc": {
"dev_class": "zone_thermostat",
"firmware": "2016-10-10T02:00:00+02:00",
"hardware": "255",
"location": "f871b8c4d63549319221e294e4f88074",
"model": "Lisa",
"name": "Lisa Badkamer",
"zigbee_mac_address": "ABCD012345670A04",
"vendor": "Plugwise",
"thermostat": {
"setpoint": 15.0,
"lower_bound": 0.0,
"upper_bound": 99.9,
"resolution": 0.01
},
"preset_modes": ["home", "asleep", "away", "no_frost"],
"active_preset": "home",
"available_schedules": ["Weekschema", "Badkamer", "Test"],
"selected_schedule": "Badkamer",
"last_used": "Badkamer",
"control_state": "off",
"mode": "auto",
"sensors": {
"temperature": 17.9,
"battery": 56,
"setpoint": 15.0
}
},
"da224107914542988a88561b4452b0f6": {
"dev_class": "gateway",
"firmware": "3.6.4",
"hardware": "AME Smile 2.0 board",
"location": "bc93488efab249e5bc54fd7e175a6f91",
"mac_address": "012345670001",
"model": "Adam",
"name": "Adam",
"zigbee_mac_address": "ABCD012345670101",
"vendor": "Plugwise B.V.",
"regulation_mode": "cooling",
"regulation_modes": [
"cooling",
"heating",
"off",
"bleeding_cold",
"bleeding_hot"
],
"binary_sensors": {
"plugwise_notification": false
},
"sensors": {
"outdoor_temperature": -1.25
}
},
"056ee145a816487eaa69243c3280f8bf": {
"dev_class": "heater_central",
"location": "bc93488efab249e5bc54fd7e175a6f91",
"model": "Generic heater",
"name": "OpenTherm",
"maximum_boiler_temperature": {
"setpoint": 60.0,
"lower_bound": 25.0,
"upper_bound": 95.0,
"resolution": 0.01
},
"adam_cooling_enabled": true,
"binary_sensors": {
"cooling_state": true,
"dhw_state": false,
"heating_state": false,
"flame_state": false
},
"sensors": {
"water_temperature": 37.0,
"intended_boiler_temperature": 38.1
},
"switches": {
"dhw_cm_switch": false
}
},
"e8ef2a01ed3b4139a53bf749204fe6b4": {
"dev_class": "switching",
"model": "Switchgroup",
"name": "Test",
"members": [
"2568cc4b9c1e401495d4741a5f89bee1",
"29542b2b6a6a4169acecc15c72a599b8"
],
"switches": {
"relay": true
}
}
}
]

View file

@ -0,0 +1,137 @@
[
{
"smile_name": "Adam",
"gateway_id": "da224107914542988a88561b4452b0f6",
"heater_id": "056ee145a816487eaa69243c3280f8bf",
"cooling_present": false,
"notifications": {}
},
{
"ad4838d7d35c4d6ea796ee12ae5aedf8": {
"dev_class": "thermostat",
"location": "f2bf9048bef64cc5b6d5110154e33c81",
"model": "Anna",
"name": "Anna",
"vendor": "Plugwise B.V.",
"thermostat": {
"setpoint": 18.5,
"lower_bound": 1.0,
"upper_bound": 35.0,
"resolution": 0.01
},
"preset_modes": ["home", "asleep", "away", "no_frost"],
"active_preset": "asleep",
"available_schedules": ["Weekschema", "Badkamer", "Test"],
"selected_schedule": "None",
"last_used": "Weekschema",
"control_state": "heating",
"mode": "heat",
"sensors": { "temperature": 18.1, "setpoint": 18.5 }
},
"1772a4ea304041adb83f357b751341ff": {
"dev_class": "thermo_sensor",
"firmware": "2020-11-04T01:00:00+01:00",
"hardware": "1",
"location": "f871b8c4d63549319221e294e4f88074",
"model": "Tom/Floor",
"name": "Tom Badkamer",
"zigbee_mac_address": "ABCD012345670A01",
"vendor": "Plugwise",
"sensors": {
"temperature": 21.6,
"battery": 99,
"temperature_difference": 2.3,
"valve_position": 0.0
}
},
"e2f4322d57924fa090fbbc48b3a140dc": {
"dev_class": "zone_thermostat",
"firmware": "2016-10-10T02:00:00+02:00",
"hardware": "255",
"location": "f871b8c4d63549319221e294e4f88074",
"model": "Lisa",
"name": "Lisa Badkamer",
"zigbee_mac_address": "ABCD012345670A04",
"vendor": "Plugwise",
"thermostat": {
"setpoint": 15.0,
"lower_bound": 0.0,
"upper_bound": 99.9,
"resolution": 0.01
},
"preset_modes": ["home", "asleep", "away", "no_frost"],
"active_preset": "home",
"available_schedules": ["Weekschema", "Badkamer", "Test"],
"selected_schedule": "Badkamer",
"last_used": "Badkamer",
"control_state": "off",
"mode": "auto",
"sensors": {
"temperature": 17.9,
"battery": 56,
"setpoint": 15.0
}
},
"da224107914542988a88561b4452b0f6": {
"dev_class": "gateway",
"firmware": "3.6.4",
"hardware": "AME Smile 2.0 board",
"location": "bc93488efab249e5bc54fd7e175a6f91",
"mac_address": "012345670001",
"model": "Adam",
"name": "Adam",
"zigbee_mac_address": "ABCD012345670101",
"vendor": "Plugwise B.V.",
"regulation_mode": "heating",
"regulation_modes": ["heating", "off", "bleeding_cold", "bleeding_hot"],
"binary_sensors": {
"plugwise_notification": false
},
"sensors": {
"outdoor_temperature": -1.25
}
},
"056ee145a816487eaa69243c3280f8bf": {
"dev_class": "heater_central",
"location": "bc93488efab249e5bc54fd7e175a6f91",
"model": "Generic heater",
"name": "OpenTherm",
"maximum_boiler_temperature": {
"setpoint": 60.0,
"lower_bound": 25.0,
"upper_bound": 95.0,
"resolution": 0.01
},
"domestic_hot_water_setpoint": {
"setpoint": 60.0,
"lower_bound": 40.0,
"upper_bound": 60.0,
"resolution": 0.01
},
"binary_sensors": {
"dhw_state": false,
"heating_state": true,
"flame_state": false
},
"sensors": {
"water_temperature": 37.0,
"intended_boiler_temperature": 38.1
},
"switches": {
"dhw_cm_switch": false
}
},
"e8ef2a01ed3b4139a53bf749204fe6b4": {
"dev_class": "switching",
"model": "Switchgroup",
"name": "Test",
"members": [
"2568cc4b9c1e401495d4741a5f89bee1",
"29542b2b6a6a4169acecc15c72a599b8"
],
"switches": {
"relay": true
}
}
}
]

View file

@ -0,0 +1,88 @@
[
{
"smile_name": "Smile",
"gateway_id": "015ae9ea3f964e668e490fa39da3870b",
"heater_id": "1cbf783bb11e4a7c8a6843dee3a86927",
"cooling_present": true,
"notifications": {}
},
{
"1cbf783bb11e4a7c8a6843dee3a86927": {
"dev_class": "heater_central",
"location": "a57efe5f145f498c9be62a9b63626fbf",
"model": "Generic heater/cooler",
"name": "OpenTherm",
"vendor": "Techneco",
"maximum_boiler_temperature": {
"setpoint": 60.0,
"lower_bound": 0.0,
"upper_bound": 100.0,
"resolution": 1.0
},
"elga_cooling_enabled": true,
"binary_sensors": {
"dhw_state": false,
"heating_state": false,
"compressor_state": true,
"cooling_state": true,
"slave_boiler_state": false,
"flame_state": false
},
"sensors": {
"water_temperature": 29.1,
"intended_boiler_temperature": 0.0,
"modulation_level": 52,
"return_temperature": 25.1,
"water_pressure": 1.57,
"outdoor_air_temperature": 3.0
},
"switches": {
"dhw_cm_switch": false
}
},
"015ae9ea3f964e668e490fa39da3870b": {
"dev_class": "gateway",
"firmware": "4.0.15",
"hardware": "AME Smile 2.0 board",
"location": "a57efe5f145f498c9be62a9b63626fbf",
"mac_address": "012345670001",
"model": "Smile",
"name": "Smile",
"vendor": "Plugwise B.V.",
"binary_sensors": {
"plugwise_notification": false
},
"sensors": {
"outdoor_temperature": 28.2
}
},
"3cb70739631c4d17a86b8b12e8a5161b": {
"dev_class": "thermostat",
"firmware": "2018-02-08T11:15:53+01:00",
"hardware": "6539-1301-5002",
"location": "c784ee9fdab44e1395b8dee7d7a497d5",
"model": "Anna",
"name": "Anna",
"vendor": "Plugwise",
"thermostat": {
"setpoint": 24.0,
"lower_bound": 4.0,
"upper_bound": 30.0,
"resolution": 0.1
},
"preset_modes": ["no_frost", "home", "away", "asleep", "vacation"],
"active_preset": "home",
"available_schedules": ["standaard"],
"selected_schedule": "standaard",
"last_used": "standaard",
"mode": "auto",
"sensors": {
"temperature": 26.3,
"setpoint": 24.0,
"illuminance": 86.0,
"cooling_activation_outdoor_temperature": 21.0,
"cooling_deactivation_threshold": 4.0
}
}
}
]

View file

@ -0,0 +1,88 @@
[
{
"smile_name": "Smile",
"gateway_id": "015ae9ea3f964e668e490fa39da3870b",
"heater_id": "1cbf783bb11e4a7c8a6843dee3a86927",
"cooling_present": true,
"notifications": {}
},
{
"1cbf783bb11e4a7c8a6843dee3a86927": {
"dev_class": "heater_central",
"location": "a57efe5f145f498c9be62a9b63626fbf",
"model": "Generic heater/cooler",
"name": "OpenTherm",
"vendor": "Techneco",
"maximum_boiler_temperature": {
"setpoint": 60.0,
"lower_bound": 0.0,
"upper_bound": 100.0,
"resolution": 1.0
},
"elga_cooling_enabled": true,
"binary_sensors": {
"dhw_state": false,
"heating_state": false,
"compressor_state": false,
"cooling_state": false,
"slave_boiler_state": false,
"flame_state": false
},
"sensors": {
"water_temperature": 29.1,
"intended_boiler_temperature": 0.0,
"modulation_level": 52,
"return_temperature": 25.1,
"water_pressure": 1.57,
"outdoor_air_temperature": 3.0
},
"switches": {
"dhw_cm_switch": false
}
},
"015ae9ea3f964e668e490fa39da3870b": {
"dev_class": "gateway",
"firmware": "4.0.15",
"hardware": "AME Smile 2.0 board",
"location": "a57efe5f145f498c9be62a9b63626fbf",
"mac_address": "012345670001",
"model": "Smile",
"name": "Smile",
"vendor": "Plugwise B.V.",
"binary_sensors": {
"plugwise_notification": false
},
"sensors": {
"outdoor_temperature": 20.2
}
},
"3cb70739631c4d17a86b8b12e8a5161b": {
"dev_class": "thermostat",
"firmware": "2018-02-08T11:15:53+01:00",
"hardware": "6539-1301-5002",
"location": "c784ee9fdab44e1395b8dee7d7a497d5",
"model": "Anna",
"name": "Anna",
"vendor": "Plugwise",
"thermostat": {
"setpoint": 20.5,
"lower_bound": 4.0,
"upper_bound": 30.0,
"resolution": 0.1
},
"preset_modes": ["no_frost", "home", "away", "asleep", "vacation"],
"active_preset": "home",
"available_schedules": ["standaard"],
"selected_schedule": "standaard",
"last_used": "standaard",
"mode": "auto",
"sensors": {
"temperature": 21.3,
"setpoint": 20.5,
"illuminance": 86.0,
"cooling_activation_outdoor_temperature": 21.0,
"cooling_deactivation_threshold": 4.0
}
}
}
]

View file

@ -1,7 +1,7 @@
"""Tests for the Plugwise Climate integration."""
from unittest.mock import MagicMock
from plugwise.exceptions import PlugwiseException
from plugwise.exceptions import PlugwiseError
import pytest
from homeassistant.components.climate import HVACMode
@ -16,12 +16,10 @@ async def test_adam_climate_entity_attributes(
) -> None:
"""Test creation of adam climate device environment."""
state = hass.states.get("climate.zone_lisa_wk")
assert state
assert state.attributes["hvac_modes"] == [
HVACMode.HEAT,
HVACMode.AUTO,
]
assert state.state == HVACMode.AUTO
assert state.attributes["hvac_modes"] == [HVACMode.HEAT, HVACMode.AUTO]
# hvac_action is not asserted as the fixture is not in line with recent firmware functionality
assert "preset_modes" in state.attributes
assert "no_frost" in state.attributes["preset_modes"]
@ -37,11 +35,9 @@ async def test_adam_climate_entity_attributes(
state = hass.states.get("climate.zone_thermostat_jessie")
assert state
assert state.attributes["hvac_modes"] == [
HVACMode.HEAT,
HVACMode.AUTO,
]
assert state.state == HVACMode.AUTO
assert state.attributes["hvac_modes"] == [HVACMode.HEAT, HVACMode.AUTO]
# hvac_action is not asserted as the fixture is not in line with recent firmware functionality
assert "preset_modes" in state.attributes
assert "no_frost" in state.attributes["preset_modes"]
@ -55,13 +51,46 @@ async def test_adam_climate_entity_attributes(
assert state.attributes["target_temp_step"] == 0.1
async def test_adam_2_climate_entity_attributes(
hass: HomeAssistant, mock_smile_adam_2: MagicMock, init_integration: MockConfigEntry
) -> None:
"""Test creation of adam climate device environment."""
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.HEAT, HVACMode.AUTO]
state = hass.states.get("climate.lisa_badkamer")
assert state
assert state.state == HVACMode.AUTO
assert state.attributes["hvac_action"] == "idle"
assert state.attributes["hvac_modes"] == [HVACMode.HEAT, HVACMode.AUTO]
async def test_adam_3_climate_entity_attributes(
hass: HomeAssistant, mock_smile_adam_3: MagicMock, init_integration: MockConfigEntry
) -> None:
"""Test creation of adam climate device environment."""
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.HEAT,
HVACMode.COOL,
HVACMode.AUTO,
]
async def test_adam_climate_adjust_negative_testing(
hass: HomeAssistant, mock_smile_adam: MagicMock, init_integration: MockConfigEntry
) -> None:
"""Test exceptions of climate entities."""
mock_smile_adam.set_preset.side_effect = PlugwiseException
mock_smile_adam.set_schedule_state.side_effect = PlugwiseException
mock_smile_adam.set_temperature.side_effect = PlugwiseException
mock_smile_adam.set_preset.side_effect = PlugwiseError
mock_smile_adam.set_schedule_state.side_effect = PlugwiseError
mock_smile_adam.set_temperature.side_effect = PlugwiseError
with pytest.raises(HomeAssistantError):
await hass.services.async_call(
@ -107,6 +136,14 @@ async def test_adam_climate_entity_climate_changes(
"c50f167537524366a5af7aa3942feb1e", 25.0
)
with pytest.raises(ValueError):
await hass.services.async_call(
"climate",
"set_temperature",
{"entity_id": "climate.zone_lisa_wk", "temperature": 150},
blocking=True,
)
await hass.services.async_call(
"climate",
"set_preset_mode",
@ -143,32 +180,82 @@ async def test_adam_climate_entity_climate_changes(
"82fa13f017d240daa0d0ea1775420f24", "home"
)
with pytest.raises(HomeAssistantError):
await hass.services.async_call(
"climate",
"set_hvac_mode",
{
"entity_id": "climate.zone_thermostat_jessie",
"hvac_mode": "dry",
},
blocking=True,
)
async def test_anna_climate_entity_attributes(
hass: HomeAssistant, mock_smile_anna: MagicMock, init_integration: MockConfigEntry
hass: HomeAssistant,
mock_smile_anna: MagicMock,
init_integration: MockConfigEntry,
) -> None:
"""Test creation of anna climate device environment."""
state = hass.states.get("climate.anna")
assert state
assert state.state == HVACMode.AUTO
assert state.attributes["hvac_action"] == "heating"
assert state.attributes["hvac_modes"] == [
HVACMode.HEAT,
HVACMode.COOL,
HVACMode.AUTO,
]
assert "no_frost" in state.attributes["preset_modes"]
assert "home" in state.attributes["preset_modes"]
assert state.attributes["current_temperature"] == 19.3
assert state.attributes["hvac_action"] == "heating"
assert state.attributes["preset_mode"] == "home"
assert state.attributes["supported_features"] == 17
assert state.attributes["temperature"] == 21.0
assert state.attributes["temperature"] == 20.5
assert state.attributes["min_temp"] == 4.0
assert state.attributes["max_temp"] == 30.0
assert state.attributes["target_temp_step"] == 0.1
async def test_anna_2_climate_entity_attributes(
hass: HomeAssistant,
mock_smile_anna_2: MagicMock,
init_integration: MockConfigEntry,
) -> None:
"""Test creation of anna climate device environment."""
state = hass.states.get("climate.anna")
assert state
assert state.state == HVACMode.AUTO
assert state.attributes["hvac_action"] == "cooling"
assert state.attributes["hvac_modes"] == [
HVACMode.HEAT,
HVACMode.COOL,
HVACMode.AUTO,
]
assert state.attributes["temperature"] == 24.0
assert state.attributes["supported_features"] == 17
async def test_anna_3_climate_entity_attributes(
hass: HomeAssistant,
mock_smile_anna_3: MagicMock,
init_integration: MockConfigEntry,
) -> None:
"""Test creation of anna climate device environment."""
state = hass.states.get("climate.anna")
assert state
assert state.state == HVACMode.AUTO
assert state.attributes["hvac_action"] == "idle"
assert state.attributes["hvac_modes"] == [
HVACMode.HEAT,
HVACMode.COOL,
HVACMode.AUTO,
]
async def test_anna_climate_entity_climate_changes(
hass: HomeAssistant, mock_smile_anna: MagicMock, init_integration: MockConfigEntry
) -> None:
@ -182,7 +269,8 @@ async def test_anna_climate_entity_climate_changes(
assert mock_smile_anna.set_temperature.call_count == 1
mock_smile_anna.set_temperature.assert_called_with(
"c784ee9fdab44e1395b8dee7d7a497d5", 25.0
"c784ee9fdab44e1395b8dee7d7a497d5",
25.0,
)
await hass.services.async_call(
@ -209,3 +297,15 @@ async def test_anna_climate_entity_climate_changes(
mock_smile_anna.set_schedule_state.assert_called_with(
"c784ee9fdab44e1395b8dee7d7a497d5", "standaard", "off"
)
await hass.services.async_call(
"climate",
"set_hvac_mode",
{"entity_id": "climate.anna", "hvac_mode": "auto"},
blocking=True,
)
assert mock_smile_anna.set_schedule_state.call_count == 2
mock_smile_anna.set_schedule_state.assert_called_with(
"c784ee9fdab44e1395b8dee7d7a497d5", "standaard", "on"
)

View file

@ -40,9 +40,12 @@ async def test_diagnostics(
"name": "Zone Lisa Bios",
"zigbee_mac_address": "ABCD012345670A06",
"vendor": "Plugwise",
"lower_bound": 0.0,
"upper_bound": 99.9,
"resolution": 0.01,
"thermostat": {
"setpoint": 13.0,
"lower_bound": 0.0,
"upper_bound": 99.9,
"resolution": 0.01,
},
"preset_modes": ["home", "asleep", "away", "no_frost"],
"active_preset": "away",
"available_schedules": [
@ -66,9 +69,6 @@ async def test_diagnostics(
"name": "Floor kraan",
"zigbee_mac_address": "ABCD012345670A02",
"vendor": "Plugwise",
"lower_bound": 0.0,
"upper_bound": 100.0,
"resolution": 0.01,
"sensors": {
"temperature": 26.0,
"setpoint": 21.5,
@ -85,9 +85,6 @@ async def test_diagnostics(
"name": "Bios Cv Thermostatic Radiator ",
"zigbee_mac_address": "ABCD012345670A09",
"vendor": "Plugwise",
"lower_bound": 0.0,
"upper_bound": 100.0,
"resolution": 0.01,
"sensors": {
"temperature": 17.2,
"setpoint": 13.0,
@ -105,9 +102,12 @@ async def test_diagnostics(
"name": "Zone Lisa WK",
"zigbee_mac_address": "ABCD012345670A07",
"vendor": "Plugwise",
"lower_bound": 0.0,
"upper_bound": 99.9,
"resolution": 0.01,
"thermostat": {
"setpoint": 21.5,
"lower_bound": 0.0,
"upper_bound": 99.9,
"resolution": 0.01,
},
"preset_modes": ["home", "asleep", "away", "no_frost"],
"active_preset": "home",
"available_schedules": [
@ -146,9 +146,6 @@ async def test_diagnostics(
"name": "Thermostatic Radiator Jessie",
"zigbee_mac_address": "ABCD012345670A10",
"vendor": "Plugwise",
"lower_bound": 0.0,
"upper_bound": 100.0,
"resolution": 0.01,
"sensors": {
"temperature": 17.1,
"setpoint": 15.0,
@ -274,9 +271,12 @@ async def test_diagnostics(
"name": "Zone Thermostat Jessie",
"zigbee_mac_address": "ABCD012345670A03",
"vendor": "Plugwise",
"lower_bound": 0.0,
"upper_bound": 99.9,
"resolution": 0.01,
"thermostat": {
"setpoint": 15.0,
"lower_bound": 0.0,
"upper_bound": 99.9,
"resolution": 0.01,
},
"preset_modes": ["home", "asleep", "away", "no_frost"],
"active_preset": "asleep",
"available_schedules": [
@ -300,9 +300,6 @@ async def test_diagnostics(
"name": "Thermostatic Radiator Badkamer",
"zigbee_mac_address": "ABCD012345670A17",
"vendor": "Plugwise",
"lower_bound": 0.0,
"upper_bound": 100.0,
"resolution": 0.01,
"sensors": {
"temperature": 19.1,
"setpoint": 14.0,
@ -320,9 +317,12 @@ async def test_diagnostics(
"name": "Zone Thermostat Badkamer",
"zigbee_mac_address": "ABCD012345670A08",
"vendor": "Plugwise",
"lower_bound": 0.0,
"upper_bound": 99.9,
"resolution": 0.01,
"thermostat": {
"setpoint": 14.0,
"lower_bound": 0.0,
"upper_bound": 99.9,
"resolution": 0.01,
},
"preset_modes": ["home", "asleep", "away", "no_frost"],
"active_preset": "away",
"available_schedules": [
@ -362,9 +362,12 @@ async def test_diagnostics(
"name": "CV Kraan Garage",
"zigbee_mac_address": "ABCD012345670A11",
"vendor": "Plugwise",
"lower_bound": 0.0,
"upper_bound": 100.0,
"resolution": 0.01,
"thermostat": {
"setpoint": 5.5,
"lower_bound": 0.0,
"upper_bound": 100.0,
"resolution": 0.01,
},
"preset_modes": ["home", "asleep", "away", "no_frost"],
"active_preset": "no_frost",
"available_schedules": [

View file

@ -36,5 +36,7 @@ async def test_anna_max_boiler_temp_change(
blocking=True,
)
assert mock_smile_anna.set_max_boiler_temperature.call_count == 1
mock_smile_anna.set_max_boiler_temperature.assert_called_with(65)
assert mock_smile_anna.set_number_setpoint.call_count == 1
mock_smile_anna.set_number_setpoint.assert_called_with(
"maximum_boiler_temperature", 65.0
)