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

aiohomematic/const.py CHANGED
@@ -19,7 +19,7 @@ import sys
19
19
  from types import MappingProxyType
20
20
  from typing import Any, Final, NamedTuple, Required, TypeAlias, TypedDict
21
21
 
22
- VERSION: Final = "2025.10.21"
22
+ VERSION: Final = "2025.10.22"
23
23
 
24
24
  # Detect test speedup mode via environment
25
25
  _TEST_SPEEDUP: Final = (
@@ -303,7 +303,7 @@ class EventKey(StrEnum):
303
303
  INTERFACE_ID = "interface_id"
304
304
  MODEL = "model"
305
305
  PARAMETER = "parameter"
306
- PONG_MISMATCH_ALLOWED = "pong_mismatch_allowed"
306
+ PONG_MISMATCH_ACCEPTABLE = "pong_mismatch_allowed"
307
307
  PONG_MISMATCH_COUNT = "pong_mismatch_count"
308
308
  SECONDS_SINCE_LAST_EVENT = "seconds_since_last_event"
309
309
  TYPE = "type"
@@ -392,36 +392,12 @@ class PingPongCache:
392
392
  return self._allowed_delta
393
393
 
394
394
  @property
395
- def high_pending_pongs(self) -> bool:
396
- """Check, if store contains too many pending pongs."""
397
- self._cleanup_pending_pongs()
398
- return len(self._pending_pongs) > self._allowed_delta
399
-
400
- @property
401
- def high_unknown_pongs(self) -> bool:
402
- """Check, if store contains too many unknown pongs."""
403
- self._cleanup_unknown_pongs()
404
- return len(self._unknown_pongs) > self._allowed_delta
405
-
406
- @property
407
- def low_pending_pongs(self) -> bool:
408
- """Return True when pending pong count is at or below the allowed delta (i.e., not high)."""
409
- self._cleanup_pending_pongs()
410
- return len(self._pending_pongs) <= self._allowed_delta
411
-
412
- @property
413
- def low_unknown_pongs(self) -> bool:
414
- """Return True when unknown pong count is at or below the allowed delta (i.e., not high)."""
415
- self._cleanup_unknown_pongs()
416
- return len(self._unknown_pongs) <= self._allowed_delta
417
-
418
- @property
419
- def pending_pong_count(self) -> int:
395
+ def _pending_pong_count(self) -> int:
420
396
  """Return the pending pong count."""
421
397
  return len(self._pending_pongs)
422
398
 
423
399
  @property
424
- def unknown_pong_count(self) -> int:
400
+ def _unknown_pong_count(self) -> int:
425
401
  """Return the unknown pong count."""
426
402
  return len(self._unknown_pongs)
427
403
 
@@ -435,18 +411,16 @@ class PingPongCache:
435
411
  def handle_send_ping(self, *, ping_ts: datetime) -> None:
436
412
  """Handle send ping timestamp."""
437
413
  self._pending_pongs.add(ping_ts)
414
+ self._cleanup_pending_pongs()
438
415
  # Throttle event emission to every second ping to avoid spamming callbacks,
439
416
  # but always emit when crossing the high threshold.
440
- count = self.pending_pong_count
417
+ count = self._pending_pong_count
441
418
  if (count > self._allowed_delta) or (count % 2 == 0):
442
- self._check_and_fire_pong_event(
443
- event_type=InterfaceEventType.PENDING_PONG,
444
- pong_mismatch_count=count,
445
- )
419
+ self._check_and_fire_pong_event(event_type=InterfaceEventType.PENDING_PONG)
446
420
  _LOGGER.debug(
447
421
  "PING PONG CACHE: Increase pending PING count: %s - %i for ts: %s",
448
422
  self._interface_id,
449
- self.pending_pong_count,
423
+ count,
450
424
  ping_ts,
451
425
  )
452
426
 
@@ -454,59 +428,56 @@ class PingPongCache:
454
428
  """Handle received pong timestamp."""
455
429
  if pong_ts in self._pending_pongs:
456
430
  self._pending_pongs.remove(pong_ts)
457
- self._check_and_fire_pong_event(
458
- event_type=InterfaceEventType.PENDING_PONG,
459
- pong_mismatch_count=self.pending_pong_count,
460
- )
431
+ self._cleanup_pending_pongs()
432
+ count = self._pending_pong_count
433
+ self._check_and_fire_pong_event(event_type=InterfaceEventType.PENDING_PONG)
461
434
  _LOGGER.debug(
462
435
  "PING PONG CACHE: Reduce pending PING count: %s - %i for ts: %s",
463
436
  self._interface_id,
464
- self.pending_pong_count,
437
+ count,
438
+ pong_ts,
439
+ )
440
+ else:
441
+ self._unknown_pongs.add(pong_ts)
442
+ self._cleanup_unknown_pongs()
443
+ count = self._unknown_pong_count
444
+ self._check_and_fire_pong_event(event_type=InterfaceEventType.UNKNOWN_PONG)
445
+ _LOGGER.debug(
446
+ "PING PONG CACHE: Increase unknown PONG count: %s - %i for ts: %s",
447
+ self._interface_id,
448
+ count,
465
449
  pong_ts,
466
450
  )
467
- return
468
-
469
- self._unknown_pongs.add(pong_ts)
470
- self._check_and_fire_pong_event(
471
- event_type=InterfaceEventType.UNKNOWN_PONG,
472
- pong_mismatch_count=self.unknown_pong_count,
473
- )
474
- _LOGGER.debug(
475
- "PING PONG CACHE: Increase unknown PONG count: %s - %i for ts: %s",
476
- self._interface_id,
477
- self.unknown_pong_count,
478
- pong_ts,
479
- )
480
451
 
481
452
  def _cleanup_pending_pongs(self) -> None:
482
453
  """Cleanup too old pending pongs."""
483
454
  dt_now = datetime.now()
484
- for pong_ts in list(self._pending_pongs):
455
+ for pp_pong_ts in list(self._pending_pongs):
485
456
  # Only expire entries that are actually older than the TTL.
486
- if (dt_now - pong_ts).total_seconds() > self._ttl:
487
- self._pending_pongs.remove(pong_ts)
457
+ if (dt_now - pp_pong_ts).total_seconds() > self._ttl:
458
+ self._pending_pongs.remove(pp_pong_ts)
488
459
  _LOGGER.debug(
489
460
  "PING PONG CACHE: Removing expired pending PONG: %s - %i for ts: %s",
490
461
  self._interface_id,
491
- self.pending_pong_count,
492
- pong_ts,
462
+ self._pending_pong_count,
463
+ pp_pong_ts,
493
464
  )
494
465
 
495
466
  def _cleanup_unknown_pongs(self) -> None:
496
467
  """Cleanup too old unknown pongs."""
497
468
  dt_now = datetime.now()
498
- for pong_ts in list(self._unknown_pongs):
469
+ for up_pong_ts in list(self._unknown_pongs):
499
470
  # Only expire entries that are actually older than the TTL.
500
- if (dt_now - pong_ts).total_seconds() > self._ttl:
501
- self._unknown_pongs.remove(pong_ts)
471
+ if (dt_now - up_pong_ts).total_seconds() > self._ttl:
472
+ self._unknown_pongs.remove(up_pong_ts)
502
473
  _LOGGER.debug(
503
474
  "PING PONG CACHE: Removing expired unknown PONG: %s - %i or ts: %s",
504
475
  self._interface_id,
505
- self.unknown_pong_count,
506
- pong_ts,
476
+ self._unknown_pong_count,
477
+ up_pong_ts,
507
478
  )
508
479
 
509
- def _check_and_fire_pong_event(self, *, event_type: InterfaceEventType, pong_mismatch_count: int) -> None:
480
+ def _check_and_fire_pong_event(self, *, event_type: InterfaceEventType) -> None:
510
481
  """Fire an event about the pong status."""
511
482
 
512
483
  def _fire_event(mismatch_count: int) -> None:
@@ -520,7 +491,7 @@ class PingPongCache:
520
491
  EventKey.TYPE: event_type,
521
492
  EventKey.DATA: {
522
493
  EventKey.CENTRAL_NAME: self._central.name,
523
- EventKey.PONG_MISMATCH_ALLOWED: mismatch_count <= self._allowed_delta,
494
+ EventKey.PONG_MISMATCH_ACCEPTABLE: mismatch_count <= self._allowed_delta,
524
495
  EventKey.PONG_MISMATCH_COUNT: mismatch_count,
525
496
  },
526
497
  }
