aiohomematic 2025.9.1__py3-none-any.whl → 2025.9.3__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.

Potentially problematic release.


This version of aiohomematic might be problematic. Click here for more details.

Files changed (55) hide show
  1. aiohomematic/caches/dynamic.py +1 -6
  2. aiohomematic/central/__init__.py +34 -23
  3. aiohomematic/central/xml_rpc_server.py +1 -1
  4. aiohomematic/client/__init__.py +35 -29
  5. aiohomematic/client/json_rpc.py +44 -12
  6. aiohomematic/client/xml_rpc.py +53 -20
  7. aiohomematic/const.py +2 -2
  8. aiohomematic/decorators.py +56 -21
  9. aiohomematic/model/__init__.py +1 -1
  10. aiohomematic/model/calculated/__init__.py +1 -1
  11. aiohomematic/model/calculated/climate.py +1 -1
  12. aiohomematic/model/calculated/data_point.py +3 -3
  13. aiohomematic/model/calculated/operating_voltage_level.py +7 -21
  14. aiohomematic/model/calculated/support.py +20 -0
  15. aiohomematic/model/custom/__init__.py +1 -1
  16. aiohomematic/model/custom/climate.py +18 -18
  17. aiohomematic/model/custom/cover.py +1 -1
  18. aiohomematic/model/custom/data_point.py +1 -1
  19. aiohomematic/model/custom/light.py +1 -1
  20. aiohomematic/model/custom/lock.py +1 -1
  21. aiohomematic/model/custom/siren.py +1 -1
  22. aiohomematic/model/custom/switch.py +1 -1
  23. aiohomematic/model/custom/valve.py +1 -1
  24. aiohomematic/model/data_point.py +24 -24
  25. aiohomematic/model/device.py +22 -21
  26. aiohomematic/model/event.py +3 -8
  27. aiohomematic/model/generic/__init__.py +1 -1
  28. aiohomematic/model/generic/binary_sensor.py +1 -1
  29. aiohomematic/model/generic/button.py +1 -1
  30. aiohomematic/model/generic/data_point.py +4 -6
  31. aiohomematic/model/generic/number.py +1 -1
  32. aiohomematic/model/generic/select.py +1 -1
  33. aiohomematic/model/generic/sensor.py +1 -1
  34. aiohomematic/model/generic/switch.py +4 -4
  35. aiohomematic/model/generic/text.py +1 -1
  36. aiohomematic/model/hub/binary_sensor.py +1 -1
  37. aiohomematic/model/hub/button.py +2 -2
  38. aiohomematic/model/hub/data_point.py +4 -7
  39. aiohomematic/model/hub/number.py +1 -1
  40. aiohomematic/model/hub/select.py +2 -2
  41. aiohomematic/model/hub/sensor.py +1 -1
  42. aiohomematic/model/hub/switch.py +3 -3
  43. aiohomematic/model/hub/text.py +1 -1
  44. aiohomematic/model/support.py +1 -40
  45. aiohomematic/model/update.py +5 -4
  46. aiohomematic/property_decorators.py +511 -0
  47. aiohomematic/support.py +71 -85
  48. {aiohomematic-2025.9.1.dist-info → aiohomematic-2025.9.3.dist-info}/METADATA +7 -5
  49. aiohomematic-2025.9.3.dist-info/RECORD +78 -0
  50. aiohomematic_support/client_local.py +5 -5
  51. aiohomematic/model/decorators.py +0 -194
  52. aiohomematic-2025.9.1.dist-info/RECORD +0 -78
  53. {aiohomematic-2025.9.1.dist-info → aiohomematic-2025.9.3.dist-info}/WHEEL +0 -0
  54. {aiohomematic-2025.9.1.dist-info → aiohomematic-2025.9.3.dist-info}/licenses/LICENSE +0 -0
  55. {aiohomematic-2025.9.1.dist-info → aiohomematic-2025.9.3.dist-info}/top_level.txt +0 -0
aiohomematic/support.py CHANGED
@@ -49,6 +49,12 @@ from aiohomematic.const import (
49
49
  SysvarType,
50
50
  )
51
51
  from aiohomematic.exceptions import AioHomematicException, BaseHomematicException
52
+ from aiohomematic.property_decorators import (
53
+ Kind,
54
+ cached_property,
55
+ get_hm_property_by_kind,
56
+ get_hm_property_by_log_context,
57
+ )
52
58
 
53
59
  _LOGGER: Final = logging.getLogger(__name__)
54
60
 
@@ -465,13 +471,13 @@ def hash_sha256(value: Any) -> str:
465
471
 
466
472
  def _make_value_hashable(value: Any) -> Any:
467
473
  """Make a hashable object."""
468
- if isinstance(value, (tuple, list)):
474
+ if isinstance(value, tuple | list):
469
475
  return tuple(_make_value_hashable(e) for e in value)
470
476
 
471
477
  if isinstance(value, dict):
472
478
  return tuple(sorted((k, _make_value_hashable(v)) for k, v in value.items()))
473
479
 
474
- if isinstance(value, (set, frozenset)):
480
+ if isinstance(value, set | frozenset):
475
481
  return tuple(sorted(_make_value_hashable(e) for e in value))
476
482
 
477
483
  return value
@@ -514,7 +520,7 @@ def cleanup_text_from_html_tags(text: str) -> str:
514
520
  _BOUNDARY_MSG = "error_boundary"
515
521
 
516
522
 
517
- def _safe_context(context: Mapping[str, Any] | None) -> dict[str, Any]:
523
+ def _safe_log_context(context: Mapping[str, Any] | None) -> dict[str, Any]:
518
524
  """Extract safe context from a mapping."""
519
525
  ctx: dict[str, Any] = {}
520
526
  if not context:
@@ -534,57 +540,6 @@ def _safe_context(context: Mapping[str, Any] | None) -> dict[str, Any]:
534
540
  return ctx
535
541
 
536
542
 
