plugwise 0.35.4__py3-none-any.whl

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/constants.py ADDED
@@ -0,0 +1,547 @@
1
+ """Plugwise Smile constants."""
2
+ from __future__ import annotations
3
+
4
+ from collections import namedtuple
5
+ from dataclasses import dataclass
6
+ import logging
7
+ from typing import Final, Literal, TypedDict, get_args
8
+
9
+ LOGGER = logging.getLogger(__name__)
10
+
11
+ # Copied homeassistant.consts
12
+ ATTR_NAME: Final = "name"
13
+ ATTR_STATE: Final = "state"
14
+ ATTR_STATE_CLASS: Final = "state_class"
15
+ ATTR_UNIT_OF_MEASUREMENT: Final = "unit_of_measurement"
16
+ DEGREE: Final = "°"
17
+ ELECTRIC_POTENTIAL_VOLT: Final = "V"
18
+ ENERGY_KILO_WATT_HOUR: Final = "kWh"
19
+ ENERGY_WATT_HOUR: Final = "Wh"
20
+ PERCENTAGE: Final = "%"
21
+ POWER_WATT: Final = "W"
22
+ PRESET_AWAY: Final = "away"
23
+ PRESSURE_BAR: Final = "bar"
24
+ SIGNAL_STRENGTH_DECIBELS_MILLIWATT: Final = "dBm"
25
+ TEMP_CELSIUS: Final = "°C"
26
+ TEMP_KELVIN: Final = "°K"
27
+ TIME_MILLISECONDS: Final = "ms"
28
+ UNIT_LUMEN: Final = "lm"
29
+ VOLUME_CUBIC_METERS: Final = "m³"
30
+ VOLUME_CUBIC_METERS_PER_HOUR: Final = "m³/h"
31
+
32
+ ADAM: Final = "Adam"
33
+ ANNA: Final = "Smile Anna"
34
+ DEFAULT_TIMEOUT: Final = 30
35
+ DEFAULT_USERNAME: Final = "smile"
36
+ DEFAULT_PORT: Final = 80
37
+ DEFAULT_PW_MAX: Final = 30.0
38
+ DEFAULT_PW_MIN: Final = 4.0
39
+ DHW_SETPOINT: Final = "domestic_hot_water_setpoint"
40
+ FAKE_APPL: Final = "aaaa0000aaaa0000aaaa0000aaaa00aa"
41
+ FAKE_LOC: Final = "0000aaaa0000aaaa0000aaaa0000aa00"
42
+ HW_MODELS: Final[dict[str, str]] = {
43
+ "143.1": "ThermoTouch",
44
+ "159.2": "Adam",
45
+ "106-03": "Tom/Floor",
46
+ "158-01": "Lisa",
47
+ "160-01": "Plug",
48
+ "168-01": "Jip",
49
+ "038500": "Stick",
50
+ "070085": "Stick",
51
+ "120002": "Stick Legrand",
52
+ "120041": "Circle+ Legrand type E",
53
+ "120000": "Circle+ Legrand type F",
54
+ "090000": "Circle+ type B",
55
+ "090007": "Circle+ type B",
56
+ "090088": "Circle+ type E",
57
+ "070073": "Circle+ type F",
58
+ "090048": "Circle+ type G",
59
+ "120049": "Stealth M+",
60
+ "090188": "Stealth+",
61
+ "120040": "Circle Legrand type E",
62
+ "120001": "Circle Legrand type F",
63
+ "090079": "Circle type B",
64
+ "090087": "Circle type E",
65
+ "070140": "Circle type F",
66
+ "090093": "Circle type G",
67
+ "100025": "Circle",
68
+ "120048": "Stealth M",
69
+ "120029": "Stealth Legrand",
70
+ "090011": "Stealth",
71
+ "001200": "Stealth",
72
+ "080007": "Scan",
73
+ "110028": "Scan Legrand",
74
+ "070030": "Sense",
75
+ "120006": "Sense Legrand",
76
+ "070051": "Switch",
77
+ "080029": "Switch",
78
+ }
79
+
80
+ MAX_SETPOINT: Final[float] = 30.0
81
+ MIN_SETPOINT: Final[float] = 4.0
82
+ NONE: Final = "None"
83
+ OFF: Final = "off"
84
+
85
+ # XML data paths
86
+ APPLIANCES: Final = "/core/appliances"
87
+ DOMAIN_OBJECTS: Final = "/core/domain_objects"
88
+ LOCATIONS: Final = "/core/locations"
89
+ MODULES: Final = "/core/modules"
90
+ NOTIFICATIONS: Final = "/core/notifications"
91
+ RULES: Final = "/core/rules"
92
+ SYSTEM: Final = "/system"
93
+ STATUS: Final = "/system/status.xml"
94
+
95
+ UOM = namedtuple("UOM", "unit_of_measurement")
96
+ DATA = namedtuple("DATA", "name unit_of_measurement")
97
+
98
+ # P1 related measurements:
99
+ P1_MEASUREMENTS: Final[dict[str, UOM]] = {
100
+ "electricity_consumed": UOM(POWER_WATT),
101
+ "electricity_produced": UOM(POWER_WATT),
102
+ "electricity_phase_one_consumed": UOM(POWER_WATT),
103
+ "electricity_phase_two_consumed": UOM(POWER_WATT),
104
+ "electricity_phase_three_consumed": UOM(POWER_WATT),
105
+ "electricity_phase_one_produced": UOM(POWER_WATT),
106
+ "electricity_phase_two_produced": UOM(POWER_WATT),
107
+ "electricity_phase_three_produced": UOM(POWER_WATT),
108
+ "gas_consumed": UOM(VOLUME_CUBIC_METERS),
109
+ "voltage_phase_one": UOM(ELECTRIC_POTENTIAL_VOLT),
110
+ "voltage_phase_two": UOM(ELECTRIC_POTENTIAL_VOLT),
111
+ "voltage_phase_three": UOM(ELECTRIC_POTENTIAL_VOLT),
112
+ }
113
+ P1_LEGACY_MEASUREMENTS: Final[dict[str, UOM]] = {
114
+ "electricity_consumed": UOM(POWER_WATT),
115
+ "electricity_produced": UOM(POWER_WATT),
116
+ "gas_consumed": UOM(VOLUME_CUBIC_METERS),
117
+ }
118
+ # Thermostat and Plug/Stretch related measurements
119
+ # Excluded:
120
+ # zone_thermosstat: 'temperature_offset'
121
+ # radiator_valve: 'uncorrected_temperature', 'temperature_offset'
122
+
123
+ DEVICE_MEASUREMENTS: Final[dict[str, DATA | UOM]] = {
124
+ # HA Core thermostat current_temperature
125
+ "temperature": UOM(TEMP_CELSIUS),
126
+ # HA Core thermostat setpoint
127
+ "thermostat": DATA("setpoint", TEMP_CELSIUS),
128
+ # Specific for an Anna
129
+ "illuminance": UOM(UNIT_LUMEN),
130
+ # Specific for an Anna with heatpump extension installed
131
+ "cooling_activation_outdoor_temperature": UOM(TEMP_CELSIUS),
132
+ "cooling_deactivation_threshold": UOM(TEMP_CELSIUS),
133
+ # Specific for a Lisa a Tom/Floor
134
+ "battery": UOM(PERCENTAGE),
135
+ "temperature_difference": UOM(DEGREE),
136
+ "valve_position": UOM(PERCENTAGE),
137
+ # Specific for a Jip
138
+ "humidity": UOM(PERCENTAGE),
139
+ # Specific for a Plug
140
+ "electricity_consumed": UOM(POWER_WATT),
141
+ "electricity_produced": UOM(POWER_WATT),
142
+ "relay": UOM(NONE),
143
+ }
144
+
145
+ # Heater Central related measurements
146
+ HEATER_CENTRAL_MEASUREMENTS: Final[dict[str, DATA | UOM]] = {
147
+ "boiler_temperature": DATA("water_temperature", TEMP_CELSIUS),
148
+ "domestic_hot_water_mode": DATA("select_dhw_mode", NONE),
149
+ "domestic_hot_water_setpoint": UOM(TEMP_CELSIUS),
150
+ "domestic_hot_water_state": DATA("dhw_state", NONE),
151
+ "domestic_hot_water_temperature": DATA("dhw_temperature", TEMP_CELSIUS),
152
+ "elga_status_code": UOM(NONE),
153
+ "intended_boiler_temperature": UOM(
154
+ TEMP_CELSIUS
155
+ ), # Non-zero when heating, zero when dhw-heating
156
+ "central_heating_state": DATA(
157
+ "c_heating_state", NONE
158
+ ), # For Elga (heatpump) use this instead of intended_central_heating_state
159
+ "intended_central_heating_state": DATA(
160
+ "heating_state", NONE
161
+ ), # This key shows in general the heating-behavior better than c-h_state. except when connected to a heatpump
162
+ "modulation_level": UOM(PERCENTAGE),
163
+ "return_water_temperature": DATA("return_temperature", TEMP_CELSIUS),
164
+ # Used with the Elga heatpump - marcelveldt
165
+ "compressor_state": UOM(NONE),
166
+ "cooling_state": UOM(NONE),
167
+ # Available with the Loria and Elga (newer Anna firmware) heatpumps
168
+ "cooling_enabled": UOM(NONE),
169
+ # Next 2 keys are used to show the state of the gas-heater used next to the Elga heatpump - marcelveldt
170
+ "slave_boiler_state": UOM(NONE),
171
+ "flame_state": UOM(NONE), # Also present when there is a single gas-heater
172
+ "central_heater_water_pressure": DATA("water_pressure", PRESSURE_BAR),
173
+ # Legacy Anna: similar to flame-state on Anna/Adam
174
+ "boiler_state": DATA("flame_state", NONE),
175
+ # Legacy Anna: shows when heating is active, we don't show dhw_state, cannot be determined reliably
176
+ "intended_boiler_state": DATA("heating_state", NONE),
177
+ # Outdoor temperature from APPLIANCES - present for a heatpump
178
+ "outdoor_temperature": DATA("outdoor_air_temperature", TEMP_CELSIUS),
179
+ }
180
+
181
+ OBSOLETE_MEASUREMENTS: Final[tuple[str, ...]] = (
182
+ "central_heater_water_pressure",
183
+ "outdoor_temperature",
184
+ )
185
+
186
+ # Known types of Smiles and Stretches
187
+ SMILE = namedtuple("SMILE", "smile_type smile_name")
188
+ SMILES: Final[dict[str, SMILE]] = {
189
+ "smile_v2": SMILE("power", "Smile P1"),
190
+ "smile_v3": SMILE("power", "Smile P1"),
191
+ "smile_v4": SMILE("power", "Smile P1"),
192
+ "smile_open_therm_v2": SMILE("thermostat", ADAM),
193
+ "smile_open_therm_v3": SMILE("thermostat", ADAM),
194
+ "smile_thermo_v1": SMILE("thermostat", ANNA),
195
+ "smile_thermo_v3": SMILE("thermostat", ANNA),
196
+ "smile_thermo_v4": SMILE("thermostat", ANNA),
197
+ "stretch_v2": SMILE("stretch", "Stretch"),
198
+ "stretch_v3": SMILE("stretch", "Stretch"),
199
+ }
200
+ REQUIRE_APPLIANCES: Final[list[str]] = [
201
+ "smile_thermo_v1",
202
+ "smile_thermo_v3",
203
+ "smile_thermo_v4",
204
+ "stretch_v2",
205
+ "stretch_v3",
206
+ ]
207
+
208
+ # Class, Literal and related tuple-definitions
209
+
210
+ ACTUATOR_CLASSES: Final[tuple[str, ...]] = (
211
+ "heater_central",
212
+ "thermostat",
213
+ "thermostatic_radiator_valve",
214
+ "zone_thermometer",
215
+ "zone_thermostat",
216
+ )
217
+ ActuatorType = Literal[
218
+ "domestic_hot_water_setpoint",
219
+ "max_dhw_temperature",
220
+ "maximum_boiler_temperature",
221
+ "temperature_offset",
222
+ "thermostat",
223
+ ]
224
+ ACTIVE_ACTUATORS: Final[tuple[str, ...]] = get_args(ActuatorType)
225
+
226
+ ActuatorDataType = Literal[
227
+ "lower_bound",
228
+ "resolution",
229
+ "setpoint",
230
+ "setpoint_high",
231
+ "setpoint_low",
232
+ "upper_bound",
233
+ ]
234
+
235
+ ApplianceType = Literal[
236
+ "dev_class",
237
+ "firmware",
238
+ "hardware",
239
+ "location",
240
+ "mac_address",
241
+ "members",
242
+ "model",
243
+ "name",
244
+ "vendor",
245
+ "zigbee_mac_address",
246
+ ]
247
+
248
+ BinarySensorType = Literal[
249
+ "cooling_enabled",
250
+ "compressor_state",
251
+ "cooling_state",
252
+ "dhw_state",
253
+ "flame_state",
254
+ "heating_state",
255
+ "plugwise_notification",
256
+ "slave_boiler_state",
257
+ ]
258
+ BINARY_SENSORS: Final[tuple[str, ...]] = get_args(BinarySensorType)
259
+
260
+ LIMITS: Final[tuple[str, ...]] = (
261
+ "offset",
262
+ "setpoint",
263
+ "resolution",
264
+ "lower_bound",
265
+ "upper_bound",
266
+ )
267
+
268
+ SensorType = Literal[
269
+ "battery",
270
+ "cooling_activation_outdoor_temperature",
271
+ "cooling_deactivation_threshold",
272
+ "dhw_temperature",
273
+ "domestic_hot_water_setpoint",
274
+ "temperature",
275
+ "electricity_consumed",
276
+ "electricity_consumed_interval",
277
+ "electricity_consumed_off_peak_cumulative",
278
+ "electricity_consumed_off_peak_interval",
279
+ "electricity_consumed_off_peak_point",
280
+ "electricity_consumed_peak_cumulative",
281
+ "electricity_consumed_peak_interval",
282
+ "electricity_consumed_peak_point",
283
+ "electricity_consumed_point",
284
+ "electricity_phase_one_consumed",
285
+ "electricity_phase_two_consumed",
286
+ "electricity_phase_three_consumed",
287
+ "electricity_phase_one_produced",
288
+ "electricity_phase_two_produced",
289
+ "electricity_phase_three_produced",
290
+ "electricity_produced",
291
+ "electricity_produced_interval",
292
+ "electricity_produced_off_peak_cumulative",
293
+ "electricity_produced_off_peak_interval",
294
+ "electricity_produced_off_peak_point",
295
+ "electricity_produced_peak_cumulative",
296
+ "electricity_produced_peak_interval",
297
+ "electricity_produced_peak_point",
298
+ "electricity_produced_point",
299
+ "gas_consumed_cumulative",
300
+ "gas_consumed_interval",
301
+ "humidity",
302
+ "illuminance",
303
+ "intended_boiler_temperature",
304
+ "modulation_level",
305
+ "net_electricity_cumulative",
306
+ "net_electricity_point",
307
+ "outdoor_air_temperature",
308
+ "outdoor_temperature",
309
+ "return_temperature",
310
+ "setpoint",
311
+ "setpoint_high",
312
+ "setpoint_low",
313
+ "temperature_difference",
314
+ "valve_position",
315
+ "voltage_phase_one",
316
+ "voltage_phase_two",
317
+ "voltage_phase_three",
318
+ "water_pressure",
319
+ "water_temperature",
320
+ ]
321
+ SENSORS: Final[tuple[str, ...]] = get_args(SensorType)
322
+
323
+ SPECIAL_PLUG_TYPES: Final[tuple[str, ...]] = (
324
+ "central_heating_pump",
325
+ "valve_actuator",
326
+ "heater_electric",
327
+ )
328
+
329
+ SPECIAL_FORMAT: Final[tuple[str, ...]] = (ENERGY_KILO_WATT_HOUR, VOLUME_CUBIC_METERS)
330
+
331
+ SwitchType = Literal[
332
+ "cooling_ena_switch",
333
+ "dhw_cm_switch",
334
+ "lock",
335
+ "relay",
336
+ ]
337
+ SWITCHES: Final[tuple[str, ...]] = get_args(SwitchType)
338
+
339
+ SWITCH_GROUP_TYPES: Final[tuple[str, ...]] = ("switching", "report")
340
+
341
+ THERMOSTAT_CLASSES: Final[tuple[str, ...]] = (
342
+ "thermostat",
343
+ "thermo_sensor",
344
+ "zone_thermometer",
345
+ "zone_thermostat",
346
+ "thermostatic_radiator_valve",
347
+ )
348
+
349
+ ToggleNameType = Literal[
350
+ "cooling_ena_switch",
351
+ "dhw_cm_switch",
352
+ ]
353
+ TOGGLES: Final[dict[str, ToggleNameType]] = {
354
+ "cooling_enabled": "cooling_ena_switch",
355
+ "domestic_hot_water_comfort_mode": "dhw_cm_switch",
356
+ }
357
+
358
+ ZONE_THERMOSTATS: Final[tuple[str, ...]] = (
359
+ "thermostat",
360
+ "thermostatic_radiator_valve",
361
+ "zone_thermometer",
362
+ "zone_thermostat",
363
+ )
364
+
365
+
366
+ class GatewayData(TypedDict, total=False):
367
+ """The Gateway Data class."""
368
+
369
+ cooling_present: bool
370
+ gateway_id: str
371
+ heater_id: str
372
+ item_count: int
373
+ notifications: dict[str, dict[str, str]]
374
+ smile_name: str
375
+
376
+
377
+ class ModelData(TypedDict):
378
+ """The ModelData class."""
379
+
380
+ contents: bool
381
+ vendor_name: str | None
382
+ vendor_model: str | None
383
+ hardware_version: str | None
384
+ firmware_version: str | None
385
+ zigbee_mac_address: str | None
386
+ reachable: bool | None
387
+
388
+
389
+ class SmileBinarySensors(TypedDict, total=False):
390
+ """Smile Binary Sensors class."""
391
+
392
+ cooling_enabled: bool
393
+ compressor_state: bool
394
+ cooling_state: bool
395
+ dhw_state: bool
396
+ flame_state: bool
397
+ heating_state: bool
398
+ plugwise_notification: bool
399
+ slave_boiler_state: bool
400
+
401
+
402
+ class SmileSensors(TypedDict, total=False):
403
+ """Smile Sensors class."""
404
+
405
+ battery: float
406
+ cooling_activation_outdoor_temperature: float
407
+ cooling_deactivation_threshold: float
408
+ dhw_temperature: float
409
+ domestic_hot_water_setpoint: float
410
+ temperature: float
411
+ electricity_consumed: float
412
+ electricity_consumed_interval: float
413
+ electricity_consumed_off_peak_cumulative: float
414
+ electricity_consumed_off_peak_interval: int
415
+ electricity_consumed_off_peak_point: int
416
+ electricity_consumed_peak_cumulative: float
417
+ electricity_consumed_peak_interval: int
418
+ electricity_consumed_peak_point: int
419
+ electricity_consumed_point: float
420
+ electricity_phase_one_consumed: float
421
+ electricity_phase_two_consumed: float
422
+ electricity_phase_three_consumed: float
423
+ electricity_phase_one_produced: float
424
+ electricity_phase_two_produced: float
425
+ electricity_phase_three_produced: float
426
+ electricity_produced: float
427
+ electricity_produced_interval: float
428
+ electricity_produced_off_peak_cumulative: float
429
+ electricity_produced_off_peak_interval: int
430
+ electricity_produced_off_peak_point: int
431
+ electricity_produced_peak_cumulative: float
432
+ electricity_produced_peak_interval: int
433
+ electricity_produced_peak_point: int
434
+ electricity_produced_point: float
435
+ gas_consumed_cumulative: float
436
+ gas_consumed_interval: float
437
+ humidity: float
438
+ illuminance: float
439
+ intended_boiler_temperature: float
440
+ modulation_level: float
441
+ net_electricity_cumulative: float
442
+ net_electricity_point: int
443
+ outdoor_air_temperature: float
444
+ outdoor_temperature: float
445
+ return_temperature: float
446
+ setpoint: float
447
+ setpoint_high: float
448
+ setpoint_low: float
449
+ temperature_difference: float
450
+ valve_position: float
451
+ voltage_phase_one: float
452
+ voltage_phase_two: float
453
+ voltage_phase_three: float
454
+ water_pressure: float
455
+ water_temperature: float
456
+
457
+
458
+ class SmileSwitches(TypedDict, total=False):
459
+ """Smile Switches class."""
460
+
461
+ cooling_ena_switch: bool
462
+ dhw_cm_switch: bool
463
+ lock: bool
464
+ relay: bool
465
+
466
+
467
+ class ThermoLoc(TypedDict, total=False):
468
+ """Thermo Location class."""
469
+
470
+ name: str
471
+ master: str | None
472
+ master_prio: int
473
+ slaves: set[str]
474
+
475
+
476
+ class ActuatorData(TypedDict, total=False):
477
+ """Actuator data for thermostat types."""
478
+
479
+ lower_bound: float
480
+ resolution: float
481
+ setpoint: float
482
+ setpoint_high: float
483
+ setpoint_low: float
484
+ upper_bound: float
485
+
486
+
487
+ class DeviceData(TypedDict, total=False):
488
+ """The Device Data class, covering the collected and ordered output-data per device."""
489
+
490
+ # Appliance base data
491
+ dev_class: str
492
+ firmware: str | None
493
+ hardware: str
494
+ location: str
495
+ mac_address: str | None
496
+ members: list[str]
497
+ model: str
498
+ name: str
499
+ vendor: str
500
+ zigbee_mac_address: str | None
501
+
502
+ # For temporary use
503
+ cooling_enabled: bool
504
+ domestic_hot_water_setpoint: float
505
+ elga_status_code: int
506
+ c_heating_state: bool
507
+
508
+ # Device availability
509
+ available: bool | None
510
+
511
+ # Loria
512
+ select_dhw_mode: str
513
+ dhw_modes: list[str]
514
+
515
+ # Gateway
516
+ select_regulation_mode: str
517
+ regulation_modes: list[str]
518
+
519
+ # Master Thermostats
520
+ # Presets:
521
+ active_preset: str | None
522
+ preset_modes: list[str] | None
523
+ # Schedules:
524
+ available_schedules: list[str]
525
+ last_used: str | None
526
+ select_schedule: str
527
+
528
+ mode: str
529
+ # Extra for Adam Master Thermostats
530
+ control_state: str | bool
531
+
532
+ # Dict-types
533
+ binary_sensors: SmileBinarySensors
534
+ max_dhw_temperature: ActuatorData
535
+ maximum_boiler_temperature: ActuatorData
536
+ sensors: SmileSensors
537
+ switches: SmileSwitches
538
+ temperature_offset: ActuatorData
539
+ thermostat: ActuatorData
540
+
541
+
542
+ @dataclass
543
+ class PlugwiseData:
544
+ """Plugwise data provided as output."""
545
+
546
+ gateway: GatewayData
547
+ devices: dict[str, DeviceData]
plugwise/exceptions.py ADDED
@@ -0,0 +1,49 @@
1
+ """Plugwise Smile Exceptions."""
2
+
3
+
4
+ class PlugwiseException(Exception):
5
+ """Base error class for this Plugwise library."""
6
+
7
+
8
+ class ConnectionFailedError(PlugwiseException):
9
+ """Raised when unable to connect."""
10
+
11
+
12
+ class InvalidAuthentication(PlugwiseException):
13
+ """Raised when unable to authenticate."""
14
+
15
+
16
+ class InvalidSetupError(PlugwiseException):
17
+ """Raised when adding an Anna while an Adam exists."""
18
+
19
+
20
+ class PlugwiseError(PlugwiseException):
21
+ """Raise when a non-specific error happens."""
22
+
23
+
24
+ class UnsupportedDeviceError(PlugwiseException):
25
+ """Raised when device is not supported."""
26
+
27
+
28
+ class DeviceSetupError(PlugwiseException):
29
+ """Raised when device is missing critical setup data."""
30
+
31
+
32
+ class DeviceTimeoutError(PlugwiseException):
33
+ """Raised when device is not supported."""
34
+
35
+
36
+ class ErrorSendingCommandError(PlugwiseException):
37
+ """Raised when device is not accepting the command."""
38
+
39
+
40
+ class ResponseError(PlugwiseException):
41
+ """Raised when empty or error in response returned."""
42
+
43
+
44
+ class InvalidXMLError(PlugwiseException):
45
+ """Raised when response holds incomplete or invalid XML data."""
46
+
47
+
48
+ class XMLDataMissingError(PlugwiseException):
49
+ """Raised when xml data is empty."""