@@ -528,44 +499,46 @@ class PingPongCache:
528
499
  ),
529
500
  )
530
501
 
531
- if self.low_pending_pongs and event_type == InterfaceEventType.PENDING_PONG:
502
+ if event_type == InterfaceEventType.PENDING_PONG:
503
+ self._cleanup_pending_pongs()
504
+ count = self._pending_pong_count
505
+ if self._pending_pong_count > self._allowed_delta:
506
+ # Emit interface event to inform subscribers about high pending pong count.
507
+ _fire_event(mismatch_count=count)
508
+ if self._pending_pong_logged is False:
509
+ _LOGGER.warning(
510
+ "Pending PONG mismatch: There is a mismatch between send ping events and received pong events for instance %s. "
511
+ "Possible reason 1: You are running multiple instances with the same instance name configured for this integration. "
512
+ "Re-add one instance! Otherwise this instance will not receive update events from your CCU. "
513
+ "Possible reason 2: Something is stuck on the CCU or hasn't been cleaned up. Therefore, try a CCU restart."
514
+ "Possible reason 3: Your setup is misconfigured and this instance is not able to receive events from the CCU.",
515
+ self._interface_id,
516
+ )
517
+ self._pending_pong_logged = True
532
518
  # In low state:
533
519
  # - If we previously logged a high state, emit a reset event (mismatch=0) exactly once.
