python-swidget 1.4.20__tar.gz → 1.4.21__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-swidget
3
- Version: 1.4.20
3
+ Version: 1.4.21
4
4
  Summary: Python API for Swidget smart devices
5
5
  Home-page: https://github.com/swidget/python-swidget
6
6
  License: GPL-3.0-or-later
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "python-swidget"
3
- version = "1.4.20"
3
+ version = "1.4.21"
4
4
  description = "Python API for Swidget smart devices"
5
5
  license = "GPL-3.0-or-later"
6
6
  authors = ["Swidget"]
@@ -502,7 +502,11 @@ class SwidgetDevice:
502
502
  # and flicker the UI.
503
503
  for assembly_key, new_assembly in new_assemblies.items():
504
504
  old_assembly = self.assemblies.get(assembly_key)
505
- if old_assembly is None:
505
+ if (
506
+ old_assembly is None
507
+ or old_assembly.type != new_assembly.type
508
+ or old_assembly.id != new_assembly.id
509
+ ):
506
510
  continue
507
511
  for component_id, new_component in new_assembly.components.items():
508
512
  old_component = old_assembly.components.get(component_id)
@@ -513,6 +517,17 @@ class SwidgetDevice:
513
517
  new_component.functions[fn_name] = old_component.functions[
514
518
  fn_name
515
519
  ]
520
+ # Fan module readings live in state["modules"], but
521
+ # "modules" is not a summary function tag. Preserve
522
+ # readings only for modules the new summary still lists;
523
+ # removed modules must not retain stale sensor state.
524
+ module_state = old_component.functions.get("modules")
525
+ if new_component.modules and isinstance(module_state, dict):
526
+ new_component.functions["modules"] = {
527
+ name: module_state[name]
528
+ for name in new_component.modules
529
+ if name in module_state
530
+ }
516
531
  self.assemblies = new_assemblies
517
532
  self.device_type = DeviceType(self.assemblies["host"].type)
518
533
  self.insert_type = InsertType(self.assemblies["insert"].type)
@@ -1064,11 +1079,11 @@ class SwidgetComponent:
1064
1079
  starts as a same-keyed dict of placeholder ``None`` values and is
1065
1080
  later mutated by ``process_state`` to carry live datapoint values.
1066
1081
 
1067
- Process_state also leaks in keys that aren't in the summary
1082
+ Process_state also adds keys that aren't in the summary
1068
1083
  functions list (e.g. fans emit a ``modules`` map in state that
1069
1084
  isn't a declared function tag), so ``functions.keys()`` is *not*
1070
- schema-stable across summary refreshes. Anything that needs a
1071
- stable schema fingerprint (entity wiring, structure-change
1085
+ the declared schema. Anything that needs a stable schema
1086
+ fingerprint (entity wiring, structure-change
1072
1087
  detection) must read ``summary_functions``, not ``functions``.
1073
1088
 
1074
1089
  ``max_cfm``, ``model_code`` and ``modules`` come from the optional
@@ -1079,7 +1094,7 @@ class SwidgetComponent:
1079
1094
  def __init__(self, component):
1080
1095
  funcs = list(component.get("functions", []))
1081
1096
  self.summary_functions: tuple[str, ...] = tuple(funcs)
1082
- self.functions = {f: None for f in funcs}
1097
+ self.functions: dict[str, Any] = {f: None for f in funcs}
1083
1098
  self.max_cfm = component.get("maxCFM")
1084
1099
  self.model_code = component.get("code")
1085
1100
  self.modules = list(component.get("modules", []))