Skip to content

Commit bcc498d

Browse files
authored
Merge pull request #647 from plugwise/fix-p1-typing
Energy-device-related improvements
2 parents c27a756 + 6c799b7 commit bcc498d

File tree

5 files changed

+66
-71
lines changed

5 files changed

+66
-71
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
## Ongoing
44

5+
- Fix typing of P1 sensors, energy-device-related improvements.
56
- Rename mode to climate_mode.
67

78
## v1.5.0

plugwise/constants.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -431,30 +431,30 @@ class SmileSensors(TypedDict, total=False):
431431
dhw_temperature: float
432432
domestic_hot_water_setpoint: float
433433
temperature: float
434-
electricity_consumed: float
435-
electricity_consumed_interval: float
434+
electricity_consumed: int
435+
electricity_consumed_interval:int
436436
electricity_consumed_off_peak_cumulative: float
437437
electricity_consumed_off_peak_interval: int
438438
electricity_consumed_off_peak_point: int
439439
electricity_consumed_peak_cumulative: float
440440
electricity_consumed_peak_interval: int
441441
electricity_consumed_peak_point: int
442-
electricity_consumed_point: float
443-
electricity_phase_one_consumed: float
444-
electricity_phase_two_consumed: float
445-
electricity_phase_three_consumed: float
446-
electricity_phase_one_produced: float
447-
electricity_phase_two_produced: float
448-
electricity_phase_three_produced: float
449-
electricity_produced: float
450-
electricity_produced_interval: float
442+
electricity_consumed_point: int
443+
electricity_phase_one_consumed: int
444+
electricity_phase_two_consumed: int
445+
electricity_phase_three_consumed: int
446+
electricity_phase_one_produced: int
447+
electricity_phase_two_produced: int
448+
electricity_phase_three_produced: int
449+
electricity_produced: int
450+
electricity_produced_interval: int
451451
electricity_produced_off_peak_cumulative: float
452452
electricity_produced_off_peak_interval: int
453453
electricity_produced_off_peak_point: int
454454
electricity_produced_peak_cumulative: float
455455
electricity_produced_peak_interval: int
456456
electricity_produced_peak_point: int
457-
electricity_produced_point: float
457+
electricity_produced_point: int
458458
gas_consumed_cumulative: float
459459
gas_consumed_interval: float
460460
humidity: float

plugwise/helper.py