534
520
  # - Otherwise, throttle emission to every second ping (even counts > 0) to avoid spamming.
535
- if self._pending_pong_logged:
521
+ elif self._pending_pong_logged:
536
522
  _fire_event(mismatch_count=0)
537
523
  self._pending_pong_logged = False
538
- return
539
- if pong_mismatch_count > 0 and pong_mismatch_count % 2 == 0:
540
- _fire_event(mismatch_count=pong_mismatch_count)
541
- return
542
-
543
- if self.low_unknown_pongs and event_type == InterfaceEventType.UNKNOWN_PONG:
544
- # For unknown pongs, only reset the logged flag when we drop below the threshold.
545
- # We do not emit an event here since there is no explicit expectation for a reset notification.
546
- self._unknown_pong_logged = False
547
- return
548
-
549
- if self.high_pending_pongs and event_type == InterfaceEventType.PENDING_PONG:
550
- _fire_event(mismatch_count=pong_mismatch_count)
551
- if self._pending_pong_logged is False:
552
- _LOGGER.warning(
553
- "Pending PONG mismatch: There is a mismatch between send ping events and received pong events for instance %s. "
554
- "Possible reason 1: You are running multiple instances with the same instance name configured for this integration. "
555
- "Re-add one instance! Otherwise this instance will not receive update events from your CCU. "
556
- "Possible reason 2: Something is stuck on the CCU or hasn't been cleaned up. Therefore, try a CCU restart."
557
- "Possible reason 3: Your setup is misconfigured and this instance is not able to receive events from the CCU.",
558
- self._interface_id,
559
- )
560
- self._pending_pong_logged = True
561
-
562
- if self.high_unknown_pongs and event_type == InterfaceEventType.UNKNOWN_PONG:
563
- if self._unknown_pong_logged is False:
564
- _LOGGER.warning(
565
- "Unknown PONG Mismatch: Your instance %s receives PONG events, that it hasn't send. "
566
- "Possible reason 1: You are running multiple instances with the same instance name configured for this integration. "
567
- "Re-add one instance! Otherwise the other instance will not receive update events from your CCU. "
568
- "Possible reason 2: Something is stuck on the CCU or hasn't been cleaned up. Therefore, try a CCU restart.",
569
- self._interface_id,
570
- )
571
- self._unknown_pong_logged = True
524
+ elif count > 0 and count % 2 == 0:
525
+ _fire_event(mismatch_count=count)
526
+ elif event_type == InterfaceEventType.UNKNOWN_PONG:
527
+ self._cleanup_unknown_pongs()
528
+ count = self._unknown_pong_count
529
+ if self._unknown_pong_count > self._allowed_delta:
530
+ # Emit interface event to inform subscribers about high unknown pong count.
531
+ _fire_event(mismatch_count=count)
532
+ if self._unknown_pong_logged is False:
533
+ _LOGGER.warning(
534
+ "Unknown PONG Mismatch: Your instance %s receives PONG events, that it hasn't send. "
535
+ "Possible reason 1: You are running multiple instances with the same instance name configured for this integration. "
536
+ "Re-add one instance! Otherwise the other instance will not receive update events from your CCU. "
537
+ "Possible reason 2: Something is stuck on the CCU or hasn't been cleaned up. Therefore, try a CCU restart.",
538
+ self._interface_id,
539
+ )
540
+ self._unknown_pong_logged = True
541
+ else:
542
+ # For unknown pongs, only reset the logged flag when we drop below the threshold.
543
+ # We do not emit an event here since there is no explicit expectation for a reset notification.
544
+ self._unknown_pong_logged = False
@@ -317,7 +317,7 @@ _IGNORE_PARAMETERS_BY_DEVICE: Final[Mapping[Parameter, frozenset[TModelName]]] =
317
317
  "HmIP-WGT",
318
318
  }
319
319
  ),
