Skip to content

Commit 9fae322

Browse files
authored
Merge pull request #678 from plugwise/improve-241222
Continuous improvement
2 parents 5806452 + 566f266 commit 9fae322

File tree

4 files changed

+58
-93
lines changed

4 files changed

+58
-93
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
## Ongoing
4+
5+
- Continuous improvements [#678](https://github.com/plugwise/python-plugwise/pull/678)
6+
37
## v1.6.4
48

59
- Continuous improvements [#662](https://github.com/plugwise/python-plugwise/pull/662)

plugwise/helper.py

Lines changed: 24 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,8 @@ def __init__(self) -> None:
233233
self._elga: bool
234234
self._gw_allowed_modes: list[str] = []
235235
self._heater_id: str
236-
self._home_location: str
236+
self._home_loc_id: str
237+
self._home_location: etree
237238
self._is_thermostat: bool
238239
self._last_active: dict[str, str | None]
239240
self._last_modified: dict[str, str] = {}
@@ -311,10 +312,10 @@ def _all_appliances(self) -> None:
311312
appl.location = None
312313
if (appl_loc := appliance.find("location")) is not None:
313314
appl.location = appl_loc.attrib["id"]
314-
# Don't assign the _home_location to thermostat-devices without a location,
315+
# Don't assign the _home_loc_id to thermostat-devices without a location,
315316
# they are not active
316317
elif appl.pwclass not in THERMOSTAT_CLASSES:
317-
appl.location = self._home_location
318+
appl.location = self._home_loc_id
318319

319320
# Don't show orphaned thermostat-types
320321
if appl.pwclass in THERMOSTAT_CLASSES and appl.location is None:
@@ -350,22 +351,16 @@ def _get_p1_smartmeter_info(self) -> None:
350351
switched to maintain backward compatibility with existing implementations.
351352
"""
352353
appl = Munch()
353-
loc_id = next(iter(self._loc_data.keys()))
354-
if (
355-
location := self._domain_objects.find(f'./location[@id="{loc_id}"]')
356-
) is None:
357-
return
358-
359354
locator = MODULE_LOCATOR
360-
module_data = self._get_module_data(location, locator)
355+
module_data = self._get_module_data(self._home_location, locator)
361356
if not module_data["contents"]:
362357
LOGGER.error("No module data found for SmartMeter") # pragma: no cover
363358
return # pragma: no cover
364359
appl.available = None
365360
appl.entity_id = self.gateway_id
366361
appl.firmware = module_data["firmware_version"]
367362
appl.hardware = module_data["hardware_version"]
368-
appl.location = loc_id
363+
appl.location = self._home_loc_id
369364
appl.mac = None
370365
appl.model = module_data["vendor_model"]
371366
appl.model_id = None # don't use model_id for SmartMeter
@@ -375,8 +370,8 @@ def _get_p1_smartmeter_info(self) -> None:
375370
appl.zigbee_mac = None
376371

377372
# Replace the entity_id of the gateway by the smartmeter location_id
378-
self.gw_entities[loc_id] = self.gw_entities.pop(self.gateway_id)
379-
self.gateway_id = loc_id
373+
self.gw_entities[self._home_loc_id] = self.gw_entities.pop(self.gateway_id)
374+
self.gateway_id = self._home_loc_id
380375

381376
self._create_gw_entities(appl)
382377

@@ -398,10 +393,14 @@ def _all_locations(self) -> None:
398393
for location in locations:
399394
loc.name = location.find("name").text
400395
loc.loc_id = location.attrib["id"]
401-
if loc.name == "Home":
402-
self._home_location = loc.loc_id
403-
404396
self._loc_data[loc.loc_id] = {"name": loc.name}
397+
if loc.name != "Home":
398+
continue
399+
400+
self._home_loc_id = loc.loc_id
401+
self._home_location = self._domain_objects.find(
402+
f"./location[@id='{loc.loc_id}']"
403+
)
405404

406405
def _appliance_info_finder(self, appl: Munch, appliance: etree) -> Munch:
407406
"""Collect info for all appliances found."""
@@ -532,7 +531,7 @@ def _get_measurement_data(self, entity_id: str) -> GwEntityData:
532531
# !! DON'T CHANGE below two if-lines, will break stuff !!
533532
if self.smile_type == "power":
534533
if entity["dev_class"] == "smartmeter":
535-
data.update(self._power_data_from_location(entity["location"]))
534+
data.update(self._power_data_from_location())
536535

537536
return data
538537

@@ -574,18 +573,17 @@ def _get_measurement_data(self, entity_id: str) -> GwEntityData:
574573

575574
return data
576575

577-
def _power_data_from_location(self, loc_id: str) -> GwEntityData:
576+
def _power_data_from_location(self) -> GwEntityData:
578577
"""Helper-function for smile.py: _get_entity_data().
579578
580-
Collect the power-data based on Location ID, from LOCATIONS.
579+
Collect the power-data from the Home location.
581580
"""
582581
data: GwEntityData = {"sensors": {}}
583582
loc = Munch()
584583
log_list: list[str] = ["point_log", "cumulative_log", "interval_log"]
585584
t_string = "tariff"
586585

587-
search = self._domain_objects
588-
loc.logs = search.find(f'./location[@id="{loc_id}"]/logs')
586+
loc.logs = self._home_location.find("./logs")
589587
for loc.measurement, loc.attrs in P1_MEASUREMENTS.items():
590588
for loc.log_type in log_list:
591589
self._collect_power_values(data, loc, t_string)
@@ -764,31 +762,14 @@ def _get_gateway_mode(
764762
self._count += 1
765763

766764
def _get_gateway_outdoor_temp(self, entity_id: str, data: GwEntityData) -> None:
767-
"""Adam & Anna: the Smile outdoor_temperature is present in DOMAIN_OBJECTS and LOCATIONS.
768-
769-
Available under the Home location.
770-
"""
765+
"""Adam & Anna: the Smile outdoor_temperature is present in the Home location."""
771766
if self._is_thermostat and entity_id == self.gateway_id:
772-
outdoor_temperature = self._object_value(
773-
self._home_location, "outdoor_temperature"
774-
)
775-
if outdoor_temperature is not None:
776-
data.update({"sensors": {"outdoor_temperature": outdoor_temperature}})
767+
locator = "./logs/point_log[type='outdoor_temperature']/period/measurement"
768+
if (found := self._home_location.find(locator)) is not None:
769+
value = format_measure(found.text, NONE)
770+
data.update({"sensors": {"outdoor_temperature": value}})
777771
self._count += 1
778772

779-
def _object_value(self, obj_id: str, measurement: str) -> float | int | None:
780-
"""Helper-function for smile.py: _get_entity_data().
781-
782-
Obtain the value/state for the given object from a location in DOMAIN_OBJECTS
783-
"""
784-
val: float | int | None = None
785-
search = self._domain_objects
786-
locator = f'./location[@id="{obj_id}"]/logs/point_log[type="{measurement}"]/period/measurement'
787-
if (found := search.find(locator)) is not None:
788-
val = format_measure(found.text, NONE)
789-
790-
return val
791-
792773
def _process_c_heating_state(self, data: GwEntityData) -> None:
793774
"""Helper-function for _get_measurement_data().
794775

plugwise/legacy/helper.py

Lines changed: 14 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ def __init__(self) -> None:
6666
self._count: int
6767
self._domain_objects: etree
6868
self._heater_id: str
69-
self._home_location: str
69+
self._home_loc_id: str
7070
self._is_thermostat: bool
7171
self._last_modified: dict[str, str] = {}
7272
self._loc_data: dict[str, ThermoLoc]
@@ -115,7 +115,7 @@ def _all_appliances(self) -> None:
115115
):
116116
continue # pragma: no cover
117117

118-
appl.location = self._home_location
118+
appl.location = self._home_loc_id
119119
appl.entity_id = appliance.attrib["id"]
120120
appl.name = appliance.find("name").text
121121
# Extend device_class name when a Circle/Stealth is type heater_central -- Pw-Beta Issue #739
@@ -161,7 +161,7 @@ def _all_locations(self) -> None:
161161

162162
# Legacy Anna without outdoor_temp and Stretches have no locations, create fake location-data
163163
if not (locations := self._locations.findall("./location")):
164-
self._home_location = FAKE_LOC
164+
self._home_loc_id = FAKE_LOC
165165
self._loc_data[FAKE_LOC] = {"name": "Home"}
166166
return
167167

@@ -174,11 +174,11 @@ def _all_locations(self) -> None:
174174
continue
175175

176176
if loc.name == "Home":
177-
self._home_location = loc.loc_id
177+
self._home_loc_id = loc.loc_id
178178
# Replace location-name for P1 legacy, can contain privacy-related info
179179
if self.smile_type == "power":
180180
loc.name = "Home"
181-
self._home_location = loc.loc_id
181+
self._home_loc_id = loc.loc_id
182182

183183
self._loc_data[loc.loc_id] = {"name": loc.name}
184184

@@ -187,15 +187,15 @@ def _create_legacy_gateway(self) -> None:
187187
188188
Use the home_location or FAKE_APPL as entity id.
189189
"""
190-
self.gateway_id = self._home_location
190+
self.gateway_id = self._home_loc_id
191191
if self.smile_type == "power":
192192
self.gateway_id = FAKE_APPL
193193

194194
self.gw_entities[self.gateway_id] = {"dev_class": "gateway"}
195195
self._count += 1
196196
for key, value in {
197197
"firmware": str(self.smile_fw_version),
198-
"location": self._home_location,
198+
"location": self._home_loc_id,
199199
"mac_address": self.smile_mac_address,
200200
"model": self.smile_model,
201201
"name": self.smile_name,
@@ -272,7 +272,7 @@ def _get_measurement_data(self, entity_id: str) -> GwEntityData:
272272
Collect the appliance-data based on entity_id.
273273
"""
274274
data: GwEntityData = {"binary_sensors": {}, "sensors": {}, "switches": {}}
275-
# Get P1 smartmeter data from LOCATIONS or MODULES
275+
# Get P1 smartmeter data from MODULES
276276
entity = self.gw_entities[entity_id]
277277
# !! DON'T CHANGE below two if-lines, will break stuff !!
278278
if self.smile_type == "power":
@@ -294,14 +294,13 @@ def _get_measurement_data(self, entity_id: str) -> GwEntityData:
294294
if appliance.find("type").text in ACTUATOR_CLASSES:
295295
self._get_actuator_functionalities(appliance, entity, data)
296296

297-
# Adam & Anna: the Smile outdoor_temperature is present in DOMAIN_OBJECTS and LOCATIONS - under Home
298-
# The outdoor_temperature present in APPLIANCES is a local sensor connected to the active device
297+
# Anna: the Smile outdoor_temperature is present in the Home location
298+
# For some Anna's LOCATIONS is empty, falling back to domain_objects!
299299
if self._is_thermostat and entity_id == self.gateway_id:
300-
outdoor_temperature = self._object_value(
301-
self._home_location, "outdoor_temperature"
302-
)
303-
if outdoor_temperature is not None:
304-
data.update({"sensors": {"outdoor_temperature": outdoor_temperature}})
300+
locator = f"./location[@id='{self._home_loc_id}']/logs/point_log[type='outdoor_temperature']/period/measurement"
301+
if (found := self._domain_objects.find(locator)) is not None:
302+
value = format_measure(found.text, NONE)
303+
data.update({"sensors": {"outdoor_temperature": value}})
305304
self._count += 1
306305

307306
if "c_heating_state" in data:
@@ -396,20 +395,6 @@ def _get_actuator_functionalities(
396395
act_item = cast(ActuatorType, item)
397396
data[act_item] = temp_dict
398397

399-
def _object_value(self, obj_id: str, measurement: str) -> float | int | None:
400-
"""Helper-function for smile.py: _get_entity_data().
401-
402-
Obtain the value/state for the given object from a location in DOMAIN_OBJECTS
403-
"""
404-
val: float | int | None = None
405-
search = self._domain_objects
406-
locator = f'./location[@id="{obj_id}"]/logs/point_log[type="{measurement}"]/period/measurement'
407-
if (found := search.find(locator)) is not None:
408-
val = format_measure(found.text, NONE)
409-
return val
410-
411-
return val
412-
413398
def _preset(self) -> str | None:
414399
"""Helper-function for smile.py: _climate_data().
415400

plugwise/util.py

Lines changed: 16 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
SPECIAL_FORMAT,
2121
SPECIALS,
2222
SWITCHES,
23-
TEMP_CELSIUS,
2423
UOM,
2524
BinarySensorType,
2625
GwEntityData,
@@ -154,26 +153,22 @@ def escape_illegal_xml_characters(xmldata: str) -> str:
154153
def format_measure(measure: str, unit: str) -> float | int:
155154
"""Format measure to correct type."""
156155
result: float | int = 0
157-
try:
158-
result = int(measure)
159-
if unit == TEMP_CELSIUS:
160-
result = float(measure)
161-
except ValueError:
162-
float_measure = float(measure)
163-
if unit == PERCENTAGE and 0 < float_measure <= 1:
164-
return int(float_measure * 100)
165-
166-
if unit == ENERGY_KILO_WATT_HOUR:
167-
float_measure = float_measure / 1000
168-
169-
if unit in SPECIAL_FORMAT:
170-
result = float(f"{round(float_measure, 3):.3f}")
171-
elif unit == ELECTRIC_POTENTIAL_VOLT:
172-
result = float(f"{round(float_measure, 1):.1f}")
173-
elif abs(float_measure) < 10:
174-
result = float(f"{round(float_measure, 2):.2f}")
175-
elif abs(float_measure) >= 10:
176-
result = float(f"{round(float_measure, 1):.1f}")
156+
157+
float_measure = float(measure)
158+
if unit == PERCENTAGE and 0 < float_measure <= 1:
159+
return int(float_measure * 100)
160+
161+
if unit == ENERGY_KILO_WATT_HOUR:
162+
float_measure = float_measure / 1000
163+
164+
if unit in SPECIAL_FORMAT:
165+
result = float(f"{round(float_measure, 3):.3f}")
166+
elif unit == ELECTRIC_POTENTIAL_VOLT:
167+
result = float(f"{round(float_measure, 1):.1f}")
168+
elif abs(float_measure) < 10:
169+
result = float(f"{round(float_measure, 2):.2f}")
170+
elif abs(float_measure) >= 10:
171+
result = float(f"{round(float_measure, 1):.1f}")
177172

178173
return result
179174

0 commit comments

Comments
 (0)