From 99e3f80f049ca9e32c2601ef1c1dcf466838f309 Mon Sep 17 00:00:00 2001
From: Laurens <43173895+GreenGrassBlueOcean@users.noreply.github.com>
Date: Sun, 25 Oct 2026 21:46:15 +0100
Subject: [PATCH] fix(gateway,climate): ingest H/L4684 FW 6.0.11 trace and fix
H/L4684 & WHO 4 bugs (#466)
---
README.md | 28 +-
custom_components/myhome/climate.py | 36 +-
custom_components/myhome/const.py | 8 +-
custom_components/myhome/diagnostics.py | 2 +
custom_components/myhome/gateway.py | 71 +-
custom_components/myhome/identity.py | 10 +-
custom_components/myhome/manifest.json | 5 +
custom_components/myhome/services.py | 11 +-
custom_components/myhome/topology.py | 14 +-
docs/configuration/gateways.md | 2 +-
.../getting-started/hardware-compatibility.md | 4 +-
docs/trace-availability.md | 26 +-
scripts/update_gateway_profiles.py | 14 +-
scripts/update_trace_matrix.py | 14 +-
tests/fixtures/traces/issue_466/README.md | 41 +
.../config_entry-myhome_HL4684_fw6_0_11.json | 5470 +++++++++++++++++
tests/test_climate_humidity_poll_gate.py | 16 +-
tests/test_diagnostics.py | 3 +-
tests/test_gateway_identification.py | 14 +-
tests/test_gateway_profiles.py | 4 +-
tests/test_issue_466_hl4684_trace.py | 491 ++
tests/test_multi_gateway.py | 10 +-
tests/test_services.py | 8 +-
tests/test_startup_requests_per_firmware.py | 19 +-
24 files changed, 6216 insertions(+), 105 deletions(-)
create mode 100644 tests/fixtures/traces/issue_466/config_entry-myhome_HL4684_fw6_0_11.json
create mode 100644 tests/test_issue_466_hl4684_trace.py
diff --git a/README.md b/README.md
index 1f63cc6e1..996df89f4 100644
--- a/README.md
+++ b/README.md
@@ -90,7 +90,7 @@ We now maintain a comprehensive, community-curated **[GitHub Wiki](https://githu
| **MH200** *(Legacy)* | OpenWebNet | 1 worker | 150 ms | ✅ SSDP | Strict single-session pacing; watchdog hardened |
| **H4890 / AM4890** | OpenWebNet | 1 worker | 50 ms | ✅ SSDP | 3.5" Touch screen display IP gateway (Axolute / Livinglight) |
| **F452 / F453AV** | OpenWebNet | 1 worker | 150 ms | ✅ Port 49153 | Audio/video & web server gateway |
-| **HL4684** | OpenWebNet | 1 worker | 50 ms | ✅ SSDP | 10" Touch screen display IP gateway |
+| **H4684 / H/L4684** | OpenWebNet | 1 worker | 50 ms | ✅ SSDP | Colour touch screen IP gateway (Axolute / LivingLight) |
| **Legrand 3578** | OpenWebNet (Serial) | 1 worker | 50 ms | ❌ Manual (Serial) | USB / Serial gateway & OpenZigBee interface |
@@ -98,19 +98,19 @@ We now maintain a comprehensive, community-curated **[GitHub Wiki](https://githu
### 📊 Hardware Trace Availability Matrix
-| Gateway Model | WHO 0
Scenario | WHO 1
Lights | WHO 2
Autom. | WHO 4
Climate | WHO 5
Alarm | WHO 9
Power | WHO 13
Gateway | WHO 14
Lock | WHO 15
CEN | WHO 16
Audio | WHO 17
Scenario | WHO 18
Energy | WHO 22
Audio Diff. | WHO 25
Diag | WHO 1001
Diag | WHO 1013
Diag | WHO 1022
Diag |
-| :--- | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: |
-| **F453AV** | | ✅ | ✅ | ✅ | ✅ | | ✅ | | | ✅ | | ✅ | | | ✅ | | |
-| **F454** | | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | ✅ | | ✅ | | ✅ | | ✅ | |
-| **F455** | | ✅ | ✅ | ✅ | ✅ | | ✅ | | | ✅ | | ✅ | | | | ✅ | |
-| **F461** | | ✅ | ✅ | ✅ | ✅ | | ✅ | ✅ | | ✅ | | ✅ | | | | ✅ | |
-| **H4684** | | | | | | | ✅ | | | | | | | | | ✅ | |
-| **H4890 / AM4890** | | ✅ | ✅ | ✅ | ✅ | ✅ | | | | ✅ | | ✅ | ✅ | ✅ | | | |
-| **MH200** | | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | | ✅ | ✅ | | | | ✅ | ✅ | |
-| **MH200N** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
-| **MH201** | ✅ | ✅ | ✅ | ✅ | | | ✅ | ✅ | ✅ | ✅ | | ✅ | | ✅ | | | |
-| **MH202** | | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | ✅ | | ✅ | ✅ | ✅ | | ✅ | |
-| **MyHomeServer1** | | ✅ | ✅ | ✅ | | ✅ | ✅ | ✅ | ✅ | ✅ | | ✅ | ✅ | ✅ | ✅ | ✅ | |
+| Gateway Model | WHO 0
Scenario | WHO 1
Lights | WHO 2
Autom. | WHO 3
Load | WHO 4
Climate | WHO 5
Alarm | WHO 9
Power | WHO 13
Gateway | WHO 14
Lock | WHO 15
CEN | WHO 16
Audio | WHO 17
Scenario | WHO 18
Energy | WHO 22
Audio Diff. | WHO 25
Diag | WHO 1001
Diag | WHO 1013
Diag | WHO 1022
Diag |
+| :--- | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: |
+| **F453AV** | | ✅ | ✅ | | ✅ | ✅ | | ✅ | | | ✅ | | ✅ | | | ✅ | | |
+| **F454** | | ✅ | ✅ | | ✅ | ✅ | ✅ | ✅ | ✅ | | ✅ | | ✅ | | ✅ | | ✅ | |
+| **F455** | | ✅ | ✅ | | ✅ | ✅ | | ✅ | | | ✅ | | ✅ | | | | ✅ | |
+| **F461** | | ✅ | ✅ | | ✅ | ✅ | | ✅ | ✅ | | ✅ | | ✅ | | | | ✅ | |
+| **H4684 / H/L4684** | | ✅ | ✅ | ✅ | ✅ | | | ✅ | | | ✅ | | ✅ | ✅ | | | ✅ | |
+| **H4890 / AM4890** | | ✅ | ✅ | | ✅ | ✅ | ✅ | | | | ✅ | | ✅ | ✅ | ✅ | | | |
+| **MH200** | | ✅ | ✅ | | ✅ | ✅ | ✅ | ✅ | | | ✅ | ✅ | | | | ✅ | ✅ | |
+| **MH200N** | ✅ | ✅ | ✅ | | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
+| **MH201** | ✅ | ✅ | ✅ | | ✅ | | | ✅ | ✅ | ✅ | ✅ | | ✅ | | ✅ | | | |
+| **MH202** | | ✅ | ✅ | | ✅ | ✅ | ✅ | ✅ | ✅ | | ✅ | | ✅ | ✅ | ✅ | | ✅ | |
+| **MyHomeServer1** | | ✅ | ✅ | | ✅ | | ✅ | ✅ | ✅ | ✅ | ✅ | | ✅ | ✅ | ✅ | ✅ | ✅ | |
*Checkmarks (✅) indicate that at least one `diagnostic_summary.json` or `.txt` bus capture in our test corpus contains frames for that subsystem from the specified gateway model. This matrix is automatically updated from the fixtures repository.*
diff --git a/custom_components/myhome/climate.py b/custom_components/myhome/climate.py
index a7e8700ec..35b037a4d 100644
--- a/custom_components/myhome/climate.py
+++ b/custom_components/myhome/climate.py
@@ -368,6 +368,7 @@ def __init__(
# Fan mode support (fancoil 3-speed + auto)
self._fan: bool = False
+ self._fan_configured: bool = bool(fan and not self._central)
self._attr_fan_mode: str | None = None
self._attr_fan_modes: list[str] | None = None
self._running_fan_speed: str | None = None
@@ -473,6 +474,18 @@ def _enable_fan_mode(self) -> None:
if self._attr_fan_mode is None:
self._attr_fan_mode = "auto"
+ def _has_no_fan_coil_sides(self) -> bool:
+ """Whether dimension 19 reported that neither side of this zone is a fan coil."""
+ return bool(self._fan_coil_sides) and not any(self._fan_coil_sides.values())
+
+ def _disable_fan_mode(self) -> None:
+ """Retract dynamically enabled fan mode once dimension 19 proves a pure-valve zone."""
+ self._fan = False
+ self._attr_supported_features &= ~ClimateEntityFeature.FAN_MODE
+ self._attr_fan_modes = None
+ self._attr_fan_mode = None
+ self._running_fan_speed = None
+
def _enable_humidity(self) -> None:
"""Dynamically enable humidity support if not already enabled."""
if self._central:
@@ -573,9 +586,10 @@ async def async_restore_last_state(self, last_state: State | None) -> None:
"heating": bool(last_state.attributes.get("fan_coil_heating", False)),
"cooling": bool(last_state.attributes.get("fan_coil_cooling", False)),
}
- if "fan_mode" in last_state.attributes or bool(
- last_state.attributes.get("supported_features", 0) & ClimateEntityFeature.FAN_MODE
- ):
+ if (
+ "fan_mode" in last_state.attributes
+ or bool(last_state.attributes.get("supported_features", 0) & ClimateEntityFeature.FAN_MODE)
+ ) and not (self._has_no_fan_coil_sides() and not self._fan_configured):
self._enable_fan_mode()
restored_fan_mode = last_state.attributes.get("fan_mode")
if restored_fan_mode in ("auto", "low", "medium", "high"):
@@ -1379,10 +1393,18 @@ def handle_event(self, message: OWNHeatingEvent) -> None:
}
self._fan_coil_sides_asked_at = None
self._fan_coil_sides_held = False
- # Actuator status (Dimension 20) with values >= 5 reports fancoil fan status in OWNd.
- is_fan = isinstance(getattr(message, "fan_speed", None), int) or isinstance(
- getattr(message, "fan_on", None), bool
- )
+ if self._has_no_fan_coil_sides() and not self._fan_configured:
+ # Pure-valve actuator channels can report value 5 on dimension 20 (issue #466 H/L4684
+ # zones 11..88) while dimension 19 carries 0..4 on both sides once _ask_fan_coil_sides()
+ # (*#4*Z*19##) is answered (myhomeserver1_live_2026-10-25 zones 35..70, issue_674 F455):
+ # retract the tentative fan_mode once dimension 19 proves neither side is a fan coil.
+ self._disable_fan_mode()
+ # Actuator status (Dimension 20) with values >= 5 reports fancoil fan status in OWNd,
+ # unless dimension 19 already proved neither side of an unconfigured zone is a fan coil.
+ is_fan = (
+ isinstance(getattr(message, "fan_speed", None), int)
+ or isinstance(getattr(message, "fan_on", None), bool)
+ ) and not (self._has_no_fan_coil_sides() and not self._fan_configured)
fan_enabled_now = is_fan and not self._fan
if is_fan:
self._enable_fan_mode()
diff --git a/custom_components/myhome/const.py b/custom_components/myhome/const.py
index dae81a40a..73d208d09 100644
--- a/custom_components/myhome/const.py
+++ b/custom_components/myhome/const.py
@@ -322,6 +322,7 @@ def percent_to_eight_bits(value: int) -> int:
"AM4890",
"H4890",
"LN4890",
+ "H4684",
"Generic",
]
@@ -353,7 +354,11 @@ def percent_to_eight_bits(value: int) -> int:
# Shared across modern gateway families (not all Linux-based, e.g. MH201),
# so it identifies none of them (see WHO13_SHARED_DEVICE_TYPES).
# It contradicts legacy gateways (e.g. MH200/F452), but only as field evidence.
+# 23: Confirmed on physical BTicino H/L4684 touchscreen gateway (issue #466
+# comment 6398442412, firmware 6.0.11, kernel 2.6.2, distro 6.0.0; UPnP
+# modelName `H/L4684`, distinct from 2006 spec code `13` on FW 2.0.54).
WHO13_OBSERVED_DEVICE_TYPES: dict[str, tuple[str, ...]] = {
+ "23": ("H4684", "H/L4684", "L4684"),
"200": ("F454", "MyHomeServer1", "MH202", "F461", "H4890", "MH201"),
}
# Codes from an independent implementation: the `device` table of Nmap's
@@ -380,7 +385,6 @@ def percent_to_eight_bits(value: int) -> int:
"12": ("F453AV",),
"15": ("F427",), # Nmap: "F427 (Gateway Open-KNX)"
"16": ("F453",),
- "23": ("H4684",), # a second code for the model the 2006 table gives as 13
"27": ("L4686SDK",),
"44": ("MH200N",),
"51": ("F454",),
@@ -487,7 +491,7 @@ def gateway_model_family(model: str | None) -> str:
"""
if not model:
return ""
- name = str(model).strip().upper().replace(" ", "").replace("-", "").replace("_", "")
+ name = str(model).strip().upper().replace(" ", "").replace("-", "").replace("_", "").replace("/", "")
m = re.match(r"^([A-Z]+\d+)[A-Z]*$", name)
return m.group(1) if m else name
diff --git a/custom_components/myhome/diagnostics.py b/custom_components/myhome/diagnostics.py
index 39f7154ae..212697fd8 100644
--- a/custom_components/myhome/diagnostics.py
+++ b/custom_components/myhome/diagnostics.py
@@ -12,6 +12,7 @@
CONF_DECODER_COMPANION,
CONF_DECODER_ENTITY,
CONF_DECODER_SLOTS,
+ CONF_FILE_PATH,
CONF_PRIMARY_GATEWAY,
DOMAIN,
INTEGRATION_VERSION,
@@ -53,6 +54,7 @@
"ssdp_location",
"friendly_name",
"file_path",
+ CONF_FILE_PATH,
CONF_PRIMARY_GATEWAY,
"primary_mac",
"secondary_mac",
diff --git a/custom_components/myhome/gateway.py b/custom_components/myhome/gateway.py
index 01ddf252a..d04e0b31c 100644
--- a/custom_components/myhome/gateway.py
+++ b/custom_components/myhome/gateway.py
@@ -124,17 +124,55 @@
]
+def _call_get_gateway_profile(
+ model: str | None, firmware: str | None = None, *, lookup: Any = None
+) -> Any:
+ """Call ``get_gateway_profile`` with optional firmware, tolerating 1-arg mocks."""
+ fn = lookup if lookup is not None else get_gateway_profile
+ if firmware:
+ try:
+ return fn(model, firmware)
+ except TypeError: # pragma: no cover - fallback for 1-arg mocks or older OWNd
+ return fn(model)
+ return fn(model)
+
+
+def _profile_for_model(
+ model: str | None, firmware: str | None = None, *, lookup: Any = None
+) -> Any:
+ """Resolve ``model`` to an OWNd profile, mapping SSDP spellings like ``H/L4684``."""
+ profile = _call_get_gateway_profile(model, firmware, lookup=lookup)
+ if isinstance(profile, GenericGatewayProfile) and model:
+ mapped = _MODEL_SPELLINGS.get(_alnum(model))
+ if mapped is not None:
+ return _call_get_gateway_profile(mapped, firmware, lookup=lookup)
+ return profile
+
+
+def _firmware_at_least(firmware: str | None, minimum: tuple[int, int, int]) -> bool:
+ """Return whether ``firmware`` parses to at least ``minimum`` (V, R, B)."""
+ if not firmware:
+ return False
+ parts = [int(m) for m in re.findall(r"\d+", str(firmware))]
+ if not parts:
+ return False
+ while len(parts) < 3:
+ parts.append(0)
+ return tuple(parts[:3]) >= minimum
+
+
def command_session_limit(model: str | None) -> int | None:
"""Return how many command sessions a known gateway model accepts at once."""
- profile = get_gateway_profile(model)
- if isinstance(profile, GenericGatewayProfile):
+ profile = _profile_for_model(model)
+ max_sessions = getattr(profile, "max_command_sessions", None)
+ if isinstance(profile, GenericGatewayProfile) or max_sessions is None:
return None
- return int(profile.max_command_sessions)
+ return int(max_sessions)
def command_session_default(model: str | None) -> int:
"""Return how many command sessions a new entry should start with."""
- profile = get_gateway_profile(model)
+ profile = _profile_for_model(model)
default = int(getattr(profile, "default_command_sessions", 1))
limit = command_session_limit(model)
return max(1, min(default, limit) if limit is not None else 1)
@@ -229,9 +267,9 @@ def command_session_default(model: str | None) -> int:
key=len,
reverse=True,
)
-# SSDP announces the H4684 as "HL4684" (manifest.json), which OWNd does not alias to its H4684
-# profile yet (it knows h4684, l4684, n4684, nt4684, lgrh4684; #794 maps the same spelling), and
-# "HC4684" is the other prefixed spelling of the same product: keyed on the oracle's product name.
+# SSDP announces the H4684 as "H/L4684" (issue #466) or "HL4684" (manifest.json), which OWNd<=2.0.0b11
+# does not alias to its H4684 profile yet, and "HC4684" is the other prefixed spelling of the same product.
+# TODO(OWNd>2.0.0b11): remove shim once manifest.json pins an OWNd release containing OWNd#147.
_MODEL_SPELLINGS = {"HL4684": "H4684", "HC4684": "H4684"}
@@ -313,6 +351,8 @@ def __init__(
self.config_entry = config_entry
self.generate_events = generate_events
self.gateway = OWNGateway(build_info)
+ if isinstance(self.gateway.profile, GenericGatewayProfile):
+ self.gateway.profile = _profile_for_model(self.gateway.model_name, self.gateway.firmware)
self.is_connected = False
self._available = False
self._failover_active = False
@@ -460,17 +500,21 @@ def firmware(self) -> str | None:
@property
def supports_zone_status(self) -> bool:
"""Whether the gateway supports point-to-point zone status queries (*#4*Z##)."""
- # The configured model string and the model OWNd resolves it to, so the spellings OWNd
- # aliases (L4684, N4684, NT4684, LGR4684 and the MyHOME_Suite item numbers such as 003598
- # for the F454) are judged like the model they stand for.
- names = (self.model.upper(), str(get_gateway_profile(self.model).model_name).upper())
+ # Check the configured model string, its alphanumeric form (so "BTicino H/L4684" matches
+ # "HL4684"), and the profile model _profile_for_model resolves it to (L4684, NT4684,
+ # H/L4684, HL4684, HC4684, and MyHOME_Suite catalogue codes such as 003598).
+ names = (
+ self.model.upper(),
+ _alnum(self.model),
+ str(_profile_for_model(self.model).model_name).upper(),
+ )
return not any(
k in norm
for norm in names
for k in (
"F450",
"F455",
- "HL4684", # the SSDP spelling of the H4684 (manifest.json); not an OWNd alias yet
+ "HL4684", # TODO(OWNd>2.0.0b11): remove once manifest.json pins OWNd containing OWNd#147
"F454",
"MH200",
"MH201",
@@ -766,6 +810,9 @@ def profile_supports_who(self, who: int) -> bool:
def _profile_supports_who(self, who: int) -> bool:
"""Return whether the gateway profile advertises a WHO subsystem (True when unknown)."""
+ # TODO(OWNd>2.0.0b11): remove shim once manifest.json pins an OWNd release containing OWNd#147.
+ if who == 22 and self._model_key == "H4684" and _firmware_at_least(self.firmware, (6, 0, 0)):
+ return True
profile = getattr(self.gateway, "profile", None)
supports = getattr(profile, "supports_who", None)
if not callable(supports):
diff --git a/custom_components/myhome/identity.py b/custom_components/myhome/identity.py
index eb5cc9576..e2bcd9b05 100644
--- a/custom_components/myhome/identity.py
+++ b/custom_components/myhome/identity.py
@@ -59,8 +59,8 @@
def _normalized(model: str | None) -> str:
- """Compare model names without case, spaces, dashes or underscores."""
- return str(model or "").strip().upper().replace(" ", "").replace("-", "").replace("_", "")
+ """Compare model names without case, spaces, dashes, slashes or underscores."""
+ return str(model or "").strip().upper().replace(" ", "").replace("-", "").replace("_", "").replace("/", "")
def _same_product_names() -> dict[str, frozenset[str]]:
@@ -403,6 +403,7 @@ def _handle_gateway_diagnostics(self, message: Any) -> None:
fw,
)
self.gateway.firmware = fw
+ self.gateway.profile = _gw._profile_for_model(self.gateway.model_name, fw)
if self.config_entry is not None:
new_data = dict(self.config_entry.data)
if new_data.get(CONF_FIRMWARE) != fw:
@@ -534,10 +535,7 @@ def _apply_model(self, model: str) -> None:
from .topology import entry_firmware
fw = entry_firmware(self.config_entry)
- try:
- self.gateway.profile = _gw.get_gateway_profile(model, fw)
- except TypeError: # pragma: no cover - fallback for OWNd without firmware argument
- self.gateway.profile = _gw.get_gateway_profile(model)
+ self.gateway.profile = _gw._profile_for_model(model, fw)
self.gateway._log_id = f"[{model} gateway - {self.gateway.host}]"
self._trim_sending_workers(model)
new_data = dict(self.config_entry.data)
diff --git a/custom_components/myhome/manifest.json b/custom_components/myhome/manifest.json
index b1e39fee7..f41aeb422 100644
--- a/custom_components/myhome/manifest.json
+++ b/custom_components/myhome/manifest.json
@@ -19,6 +19,11 @@
"OWNd==2.0.0b11"
],
"ssdp": [
+ {
+ "st": "upnp:rootdevice",
+ "manufacturer": "BTicino S.p.A.",
+ "modelName": "H/L4684"
+ },
{
"st": "upnp:rootdevice",
"manufacturer": "BTicino S.p.A.",
diff --git a/custom_components/myhome/services.py b/custom_components/myhome/services.py
index 3b45808c5..356adf217 100644
--- a/custom_components/myhome/services.py
+++ b/custom_components/myhome/services.py
@@ -246,11 +246,12 @@ async def handle_sweep_bus(call: ServiceCall) -> None:
for gw_mac, handler in target_gateways.items():
_LOGGER.info("Executing diagnostic bus sweep on gateway %s (force=%s)", gw_mac, force)
gateway_queries = [
- "*#13**0##", # Gateway real-time clock
- "*#13**15##", # Gateway device model
- "*#13**16##", # Gateway firmware version
- "*#13**23##", # Gateway kernel version (modern Linux gateways; NACK ignored on older)
- "*#13**24##", # Gateway distribution version (modern Linux gateways; NACK ignored on older)
+ "*#13**0##", # Gateway real-time clock
+ "*#13**15##", # Gateway device model
+ "*#13**16##", # Gateway firmware version
+ "*#13**23##", # Gateway kernel version (modern Linux gateways; NACK ignored on older)
+ "*#13**24##", # Gateway distribution version (modern Linux gateways; NACK ignored on older)
+ "*#1013*0*1##", # Gateway diagnostic OBJECT_MODEL
]
general_queries = ((1, "*#1*0##"), (2, "*#2*0##"), (4, "*#4*0##"), (5, "*#5*0##"), (16, "*#16*0*5##"))
diff --git a/custom_components/myhome/topology.py b/custom_components/myhome/topology.py
index a2c24bf54..2c2c9d32e 100644
--- a/custom_components/myhome/topology.py
+++ b/custom_components/myhome/topology.py
@@ -87,16 +87,20 @@ def gateway_supported_whos(model: str | None, firmware: str | None = None) -> se
whos: set[int] = set()
norm = (model or "").strip().upper()
try:
- from OWNd.profiles import get_gateway_profile
+ import OWNd.profiles as _ownd_profiles
- try:
- profile = get_gateway_profile(model or "", firmware)
- except TypeError: # pragma: no cover - fallback for OWNd without firmware argument
- profile = get_gateway_profile(model or "")
+ from .gateway import _firmware_at_least, _profile_for_model
+
+ profile = _profile_for_model(
+ model or "", firmware, lookup=_ownd_profiles.get_gateway_profile
+ )
supports = getattr(profile, "supports_who", None)
for w in getattr(profile, "supported_who", None) or ():
if not callable(supports) or supports(int(w)):
whos.add(int(w))
+ # TODO(OWNd>2.0.0b11): remove shim once manifest.json pins an OWNd release containing OWNd#147.
+ if getattr(profile, "model_name", None) == "H4684" and _firmware_at_least(firmware, (6, 0, 0)):
+ whos.add(22)
except Exception:
pass
# Model-specific hardware capability constraints:
diff --git a/docs/configuration/gateways.md b/docs/configuration/gateways.md
index 80cac114c..6e2b966f5 100644
--- a/docs/configuration/gateways.md
+++ b/docs/configuration/gateways.md
@@ -21,7 +21,7 @@ The MyHOME integration communicates with SCS bus gateways over TCP/IP or RS232/U
| **MH200** *(Legacy)* | OpenWebNet | 1 worker | 150 ms | ✅ SSDP | Strict single-session pacing; watchdog hardened |
| **H4890 / AM4890** | OpenWebNet | 1 worker | 50 ms | ✅ SSDP | 3.5" Touch screen display IP gateway (Axolute / Livinglight) |
| **F452 / F453AV** | OpenWebNet | 1 worker | 150 ms | ✅ Port 49153 | Audio/video & web server gateway |
-| **HL4684** | OpenWebNet | 1 worker | 50 ms | ✅ SSDP | 10" Touch screen display IP gateway |
+| **H4684 / H/L4684** | OpenWebNet | 1 worker | 50 ms | ✅ SSDP | Colour touch screen IP gateway (Axolute / LivingLight) |
| **Legrand 3578** | OpenWebNet (Serial) | 1 worker | 50 ms | ❌ Manual (Serial) | USB / Serial gateway & OpenZigBee interface |
diff --git a/docs/getting-started/hardware-compatibility.md b/docs/getting-started/hardware-compatibility.md
index 2bde9b5d4..d80121e00 100644
--- a/docs/getting-started/hardware-compatibility.md
+++ b/docs/getting-started/hardware-compatibility.md
@@ -19,7 +19,7 @@ The MyHOME integration supports all official BTicino and Legrand OpenWebNet gate
| **MH200** *(Legacy)* | OpenWebNet | 1 worker | 150 ms | ✅ SSDP | Strict single-session pacing; watchdog hardened |
| **H4890 / AM4890** | OpenWebNet | 1 worker | 50 ms | ✅ SSDP | 3.5" Touch screen display IP gateway (Axolute / Livinglight) |
| **F452 / F453AV** | OpenWebNet | 1 worker | 150 ms | ✅ Port 49153 | Audio/video & web server gateway |
-| **HL4684** | OpenWebNet | 1 worker | 50 ms | ✅ SSDP | 10" Touch screen display IP gateway |
+| **H4684 / H/L4684** | OpenWebNet | 1 worker | 50 ms | ✅ SSDP | Colour touch screen IP gateway (Axolute / LivingLight) |
| **Legrand 3578** | OpenWebNet (Serial) | 1 worker | 50 ms | ❌ Manual (Serial) | USB / Serial gateway & OpenZigBee interface |
@@ -64,6 +64,6 @@ During startup initial discovery, status queries are gated by the gateway profil
| **MH201** | WHO 1, 2, 4, 16, 18 | WHO 1, 2, 16, 18 (firmware drops `*#4*0##`) | Lighting, cover, climate, sound (unmeasured class default), and energy meter 51 queries. Not in own-firmware-oracle; a physical MH201 3.3.6 returned no zone frame to `*#4*0##` although its zones report (`tests/fixtures/traces/issue_466`), so the sweep leaves it out and each climate zone asks `*#4**0##` itself. Alarm query omitted. |
| **MH200** *(Legacy)* | WHO 1, 2, 4, 16 | WHO 1, 2, 16 (firmware drops `*#4*0##`) | Lighting, cover, climate, and sound queries (`bt_difson` hardware and firmware-verified). The `020102` firmware drops `*#4*0##` (own-firmware-oracle), so its climate zones ask `*#4**0##` themselves. Alarm and energy queries omitted. |
| **MH200N** | WHO 1, 2, 4, 16 | WHO 1, 2, 4, 16 | Lighting, cover, climate, and sound queries (`bt_difson` hardware and firmware-verified). The `010108` firmware forwards `*#4*0##` (own-firmware-oracle), unlike the MH200. Alarm and energy queries omitted. |
-| **H4684 / HL4684** | WHO 1, 2, 4, 16, 18 | WHO 1, 2, 16, 18 (firmware drops `*#4*0##`) | Lighting, cover, climate, sound, and energy meter 51 queries. The `020054` firmware drops `*#4*0##` (own-firmware-oracle), so each climate zone asks `*#4**0##` itself; the SSDP spelling `HL4684` is keyed on the same tables. Alarm query omitted. |
+| **H4684 / H/L4684** | WHO 1, 2, 4, 16, 18 | WHO 1, 2, 16, 18 (firmware drops `*#4*0##`) | Lighting, cover, climate, sound, and energy meter 51 queries (`bt_multi` WHO 22 also enabled on firmware >= `6.0.0`). Both `020054` (own-firmware-oracle) and live `6.0.11` (`tests/fixtures/traces/issue_466`) drop `*#4*0##`, so each climate zone asks `*#4**0##` itself; the SSDP spellings `H/L4684` and `HL4684` resolve to `H4684Profile`. Alarm query omitted. |
diff --git a/docs/trace-availability.md b/docs/trace-availability.md
index d893de923..9a9dac628 100644
--- a/docs/trace-availability.md
+++ b/docs/trace-availability.md
@@ -3,19 +3,19 @@
This matrix documents the real-world physical gateways for which we have captured bus traces and diagnostic bundles in our `tests/fixtures` corpus. It is an honest representation of the hardware configurations that we actively test against to validate our protocol implementation.
-| Gateway Model | WHO 0
Scenario | WHO 1
Lights | WHO 2
Autom. | WHO 4
Climate | WHO 5
Alarm | WHO 9
Power | WHO 13
Gateway | WHO 14
Lock | WHO 15
CEN | WHO 16
Audio | WHO 17
Scenario | WHO 18
Energy | WHO 22
Audio Diff. | WHO 25
Diag | WHO 1001
Diag | WHO 1013
Diag | WHO 1022
Diag |
-| :--- | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: |
-| **F453AV** | | ✅ | ✅ | ✅ | ✅ | | ✅ | | | ✅ | | ✅ | | | ✅ | | |
-| **F454** | | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | ✅ | | ✅ | | ✅ | | ✅ | |
-| **F455** | | ✅ | ✅ | ✅ | ✅ | | ✅ | | | ✅ | | ✅ | | | | ✅ | |
-| **F461** | | ✅ | ✅ | ✅ | ✅ | | ✅ | ✅ | | ✅ | | ✅ | | | | ✅ | |
-| **H4684** | | | | | | | ✅ | | | | | | | | | ✅ | |
-| **H4890 / AM4890** | | ✅ | ✅ | ✅ | ✅ | ✅ | | | | ✅ | | ✅ | ✅ | ✅ | | | |
-| **MH200** | | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | | ✅ | ✅ | | | | ✅ | ✅ | |
-| **MH200N** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
-| **MH201** | ✅ | ✅ | ✅ | ✅ | | | ✅ | ✅ | ✅ | ✅ | | ✅ | | ✅ | | | |
-| **MH202** | | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | ✅ | | ✅ | ✅ | ✅ | | ✅ | |
-| **MyHomeServer1** | | ✅ | ✅ | ✅ | | ✅ | ✅ | ✅ | ✅ | ✅ | | ✅ | ✅ | ✅ | ✅ | ✅ | |
+| Gateway Model | WHO 0
Scenario | WHO 1
Lights | WHO 2
Autom. | WHO 3
Load | WHO 4
Climate | WHO 5
Alarm | WHO 9
Power | WHO 13
Gateway | WHO 14
Lock | WHO 15
CEN | WHO 16
Audio | WHO 17
Scenario | WHO 18
Energy | WHO 22
Audio Diff. | WHO 25
Diag | WHO 1001
Diag | WHO 1013
Diag | WHO 1022
Diag |
+| :--- | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: |
+| **F453AV** | | ✅ | ✅ | | ✅ | ✅ | | ✅ | | | ✅ | | ✅ | | | ✅ | | |
+| **F454** | | ✅ | ✅ | | ✅ | ✅ | ✅ | ✅ | ✅ | | ✅ | | ✅ | | ✅ | | ✅ | |
+| **F455** | | ✅ | ✅ | | ✅ | ✅ | | ✅ | | | ✅ | | ✅ | | | | ✅ | |
+| **F461** | | ✅ | ✅ | | ✅ | ✅ | | ✅ | ✅ | | ✅ | | ✅ | | | | ✅ | |
+| **H4684 / H/L4684** | | ✅ | ✅ | ✅ | ✅ | | | ✅ | | | ✅ | | ✅ | ✅ | | | ✅ | |
+| **H4890 / AM4890** | | ✅ | ✅ | | ✅ | ✅ | ✅ | | | | ✅ | | ✅ | ✅ | ✅ | | | |
+| **MH200** | | ✅ | ✅ | | ✅ | ✅ | ✅ | ✅ | | | ✅ | ✅ | | | | ✅ | ✅ | |
+| **MH200N** | ✅ | ✅ | ✅ | | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
+| **MH201** | ✅ | ✅ | ✅ | | ✅ | | | ✅ | ✅ | ✅ | ✅ | | ✅ | | ✅ | | | |
+| **MH202** | | ✅ | ✅ | | ✅ | ✅ | ✅ | ✅ | ✅ | | ✅ | | ✅ | ✅ | ✅ | | ✅ | |
+| **MyHomeServer1** | | ✅ | ✅ | | ✅ | | ✅ | ✅ | ✅ | ✅ | ✅ | | ✅ | ✅ | ✅ | ✅ | ✅ | |
*Checkmarks (✅) indicate that at least one `diagnostic_summary.json` or `.txt` bus capture in our test corpus contains frames for that subsystem from the specified gateway model.*
diff --git a/scripts/update_gateway_profiles.py b/scripts/update_gateway_profiles.py
index d855cdd3c..b21334fee 100644
--- a/scripts/update_gateway_profiles.py
+++ b/scripts/update_gateway_profiles.py
@@ -154,15 +154,15 @@
"notes": "Audio/video & web server gateway",
},
{
- "model": "**HL4684**",
- "const_models": [],
- "ssdp_models": ["HL4684"],
- "profile_name": "Generic",
+ "model": "**H4684 / H/L4684**",
+ "const_models": ["H4684"],
+ "ssdp_models": ["H/L4684", "HL4684"],
+ "profile_name": "H4684",
"protocol": "OpenWebNet",
"max_workers": "1 worker",
"delay": "50 ms",
"upnp": "✅ SSDP",
- "notes": '10" Touch screen display IP gateway',
+ "notes": "Colour touch screen IP gateway (Axolute / LivingLight)",
},
{
"model": "**Legrand 3578**",
@@ -257,11 +257,11 @@
"notes": "Lighting, cover, climate, and sound queries (`bt_difson` hardware and firmware-verified). The `010108` firmware forwards `*#4*0##` (own-firmware-oracle), unlike the MH200. Alarm and energy queries omitted.",
},
{
- "model": "**H4684 / HL4684**",
+ "model": "**H4684 / H/L4684**",
"profile_name": "H4684",
"queried_whos": [1, 2, 4, 16, 18],
"sweep_omits": [GENERAL_ZONE_STATUS],
- "notes": "Lighting, cover, climate, sound, and energy meter 51 queries. The `020054` firmware drops `*#4*0##` (own-firmware-oracle), so each climate zone asks `*#4**0##` itself; the SSDP spelling `HL4684` is keyed on the same tables. Alarm query omitted.",
+ "notes": "Lighting, cover, climate, sound, and energy meter 51 queries (`bt_multi` WHO 22 also enabled on firmware >= `6.0.0`). Both `020054` (own-firmware-oracle) and live `6.0.11` (`tests/fixtures/traces/issue_466`) drop `*#4*0##`, so each climate zone asks `*#4**0##` itself; the SSDP spellings `H/L4684` and `HL4684` resolve to `H4684Profile`. Alarm query omitted.",
},
]
diff --git a/scripts/update_trace_matrix.py b/scripts/update_trace_matrix.py
index c1d6da964..30689ce28 100644
--- a/scripts/update_trace_matrix.py
+++ b/scripts/update_trace_matrix.py
@@ -10,6 +10,16 @@
START_MARKER = ''
END_MARKER = ''
+GATEWAY_CANONICAL_KEYS = {
+ "H/L4684": "H4684",
+ "HL4684": "H4684",
+ "L4684": "H4684",
+ "AM4890": "H4890",
+ "LN4890": "H4890",
+ "LN4890A": "H4890",
+}
+
+
def get_who(frame):
if not frame.startswith('*'):
return None
@@ -53,6 +63,7 @@ def build_matrix():
if m:
gateway = m.group(1)
+ gateway = GATEWAY_CANONICAL_KEYS.get(gateway, gateway)
if whos:
if gateway not in gateways_data:
gateways_data[gateway] = set()
@@ -84,7 +95,7 @@ def build_matrix():
all_whos_found.update(whos)
who_names = {
- 0: 'Scenario', 1: 'Lights', 2: 'Autom.', 4: 'Climate', 5: 'Alarm', 9: 'Power',
+ 0: 'Scenario', 1: 'Lights', 2: 'Autom.', 3: 'Load', 4: 'Climate', 5: 'Alarm', 9: 'Power',
13: 'Gateway', 14: 'Lock', 15: 'CEN', 16: 'Audio', 17: 'Scenario', 18: 'Energy',
22: 'Audio Diff.', 25: 'Diag', 1001: 'Diag', 1013: 'Diag', 1022: 'Diag'
}
@@ -92,6 +103,7 @@ def build_matrix():
whos_list = sorted([w for w in all_whos_found if w in who_names])
GATEWAY_DISPLAY_NAMES = {
+ "H4684": "H4684 / H/L4684",
"H4890": "H4890 / AM4890",
}
diff --git a/tests/fixtures/traces/issue_466/README.md b/tests/fixtures/traces/issue_466/README.md
index 07af5bb45..d2149aea4 100644
--- a/tests/fixtures/traces/issue_466/README.md
+++ b/tests/fixtures/traces/issue_466/README.md
@@ -667,5 +667,46 @@ Verbatim bus trace contributed by **@TheDarkWizard** on [#466 (comment 608572493
4. **Lighting (WHO 1)**:
- 9 frames reporting actuator lighting status (`*1*0*0014##`, `*1*0*0016##`, etc.).
+---
+
+# #466 BTicino H/L4684 Colour Touchscreen Gateway Trace (FW 6.0.11 — WHO 1, 2, 3, 4, 13, 16, 18, 22)
+
+Verbatim Home Assistant diagnostics download contributed by **@andrea-mag** on [#466 (comment 6398442412)](https://github.com/OpenWebNet-HA/MyHOME/issues/466#issuecomment-6398442412) (`HA 2026.10.2`, `MyHOME 2.0.0b16`, `OWNd 2.0.0b11`).
+
+## Hardware Profile
+
+- **Gateway Model**: BTicino H/L4684 (Colour Touch Screen IP Gateway, Axolute / LivingLight; UPnP `modelName`: `H/L4684`, `deviceType`: `urn:schemas-bticino-it:device:touchscreen:1`)
+- **Firmware**: `6.0.11` (`*#13**16*6*0*11##`)
+- **Kernel**: `2.6.2` (`*#13**23*2*6*2##`)
+- **Distribution**: `6.0.0` (`*#13**24*6*0*0##`)
+- **WHO 13 Device Type Code**: `23` (`*#13**15*23##`)
+- **Connection**: TCP OpenWebNet (Port 20000)
+
+## Contributed Files
+
+| File | Type | Description |
+|---|---|---|
+| `config_entry-myhome_HL4684_fw6_0_11.json` | HA Diagnostic Download (257 frames: 212 rx / 45 tx) | First physical on-wire capture from a BTicino H/L4684 touchscreen gateway, spanning `myhome.sweep_bus` across lighting & covers (`WHO 1` / `WHO 2`), autonomous load-control priority broadcasts (`WHO 3`), 8-zone thermoregulation (`WHO 4`), gateway identification (`WHO 13`), dual-dialect `WHO 16` + `WHO 22` sound system inventory, and `WHO 18` energy sweep queries. |
+
+## Sequence of Actions Recorded & Subsystems Verified
+
+1. **Gateway Identity & Software Stack (WHO 13 — 5 tx / 5 rx)**:
+ - Clock `*#13**0##` -> `*#13**0*14*41*00*##`.
+ - Device type `*#13**15##` -> `*#13**15*23##` (empirically confirming Nmap's code `23` on physical `H/L4684` FW `6.0.11`, distinct from 2006 spec code `13` on FW `2.0.54`).
+ - Firmware `*#13**16##` -> `*#13**16*6*0*11##` (`6.0.11`), kernel `*#13**23##` -> `*#13**23*2*6*2##` (`2.6.2`), distribution `*#13**24##` -> `*#13**24*6*0*0##` (`6.0.0`).
+
+2. **Load Control Priority Cycle (WHO 3 — 29 rx)**:
+ - Autonomous periodic load-priority status broadcasts (`*3*1*#1##` through `*3*1*#8##`, *Load priority 1–8 enabled*), cycling one priority every `~25.8 s` (`~3 min 26 s` per full `#1 -> #8` round-robin loop).
+
+3. **Thermoregulation & Actuator Telemetry (WHO 4 — 1 tx / 64 rx)**:
+ - General status query `[tx] *#4*0##` returns `0` `WHO 4` frames on the event session before the next sweep query `1.33 s` later (`*#16*0*5##`), corroborating the `020054` firmware-oracle `silent` verdict on live `6.0.11` hardware.
+ - Autonomous bus traffic covers 8 zones (`11, 22, 33, 44, 55, 66, 77, 88`; `Zone 33` configured with `fan: true`, the other 7 with `fan: false`) plus central pump/boiler actuators `0#1`, `0#3`, `0#4` (`*#4*0#*20*0##`, `26` frames):
+ - Ambient temperatures (`*#4*Z*0*0TTT##`), generic effective setpoints (`*#4*Z*14*0TTT*3##`), and Zone `66` mode/setpoint frames (`*4*1*66##`, `*4*303*66##`, `*#4*66*12*0170*3##`).
+ - Zone actuator channels (`11#1`, `22#1`, `33#1`, `33#2`, `44#1`, `55#1`, `66#2`, `66#3`, `77#1`, `77#2`, `88#1`) emit `*#4*#*20*5##` (`18` frames).
+4. **Dual-Dialect Sound System Inventory (WHO 16 & WHO 22 — 1 tx / 28 rx)**:
+ - Single `[tx] *#16*0*5##` triggers `14` paired `WHO 16` (`bt_difson`) + `WHO 22` (`bt_multi`) replies (`4–14 ms` apart) on `H/L4684` FW `6.0.11`, covering `2` active stereo sources (`*16*3*101##` / `*#22*2#1*12*1*4##`, `*16*3*102##` / `*#22*2#2*12*1*4##`) and `12` amplifier zones (`11, 12, 13, 21, 22, 31, 32, 33, 41, 42, 51, 61` via `*16*13*##` / `*#22*3##*12*0*10##`; on zone `61`, `*#22*3#6#1*12*0*10##` arrives `13.6 ms` before `*16*13*61##`).
+5. **Lighting, Automation & Energy Sweep Queries (WHO 1, WHO 2 & WHO 18 — 38 tx / 86 rx)**:
+ - `[tx] *#1*0##` and `[tx] *#2*0##` each trigger the same `43`-frame A/PL status burst (`42` `WHO 1` light/relay status frames + `1` `WHO 2` shutter status frame `*2*0*99##`, `86` `rx` frames total).
+ - `36` `WHO 18` `tx` queries (`*#18*51..59*51##` / `*1200##` and `*#18*71#0..79#0*51##` / `*1200##`): Dimension `51` queries wait `~943 ms` (SCS bus timeout with no meter answering), whereas unsupported Dimension `1200` queries return immediately (`~57 ms` including `50 ms` queue pacing).
diff --git a/tests/fixtures/traces/issue_466/config_entry-myhome_HL4684_fw6_0_11.json b/tests/fixtures/traces/issue_466/config_entry-myhome_HL4684_fw6_0_11.json
new file mode 100644
index 000000000..520e0b5b2
--- /dev/null
+++ b/tests/fixtures/traces/issue_466/config_entry-myhome_HL4684_fw6_0_11.json
@@ -0,0 +1,5470 @@
+{
+ "home_assistant": {
+ "arch": "aarch64",
+ "dev": false,
+ "docker": true,
+ "hassio": true,
+ "installation_type": "Home Assistant OS",
+ "os_name": "Linux",
+ "os_version": "6.18.39-haos-raspi",
+ "python_version": "3.14.6",
+ "timezone": "Europe/Rome",
+ "version": "2026.10.2",
+ "virtualenv": false,
+ "container_arch": "aarch64",
+ "supervisor": "2026.10.3",
+ "host_os": "Home Assistant OS 18.3",
+ "docker_version": "29.7.2",
+ "chassis": "embedded",
+ "run_as_root": true
+ },
+ "custom_components": {
+ "myhome": {
+ "documentation": "https://openwebnet-ha.github.io/MyHOME/beta/",
+ "version": "2.0.0b16",
+ "requirements": [
+ "OWNd==2.0.0b11"
+ ]
+ },
+ "hon": {
+ "documentation": "https://github.com/gvigroux/hon",
+ "version": "0.8.4",
+ "requirements": []
+ },
+ "hacs": {
+ "documentation": "https://hacs.xyz/docs/use/",
+ "version": "2.0.5",
+ "requirements": [
+ "aiogithubapi>=22.10.1"
+ ]
+ },
+ "tigo": {
+ "documentation": "https://github.com/Bobsilvio/tigosolar-local/",
+ "version": "4.0.1",
+ "requirements": [
+ "websocket-client>=1.6.0"
+ ]
+ },
+ "sonoff": {
+ "documentation": "https://github.com/AlexxIT/SonoffLAN",
+ "version": "3.13.2",
+ "requirements": []
+ }
+ },
+ "integration_manifest": {
+ "domain": "myhome",
+ "name": "MyHOME",
+ "after_dependencies": [
+ "frontend",
+ "http",
+ "lovelace"
+ ],
+ "codeowners": [
+ "anotherjulien",
+ "GreenGrassBlueOcean"
+ ],
+ "config_flow": true,
+ "documentation": "https://openwebnet-ha.github.io/MyHOME/beta/",
+ "integration_type": "hub",
+ "iot_class": "local_push",
+ "issue_tracker": "https://github.com/OpenWebNet-HA/MyHOME/issues",
+ "requirements": [
+ "OWNd==2.0.0b11"
+ ],
+ "ssdp": [
+ {
+ "st": "upnp:rootdevice",
+ "manufacturer": "BTicino S.p.A.",
+ "modelName": "HL4684"
+ },
+ {
+ "st": "upnp:rootdevice",
+ "manufacturer": "BTicino S.p.A.",
+ "modelName": "AM4890"
+ },
+ {
+ "st": "upnp:rootdevice",
+ "manufacturer": "BTicino S.p.A.",
+ "modelName": "H4890"
+ },
+ {
+ "st": "upnp:rootdevice",
+ "manufacturer": "BTicino S.p.A.",
+ "modelName": "LN4890"
+ },
+ {
+ "st": "upnp:rootdevice",
+ "manufacturer": "BTicino S.p.A.",
+ "modelName": "LN4890A"
+ },
+ {
+ "st": "upnp:rootdevice",
+ "manufacturer": "BTicino S.p.A.",
+ "modelName": "MyHomeServer1"
+ },
+ {
+ "st": "upnp:rootdevice",
+ "manufacturer": "BTicino S.p.A.",
+ "modelName": "F455"
+ },
+ {
+ "st": "upnp:rootdevice",
+ "manufacturer": "BTicino S.p.A.",
+ "modelName": "F454"
+ },
+ {
+ "st": "upnp:rootdevice",
+ "manufacturer": "BTicino S.p.A.",
+ "modelName": "F453AV"
+ },
+ {
+ "st": "upnp:rootdevice",
+ "manufacturer": "BTicino S.p.A.",
+ "modelName": "F452"
+ },
+ {
+ "st": "upnp:rootdevice",
+ "manufacturer": "BTicino S.p.A.",
+ "modelName": "MH200N"
+ },
+ {
+ "st": "upnp:rootdevice",
+ "manufacturer": "BTicino S.p.A.",
+ "modelName": "MH200"
+ },
+ {
+ "st": "upnp:rootdevice",
+ "manufacturer": "BTicino S.p.A.",
+ "modelName": "MH202"
+ },
+ {
+ "st": "upnp:rootdevice",
+ "manufacturer": "BTicino S.p.A.",
+ "modelName": "MH201"
+ }
+ ],
+ "version": "2.0.0b16",
+ "is_built_in": false,
+ "overwrites_built_in": false
+ },
+ "setup_times": {},
+ "data": {
+ "integration_version": "2.0.0b16",
+ "ownd_version": "2.0.0b11",
+ "config_entry": {
+ "entry_id": "**REDACTED**",
+ "version": 1,
+ "domain": "myhome",
+ "title": "H/L4684 Gateway",
+ "data": {
+ "UDN": "**REDACTED**",
+ "deviceType": "urn:schemas-bticino-it:device:touchscreen:1",
+ "firmware": "6.0.11",
+ "friendly_name": "**REDACTED**",
+ "host": "**REDACTED**",
+ "id": "**REDACTED**",
+ "mac": "**REDACTED**",
+ "manufacturer": "BTicino S.p.A.",
+ "manufacturerURL": "http://www.bticino.it",
+ "name": "H/L4684",
+ "password": "**REDACTED**",
+ "port": 20000,
+ "ssdp_location": "**REDACTED**",
+ "ssdp_st": "upnp:rootdevice"
+ },
+ "options": {
+ "command_worker_count": 1,
+ "config_file_path": "**REDACTED**",
+ "devices": {
+ "media_player": {
+ "16-101": {
+ "name": "Sound Zone 101",
+ "where": "101",
+ "who": "16"
+ },
+ "16-102": {
+ "name": "Sound Zone 102",
+ "where": "102",
+ "who": "16"
+ },
+ "16-103": {
+ "name": "Sound Zone 103",
+ "where": "103",
+ "who": "16"
+ },
+ "16-121": {
+ "name": "Sound Zone 121",
+ "where": "121",
+ "who": "16"
+ },
+ "16-21": {
+ "name": "Sound Zone 21",
+ "where": "21",
+ "who": "16"
+ },
+ "22-3#2#1": {
+ "name": "Sound Zone 3#2#1",
+ "where": "3#2#1",
+ "who": "22"
+ }
+ }
+ },
+ "enable_auto_learning": false,
+ "generate_events": false,
+ "migrated_yaml_platforms": {
+ "light": {
+ "1-27": {
+ "where": "27",
+ "name": "Scale PS1-PTE",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-28": {
+ "where": "28",
+ "name": "Scale PTE-P1",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-29": {
+ "where": "29",
+ "name": "EXT - Matrimoniale P1",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-31": {
+ "where": "31",
+ "name": "EXT - Cucina Muro",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-32": {
+ "where": "32",
+ "name": "EXT - Patio Ingresso",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-33": {
+ "where": "33",
+ "name": "EXT - Balcone Ingresso",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-34": {
+ "where": "34",
+ "name": "PT - Bagno",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-35": {
+ "where": "35",
+ "name": "PT - Bagno Specchio",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-36": {
+ "where": "36",
+ "name": "PT - Sala",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-38": {
+ "where": "38",
+ "name": "P1 - Corridoio",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-39": {
+ "where": "39",
+ "name": "P1 - Matrimoniale",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-42": {
+ "where": "42",
+ "name": "P1 - Bagno Specchio",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-43": {
+ "where": "43",
+ "name": "P1 - Bagno",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-44": {
+ "where": "44",
+ "name": "P1 - Cameretta",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-45": {
+ "where": "45",
+ "name": "P1 - Matrimoniale Armadio",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-78": {
+ "where": "78",
+ "name": "PT - Cucina",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-79": {
+ "where": "79",
+ "name": "PT - Cucina Faretti",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-91": {
+ "where": "91",
+ "name": "EXT - Cucina Muretto",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-92": {
+ "where": "92",
+ "name": "EXT - Giardino Piscina",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-93": {
+ "where": "93",
+ "name": "EXT - Giardino Ingresso",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-94": {
+ "where": "94",
+ "name": "EXT - Giardino Ingresso",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-98": {
+ "where": "98",
+ "name": "EXT - Circolatore ACS",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-11": {
+ "where": "11",
+ "name": "PS1 - Dimmer Biliardo",
+ "dimmable": true,
+ "manufacturer": "BTicino",
+ "model": "F412",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-14": {
+ "where": "14",
+ "name": "PS1 - Cucina",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-15": {
+ "where": "15",
+ "name": "PS1 - Ripostiglio",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-16": {
+ "where": "16",
+ "name": "PS1 - Biliardo",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-17": {
+ "where": "17",
+ "name": "PS1 - Camino",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-18": {
+ "where": "18",
+ "name": "PS1 - Libreria",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-81": {
+ "where": "81",
+ "name": "PS1 - Disimpegno",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-21": {
+ "where": "21",
+ "name": "PS1 - Cameretta",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-22": {
+ "where": "22",
+ "name": "PS1 - Bagno Specchio",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-23": {
+ "where": "23",
+ "name": "PS1 - Bagno",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-24": {
+ "where": "24",
+ "name": "PS1 - Matrimoniale Applique",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-26": {
+ "where": "26",
+ "name": "PS1 - Matrimoniale Faretti",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ }
+ },
+ "switch": {
+ "1-37": {
+ "where": "37",
+ "name": "PTE - Presa Comandata",
+ "class": "outlet",
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null
+ },
+ "1-19": {
+ "where": "19",
+ "name": "PS1 - Presa Camino",
+ "class": "outlet",
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null
+ },
+ "1-13": {
+ "where": "13",
+ "name": "PS1 - Presa Biliardo",
+ "class": "outlet",
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null
+ },
+ "1-12": {
+ "where": "12",
+ "name": "PS1 - Presa Finestra Biliardo",
+ "class": "outlet",
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null
+ }
+ },
+ "climate": {
+ "4-#0": {
+ "zone": "#0",
+ "name": "Central unit",
+ "heat": true,
+ "cool": false,
+ "standalone": false,
+ "manufacturer": "BTicino",
+ "model": "3550",
+ "who": "4",
+ "fan": false,
+ "humidity": false,
+ "central": false,
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "central_unit": {
+ "zone": "#0",
+ "name": "Central unit",
+ "heat": true,
+ "cool": false,
+ "standalone": false,
+ "manufacturer": "BTicino",
+ "model": "3550",
+ "who": "4",
+ "fan": false,
+ "humidity": false,
+ "central": false,
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "0": {
+ "zone": "#0",
+ "name": "Central unit",
+ "heat": true,
+ "cool": false,
+ "standalone": false,
+ "manufacturer": "BTicino",
+ "model": "3550",
+ "who": "4",
+ "fan": false,
+ "humidity": false,
+ "central": false,
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "#0": {
+ "zone": "#0",
+ "name": "Central unit",
+ "heat": true,
+ "cool": false,
+ "standalone": false,
+ "manufacturer": "BTicino",
+ "model": "3550",
+ "who": "4",
+ "fan": false,
+ "humidity": false,
+ "central": false,
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "zone_0": {
+ "zone": "#0",
+ "name": "Central unit",
+ "heat": true,
+ "cool": false,
+ "standalone": false,
+ "manufacturer": "BTicino",
+ "model": "3550",
+ "who": "4",
+ "fan": false,
+ "humidity": false,
+ "central": false,
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "4-11": {
+ "zone": "11",
+ "name": "P1 - Matrimoniale",
+ "heat": true,
+ "cool": false,
+ "standalone": false,
+ "manufacturer": "BTicino",
+ "model": "F430/4",
+ "who": "4",
+ "fan": false,
+ "humidity": false,
+ "central": false,
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "zone_11_matrimoniale": {
+ "zone": "11",
+ "name": "P1 - Matrimoniale",
+ "heat": true,
+ "cool": false,
+ "standalone": false,
+ "manufacturer": "BTicino",
+ "model": "F430/4",
+ "who": "4",
+ "fan": false,
+ "humidity": false,
+ "central": false,
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "11": {
+ "zone": "11",
+ "name": "P1 - Matrimoniale",
+ "heat": true,
+ "cool": false,
+ "standalone": false,
+ "manufacturer": "BTicino",
+ "model": "F430/4",
+ "who": "4",
+ "fan": false,
+ "humidity": false,
+ "central": false,
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "zone_11": {
+ "zone": "11",
+ "name": "P1 - Matrimoniale",
+ "heat": true,
+ "cool": false,
+ "standalone": false,
+ "manufacturer": "BTicino",
+ "model": "F430/4",
+ "who": "4",
+ "fan": false,
+ "humidity": false,
+ "central": false,
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "4-22": {
+ "zone": "22",
+ "name": "P1 - Cameretta",
+ "heat": true,
+ "cool": false,
+ "standalone": false,
+ "manufacturer": "BTicino",
+ "model": "F430/4",
+ "who": "4",
+ "fan": false,
+ "humidity": false,
+ "central": false,
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "zone_22_cameretta": {
+ "zone": "22",
+ "name": "P1 - Cameretta",
+ "heat": true,
+ "cool": false,
+ "standalone": false,
+ "manufacturer": "BTicino",
+ "model": "F430/4",
+ "who": "4",
+ "fan": false,
+ "humidity": false,
+ "central": false,
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "22": {
+ "zone": "22",
+ "name": "P1 - Cameretta",
+ "heat": true,
+ "cool": false,
+ "standalone": false,
+ "manufacturer": "BTicino",
+ "model": "F430/4",
+ "who": "4",
+ "fan": false,
+ "humidity": false,
+ "central": false,
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "zone_22": {
+ "zone": "22",
+ "name": "P1 - Cameretta",
+ "heat": true,
+ "cool": false,
+ "standalone": false,
+ "manufacturer": "BTicino",
+ "model": "F430/4",
+ "who": "4",
+ "fan": false,
+ "humidity": false,
+ "central": false,
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "4-33": {
+ "zone": "33",
+ "name": "PTE - Soggiorno",
+ "heat": true,
+ "cool": false,
+ "standalone": false,
+ "manufacturer": "BTicino",
+ "model": "F430/4",
+ "fan": true,
+ "who": "4",
+ "humidity": false,
+ "central": false,
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "zone_33_soggiorno": {
+ "zone": "33",
+ "name": "PTE - Soggiorno",
+ "heat": true,
+ "cool": false,
+ "standalone": false,
+ "manufacturer": "BTicino",
+ "model": "F430/4",
+ "fan": true,
+ "who": "4",
+ "humidity": false,
+ "central": false,
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "33": {
+ "zone": "33",
+ "name": "PTE - Soggiorno",
+ "heat": true,
+ "cool": false,
+ "standalone": false,
+ "manufacturer": "BTicino",
+ "model": "F430/4",
+ "fan": true,
+ "who": "4",
+ "humidity": false,
+ "central": false,
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "zone_33": {
+ "zone": "33",
+ "name": "PTE - Soggiorno",
+ "heat": true,
+ "cool": false,
+ "standalone": false,
+ "manufacturer": "BTicino",
+ "model": "F430/4",
+ "fan": true,
+ "who": "4",
+ "humidity": false,
+ "central": false,
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "4-44": {
+ "zone": "44",
+ "name": "PS1 - Matrimoniale",
+ "heat": true,
+ "cool": false,
+ "standalone": false,
+ "manufacturer": "BTicino",
+ "model": "F430/4",
+ "who": "4",
+ "fan": false,
+ "humidity": false,
+ "central": false,
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "zone_44_ps1_matrimoniale": {
+ "zone": "44",
+ "name": "PS1 - Matrimoniale",
+ "heat": true,
+ "cool": false,
+ "standalone": false,
+ "manufacturer": "BTicino",
+ "model": "F430/4",
+ "who": "4",
+ "fan": false,
+ "humidity": false,
+ "central": false,
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "44": {
+ "zone": "44",
+ "name": "PS1 - Matrimoniale",
+ "heat": true,
+ "cool": false,
+ "standalone": false,
+ "manufacturer": "BTicino",
+ "model": "F430/4",
+ "who": "4",
+ "fan": false,
+ "humidity": false,
+ "central": false,
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "zone_44": {
+ "zone": "44",
+ "name": "PS1 - Matrimoniale",
+ "heat": true,
+ "cool": false,
+ "standalone": false,
+ "manufacturer": "BTicino",
+ "model": "F430/4",
+ "who": "4",
+ "fan": false,
+ "humidity": false,
+ "central": false,
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "4-55": {
+ "zone": "55",
+ "name": "PS1 - Cameretta",
+ "heat": true,
+ "cool": false,
+ "standalone": false,
+ "manufacturer": "BTicino",
+ "model": "F430/4",
+ "who": "4",
+ "fan": false,
+ "humidity": false,
+ "central": false,
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "zone_55_ps1_cameretta": {
+ "zone": "55",
+ "name": "PS1 - Cameretta",
+ "heat": true,
+ "cool": false,
+ "standalone": false,
+ "manufacturer": "BTicino",
+ "model": "F430/4",
+ "who": "4",
+ "fan": false,
+ "humidity": false,
+ "central": false,
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "55": {
+ "zone": "55",
+ "name": "PS1 - Cameretta",
+ "heat": true,
+ "cool": false,
+ "standalone": false,
+ "manufacturer": "BTicino",
+ "model": "F430/4",
+ "who": "4",
+ "fan": false,
+ "humidity": false,
+ "central": false,
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "zone_55": {
+ "zone": "55",
+ "name": "PS1 - Cameretta",
+ "heat": true,
+ "cool": false,
+ "standalone": false,
+ "manufacturer": "BTicino",
+ "model": "F430/4",
+ "who": "4",
+ "fan": false,
+ "humidity": false,
+ "central": false,
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "4-66": {
+ "zone": "66",
+ "name": "PS1 - Camino",
+ "heat": true,
+ "cool": false,
+ "standalone": false,
+ "manufacturer": "BTicino",
+ "model": "F430/4",
+ "who": "4",
+ "fan": false,
+ "humidity": false,
+ "central": false,
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "zone_66_ps1_camino": {
+ "zone": "66",
+ "name": "PS1 - Camino",
+ "heat": true,
+ "cool": false,
+ "standalone": false,
+ "manufacturer": "BTicino",
+ "model": "F430/4",
+ "who": "4",
+ "fan": false,
+ "humidity": false,
+ "central": false,
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "66": {
+ "zone": "66",
+ "name": "PS1 - Camino",
+ "heat": true,
+ "cool": false,
+ "standalone": false,
+ "manufacturer": "BTicino",
+ "model": "F430/4",
+ "who": "4",
+ "fan": false,
+ "humidity": false,
+ "central": false,
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "zone_66": {
+ "zone": "66",
+ "name": "PS1 - Camino",
+ "heat": true,
+ "cool": false,
+ "standalone": false,
+ "manufacturer": "BTicino",
+ "model": "F430/4",
+ "who": "4",
+ "fan": false,
+ "humidity": false,
+ "central": false,
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "4-77": {
+ "zone": "77",
+ "name": "PS1 - Biliardo",
+ "heat": true,
+ "cool": false,
+ "standalone": false,
+ "manufacturer": "BTicino",
+ "model": "F430/4",
+ "who": "4",
+ "fan": false,
+ "humidity": false,
+ "central": false,
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "zone_77_ps1_biliardo": {
+ "zone": "77",
+ "name": "PS1 - Biliardo",
+ "heat": true,
+ "cool": false,
+ "standalone": false,
+ "manufacturer": "BTicino",
+ "model": "F430/4",
+ "who": "4",
+ "fan": false,
+ "humidity": false,
+ "central": false,
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "77": {
+ "zone": "77",
+ "name": "PS1 - Biliardo",
+ "heat": true,
+ "cool": false,
+ "standalone": false,
+ "manufacturer": "BTicino",
+ "model": "F430/4",
+ "who": "4",
+ "fan": false,
+ "humidity": false,
+ "central": false,
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "zone_77": {
+ "zone": "77",
+ "name": "PS1 - Biliardo",
+ "heat": true,
+ "cool": false,
+ "standalone": false,
+ "manufacturer": "BTicino",
+ "model": "F430/4",
+ "who": "4",
+ "fan": false,
+ "humidity": false,
+ "central": false,
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "4-88": {
+ "zone": "88",
+ "name": "PS1 - Cucina",
+ "heat": true,
+ "cool": false,
+ "standalone": false,
+ "manufacturer": "BTicino",
+ "model": "F430/4",
+ "who": "4",
+ "fan": false,
+ "humidity": false,
+ "central": false,
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "zone_88_ps1_cucina": {
+ "zone": "88",
+ "name": "PS1 - Cucina",
+ "heat": true,
+ "cool": false,
+ "standalone": false,
+ "manufacturer": "BTicino",
+ "model": "F430/4",
+ "who": "4",
+ "fan": false,
+ "humidity": false,
+ "central": false,
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "88": {
+ "zone": "88",
+ "name": "PS1 - Cucina",
+ "heat": true,
+ "cool": false,
+ "standalone": false,
+ "manufacturer": "BTicino",
+ "model": "F430/4",
+ "who": "4",
+ "fan": false,
+ "humidity": false,
+ "central": false,
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "zone_88": {
+ "zone": "88",
+ "name": "PS1 - Cucina",
+ "heat": true,
+ "cool": false,
+ "standalone": false,
+ "manufacturer": "BTicino",
+ "model": "F430/4",
+ "who": "4",
+ "fan": false,
+ "humidity": false,
+ "central": false,
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ }
+ },
+ "button": {
+ "1-27": {
+ "where": "27",
+ "name": "Scale PS1-PTE",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-28": {
+ "where": "28",
+ "name": "Scale PTE-P1",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-29": {
+ "where": "29",
+ "name": "EXT - Matrimoniale P1",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-31": {
+ "where": "31",
+ "name": "EXT - Cucina Muro",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-32": {
+ "where": "32",
+ "name": "EXT - Patio Ingresso",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-33": {
+ "where": "33",
+ "name": "EXT - Balcone Ingresso",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-34": {
+ "where": "34",
+ "name": "PT - Bagno",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-35": {
+ "where": "35",
+ "name": "PT - Bagno Specchio",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-36": {
+ "where": "36",
+ "name": "PT - Sala",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-38": {
+ "where": "38",
+ "name": "P1 - Corridoio",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-39": {
+ "where": "39",
+ "name": "P1 - Matrimoniale",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-42": {
+ "where": "42",
+ "name": "P1 - Bagno Specchio",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-43": {
+ "where": "43",
+ "name": "P1 - Bagno",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-44": {
+ "where": "44",
+ "name": "P1 - Cameretta",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-45": {
+ "where": "45",
+ "name": "P1 - Matrimoniale Armadio",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-78": {
+ "where": "78",
+ "name": "PT - Cucina",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-79": {
+ "where": "79",
+ "name": "PT - Cucina Faretti",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-91": {
+ "where": "91",
+ "name": "EXT - Cucina Muretto",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-92": {
+ "where": "92",
+ "name": "EXT - Giardino Piscina",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-93": {
+ "where": "93",
+ "name": "EXT - Giardino Ingresso",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-94": {
+ "where": "94",
+ "name": "EXT - Giardino Ingresso",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-98": {
+ "where": "98",
+ "name": "EXT - Circolatore ACS",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-11": {
+ "where": "11",
+ "name": "PS1 - Dimmer Biliardo",
+ "dimmable": true,
+ "manufacturer": "BTicino",
+ "model": "F412",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-14": {
+ "where": "14",
+ "name": "PS1 - Cucina",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-15": {
+ "where": "15",
+ "name": "PS1 - Ripostiglio",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-16": {
+ "where": "16",
+ "name": "PS1 - Biliardo",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-17": {
+ "where": "17",
+ "name": "PS1 - Camino",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-18": {
+ "where": "18",
+ "name": "PS1 - Libreria",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-81": {
+ "where": "81",
+ "name": "PS1 - Disimpegno",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-21": {
+ "where": "21",
+ "name": "PS1 - Cameretta",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-22": {
+ "where": "22",
+ "name": "PS1 - Bagno Specchio",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-23": {
+ "where": "23",
+ "name": "PS1 - Bagno",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-24": {
+ "where": "24",
+ "name": "PS1 - Matrimoniale Applique",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-26": {
+ "where": "26",
+ "name": "PS1 - Matrimoniale Faretti",
+ "dimmable": false,
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null,
+ "class": "switch"
+ },
+ "1-37": {
+ "where": "37",
+ "name": "PTE - Presa Comandata",
+ "class": "outlet",
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null
+ },
+ "1-19": {
+ "where": "19",
+ "name": "PS1 - Presa Camino",
+ "class": "outlet",
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null
+ },
+ "1-13": {
+ "where": "13",
+ "name": "PS1 - Presa Biliardo",
+ "class": "outlet",
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null
+ },
+ "1-12": {
+ "where": "12",
+ "name": "PS1 - Presa Finestra Biliardo",
+ "class": "outlet",
+ "manufacturer": "BTicino",
+ "model": "F411U2",
+ "who": "1",
+ "entities": {},
+ "icon": null,
+ "icon_on": null,
+ "entity_name": null
+ }
+ }
+ },
+ "broadcast_resync": true,
+ "transition_mode": "software_stepped",
+ "auto_join_streaming": true,
+ "ignored_addresses": [],
+ "source_defaults": {},
+ "source_1_name": "Radio",
+ "source_1_tuner": true,
+ "source_2_name": "AUX",
+ "source_2_tuner": false,
+ "source_3_name": "TouchScreen",
+ "source_3_tuner": false,
+ "source_4_name": "",
+ "source_4_tuner": false,
+ "decoder_1_entity": "",
+ "decoder_1_source": 1,
+ "decoder_1_pre_gain": 0,
+ "decoder_2_entity": "",
+ "decoder_2_source": 2,
+ "decoder_2_pre_gain": 0,
+ "decoder_3_entity": "",
+ "decoder_3_source": 3,
+ "decoder_3_pre_gain": 0,
+ "decoder_4_entity": "",
+ "decoder_4_source": 4,
+ "decoder_4_pre_gain": 0,
+ "bus_topology": "standalone",
+ "gateway_role": "primary"
+ }
+ },
+ "gateway": {
+ "model_name": "H/L4684",
+ "manufacturer": "BTicino S.p.A.",
+ "firmware": "6.0.11",
+ "is_connected": true,
+ "send_workers": 1,
+ "bus_topology": "standalone",
+ "gateway_role": "primary",
+ "is_follower": false,
+ "is_standby": false,
+ "failover_active": false,
+ "primary_gateway": null,
+ "delegated_whos": [],
+ "identification": {
+ "model": "H/L4684",
+ "source": "ssdp",
+ "configured_model": "H/L4684",
+ "ssdp_model": "H/L4684",
+ "ssdp_location": "**REDACTED**",
+ "who13_code": "23",
+ "who13_model": "H4684",
+ "who13_model_official": null,
+ "who13_model_observed": "H4684",
+ "who13_firmware": "6.0.11",
+ "who13_kernel": "2.6.2",
+ "who13_distribution": "6.0.0",
+ "who1013_code": null,
+ "who1013_model": null,
+ "who1013_other_names": [],
+ "who1013_n_conf": null,
+ "who1013_brand": null,
+ "who1013_line": null,
+ "profile": "GenericGatewayProfile",
+ "conflict": null
+ },
+ "device_faults": []
+ },
+ "profile": {
+ "name": "Generic",
+ "command_queue_delay": 0.05,
+ "max_queue_size": 200,
+ "keepalive_interval": 90.0
+ },
+ "queue": {
+ "queue_depth": 0,
+ "max_size": 200
+ },
+ "platforms": {
+ "light": 68,
+ "switch": 8,
+ "cover": 0,
+ "climate": 37,
+ "binary_sensor": 0,
+ "sensor": 0,
+ "media_player": 0,
+ "button": 76,
+ "alarm_control_panel": 0,
+ "lock": 0
+ },
+ "expose": [],
+ "audio": {
+ "zones": {
+ "zone_11": {
+ "state": "off",
+ "source": 1,
+ "volume_level": 0.03225806451612903,
+ "is_volume_muted": false,
+ "has_decoder": false,
+ "parked": false,
+ "wake_pending": false,
+ "status_seen": true
+ },
+ "zone_12": {
+ "state": "off",
+ "source": 1,
+ "volume_level": 0.03225806451612903,
+ "is_volume_muted": false,
+ "has_decoder": false,
+ "parked": false,
+ "wake_pending": false,
+ "status_seen": true
+ },
+ "zone_13": {
+ "state": "off",
+ "source": 1,
+ "volume_level": 0.6774193548387096,
+ "is_volume_muted": false,
+ "has_decoder": false,
+ "parked": false,
+ "wake_pending": false,
+ "status_seen": true
+ },
+ "zone_21": {
+ "state": "off",
+ "source": 2,
+ "volume_level": 0.3225806451612903,
+ "is_volume_muted": false,
+ "has_decoder": false,
+ "parked": false,
+ "wake_pending": false,
+ "status_seen": true
+ },
+ "zone_22": {
+ "state": "off",
+ "source": 2,
+ "volume_level": 0.03225806451612903,
+ "is_volume_muted": false,
+ "has_decoder": false,
+ "parked": false,
+ "wake_pending": false,
+ "status_seen": true
+ },
+ "zone_31": {
+ "state": "off",
+ "source": null,
+ "volume_level": 0.03225806451612903,
+ "is_volume_muted": false,
+ "has_decoder": false,
+ "parked": false,
+ "wake_pending": false,
+ "status_seen": true
+ },
+ "zone_32": {
+ "state": "off",
+ "source": null,
+ "volume_level": 0.03225806451612903,
+ "is_volume_muted": false,
+ "has_decoder": false,
+ "parked": false,
+ "wake_pending": false,
+ "status_seen": true
+ },
+ "zone_33": {
+ "state": "off",
+ "source": null,
+ "volume_level": 0.03225806451612903,
+ "is_volume_muted": false,
+ "has_decoder": false,
+ "parked": false,
+ "wake_pending": false,
+ "status_seen": true
+ },
+ "zone_41": {
+ "state": "off",
+ "source": null,
+ "volume_level": 0.03225806451612903,
+ "is_volume_muted": false,
+ "has_decoder": false,
+ "parked": false,
+ "wake_pending": false,
+ "status_seen": true
+ },
+ "zone_42": {
+ "state": "off",
+ "source": null,
+ "volume_level": 0.03225806451612903,
+ "is_volume_muted": false,
+ "has_decoder": false,
+ "parked": false,
+ "wake_pending": false,
+ "status_seen": true
+ },
+ "zone_51": {
+ "state": "off",
+ "source": null,
+ "volume_level": null,
+ "is_volume_muted": false,
+ "has_decoder": false,
+ "parked": false,
+ "wake_pending": false,
+ "status_seen": true
+ },
+ "zone_61": {
+ "state": "off",
+ "source": null,
+ "volume_level": 0.03225806451612903,
+ "is_volume_muted": false,
+ "has_decoder": false,
+ "parked": false,
+ "wake_pending": false,
+ "status_seen": true
+ }
+ },
+ "decoder_pool": null
+ },
+ "bus_monitor": {
+ "stats": {
+ "capacity": 500,
+ "captured": 257,
+ "total_rx": 212,
+ "total_tx": 45,
+ "subscribers": 0
+ },
+ "recent_frames": [
+ {
+ "timestamp": 1792934989.003769,
+ "iso_time": "2026-10-25T13:29:49.003769+00:00",
+ "direction": "rx",
+ "raw": "*3*1*#7##",
+ "who": "3",
+ "where": "#7",
+ "what": "1",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935002.074709,
+ "iso_time": "2026-10-25T13:30:02.074709+00:00",
+ "direction": "rx",
+ "raw": "*#4*11*0*0209##",
+ "who": "4",
+ "where": "11",
+ "what": null,
+ "dimension": "0",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935002.294802,
+ "iso_time": "2026-10-25T13:30:02.294802+00:00",
+ "direction": "rx",
+ "raw": "*#4*11*14*0195*3##",
+ "who": "4",
+ "where": "11",
+ "what": null,
+ "dimension": "14",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935002.584182,
+ "iso_time": "2026-10-25T13:30:02.584182+00:00",
+ "direction": "rx",
+ "raw": "*#4*22*0*0224##",
+ "who": "4",
+ "where": "22",
+ "what": null,
+ "dimension": "0",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935002.786117,
+ "iso_time": "2026-10-25T13:30:02.786117+00:00",
+ "direction": "rx",
+ "raw": "*#4*22*14*0170*3##",
+ "who": "4",
+ "where": "22",
+ "what": null,
+ "dimension": "14",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935003.103726,
+ "iso_time": "2026-10-25T13:30:03.103726+00:00",
+ "direction": "rx",
+ "raw": "*#4*33*0*0214##",
+ "who": "4",
+ "where": "33",
+ "what": null,
+ "dimension": "0",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935003.274266,
+ "iso_time": "2026-10-25T13:30:03.274266+00:00",
+ "direction": "rx",
+ "raw": "*#4*33*14*0160*3##",
+ "who": "4",
+ "where": "33",
+ "what": null,
+ "dimension": "14",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935003.584058,
+ "iso_time": "2026-10-25T13:30:03.584058+00:00",
+ "direction": "rx",
+ "raw": "*#4*0#3*20*0##",
+ "who": "4",
+ "where": "0",
+ "what": null,
+ "dimension": "20",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935003.782921,
+ "iso_time": "2026-10-25T13:30:03.782921+00:00",
+ "direction": "rx",
+ "raw": "*#4*0#4*20*0##",
+ "who": "4",
+ "where": "0",
+ "what": null,
+ "dimension": "20",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935003.854139,
+ "iso_time": "2026-10-25T13:30:03.854139+00:00",
+ "direction": "rx",
+ "raw": "*#4*11#1*20*5##",
+ "who": "4",
+ "where": "11",
+ "what": null,
+ "dimension": "20",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935003.914058,
+ "iso_time": "2026-10-25T13:30:03.914058+00:00",
+ "direction": "rx",
+ "raw": "*#4*44*0*0233##",
+ "who": "4",
+ "where": "44",
+ "what": null,
+ "dimension": "0",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935004.145349,
+ "iso_time": "2026-10-25T13:30:04.145349+00:00",
+ "direction": "rx",
+ "raw": "*#4*0#1*20*0##",
+ "who": "4",
+ "where": "0",
+ "what": null,
+ "dimension": "20",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935004.30411,
+ "iso_time": "2026-10-25T13:30:04.304110+00:00",
+ "direction": "rx",
+ "raw": "*#4*0#3*20*0##",
+ "who": "4",
+ "where": "0",
+ "what": null,
+ "dimension": "20",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935004.444204,
+ "iso_time": "2026-10-25T13:30:04.444204+00:00",
+ "direction": "rx",
+ "raw": "*#4*0#4*20*0##",
+ "who": "4",
+ "where": "0",
+ "what": null,
+ "dimension": "20",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935004.494073,
+ "iso_time": "2026-10-25T13:30:04.494073+00:00",
+ "direction": "rx",
+ "raw": "*#4*33#2*20*5##",
+ "who": "4",
+ "where": "33",
+ "what": null,
+ "dimension": "20",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935004.533891,
+ "iso_time": "2026-10-25T13:30:04.533891+00:00",
+ "direction": "rx",
+ "raw": "*#4*33#1*20*5##",
+ "who": "4",
+ "where": "33",
+ "what": null,
+ "dimension": "20",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935004.624258,
+ "iso_time": "2026-10-25T13:30:04.624258+00:00",
+ "direction": "rx",
+ "raw": "*#4*55*0*0226##",
+ "who": "4",
+ "where": "55",
+ "what": null,
+ "dimension": "0",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935004.704132,
+ "iso_time": "2026-10-25T13:30:04.704132+00:00",
+ "direction": "rx",
+ "raw": "*#4*66*0*0228##",
+ "who": "4",
+ "where": "66",
+ "what": null,
+ "dimension": "0",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935004.743794,
+ "iso_time": "2026-10-25T13:30:04.743794+00:00",
+ "direction": "rx",
+ "raw": "*4*1*66##",
+ "who": "4",
+ "where": "66",
+ "what": "1",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935004.78469,
+ "iso_time": "2026-10-25T13:30:04.784690+00:00",
+ "direction": "rx",
+ "raw": "*#4*66*12*0170*3##",
+ "who": "4",
+ "where": "66",
+ "what": null,
+ "dimension": "12",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935004.854856,
+ "iso_time": "2026-10-25T13:30:04.854856+00:00",
+ "direction": "rx",
+ "raw": "*#4*44*14*0180*3##",
+ "who": "4",
+ "where": "44",
+ "what": null,
+ "dimension": "14",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935005.224287,
+ "iso_time": "2026-10-25T13:30:05.224287+00:00",
+ "direction": "rx",
+ "raw": "*#4*77*0*0224##",
+ "who": "4",
+ "where": "77",
+ "what": null,
+ "dimension": "0",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935005.354506,
+ "iso_time": "2026-10-25T13:30:05.354506+00:00",
+ "direction": "rx",
+ "raw": "*#4*55*14*0180*3##",
+ "who": "4",
+ "where": "55",
+ "what": null,
+ "dimension": "14",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935005.484679,
+ "iso_time": "2026-10-25T13:30:05.484679+00:00",
+ "direction": "rx",
+ "raw": "*#4*66*14*0200*3##",
+ "who": "4",
+ "where": "66",
+ "what": null,
+ "dimension": "14",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935005.584652,
+ "iso_time": "2026-10-25T13:30:05.584652+00:00",
+ "direction": "rx",
+ "raw": "*#4*55*14*0180*3##",
+ "who": "4",
+ "where": "55",
+ "what": null,
+ "dimension": "14",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935005.814161,
+ "iso_time": "2026-10-25T13:30:05.814161+00:00",
+ "direction": "rx",
+ "raw": "*#4*88*0*0221##",
+ "who": "4",
+ "where": "88",
+ "what": null,
+ "dimension": "0",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935005.884495,
+ "iso_time": "2026-10-25T13:30:05.884495+00:00",
+ "direction": "rx",
+ "raw": "*#4*77*14*0180*3##",
+ "who": "4",
+ "where": "77",
+ "what": null,
+ "dimension": "14",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935006.195248,
+ "iso_time": "2026-10-25T13:30:06.195248+00:00",
+ "direction": "rx",
+ "raw": "*#4*88*14*0180*3##",
+ "who": "4",
+ "where": "88",
+ "what": null,
+ "dimension": "14",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935007.135683,
+ "iso_time": "2026-10-25T13:30:07.135683+00:00",
+ "direction": "rx",
+ "raw": "*#4*0#1*20*0##",
+ "who": "4",
+ "where": "0",
+ "what": null,
+ "dimension": "20",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935007.23423,
+ "iso_time": "2026-10-25T13:30:07.234230+00:00",
+ "direction": "rx",
+ "raw": "*#4*0#3*20*0##",
+ "who": "4",
+ "where": "0",
+ "what": null,
+ "dimension": "20",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935007.354015,
+ "iso_time": "2026-10-25T13:30:07.354015+00:00",
+ "direction": "rx",
+ "raw": "*#4*0#4*20*0##",
+ "who": "4",
+ "where": "0",
+ "what": null,
+ "dimension": "20",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935007.444058,
+ "iso_time": "2026-10-25T13:30:07.444058+00:00",
+ "direction": "rx",
+ "raw": "*#4*55#1*20*5##",
+ "who": "4",
+ "where": "55",
+ "what": null,
+ "dimension": "20",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935007.894923,
+ "iso_time": "2026-10-25T13:30:07.894923+00:00",
+ "direction": "rx",
+ "raw": "*#4*0#1*20*0##",
+ "who": "4",
+ "where": "0",
+ "what": null,
+ "dimension": "20",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935008.01387,
+ "iso_time": "2026-10-25T13:30:08.013870+00:00",
+ "direction": "rx",
+ "raw": "*#4*0#3*20*0##",
+ "who": "4",
+ "where": "0",
+ "what": null,
+ "dimension": "20",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935008.144136,
+ "iso_time": "2026-10-25T13:30:08.144136+00:00",
+ "direction": "rx",
+ "raw": "*#4*0#4*20*0##",
+ "who": "4",
+ "where": "0",
+ "what": null,
+ "dimension": "20",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935008.244248,
+ "iso_time": "2026-10-25T13:30:08.244248+00:00",
+ "direction": "rx",
+ "raw": "*#4*44#1*20*5##",
+ "who": "4",
+ "where": "44",
+ "what": null,
+ "dimension": "20",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935009.484523,
+ "iso_time": "2026-10-25T13:30:09.484523+00:00",
+ "direction": "rx",
+ "raw": "*#4*0#1*20*0##",
+ "who": "4",
+ "where": "0",
+ "what": null,
+ "dimension": "20",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935009.614385,
+ "iso_time": "2026-10-25T13:30:09.614385+00:00",
+ "direction": "rx",
+ "raw": "*#4*0#3*20*0##",
+ "who": "4",
+ "where": "0",
+ "what": null,
+ "dimension": "20",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935009.734336,
+ "iso_time": "2026-10-25T13:30:09.734336+00:00",
+ "direction": "rx",
+ "raw": "*#4*0#4*20*0##",
+ "who": "4",
+ "where": "0",
+ "what": null,
+ "dimension": "20",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935009.824162,
+ "iso_time": "2026-10-25T13:30:09.824162+00:00",
+ "direction": "rx",
+ "raw": "*#4*88#1*20*5##",
+ "who": "4",
+ "where": "88",
+ "what": null,
+ "dimension": "20",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935009.924394,
+ "iso_time": "2026-10-25T13:30:09.924394+00:00",
+ "direction": "rx",
+ "raw": "*#4*0#1*20*0##",
+ "who": "4",
+ "where": "0",
+ "what": null,
+ "dimension": "20",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935010.067771,
+ "iso_time": "2026-10-25T13:30:10.067771+00:00",
+ "direction": "rx",
+ "raw": "*#4*0#3*20*0##",
+ "who": "4",
+ "where": "0",
+ "what": null,
+ "dimension": "20",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935010.144441,
+ "iso_time": "2026-10-25T13:30:10.144441+00:00",
+ "direction": "rx",
+ "raw": "*#4*0#4*20*0##",
+ "who": "4",
+ "where": "0",
+ "what": null,
+ "dimension": "20",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935010.304411,
+ "iso_time": "2026-10-25T13:30:10.304411+00:00",
+ "direction": "rx",
+ "raw": "*#4*66#3*20*5##",
+ "who": "4",
+ "where": "66",
+ "what": null,
+ "dimension": "20",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935010.334557,
+ "iso_time": "2026-10-25T13:30:10.334557+00:00",
+ "direction": "rx",
+ "raw": "*#4*66#2*20*5##",
+ "who": "4",
+ "where": "66",
+ "what": null,
+ "dimension": "20",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935010.444315,
+ "iso_time": "2026-10-25T13:30:10.444315+00:00",
+ "direction": "rx",
+ "raw": "*#4*0#1*20*0##",
+ "who": "4",
+ "where": "0",
+ "what": null,
+ "dimension": "20",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935010.564479,
+ "iso_time": "2026-10-25T13:30:10.564479+00:00",
+ "direction": "rx",
+ "raw": "*#4*0#3*20*0##",
+ "who": "4",
+ "where": "0",
+ "what": null,
+ "dimension": "20",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935010.684512,
+ "iso_time": "2026-10-25T13:30:10.684512+00:00",
+ "direction": "rx",
+ "raw": "*#4*0#4*20*0##",
+ "who": "4",
+ "where": "0",
+ "what": null,
+ "dimension": "20",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935010.774631,
+ "iso_time": "2026-10-25T13:30:10.774631+00:00",
+ "direction": "rx",
+ "raw": "*#4*22#1*20*5##",
+ "who": "4",
+ "where": "22",
+ "what": null,
+ "dimension": "20",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935013.58489,
+ "iso_time": "2026-10-25T13:30:13.584890+00:00",
+ "direction": "rx",
+ "raw": "*3*1*#8##",
+ "who": "3",
+ "where": "#8",
+ "what": "1",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935014.555524,
+ "iso_time": "2026-10-25T13:30:14.555524+00:00",
+ "direction": "rx",
+ "raw": "*#4*0#1*20*0##",
+ "who": "4",
+ "where": "0",
+ "what": null,
+ "dimension": "20",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935014.726034,
+ "iso_time": "2026-10-25T13:30:14.726034+00:00",
+ "direction": "rx",
+ "raw": "*#4*0#3*20*0##",
+ "who": "4",
+ "where": "0",
+ "what": null,
+ "dimension": "20",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935014.834526,
+ "iso_time": "2026-10-25T13:30:14.834526+00:00",
+ "direction": "rx",
+ "raw": "*#4*0#4*20*0##",
+ "who": "4",
+ "where": "0",
+ "what": null,
+ "dimension": "20",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935015.054556,
+ "iso_time": "2026-10-25T13:30:15.054556+00:00",
+ "direction": "rx",
+ "raw": "*#4*77#1*20*5##",
+ "who": "4",
+ "where": "77",
+ "what": null,
+ "dimension": "20",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935015.075108,
+ "iso_time": "2026-10-25T13:30:15.075108+00:00",
+ "direction": "rx",
+ "raw": "*#4*77#2*20*5##",
+ "who": "4",
+ "where": "77",
+ "what": null,
+ "dimension": "20",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935020.00496,
+ "iso_time": "2026-10-25T13:30:20.004960+00:00",
+ "direction": "rx",
+ "raw": "*#4*66#3*20*5##",
+ "who": "4",
+ "where": "66",
+ "what": null,
+ "dimension": "20",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935020.048923,
+ "iso_time": "2026-10-25T13:30:20.048923+00:00",
+ "direction": "rx",
+ "raw": "*#4*66#2*20*5##",
+ "who": "4",
+ "where": "66",
+ "what": null,
+ "dimension": "20",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935029.932124,
+ "iso_time": "2026-10-25T13:30:29.932124+00:00",
+ "direction": "rx",
+ "raw": "*4*303*66##",
+ "who": "4",
+ "where": "66",
+ "what": "303",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935030.043664,
+ "iso_time": "2026-10-25T13:30:30.043664+00:00",
+ "direction": "rx",
+ "raw": "*#4*0#1*20*0##",
+ "who": "4",
+ "where": "0",
+ "what": null,
+ "dimension": "20",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935030.145423,
+ "iso_time": "2026-10-25T13:30:30.145423+00:00",
+ "direction": "rx",
+ "raw": "*#4*0#3*20*0##",
+ "who": "4",
+ "where": "0",
+ "what": null,
+ "dimension": "20",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935030.275479,
+ "iso_time": "2026-10-25T13:30:30.275479+00:00",
+ "direction": "rx",
+ "raw": "*#4*0#4*20*0##",
+ "who": "4",
+ "where": "0",
+ "what": null,
+ "dimension": "20",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935030.405247,
+ "iso_time": "2026-10-25T13:30:30.405247+00:00",
+ "direction": "rx",
+ "raw": "*#4*66#3*20*5##",
+ "who": "4",
+ "where": "66",
+ "what": null,
+ "dimension": "20",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935030.435639,
+ "iso_time": "2026-10-25T13:30:30.435639+00:00",
+ "direction": "rx",
+ "raw": "*#4*66#2*20*5##",
+ "who": "4",
+ "where": "66",
+ "what": null,
+ "dimension": "20",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935039.096078,
+ "iso_time": "2026-10-25T13:30:39.096078+00:00",
+ "direction": "rx",
+ "raw": "*3*1*#1##",
+ "who": "3",
+ "where": "#1",
+ "what": "1",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935040.086685,
+ "iso_time": "2026-10-25T13:30:40.086685+00:00",
+ "direction": "rx",
+ "raw": "*#4*66#3*20*5##",
+ "who": "4",
+ "where": "66",
+ "what": null,
+ "dimension": "20",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935040.135733,
+ "iso_time": "2026-10-25T13:30:40.135733+00:00",
+ "direction": "rx",
+ "raw": "*#4*66#2*20*5##",
+ "who": "4",
+ "where": "66",
+ "what": null,
+ "dimension": "20",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935050.022258,
+ "iso_time": "2026-10-25T13:30:50.022258+00:00",
+ "direction": "rx",
+ "raw": "*4*303*66##",
+ "who": "4",
+ "where": "66",
+ "what": "303",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935064.807417,
+ "iso_time": "2026-10-25T13:31:04.807417+00:00",
+ "direction": "rx",
+ "raw": "*3*1*#2##",
+ "who": "3",
+ "where": "#2",
+ "what": "1",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935090.628003,
+ "iso_time": "2026-10-25T13:31:30.628003+00:00",
+ "direction": "rx",
+ "raw": "*3*1*#3##",
+ "who": "3",
+ "where": "#3",
+ "what": "1",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935116.389482,
+ "iso_time": "2026-10-25T13:31:56.389482+00:00",
+ "direction": "rx",
+ "raw": "*3*1*#4##",
+ "who": "3",
+ "where": "#4",
+ "what": "1",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935142.210597,
+ "iso_time": "2026-10-25T13:32:22.210597+00:00",
+ "direction": "rx",
+ "raw": "*3*1*#5##",
+ "who": "3",
+ "where": "#5",
+ "what": "1",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935167.962611,
+ "iso_time": "2026-10-25T13:32:47.962611+00:00",
+ "direction": "rx",
+ "raw": "*3*1*#6##",
+ "who": "3",
+ "where": "#6",
+ "what": "1",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935193.783431,
+ "iso_time": "2026-10-25T13:33:13.783431+00:00",
+ "direction": "rx",
+ "raw": "*3*1*#7##",
+ "who": "3",
+ "where": "#7",
+ "what": "1",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935219.545181,
+ "iso_time": "2026-10-25T13:33:39.545181+00:00",
+ "direction": "rx",
+ "raw": "*3*1*#8##",
+ "who": "3",
+ "where": "#8",
+ "what": "1",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935245.325858,
+ "iso_time": "2026-10-25T13:34:05.325858+00:00",
+ "direction": "rx",
+ "raw": "*3*1*#1##",
+ "who": "3",
+ "where": "#1",
+ "what": "1",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935271.117695,
+ "iso_time": "2026-10-25T13:34:31.117695+00:00",
+ "direction": "rx",
+ "raw": "*3*1*#2##",
+ "who": "3",
+ "where": "#2",
+ "what": "1",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935296.898458,
+ "iso_time": "2026-10-25T13:34:56.898458+00:00",
+ "direction": "rx",
+ "raw": "*3*1*#3##",
+ "who": "3",
+ "where": "#3",
+ "what": "1",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935322.700195,
+ "iso_time": "2026-10-25T13:35:22.700195+00:00",
+ "direction": "rx",
+ "raw": "*3*1*#4##",
+ "who": "3",
+ "where": "#4",
+ "what": "1",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935348.480913,
+ "iso_time": "2026-10-25T13:35:48.480913+00:00",
+ "direction": "rx",
+ "raw": "*3*1*#5##",
+ "who": "3",
+ "where": "#5",
+ "what": "1",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935374.192885,
+ "iso_time": "2026-10-25T13:36:14.192885+00:00",
+ "direction": "rx",
+ "raw": "*3*1*#6##",
+ "who": "3",
+ "where": "#6",
+ "what": "1",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935399.973797,
+ "iso_time": "2026-10-25T13:36:39.973797+00:00",
+ "direction": "rx",
+ "raw": "*3*1*#7##",
+ "who": "3",
+ "where": "#7",
+ "what": "1",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935425.734274,
+ "iso_time": "2026-10-25T13:37:05.734274+00:00",
+ "direction": "rx",
+ "raw": "*3*1*#8##",
+ "who": "3",
+ "where": "#8",
+ "what": "1",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935451.555935,
+ "iso_time": "2026-10-25T13:37:31.555935+00:00",
+ "direction": "rx",
+ "raw": "*3*1*#1##",
+ "who": "3",
+ "where": "#1",
+ "what": "1",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935477.317713,
+ "iso_time": "2026-10-25T13:37:57.317713+00:00",
+ "direction": "rx",
+ "raw": "*3*1*#2##",
+ "who": "3",
+ "where": "#2",
+ "what": "1",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935503.138728,
+ "iso_time": "2026-10-25T13:38:23.138728+00:00",
+ "direction": "rx",
+ "raw": "*3*1*#3##",
+ "who": "3",
+ "where": "#3",
+ "what": "1",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935528.891552,
+ "iso_time": "2026-10-25T13:38:48.891552+00:00",
+ "direction": "rx",
+ "raw": "*3*1*#4##",
+ "who": "3",
+ "where": "#4",
+ "what": "1",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935554.691384,
+ "iso_time": "2026-10-25T13:39:14.691384+00:00",
+ "direction": "rx",
+ "raw": "*3*1*#5##",
+ "who": "3",
+ "where": "#5",
+ "what": "1",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935580.472874,
+ "iso_time": "2026-10-25T13:39:40.472874+00:00",
+ "direction": "rx",
+ "raw": "*3*1*#6##",
+ "who": "3",
+ "where": "#6",
+ "what": "1",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935606.253657,
+ "iso_time": "2026-10-25T13:40:06.253657+00:00",
+ "direction": "rx",
+ "raw": "*3*1*#7##",
+ "who": "3",
+ "where": "#7",
+ "what": "1",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935632.061033,
+ "iso_time": "2026-10-25T13:40:32.061033+00:00",
+ "direction": "rx",
+ "raw": "*3*1*#8##",
+ "who": "3",
+ "where": "#8",
+ "what": "1",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935657.836375,
+ "iso_time": "2026-10-25T13:40:57.836375+00:00",
+ "direction": "rx",
+ "raw": "*3*1*#1##",
+ "who": "3",
+ "where": "#1",
+ "what": "1",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935662.260423,
+ "iso_time": "2026-10-25T13:41:02.260423+00:00",
+ "direction": "tx",
+ "raw": "*#13**0##",
+ "who": "13",
+ "where": "",
+ "what": null,
+ "dimension": "0",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935662.275146,
+ "iso_time": "2026-10-25T13:41:02.275146+00:00",
+ "direction": "rx",
+ "raw": "*#13**0*14*41*00*##",
+ "who": "13",
+ "where": "",
+ "what": null,
+ "dimension": "0",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935662.326815,
+ "iso_time": "2026-10-25T13:41:02.326815+00:00",
+ "direction": "tx",
+ "raw": "*#13**15##",
+ "who": "13",
+ "where": "",
+ "what": null,
+ "dimension": "15",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935662.334342,
+ "iso_time": "2026-10-25T13:41:02.334342+00:00",
+ "direction": "rx",
+ "raw": "*#13**15*23##",
+ "who": "13",
+ "where": "",
+ "what": null,
+ "dimension": "15",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935662.385798,
+ "iso_time": "2026-10-25T13:41:02.385798+00:00",
+ "direction": "tx",
+ "raw": "*#13**16##",
+ "who": "13",
+ "where": "",
+ "what": null,
+ "dimension": "16",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935662.39168,
+ "iso_time": "2026-10-25T13:41:02.391680+00:00",
+ "direction": "rx",
+ "raw": "*#13**16*6*0*11##",
+ "who": "13",
+ "where": "",
+ "what": null,
+ "dimension": "16",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935662.443527,
+ "iso_time": "2026-10-25T13:41:02.443527+00:00",
+ "direction": "tx",
+ "raw": "*#13**23##",
+ "who": "13",
+ "where": "",
+ "what": null,
+ "dimension": "23",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935662.449567,
+ "iso_time": "2026-10-25T13:41:02.449567+00:00",
+ "direction": "rx",
+ "raw": "*#13**23*2*6*2##",
+ "who": "13",
+ "where": "",
+ "what": null,
+ "dimension": "23",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935662.501909,
+ "iso_time": "2026-10-25T13:41:02.501909+00:00",
+ "direction": "tx",
+ "raw": "*#13**24##",
+ "who": "13",
+ "where": "",
+ "what": null,
+ "dimension": "24",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935662.507936,
+ "iso_time": "2026-10-25T13:41:02.507936+00:00",
+ "direction": "rx",
+ "raw": "*#13**24*6*0*0##",
+ "who": "13",
+ "where": "",
+ "what": null,
+ "dimension": "24",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935662.559214,
+ "iso_time": "2026-10-25T13:41:02.559214+00:00",
+ "direction": "tx",
+ "raw": "*#1*0##",
+ "who": "1",
+ "where": "0",
+ "what": null,
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935662.630505,
+ "iso_time": "2026-10-25T13:41:02.630505+00:00",
+ "direction": "rx",
+ "raw": "*1*0*11##",
+ "who": "1",
+ "where": "11",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935662.670423,
+ "iso_time": "2026-10-25T13:41:02.670423+00:00",
+ "direction": "rx",
+ "raw": "*1*0*21##",
+ "who": "1",
+ "where": "21",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935662.709112,
+ "iso_time": "2026-10-25T13:41:02.709112+00:00",
+ "direction": "rx",
+ "raw": "*1*0*81##",
+ "who": "1",
+ "where": "81",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935662.739436,
+ "iso_time": "2026-10-25T13:41:02.739436+00:00",
+ "direction": "rx",
+ "raw": "*1*0*22##",
+ "who": "1",
+ "where": "22",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935662.783754,
+ "iso_time": "2026-10-25T13:41:02.783754+00:00",
+ "direction": "rx",
+ "raw": "*1*0*82##",
+ "who": "1",
+ "where": "82",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935662.821192,
+ "iso_time": "2026-10-25T13:41:02.821192+00:00",
+ "direction": "rx",
+ "raw": "*1*0*91##",
+ "who": "1",
+ "where": "91",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935662.886909,
+ "iso_time": "2026-10-25T13:41:02.886909+00:00",
+ "direction": "rx",
+ "raw": "*1*0*92##",
+ "who": "1",
+ "where": "92",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935662.915703,
+ "iso_time": "2026-10-25T13:41:02.915703+00:00",
+ "direction": "rx",
+ "raw": "*1*0*12##",
+ "who": "1",
+ "where": "12",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935662.949535,
+ "iso_time": "2026-10-25T13:41:02.949535+00:00",
+ "direction": "rx",
+ "raw": "*1*0*42##",
+ "who": "1",
+ "where": "42",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935662.996809,
+ "iso_time": "2026-10-25T13:41:02.996809+00:00",
+ "direction": "rx",
+ "raw": "*1*0*13##",
+ "who": "1",
+ "where": "13",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935663.045868,
+ "iso_time": "2026-10-25T13:41:03.045868+00:00",
+ "direction": "rx",
+ "raw": "*1*0*43##",
+ "who": "1",
+ "where": "43",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935663.095511,
+ "iso_time": "2026-10-25T13:41:03.095511+00:00",
+ "direction": "rx",
+ "raw": "*1*0*23##",
+ "who": "1",
+ "where": "23",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935663.158503,
+ "iso_time": "2026-10-25T13:41:03.158503+00:00",
+ "direction": "rx",
+ "raw": "*1*0*33##",
+ "who": "1",
+ "where": "33",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935663.189571,
+ "iso_time": "2026-10-25T13:41:03.189571+00:00",
+ "direction": "rx",
+ "raw": "*1*0*24##",
+ "who": "1",
+ "where": "24",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935663.24044,
+ "iso_time": "2026-10-25T13:41:03.240440+00:00",
+ "direction": "rx",
+ "raw": "*1*0*31##",
+ "who": "1",
+ "where": "31",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935663.270421,
+ "iso_time": "2026-10-25T13:41:03.270421+00:00",
+ "direction": "rx",
+ "raw": "*1*0*93##",
+ "who": "1",
+ "where": "93",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935663.302097,
+ "iso_time": "2026-10-25T13:41:03.302097+00:00",
+ "direction": "rx",
+ "raw": "*1*0*94##",
+ "who": "1",
+ "where": "94",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935663.350825,
+ "iso_time": "2026-10-25T13:41:03.350825+00:00",
+ "direction": "rx",
+ "raw": "*1*0*14##",
+ "who": "1",
+ "where": "14",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935663.409955,
+ "iso_time": "2026-10-25T13:41:03.409955+00:00",
+ "direction": "rx",
+ "raw": "*1*0*34##",
+ "who": "1",
+ "where": "34",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935663.441718,
+ "iso_time": "2026-10-25T13:41:03.441718+00:00",
+ "direction": "rx",
+ "raw": "*1*0*15##",
+ "who": "1",
+ "where": "15",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935663.487728,
+ "iso_time": "2026-10-25T13:41:03.487728+00:00",
+ "direction": "rx",
+ "raw": "*1*0*37##",
+ "who": "1",
+ "where": "37",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935663.545248,
+ "iso_time": "2026-10-25T13:41:03.545248+00:00",
+ "direction": "rx",
+ "raw": "*1*0*44##",
+ "who": "1",
+ "where": "44",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935663.575163,
+ "iso_time": "2026-10-25T13:41:03.575163+00:00",
+ "direction": "rx",
+ "raw": "*1*0*45##",
+ "who": "1",
+ "where": "45",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935663.62509,
+ "iso_time": "2026-10-25T13:41:03.625090+00:00",
+ "direction": "rx",
+ "raw": "*1*0*25##",
+ "who": "1",
+ "where": "25",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935663.676028,
+ "iso_time": "2026-10-25T13:41:03.676028+00:00",
+ "direction": "rx",
+ "raw": "*1*0*35##",
+ "who": "1",
+ "where": "35",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935663.706269,
+ "iso_time": "2026-10-25T13:41:03.706269+00:00",
+ "direction": "rx",
+ "raw": "*1*0*26##",
+ "who": "1",
+ "where": "26",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935663.753354,
+ "iso_time": "2026-10-25T13:41:03.753354+00:00",
+ "direction": "rx",
+ "raw": "*1*0*32##",
+ "who": "1",
+ "where": "32",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935663.80128,
+ "iso_time": "2026-10-25T13:41:03.801280+00:00",
+ "direction": "rx",
+ "raw": "*1*0*95##",
+ "who": "1",
+ "where": "95",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935663.840133,
+ "iso_time": "2026-10-25T13:41:03.840133+00:00",
+ "direction": "rx",
+ "raw": "*1*0*16##",
+ "who": "1",
+ "where": "16",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935663.887842,
+ "iso_time": "2026-10-25T13:41:03.887842+00:00",
+ "direction": "rx",
+ "raw": "*1*0*36##",
+ "who": "1",
+ "where": "36",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935663.932435,
+ "iso_time": "2026-10-25T13:41:03.932435+00:00",
+ "direction": "rx",
+ "raw": "*1*0*17##",
+ "who": "1",
+ "where": "17",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935663.985839,
+ "iso_time": "2026-10-25T13:41:03.985839+00:00",
+ "direction": "rx",
+ "raw": "*1*0*27##",
+ "who": "1",
+ "where": "27",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935664.032359,
+ "iso_time": "2026-10-25T13:41:04.032359+00:00",
+ "direction": "rx",
+ "raw": "*1*0*29##",
+ "who": "1",
+ "where": "29",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935664.07796,
+ "iso_time": "2026-10-25T13:41:04.077960+00:00",
+ "direction": "rx",
+ "raw": "*1*0*18##",
+ "who": "1",
+ "where": "18",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935664.127114,
+ "iso_time": "2026-10-25T13:41:04.127114+00:00",
+ "direction": "rx",
+ "raw": "*1*0*38##",
+ "who": "1",
+ "where": "38",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935664.169958,
+ "iso_time": "2026-10-25T13:41:04.169958+00:00",
+ "direction": "rx",
+ "raw": "*1*0*19##",
+ "who": "1",
+ "where": "19",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935664.219877,
+ "iso_time": "2026-10-25T13:41:04.219877+00:00",
+ "direction": "rx",
+ "raw": "*1*0*39##",
+ "who": "1",
+ "where": "39",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935664.268708,
+ "iso_time": "2026-10-25T13:41:04.268708+00:00",
+ "direction": "rx",
+ "raw": "*1*0*98##",
+ "who": "1",
+ "where": "98",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935664.282087,
+ "iso_time": "2026-10-25T13:41:04.282087+00:00",
+ "direction": "rx",
+ "raw": "*1*0*29##",
+ "who": "1",
+ "where": "29",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935664.32795,
+ "iso_time": "2026-10-25T13:41:04.327950+00:00",
+ "direction": "rx",
+ "raw": "*1*0*28##",
+ "who": "1",
+ "where": "28",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935664.378287,
+ "iso_time": "2026-10-25T13:41:04.378287+00:00",
+ "direction": "rx",
+ "raw": "*1*1*79##",
+ "who": "1",
+ "where": "79",
+ "what": "1",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935664.408652,
+ "iso_time": "2026-10-25T13:41:04.408652+00:00",
+ "direction": "rx",
+ "raw": "*2*0*99##",
+ "who": "2",
+ "where": "99",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935664.45302,
+ "iso_time": "2026-10-25T13:41:04.453020+00:00",
+ "direction": "rx",
+ "raw": "*1*0*78##",
+ "who": "1",
+ "where": "78",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935666.218415,
+ "iso_time": "2026-10-25T13:41:06.218415+00:00",
+ "direction": "tx",
+ "raw": "*#2*0##",
+ "who": "2",
+ "where": "0",
+ "what": null,
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935666.289241,
+ "iso_time": "2026-10-25T13:41:06.289241+00:00",
+ "direction": "rx",
+ "raw": "*1*0*11##",
+ "who": "1",
+ "where": "11",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935666.330646,
+ "iso_time": "2026-10-25T13:41:06.330646+00:00",
+ "direction": "rx",
+ "raw": "*1*0*21##",
+ "who": "1",
+ "where": "21",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935666.368601,
+ "iso_time": "2026-10-25T13:41:06.368601+00:00",
+ "direction": "rx",
+ "raw": "*1*0*81##",
+ "who": "1",
+ "where": "81",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935666.398907,
+ "iso_time": "2026-10-25T13:41:06.398907+00:00",
+ "direction": "rx",
+ "raw": "*1*0*91##",
+ "who": "1",
+ "where": "91",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935666.437151,
+ "iso_time": "2026-10-25T13:41:06.437151+00:00",
+ "direction": "rx",
+ "raw": "*1*0*22##",
+ "who": "1",
+ "where": "22",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935666.48026,
+ "iso_time": "2026-10-25T13:41:06.480260+00:00",
+ "direction": "rx",
+ "raw": "*1*0*82##",
+ "who": "1",
+ "where": "82",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935666.530057,
+ "iso_time": "2026-10-25T13:41:06.530057+00:00",
+ "direction": "rx",
+ "raw": "*1*0*92##",
+ "who": "1",
+ "where": "92",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935666.578987,
+ "iso_time": "2026-10-25T13:41:06.578987+00:00",
+ "direction": "rx",
+ "raw": "*1*0*12##",
+ "who": "1",
+ "where": "12",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935666.620453,
+ "iso_time": "2026-10-25T13:41:06.620453+00:00",
+ "direction": "rx",
+ "raw": "*1*0*42##",
+ "who": "1",
+ "where": "42",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935666.671232,
+ "iso_time": "2026-10-25T13:41:06.671232+00:00",
+ "direction": "rx",
+ "raw": "*1*0*13##",
+ "who": "1",
+ "where": "13",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935666.713004,
+ "iso_time": "2026-10-25T13:41:06.713004+00:00",
+ "direction": "rx",
+ "raw": "*1*0*43##",
+ "who": "1",
+ "where": "43",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935666.772232,
+ "iso_time": "2026-10-25T13:41:06.772232+00:00",
+ "direction": "rx",
+ "raw": "*1*0*23##",
+ "who": "1",
+ "where": "23",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935666.80886,
+ "iso_time": "2026-10-25T13:41:06.808860+00:00",
+ "direction": "rx",
+ "raw": "*1*0*33##",
+ "who": "1",
+ "where": "33",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935666.859708,
+ "iso_time": "2026-10-25T13:41:06.859708+00:00",
+ "direction": "rx",
+ "raw": "*1*0*24##",
+ "who": "1",
+ "where": "24",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935666.887348,
+ "iso_time": "2026-10-25T13:41:06.887348+00:00",
+ "direction": "rx",
+ "raw": "*1*0*31##",
+ "who": "1",
+ "where": "31",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935666.932355,
+ "iso_time": "2026-10-25T13:41:06.932355+00:00",
+ "direction": "rx",
+ "raw": "*1*0*93##",
+ "who": "1",
+ "where": "93",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935666.977557,
+ "iso_time": "2026-10-25T13:41:06.977557+00:00",
+ "direction": "rx",
+ "raw": "*1*0*94##",
+ "who": "1",
+ "where": "94",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935667.019223,
+ "iso_time": "2026-10-25T13:41:07.019223+00:00",
+ "direction": "rx",
+ "raw": "*1*0*14##",
+ "who": "1",
+ "where": "14",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935667.068717,
+ "iso_time": "2026-10-25T13:41:07.068717+00:00",
+ "direction": "rx",
+ "raw": "*1*0*34##",
+ "who": "1",
+ "where": "34",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935667.11237,
+ "iso_time": "2026-10-25T13:41:07.112370+00:00",
+ "direction": "rx",
+ "raw": "*1*0*15##",
+ "who": "1",
+ "where": "15",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935667.166378,
+ "iso_time": "2026-10-25T13:41:07.166378+00:00",
+ "direction": "rx",
+ "raw": "*1*0*37##",
+ "who": "1",
+ "where": "37",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935667.203771,
+ "iso_time": "2026-10-25T13:41:07.203771+00:00",
+ "direction": "rx",
+ "raw": "*1*0*44##",
+ "who": "1",
+ "where": "44",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935667.254917,
+ "iso_time": "2026-10-25T13:41:07.254917+00:00",
+ "direction": "rx",
+ "raw": "*1*0*45##",
+ "who": "1",
+ "where": "45",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935667.304127,
+ "iso_time": "2026-10-25T13:41:07.304127+00:00",
+ "direction": "rx",
+ "raw": "*1*0*25##",
+ "who": "1",
+ "where": "25",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935667.335952,
+ "iso_time": "2026-10-25T13:41:07.335952+00:00",
+ "direction": "rx",
+ "raw": "*1*0*35##",
+ "who": "1",
+ "where": "35",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935667.399777,
+ "iso_time": "2026-10-25T13:41:07.399777+00:00",
+ "direction": "rx",
+ "raw": "*1*0*32##",
+ "who": "1",
+ "where": "32",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935667.446143,
+ "iso_time": "2026-10-25T13:41:07.446143+00:00",
+ "direction": "rx",
+ "raw": "*1*0*26##",
+ "who": "1",
+ "where": "26",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935667.486414,
+ "iso_time": "2026-10-25T13:41:07.486414+00:00",
+ "direction": "rx",
+ "raw": "*1*0*95##",
+ "who": "1",
+ "where": "95",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935667.529877,
+ "iso_time": "2026-10-25T13:41:07.529877+00:00",
+ "direction": "rx",
+ "raw": "*1*0*16##",
+ "who": "1",
+ "where": "16",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935667.578937,
+ "iso_time": "2026-10-25T13:41:07.578937+00:00",
+ "direction": "rx",
+ "raw": "*1*0*36##",
+ "who": "1",
+ "where": "36",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935667.616813,
+ "iso_time": "2026-10-25T13:41:07.616813+00:00",
+ "direction": "rx",
+ "raw": "*1*0*17##",
+ "who": "1",
+ "where": "17",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935667.664294,
+ "iso_time": "2026-10-25T13:41:07.664294+00:00",
+ "direction": "rx",
+ "raw": "*1*0*27##",
+ "who": "1",
+ "where": "27",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935667.697884,
+ "iso_time": "2026-10-25T13:41:07.697884+00:00",
+ "direction": "rx",
+ "raw": "*1*0*29##",
+ "who": "1",
+ "where": "29",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935667.759927,
+ "iso_time": "2026-10-25T13:41:07.759927+00:00",
+ "direction": "rx",
+ "raw": "*1*0*18##",
+ "who": "1",
+ "where": "18",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935667.787864,
+ "iso_time": "2026-10-25T13:41:07.787864+00:00",
+ "direction": "rx",
+ "raw": "*1*0*38##",
+ "who": "1",
+ "where": "38",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935667.828866,
+ "iso_time": "2026-10-25T13:41:07.828866+00:00",
+ "direction": "rx",
+ "raw": "*1*0*19##",
+ "who": "1",
+ "where": "19",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935667.872136,
+ "iso_time": "2026-10-25T13:41:07.872136+00:00",
+ "direction": "rx",
+ "raw": "*1*0*39##",
+ "who": "1",
+ "where": "39",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935667.918247,
+ "iso_time": "2026-10-25T13:41:07.918247+00:00",
+ "direction": "rx",
+ "raw": "*1*0*98##",
+ "who": "1",
+ "where": "98",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935667.960239,
+ "iso_time": "2026-10-25T13:41:07.960239+00:00",
+ "direction": "rx",
+ "raw": "*1*0*29##",
+ "who": "1",
+ "where": "29",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935667.988179,
+ "iso_time": "2026-10-25T13:41:07.988179+00:00",
+ "direction": "rx",
+ "raw": "*1*1*79##",
+ "who": "1",
+ "where": "79",
+ "what": "1",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935668.030402,
+ "iso_time": "2026-10-25T13:41:08.030402+00:00",
+ "direction": "rx",
+ "raw": "*1*0*28##",
+ "who": "1",
+ "where": "28",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935668.073979,
+ "iso_time": "2026-10-25T13:41:08.073979+00:00",
+ "direction": "rx",
+ "raw": "*1*0*78##",
+ "who": "1",
+ "where": "78",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935668.119749,
+ "iso_time": "2026-10-25T13:41:08.119749+00:00",
+ "direction": "rx",
+ "raw": "*2*0*99##",
+ "who": "2",
+ "where": "99",
+ "what": "0",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935668.871935,
+ "iso_time": "2026-10-25T13:41:08.871935+00:00",
+ "direction": "tx",
+ "raw": "*#4*0##",
+ "who": "4",
+ "where": "0",
+ "what": null,
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935670.197834,
+ "iso_time": "2026-10-25T13:41:10.197834+00:00",
+ "direction": "tx",
+ "raw": "*#16*0*5##",
+ "who": "16",
+ "where": "0",
+ "what": null,
+ "dimension": "5",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935670.330277,
+ "iso_time": "2026-10-25T13:41:10.330277+00:00",
+ "direction": "rx",
+ "raw": "*16*3*101##",
+ "who": "16",
+ "where": "101",
+ "what": "3",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935670.335537,
+ "iso_time": "2026-10-25T13:41:10.335537+00:00",
+ "direction": "rx",
+ "raw": "*#22*2#1*12*1*4##",
+ "who": "22",
+ "where": "2",
+ "what": null,
+ "dimension": "12",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935670.379009,
+ "iso_time": "2026-10-25T13:41:10.379009+00:00",
+ "direction": "rx",
+ "raw": "*16*3*102##",
+ "who": "16",
+ "where": "102",
+ "what": "3",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935670.383856,
+ "iso_time": "2026-10-25T13:41:10.383856+00:00",
+ "direction": "rx",
+ "raw": "*#22*2#2*12*1*4##",
+ "who": "22",
+ "where": "2",
+ "what": null,
+ "dimension": "12",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935670.848101,
+ "iso_time": "2026-10-25T13:41:10.848101+00:00",
+ "direction": "rx",
+ "raw": "*16*13*42##",
+ "who": "16",
+ "where": "42",
+ "what": "13",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935670.853709,
+ "iso_time": "2026-10-25T13:41:10.853709+00:00",
+ "direction": "rx",
+ "raw": "*#22*3#4#2*12*0*10##",
+ "who": "22",
+ "where": "3",
+ "what": null,
+ "dimension": "12",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935670.889427,
+ "iso_time": "2026-10-25T13:41:10.889427+00:00",
+ "direction": "rx",
+ "raw": "*16*13*32##",
+ "who": "16",
+ "where": "32",
+ "what": "13",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935670.896421,
+ "iso_time": "2026-10-25T13:41:10.896421+00:00",
+ "direction": "rx",
+ "raw": "*#22*3#3#2*12*0*10##",
+ "who": "22",
+ "where": "3",
+ "what": null,
+ "dimension": "12",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935670.918736,
+ "iso_time": "2026-10-25T13:41:10.918736+00:00",
+ "direction": "rx",
+ "raw": "*16*13*41##",
+ "who": "16",
+ "where": "41",
+ "what": "13",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935670.9251,
+ "iso_time": "2026-10-25T13:41:10.925100+00:00",
+ "direction": "rx",
+ "raw": "*#22*3#4#1*12*0*10##",
+ "who": "22",
+ "where": "3",
+ "what": null,
+ "dimension": "12",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935671.068936,
+ "iso_time": "2026-10-25T13:41:11.068936+00:00",
+ "direction": "rx",
+ "raw": "*16*13*22##",
+ "who": "16",
+ "where": "22",
+ "what": "13",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935671.073764,
+ "iso_time": "2026-10-25T13:41:11.073764+00:00",
+ "direction": "rx",
+ "raw": "*#22*3#2#2*12*0*10##",
+ "who": "22",
+ "where": "3",
+ "what": null,
+ "dimension": "12",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935671.099686,
+ "iso_time": "2026-10-25T13:41:11.099686+00:00",
+ "direction": "rx",
+ "raw": "*16*13*51##",
+ "who": "16",
+ "where": "51",
+ "what": "13",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935671.1053,
+ "iso_time": "2026-10-25T13:41:11.105300+00:00",
+ "direction": "rx",
+ "raw": "*#22*3#5#1*12*0*10##",
+ "who": "22",
+ "where": "3",
+ "what": null,
+ "dimension": "12",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935671.19946,
+ "iso_time": "2026-10-25T13:41:11.199460+00:00",
+ "direction": "rx",
+ "raw": "*16*13*33##",
+ "who": "16",
+ "where": "33",
+ "what": "13",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935671.204139,
+ "iso_time": "2026-10-25T13:41:11.204139+00:00",
+ "direction": "rx",
+ "raw": "*#22*3#3#3*12*0*10##",
+ "who": "22",
+ "where": "3",
+ "what": null,
+ "dimension": "12",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935671.229306,
+ "iso_time": "2026-10-25T13:41:11.229306+00:00",
+ "direction": "rx",
+ "raw": "*16*13*13##",
+ "who": "16",
+ "where": "13",
+ "what": "13",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935671.236039,
+ "iso_time": "2026-10-25T13:41:11.236039+00:00",
+ "direction": "rx",
+ "raw": "*#22*3#1#3*12*0*10##",
+ "who": "22",
+ "where": "3",
+ "what": null,
+ "dimension": "12",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935671.599414,
+ "iso_time": "2026-10-25T13:41:11.599414+00:00",
+ "direction": "rx",
+ "raw": "*16*13*31##",
+ "who": "16",
+ "where": "31",
+ "what": "13",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935671.604306,
+ "iso_time": "2026-10-25T13:41:11.604306+00:00",
+ "direction": "rx",
+ "raw": "*#22*3#3#1*12*0*10##",
+ "who": "22",
+ "where": "3",
+ "what": null,
+ "dimension": "12",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935671.829112,
+ "iso_time": "2026-10-25T13:41:11.829112+00:00",
+ "direction": "rx",
+ "raw": "*16*13*21##",
+ "who": "16",
+ "where": "21",
+ "what": "13",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935671.833942,
+ "iso_time": "2026-10-25T13:41:11.833942+00:00",
+ "direction": "rx",
+ "raw": "*#22*3#2#1*12*0*10##",
+ "who": "22",
+ "where": "3",
+ "what": null,
+ "dimension": "12",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935671.900125,
+ "iso_time": "2026-10-25T13:41:11.900125+00:00",
+ "direction": "rx",
+ "raw": "*#22*3#6#1*12*0*10##",
+ "who": "22",
+ "where": "3",
+ "what": null,
+ "dimension": "12",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935671.913711,
+ "iso_time": "2026-10-25T13:41:11.913711+00:00",
+ "direction": "rx",
+ "raw": "*16*13*61##",
+ "who": "16",
+ "where": "61",
+ "what": "13",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935671.97786,
+ "iso_time": "2026-10-25T13:41:11.977860+00:00",
+ "direction": "rx",
+ "raw": "*16*13*12##",
+ "who": "16",
+ "where": "12",
+ "what": "13",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935671.979603,
+ "iso_time": "2026-10-25T13:41:11.979603+00:00",
+ "direction": "rx",
+ "raw": "*#22*3#1#2*12*0*10##",
+ "who": "22",
+ "where": "3",
+ "what": null,
+ "dimension": "12",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935672.058725,
+ "iso_time": "2026-10-25T13:41:12.058725+00:00",
+ "direction": "rx",
+ "raw": "*16*13*11##",
+ "who": "16",
+ "where": "11",
+ "what": "13",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935672.063847,
+ "iso_time": "2026-10-25T13:41:12.063847+00:00",
+ "direction": "rx",
+ "raw": "*#22*3#1#1*12*0*10##",
+ "who": "22",
+ "where": "3",
+ "what": null,
+ "dimension": "12",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935674.848816,
+ "iso_time": "2026-10-25T13:41:14.848816+00:00",
+ "direction": "tx",
+ "raw": "*#18*51*51##",
+ "who": "18",
+ "where": "51",
+ "what": null,
+ "dimension": "51",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935674.925752,
+ "iso_time": "2026-10-25T13:41:14.925752+00:00",
+ "direction": "tx",
+ "raw": "*#18*51*1200##",
+ "who": "18",
+ "where": "51",
+ "what": null,
+ "dimension": "1200",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935674.982456,
+ "iso_time": "2026-10-25T13:41:14.982456+00:00",
+ "direction": "tx",
+ "raw": "*#18*52*51##",
+ "who": "18",
+ "where": "52",
+ "what": null,
+ "dimension": "51",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935675.92585,
+ "iso_time": "2026-10-25T13:41:15.925850+00:00",
+ "direction": "tx",
+ "raw": "*#18*52*1200##",
+ "who": "18",
+ "where": "52",
+ "what": null,
+ "dimension": "1200",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935675.981999,
+ "iso_time": "2026-10-25T13:41:15.981999+00:00",
+ "direction": "tx",
+ "raw": "*#18*53*51##",
+ "who": "18",
+ "where": "53",
+ "what": null,
+ "dimension": "51",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935676.925961,
+ "iso_time": "2026-10-25T13:41:16.925961+00:00",
+ "direction": "tx",
+ "raw": "*#18*53*1200##",
+ "who": "18",
+ "where": "53",
+ "what": null,
+ "dimension": "1200",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935676.982433,
+ "iso_time": "2026-10-25T13:41:16.982433+00:00",
+ "direction": "tx",
+ "raw": "*#18*54*51##",
+ "who": "18",
+ "where": "54",
+ "what": null,
+ "dimension": "51",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935677.92598,
+ "iso_time": "2026-10-25T13:41:17.925980+00:00",
+ "direction": "tx",
+ "raw": "*#18*54*1200##",
+ "who": "18",
+ "where": "54",
+ "what": null,
+ "dimension": "1200",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935677.98265,
+ "iso_time": "2026-10-25T13:41:17.982650+00:00",
+ "direction": "tx",
+ "raw": "*#18*55*51##",
+ "who": "18",
+ "where": "55",
+ "what": null,
+ "dimension": "51",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935678.926197,
+ "iso_time": "2026-10-25T13:41:18.926197+00:00",
+ "direction": "tx",
+ "raw": "*#18*55*1200##",
+ "who": "18",
+ "where": "55",
+ "what": null,
+ "dimension": "1200",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935678.983482,
+ "iso_time": "2026-10-25T13:41:18.983482+00:00",
+ "direction": "tx",
+ "raw": "*#18*56*51##",
+ "who": "18",
+ "where": "56",
+ "what": null,
+ "dimension": "51",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935679.926535,
+ "iso_time": "2026-10-25T13:41:19.926535+00:00",
+ "direction": "tx",
+ "raw": "*#18*56*1200##",
+ "who": "18",
+ "where": "56",
+ "what": null,
+ "dimension": "1200",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935679.982793,
+ "iso_time": "2026-10-25T13:41:19.982793+00:00",
+ "direction": "tx",
+ "raw": "*#18*57*51##",
+ "who": "18",
+ "where": "57",
+ "what": null,
+ "dimension": "51",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935680.925684,
+ "iso_time": "2026-10-25T13:41:20.925684+00:00",
+ "direction": "tx",
+ "raw": "*#18*57*1200##",
+ "who": "18",
+ "where": "57",
+ "what": null,
+ "dimension": "1200",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935680.981808,
+ "iso_time": "2026-10-25T13:41:20.981808+00:00",
+ "direction": "tx",
+ "raw": "*#18*58*51##",
+ "who": "18",
+ "where": "58",
+ "what": null,
+ "dimension": "51",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935681.926342,
+ "iso_time": "2026-10-25T13:41:21.926342+00:00",
+ "direction": "tx",
+ "raw": "*#18*58*1200##",
+ "who": "18",
+ "where": "58",
+ "what": null,
+ "dimension": "1200",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935681.983168,
+ "iso_time": "2026-10-25T13:41:21.983168+00:00",
+ "direction": "tx",
+ "raw": "*#18*59*51##",
+ "who": "18",
+ "where": "59",
+ "what": null,
+ "dimension": "51",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935682.548558,
+ "iso_time": "2026-10-25T13:41:22.548558+00:00",
+ "direction": "rx",
+ "raw": "*3*1*#2##",
+ "who": "3",
+ "where": "#2",
+ "what": "1",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935682.926713,
+ "iso_time": "2026-10-25T13:41:22.926713+00:00",
+ "direction": "tx",
+ "raw": "*#18*59*1200##",
+ "who": "18",
+ "where": "59",
+ "what": null,
+ "dimension": "1200",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935682.982922,
+ "iso_time": "2026-10-25T13:41:22.982922+00:00",
+ "direction": "tx",
+ "raw": "*#18*71#0*51##",
+ "who": "18",
+ "where": "71",
+ "what": null,
+ "dimension": "51",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935683.926587,
+ "iso_time": "2026-10-25T13:41:23.926587+00:00",
+ "direction": "tx",
+ "raw": "*#18*71#0*1200##",
+ "who": "18",
+ "where": "71",
+ "what": null,
+ "dimension": "1200",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935683.983229,
+ "iso_time": "2026-10-25T13:41:23.983229+00:00",
+ "direction": "tx",
+ "raw": "*#18*72#0*51##",
+ "who": "18",
+ "where": "72",
+ "what": null,
+ "dimension": "51",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935684.926507,
+ "iso_time": "2026-10-25T13:41:24.926507+00:00",
+ "direction": "tx",
+ "raw": "*#18*72#0*1200##",
+ "who": "18",
+ "where": "72",
+ "what": null,
+ "dimension": "1200",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935684.982967,
+ "iso_time": "2026-10-25T13:41:24.982967+00:00",
+ "direction": "tx",
+ "raw": "*#18*73#0*51##",
+ "who": "18",
+ "where": "73",
+ "what": null,
+ "dimension": "51",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935685.926137,
+ "iso_time": "2026-10-25T13:41:25.926137+00:00",
+ "direction": "tx",
+ "raw": "*#18*73#0*1200##",
+ "who": "18",
+ "where": "73",
+ "what": null,
+ "dimension": "1200",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935685.982745,
+ "iso_time": "2026-10-25T13:41:25.982745+00:00",
+ "direction": "tx",
+ "raw": "*#18*74#0*51##",
+ "who": "18",
+ "where": "74",
+ "what": null,
+ "dimension": "51",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935686.926572,
+ "iso_time": "2026-10-25T13:41:26.926572+00:00",
+ "direction": "tx",
+ "raw": "*#18*74#0*1200##",
+ "who": "18",
+ "where": "74",
+ "what": null,
+ "dimension": "1200",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935686.983581,
+ "iso_time": "2026-10-25T13:41:26.983581+00:00",
+ "direction": "tx",
+ "raw": "*#18*75#0*51##",
+ "who": "18",
+ "where": "75",
+ "what": null,
+ "dimension": "51",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935687.926448,
+ "iso_time": "2026-10-25T13:41:27.926448+00:00",
+ "direction": "tx",
+ "raw": "*#18*75#0*1200##",
+ "who": "18",
+ "where": "75",
+ "what": null,
+ "dimension": "1200",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935687.98318,
+ "iso_time": "2026-10-25T13:41:27.983180+00:00",
+ "direction": "tx",
+ "raw": "*#18*76#0*51##",
+ "who": "18",
+ "where": "76",
+ "what": null,
+ "dimension": "51",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935688.926535,
+ "iso_time": "2026-10-25T13:41:28.926535+00:00",
+ "direction": "tx",
+ "raw": "*#18*76#0*1200##",
+ "who": "18",
+ "where": "76",
+ "what": null,
+ "dimension": "1200",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935688.983141,
+ "iso_time": "2026-10-25T13:41:28.983141+00:00",
+ "direction": "tx",
+ "raw": "*#18*77#0*51##",
+ "who": "18",
+ "where": "77",
+ "what": null,
+ "dimension": "51",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935689.930527,
+ "iso_time": "2026-10-25T13:41:29.930527+00:00",
+ "direction": "tx",
+ "raw": "*#18*77#0*1200##",
+ "who": "18",
+ "where": "77",
+ "what": null,
+ "dimension": "1200",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935689.987287,
+ "iso_time": "2026-10-25T13:41:29.987287+00:00",
+ "direction": "tx",
+ "raw": "*#18*78#0*51##",
+ "who": "18",
+ "where": "78",
+ "what": null,
+ "dimension": "51",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935690.926272,
+ "iso_time": "2026-10-25T13:41:30.926272+00:00",
+ "direction": "tx",
+ "raw": "*#18*78#0*1200##",
+ "who": "18",
+ "where": "78",
+ "what": null,
+ "dimension": "1200",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935690.982317,
+ "iso_time": "2026-10-25T13:41:30.982317+00:00",
+ "direction": "tx",
+ "raw": "*#18*79#0*51##",
+ "who": "18",
+ "where": "79",
+ "what": null,
+ "dimension": "51",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935691.926814,
+ "iso_time": "2026-10-25T13:41:31.926814+00:00",
+ "direction": "tx",
+ "raw": "*#18*79#0*1200##",
+ "who": "18",
+ "where": "79",
+ "what": null,
+ "dimension": "1200",
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ },
+ {
+ "timestamp": 1792935708.189435,
+ "iso_time": "2026-10-25T13:41:48.189435+00:00",
+ "direction": "rx",
+ "raw": "*3*1*#3##",
+ "who": "3",
+ "where": "#3",
+ "what": "1",
+ "dimension": null,
+ "is_ack": false,
+ "is_nack": false,
+ "is_duplicate": false
+ }
+ ]
+ },
+ "topology_inference": {
+ "target_gateway": {
+ "mac": "**REDACTED**",
+ "model": "H/L4684",
+ "hardware_tier": 3,
+ "command_queue_delay": 0.05,
+ "supported_whos": [
+ 1,
+ 2,
+ 4,
+ 15,
+ 16,
+ 18,
+ 22,
+ 25
+ ],
+ "configured_topology": "standalone",
+ "configured_role": "primary",
+ "configured_primary": null,
+ "configured_delegated_whos": []
+ },
+ "peer_count": 0,
+ "evaluations": []
+ }
+ },
+ "issues": [
+ {
+ "created": "2026-10-24T17:34:56.287730+00:00",
+ "dismissed_version": "2026.10.2",
+ "domain": "myhome",
+ "is_persistent": false,
+ "issue_id": "legacy_yaml_imported_01M1VB4S1ZJM0GCE7T4D18PAGZ"
+ }
+ ]
+}
diff --git a/tests/test_climate_humidity_poll_gate.py b/tests/test_climate_humidity_poll_gate.py
index 48eff51f1..981903a82 100644
--- a/tests/test_climate_humidity_poll_gate.py
+++ b/tests/test_climate_humidity_poll_gate.py
@@ -68,7 +68,6 @@ def _entry(model: str) -> MagicMock:
def _handler(hass, model: str) -> MyHOMEGatewayHandler:
handler = MyHOMEGatewayHandler(hass, _entry(model))
- handler.gateway.profile = get_gateway_profile(model)
handler.send_status_request = AsyncMock()
return handler
@@ -202,20 +201,23 @@ def test_aliased_model_strings_follow_their_profile(hass, model, profile_model):
assert handler._model_key == profile_model
-def test_ssdp_model_string_hl4684_is_keyed_as_h4684(hass):
- """SSDP announces the H4684 as ``HL4684`` (manifest.json); OWNd has no alias for it yet."""
- handler = _handler(hass, "HL4684")
- assert handler.gateway.profile.model_name == "HL4684" # the Generic profile keeps the SSDP string
+@pytest.mark.parametrize("ssdp_model", ["HL4684", "H/L4684", "HC4684"])
+def test_ssdp_model_string_hl4684_is_keyed_as_h4684(hass, ssdp_model):
+ """SSDP announces the H4684 as ``H/L4684`` or ``HL4684``; handler keeps the SSDP name and resolves H4684Profile."""
+ handler = _handler(hass, ssdp_model)
+ assert handler.model == ssdp_model
+ assert handler.gateway.profile.model_name == "H4684"
assert handler._model_key == "H4684"
+ assert handler.humidity_request_refused is True
@pytest.mark.parametrize(
("model", "key", "refused"),
[
- ("HL4684", "H4684", True), # the SSDP modelName, never overwritten by WHO 13 / 1013
- ("HC4684", "H4684", True),
("BTicino L4684", "H4684", True), # OWNd aliases the word, not the brand-prefixed string
("BTicino HL4684", "H4684", True),
+ ("BTicino H/L4684", "H4684", True),
+ ("BTicino HC4684", "H4684", True),
("Legrand 003598", "F454", True), # the MyHOME_Suite item number of the F454
("Legrand 003565", "MH200N", True), # ... of the MH200N: not collapsed to MH200
("Legrand 003535", "MH202", True),
diff --git a/tests/test_diagnostics.py b/tests/test_diagnostics.py
index 93d11307f..0bc20c463 100644
--- a/tests/test_diagnostics.py
+++ b/tests/test_diagnostics.py
@@ -195,7 +195,7 @@ async def test_diagnostics_carry_no_household_identity(hass: HomeAssistant):
"name": "MyHomeServer1",
}
mock_entry.options = {
- "file_path": "C:/Users/rossi/myhome.yaml", "command_worker_count": 1,
+ "file_path": "C:/Users/rossi/myhome.yaml", "config_file_path": "/config/myhome.yaml", "command_worker_count": 1,
# decoder slots: the media_player is named after a room, its slot -> source mapping is diagnostics
"decoder_1_entity": "media_player.rossi_living_room", "decoder_1_source": 2, "decoder_1_pre_gain": 10,
"decoder_2_entity": "", "decoder_2_source": 2, "decoder_2_pre_gain": 0,
@@ -208,6 +208,7 @@ async def test_diagnostics_carry_no_household_identity(hass: HomeAssistant):
for key in ("host", CONF_MAC, "id", "UDN", "ssdp_location", "friendly_name", CONF_PASSWORD):
assert data[key] == "**REDACTED**", key
assert options["file_path"] == "**REDACTED**"
+ assert options["config_file_path"] == "**REDACTED**"
assert data["port"] == 20000 and data["name"] == "MyHomeServer1" and options["command_worker_count"] == 1
assert options["decoder_1_entity"] == "media_player.decoder_1" # the slot, not the room
assert options["decoder_1_source"] == 2 and options["decoder_1_pre_gain"] == 10
diff --git a/tests/test_gateway_identification.py b/tests/test_gateway_identification.py
index ce883dd11..33368d56c 100644
--- a/tests/test_gateway_identification.py
+++ b/tests/test_gateway_identification.py
@@ -92,7 +92,10 @@ def test_official_table_is_the_2006_document_verbatim():
assert GATEWAY_DEVICE_TYPE_MAP["44"] == "MH200N"
# code 200: observed on F454 (#370, #420), MyHOMEServer1 (#292 / #297, #420), MH202 (#420);
# reported for F461 (#370), observed on H4890 (#466), observed on MH201 (OWNd#116)
- assert WHO13_OBSERVED_DEVICE_TYPES == {"200": ("F454", "MyHomeServer1", "MH202", "F461", "H4890", "MH201")}
+ assert WHO13_OBSERVED_DEVICE_TYPES == {
+ "23": ("H4684", "H/L4684", "L4684"),
+ "200": ("F454", "MyHomeServer1", "MH202", "F461", "H4890", "MH201"),
+ }
assert WHO13_SHARED_DEVICE_TYPES == {"200"}
assert WHO13_SHARED_DEVICE_TYPES <= set(WHO13_OBSERVED_DEVICE_TYPES)
# every model a shared WHO=13 code may stand for has a WHO=1013 code that settles it
@@ -124,7 +127,6 @@ def test_third_party_table_is_the_nmap_device_table():
"12": ("F453AV",),
"15": ("F427",),
"16": ("F453",),
- "23": ("H4684",),
"27": ("L4686SDK",),
"44": ("MH200N",),
"51": ("F454",),
@@ -154,7 +156,11 @@ def test_who13_and_who1013_are_separate_identifier_spaces():
12, 44, 51 - which is exactly why this is pinned: agreeing on a few codes is
not a reason to treat one table as the other.
"""
- who13_all = {**WHO13_OFFICIAL_DEVICE_TYPES, **{c: m[0] for c, m in WHO13_THIRD_PARTY_DEVICE_TYPES.items()}}
+ who13_all = {
+ **WHO13_OFFICIAL_DEVICE_TYPES,
+ **{c: m[0] for c, m in WHO13_OBSERVED_DEVICE_TYPES.items() if c not in WHO13_SHARED_DEVICE_TYPES},
+ **{c: m[0] for c, m in WHO13_THIRD_PARTY_DEVICE_TYPES.items()},
+ }
by_model_who13 = {model: code for code, model in who13_all.items()}
by_model_who1013 = {models[0]: code for code, models in WHO1013_OBJECT_MODELS.items()}
@@ -219,6 +225,8 @@ def test_h4684_ground_truth_firmware_mapping():
assert r19.alternative_names == ("L4684",)
assert r19.compatible_with("H4684") is True
assert r19.compatible_with("L4684") is True
+ assert r19.compatible_with("H/L4684") is True
+ assert r19.compatible_with("HL4684") is True
r26 = read_who1013("26")
assert r26.canonical == "LGRH4684"
diff --git a/tests/test_gateway_profiles.py b/tests/test_gateway_profiles.py
index 17e69941f..de4f0d763 100644
--- a/tests/test_gateway_profiles.py
+++ b/tests/test_gateway_profiles.py
@@ -43,6 +43,7 @@ def test_extract_gateway_models_from_const():
assert "F452V" in models
assert "F453" in models
assert "F453AV" in models
+ assert "H4684" in models
assert "Generic" in models
@@ -53,6 +54,7 @@ def test_extract_ssdp_models_from_manifest():
assert "MyHomeServer1" in ssdp_models
assert "AM4890" in ssdp_models
assert "HL4684" in ssdp_models
+ assert "H/L4684" in ssdp_models
def test_generate_gateway_profiles_table():
@@ -172,7 +174,7 @@ def test_generate_gateway_discovery_table_and_verify_oracle():
assert "| **MH200** *(Legacy)* | WHO 1, 2, 4, 16 | WHO 1, 2, 16 (firmware drops `*#4*0##`) |" in block
assert "| **MH200N** | WHO 1, 2, 4, 16 | WHO 1, 2, 4, 16 |" in block
assert "| **MH201** | WHO 1, 2, 4, 16, 18 | WHO 1, 2, 16, 18 (firmware drops `*#4*0##`) |" in block
- assert "| **H4684 / HL4684** | WHO 1, 2, 4, 16, 18 | WHO 1, 2, 16, 18 (firmware drops `*#4*0##`) |" in block
+ assert "| **H4684 / H/L4684** | WHO 1, 2, 4, 16, 18 | WHO 1, 2, 16, 18 (firmware drops `*#4*0##`) |" in block
assert ugp.verify_profiles_against_oracle() == []
diff --git a/tests/test_issue_466_hl4684_trace.py b/tests/test_issue_466_hl4684_trace.py
new file mode 100644
index 000000000..d58426ba5
--- /dev/null
+++ b/tests/test_issue_466_hl4684_trace.py
@@ -0,0 +1,491 @@
+"""Replay and regression tests for physical BTicino H/L4684 trace (Issue #466).
+
+Contributed by @andrea-mag in https://github.com/OpenWebNet-HA/MyHOME/issues/466#issuecomment-6398442412
+(H/L4684 firmware 6.0.11, kernel 2.6.2, distro 6.0.0, WHO 13 device type 23).
+"""
+from __future__ import annotations
+
+import json
+from pathlib import Path
+from unittest.mock import AsyncMock, MagicMock, patch
+
+import pytest
+from homeassistant.components.climate import ClimateEntityFeature, HVACAction, HVACMode
+from homeassistant.const import CONF_HOST, CONF_MAC, CONF_NAME, CONF_PASSWORD, CONF_PORT
+from homeassistant.core import HomeAssistant, State
+from homeassistant.helpers import entity_registry as er
+from OWNd.message import (
+ OWNEvent,
+ OWNGatewayDiagnosticEvent,
+ OWNGatewayEvent,
+ OWNHeatingEvent,
+ OWNLoadEvent,
+ OWNMessage,
+)
+from OWNd.profiles import GenericGatewayProfile
+from pytest_homeassistant_custom_component.common import MockConfigEntry
+
+from custom_components.myhome.climate import MyHOMEClimate, _zone_address
+from custom_components.myhome.const import (
+ CONF_DEVICE_TYPE,
+ CONF_ENTITY,
+ CONF_FILE_PATH,
+ CONF_FIRMWARE,
+ CONF_SSDP_LOCATION,
+ CONF_SSDP_ST,
+ DOMAIN,
+ IDENTIFICATION_SSDP,
+)
+from custom_components.myhome.diagnostics import TO_REDACT, async_get_config_entry_diagnostics
+from custom_components.myhome.gateway import (
+ MyHOMEGatewayHandler,
+ _firmware_at_least,
+ _profile_for_model,
+ command_session_default,
+ command_session_limit,
+)
+from custom_components.myhome.topology import gateway_supported_whos
+
+TRACE_PATH = (
+ Path(__file__).resolve().parent
+ / "fixtures"
+ / "traces"
+ / "issue_466"
+ / "config_entry-myhome_HL4684_fw6_0_11.json"
+)
+
+
+def _load_fixture() -> dict:
+ return json.loads(TRACE_PATH.read_text(encoding="utf-8"))
+
+
+def test_hl4684_trace_fixture_integrity_and_parser_coverage() -> None:
+ """Every frame in the H/L4684 diagnostics trace parses cleanly and redacts file paths."""
+ doc = _load_fixture()
+ assert CONF_FILE_PATH in TO_REDACT
+ assert doc["data"]["config_entry"]["options"][CONF_FILE_PATH] == "**REDACTED**"
+
+ frames = doc["data"]["bus_monitor"]["recent_frames"]
+ assert len(frames) == 257
+
+ rx_frames = [f["raw"] for f in frames if f["direction"] == "rx"]
+ tx_frames = [f["raw"] for f in frames if f["direction"] == "tx"]
+ assert len(rx_frames) == 212
+ assert len(tx_frames) == 45
+
+ rx_whos: set[int] = set()
+ who3_events: list[OWNLoadEvent] = []
+ for raw in rx_frames:
+ msg = OWNEvent.parse(raw) or OWNMessage.parse(raw)
+ assert msg is not None, f"Failed to parse rx frame: {raw}"
+ if getattr(msg, "who", None) is not None:
+ rx_whos.add(int(msg.who))
+ if isinstance(msg, OWNLoadEvent):
+ who3_events.append(msg)
+
+ assert rx_whos == {1, 2, 3, 4, 13, 16, 22}
+ assert len(who3_events) == 29
+ assert {e.where for e in who3_events} == {f"#{i}" for i in range(1, 9)}
+ assert {str(e.what) for e in who3_events} == {"1"}
+
+
+@pytest.mark.parametrize(
+ "model",
+ [
+ "H/L4684",
+ "HL4684",
+ "HC4684",
+ "H4684",
+ "L4684",
+ "BTicino H/L4684",
+ "003598", # F454
+ "003594", # F455
+ "003565", # MH200N
+ "003535", # MH202
+ "573992", # F453AV
+ ],
+)
+def test_scs_gateway_aliases_disable_point_to_point_zone_status(
+ hass: HomeAssistant, model: str
+) -> None:
+ """All H/L4684 spellings and MyHOME_Suite catalogue codes disable *#4*Z## zone polling."""
+ entry = MockConfigEntry(
+ domain=DOMAIN,
+ data={
+ CONF_HOST: "192.0.2.10",
+ CONF_PORT: 20000,
+ CONF_PASSWORD: "12345",
+ CONF_MAC: "00:03:50:aa:bb:cc",
+ CONF_NAME: model,
+ },
+ )
+ handler = MyHOMEGatewayHandler(hass, entry)
+ assert handler.supports_zone_status is False
+
+
+def test_hl4684_ssdp_profile_identity_and_fw6_who22(hass: HomeAssistant) -> None:
+ """SSDP H/L4684 binds H4684Profile, dynamically enables WHO 22 on FW >= 6.0.0, and corroborates code 23."""
+ # Start without CONF_FIRMWARE so we also verify dynamic profile refresh on *#13**16*6*0*11##!
+ entry = MockConfigEntry(
+ domain=DOMAIN,
+ title="H/L4684 Gateway",
+ data={
+ CONF_HOST: "192.0.2.10",
+ CONF_PORT: 20000,
+ CONF_PASSWORD: "12345",
+ CONF_MAC: "00:03:50:aa:bb:cc",
+ CONF_NAME: "H/L4684",
+ CONF_DEVICE_TYPE: "urn:schemas-bticino-it:device:touchscreen:1",
+ CONF_SSDP_LOCATION: "http://192.0.2.10:49152/description.xml",
+ CONF_SSDP_ST: "upnp:rootdevice",
+ },
+ options={CONF_FILE_PATH: "/config/myhome.yaml"},
+ )
+ entry.add_to_hass(hass)
+ handler = MyHOMEGatewayHandler(hass, entry)
+
+ assert handler.model == "H/L4684"
+ assert not isinstance(handler.gateway.profile, GenericGatewayProfile)
+ assert handler.gateway.profile.model_name == "H4684"
+ assert command_session_limit("H/L4684") == 1
+ assert command_session_default("H/L4684") == 1
+
+ # Before firmware is known (or on 2.0.54), WHO 22 is False; WHO 16 is True; WHO 5/25 are False
+ assert handler.profile_supports_who(16) is True
+ assert handler.profile_supports_who(22) is False
+ assert handler.profile_supports_who(5) is False
+ assert handler.profile_supports_who(25) is False
+ assert gateway_supported_whos("H/L4684", "6.0.11") == {1, 2, 4, 15, 16, 18, 22}
+ assert gateway_supported_whos("H/L4684", "2.0.54") == {1, 2, 4, 15, 16, 18}
+ assert _firmware_at_least("unknown", (6, 0, 0)) is False
+ assert _firmware_at_least("6.0", (6, 0, 0)) is True
+
+ # Simulate OWNd <= 2.0.0b11 where get_gateway_profile("H/L4684") returned GenericGatewayProfile
+ def _legacy_lookup(m: str | None, fw: str | None = None):
+ if m == "H4684":
+ return handler.gateway.profile
+ return GenericGatewayProfile(model_name=m or "Generic")
+
+ assert _profile_for_model("H/L4684", "6.0.11", lookup=_legacy_lookup).model_name == "H4684"
+
+ # Replay the real WHO 13 identification sequence from the H/L4684 trace
+ for raw in (
+ "*#13**15*23##",
+ "*#13**16*6*0*11##",
+ "*#13**23*2*6*2##",
+ "*#13**24*6*0*0##",
+ ):
+ evt = OWNEvent.parse(raw)
+ assert isinstance(evt, OWNGatewayEvent)
+ handler._handle_gateway_diagnostics(evt)
+
+ # After *#13**16*6*0*11##, firmware 6.0.11 upgrades the bound H4684Profile to support WHO 22!
+ assert handler.firmware == "6.0.11"
+ assert handler.profile_supports_who(22) is True
+
+ ident = handler.identification()
+ assert ident["model"] == "H/L4684"
+ assert ident["source"] == IDENTIFICATION_SSDP
+ assert ident["who13_code"] == "23"
+ assert ident["who13_model"] == "H4684"
+ assert ident["who13_firmware"] == "6.0.11"
+ assert ident["who13_kernel"] == "2.6.2"
+ assert ident["who13_distribution"] == "6.0.0"
+ assert ident["conflict"] is None
+
+ # Official 2006 code 13 and WHO 1013 OBJECT_MODEL 19 must also corroborate H/L4684 without conflict
+ evt_official = OWNEvent.parse("*#13**15*13##")
+ assert isinstance(evt_official, OWNGatewayEvent)
+ handler._handle_gateway_diagnostics(evt_official)
+ assert handler.identification()["conflict"] is None
+
+ evt_1013 = OWNEvent.parse("*#1013**1*19*15*5*0##")
+ assert isinstance(evt_1013, OWNGatewayDiagnosticEvent)
+ handler._handle_gateway_identity_diagnostics(evt_1013)
+ assert handler.identification()["who1013_code"] == "19"
+ assert handler.identification()["conflict"] is None
+
+
+async def test_hl4684_diagnostics_redacts_config_file_path(hass: HomeAssistant) -> None:
+ """Diagnostics redacts options['config_file_path'] alongside legacy 'file_path'."""
+ entry = MockConfigEntry(
+ domain=DOMAIN,
+ data={CONF_HOST: "192.0.2.10", CONF_MAC: "00:03:50:aa:bb:cc", CONF_NAME: "H/L4684"},
+ options={CONF_FILE_PATH: "/config/myhome.yaml"},
+ )
+ entry.add_to_hass(hass)
+ hass.data[DOMAIN] = {}
+
+ diag = await async_get_config_entry_diagnostics(hass, entry)
+ assert diag["config_entry"]["options"][CONF_FILE_PATH] == "**REDACTED**"
+
+
+async def test_hl4684_trace_f430_4_pure_valve_vs_fancoil_zones(hass: HomeAssistant) -> None:
+ """Replay WHO 4 frames from H/L4684 trace: F430/4 pure-valve zones retract tentative fan_mode."""
+ doc = _load_fixture()
+ rx_who4 = [
+ f["raw"]
+ for f in doc["data"]["bus_monitor"]["recent_frames"]
+ if f["direction"] == "rx" and f["who"] == "4"
+ ]
+ assert len(rx_who4) == 64
+
+ gateway = MagicMock()
+ gateway.mac = "00:03:50:aa:bb:cc"
+ gateway.log_id = "[H/L4684 gateway]"
+ gateway.send = AsyncMock()
+ gateway.send_status_request = AsyncMock()
+ gateway.request_zone_dimension = MagicMock()
+ gateway.has_central_unit = True
+ gateway.supports_zone_status = False
+
+ # Match @andrea-mag's plant config: zones 11, 22, 44, 55, 66, 77, 88 have fan=False;
+ # zone 33 (PTE - Soggiorno) is explicitly configured with fan=True.
+ zone_addrs = ("11", "22", "33", "44", "55", "66", "77", "88")
+ zones: dict[str, MyHOMEClimate] = {}
+ for where in zone_addrs:
+ entity = MyHOMEClimate(
+ hass=hass,
+ name=f"Zone {where}",
+ device_id=f"4-{where}",
+ who="4",
+ where=where,
+ heating=True,
+ cooling=False,
+ fan=(where == "33"),
+ standalone=False,
+ central=False,
+ manufacturer="BTicino",
+ model="F430/4",
+ gateway=gateway,
+ )
+ entity.hass = hass
+ entity.entity_id = f"climate.zone_{where}"
+ entity.async_write_ha_state = MagicMock()
+ zones[where] = entity
+
+ central = MyHOMEClimate(
+ hass=hass,
+ name="Central Unit",
+ device_id="4-#0",
+ who="4",
+ where="#0",
+ heating=True,
+ cooling=False,
+ fan=False,
+ standalone=False,
+ central=True,
+ manufacturer="BTicino",
+ model="3550",
+ gateway=gateway,
+ )
+ central.hass = hass
+ central.entity_id = "climate.central_unit"
+ central.async_write_ha_state = MagicMock()
+
+ for raw in rx_who4:
+ evt = OWNEvent.parse(raw)
+ assert isinstance(evt, OWNHeatingEvent)
+ addr_obj = _zone_address(evt)
+ addr = addr_obj.where if addr_obj else None
+ if addr in ("#0", "0"):
+ central.handle_event(evt)
+ elif addr in zones:
+ zones[addr].handle_event(evt)
+
+ expected_temps = {
+ "11": 20.9,
+ "22": 22.4,
+ "33": 21.4,
+ "44": 23.3,
+ "55": 22.6,
+ "66": 22.8,
+ "77": 22.4,
+ "88": 22.1,
+ }
+ expected_setpoints = {
+ "11": 19.5,
+ "22": 17.0,
+ "33": 16.0,
+ "44": 18.0,
+ "55": 18.0,
+ "66": 17.0,
+ "77": 18.0,
+ "88": 18.0,
+ }
+ expected_actuators = {
+ "11": {"1": "off"},
+ "22": {"1": "off"},
+ "33": {"1": "off", "2": "off"},
+ "44": {"1": "off"},
+ "55": {"1": "off"},
+ "66": {"2": "off", "3": "off"},
+ "77": {"1": "off", "2": "off"},
+ "88": {"1": "off"},
+ }
+ for where, entity in zones.items():
+ assert entity.current_temperature == expected_temps[where]
+ assert entity.target_temperature == expected_setpoints[where]
+ assert entity.extra_state_attributes["actuators"] == expected_actuators[where]
+
+ # Receiving 20*5 on each zone triggered _ask_fan_coil_sides() (*#4*ZZ*19##) via request_zone_dimension:
+ asked_dim19 = {
+ call.args[0]
+ for call in gateway.request_zone_dimension.call_args_list
+ if len(call.args) >= 2 and call.args[1] == 19
+ }
+ assert asked_dim19 == set(zone_addrs)
+
+ # Once Dimension 19 answers *#4*ZZ*19*0*0## (no fan-coil sides, as captured on MyHomeServer1 zones
+ # 35..70 and F455 issue #674), unconfigured pure-valve zones (11, 22, 44, 55, 66, 77, 88) retract
+ # tentative FAN_MODE, while explicitly configured Zone 33 (fan=True) keeps FAN_MODE:
+ for where, entity in zones.items():
+ entity.handle_event(OWNHeatingEvent(f"*#4*{where}*19*0*0##"))
+ attrs = entity.extra_state_attributes
+ assert attrs["fan_coil_heating"] is False
+ assert attrs["fan_coil_cooling"] is False
+ if where == "33":
+ assert bool(entity.supported_features & ClimateEntityFeature.FAN_MODE)
+ assert entity.fan_modes == ["auto", "low", "medium", "high"]
+ assert "fan_mode" in attrs
+ else:
+ assert not bool(entity.supported_features & ClimateEntityFeature.FAN_MODE)
+ assert entity.fan_modes is None
+ assert entity.fan_mode is None
+ assert "fan_mode" not in attrs
+
+ # A subsequent *#4*11#1*20*5## or active heating valve report on pure-valve Zone 11
+ # must NOT re-enable FAN_MODE, while still tracking actuator state and hvac_action.
+ z11 = zones["11"]
+ z11.handle_event(OWNHeatingEvent("*#4*11#1*20*5##"))
+ assert not bool(z11.supported_features & ClimateEntityFeature.FAN_MODE)
+
+ z11._attr_hvac_mode = HVACMode.HEAT
+ z11.handle_event(OWNHeatingEvent("*#4*11#1*20*1##"))
+ assert z11.extra_state_attributes["actuators"] == {"1": "on"}
+ assert z11.hvac_action == HVACAction.HEATING
+ assert not bool(z11.supported_features & ClimateEntityFeature.FAN_MODE)
+
+ # Genuine auto-discovered fan-coil zone (e.g. F461 reporting 19*5*0 + 20*5 with fan=False)
+ # MUST keep FAN_MODE enabled.
+ fancoil_zone = MyHOMEClimate(
+ hass=hass,
+ name="Fan-Coil Zone 99",
+ device_id="4-99",
+ who="4",
+ where="99",
+ heating=True,
+ cooling=False,
+ fan=False,
+ standalone=False,
+ central=False,
+ manufacturer="BTicino",
+ model="F461",
+ gateway=gateway,
+ )
+ fancoil_zone.hass = hass
+ fancoil_zone.entity_id = "climate.zone_99"
+ fancoil_zone.async_write_ha_state = MagicMock()
+ fancoil_zone.handle_event(OWNHeatingEvent("*#4*99*19*5*0##"))
+ fancoil_zone.handle_event(OWNHeatingEvent("*#4*99#1*20*5##"))
+ assert bool(fancoil_zone.supported_features & ClimateEntityFeature.FAN_MODE)
+ assert fancoil_zone.fan_mode == "auto"
+
+ # Restoring stale fan_mode attributes alongside fan_coil_heating=False, fan_coil_cooling=False
+ # on an unconfigured zone must NOT re-enable FAN_MODE.
+ fresh_z11 = MyHOMEClimate(
+ hass=hass,
+ name="Zone 11",
+ device_id="4-11",
+ who="4",
+ where="11",
+ heating=True,
+ cooling=False,
+ fan=False,
+ standalone=False,
+ central=False,
+ manufacturer="BTicino",
+ model="F430/4",
+ gateway=gateway,
+ )
+ await fresh_z11.async_restore_last_state(
+ State(
+ "climate.zone_11",
+ HVACMode.HEAT,
+ {
+ "temperature": 19.5,
+ "fan_coil_heating": False,
+ "fan_coil_cooling": False,
+ "fan_mode": "auto",
+ "supported_features": int(
+ ClimateEntityFeature.TARGET_TEMPERATURE
+ | ClimateEntityFeature.TURN_ON
+ | ClimateEntityFeature.TURN_OFF
+ | ClimateEntityFeature.FAN_MODE
+ ),
+ },
+ )
+ )
+ assert not bool(fresh_z11.supported_features & ClimateEntityFeature.FAN_MODE)
+ assert fresh_z11.fan_mode is None
+
+
+async def test_hl4684_trace_dual_dialect_sound_inventory_deduplication(
+ hass: HomeAssistant,
+) -> None:
+ """Replay all 28 WHO 16 + WHO 22 frames from H/L4684 FW 6.0.11: 12 amplifier zones deduplicate."""
+ doc = _load_fixture()
+ sound_rx = [
+ f["raw"]
+ for f in doc["data"]["bus_monitor"]["recent_frames"]
+ if f["direction"] == "rx" and f["who"] in ("16", "22")
+ ]
+ assert len(sound_rx) == 28
+
+ mac = "00:03:50:aa:bb:cc"
+ entry = MockConfigEntry(
+ domain=DOMAIN,
+ title="H/L4684 Gateway",
+ data={
+ CONF_HOST: "192.0.2.10",
+ CONF_PORT: 20000,
+ CONF_PASSWORD: "12345",
+ CONF_MAC: mac,
+ CONF_NAME: "H/L4684",
+ CONF_FIRMWARE: "6.0.11",
+ },
+ options={"enable_auto_learning": True},
+ unique_id=mac,
+ )
+ entry.add_to_hass(hass)
+
+ with (
+ patch(
+ "custom_components.myhome.gateway.OWNSession.test_connection",
+ return_value={"Success": True},
+ ),
+ patch("custom_components.myhome.gateway.MyHOMEGatewayHandler.listening_loop"),
+ patch("custom_components.myhome.gateway.MyHOMEGatewayHandler.sending_loop"),
+ ):
+ assert await hass.config_entries.async_setup(entry.entry_id)
+ await hass.async_block_till_done()
+
+ handler: MyHOMEGatewayHandler = hass.data[DOMAIN][mac][CONF_ENTITY]
+ handler._on_event_connection_state_change(True)
+ await hass.async_block_till_done()
+
+ for raw in sound_rx:
+ msg = OWNEvent.parse(raw)
+ assert msg is not None
+ await handler._process_message(msg)
+ await hass.async_block_till_done()
+
+ ent_reg = er.async_get(hass)
+ mp_entries = [
+ e
+ for e in er.async_entries_for_config_entry(ent_reg, entry.entry_id)
+ if e.domain == "media_player"
+ ]
+ # 24 zone frames + 4 stereo-source frames (101/2#1, 102/2#2 are source inputs, not entities)
+ # register 12 deduplicated amplifier-zone media_player entities (11..13, 21..22, 31..33, 41..42, 51, 61).
+ assert len(mp_entries) == 12
diff --git a/tests/test_multi_gateway.py b/tests/test_multi_gateway.py
index e0e1ee3dc..c82489fc4 100644
--- a/tests/test_multi_gateway.py
+++ b/tests/test_multi_gateway.py
@@ -359,7 +359,7 @@ async def test_services_multi_gateway(hass: HomeAssistant, fast_bus_pacing) -> N
# covers/climate/audio/energy are delegated away, so its only general request is Lighting (*#1*0##). All are status requests.
gw_a.send.assert_not_called()
assert [str(c.args[0]) for c in gw_a.send_status_request.call_args_list] == [
- "*#13**0##", "*#13**15##", "*#13**16##", "*#13**23##", "*#13**24##", "*#1*0##",
+ "*#13**0##", "*#13**15##", "*#13**16##", "*#13**23##", "*#13**24##", "*#1013*0*1##", "*#1*0##",
]
gw_b.send.assert_not_called()
gw_b.send_status_request.assert_not_called()
@@ -380,10 +380,10 @@ async def test_services_multi_gateway(hass: HomeAssistant, fast_bus_pacing) -> N
# them out like the startup sweep does), plus the 36 delegated WHO=18 energy queries = 43
gw_b.send.assert_not_called()
calls_b = [str(c.args[0]) for c in gw_b.send_status_request.call_args_list]
- assert len(calls_b) == 43
- assert calls_b[:5] == ["*#13**0##", "*#13**15##", "*#13**16##", "*#13**23##", "*#13**24##"]
- assert calls_b[5:7] == ["*#2*0##", "*#5*0##"]
- assert len(calls_b[7:]) == 36
+ assert len(calls_b) == 44
+ assert calls_b[:6] == ["*#13**0##", "*#13**15##", "*#13**16##", "*#13**23##", "*#13**24##", "*#1013*0*1##"]
+ assert calls_b[6:8] == ["*#2*0##", "*#5*0##"]
+ assert len(calls_b[8:]) == 36
# 4. _get_gateway_handler falls back to next(iter(gateways.values())) if no primary
hass.config_entries.async_update_entry(
diff --git a/tests/test_services.py b/tests/test_services.py
index c19d449ca..e0f52982c 100644
--- a/tests/test_services.py
+++ b/tests/test_services.py
@@ -162,11 +162,13 @@ async def test_sweep_bus_queries_sent(hass: HomeAssistant, attach_gateway) -> No
# ... after the gateway queries and before the energy queries
names = [call[0] for call in order.mock_calls]
paced_at = names.index("send_paced")
- assert names[:paced_at] == ["send_status_request"] * 5
+ assert names[:paced_at] == ["send_status_request"] * 6
assert names[paced_at + 1:] == ["send_status_request"] * 36
sent_raw = [str(call.args[0]) for call in mock_handler.send_status_request.await_args_list]
- assert sent_raw[:5] == ["*#13**0##", "*#13**15##", "*#13**16##", "*#13**23##", "*#13**24##"]
+ assert sent_raw[:6] == [
+ "*#13**0##", "*#13**15##", "*#13**16##", "*#13**23##", "*#13**24##", "*#1013*0*1##",
+ ]
assert not any(raw.startswith(("*#1*", "*#2*", "*#4*", "*#5*", "*#16*")) for raw in sent_raw)
mock_handler.send.assert_not_called()
# Energy Management (WHO 18) F520 discovery queries
@@ -248,7 +250,7 @@ async def test_sweep_bus_follower_delegated_who1(hass: HomeAssistant, attach_gat
mock_handler.send_paced.assert_awaited_once_with(["*#1*0##"])
sent_raw = [str(call.args[0]) for call in mock_handler.send_status_request.await_args_list]
- assert sent_raw == ["*#13**0##", "*#13**15##", "*#13**16##", "*#13**23##", "*#13**24##"]
+ assert sent_raw == ["*#13**0##", "*#13**15##", "*#13**16##", "*#13**23##", "*#13**24##", "*#1013*0*1##"]
mock_handler.send.assert_not_called()
diff --git a/tests/test_startup_requests_per_firmware.py b/tests/test_startup_requests_per_firmware.py
index 70c99e0f4..a94599dd9 100644
--- a/tests/test_startup_requests_per_firmware.py
+++ b/tests/test_startup_requests_per_firmware.py
@@ -32,7 +32,6 @@
from homeassistant.const import CONF_HOST, CONF_MAC, CONF_NAME, CONF_PASSWORD, CONF_PORT
from homeassistant.core import State
from OWNd.message import OWNHeatingEvent
-from OWNd.profiles import get_gateway_profile
from custom_components.myhome.climate import MyHOMEClimate
from custom_components.myhome.const import CONF_FIRMWARE
@@ -60,9 +59,7 @@ def _entry(model: str) -> MagicMock:
def _handler(hass, model: str) -> MyHOMEGatewayHandler:
- handler = MyHOMEGatewayHandler(hass, _entry(model))
- handler.gateway.profile = get_gateway_profile(model)
- return handler
+ return MyHOMEGatewayHandler(hass, _entry(model))
def _zone(hass, handler, where="3", **kwargs) -> MyHOMEClimate:
@@ -85,7 +82,7 @@ def _zone(hass, handler, where="3", **kwargs) -> MyHOMEClimate:
("F461", False, False), ("MyHomeServer1", False, False), ("MH202", False, False),
("F459", False, False), ("F460", False, False), ("F450", False, False),
("F454", False, True), ("MH200", False, True), ("MH201", False, True),
- ("H4684", False, True), ("HL4684", False, True),
+ ("H4684", False, True), ("HL4684", False, True), ("H/L4684", False, True),
("F453", True, True), ("F453AV", True, True), ("MH200N", True, True), ("Generic", True, True),
],
)
@@ -153,10 +150,12 @@ def test_aliased_model_strings_follow_their_profile(hass, model, profile_model):
assert handler.zone_temperature_poll_needed is True
-def test_ssdp_model_string_hl4684_is_keyed_as_h4684(hass):
- """SSDP announces the H4684 as ``HL4684`` (manifest.json); OWNd has no alias for it yet."""
- handler = _handler(hass, "HL4684")
- assert handler.gateway.profile.model_name == "HL4684" # the Generic profile keeps the SSDP string
+@pytest.mark.parametrize("ssdp_model", ["H/L4684", "HL4684", "HC4684"])
+def test_ssdp_model_string_hl4684_is_keyed_as_h4684(hass, ssdp_model):
+ """SSDP announces the H4684 as ``H/L4684`` (or ``HL4684``); handler resolves ``H4684Profile``."""
+ handler = _handler(hass, ssdp_model)
+ assert handler.model == ssdp_model
+ assert handler.gateway.profile.model_name == "H4684"
assert handler._model_key == "H4684"
assert handler.supports_zone_status is False
assert handler.general_zone_request_refused is True
@@ -190,7 +189,7 @@ def test_profile_without_who_16_skips_the_sound_request(hass):
("model", "expected"),
[
("MH201", True), ("MH202", True), ("F450", True), ("F454", True), ("MH200", True), ("H4684", True),
- ("HL4684", True), ("F459", True),
+ ("HL4684", True), ("H/L4684", True), ("F459", True),
("F455", False), ("F453", False), ("MH200N", False), # unknown, or the sweep answers
("F460", False), ("F461", False), ("MyHomeServer1", False), ("Generic", False), # polled with *#4*Z##
],