Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion plugwise/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@ async def async_update(self) -> dict[str, GwEntityData]:
try:
data = await self._smile_api.async_update()
except (DataMissingError, KeyError) as err:
raise PlugwiseError("No Plugwise data received") from err
raise PlugwiseError(f"No Plugwise data received: {err}") from err

return data

Expand Down
25 changes: 17 additions & 8 deletions plugwise/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,17 +198,16 @@ def _get_groups(self) -> None:
return

for group in self._domain_objects.findall("./group"):
members: list[str] = []
group_id = group.get("id")
if group_id is None:
continue # pragma: no cover

if not (members := self._collect_members(group)):
continue

group_name = group.find("name").text
group_type = group.find("type").text
group_appliances = group.findall("appliances/appliance")
for item in group_appliances:
# Check if members are not orphaned - stretch
if item.get("id") in self.gw_entities:
members.append(item.get("id"))

if group_type in GROUP_TYPES and members and group_id:
if group_type in GROUP_TYPES:
self.gw_entities[group_id] = {
"dev_class": group_type,
"model": "Group",
Expand All @@ -218,6 +217,16 @@ def _get_groups(self) -> None:
}
self._count += 5

def _collect_members(self, element: etree.Element) -> list[str]:
"""Check and collect members."""
members: list[str] = []
group_appliances = element.findall("appliances/appliance")
for item in group_appliances:
if (member_id := item.get("id")) in self.gw_entities:
members.append(member_id)

return members

def _get_lock_state(
self, xml: etree.Element, data: GwEntityData, stretch_v2: bool = False
) -> None:
Expand Down
52 changes: 25 additions & 27 deletions plugwise/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,19 +106,18 @@ def _detect_low_batteries(self) -> list[str]:
def _add_or_update_notifications(
self, entity_id: str, entity: GwEntityData
) -> None:
"""Helper-function adding or updating the Plugwise notifications."""
if (
entity_id == self._gateway_id
and (self._is_thermostat or self.smile.type == "power")
) or (
"binary_sensors" in entity
and "plugwise_notification" in entity["binary_sensors"]
):
entity["binary_sensors"]["plugwise_notification"] = bool(
self._notifications
)
entity["notifications"] = self._notifications
self._count += 2
"""Helper-function adding or updating the Plugwise notifications to the gateway."""

if entity_id != self._gateway_id:
return # pragma: no cover

if self._is_thermostat or self.smile.type == "power":
if "plugwise_notification" not in entity["binary_sensors"]:
entity["binary_sensors"].update(
{"plugwise_notification": bool(self._notifications)}
)
entity.update({"notifications": self._notifications})
self._count += 2

def _update_for_cooling(self, entity: GwEntityData) -> None:
"""Helper-function for adding/updating various cooling-related values."""
Expand Down Expand Up @@ -184,29 +183,28 @@ def _get_entity_data(self, entity_id: str, entity: GwEntityData) -> None:
Provide entity-data, based on appliance_id (= entity_id).
"""
self._get_measurement_data(entity_id, entity)

# Check availability of wired-connected entities
# Smartmeter
self._check_availability(
entity, "smartmeter", "P1 does not seem to be connected"
)
# OpenTherm entity
if entity["name"] != "OnOff":
self._check_availability(
entity, "heater_central", "no OpenTherm communication"
)

# Switching groups data
self._entity_switching_group(entity)
# Adam data
if self.check_name(ADAM):
self._get_adam_data(entity)
# Update switching-group status
self._entity_switching_group(entity)

# Thermostat data for Anna (presets, temperatures etc)
if self.check_name(ANNA) and entity["dev_class"] == "thermostat":
self._climate_data(entity_id, entity)
self._get_anna_control_state(entity)

# Check availability of wired entities:
# - Smartmeter
self._check_availability(
entity, "smartmeter", "P1 does not seem to be connected"
)
# - OpenTherm entity
if entity["name"] != "OnOff":
self._check_availability(
entity, "heater_central", "no OpenTherm communication"
)

def _check_availability(
self, entity: GwEntityData, dev_class: str, message: str
) -> None:
Expand Down
21 changes: 3 additions & 18 deletions plugwise/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ def _get_appliances(self) -> None:
elif appl.pwclass not in THERMOSTAT_CLASSES:
appl.location = self._home_loc_id

# Don't show orphaned thermostat-types
# Don't show orphaned (no location) thermostat-types
if appl.pwclass in THERMOSTAT_CLASSES and appl.location is None:
continue

Expand Down Expand Up @@ -212,12 +212,7 @@ def _get_locations(self) -> None:
loc.loc_id = location.get("id")
loc.name = location.find("name").text
loc._type = location.find("type").text
self._loc_data[loc.loc_id] = {
"name": loc.name,
"primary": [],
"primary_prio": 0,
"secondary": [],
}
self._loc_data[loc.loc_id] = {"name": loc.name}
# Home location is of type building
if loc._type == "building":
counter += 1
Expand Down Expand Up @@ -316,17 +311,6 @@ def _get_appl_actuator_modes(

return mode_list

def _get_appliances_with_offset_functionality(self) -> list[str]:
"""Helper-function collecting all appliance that have offset_functionality."""
therm_list: list[str] = []
offset_appls = self._domain_objects.findall(
'.//actuator_functionalities/offset_functionality[type="temperature_offset"]/offset/../../..'
)
for item in offset_appls:
therm_list.append(item.get("id"))

return therm_list

def _get_zone_data(self, loc_id: str, zone: GwEntityData) -> None:
"""Helper-function for smile.py: _get_entity_data().

