Skip to content

Commit 408682b

Browse files
CoMPaTechbouwew
authored andcommitted
Further progress
1 parent eca817e commit 408682b

4 files changed

Lines changed: 145 additions & 49 deletions

File tree

plugwise/common.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ def _get_groups(self) -> None:
197197
if self.smile.type == "power" or self.check_name(ANNA):
198198
return
199199

200-
for group in self._domain_objects.findall("./group"):
200+
for group in self._domain_objects.group:
201201
group_id = group.get("id")
202202
if group_id is None:
203203
continue # pragma: no cover
@@ -208,10 +208,10 @@ def _get_groups(self) -> None:
208208
group_name = group.find("name").text
209209
group_type = group.find("type").text
210210
if group_type in GROUP_TYPES:
211-
self.gw_entities[group_id] = {
212-
"dev_class": group_type,
211+
self.gw_entities[group.id] = {
212+
"dev_class": group.type,
213213
"model": "Group",
214-
"name": group_name,
214+
"name": group.name,
215215
"members": members,
216216
"vendor": "Plugwise",
217217
}

plugwise/helper.py

Lines changed: 31 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
ThermoLoc,
4444
ToggleNameType,
4545
)
46+
from plugwise.model import Appliance, ApplianceType, OffsetFunctionality
4647
from plugwise.util import (
4748
check_model,
4849
collect_power_values,
@@ -57,15 +58,6 @@
5758
from packaging import version
5859

5960

60-
def extend_plug_device_class(appl: Munch, appliance: etree.Element) -> None:
61-
"""Extend device_class name of Plugs (Plugwise and Aqara) - Pw-Beta Issue #739."""
62-
63-
if (description := appliance.description) is not None and (
64-
"ZigBee protocol" in description or "smart plug" in description
65-
):
66-
appl.pwclass = f"{appl.pwclass}_plug"
67-
68-
6961
def search_actuator_functionalities(
7062
appliance: etree.Element, actuator: str
7163
) -> etree.Element | None:
@@ -130,28 +122,34 @@ def _get_appliances(self) -> None:
130122

131123
# Don't collect data for the OpenThermGateway appliance, skip thermostat(s)
132124
# without actuator_functionalities, should be an orphaned device(s) (Core #81712)
133-
if appl.pwclass == "open_therm_gateway" or (
134-
appl.pwclass == "thermostat"
135-
and appliance.find("actuator_functionalities/") is None
125+
if appliance.type == ApplianceType.OPENTHERMGW or (
126+
appliance.type == ApplianceType.THERMOSTAT
127+
and appliance.actuator_functionalities is None
136128
):
137129
continue
138130

139-
if (appl_loc := appliance.location) is not None:
140-
appl.location = appl_loc.get("id")
131+
if appliance.location is not None:
132+
appl.fixed_location = appliance.id
141133
# Set location to the _home_loc_id when the appliance-location is not found,
142134
# except for thermostat-devices without a location, they are not active
143-
elif appl.pwclass not in THERMOSTAT_CLASSES:
144-
appl.location = self._home_loc_id
135+
elif appliance.type not in THERMOSTAT_CLASSES:
136+
appliance.fixed_location = self._home_loc_id
145137

146-
# Don't show orphaned (no location) thermostat-types
147-
if appl.pwclass in THERMOSTAT_CLASSES and appl.location is None:
138+
# Don't show orphaned thermostat-types
139+
if appliance.type in THERMOSTAT_CLASSES and appliance.location is None:
148140
continue
149141

150-
extend_plug_device_class(appl, appliance)
142+
# Extend device_class name of Plugs (Plugwise and Aqara) - Pw-Beta Issue #739
143+
if appliance.description is not None and (
144+
"ZigBee protocol" in appliance.description
145+
or "smart plug" in appliance.description
146+
):
147+
appliance.type = f"{appliance.type}_plug"
151148

152-
# Collect appliance info, skip orphaned/removed devices
153-
if not (appl := self._appliance_info_finder(appl, appliance)):
154-
continue
149+
# TODO: recreate functionality
150+
# # Collect appliance info, skip orphaned/removed devices
151+
# if not (appl := self._appliance_info_finder(appl, appliance)):
152+
# continue
155153

156154
self._create_gw_entities(appl)
157155

@@ -225,12 +223,13 @@ def _get_locations(self) -> None:
225223
"Error, location Home (building) not found!"
226224
) # pragma: no cover
227225

228-
def _appliance_info_finder(self, appl: Munch, appliance: etree.Element) -> Munch:
226+
def _appliance_info_finder(self, appliance: Appliance) -> Appliance:
229227
"""Collect info for all appliances found."""
230-
match appl.pwclass:
231-
case "gateway":
232-
# Collect gateway entity info
233-
return self._appl_gateway_info(appl, appliance)
228+
match application.type:
229+
# No longer needed since we have a Gateway
230+
# case "gateway":
231+
# # Collect gateway entity info
232+
# return self._appl_gateway_info(appl, appliance)
234233
case _ as dev_class if dev_class in THERMOSTAT_CLASSES:
235234
# Collect thermostat entity info
236235
return self._appl_thermostat_info(appl, appliance)
@@ -264,24 +263,14 @@ def _appliance_info_finder(self, appl: Munch, appliance: etree.Element) -> Munch
264263
case _: # pragma: no cover
265264
return Munch()
266265

267-
def _appl_gateway_info(self, appl: Munch, appliance: etree.Element) -> Munch:
266+
def _appl_gateway_info(self, appliance: Appliance) -> Appliance:
268267
"""Helper-function for _appliance_info_finder()."""
269-
self._gateway_id = appl.entity_id
270-
locator = "./gateway/firmware_version"
271-
appl.firmware = self._domain_objects.find(locator).text
272-
appl.hardware = self.smile.hw_version
273-
appl.mac = self.smile.mac_address
274-
appl.model = self.smile.model
275-
appl.model_id = self.smile.model_id
276-
appl.name = self.smile.name
277-
appl.vendor_name = "Plugwise"
268+
self._gateway_id = application.id
278269

279270
# Adam: collect the ZigBee MAC address of the Smile
280-
if self.check_name(ADAM):
281-
if (
282-
found := self._domain_objects.find(".//protocols/zig_bee_coordinator")
283-
) is not None:
284-
appl.zigbee_mac = found.find("mac_address").text
271+
if ADAM in appliance.name:
272+
if (found := appliance.protocols.zig_bee_coordinator) is not None:
273+
application.zigbee_mac = found.mac_address
285274

286275
# Also, collect regulation_modes and check for cooling, indicating cooling-mode is present
287276
self._reg_allowed_modes = self._get_appl_actuator_modes(

plugwise/model.py

Lines changed: 108 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""Plugwise models."""
22

3+
from enum import Enum
34
from typing import Any
45

56
from pydantic import BaseModel, ConfigDict, Field
@@ -167,12 +168,31 @@ class ZigBeeNode(WithID):
167168

168169

169170
# Appliance
171+
class ApplianceType(str, Enum):
172+
"""Define application types."""
173+
174+
GATEWAY = "gateway"
175+
OPENTHERMGW = "open_therm_gateway"
176+
THERMOSTAT = "thermostat"
177+
CHP = "central_heating_pump"
178+
CD = "computer_desktop"
179+
HC = "heater_central"
180+
HT = "hometheater"
181+
THERMO_RV = "thermostatic_radiator_valve"
182+
VA = "valve_actuator"
183+
WHV = "water_heater_vessel"
184+
ZONETHERMOMETER = "zone_thermometer"
185+
ZONETHERMOSTAT = "zone_thermostat"
186+
187+
# TODO we still need all the '{}_plug' things here eventually
188+
189+
170190
class Appliance(WithID):
171191
"""Plugwise Appliance."""
172192

173193
name: str
174194
description: str | None = None
175-
type: str
195+
type: ApplianceType
176196
created_date: str
177197
modified_date: str | list[str] | None = None
178198
deleted_date: str | None = None
@@ -184,6 +204,9 @@ class Appliance(WithID):
184204
dict[str, BaseFunctionality | list[BaseFunctionality]] | None
185205
) = None
186206

207+
# Internal processing
208+
fixed_location: str | None = None
209+
187210

188211
# Module
189212
class Module(WithID):
@@ -204,6 +227,54 @@ class Module(WithID):
204227
protocols: dict[str, Any] | None = None # ZigBeeNode, WLAN, LAN
205228

206229

230+
# Gateway
231+
class Gateway(Module):
232+
"""Plugwise Gateway."""
233+
234+
last_reset_date: str | list[str] | None = None
235+
last_boot_date: str | list[str] | None = None
236+
237+
project: dict[str, Any] | None = None
238+
gateway_environment: dict[str, Any] | None = None
239+
features: dict[str, Any] | None = None
240+
241+
242+
# Group
243+
class ApplianceRef(WithID):
244+
"""Group appliance reference."""
245+
246+
pass
247+
248+
249+
class AppliancesContainer(PWBase):
250+
"""Group container containing appliance IDs."""
251+
252+
appliance: list[ApplianceRef] | ApplianceRef
253+
254+
255+
class GroupType(str, Enum):
256+
"""Define group types."""
257+
258+
PUMPING = "pumping"
259+
SWITCHING = "switching"
260+
261+
262+
class Group(WithID):
263+
"""Group of appliances."""
264+
265+
name: str
266+
description: str | None = None
267+
type: GroupType | None = None
268+
269+
created_date: str
270+
modified_date: str | list[str] | None = None
271+
deleted_date: str | None = None
272+
273+
logs: dict[str, BaseLog | list[BaseLog]] | list[BaseLog] | None
274+
appliances: AppliancesContainer | None = None
275+
actuator_functionalities: dict[str, BaseFunctionality] | None = None
276+
277+
207278
# Location
208279
class Location(WithID):
209280
"""Plugwise Location."""
@@ -226,6 +297,8 @@ class DomainObjects(PWBase):
226297
"""Plugwise Domain Objects."""
227298

228299
appliance: list[Appliance] = []
300+
gateway: Gateway | list[Gateway] | None = None
301+
group: Group | list[Group] | None = None
229302
module: list[Module] = []
230303
location: list[Location] = []
231304
notification: Notification | list[Notification] | None = None
@@ -237,3 +310,37 @@ class Root(PWBase):
237310
"""Main XML definition."""
238311

239312
domain_objects: DomainObjects
313+
314+
315+
# Mappings
316+
317+
318+
class SwitchDeviceType(str, Enum):
319+
"""Define switch device types."""
320+
321+
TOGGLE = "toggle"
322+
LOCK = "lock"
323+
324+
325+
class SwitchFunctionType(str, Enum):
326+
"""Define switch function types."""
327+
328+
TOGGLE = "toggle_functionality"
329+
LOCK = "lock"
330+
NONE = None
331+
332+
333+
class SwitchActuatorType(str, Enum):
334+
"""Define switch actuator types."""
335+
336+
DHWCM = "domestic_hot_water_comfort_mode"
337+
CE = "cooling_enabled"
338+
339+
340+
class Switch(BaseModel):
341+
"""Switch/relay definition."""
342+
343+
device: SwitchDeviceType = SwitchDeviceType.TOGGLE
344+
func_type: SwitchFunctionType = SwitchFunctionType.TOGGLE
345+
act_type: SwitchActuatorType = SwitchActuatorType.CE
346+
func: SwitchFunctionType = SwitchFunctionType.NONE

plugwise/smile.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,10 @@
3838
from munch import Munch
3939
import xmltodict
4040

41-
from .model import Appliance, Root
41+
from .model import Appliance, Root, Switch
4242

4343

44-
def model_to_switch_items(model: str, state: str, switch: Munch) -> tuple[str, Munch]:
44+
def model_to_switch_items(model: str, state: str, switch: Switch) -> tuple[str, Switch]:
4545
"""Translate state and switch attributes based on model name.
4646
4747
Helper function for set_switch_state().

0 commit comments

Comments
 (0)