Use upstream device information for Plugwise (#66074)

This commit is contained in:
Franck Nijhof 2022-02-08 19:08:01 +01:00 committed by GitHub
parent 199c8fef40
commit 4efebcb86c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 70 additions and 118 deletions

View file

@ -1,6 +1,4 @@
"""Plugwise Binary Sensor component for Home Assistant."""
from plugwise.smile import Smile
from homeassistant.components.binary_sensor import (
BinarySensorEntity,
BinarySensorEntityDescription,
@ -45,37 +43,32 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up the Smile binary_sensors from a config entry."""
api: Smile = hass.data[DOMAIN][config_entry.entry_id]["api"]
coordinator: PlugwiseDataUpdateCoordinator = hass.data[DOMAIN][
config_entry.entry_id
][COORDINATOR]
entities: list[PlugwiseBinarySensorEntity] = []
for device_id, device_properties in coordinator.data.devices.items():
if device_properties["class"] == "heater_central":
for device_id, device in coordinator.data.devices.items():
if device["class"] == "heater_central":
for description in BINARY_SENSORS:
if (
"binary_sensors" not in device_properties
or description.key not in device_properties["binary_sensors"]
"binary_sensors" not in device
or description.key not in device["binary_sensors"]
):
continue
entities.append(
PlugwiseBinarySensorEntity(
api,
coordinator,
device_properties["name"],
device_id,
description,
)
)
if device_properties["class"] == "gateway":
if device["class"] == "gateway":
entities.append(
PlugwiseNotifyBinarySensorEntity(
api,
coordinator,
device_properties["name"],
device_id,
BinarySensorEntityDescription(
key="plugwise_notification",
@ -92,25 +85,18 @@ class PlugwiseBinarySensorEntity(PlugwiseEntity, BinarySensorEntity):
def __init__(
self,
api: Smile,
coordinator: PlugwiseDataUpdateCoordinator,
name: str,
dev_id: str,
device_id: str,
description: BinarySensorEntityDescription,
) -> None:
"""Initialise the binary_sensor."""
super().__init__(api, coordinator, name, dev_id)
super().__init__(coordinator, device_id)
self.entity_description = description
self._attr_is_on = False
self._attr_unique_id = f"{dev_id}-{description.key}"
if dev_id == coordinator.data.gateway["heater_id"]:
self._entity_name = "Auxiliary"
self._name = f"{self._entity_name} {description.name}"
if dev_id == coordinator.data.gateway["gateway_id"]:
self._entity_name = f"Smile {self._entity_name}"
self._attr_unique_id = f"{device_id}-{description.key}"
self._attr_name = (
f"{coordinator.data.devices[device_id].get('name', '')} {description.name}"
).lstrip()
@callback
def _async_process_data(self) -> None:

View file

@ -52,17 +52,14 @@ async def async_setup_entry(
"zone_thermostat",
"thermostatic_radiator_valve",
]
for device_id, device_properties in coordinator.data.devices.items():
if device_properties["class"] not in thermostat_classes:
for device_id, device in coordinator.data.devices.items():
if device["class"] not in thermostat_classes:
continue
thermostat = PlugwiseClimateEntity(
api,
coordinator,
device_properties["name"],
device_id,
device_properties["location"],
device_properties["class"],
)
entities.append(thermostat)
@ -87,18 +84,16 @@ class PlugwiseClimateEntity(PlugwiseEntity, ClimateEntity):
self,
api: Smile,
coordinator: PlugwiseDataUpdateCoordinator,
name: str,
device_id: str,
loc_id: str,
model: str,
) -> None:
"""Set up the Plugwise API."""
super().__init__(api, coordinator, name, device_id)
super().__init__(coordinator, device_id)
self._attr_extra_state_attributes = {}
self._attr_unique_id = f"{device_id}-climate"
self._attr_name = coordinator.data.devices[device_id].get("name")
self._api = api
self._loc_id = loc_id
self._loc_id = coordinator.data.devices[device_id]["location"]
self._presets = None

View file

@ -1,14 +1,7 @@
"""Generic Plugwise Entity Class."""
from __future__ import annotations
from plugwise.smile import Smile
from homeassistant.const import (
ATTR_CONFIGURATION_URL,
ATTR_MODEL,
ATTR_VIA_DEVICE,
CONF_HOST,
)
from homeassistant.const import ATTR_NAME, ATTR_VIA_DEVICE, CONF_HOST
from homeassistant.core import callback
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.update_coordinator import CoordinatorEntity
@ -22,50 +15,38 @@ class PlugwiseEntity(CoordinatorEntity[PlugwiseData]):
def __init__(
self,
api: Smile,
coordinator: PlugwiseDataUpdateCoordinator,
name: str,
dev_id: str,
device_id: str,
) -> None:
"""Initialise the gateway."""
super().__init__(coordinator)
self._dev_id = device_id
self._api = api
self._name = name
self._dev_id = dev_id
self._entity_name = self._name
configuration_url: str | None = None
if entry := self.coordinator.config_entry:
configuration_url = f"http://{entry.data[CONF_HOST]}"
@property
def name(self) -> str | None:
"""Return the name of the entity, if any."""
return self._name
@property
def device_info(self) -> DeviceInfo:
"""Return the device information."""
data = self.coordinator.data.devices[self._dev_id]
device_information = DeviceInfo(
identifiers={(DOMAIN, self._dev_id)},
name=self._entity_name,
manufacturer="Plugwise",
data = coordinator.data.devices[device_id]
self._attr_device_info = DeviceInfo(
configuration_url=configuration_url,
identifiers={(DOMAIN, device_id)},
manufacturer=data.get("vendor"),
model=data.get("model"),
name=f"Smile {coordinator.data.gateway['smile_name']}",
sw_version=data.get("fw"),
)
if entry := self.coordinator.config_entry:
device_information[
ATTR_CONFIGURATION_URL
] = f"http://{entry.data[CONF_HOST]}"
if model := data.get("model"):
device_information[ATTR_MODEL] = model
if self._dev_id != self.coordinator.data.gateway["gateway_id"]:
device_information[ATTR_VIA_DEVICE] = (
if device_id != coordinator.data.gateway["gateway_id"]:
self._attr_device_info.update(
{
ATTR_NAME: data.get("name"),
ATTR_VIA_DEVICE: (
DOMAIN,
str(self.coordinator.data.gateway["gateway_id"]),
),
}
)
return device_information
async def async_added_to_hass(self) -> None:
"""Subscribe to updates."""
self._async_process_data()

View file

@ -290,11 +290,11 @@ async def async_setup_entry(
coordinator = hass.data[DOMAIN][config_entry.entry_id][COORDINATOR]
entities: list[PlugwiseSensorEnity] = []
for device_id, device_properties in coordinator.data.devices.items():
for device_id, device in coordinator.data.devices.items():
for description in SENSORS:
if (
"sensors" not in device_properties
or device_properties["sensors"].get(description.key) is None
"sensors" not in device
or device["sensors"].get(description.key) is None
):
continue
@ -302,7 +302,6 @@ async def async_setup_entry(
PlugwiseSensorEnity(
api,
coordinator,
device_properties["name"],
device_id,
description,
)
@ -311,14 +310,13 @@ async def async_setup_entry(
if coordinator.data.gateway["single_master_thermostat"] is False:
# These sensors should actually be binary sensors.
for description in INDICATE_ACTIVE_LOCAL_DEVICE_SENSORS:
if description.key not in device_properties:
if description.key not in device:
continue
entities.append(
PlugwiseAuxSensorEntity(
api,
coordinator,
device_properties["name"],
device_id,
description,
)
@ -335,28 +333,23 @@ class PlugwiseSensorEnity(PlugwiseEntity, SensorEntity):
self,
api: Smile,
coordinator: PlugwiseDataUpdateCoordinator,
name: str,
device_id: str,
description: SensorEntityDescription,
) -> None:
"""Initialise the sensor."""
super().__init__(api, coordinator, name, device_id)
super().__init__(coordinator, device_id)
self.entity_description = description
self._api = api
self._attr_unique_id = f"{device_id}-{description.key}"
if device_id == coordinator.data.gateway["heater_id"]:
self._entity_name = "Auxiliary"
self._name = f"{self._entity_name} {description.name}"
if device_id == coordinator.data.gateway["gateway_id"]:
self._entity_name = f"Smile {self._entity_name}"
self._attr_name = (
f"{coordinator.data.devices[device_id].get('name', '')} {description.name}"
).lstrip()
@callback
def _async_process_data(self) -> None:
"""Update the entity."""
if not (data := self.coordinator.data.devices.get(self._dev_id)):
LOGGER.error("Received no data for device %s", self._entity_name)
LOGGER.error("Received no data for device %s", self._dev_id)
self.async_write_ha_state()
return
@ -374,7 +367,7 @@ class PlugwiseAuxSensorEntity(PlugwiseSensorEnity):
def _async_process_data(self) -> None:
"""Update the entity."""
if not (data := self.coordinator.data.devices.get(self._dev_id)):
LOGGER.error("Received no data for device %s", self._entity_name)
LOGGER.error("Received no data for device %s", self._dev_id)
self.async_write_ha_state()
return

View file

@ -26,18 +26,14 @@ async def async_setup_entry(
coordinator = hass.data[DOMAIN][config_entry.entry_id][COORDINATOR]
entities: list[PlugwiseSwitchEntity] = []
for device_id, device_properties in coordinator.data.devices.items():
if (
"switches" not in device_properties
or "relay" not in device_properties["switches"]
):
for device_id, device in coordinator.data.devices.items():
if "switches" not in device or "relay" not in device["switches"]:
continue
entities.append(
PlugwiseSwitchEntity(
api,
coordinator,
device_properties["name"],
device_id,
)
)
@ -54,14 +50,15 @@ class PlugwiseSwitchEntity(PlugwiseEntity, SwitchEntity):
self,
api: Smile,
coordinator: PlugwiseDataUpdateCoordinator,
name: str,
device_id: str,
) -> None:
"""Set up the Plugwise API."""
super().__init__(api, coordinator, name, device_id)
super().__init__(coordinator, device_id)
self._api = api
self._attr_unique_id = f"{device_id}-plug"
self._members = coordinator.data.devices[device_id].get("members")
self._attr_is_on = False
self._attr_name = coordinator.data.devices[device_id].get("name")
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the device on."""
@ -93,7 +90,7 @@ class PlugwiseSwitchEntity(PlugwiseEntity, SwitchEntity):
def _async_process_data(self) -> None:
"""Update the data from the Plugs."""
if not (data := self.coordinator.data.devices.get(self._dev_id)):
LOGGER.error("Received no data for device %s", self._name)
LOGGER.error("Received no data for device %s", self._dev_id)
self.async_write_ha_state()
return

View file

@ -11,11 +11,11 @@ async def test_anna_climate_binary_sensor_entities(hass, mock_smile_anna):
entry = await async_init_integration(hass, mock_smile_anna)
assert entry.state is ConfigEntryState.LOADED
state = hass.states.get("binary_sensor.auxiliary_secondary_boiler_state")
assert str(state.state) == STATE_OFF
state = hass.states.get("binary_sensor.opentherm_secondary_boiler_state")
assert state.state == STATE_OFF
state = hass.states.get("binary_sensor.auxiliary_dhw_state")
assert str(state.state) == STATE_OFF
state = hass.states.get("binary_sensor.opentherm_dhw_state")
assert state.state == STATE_OFF
async def test_anna_climate_binary_sensor_change(hass, mock_smile_anna):
@ -23,18 +23,18 @@ async def test_anna_climate_binary_sensor_change(hass, mock_smile_anna):
entry = await async_init_integration(hass, mock_smile_anna)
assert entry.state is ConfigEntryState.LOADED
hass.states.async_set("binary_sensor.auxiliary_dhw_state", STATE_ON, {})
hass.states.async_set("binary_sensor.opentherm_dhw_state", STATE_ON, {})
await hass.async_block_till_done()
state = hass.states.get("binary_sensor.auxiliary_dhw_state")
assert str(state.state) == STATE_ON
state = hass.states.get("binary_sensor.opentherm_dhw_state")
assert state.state == STATE_ON
await hass.helpers.entity_component.async_update_entity(
"binary_sensor.auxiliary_dhw_state"
"binary_sensor.opentherm_dhw_state"
)
state = hass.states.get("binary_sensor.auxiliary_dhw_state")
assert str(state.state) == STATE_OFF
state = hass.states.get("binary_sensor.opentherm_dhw_state")
assert state.state == STATE_OFF
async def test_adam_climate_binary_sensor_change(hass, mock_smile_adam):

View file

@ -17,7 +17,7 @@ async def test_adam_climate_sensor_entities(hass, mock_smile_adam):
state = hass.states.get("sensor.cv_pomp_electricity_consumed")
assert float(state.state) == 35.6
state = hass.states.get("sensor.auxiliary_water_temperature")
state = hass.states.get("sensor.onoff_water_temperature")
assert float(state.state) == 70.0
state = hass.states.get("sensor.cv_pomp_electricity_consumed_interval")
@ -36,10 +36,10 @@ async def test_anna_as_smt_climate_sensor_entities(hass, mock_smile_anna):
entry = await async_init_integration(hass, mock_smile_anna)
assert entry.state is ConfigEntryState.LOADED
state = hass.states.get("sensor.auxiliary_outdoor_temperature")
state = hass.states.get("sensor.opentherm_outdoor_temperature")
assert float(state.state) == 3.0
state = hass.states.get("sensor.auxiliary_water_temperature")
state = hass.states.get("sensor.opentherm_water_temperature")
assert float(state.state) == 29.1
state = hass.states.get("sensor.anna_illuminance")
@ -52,7 +52,7 @@ async def test_anna_climate_sensor_entities(hass, mock_smile_anna):
entry = await async_init_integration(hass, mock_smile_anna)
assert entry.state is ConfigEntryState.LOADED
state = hass.states.get("sensor.auxiliary_outdoor_temperature")
state = hass.states.get("sensor.opentherm_outdoor_temperature")
assert float(state.state) == 3.0