Lines changed: 51 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,10 @@ def _all_appliances(self) -> None:
269269
for appliance in self._domain_objects.findall("./appliance"):
270270
appl = Munch()
271271
appl.pwclass = appliance.find("type").text
272+
# Don't collect data for the OpenThermGateway appliance
273+
if appl.pwclass == "open_therm_gateway":
274+
continue
275+
272276
# Extend device_class name of Plugs (Plugwise and Aqara) - Pw-Beta Issue #739
273277
description = appliance.find("description").text
274278
if description is not None and (
@@ -291,6 +295,10 @@ def _all_appliances(self) -> None:
291295
elif appl.pwclass not in THERMOSTAT_CLASSES:
292296
appl.location = self._home_location
293297

298+
# Don't show orphaned thermostat-types
299+
if appl.pwclass in THERMOSTAT_CLASSES and appl.location is None:
300+
continue
301+
294302
appl.dev_id = appliance.attrib["id"]
295303
appl.name = appliance.find("name").text
296304
appl.model = None
@@ -301,24 +309,15 @@ def _all_appliances(self) -> None:
301309
appl.zigbee_mac = None
302310
appl.vendor_name = None
303311

304-
# Determine class for this appliance
305-
# Skip on heater_central when no active device present
312+
# Collect appliance info, skip orphaned/removed devices
306313
if not (appl := self._appliance_info_finder(appl, appliance)):
307314
continue
308315

309-
# Skip orphaned heater_central (Core Issue #104433)
310-
if appl.pwclass == "heater_central" and appl.dev_id != self._heater_id:
311-
continue
312-
313316
# P1: for gateway and smartmeter switch device_id - part 1
314317
# This is done to avoid breakage in HA Core
315318
if appl.pwclass == "gateway" and self.smile_type == "power":
316319
appl.dev_id = appl.location
317320

318-
# Don't show orphaned thermostat-types or the OpenTherm Gateway.
319-
if appl.pwclass in THERMOSTAT_CLASSES and appl.location is None:
320-
continue
321-
322321
self._create_gw_devices(appl)
323322

324323
# For P1 collect the connected SmartMeter info
@@ -354,23 +353,32 @@ def _all_locations(self) -> None:
354353
self.loc_data[loc.loc_id] = {"name": loc.name}
355354

356355
def _p1_smartmeter_info_finder(self, appl: Munch) -> None:
357-
"""Collect P1 DSMR Smartmeter info."""
356+
"""Collect P1 DSMR SmartMeter info."""
358357
loc_id = next(iter(self.loc_data.keys()))
358+
location = self._domain_objects.find(f'./location[@id="{loc_id}"]')
359+
locator = "./logs/point_log/electricity_point_meter"
360+
mod_type = "electricity_point_meter"
361+
module_data = self._get_module_data(location, locator, mod_type)
362+
if not module_data["contents"]:
363+
LOGGER.error("No module data found for SmartMeter") # pragma: no cover
364+
return None # pragma: no cover
365+
359366
appl.dev_id = self.gateway_id
367+
appl.firmware = module_data["firmware_version"]
368+
appl.hardware = module_data["hardware_version"]
360369
appl.location = loc_id
361370
appl.mac = None
362-
appl.model = None
363-
appl.model_id = None
371+
appl.model = module_data["vendor_model"]
372+
appl.model_id = None # don't use model_id for SmartMeter
364373
appl.name = "P1"
365374
appl.pwclass = "smartmeter"
375+
appl.vendor_name = module_data["vendor_name"]
366376
appl.zigbee_mac = None
367-
location = self._domain_objects.find(f'./location[@id="{loc_id}"]')
368-
appl = self._energy_device_info_finder(appl, location)
369377

370378
self._create_gw_devices(appl)
371379

372380
def _appliance_info_finder(self, appl: Munch, appliance: etree) -> Munch:
373-
"""Collect device info (Smile/Stretch, Thermostats, OpenTherm/On-Off): firmware, model and vendor name."""
381+
"""Collect info for all appliances found."""
374382
match appl.pwclass:
375383
case "gateway":
376384
# Collect gateway device info
@@ -382,45 +390,28 @@ def _appliance_info_finder(self, appl: Munch, appliance: etree) -> Munch:
382390
# Collect heater_central device info
383391
self._appl_heater_central_info(appl, appliance, False) # False means non-legacy device
384392
self._appl_dhw_mode_info(appl, appliance)
393+
# Skip orphaned heater_central (Core Issue #104433)
394+
if appl.dev_id != self._heater_id:
395+
return Munch()
396+
return appl
397+
case _ as s if s.endswith("_plug"):
398+
# Collect info from plug-types (Plug, Aqara Smart Plug)
399+
locator = "./logs/interval_log/electricity_interval_meter"
400+
mod_type = "electricity_interval_meter"
401+
module_data = self._get_module_data(appliance, locator, mod_type)
402+
# A plug without module-data is orphaned/ no present
403+
if not module_data["contents"]:
404+
return Munch()
405+
406+
appl.firmware = module_data["firmware_version"]
407+
appl.hardware = module_data["hardware_version"]
408+
appl.model_id = module_data["vendor_model"]
409+
appl.vendor_name = module_data["vendor_name"]
410+
appl.model = check_model(appl.model_id, appl.vendor_name)
411+
appl.zigbee_mac = module_data["zigbee_mac_address"]
412+
return appl
413+
case _: # pragma: no cover
385414
return appl
386-
case _:
387-
# Collect info from power-related devices (Plug, Aqara Smart Plug)
388-
return self._energy_device_info_finder(appl, appliance)
389-
390-
def _energy_device_info_finder(self, appl: Munch, appliance: etree) -> Munch:
391-
"""Helper-function for _appliance_info_finder().
392-
393-
Collect energy device info (Smartmeter): firmware, model and vendor name.
394-
"""
395-
if self.smile_type == "power":
396-
locator = "./logs/point_log/electricity_point_meter"
397-
mod_type = "electricity_point_meter"
398-
module_data = self._get_module_data(appliance, locator, mod_type)
399-
appl.hardware = module_data["hardware_version"]
400-
appl.model = module_data["vendor_model"] # don't use model_id for Smartmeter
401-
appl.vendor_name = module_data["vendor_name"]
402-
appl.firmware = module_data["firmware_version"]
403-
404-
return appl
405-
406-
if self.smile(ADAM):
407-
locator = "./logs/interval_log/electricity_interval_meter"
408-
mod_type = "electricity_interval_meter"
409-
module_data = self._get_module_data(appliance, locator, mod_type)
410-
# Filter appliance without zigbee_mac, it's an orphaned device
411-
appl.zigbee_mac = module_data["zigbee_mac_address"]
412-
if appl.zigbee_mac is None:
413-
return None
414-
415-
appl.vendor_name = module_data["vendor_name"]
416-
appl.model_id = module_data["vendor_model"]
417-
appl.model = check_model(appl.model_id, appl.vendor_name)
418-
appl.hardware = module_data["hardware_version"]
419-
appl.firmware = module_data["firmware_version"]
420-
421-
return appl
422-
423-
return appl # pragma: no cover
424415

425416
def _appl_gateway_info(self, appl: Munch, appliance: etree) -> Munch:
426417
"""Helper-function for _appliance_info_finder()."""
@@ -725,15 +716,18 @@ def _wireless_availability(self, appliance: etree, data: DeviceData) -> None:
725716
Collect the availability-status for wireless connected devices.
726717
"""
727718
if self.smile(ADAM):
728-
# Collect for Plugs
719+
# Try collecting for a Plug
729720
locator = "./logs/interval_log/electricity_interval_meter"
730721
mod_type = "electricity_interval_meter"
731722
module_data = self._get_module_data(appliance, locator, mod_type)
732-
if module_data["reachable"] is None:
733-
# Collect for wireless thermostats
723+
if not module_data["contents"]:
724+
# Try collecting for a wireless thermostat
734725
locator = "./logs/point_log[type='thermostat']/thermostat"
735726
mod_type = "thermostat"
736727
module_data = self._get_module_data(appliance, locator, mod_type)
728+
if not module_data["contents"]:
729+
LOGGER.error("No module data found for Plug or wireless thermostat") # pragma: no cover
730+
return None # pragma: no cover
737731

738732
if module_data["reachable"] is not None:
739733
data["available"] = module_data["reachable"]

plugwise/util.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ def check_model(name: str | None, vendor_name: str | None) -> str | None:
115115
if name is not None and "lumi.plug" in name:
116116
return "Aqara Smart Plug"
117117

118-
return name
118+
return name # pragma: no cover
119119

120120

121121
def common_match_cases(

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "plugwise"
7-
version = "1.5.0"
7+
version = "1.5.1a3"
88
license = {file = "LICENSE"}
99
description = "Plugwise Smile (Adam/Anna/P1) and Stretch module for Python 3."
1010
readme = "README.md"

0 commit comments

Comments
 (0)