537
- def build_log_context_from_obj(obj: Any | None) -> dict[str, Any]:
538
- """
539
- Extract structured context like device_id/channel/parameter from common objects.
540
-
541
- Tries best-effort extraction without raising. Returns a dict suitable for logger.extra.
542
- """
543
- ctx: dict[str, Any] = {}
544
- if obj is None:
545
- return ctx
546
- try:
547
- # DataPoint-like: has channel and parameter
548
- if hasattr(obj, "channel"):
549
- ch = getattr(obj, "channel")
550
- try:
551
- # channel address/id
552
- channel_address = ch.address if not callable(ch.address) else ch.address()
553
- ctx["channel"] = channel_address
554
- except Exception:
555
- # Fallback to str
556
- ctx["channel"] = str(ch)
557
- try:
558
- if (dev := ch.device if hasattr(ch, "device") else None) is not None:
559
- device_id = dev.id if not callable(dev.id) else dev.id()
560
- ctx["device_id"] = device_id
561
- except Exception:
562
- pass
563
- # Parameter on DataPoint-like
564
- if hasattr(obj, "parameter"):
565
- with contextlib.suppress(Exception):
566
- ctx["parameter"] = getattr(obj, "parameter")
567
-
568
- # Also support objects exposing address directly
569
- if "device_id" not in ctx and hasattr(obj, "device"):
570
- dev = getattr(obj, "device")
571
- try:
572
- device_id = dev.id if not callable(dev.id) else dev.id()
573
- ctx["device_id"] = device_id
574
- except Exception:
575
- pass
576
- if "channel" not in ctx and hasattr(obj, "address"):
577
- try:
578
- addr = obj.address if not callable(obj.address) else obj.address()
579
- ctx["channel"] = addr
580
- except Exception:
581
- pass
582
- except Exception:
583
- # Never allow context building to break the application
584
- return {}
585
- return ctx
586
-
587
-
588
543
  def log_boundary_error(
589
544
  logger: logging.Logger,
590
545
  *,
@@ -592,7 +547,8 @@ def log_boundary_error(
592
547
  action: str,
593
548
  err: Exception,
594
549
  level: int | None = None,
595
- context: Mapping[str, Any] | None = None,
550
+ log_context: Mapping[str, Any] | None = None,
551
+ message: str | None = None,
596
552
  ) -> None:
597
553
  """
598
554
  Log a boundary error with the provided logger.
@@ -602,42 +558,72 @@ def log_boundary_error(
602
558
  logging level if not explicitly provided. Additionally, it enriches the log
603
559
  record with extra context about the error and action boundaries.
604
560
 
605
- :param logger: The logger instance used to log the error.
606
- :type logger: logging.Logger
607
- :param boundary: The name of the boundary at which the error occurred.
608
- :type boundary: str
609
- :param action: The action being performed when the error occurred.
610
- :type action: str
611
- :param err: The exception instance representing the error to log.
612
- :type err: Exception
613
- :param level: The optional logging level. Defaults to WARNING for recoverable
614
- domain errors and ERROR for non-recoverable errors if not provided.
615
- :type level: int | None
616
- :param context: Optional mapping of additional information or context to
617
- include in the log record.
618
- :type context: Mapping[str, Any] | None
619
- :return: None. This function logs the provided information but does not
620
- return a value.
621
- :rtype: None
622
561
  """
623
- extra = {
624
- "boundary": boundary,
625
- "action": action,
626
- "err_type": err.__class__.__name__,
627
- "err": extract_exc_args(exc=err),
628
- **_safe_context(context),
629
- }
562
+ err_name = err.__class__.__name__
563
+ log_message = f"[boundary={boundary} action={action} err={err_name}"
564
+
565
+ if (err_args := extract_exc_args(exc=err)) and err_args != err_name:
566
+ log_message += f": {err_args}"
567
+ log_message += "]"
568
+
569
+ if message:
570
+ log_message += f" {message}"
571
+
572
+ if log_context:
573
+ log_message += f" ctx={orjson.dumps(_safe_log_context(log_context), option=orjson.OPT_SORT_KEYS).decode()}"
630
574
 
631
575
  # Choose level if not provided:
632
- chosen_level = level
633
- if chosen_level is None:
576
+ if (chosen_level := level) is None:
634
577
  # Use WARNING for expected/recoverable domain errors, ERROR otherwise.
635
578
  chosen_level = logging.WARNING if isinstance(err, BaseHomematicException) else logging.ERROR
636
579
 
637
- if chosen_level >= logging.ERROR:
638
- logger.exception(_BOUNDARY_MSG, extra=extra)
639
- else:
640
- logger.log(chosen_level, _BOUNDARY_MSG, extra=extra)
580
+ logger.log(chosen_level, log_message)
581
+
582
+
583
+ class LogContextMixin:
584
+ """Mixin to add log context methods to class."""
585
+
586
+ __slots__ = ("_cached_log_context",)
587
+
588
+ @cached_property
589
+ def log_context(self) -> Mapping[str, Any]:
590
+ """Return the log context for this object."""
591
+ return {
592
+ key: value for key, value in get_hm_property_by_log_context(data_object=self).items() if value is not None
593
+ }
594
+
595
+
596
+ class PayloadMixin:
597
+ """Mixin to add payload methods to class."""
598
+
599
+ __slots__ = ()
600
+
601
+ @property
602
+ def config_payload(self) -> Mapping[str, Any]:
603
+ """Return the config payload."""
604
+ return {
605
+ key: value
606
+ for key, value in get_hm_property_by_kind(data_object=self, kind=Kind.CONFIG).items()
607
+ if value is not None
608
+ }
609
+
610
+ @property
611
+ def info_payload(self) -> Mapping[str, Any]:
612
+ """Return the info payload."""
613
+ return {
614
+ key: value
615
+ for key, value in get_hm_property_by_kind(data_object=self, kind=Kind.INFO).items()
616
+ if value is not None
617
+ }
618
+
619
+ @property
620
+ def state_payload(self) -> Mapping[str, Any]:
621
+ """Return the state payload."""
622
+ return {
623
+ key: value
624
+ for key, value in get_hm_property_by_kind(data_object=self, kind=Kind.STATE).items()
625
+ if value is not None
626
+ }
641
627
 
642
628
 
643
629
  # Define public API for this module
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aiohomematic
3
- Version: 2025.9.1
3
+ Version: 2025.9.3
4
4
  Summary: Homematic interface for Home Assistant running on Python 3.
5
5
  Home-page: https://github.com/sukramj/aiohomematic
6
6
  Author-email: SukramJ <sukramj@icloud.com>, Daniel Perna <danielperna84@gmail.com>
@@ -52,7 +52,7 @@ Unlike pyhomematic, which required manual device mappings, aiohomematic automati
52
52
  Use the Home Assistant custom integration "Homematic(IP) Local", which is powered by aiohomematic.
53
53
 
54
54
  1. Prerequisites
55
- - Home Assistant 2024.6 or newer recommended.
55
+ - Use latest version of Home Assistant.
56
56
  - A CCU3, RaspberryMatic, or Homegear instance reachable from Home Assistant.
57
57
  - For HomematicIP devices, ensure CCU firmware meets the minimum versions listed below.
58
58
  2. Install the integration
@@ -60,11 +60,11 @@ Use the Home Assistant custom integration "Homematic(IP) Local", which is powere
60
60
  - Follow the installation guide: https://github.com/sukramj/homematicip_local/wiki/Installation
61
61
  3. Configure via Home Assistant UI
62
62
  - In Home Assistant: Settings → Devices & Services → Add Integration → search for "Homematic(IP) Local".
63
- - Enter the CCU/Homegear host (IP or hostname). If you use HTTPS on the CCU, enable SSL and accept the certificate if self‑signed.
64
- - Provide credentials if your CCU requires them.
63
+ - Enter the CCU/Homegear host (IP or hostname). If you use HTTPS on the CCU, enable TLS and don't use verify if self‑signed.
64
+ - Always enter credentials.
65
65
  - Choose which interfaces to enable (HM, HmIP, Virtual). Default ports are typically 2001 (HM), 2010 (HmIP), 9292 (Virtual).
66
66
  4. Network callbacks
67
- - The integration needs to receive XML‑RPC callbacks from the CCU. Make sure Home Assistant is reachable from the CCU (no NAT/firewall blocking). The default callback port is 43439; you can adjust it in advanced options.
67
+ - The integration needs to receive XML‑RPC callbacks from the CCU. Make sure Home Assistant is reachable from the CCU (no NAT/firewall blocking). Callbacks ar only required for special network setups.
68
68
  5. Verify
69
69
  - After setup, devices should appear under Devices & Services → Homematic(IP) Local. Discovery may take a few seconds after the first connection while paramsets are fetched and cached for faster restarts.
70
70
 
@@ -84,10 +84,12 @@ See details here: https://github.com/jens-maus/RaspberryMatic/issues/843. Other
84
84
  - The public API of aiohomematic is explicitly defined via **all** in each module and subpackage.
85
85
  - Backwards‑compatible imports should target these modules:
86
86
  - aiohomematic.central: CentralUnit, CentralConfig and related schemas
87
+ - aiohomematic.central.event: display received events from the backend
87
88
  - aiohomematic.client: Client, InterfaceConfig, create_client, get_client
88
89
  - aiohomematic.model: device/data point abstractions (see subpackages for details)
89
90
  - aiohomematic.exceptions: library exception types intended for consumers
90
91
  - aiohomematic.const: constants and enums (stable subset; see module **all**)
92
+ - aiohomematic.performance: display some performance metrics (enabled when DEBUG is enabled)
91
93
  - The top‑level package only exposes **version** to avoid import cycles and keep startup lean. Prefer importing from the specific submodules listed above.
92
94
 
93
95
  Example:
@@ -0,0 +1,78 @@
1
+ aiohomematic/__init__.py,sha256=VPESkjzeVserFI2DDVAxD782cgYRlLK0sXJzLrr3e_k,2283
2
+ aiohomematic/async_support.py,sha256=Xc55KkIV0h8rf936QKyU4OHSZsPEZ8TwuV8gVveeRh8,6106
3
+ aiohomematic/const.py,sha256=U6GjonNWYX6PLDyzMZqVeYsrVPgG7QqoBhVjRec0Xwk,25411
4
+ aiohomematic/context.py,sha256=M7gkA7KFT0dp35gzGz2dzKVXu1PP0sAnepgLlmjyRS4,451
5
+ aiohomematic/converter.py,sha256=QTOL8_B6SoCoqLuRSkjtOlfa7BVFSvOfnSBrDngiinA,3558
6
+ aiohomematic/decorators.py,sha256=oHEFSJoJVd-h9RYb6NLTJ60erkpRHPYv7EEipKn1a9Q,10636
7
+ aiohomematic/exceptions.py,sha256=o_H3Z0A2TQ0irNxUM9u8bmivq0L_mwPCB7nhxEawDxE,5018
8
+ aiohomematic/hmcli.py,sha256=wHOLq4IJRSY9RJux_ZfDH1gQ1ZqD0k68ua5agyBkkE8,4933
9
+ aiohomematic/property_decorators.py,sha256=fDuh3sydnlc-8R1gJAh-9ZxW4jApkk-LohA2Rqk-qPI,17609
10
+ aiohomematic/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
+ aiohomematic/support.py,sha256=THiA1zqOSVkR6rnPTdNpRZkdEhGgEiP-jtgTU68tEwE,20331
12
+ aiohomematic/validator.py,sha256=vChkYQ9dGKCQEigiBMnoeyPkCJDJlIm6DSgR1gmeHH0,3545
13
+ aiohomematic/caches/__init__.py,sha256=_gI30tbsWgPRaHvP6cRxOQr6n9bYZzU-jp1WbHhWg-A,470
14
+ aiohomematic/caches/dynamic.py,sha256=Kp0bL5JZaPGGfsezuBX3VtADhEcUvu93AEB5j2rgzzY,21326
15
+ aiohomematic/caches/persistent.py,sha256=YBThIByt0mLQCgcZwBBmqanI8obAhHNcFULz_H-de8o,19932
16
+ aiohomematic/caches/visibility.py,sha256=uZ1sSCfmEQURllPvSbJ3EzFVFE7TU8XcxMDSHRpNmMs,31481
17
+ aiohomematic/central/__init__.py,sha256=y8JErLEY4zh8qsGyS1qMQPaPlQn3GEx6q5_L4OvqxlQ,86148
18
+ aiohomematic/central/decorators.py,sha256=Sl-cMDhreiAOKkIHiei-QbIOcvbWGVX-QwB5bLeohak,6904
19
+ aiohomematic/central/xml_rpc_server.py,sha256=DZcE3t_KWgJyQJM5Z9_yPO4s075DRJmnIdkIx0OTL9M,10549
20
+ aiohomematic/client/__init__.py,sha256=6ik0d9VPcwBguL31zdKw_2Zslkra4rnTmwH_sY7oWXo,69964
21
+ aiohomematic/client/_rpc_errors.py,sha256=vu76ZWMwJJEgB0cnXc10JzK8PaiRU7jYOvj1aC21CqE,2971
22
+ aiohomematic/client/json_rpc.py,sha256=MR7lpxd3UddNuUojWh8-CZkxVY8_jUPhLXkDQeIXbfA,49307
23
+ aiohomematic/client/xml_rpc.py,sha256=pTnLjY81bJd8goD_zAkSEE7Q_UTKwBmiiyLOwJ1DOMs,9640
24
+ aiohomematic/model/__init__.py,sha256=LBKHws0W-zgFywTsWfklVVCxrO0w6QUY9ptT6LKxJls,5457
25
+ aiohomematic/model/data_point.py,sha256=Mls6b20e_fzYhB3EPYab0tocRqbl-v5ityV6uKTBOnU,40837
26
+ aiohomematic/model/device.py,sha256=JNRY7mEIZjwdvDnt-kV95QOjWVLrny-PnamitQqjZ-w,52136
27
+ aiohomematic/model/event.py,sha256=EMoKjbOZRXD3jumRrSlSoUr3nZ2QM3GDKKFfhlf-D50,6837
28
+ aiohomematic/model/support.py,sha256=4VQsOlkJuPhq0hLHKt5LB43YgbITjnyJMwucUYGghzM,19616
29
+ aiohomematic/model/update.py,sha256=QVYFhaEqtHZhZ8yGPXEx2hPMne8WAc_SkVaQ0J20oQw,5138
30
+ aiohomematic/model/calculated/__init__.py,sha256=pk56FYa0MNyN0MTsejSy6RdQjOUCKAqzwWSzN6_ENmk,2808
31
+ aiohomematic/model/calculated/climate.py,sha256=0BhrsiVS74_6jo24I8Cg9WzVAD5od3IqgiwTM0ao2n0,8518
32
+ aiohomematic/model/calculated/data_point.py,sha256=Bnv2R9ViFf48C7-BjmJh6999EcWZdJxYZfLpBg57oQg,11525
33
+ aiohomematic/model/calculated/operating_voltage_level.py,sha256=urlCkHjisCJ-DO6tUE0evfp45tPwsYcb81_2uoHYxS0,13521
34
+ aiohomematic/model/calculated/support.py,sha256=K-oUSSH6k6_J7zZ9cwxt6qIfArWd-SBWo93LuZUs1_s,6674
35
+ aiohomematic/model/custom/__init__.py,sha256=1sWRrAjHnHSakdrrlNW4_SZ2rg5g2WjGdI4X2PUg_h0,6064
36
+ aiohomematic/model/custom/climate.py,sha256=utcyx2cWOK-jYz-EWQlCD9tjBsl_uDk77Fzt9kIcdXc,53987
37
+ aiohomematic/model/custom/const.py,sha256=Kh1pnab6nmwbaY43CfXQy3yrWpPwsrQdl1Ea2aZ6aw0,4961
38
+ aiohomematic/model/custom/cover.py,sha256=xaU-MfgpnG3Zkdb20BIDUezbvh3rI3HpM0sBg6IcX90,28555
39
+ aiohomematic/model/custom/data_point.py,sha256=d7B8gfyDDZf8HW3f0ofeHYF32GE8kgnQ0DZYSUbLHxY,14112
40
+ aiohomematic/model/custom/definition.py,sha256=1GkUyEsmjJfAdrBCW1ZBmboYZOyb00MctUE_xn4Zyl8,35481
41
+ aiohomematic/model/custom/light.py,sha256=8ourpK9U89a3nVp0_WZBTPl3TK9km9fir7W23Q9c4Ns,44173
42
+ aiohomematic/model/custom/lock.py,sha256=5G5BsQFHbYul-_FQwFWDq9E3o8RL54FjJm0yfnIMejc,11940
43
+ aiohomematic/model/custom/siren.py,sha256=O9SnAsR8E04d46O-Ahx_xRK4XwYQ_pk5BLpufCQ9BTc,9729
44
+ aiohomematic/model/custom/support.py,sha256=UvencsvCwgpm4iqRNRt5KRs560tyw1NhYP5ZaqmCT2k,1453
45
+ aiohomematic/model/custom/switch.py,sha256=sFBt0orv9NeWEkUqjGvc5g5COVQU239u2_9XxnSNGSs,6872
46
+ aiohomematic/model/custom/valve.py,sha256=XTjs0yqLvqjYEk2SS2SH4UGCMJOuqNgMd43TfQPKa2s,4229
47
+ aiohomematic/model/generic/__init__.py,sha256=goR2uBBGizm-A5Vvd7ii9Ai3OihBl3hIztmAEI3ceVQ,7611
48
+ aiohomematic/model/generic/action.py,sha256=5SPWVliRqXlPgtb2bI6le0-V6JR5krzw8z_0FaOXu5U,994
49
+ aiohomematic/model/generic/binary_sensor.py,sha256=U5GvfRYbhwe0jRmaedD4LVZ_24SyyPbVr74HEfZXoxE,887
50
+ aiohomematic/model/generic/button.py,sha256=6jZ49woI9gYJEx__PjguDNbc5vdE1P-YcLMZZFkYRCg,740
51
+ aiohomematic/model/generic/data_point.py,sha256=wlPQQjm3YDQRbSONpSyzLVWeivVHdkoo1lLxRE7AAWE,5923
52
+ aiohomematic/model/generic/number.py,sha256=wHNIIVr41IOoROPOnBXuMRsANG6doguIEWXYnQHJbBk,2669
53
+ aiohomematic/model/generic/select.py,sha256=kxN5BR85Yli7kQF2DYkGiLnTgcY9LBJWI7MMwWpH1mo,1535
54
+ aiohomematic/model/generic/sensor.py,sha256=dWff7yBJfJSjoD4rcYdmH5HhaRCUEKfPFfOu4T1LrPo,2253
55
+ aiohomematic/model/generic/switch.py,sha256=oaXhECjSPT1WUdItEFTEbOLNCFZlaaqYByEHtL3GpTY,1833
56
+ aiohomematic/model/generic/text.py,sha256=vtNV7YxZuxF6LzNRKRAeOtSQtPQxPaEd560OFaVR13U,854
57
+ aiohomematic/model/hub/__init__.py,sha256=_UEEiNHfUVb0D0qTMcR2_YH1ZcYu1jzgLnUApPWfT2k,13493
58
+ aiohomematic/model/hub/binary_sensor.py,sha256=Z4o-zghHSc83ZHUUCtHqWEGueD9K1Fe0JEt_xJNdx_Y,752
59
+ aiohomematic/model/hub/button.py,sha256=XO4ied740BDOXBfTxFW30ROdZiOOxxu05Rmt4b_h06k,890
60
+ aiohomematic/model/hub/data_point.py,sha256=H_3REBK2lvoqITvqvupvg_ZnkcdxAIu_Qu8o0Wgc2Ds,10555
61
+ aiohomematic/model/hub/number.py,sha256=O5KrBU4nfZaxw2Pi2AmWAwQo8V_x79xrdt8ukoPme5Q,1222
62
+ aiohomematic/model/hub/select.py,sha256=My6flvM7Lmt5hF-Szx4a4X1KZZPkQ5lCVPCXTLuqBbQ,1659
63
+ aiohomematic/model/hub/sensor.py,sha256=cIr-7bsbOLBxnH33EHQ6D42vc61abO4QYLWLzkm1T10,1192
64
+ aiohomematic/model/hub/switch.py,sha256=510Vrlyak5TRsMvURrGYMkad-CbGUKWh20jjHQGOios,1390
65
+ aiohomematic/model/hub/text.py,sha256=bSNq4rgv2AIXELz-4HsaY-IMTYQYKsa-UX2aIwWle34,990
66
+ aiohomematic/rega_scripts/fetch_all_device_data.fn,sha256=7uxhHoelAOsH6yYr1n1M1XwIRgDmItiHnWIMhDYEimk,4373
67
+ aiohomematic/rega_scripts/get_program_descriptions.fn,sha256=pGmj377MkqbZi6j-UBKQAsXTphwh1kDwDKqXij8zUBE,835
68
+ aiohomematic/rega_scripts/get_serial.fn,sha256=t1oeo-sB_EuVeiY24PLcxFSkdQVgEWGXzpemJQZFybY,1079
69
+ aiohomematic/rega_scripts/get_system_variable_descriptions.fn,sha256=UKXvC0_5lSApdQ2atJc0E5Stj5Zt3lqh0EcliokYu2c,849
70
+ aiohomematic/rega_scripts/set_program_state.fn,sha256=0bnv7lUj8FMjDZBz325tDVP61m04cHjVj4kIOnUUgpY,279
71
+ aiohomematic/rega_scripts/set_system_variable.fn,sha256=sTmr7vkPTPnPkor5cnLKlDvfsYRbGO1iq2z_2pMXq5E,383
72
+ aiohomematic-2025.9.3.dist-info/licenses/LICENSE,sha256=q-B0xpREuZuvKsmk3_iyVZqvZ-vJcWmzMZpeAd0RqtQ,1083
73
+ aiohomematic_support/__init__.py,sha256=_0YtF4lTdC_k6-zrM2IefI0u0LMr_WA61gXAyeGLgbY,66
74
+ aiohomematic_support/client_local.py,sha256=4p0-PagFeNgu-r0EjhpHWfImgHMrdSgt8VUMBBC66J0,12514
75
+ aiohomematic-2025.9.3.dist-info/METADATA,sha256=vI27ZQjSHKOzV8Qs8e-W7Gk1iHhAG-qLk-W234XkXZo,7075
76
+ aiohomematic-2025.9.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
77
+ aiohomematic-2025.9.3.dist-info/top_level.txt,sha256=5TDRlUWQPThIUwQjOj--aUo4UA-ow4m0sNhnoCBi5n8,34
78
+ aiohomematic-2025.9.3.dist-info/RECORD,,
@@ -126,12 +126,12 @@ class ClientLocal(Client): # pragma: no cover
126
126
  self._ping_pong_cache.handle_send_ping(ping_ts=datetime.now())
127
127
  return True
128
128
 
129
- @inspector()
129
+ @inspector
130
130
  async def execute_program(self, pid: str) -> bool:
131
131
  """Execute a program on CCU / Homegear."""
132
132
  return True
133
133
 
134
- @inspector()
134
+ @inspector
135
135
  async def set_program_state(self, pid: str, state: bool) -> bool:
136
136
  """Set the program state on CCU / Homegear."""
137
137
  return True
@@ -141,12 +141,12 @@ class ClientLocal(Client): # pragma: no cover
141
141
  """Set a system variable on CCU / Homegear."""
142
142
  return True
143
143
 
144
- @inspector()
144
+ @inspector
145
145
  async def delete_system_variable(self, name: str) -> bool:
146
146
  """Delete a system variable from CCU / Homegear."""
147
147
  return True
148
148
 
149
- @inspector()
149
+ @inspector
150
150
  async def get_system_variable(self, name: str) -> str:
151
151
  """Get single system variable from CCU / Homegear."""
152
152
  return "Empty"
@@ -231,7 +231,7 @@ class ClientLocal(Client): # pragma: no cover
231
231
  await self.central.data_point_event(self.interface_id, channel_address, parameter, value)
232
232
  return result
233
233
 
234
- @inspector()
234
+ @inspector
235
235
  async def get_paramset(
236
236
  self,
237
237
  address: str,
@@ -1,194 +0,0 @@
1
- # SPDX-License-Identifier: MIT
2
- # Copyright (c) 2021-2025 Daniel Perna, SukramJ
3
- """Decorators for data points used within aiohomematic."""
4
-
5
- from __future__ import annotations
6
-
7
- from collections.abc import Callable, Mapping
8
- from datetime import datetime
9
- from enum import Enum
10
- from typing import Any, ParamSpec, TypeVar, cast
11
- from weakref import WeakKeyDictionary
12
-
13
- __all__ = [
14
- "config_property",
15
- "get_public_attributes_for_config_property",
16
- "get_public_attributes_for_info_property",
17
- "get_public_attributes_for_state_property",
18
- "info_property",
19
- "state_property",
20
- ]
21
-
22
- P = ParamSpec("P")
23
- T = TypeVar("T")
24
-
25
-
26
- # pylint: disable=invalid-name
27
- class generic_property[GETTER, SETTER](property):
28
- """Generic property implementation."""
29
-
30
- fget: Callable[[Any], GETTER] | None
31
- fset: Callable[[Any, SETTER], None] | None
32
- fdel: Callable[[Any], None] | None
33
-
34
- def __init__(
35
- self,
36
- fget: Callable[[Any], GETTER] | None = None,
37
- fset: Callable[[Any, SETTER], None] | None = None,
38
- fdel: Callable[[Any], None] | None = None,
39
- doc: str | None = None,
40
- ) -> None:
41
- """Init the generic property."""
42
- super().__init__(fget, fset, fdel, doc)
43
- if doc is None and fget is not None:
44
- doc = fget.__doc__
45
- self.__doc__ = doc
46
-
47
- def getter(self, fget: Callable[[Any], GETTER], /) -> generic_property:
48
- """Return generic getter."""
49
- return type(self)(fget, self.fset, self.fdel, self.__doc__) # pragma: no cover
50
-
51
- def setter(self, fset: Callable[[Any, SETTER], None], /) -> generic_property:
52
- """Return generic setter."""
53
- return type(self)(self.fget, fset, self.fdel, self.__doc__)
54
-
55
- def deleter(self, fdel: Callable[[Any], None], /) -> generic_property:
56
- """Return generic deleter."""
57
- return type(self)(self.fget, self.fset, fdel, self.__doc__)
58
-
59
- def __get__(self, obj: Any, gtype: type | None = None, /) -> GETTER: # type: ignore[override]
60
- """Return the attribute."""
61
- if obj is None:
62
- return self # type: ignore[return-value]
63
- if self.fget is None:
64
- raise AttributeError("unreadable attribute") # pragma: no cover
65
- return self.fget(obj)
66
-
67
- def __set__(self, obj: Any, value: Any, /) -> None:
68
- """Set the attribute."""
69
- if self.fset is None:
70
- raise AttributeError("can't set attribute") # pragma: no cover
71
- self.fset(obj, value)
72
-
73
- def __delete__(self, obj: Any, /) -> None:
74
- """Delete the attribute."""
75
- if self.fdel is None:
76
- raise AttributeError("can't delete attribute") # pragma: no cover
77
- self.fdel(obj)
78
-
79
-
80
- # pylint: disable=invalid-name
81
- class config_property[GETTER, SETTER](generic_property[GETTER, SETTER]):
82
- """Decorate to mark own config properties."""
83
-
84
-
85
- # pylint: disable=invalid-name
86
- class info_property[GETTER, SETTER](generic_property[GETTER, SETTER]):
87
- """Decorate to mark own info properties."""
88
-
89
-
90
- # pylint: disable=invalid-name
91
- class state_property[GETTER, SETTER](generic_property[GETTER, SETTER]):
92
- """Decorate to mark own value properties."""
93
-
94
-
95
- # Cache for per-class attribute names by decorator to avoid repeated dir() scans
96
- # Use WeakKeyDictionary to allow classes to be garbage-collected without leaking cache entries.
97
- # Structure: {cls: {decorator_class: (attr_name1, attr_name2, ...)}}
98
- _PUBLIC_ATTR_CACHE: WeakKeyDictionary[type, dict[type, tuple[str, ...]]] = WeakKeyDictionary()
99
-
100
-
101
- def _get_public_attributes_by_class_decorator(data_object: Any, class_decorator: type) -> Mapping[str, Any]:
102
- """
103
- Return the object attributes by decorator.
104
-
105
- This caches the attribute names per (class, decorator) to reduce overhead
106
- from repeated dir()/getattr() scans. Values are not cached as they are
107
- instance-dependent and may change over time.
108
-
109
- To minimize side effects, exceptions raised by property getters are caught
110
- and the corresponding value is set to None. This ensures that payload
111
- construction and attribute introspection do not fail due to individual
112
- properties with transient errors or expensive side effects.
113
- """
114
- cls = data_object.__class__
115
-
116
- # Get or create the per-class cache dict
117
- if (decorator_cache := _PUBLIC_ATTR_CACHE.get(cls)) is None:
118
- decorator_cache = {}
119
- _PUBLIC_ATTR_CACHE[cls] = decorator_cache
120
-
121
- # Get or compute the attribute names for this decorator
122
- if (names := decorator_cache.get(class_decorator)) is None:
123
- names = tuple(y for y in dir(cls) if not y.startswith("_") and isinstance(getattr(cls, y), class_decorator))
124
- decorator_cache[class_decorator] = names
125
-
126
- result: dict[str, Any] = {}
127
- for name in names:
128
- try:
129
- value = getattr(data_object, name)
130
- except Exception:
131
- # Avoid propagating side effects/errors from getters
132
- value = None
133
- result[name] = _get_text_value(value)
134
- return result
135
-
136
-
137
- def _get_text_value(value: Any) -> Any:
138
- """Convert value to text."""
139
- if isinstance(value, (list, tuple, set)):
140
- return tuple(_get_text_value(v) for v in value)
141
- if isinstance(value, Enum):
142
- return str(value)
143
- if isinstance(value, datetime):
144
- return datetime.timestamp(value)
145
- return value
146
-
147
-
148
- def get_public_attributes_for_config_property(data_object: Any) -> Mapping[str, Any]:
149
- """Return the object attributes by decorator config_property."""
150
- return _get_public_attributes_by_class_decorator(data_object=data_object, class_decorator=config_property)
151
-
152
-
153
- def get_public_attributes_for_info_property(data_object: Any) -> Mapping[str, Any]:
154
- """Return the object attributes by decorator info_property."""
155
- return _get_public_attributes_by_class_decorator(data_object=data_object, class_decorator=info_property)
156
-
157
-
158
- def get_public_attributes_for_state_property(data_object: Any) -> Mapping[str, Any]:
159
- """Return the object attributes by decorator state_property."""
160
- return _get_public_attributes_by_class_decorator(data_object=data_object, class_decorator=state_property)
161
-
162
-
163
- # pylint: disable=invalid-name
164
- class cached_slot_property[T, R]:
165
- """A property-like descriptor that caches the computed value in a slot attribute. Designed to work with classes that use __slots__ and do not define __dict__."""
166
-
167
- def __init__(self, func: Callable[[T], R]) -> None:
168
- """Init the cached property."""
169
- self._func = func # The function to compute the value
170
- self._cache_attr = f"_cached_{func.__name__}" # Default name of the cache attribute
171
- self._name = func.__name__
172
-
173
- def __get__(self, instance: T | None, owner: type | None = None) -> R:
174
- """Return the cached value if it exists. Otherwise, compute it using the function and cache it."""
175
- if instance is None:
176
- # Accessed from class, return the descriptor itself
177
- return cast(R, self)
178
-
179
- # If the cached value is not set yet, compute and store it
180
- if not hasattr(instance, self._cache_attr):
181
- value = self._func(instance)
182
- setattr(instance, self._cache_attr, value)
183
-
184
- # Return the cached value
185
- return cast(R, getattr(instance, self._cache_attr))
186
-
187
- def __set__(self, instance: T, value: Any) -> None:
188
- """Raise an error to prevent manual assignment to the property."""
189
- raise AttributeError(f"Can't set read-only cached property '{self._name}'")
190
-
191
- def __delete__(self, instance: T) -> None:
192
- """Delete the cached value so it can be recomputed on next access."""
193
- if hasattr(instance, self._cache_attr):
194
- delattr(instance, self._cache_attr)
@@ -1,78 +0,0 @@
1
- aiohomematic/__init__.py,sha256=VPESkjzeVserFI2DDVAxD782cgYRlLK0sXJzLrr3e_k,2283
2
- aiohomematic/async_support.py,sha256=Xc55KkIV0h8rf936QKyU4OHSZsPEZ8TwuV8gVveeRh8,6106
3
- aiohomematic/const.py,sha256=1a7G-J_JY45Ta7qAPcfp5XLOHI0Ubw7Y6yJrz3FB0aI,25407
4
- aiohomematic/context.py,sha256=M7gkA7KFT0dp35gzGz2dzKVXu1PP0sAnepgLlmjyRS4,451
5
- aiohomematic/converter.py,sha256=QTOL8_B6SoCoqLuRSkjtOlfa7BVFSvOfnSBrDngiinA,3558
6
- aiohomematic/decorators.py,sha256=piayP-1t9PQe0pR8IGc_85so5XSiFLi-sIwtgkf3eWU,9725
7
- aiohomematic/exceptions.py,sha256=o_H3Z0A2TQ0irNxUM9u8bmivq0L_mwPCB7nhxEawDxE,5018
8
- aiohomematic/hmcli.py,sha256=wHOLq4IJRSY9RJux_ZfDH1gQ1ZqD0k68ua5agyBkkE8,4933
9
- aiohomematic/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
- aiohomematic/support.py,sha256=d70tjuajEQYGFsINJQA9-RkKuCRN8-2Sw33B0bPMym8,21495
11
- aiohomematic/validator.py,sha256=vChkYQ9dGKCQEigiBMnoeyPkCJDJlIm6DSgR1gmeHH0,3545
12
- aiohomematic/caches/__init__.py,sha256=_gI30tbsWgPRaHvP6cRxOQr6n9bYZzU-jp1WbHhWg-A,470
13
- aiohomematic/caches/dynamic.py,sha256=ZrAQqXlzhoCvzjZ9cTEtlWqygvkn5xTzOJ987WcrPBc,21539
14
- aiohomematic/caches/persistent.py,sha256=YBThIByt0mLQCgcZwBBmqanI8obAhHNcFULz_H-de8o,19932
15
- aiohomematic/caches/visibility.py,sha256=uZ1sSCfmEQURllPvSbJ3EzFVFE7TU8XcxMDSHRpNmMs,31481
16
- aiohomematic/central/__init__.py,sha256=uZ_Ns5upjTcz4AaA6cN1WNWYkpxtZZebDRvag0g7bwM,85736
17
- aiohomematic/central/decorators.py,sha256=Sl-cMDhreiAOKkIHiei-QbIOcvbWGVX-QwB5bLeohak,6904
18
- aiohomematic/central/xml_rpc_server.py,sha256=dM-gbAS1NpRy4wJKUZTsMI2LJfv_vjd4kvyv3pl86yw,10545
19
- aiohomematic/client/__init__.py,sha256=hGpdDb518VdMYMHxrBBj2PBpUBN2Ah8ID5vjokd9PAU,69832
20
- aiohomematic/client/_rpc_errors.py,sha256=vu76ZWMwJJEgB0cnXc10JzK8PaiRU7jYOvj1aC21CqE,2971
21
- aiohomematic/client/json_rpc.py,sha256=6eHRYM4xcXUS_Ny3tnjSxw7F0GpaRMUjkqwj1HGaT7w,48231
22
- aiohomematic/client/xml_rpc.py,sha256=OUWQkvYYbw9duFzNAB4CXavwwh-slBtitmMPGWShbNg,8504
23
- aiohomematic/model/__init__.py,sha256=l1YMXkx1ugdUf7ms8lD8ObVaOzKAhi-4O2TO5s0IMd0,5459
24
- aiohomematic/model/data_point.py,sha256=N446xJizb9jMtrX8TFW7NQ0wj8VbTxQREFsiVUnArKA,40738
25
- aiohomematic/model/decorators.py,sha256=JGhDzWx983zBCEKA9UoptjFmIHOGX-UfgozhqXAeZsM,7663
26
- aiohomematic/model/device.py,sha256=6PDtZPZ6S-3cjEmb20zrnAh6VYG9fMtAIi1OnuV9r5s,51991
27
- aiohomematic/model/event.py,sha256=vXtqb9OfpumOOmbgj9z6DG-QeVUEHMOvK1gnv0piKDY,7014
28
- aiohomematic/model/support.py,sha256=TnEpCB8EgJq_-4LoH-et1R0cPT6Lq4LTv7MxmRn5JQU,20767
29
- aiohomematic/model/update.py,sha256=DemrlE8TleJgaxBwj984NxzZGeLEKpC3xAo0Ew21HLU,5107
30
- aiohomematic/model/calculated/__init__.py,sha256=BHYLdWMMMjqvDI5T9cQ1sPOyk7t8_q-Ngfm4cFySvJ4,2810
31
- aiohomematic/model/calculated/climate.py,sha256=BUKXT5kd6mbEE7TAJpRs8-A1K4pVq2d2E5cSfjsOPJw,8515
32
- aiohomematic/model/calculated/data_point.py,sha256=hspOOnEp-WbXb4VMTcuXkgTWxjy0ug5E46zXZy6YReQ,11493
33
- aiohomematic/model/calculated/operating_voltage_level.py,sha256=sf5Fy0bSbvee9lvq95-iFC05rSGkHBOKIQkdjQBU_XY,14011
34
- aiohomematic/model/calculated/support.py,sha256=ODH3a2B1y8Mr9N_3sMt0pyUs1qQ1tDZsD2GthScntbg,6098
35
- aiohomematic/model/custom/__init__.py,sha256=KNvUy3hZvdr3aRZk9CDyMqJBqRorQedZfOP3M-zwjp8,6066
36
- aiohomematic/model/custom/climate.py,sha256=te8GoqqDU8wQAEDTf3Pyi8NFdm9MACCe8WyhBG74x28,54018
37
- aiohomematic/model/custom/const.py,sha256=Kh1pnab6nmwbaY43CfXQy3yrWpPwsrQdl1Ea2aZ6aw0,4961
38
- aiohomematic/model/custom/cover.py,sha256=dXYZGVzR-cFELbVP2az8tkM8h7jIrvzweUz4yWh7AM8,28552
39
- aiohomematic/model/custom/data_point.py,sha256=mz143_hLKaF9HzJRhqqqzsoJhouLF0L_9Jo_4oq_6Gc,14109
40
- aiohomematic/model/custom/definition.py,sha256=1GkUyEsmjJfAdrBCW1ZBmboYZOyb00MctUE_xn4Zyl8,35481
41
- aiohomematic/model/custom/light.py,sha256=0QC6x6eYuDypNMbk-Ey7CMfVs1B0vgFz2YzFzqQhbzw,44170
42
- aiohomematic/model/custom/lock.py,sha256=q-jsvMTly6s7kEWDT42rMEfEEJsd9lBIuwzpO2STFBk,11937
43
- aiohomematic/model/custom/siren.py,sha256=yfWdNj-vEUGLqXBejjURd98RzY8MQv99oXxy35Ul_6U,9726
44
- aiohomematic/model/custom/support.py,sha256=UvencsvCwgpm4iqRNRt5KRs560tyw1NhYP5ZaqmCT2k,1453
45
- aiohomematic/model/custom/switch.py,sha256=JSmWUWc_WB8XmZPPgcISB9r3TNNUeg-E4skX46z7vwA,6869
46
- aiohomematic/model/custom/valve.py,sha256=S5i2ZePa9Jt8JplIykzeJxpJrSBEN5w-3UEmGu0Q5G0,4226
47
- aiohomematic/model/generic/__init__.py,sha256=OI0-ijDRYS6OkDqVd9C9UOfde2rfcB7WZZ9CLTlX1S0,7613
48
- aiohomematic/model/generic/action.py,sha256=5SPWVliRqXlPgtb2bI6le0-V6JR5krzw8z_0FaOXu5U,994
49
- aiohomematic/model/generic/binary_sensor.py,sha256=TI_KNzObiprHFS6rUKb7cYr2EsQQElfNo8Sk7eBY9NQ,884
50
- aiohomematic/model/generic/button.py,sha256=9wStCwiE3NeGfKHum5IVpLlbq5jRdYOA1bGHSqReY8k,742
51
- aiohomematic/model/generic/data_point.py,sha256=a7AXyYd0o-AfMOaGIn1qu8Q73ipjjsLLQrxMFJRw5xQ,6019
52
- aiohomematic/model/generic/number.py,sha256=39paL6us6KMLD5L8GhR7BLl8bKtEiicAIfcd_3HyOw4,2666
53
- aiohomematic/model/generic/select.py,sha256=vCe5FpMe2iqePQMHFBxKmkIBY6Y99SnTyiatmeZC6dM,1532
54
- aiohomematic/model/generic/sensor.py,sha256=N2GeZjZQbRyc4s8lX11xvho_TOqIdT6Uqc7LguBF9Ho,2250
55
- aiohomematic/model/generic/switch.py,sha256=wr6vEBc0iTyCGaRSVdoO_9UY8lt3-NPTyAXEhuZbAqU,1836
56
- aiohomematic/model/generic/text.py,sha256=vTgQpX_COn1OxExZ5az9jPU410fGZIbq0PajPS8vXgs,851
57
- aiohomematic/model/hub/__init__.py,sha256=_UEEiNHfUVb0D0qTMcR2_YH1ZcYu1jzgLnUApPWfT2k,13493
58
- aiohomematic/model/hub/binary_sensor.py,sha256=0cF1SCJY1BRM1_Fx5oYPMaSZu9tDy1GnBMn_UqpqqPk,749
59
- aiohomematic/model/hub/button.py,sha256=N0QSN-SdnLY38JslCoUaqjI2pcdp0DJY2kxoODUKO0s,889
60
- aiohomematic/model/hub/data_point.py,sha256=3HscnhIbOQlipWZKliHZPvZulvtpTAF83qS0-4iCzVQ,10645
61
- aiohomematic/model/hub/number.py,sha256=mj4HVRGgUqoyAyluSzKwlE_0jconltBAx-2nJuL9bhY,1224
62
- aiohomematic/model/hub/select.py,sha256=s5_nmMCae35I7WtUK2Oo_577JzBt68BSpgv5cTussps,1658
63
- aiohomematic/model/hub/sensor.py,sha256=Uqqb1rqVmYCemJK4yIkarkllKWZJTpd-VNiyLOKXl3M,1189
64
- aiohomematic/model/hub/switch.py,sha256=1Dp_jG4qtnAs-itaZDKStmeD4PdiMtdZctDOoGWI-Qg,1391
65
- aiohomematic/model/hub/text.py,sha256=AQOQB2W0jt8t__FxahDAAEOGhshkAX1pImHIFt_Xwqg,987
66
- aiohomematic/rega_scripts/fetch_all_device_data.fn,sha256=7uxhHoelAOsH6yYr1n1M1XwIRgDmItiHnWIMhDYEimk,4373
67
- aiohomematic/rega_scripts/get_program_descriptions.fn,sha256=pGmj377MkqbZi6j-UBKQAsXTphwh1kDwDKqXij8zUBE,835
68
- aiohomematic/rega_scripts/get_serial.fn,sha256=t1oeo-sB_EuVeiY24PLcxFSkdQVgEWGXzpemJQZFybY,1079
69
- aiohomematic/rega_scripts/get_system_variable_descriptions.fn,sha256=UKXvC0_5lSApdQ2atJc0E5Stj5Zt3lqh0EcliokYu2c,849
70
- aiohomematic/rega_scripts/set_program_state.fn,sha256=0bnv7lUj8FMjDZBz325tDVP61m04cHjVj4kIOnUUgpY,279
71
- aiohomematic/rega_scripts/set_system_variable.fn,sha256=sTmr7vkPTPnPkor5cnLKlDvfsYRbGO1iq2z_2pMXq5E,383
72
- aiohomematic-2025.9.1.dist-info/licenses/LICENSE,sha256=q-B0xpREuZuvKsmk3_iyVZqvZ-vJcWmzMZpeAd0RqtQ,1083
73
- aiohomematic_support/__init__.py,sha256=_0YtF4lTdC_k6-zrM2IefI0u0LMr_WA61gXAyeGLgbY,66
74
- aiohomematic_support/client_local.py,sha256=cvkO3tcxSJKy5ZLVdeqDbvSmeottxqZJNI4ccdxIVD4,12524
75
- aiohomematic-2025.9.1.dist-info/METADATA,sha256=qsC9QfkxhshJIvpgktrIYwKsmoTrBtnsPtpcl2HbgE4,6960
76
- aiohomematic-2025.9.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
77
- aiohomematic-2025.9.1.dist-info/top_level.txt,sha256=5TDRlUWQPThIUwQjOj--aUo4UA-ow4m0sNhnoCBi5n8,34
78
- aiohomematic-2025.9.1.dist-info/RECORD,,