Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 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"]
}
}
}