Bump plugwise to v1.7.7 and adapt (#147809)

This commit is contained in:
Bouwe Westerdijk 2025-06-30 15:40:10 +02:00 committed by GitHub
parent ea70229426
commit b52a248def
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 177 additions and 126 deletions

View File

@ -27,10 +27,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: PlugwiseConfigEntry) ->
config_entry_id=entry.entry_id, config_entry_id=entry.entry_id,
identifiers={(DOMAIN, str(coordinator.api.gateway_id))}, identifiers={(DOMAIN, str(coordinator.api.gateway_id))},
manufacturer="Plugwise", manufacturer="Plugwise",
model=coordinator.api.smile_model, model=coordinator.api.smile.model,
model_id=coordinator.api.smile_model_id, model_id=coordinator.api.smile.model_id,
name=coordinator.api.smile_name, name=coordinator.api.smile.name,
sw_version=str(coordinator.api.smile_version), sw_version=str(coordinator.api.smile.version),
) # required for adding the entity-less P1 Gateway ) # required for adding the entity-less P1 Gateway
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)

View File

@ -39,7 +39,7 @@ async def async_setup_entry(
if not coordinator.new_devices: if not coordinator.new_devices:
return return
if coordinator.api.smile_name == "Adam": if coordinator.api.smile.name == "Adam":
async_add_entities( async_add_entities(
PlugwiseClimateEntity(coordinator, device_id) PlugwiseClimateEntity(coordinator, device_id)
for device_id in coordinator.new_devices for device_id in coordinator.new_devices
@ -85,7 +85,7 @@ class PlugwiseClimateEntity(PlugwiseEntity, ClimateEntity):
self._attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE self._attr_supported_features = ClimateEntityFeature.TARGET_TEMPERATURE
if ( if (
self.coordinator.api.cooling_present self.coordinator.api.cooling_present
and coordinator.api.smile_name != "Adam" and coordinator.api.smile.name != "Adam"
): ):
self._attr_supported_features = ( self._attr_supported_features = (
ClimateEntityFeature.TARGET_TEMPERATURE_RANGE ClimateEntityFeature.TARGET_TEMPERATURE_RANGE

View File

@ -204,11 +204,11 @@ class PlugwiseConfigFlow(ConfigFlow, domain=DOMAIN):
api, errors = await verify_connection(self.hass, user_input) api, errors = await verify_connection(self.hass, user_input)
if api: if api:
await self.async_set_unique_id( await self.async_set_unique_id(
api.smile_hostname or api.gateway_id, api.smile.hostname or api.gateway_id,
raise_on_progress=False, raise_on_progress=False,
) )
self._abort_if_unique_id_configured() self._abort_if_unique_id_configured()
return self.async_create_entry(title=api.smile_name, data=user_input) return self.async_create_entry(title=api.smile.name, data=user_input)
return self.async_show_form( return self.async_show_form(
step_id=SOURCE_USER, step_id=SOURCE_USER,
@ -236,7 +236,7 @@ class PlugwiseConfigFlow(ConfigFlow, domain=DOMAIN):
api, errors = await verify_connection(self.hass, full_input) api, errors = await verify_connection(self.hass, full_input)
if api: if api:
await self.async_set_unique_id( await self.async_set_unique_id(
api.smile_hostname or api.gateway_id, api.smile.hostname or api.gateway_id,
raise_on_progress=False, raise_on_progress=False,
) )
self._abort_if_unique_id_mismatch(reason="not_the_same_smile") self._abort_if_unique_id_mismatch(reason="not_the_same_smile")

View File

@ -48,7 +48,7 @@ class PlugwiseEntity(CoordinatorEntity[PlugwiseDataUpdateCoordinator]):
manufacturer=data.get("vendor"), manufacturer=data.get("vendor"),
model=data.get("model"), model=data.get("model"),
model_id=data.get("model_id"), model_id=data.get("model_id"),
name=coordinator.api.smile_name, name=coordinator.api.smile.name,
sw_version=data.get("firmware"), sw_version=data.get("firmware"),
hw_version=data.get("hardware"), hw_version=data.get("hardware"),
) )

View File

@ -8,6 +8,6 @@
"iot_class": "local_polling", "iot_class": "local_polling",
"loggers": ["plugwise"], "loggers": ["plugwise"],
"quality_scale": "platinum", "quality_scale": "platinum",
"requirements": ["plugwise==1.7.6"], "requirements": ["plugwise==1.7.7"],
"zeroconf": ["_plugwise._tcp.local."] "zeroconf": ["_plugwise._tcp.local."]
} }

2
requirements_all.txt generated
View File

@ -1689,7 +1689,7 @@ plexauth==0.0.6
plexwebsocket==0.0.14 plexwebsocket==0.0.14
# homeassistant.components.plugwise # homeassistant.components.plugwise
plugwise==1.7.6 plugwise==1.7.7
# homeassistant.components.plum_lightpad # homeassistant.components.plum_lightpad
plumlightpad==0.0.11 plumlightpad==0.0.11

View File

@ -1427,7 +1427,7 @@ plexauth==0.0.6
plexwebsocket==0.0.14 plexwebsocket==0.0.14
# homeassistant.components.plugwise # homeassistant.components.plugwise
plugwise==1.7.6 plugwise==1.7.7
# homeassistant.components.plum_lightpad # homeassistant.components.plum_lightpad
plumlightpad==0.0.11 plumlightpad==0.0.11

View File

@ -7,6 +7,7 @@ import json
from typing import Any from typing import Any
from unittest.mock import AsyncMock, MagicMock, patch from unittest.mock import AsyncMock, MagicMock, patch
from munch import Munch
from packaging.version import Version from packaging.version import Version
import pytest import pytest
@ -23,6 +24,14 @@ from homeassistant.core import HomeAssistant
from tests.common import MockConfigEntry, load_fixture from tests.common import MockConfigEntry, load_fixture
def build_smile(**attrs):
"""Build smile Munch from provided attributes."""
smile = Munch()
for k, v in attrs.items():
setattr(smile, k, v)
return smile
def _read_json(environment: str, call: str) -> dict[str, Any]: def _read_json(environment: str, call: str) -> dict[str, Any]:
"""Undecode the json data.""" """Undecode the json data."""
fixture = load_fixture(f"plugwise/{environment}/{call}.json") fixture = load_fixture(f"plugwise/{environment}/{call}.json")
@ -106,17 +115,19 @@ def mock_smile_config_flow() -> Generator[MagicMock]:
with patch( with patch(
"homeassistant.components.plugwise.config_flow.Smile", "homeassistant.components.plugwise.config_flow.Smile",
autospec=True, autospec=True,
) as smile_mock: ) as api_mock:
smile = smile_mock.return_value api = api_mock.return_value
smile.connect.return_value = Version("4.3.2") api.connect.return_value = Version("4.3.2")
smile.smile_hostname = "smile12345" api.smile = build_smile(
smile.smile_model = "Test Model" hostname="smile12345",
smile.smile_model_id = "Test Model ID" model="Test Model",
smile.smile_name = "Test Smile Name" model_id="Test Model ID",
smile.smile_version = "4.3.2" name="Test Smile Name",
version="4.3.2",
)
yield smile yield api
@pytest.fixture @pytest.fixture
@ -127,28 +138,30 @@ def mock_smile_adam() -> Generator[MagicMock]:
with ( with (
patch( patch(
"homeassistant.components.plugwise.coordinator.Smile", autospec=True "homeassistant.components.plugwise.coordinator.Smile", autospec=True
) as smile_mock, ) as api_mock,
patch( patch(
"homeassistant.components.plugwise.config_flow.Smile", "homeassistant.components.plugwise.config_flow.Smile",
new=smile_mock, new=api_mock,
), ),
): ):
smile = smile_mock.return_value api = api_mock.return_value
smile.async_update.return_value = data api.async_update.return_value = data
smile.cooling_present = False api.cooling_present = False
smile.connect.return_value = Version("3.0.15") api.connect.return_value = Version("3.0.15")
smile.gateway_id = "fe799307f1624099878210aa0b9f1475" api.gateway_id = "fe799307f1624099878210aa0b9f1475"
smile.heater_id = "90986d591dcd426cae3ec3e8111ff730" api.heater_id = "90986d591dcd426cae3ec3e8111ff730"
smile.reboot = True api.reboot = True
smile.smile_hostname = "smile98765" api.smile = build_smile(
smile.smile_model = "Gateway" hostname="smile98765",
smile.smile_model_id = "smile_open_therm" model="Gateway",
smile.smile_name = "Adam" model_id="smile_open_therm",
smile.smile_type = "thermostat" name="Adam",
smile.smile_version = "3.0.15" type="thermostat",
version="3.0.15",
)
yield smile yield api
@pytest.fixture @pytest.fixture
@ -159,23 +172,25 @@ def mock_smile_adam_heat_cool(
data = _read_json(chosen_env, "data") data = _read_json(chosen_env, "data")
with patch( with patch(
"homeassistant.components.plugwise.coordinator.Smile", autospec=True "homeassistant.components.plugwise.coordinator.Smile", autospec=True
) as smile_mock: ) as api_mock:
smile = smile_mock.return_value api = api_mock.return_value
smile.async_update.return_value = data api.async_update.return_value = data
smile.connect.return_value = Version("3.6.4") api.connect.return_value = Version("3.6.4")
smile.cooling_present = cooling_present api.cooling_present = cooling_present
smile.gateway_id = "da224107914542988a88561b4452b0f6" api.gateway_id = "da224107914542988a88561b4452b0f6"
smile.heater_id = "056ee145a816487eaa69243c3280f8bf" api.heater_id = "056ee145a816487eaa69243c3280f8bf"
smile.reboot = True api.reboot = True
smile.smile_hostname = "smile98765" api.smile = build_smile(
smile.smile_model = "Gateway" hostname="smile98765",
smile.smile_model_id = "smile_open_therm" model="Gateway",
smile.smile_name = "Adam" model_id="smile_open_therm",
smile.smile_type = "thermostat" name="Adam",
smile.smile_version = "3.6.4" type="thermostat",
version="3.6.4",
)
yield smile yield api
@pytest.fixture @pytest.fixture
@ -185,23 +200,25 @@ def mock_smile_adam_jip() -> Generator[MagicMock]:
data = _read_json(chosen_env, "data") data = _read_json(chosen_env, "data")
with patch( with patch(
"homeassistant.components.plugwise.coordinator.Smile", autospec=True "homeassistant.components.plugwise.coordinator.Smile", autospec=True
) as smile_mock: ) as api_mock:
smile = smile_mock.return_value api = api_mock.return_value
smile.async_update.return_value = data api.async_update.return_value = data
smile.connect.return_value = Version("3.2.8") api.connect.return_value = Version("3.2.8")
smile.cooling_present = False api.cooling_present = False
smile.gateway_id = "b5c2386c6f6342669e50fe49dd05b188" api.gateway_id = "b5c2386c6f6342669e50fe49dd05b188"
smile.heater_id = "e4684553153b44afbef2200885f379dc" api.heater_id = "e4684553153b44afbef2200885f379dc"
smile.reboot = True api.reboot = True
smile.smile_hostname = "smile98765" api.smile = build_smile(
smile.smile_model = "Gateway" hostname="smile98765",
smile.smile_model_id = "smile_open_therm" model="Gateway",
smile.smile_name = "Adam" model_id="smile_open_therm",
smile.smile_type = "thermostat" name="Adam",
smile.smile_version = "3.2.8" type="thermostat",
version="3.2.8",
)
yield smile yield api
@pytest.fixture @pytest.fixture
@ -210,23 +227,25 @@ def mock_smile_anna(chosen_env: str, cooling_present: bool) -> Generator[MagicMo
data = _read_json(chosen_env, "data") data = _read_json(chosen_env, "data")
with patch( with patch(
"homeassistant.components.plugwise.coordinator.Smile", autospec=True "homeassistant.components.plugwise.coordinator.Smile", autospec=True
) as smile_mock: ) as api_mock:
smile = smile_mock.return_value api = api_mock.return_value
smile.async_update.return_value = data api.async_update.return_value = data
smile.connect.return_value = Version("4.0.15") api.connect.return_value = Version("4.0.15")
smile.cooling_present = cooling_present api.cooling_present = cooling_present
smile.gateway_id = "015ae9ea3f964e668e490fa39da3870b" api.gateway_id = "015ae9ea3f964e668e490fa39da3870b"
smile.heater_id = "1cbf783bb11e4a7c8a6843dee3a86927" api.heater_id = "1cbf783bb11e4a7c8a6843dee3a86927"
smile.reboot = True api.reboot = True
smile.smile_hostname = "smile98765" api.smile = build_smile(
smile.smile_model = "Gateway" hostname="smile98765",
smile.smile_model_id = "smile_thermo" model="Gateway",
smile.smile_name = "Smile Anna" model_id="smile_thermo",
smile.smile_type = "thermostat" name="Smile Anna",
smile.smile_version = "4.0.15" type="thermostat",
version="4.0.15",
)
yield smile yield api
@pytest.fixture @pytest.fixture
@ -235,22 +254,24 @@ def mock_smile_p1(chosen_env: str, gateway_id: str) -> Generator[MagicMock]:
data = _read_json(chosen_env, "data") data = _read_json(chosen_env, "data")
with patch( with patch(
"homeassistant.components.plugwise.coordinator.Smile", autospec=True "homeassistant.components.plugwise.coordinator.Smile", autospec=True
) as smile_mock: ) as api_mock:
smile = smile_mock.return_value api = api_mock.return_value
smile.async_update.return_value = data api.async_update.return_value = data
smile.connect.return_value = Version("4.4.2") api.connect.return_value = Version("4.4.2")
smile.gateway_id = gateway_id api.gateway_id = gateway_id
smile.heater_id = None api.heater_id = None
smile.reboot = True api.reboot = True
smile.smile_hostname = "smile98765" api.smile = build_smile(
smile.smile_model = "Gateway" hostname="smile98765",
smile.smile_model_id = "smile" model="Gateway",
smile.smile_name = "Smile P1" model_id="smile",
smile.smile_type = "power" name="Smile P1",
smile.smile_version = "4.4.2" type="power",
version="4.4.2",
)
yield smile yield api
@pytest.fixture @pytest.fixture
@ -260,22 +281,24 @@ def mock_smile_legacy_anna() -> Generator[MagicMock]:
data = _read_json(chosen_env, "data") data = _read_json(chosen_env, "data")
with patch( with patch(
"homeassistant.components.plugwise.coordinator.Smile", autospec=True "homeassistant.components.plugwise.coordinator.Smile", autospec=True
) as smile_mock: ) as api_mock:
smile = smile_mock.return_value api = api_mock.return_value
smile.async_update.return_value = data api.async_update.return_value = data
smile.connect.return_value = Version("1.8.22") api.connect.return_value = Version("1.8.22")
smile.gateway_id = "0000aaaa0000aaaa0000aaaa0000aa00" api.gateway_id = "0000aaaa0000aaaa0000aaaa0000aa00"
smile.heater_id = "04e4cbfe7f4340f090f85ec3b9e6a950" api.heater_id = "04e4cbfe7f4340f090f85ec3b9e6a950"
smile.reboot = False api.reboot = False
smile.smile_hostname = "smile98765" api.smile = build_smile(
smile.smile_model = "Gateway" hostname="smile98765",
smile.smile_model_id = None model="Gateway",
smile.smile_name = "Smile Anna" model_id=None,
smile.smile_type = "thermostat" name="Smile Anna",
smile.smile_version = "1.8.22" type="thermostat",
version="1.8.22",
)
yield smile yield api
@pytest.fixture @pytest.fixture
@ -285,22 +308,24 @@ def mock_stretch() -> Generator[MagicMock]:
data = _read_json(chosen_env, "data") data = _read_json(chosen_env, "data")
with patch( with patch(
"homeassistant.components.plugwise.coordinator.Smile", autospec=True "homeassistant.components.plugwise.coordinator.Smile", autospec=True
) as smile_mock: ) as api_mock:
smile = smile_mock.return_value api = api_mock.return_value
smile.async_update.return_value = data api.async_update.return_value = data
smile.connect.return_value = Version("3.1.11") api.connect.return_value = Version("3.1.11")
smile.gateway_id = "259882df3c05415b99c2d962534ce820" api.gateway_id = "259882df3c05415b99c2d962534ce820"
smile.heater_id = None api.heater_id = None
smile.reboot = False api.reboot = False
smile.smile_hostname = "stretch98765" api.smile = build_smile(
smile.smile_model = "Gateway" hostname="stretch98765",
smile.smile_model_id = None model="Gateway",
smile.smile_name = "Stretch" model_id=None,
smile.smile_type = "stretch" name="Stretch",
smile.smile_version = "3.1.11" type="stretch",
version="3.1.11",
)
yield smile yield api
@pytest.fixture @pytest.fixture

View File

@ -531,6 +531,19 @@
"vendor": "Plugwise", "vendor": "Plugwise",
"zigbee_mac_address": "ABCD012345670A11" "zigbee_mac_address": "ABCD012345670A11"
}, },
"e8ef2a01ed3b4139a53bf749204fe6b4": {
"dev_class": "switching",
"members": [
"02cf28bfec924855854c544690a609ef",
"4a810418d5394b3f82727340b91ba740"
],
"model": "Switchgroup",
"name": "Test",
"switches": {
"relay": true
},
"vendor": "Plugwise"
},
"f1fee6043d3642a9b0a65297455f008e": { "f1fee6043d3642a9b0a65297455f008e": {
"available": true, "available": true,
"binary_sensors": { "binary_sensors": {

View File

@ -579,6 +579,19 @@
'vendor': 'Plugwise', 'vendor': 'Plugwise',
'zigbee_mac_address': 'ABCD012345670A11', 'zigbee_mac_address': 'ABCD012345670A11',
}), }),
'e8ef2a01ed3b4139a53bf749204fe6b4': dict({
'dev_class': 'switching',
'members': list([
'02cf28bfec924855854c544690a609ef',
'4a810418d5394b3f82727340b91ba740',
]),
'model': 'Switchgroup',
'name': 'Test',
'switches': dict({
'relay': True,
}),
'vendor': 'Plugwise',
}),
'f1fee6043d3642a9b0a65297455f008e': dict({ 'f1fee6043d3642a9b0a65297455f008e': dict({
'available': True, 'available': True,
'binary_sensors': dict({ 'binary_sensors': dict({

View File

@ -478,7 +478,7 @@ async def test_reconfigure_flow_smile_mismatch(
mock_config_entry: MockConfigEntry, mock_config_entry: MockConfigEntry,
) -> None: ) -> None:
"""Test reconfigure flow aborts on other Smile ID.""" """Test reconfigure flow aborts on other Smile ID."""
mock_smile_adam.smile_hostname = TEST_SMILE_HOST mock_smile_adam.smile.hostname = TEST_SMILE_HOST
result = await _start_reconfigure_flow(hass, mock_config_entry, TEST_HOST) result = await _start_reconfigure_flow(hass, mock_config_entry, TEST_HOST)