plugwise 1.5.0__tar.gz → 1.5.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {plugwise-1.5.0 → plugwise-1.5.1}/PKG-INFO +1 -1
- {plugwise-1.5.0 → plugwise-1.5.1}/plugwise/constants.py +14 -14
- {plugwise-1.5.0 → plugwise-1.5.1}/plugwise/data.py +6 -6
- {plugwise-1.5.0 → plugwise-1.5.1}/plugwise/helper.py +51 -57
- {plugwise-1.5.0 → plugwise-1.5.1}/plugwise/legacy/data.py +3 -3
- {plugwise-1.5.0 → plugwise-1.5.1}/plugwise/util.py +2 -4
- {plugwise-1.5.0 → plugwise-1.5.1}/plugwise.egg-info/PKG-INFO +1 -1
- {plugwise-1.5.0 → plugwise-1.5.1}/pyproject.toml +2 -2
- {plugwise-1.5.0 → plugwise-1.5.1}/LICENSE +0 -0
- {plugwise-1.5.0 → plugwise-1.5.1}/README.md +0 -0
- {plugwise-1.5.0 → plugwise-1.5.1}/plugwise/__init__.py +0 -0
- {plugwise-1.5.0 → plugwise-1.5.1}/plugwise/common.py +0 -0
- {plugwise-1.5.0 → plugwise-1.5.1}/plugwise/exceptions.py +0 -0
- {plugwise-1.5.0 → plugwise-1.5.1}/plugwise/legacy/helper.py +0 -0
- {plugwise-1.5.0 → plugwise-1.5.1}/plugwise/legacy/smile.py +0 -0
- {plugwise-1.5.0 → plugwise-1.5.1}/plugwise/py.typed +0 -0
- {plugwise-1.5.0 → plugwise-1.5.1}/plugwise/smile.py +0 -0
- {plugwise-1.5.0 → plugwise-1.5.1}/plugwise.egg-info/SOURCES.txt +0 -0
- {plugwise-1.5.0 → plugwise-1.5.1}/plugwise.egg-info/dependency_links.txt +0 -0
- {plugwise-1.5.0 → plugwise-1.5.1}/plugwise.egg-info/requires.txt +0 -0
- {plugwise-1.5.0 → plugwise-1.5.1}/plugwise.egg-info/top_level.txt +0 -0
- {plugwise-1.5.0 → plugwise-1.5.1}/setup.cfg +0 -0
- {plugwise-1.5.0 → plugwise-1.5.1}/setup.py +0 -0
- {plugwise-1.5.0 → plugwise-1.5.1}/tests/test_adam.py +0 -0
- {plugwise-1.5.0 → plugwise-1.5.1}/tests/test_anna.py +0 -0
- {plugwise-1.5.0 → plugwise-1.5.1}/tests/test_generic.py +0 -0
- {plugwise-1.5.0 → plugwise-1.5.1}/tests/test_init.py +0 -0
- {plugwise-1.5.0 → plugwise-1.5.1}/tests/test_legacy_anna.py +0 -0
- {plugwise-1.5.0 → plugwise-1.5.1}/tests/test_legacy_generic.py +0 -0
- {plugwise-1.5.0 → plugwise-1.5.1}/tests/test_legacy_p1.py +0 -0
- {plugwise-1.5.0 → plugwise-1.5.1}/tests/test_legacy_stretch.py +0 -0
- {plugwise-1.5.0 → plugwise-1.5.1}/tests/test_p1.py +0 -0
@@ -123,7 +123,7 @@ P1_LEGACY_MEASUREMENTS: Final[dict[str, UOM]] = {
|
|
123
123
|
# radiator_valve: 'uncorrected_temperature', 'temperature_offset'
|
124
124
|
|
125
125
|
DEVICE_MEASUREMENTS: Final[dict[str, DATA | UOM]] = {
|
126
|
-
"humidity": UOM(
|
126
|
+
"humidity": UOM(NONE), # Specific for a Jip
|
127
127
|
"illuminance": UOM(UNIT_LUMEN), # Specific for an Anna
|
128
128
|
"temperature": UOM(TEMP_CELSIUS), # HA Core thermostat current_temperature
|
129
129
|
"thermostat": DATA("setpoint", TEMP_CELSIUS), # HA Core thermostat setpoint
|
@@ -425,7 +425,7 @@ class SmileBinarySensors(TypedDict, total=False):
|
|
425
425
|
class SmileSensors(TypedDict, total=False):
|
426
426
|
"""Smile Sensors class."""
|
427
427
|
|
428
|
-
battery:
|
428
|
+
battery: int
|
429
429
|
cooling_activation_outdoor_temperature: float
|
430
430
|
cooling_deactivation_threshold: float
|
431
431
|
dhw_temperature: float
|
@@ -434,11 +434,11 @@ class SmileSensors(TypedDict, total=False):
|
|
434
434
|
electricity_consumed: float
|
435
435
|
electricity_consumed_interval: float
|
436
436
|
electricity_consumed_off_peak_cumulative: float
|
437
|
-
electricity_consumed_off_peak_interval:
|
438
|
-
electricity_consumed_off_peak_point:
|
437
|
+
electricity_consumed_off_peak_interval: float
|
438
|
+
electricity_consumed_off_peak_point: float
|
439
439
|
electricity_consumed_peak_cumulative: float
|
440
|
-
electricity_consumed_peak_interval:
|
441
|
-
electricity_consumed_peak_point:
|
440
|
+
electricity_consumed_peak_interval: float
|
441
|
+
electricity_consumed_peak_point: float
|
442
442
|
electricity_consumed_point: float
|
443
443
|
electricity_phase_one_consumed: float
|
444
444
|
electricity_phase_two_consumed: float
|
@@ -449,20 +449,20 @@ class SmileSensors(TypedDict, total=False):
|
|
449
449
|
electricity_produced: float
|
450
450
|
electricity_produced_interval: float
|
451
451
|
electricity_produced_off_peak_cumulative: float
|
452
|
-
electricity_produced_off_peak_interval:
|
453
|
-
electricity_produced_off_peak_point:
|
452
|
+
electricity_produced_off_peak_interval: float
|
453
|
+
electricity_produced_off_peak_point: float
|
454
454
|
electricity_produced_peak_cumulative: float
|
455
|
-
electricity_produced_peak_interval:
|
456
|
-
electricity_produced_peak_point:
|
455
|
+
electricity_produced_peak_interval: float
|
456
|
+
electricity_produced_peak_point: float
|
457
457
|
electricity_produced_point: float
|
458
458
|
gas_consumed_cumulative: float
|
459
459
|
gas_consumed_interval: float
|
460
460
|
humidity: float
|
461
461
|
illuminance: float
|
462
462
|
intended_boiler_temperature: float
|
463
|
-
modulation_level:
|
463
|
+
modulation_level: int
|
464
464
|
net_electricity_cumulative: float
|
465
|
-
net_electricity_point:
|
465
|
+
net_electricity_point: float
|
466
466
|
outdoor_air_temperature: float
|
467
467
|
outdoor_temperature: float
|
468
468
|
return_temperature: float
|
@@ -470,7 +470,7 @@ class SmileSensors(TypedDict, total=False):
|
|
470
470
|
setpoint_high: float
|
471
471
|
setpoint_low: float
|
472
472
|
temperature_difference: float
|
473
|
-
valve_position:
|
473
|
+
valve_position: int
|
474
474
|
voltage_phase_one: float
|
475
475
|
voltage_phase_two: float
|
476
476
|
voltage_phase_three: float
|
@@ -552,7 +552,7 @@ class DeviceData(TypedDict, total=False):
|
|
552
552
|
last_used: str | None
|
553
553
|
select_schedule: str
|
554
554
|
|
555
|
-
|
555
|
+
climate_mode: str
|
556
556
|
# Extra for Adam Master Thermostats
|
557
557
|
control_state: str | bool
|
558
558
|
|
@@ -240,16 +240,16 @@ class SmileData(SmileHelper):
|
|
240
240
|
data["select_schedule"] = sel_schedule
|
241
241
|
self._count += 2
|
242
242
|
|
243
|
-
#
|
244
|
-
data["
|
243
|
+
# Set HA climate HVACMode: auto, heat, heat_cool, cool and off
|
244
|
+
data["climate_mode"] = "auto"
|
245
245
|
self._count += 1
|
246
246
|
if sel_schedule in (NONE, OFF):
|
247
|
-
data["
|
247
|
+
data["climate_mode"] = "heat"
|
248
248
|
if self._cooling_present:
|
249
|
-
data["
|
249
|
+
data["climate_mode"] = "cool" if self.check_reg_mode("cooling") else "heat_cool"
|
250
250
|
|
251
251
|
if self.check_reg_mode("off"):
|
252
|
-
data["
|
252
|
+
data["climate_mode"] = "off"
|
253
253
|
|
254
254
|
if NONE not in avail_schedules:
|
255
255
|
self._get_schedule_states_with_off(
|
@@ -273,7 +273,7 @@ class SmileData(SmileHelper):
|
|
273
273
|
loc_schedule_states: dict[str, str] = {}
|
274
274
|
for schedule in schedules:
|
275
275
|
loc_schedule_states[schedule] = "off"
|
276
|
-
if schedule == selected and data["
|
276
|
+
if schedule == selected and data["climate_mode"] == "auto":
|
277
277
|
loc_schedule_states[schedule] = "on"
|
278
278
|
self._schedule_old_states[location] = loc_schedule_states
|
279
279
|
|
@@ -269,6 +269,10 @@ class SmileHelper(SmileCommon):
|
|
269
269
|
for appliance in self._domain_objects.findall("./appliance"):
|
270
270
|
appl = Munch()
|
271
271
|
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
|
+
|
272
276
|
# Extend device_class name of Plugs (Plugwise and Aqara) - Pw-Beta Issue #739
|
273
277
|
description = appliance.find("description").text
|
274
278
|
if description is not None and (
|
@@ -291,6 +295,10 @@ class SmileHelper(SmileCommon):
|
|
291
295
|
elif appl.pwclass not in THERMOSTAT_CLASSES:
|
292
296
|
appl.location = self._home_location
|
293
297
|
|
298
|
+
# Don't show orphaned thermostat-types
|
299
|
+
if appl.pwclass in THERMOSTAT_CLASSES and appl.location is None:
|
300
|
+
continue
|
301
|
+
|
294
302
|
appl.dev_id = appliance.attrib["id"]
|
295
303
|
appl.name = appliance.find("name").text
|
296
304
|
appl.model = None
|
@@ -301,24 +309,15 @@ class SmileHelper(SmileCommon):
|
|
301
309
|
appl.zigbee_mac = None
|
302
310
|
appl.vendor_name = None
|
303
311
|
|
304
|
-
#
|
305
|
-
# Skip on heater_central when no active device present
|
312
|
+
# Collect appliance info, skip orphaned/removed devices
|
306
313
|
if not (appl := self._appliance_info_finder(appl, appliance)):
|
307
314
|
continue
|
308
315
|
|
309
|
-
# Skip orphaned heater_central (Core Issue #104433)
|
310
|
-
if appl.pwclass == "heater_central" and appl.dev_id != self._heater_id:
|
311
|
-
continue
|
312
|
-
|
313
316
|
# P1: for gateway and smartmeter switch device_id - part 1
|
314
317
|
# This is done to avoid breakage in HA Core
|
315
318
|
if appl.pwclass == "gateway" and self.smile_type == "power":
|
316
319
|
appl.dev_id = appl.location
|
317
320
|
|
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
|
-
|
322
321
|
self._create_gw_devices(appl)
|
323
322
|
|
324
323
|
# For P1 collect the connected SmartMeter info
|
@@ -354,23 +353,32 @@ class SmileHelper(SmileCommon):
|
|
354
353
|
self.loc_data[loc.loc_id] = {"name": loc.name}
|
355
354
|
|
356
355
|
def _p1_smartmeter_info_finder(self, appl: Munch) -> None:
|
357
|
-
"""Collect P1 DSMR
|
356
|
+
"""Collect P1 DSMR SmartMeter info."""
|
358
357
|
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
|
+
|
359
366
|
appl.dev_id = self.gateway_id
|
367
|
+
appl.firmware = module_data["firmware_version"]
|
368
|
+
appl.hardware = module_data["hardware_version"]
|
360
369
|
appl.location = loc_id
|
361
370
|
appl.mac = None
|
362
|
-
appl.model =
|
363
|
-
appl.model_id = None
|
371
|
+
appl.model = module_data["vendor_model"]
|
372
|
+
appl.model_id = None # don't use model_id for SmartMeter
|
364
373
|
appl.name = "P1"
|
365
374
|
appl.pwclass = "smartmeter"
|
375
|
+
appl.vendor_name = module_data["vendor_name"]
|
366
376
|
appl.zigbee_mac = None
|
367
|
-
location = self._domain_objects.find(f'./location[@id="{loc_id}"]')
|
368
|
-
appl = self._energy_device_info_finder(appl, location)
|
369
377
|
|
370
378
|
self._create_gw_devices(appl)
|
371
379
|
|
372
380
|
def _appliance_info_finder(self, appl: Munch, appliance: etree) -> Munch:
|
373
|
-
"""Collect
|
381
|
+
"""Collect info for all appliances found."""
|
374
382
|
match appl.pwclass:
|
375
383
|
case "gateway":
|
376
384
|
# Collect gateway device info
|
@@ -382,45 +390,28 @@ class SmileHelper(SmileCommon):
|
|
382
390
|
# Collect heater_central device info
|
383
391
|
self._appl_heater_central_info(appl, appliance, False) # False means non-legacy device
|
384
392
|
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
|
385
414
|
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
|
424
415
|
|
425
416
|
def _appl_gateway_info(self, appl: Munch, appliance: etree) -> Munch:
|
426
417
|
"""Helper-function for _appliance_info_finder()."""
|
@@ -725,15 +716,18 @@ class SmileHelper(SmileCommon):
|
|
725
716
|
Collect the availability-status for wireless connected devices.
|
726
717
|
"""
|
727
718
|
if self.smile(ADAM):
|
728
|
-
#
|
719
|
+
# Try collecting for a Plug
|
729
720
|
locator = "./logs/interval_log/electricity_interval_meter"
|
730
721
|
mod_type = "electricity_interval_meter"
|
731
722
|
module_data = self._get_module_data(appliance, locator, mod_type)
|
732
|
-
if module_data["
|
733
|
-
#
|
723
|
+
if not module_data["contents"]:
|
724
|
+
# Try collecting for a wireless thermostat
|
734
725
|
locator = "./logs/point_log[type='thermostat']/thermostat"
|
735
726
|
mod_type = "thermostat"
|
736
727
|
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
|
737
731
|
|
738
732
|
if module_data["reachable"] is not None:
|
739
733
|
data["available"] = module_data["reachable"]
|
@@ -86,8 +86,8 @@ class SmileLegacyData(SmileLegacyHelper):
|
|
86
86
|
data["select_schedule"] = sel_schedule
|
87
87
|
self._count += 2
|
88
88
|
|
89
|
-
#
|
90
|
-
data["
|
89
|
+
# Set HA climate HVACMode: auto, heat
|
90
|
+
data["climate_mode"] = "auto"
|
91
91
|
self._count += 1
|
92
92
|
if sel_schedule in (NONE, OFF):
|
93
|
-
data["
|
93
|
+
data["climate_mode"] = "heat"
|
@@ -115,7 +115,7 @@ def check_model(name: str | None, vendor_name: str | None) -> str | None:
|
|
115
115
|
if name is not None and "lumi.plug" in name:
|
116
116
|
return "Aqara Smart Plug"
|
117
117
|
|
118
|
-
return name
|
118
|
+
return name # pragma: no cover
|
119
119
|
|
120
120
|
|
121
121
|
def common_match_cases(
|
@@ -173,10 +173,8 @@ def format_measure(measure: str, unit: str) -> float | int:
|
|
173
173
|
result = float(f"{round(float_measure, 1):.1f}")
|
174
174
|
elif abs(float_measure) < 10:
|
175
175
|
result = float(f"{round(float_measure, 2):.2f}")
|
176
|
-
elif abs(float_measure) >= 10
|
176
|
+
elif abs(float_measure) >= 10:
|
177
177
|
result = float(f"{round(float_measure, 1):.1f}")
|
178
|
-
elif abs(float_measure) >= 100:
|
179
|
-
result = int(round(float_measure))
|
180
178
|
|
181
179
|
return result
|
182
180
|
|
@@ -1,10 +1,10 @@
|
|
1
1
|
[build-system]
|
2
|
-
requires = ["setuptools~=75.1", "wheel~=0.
|
2
|
+
requires = ["setuptools~=75.1", "wheel~=0.45.0"]
|
3
3
|
build-backend = "setuptools.build_meta"
|
4
4
|
|
5
5
|
[project]
|
6
6
|
name = "plugwise"
|
7
|
-
version = "1.5.
|
7
|
+
version = "1.5.1"
|
8
8
|
license = {file = "LICENSE"}
|
9
9
|
description = "Plugwise Smile (Adam/Anna/P1) and Stretch module for Python 3."
|
10
10
|
readme = "README.md"
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|