Expand Down Expand Up @@ -788,6 +772,7 @@ def _match_and_rank_thermostats(self) -> None:
Match thermostat-appliances with locations, rank them for locations with multiple thermostats.
"""
for location_id, location in self._loc_data.items():
location.update({"primary": [], "primary_prio": 0, "secondary": []})
for entity_id, entity in self.gw_entities.items():
self._rank_thermostat(
entity_id, entity, location_id, location, THERMO_MATCHING
Expand Down
6 changes: 2 additions & 4 deletions plugwise/legacy/smile.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,7 @@ async def async_update(self) -> dict[str, GwEntityData]:
# Detect failed data-retrieval
_ = self.gw_entities[self.gateway_id]["location"]
except KeyError as err: # pragma: no cover
raise DataMissingError(
"No (full) Plugwise legacy data received"
) from err
raise DataMissingError(f"No (full) legacy data: {err}") from err
else:
try:
self._domain_objects = await self._request(DOMAIN_OBJECTS)
Expand All @@ -117,7 +115,7 @@ async def async_update(self) -> dict[str, GwEntityData]:
# Detect failed data-retrieval
_ = self.gw_entities[self.gateway_id]["location"]
except KeyError as err: # pragma: no cover
raise DataMissingError("No legacy Plugwise data received") from err
raise DataMissingError(f"No legacy data: {err}") from err

self._first_update = False
self._previous_day_number = day_number
Expand Down
13 changes: 12 additions & 1 deletion plugwise/smile.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,17 @@ def get_all_gateway_entities(self) -> None:
self._get_groups()
self._all_entity_data()

def _get_appliances_with_offset_functionality(self) -> list[str]:
"""Helper-function collecting all appliance that have offset_functionality."""
therm_list: list[str] = []
offset_appls = self._domain_objects.findall(
'.//actuator_functionalities/offset_functionality[type="temperature_offset"]/offset/../../..'
)
for item in offset_appls:
therm_list.append(item.get("id"))

return therm_list

async def async_update(self) -> dict[str, GwEntityData]:
"""Perform an full update: re-collect all gateway entities and their data and states.

Expand All @@ -138,7 +149,7 @@ async def async_update(self) -> dict[str, GwEntityData]:
"cooling_enabled"
]
except KeyError as err:
raise DataMissingError("No Plugwise actual data received") from err
raise DataMissingError(f"No data: {err}") from err

return self.gw_entities

Expand Down
16 changes: 16 additions & 0 deletions tests/data/adam/adam_plus_anna_new_UPDATED_DATA.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,21 @@
"switches": {
"relay": false
}
},
"f2bf9048bef64cc5b6d5110154e33c81": {
"thermostats": {
"primary": [
"ad4838d7d35c4d6ea796ee12ae5aedf8",
"14df5c4dc8cb4ba69f9d1ac0eaf7c5c6",
"da575e9e09b947e281fb6e3ebce3b174"
],
"secondary": []
}
},
"f871b8c4d63549319221e294e4f88074": {
"thermostats": {
"primary": ["e2f4322d57924fa090fbbc48b3a140dc"],
"secondary": ["1772a4ea304041adb83f357b751341ff"]
}
}
}