320
- Parameter.VALVE_STATE: frozenset({"HmIPW-FALMOT-C12", "HmIP-FALMOT-C12"}),
320
+ Parameter.VALVE_STATE: frozenset({"HmIP-FALMOT-C8", "HmIPW-FALMOT-C12", "HmIP-FALMOT-C12"}),
321
321
  }
322
322
 
323
323
  _IGNORE_PARAMETERS_BY_DEVICE_LOWER: Final[dict[TParameterName, frozenset[TModelName]]] = {
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aiohomematic
3
- Version: 2025.10.21
3
+ Version: 2025.10.22
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>
@@ -1,6 +1,6 @@
1
1
  aiohomematic/__init__.py,sha256=Uo9CIoil0Arl3GwtgMZAwM8jhcgoBKcZEgj8cXYlswY,2258
2
2
  aiohomematic/async_support.py,sha256=Fg6RLD7Irt1mTwXbLkfphJbfd7oU_Svhp23i3Bb4Q7k,8762
3
- aiohomematic/const.py,sha256=YOCIFXt3xTY7GT29XqKpHi8Ir9eYYtY6Ur2bzWwtCIs,27479
3
+ aiohomematic/const.py,sha256=omyYHT1lCVMAju-lRA3nTU0yuX2C_vM8MuZ5K3ofstA,27482
4
4
  aiohomematic/context.py,sha256=hGE-iPcPt21dY-1MZar-Hyh9YaKL-VS42xjrulIVyRQ,429
5
5
  aiohomematic/converter.py,sha256=FiHU71M5RZ7N5FXJYh2CN14s63-PM-SHdb0cJ_CLx54,3602
6
6
  aiohomematic/decorators.py,sha256=cSW0aF3PzrW_qW6H0sjRNH9eqO8ysqhXZDgJ2OJTZM4,11038
@@ -66,11 +66,11 @@ aiohomematic/rega_scripts/get_system_variable_descriptions.fn,sha256=UKXvC0_5lSA
66
66
  aiohomematic/rega_scripts/set_program_state.fn,sha256=0bnv7lUj8FMjDZBz325tDVP61m04cHjVj4kIOnUUgpY,279
67
67
  aiohomematic/rega_scripts/set_system_variable.fn,sha256=sTmr7vkPTPnPkor5cnLKlDvfsYRbGO1iq2z_2pMXq5E,383
68
68
  aiohomematic/store/__init__.py,sha256=PHwF_tw_zL20ODwLywHgpOLWrghQo_BMZzeiQSXN1Fc,1081
69
- aiohomematic/store/dynamic.py,sha256=K1nTal_bYTxOXF8Brmyv3yl7A4cy9ZZm0SQ9ANbUKi4,22757
69
+ aiohomematic/store/dynamic.py,sha256=7grL-rpIh4CYWVNqL7WO66zRJuw7qfi34S2sxSbsOac,22049
70
70
  aiohomematic/store/persistent.py,sha256=SBL8AhqUzpoPtJ50GkLYHwvRJS52fBWqNPjgvykxbY8,40233
71
- aiohomematic/store/visibility.py,sha256=0y93kPTugqQsrh6kKamfgwBkbIdBPEZpQVv_1NaLz3A,31662
72
- aiohomematic-2025.10.21.dist-info/licenses/LICENSE,sha256=q-B0xpREuZuvKsmk3_iyVZqvZ-vJcWmzMZpeAd0RqtQ,1083
73
- aiohomematic-2025.10.21.dist-info/METADATA,sha256=l1aVOPRrDGn6JSPcroaoRuZkFlmja6f-lv35NUnU-oI,7672
74
- aiohomematic-2025.10.21.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
75
- aiohomematic-2025.10.21.dist-info/top_level.txt,sha256=iGUvt1N-E72vKRq7Anpp62HwkQngStrUK0JfL1zj1TE,13
76
- aiohomematic-2025.10.21.dist-info/RECORD,,
71
+ aiohomematic/store/visibility.py,sha256=VAoaHMsCnIwcaTQjx_RkpPb64o5xjVNRldCfB2MGc1M,31680
72
+ aiohomematic-2025.10.22.dist-info/licenses/LICENSE,sha256=q-B0xpREuZuvKsmk3_iyVZqvZ-vJcWmzMZpeAd0RqtQ,1083
73
+ aiohomematic-2025.10.22.dist-info/METADATA,sha256=EG8oIYVQQVkO-mIfd5qUP8m1dxEULgWytT8DReLcBlo,7672
74
+ aiohomematic-2025.10.22.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
75
+ aiohomematic-2025.10.22.dist-info/top_level.txt,sha256=iGUvt1N-E72vKRq7Anpp62HwkQngStrUK0JfL1zj1TE,13
76
+ aiohomematic-2025.10.22.dist-info/RECORD,,