diff --git a/tests/fixtures/traces/f461_live_2026-10-18/README.md b/tests/fixtures/traces/f461_live_2026-10-18/README.md new file mode 100644 index 000000000..a3ac471b9 --- /dev/null +++ b/tests/fixtures/traces/f461_live_2026-10-18/README.md @@ -0,0 +1,65 @@ +# Live Capture: BTicino F461 (Firmware 2.0.11 / WHO 1013 `134.15.5.0`), 2026-10-18 + +Authentic multi-session OpenWebNet capture from the expanded physical BTicino F461 test plant (`F411/4` lights with Group `#1` & General `0`, `F411/2` 5-second timed shutter at `WHERE=05`, `KW4691` standalone thermostat + `F430/2` + `F430/4` at `Zone 2`) and live bus verification of the declarative `expose` engine ([PR #750](https://github.com/OpenWebNet-HA/MyHOME/pull/750) / `OWNd#129`). + +--- + +## Plant Topology + +| Subsystem | Hardware & Addressing | +|---|---| +| **Gateway (`WHO=13` / `WHO=1013`)** | BTicino **F461**, firmware `2.0.11` (`*#13**16*2*0*11##`), kernel `5.10.35`, `WHO=13` device type `200` (`*#13**15*200##`), spontaneous `WHO=1013` object model `*#1013**1*134*15*5*0##`, HMAC-SHA256 authentication on both command and event sessions | +| **Lighting (`WHO=1`)** | **BT-F411/4** 4-channel relay actuator at Area 0 Point 6 (`WHERE="06"`, *"Light 1"*) and Area 0 Point 7 (`WHERE="07"`, *"Light 2"*), linked in **Group `#1`** (`*1*1*#1##` / `*1*0*#1##`) and responding to **General `0`** (`*1*1*0##` / `*1*0*0##`). Previous `01` / `04` addresses are now unpopulated (`NACK`). | +| **Automation / Shutter (`WHO=2`)** | **BT-F411/2** configured as an advanced shutter actuator at Area 0 Point 5 (`WHERE="05"`, *"Shutter"*) with a **5-second hardware travel time** | +| **Thermoregulation (`WHO=4`)** | **Living Now KW4691** standalone thermostat at `Zone 2` (no central unit), driving a **BT-F430/2** heating valve (`actuator 1`) and a **BT-F430/4** cooling fan-coil (`actuator 2`) | + +--- + +## File Format: `myhome_trace_F461_live_2026-10-18.json` + +Standard `MyHOME` derived trace archive containing: +- `capture` & `gateway`: metadata with RFC 5737 documentation IP `192.0.2.92` and synthetic MAC `00:03:50:00:04:97`. +- `frames`: all event-session `rx` frames (`direction: "rx"`, `raw: "..."`) replayed by the corpus health checks (`test_device_health_corpus.py`) and parser resilience suite (`test_trace_oracle_validation.py`). +- `rows`: the complete chronological session log (`tx`, `rx` on `cmd`/`evt`, and `result` with `ack`, `reply_time_s`, `replies`, `events_after`, and `note`) across four structured runs: + +| `run` | Scenario | +|---|---| +| `run1_lights_group_and_general` | `WHO=13`/`1013` identification, `F411/4` point switching (`06`, `07`), Group `#1` queries/commands (`*#1*#1##`, `*1*1*#1##`, `*1*0*#1##`), General `0` broadcasts (`*1*1*0##`, `*1*0*0##`) with post-broadcast resync polls (`*#1*06##`, `*#1*07##`), and unpopulated `01`/`04` checks | +| `run2_shutter_05_travel_and_reversal` | `F411/2` shutter (`WHERE=05`): initial status (`*#2*05##`) and Dim 10 (`*#2*05*10##`), 2.0 s partial `UP` + `STOP` (`*2*1*05##` -> `*2*0*05##`), and mid-flight direction reversal (`*2*1*05##` `UP` -> 1.5 s -> `*2*2*05##` `DOWN`) through the full 5.0 s hardware auto-stop (`*2*0*05##`) | +| `run3_climate_zone2` | `KW4691` standalone `Zone 2` status poll (`*#4*2##` -> 5 frames), temperature (`*#4*2*0##`), fan-coil/valve status (`*#4*2*19##`), and actuator status (`*#4*2*20##` -> `2#1` and `2#2`) | +| `run4_expose_virtual_bus_verification` | End-to-end physical F461 verification of `ExposeManager` (`WHO=1`, `WHO=2`, `WHO=4`, `WHO=13`, `WHO=18`) across populated (`06`, `2`) and unpopulated virtual (`08`, `25`, `50`, `000`, `102`, `51`) addresses | + +--- + +## Empirical Hardware Findings + +### 1. Physical `F411/4` (`06`, `07`), Group `#1` Aggregate Dimension 15, and General `0` +- **Group `#1` aggregate status (`*#1*#1*15*0##` / `*#1*#1*15*1##`)**: Whenever any member of Group `#1` (`06` or `07`) transitions Group `#1` from all-OFF to any-ON, the plant broadcasts `*#1*#1*15*1##` on the event session; when the last member turns OFF, it broadcasts `*#1*#1*15*0##`. +- **Group `#1` vs General `0` individual status reporting**: + - Group `#1` poll `*#1*#1##` is ACKed in ~1.2 s and returns both individual channel states (`*1*0*06##`, `*1*0*07##`). + - Group `#1` commands `*1*1*#1##` and `*1*0*#1##` emit `*1*1*#1##`, individual status frames `*1*1*06##` and `*1*1*07##`, and `*#1*#1*15*1##`. + - General `0` commands `*1*1*0##` and `*1*0*0##` switch both physical relays (`06` and `07`) and emit `*1*1*0##` + `*#1*#1*15*1##` **without** individual point-to-point status frames; subsequent point polls (`*#1*06##`, `*#1*07##`) confirm `06` and `07` switched ON/OFF. + +### 2. Physical `F411/2` 5-Second Timed Shutter (`WHERE=05`) +- **Command translation + actuator status sequence**: + - Raising from idle (`*2*1*05##`) emits four event-session frames in ~170 ms: gateway pre-move `STOP` translation (`*2*1000#0*05##`), actuator `STOP` confirmation (`*2*0*05##`), gateway `UP` translation (`*2*1000#1*05##`), and actuator `UP` status (`*2*1*05##`). + - Stopping (`*2*0*05##`) emits gateway `STOP` translation (`*2*1000#0*05##`) followed by actuator `STOP` status (`*2*0*05##`). + - Reversing direction mid-flight (`*2*1*05##` -> 1.5 s -> `*2*2*05##`) emits `*2*1000#0*05##`, `*2*0*05##`, `*2*1000#2*05##`, `*2*2*05##`, and **5.0 seconds later** the physical `F411/2` relay automatically emits end-of-travel `*2*0*05##` on the event monitor session. + +### 3. `Expose` Virtual Device & Protocol Behaviour on Physical `F461` ([PR #750](https://github.com/OpenWebNet-HA/MyHOME/pull/750)) +1. **`WHO=2` Virtual Cover (`WHERE=25`) — On-Wire Pre-Move `STOP` + `NACK` Verification**: + - Sending `s:*2*1*25##`, `s:*2*2*25##`, or `s:*2*0*25##` for an unpopulated virtual cover address `25` **immediately broadcasts the translated command sequence `['*2*1000#0*25##', '*2*1000#1*25##']`, `['*2*1000#0*25##', '*2*1000#2*25##']`, and `['*2*1000#0*25##']` onto the event monitor session** before `bt_fls` returns `*#*0##` (`NACK`) on the command session after ~0.55 s (because no physical shutter relay answers on the 2-wire bus). + - This directly validates PR #750's two key `WHO=2` mechanisms on physical `F461` hardware: + - `CommandWorkerPool` sending exposed frames with `is_status_request=True` (`no retry`) and resolving the `written` future on `NACK` when the command session is open. + - `ExposeManager`'s `_pre_move_stop_guard[(2, "25")]` and `_tx_echo_guard["*2*1000#1*25##"]` absorbing the gateway's 2-frame translation echo on `evt` without triggering a self-oscillating `stop_cover` / `open_cover` loop. +2. **`WHO=1` Virtual Light/Switch/Dimmer (`WHERE=08` vs Populated `06`)**: + - When an address is populated on the bus (`06`), `s:*1*1*06##` and `s:*1*0*06##` ACK in ~180 ms and echo `*1*1*06##` / `*1*0*06##` on `evt`. + - When no physical actuator exists at `08`, `bt_fls` transmits `*1*1*08##` / `*1*0*08##` / `*1*5*08##` on the 2-wire SCS bus, waits 2.06 s for a physical actuator confirmation, and returns `NACK` on the TCP command session without echoing on `evt`. Conversely, Dimension `#1` brightness write `*#1*08*#1*150*0##` is **ACKed in 177 ms**. +3. **`WHO=4` Virtual Temperature Probe (`*#4*W*0*T##` vs `*#4*W*#0*T##`)**: + - On physical `F461` (`bt_termo`), a dimension frame without `#` before the dimension number (`*#4*W*0*TTTT##`) is parsed by `bt_termo` as a **Dimension 0 READ request (`*#4*W*0##`)**: sending `*#4*2*0*0999##` to populated `Zone 2` returns the thermostat's actual measured temperature `*#4*2*0*0250##` (`25.0 °C`), and sending `*#4*50*0*0215##` / `*#4*000*0*0215##` / `*#4*102*0*0215##` to unpopulated addresses times out after 2.1 s with `NACK`. + - Conversely, `OWNHeatingCommand.write_temperature("50", 21.5)` (`*#4*50*#0*0215##`, with `#0` on a 2-digit address) is **ACKed in 134 ms** by the physical `F461`. +4. **`WHO=13` Clock / Date Synchronization**: + - `*#13**0##`, `*#13**1##`, and `*#13**22##` ACK in ~180 ms and emit the clock/date on both `cmd` and `evt`. + - `OWNGatewayCommand.set_time` (`*#13**#0*HH*MM*SS*999##`) ACKs in ~70 ms and broadcasts `*#13**#0*HH*MM*SS*999##` on `evt`, whereas `*#13**0*HH*MM*SS*999##` (without `#0`) is parsed by `F461` as a Dimension 0 read request that returns the gateway's current time. +5. **`WHO=18` Virtual Energy Meter (`WHERE=51`)**: + - `bt_energy` on the physical `F461` ACKs `*#18*51*113*450##` (Active Power), `*#18*51*51*12345##` (Total Energy), `*#18*51*54*320##` (Partial Day Energy), `*#18*51*#1200#1*1##` (Enable Streaming), `*#18*51*#1200#1*0##` (Disable Streaming), and `*#18*51*113##` (Active Power Query). diff --git a/tests/fixtures/traces/f461_live_2026-10-18/myhome_trace_F461_live_2026-10-18.json b/tests/fixtures/traces/f461_live_2026-10-18/myhome_trace_F461_live_2026-10-18.json new file mode 100644 index 000000000..a9973924b --- /dev/null +++ b/tests/fixtures/traces/f461_live_2026-10-18/myhome_trace_F461_live_2026-10-18.json @@ -0,0 +1,3523 @@ +{ + "capture": { + "kind": "derived_trace_archive", + "started_at": "2026-10-18T19:49:42+00:00", + "notes": "Live BTicino F461 (firmware 2.0.11 / WHO 1013 134.15.5.0) multi-session bus capture covering the expanded plant (F411/4 lights at WHERE 06 & 07 + Group #1 + General 0, F411/2 5s timed shutter at WHERE 05 with pre-move STOP and hardware auto-stop, KW4691 standalone climate Zone 2) and PR #750 Expose virtual actuator/sensor/clock/energy verification.", + "window": { + "first": "2026-10-18T19:49:42+00:00", + "last": "2026-10-18T19:51:14+00:00", + "frames": 73, + "rows": 294 + } + }, + "gateway": { + "model": "F461", + "manufacturer": "BTicino", + "firmware": "2.0.11", + "kernel": "5.10.35", + "ip": "192.0.2.92", + "mac": "00:03:50:00:04:97", + "identification": { + "model": "F461", + "who13_code": "200", + "who1013_code": "134" + } + }, + "frames": [ + { + "timestamp": 0.554, + "direction": "rx", + "raw": "*#13**15*200##", + "run": "run1_lights_group_and_general", + "session": "evt" + }, + { + "timestamp": 1.509, + "direction": "rx", + "raw": "*#13**16*2*0*11##", + "run": "run1_lights_group_and_general", + "session": "evt" + }, + { + "timestamp": 2.308, + "direction": "rx", + "raw": "*1*0*06##", + "run": "run1_lights_group_and_general", + "session": "evt" + }, + { + "timestamp": 3.115, + "direction": "rx", + "raw": "*1*0*07##", + "run": "run1_lights_group_and_general", + "session": "evt" + }, + { + "timestamp": 3.93, + "direction": "rx", + "raw": "*1*1*06##", + "run": "run1_lights_group_and_general", + "session": "evt" + }, + { + "timestamp": 4.018, + "direction": "rx", + "raw": "*#1*#1*15*1##", + "run": "run1_lights_group_and_general", + "session": "evt" + }, + { + "timestamp": 4.65, + "direction": "rx", + "raw": "*1*0*06##", + "run": "run1_lights_group_and_general", + "session": "evt" + }, + { + "timestamp": 4.73, + "direction": "rx", + "raw": "*#1*#1*15*0##", + "run": "run1_lights_group_and_general", + "session": "evt" + }, + { + "timestamp": 5.337, + "direction": "rx", + "raw": "*1*1*07##", + "run": "run1_lights_group_and_general", + "session": "evt" + }, + { + "timestamp": 5.408, + "direction": "rx", + "raw": "*#1*#1*15*1##", + "run": "run1_lights_group_and_general", + "session": "evt" + }, + { + "timestamp": 6.048, + "direction": "rx", + "raw": "*1*0*07##", + "run": "run1_lights_group_and_general", + "session": "evt" + }, + { + "timestamp": 6.136, + "direction": "rx", + "raw": "*#1*#1*15*0##", + "run": "run1_lights_group_and_general", + "session": "evt" + }, + { + "timestamp": 6.752, + "direction": "rx", + "raw": "*1*0*06##", + "run": "run1_lights_group_and_general", + "session": "evt" + }, + { + "timestamp": 6.807, + "direction": "rx", + "raw": "*1*0*07##", + "run": "run1_lights_group_and_general", + "session": "evt" + }, + { + "timestamp": 8.738, + "direction": "rx", + "raw": "*1*1*#1##", + "run": "run1_lights_group_and_general", + "session": "evt" + }, + { + "timestamp": 8.786, + "direction": "rx", + "raw": "*1*1*06##", + "run": "run1_lights_group_and_general", + "session": "evt" + }, + { + "timestamp": 8.836, + "direction": "rx", + "raw": "*#1*#1*15*1##", + "run": "run1_lights_group_and_general", + "session": "evt" + }, + { + "timestamp": 8.898, + "direction": "rx", + "raw": "*1*1*07##", + "run": "run1_lights_group_and_general", + "session": "evt" + }, + { + "timestamp": 9.447, + "direction": "rx", + "raw": "*1*0*#1##", + "run": "run1_lights_group_and_general", + "session": "evt" + }, + { + "timestamp": 9.505, + "direction": "rx", + "raw": "*1*0*06##", + "run": "run1_lights_group_and_general", + "session": "evt" + }, + { + "timestamp": 9.555, + "direction": "rx", + "raw": "*1*0*07##", + "run": "run1_lights_group_and_general", + "session": "evt" + }, + { + "timestamp": 9.606, + "direction": "rx", + "raw": "*#1*#1*15*0##", + "run": "run1_lights_group_and_general", + "session": "evt" + }, + { + "timestamp": 10.183, + "direction": "rx", + "raw": "*1*1*0##", + "run": "run1_lights_group_and_general", + "session": "evt" + }, + { + "timestamp": 10.245, + "direction": "rx", + "raw": "*#1*#1*15*1##", + "run": "run1_lights_group_and_general", + "session": "evt" + }, + { + "timestamp": 10.91, + "direction": "rx", + "raw": "*1*1*06##", + "run": "run1_lights_group_and_general", + "session": "evt" + }, + { + "timestamp": 11.693, + "direction": "rx", + "raw": "*1*1*07##", + "run": "run1_lights_group_and_general", + "session": "evt" + }, + { + "timestamp": 12.461, + "direction": "rx", + "raw": "*1*0*0##", + "run": "run1_lights_group_and_general", + "session": "evt" + }, + { + "timestamp": 12.541, + "direction": "rx", + "raw": "*#1*#1*15*0##", + "run": "run1_lights_group_and_general", + "session": "evt" + }, + { + "timestamp": 13.205, + "direction": "rx", + "raw": "*1*0*06##", + "run": "run1_lights_group_and_general", + "session": "evt" + }, + { + "timestamp": 13.997, + "direction": "rx", + "raw": "*1*0*07##", + "run": "run1_lights_group_and_general", + "session": "evt" + }, + { + "timestamp": 18.265, + "direction": "rx", + "raw": "*#13**#1*00*18*10*2026##", + "run": "run1_lights_group_and_general", + "session": "evt" + }, + { + "timestamp": 18.313, + "direction": "rx", + "raw": "*#13**#0*22*50*00*##", + "run": "run1_lights_group_and_general", + "session": "evt" + }, + { + "timestamp": 20.114, + "direction": "rx", + "raw": "*2*0*05##", + "run": "run2_shutter_05_travel_and_reversal", + "session": "evt" + }, + { + "timestamp": 22.602, + "direction": "rx", + "raw": "*2*1000#0*05##", + "run": "run2_shutter_05_travel_and_reversal", + "session": "evt" + }, + { + "timestamp": 22.67, + "direction": "rx", + "raw": "*2*0*05##", + "run": "run2_shutter_05_travel_and_reversal", + "session": "evt" + }, + { + "timestamp": 22.72, + "direction": "rx", + "raw": "*2*1000#1*05##", + "run": "run2_shutter_05_travel_and_reversal", + "session": "evt" + }, + { + "timestamp": 22.72, + "direction": "rx", + "raw": "*2*1*05##", + "run": "run2_shutter_05_travel_and_reversal", + "session": "evt" + }, + { + "timestamp": 24.792, + "direction": "rx", + "raw": "*2*1000#0*05##", + "run": "run2_shutter_05_travel_and_reversal", + "session": "evt" + }, + { + "timestamp": 24.852, + "direction": "rx", + "raw": "*2*0*05##", + "run": "run2_shutter_05_travel_and_reversal", + "session": "evt" + }, + { + "timestamp": 25.526, + "direction": "rx", + "raw": "*2*0*05##", + "run": "run2_shutter_05_travel_and_reversal", + "session": "evt" + }, + { + "timestamp": 26.275, + "direction": "rx", + "raw": "*2*1000#0*05##", + "run": "run2_shutter_05_travel_and_reversal", + "session": "evt" + }, + { + "timestamp": 26.325, + "direction": "rx", + "raw": "*2*0*05##", + "run": "run2_shutter_05_travel_and_reversal", + "session": "evt" + }, + { + "timestamp": 26.373, + "direction": "rx", + "raw": "*2*1000#1*05##", + "run": "run2_shutter_05_travel_and_reversal", + "session": "evt" + }, + { + "timestamp": 26.421, + "direction": "rx", + "raw": "*2*1*05##", + "run": "run2_shutter_05_travel_and_reversal", + "session": "evt" + }, + { + "timestamp": 27.964, + "direction": "rx", + "raw": "*2*1000#0*05##", + "run": "run2_shutter_05_travel_and_reversal", + "session": "evt" + }, + { + "timestamp": 28.014, + "direction": "rx", + "raw": "*2*0*05##", + "run": "run2_shutter_05_travel_and_reversal", + "session": "evt" + }, + { + "timestamp": 28.072, + "direction": "rx", + "raw": "*2*1000#2*05##", + "run": "run2_shutter_05_travel_and_reversal", + "session": "evt" + }, + { + "timestamp": 28.119, + "direction": "rx", + "raw": "*2*2*05##", + "run": "run2_shutter_05_travel_and_reversal", + "session": "evt" + }, + { + "timestamp": 34.341, + "direction": "rx", + "raw": "*2*0*05##", + "run": "run2_shutter_05_travel_and_reversal", + "session": "evt" + }, + { + "timestamp": 34.657, + "direction": "rx", + "raw": "*2*0*05##", + "run": "run2_shutter_05_travel_and_reversal", + "session": "evt" + }, + { + "timestamp": 35.953, + "direction": "rx", + "raw": "*#4*2*0*0249##", + "run": "run3_climate_zone2", + "session": "evt" + }, + { + "timestamp": 36.277, + "direction": "rx", + "raw": "*#4*2*12*0245*3##", + "run": "run3_climate_zone2", + "session": "evt" + }, + { + "timestamp": 36.323, + "direction": "rx", + "raw": "*4*1*2##", + "run": "run3_climate_zone2", + "session": "evt" + }, + { + "timestamp": 36.598, + "direction": "rx", + "raw": "*#4*2*14*0245*3##", + "run": "run3_climate_zone2", + "session": "evt" + }, + { + "timestamp": 36.691, + "direction": "rx", + "raw": "*#4*2*13*00##", + "run": "run3_climate_zone2", + "session": "evt" + }, + { + "timestamp": 37.492, + "direction": "rx", + "raw": "*#4*2*0*0249##", + "run": "run3_climate_zone2", + "session": "evt" + }, + { + "timestamp": 38.325, + "direction": "rx", + "raw": "*#4*2*19*5*0##", + "run": "run3_climate_zone2", + "session": "evt" + }, + { + "timestamp": 39.14, + "direction": "rx", + "raw": "*#4*2#1*20*0##", + "run": "run3_climate_zone2", + "session": "evt" + }, + { + "timestamp": 39.189, + "direction": "rx", + "raw": "*#4*2#2*20*5##", + "run": "run3_climate_zone2", + "session": "evt" + }, + { + "timestamp": 39.94, + "direction": "rx", + "raw": "*1*1*06##", + "run": "run4_expose_virtual_bus_verification", + "session": "evt" + }, + { + "timestamp": 40.019, + "direction": "rx", + "raw": "*#1*#1*15*1##", + "run": "run4_expose_virtual_bus_verification", + "session": "evt" + }, + { + "timestamp": 40.645, + "direction": "rx", + "raw": "*1*0*06##", + "run": "run4_expose_virtual_bus_verification", + "session": "evt" + }, + { + "timestamp": 40.732, + "direction": "rx", + "raw": "*#1*#1*15*0##", + "run": "run4_expose_virtual_bus_verification", + "session": "evt" + }, + { + "timestamp": 56.186, + "direction": "rx", + "raw": "*2*1000#0*25##", + "run": "run4_expose_virtual_bus_verification", + "session": "evt" + }, + { + "timestamp": 56.416, + "direction": "rx", + "raw": "*2*1000#1*25##", + "run": "run4_expose_virtual_bus_verification", + "session": "evt" + }, + { + "timestamp": 57.553, + "direction": "rx", + "raw": "*2*1000#0*25##", + "run": "run4_expose_virtual_bus_verification", + "session": "evt" + }, + { + "timestamp": 57.833, + "direction": "rx", + "raw": "*2*1000#2*25##", + "run": "run4_expose_virtual_bus_verification", + "session": "evt" + }, + { + "timestamp": 58.946, + "direction": "rx", + "raw": "*2*1000#0*25##", + "run": "run4_expose_virtual_bus_verification", + "session": "evt" + }, + { + "timestamp": 63.229, + "direction": "rx", + "raw": "*#4*2*0*0249##", + "run": "run4_expose_virtual_bus_verification", + "session": "evt" + }, + { + "timestamp": 78.212, + "direction": "rx", + "raw": "*#13**0*22*51*00*999##", + "run": "run4_expose_virtual_bus_verification", + "session": "evt" + }, + { + "timestamp": 78.908, + "direction": "rx", + "raw": "*#13**1*00*18*10*2026##", + "run": "run4_expose_virtual_bus_verification", + "session": "evt" + }, + { + "timestamp": 79.619, + "direction": "rx", + "raw": "*#13**22*22*51*02*999*00*18*10*2026##", + "run": "run4_expose_virtual_bus_verification", + "session": "evt" + }, + { + "timestamp": 80.337, + "direction": "rx", + "raw": "*#13**0*22*51*02*999##", + "run": "run4_expose_virtual_bus_verification", + "session": "evt" + } + ], + "rows": [ + { + "run": "run1_lights_group_and_general", + "t": 0.501, + "step": "gateway_id", + "dir": "tx", + "session": "cmd", + "frame": "*#13**15##", + "is_status_request": true + }, + { + "run": "run1_lights_group_and_general", + "t": 0.554, + "step": "gateway_id", + "dir": "rx", + "session": "evt", + "frame": "*#13**15*200##", + "ownd_class": "OWNGatewayEvent", + "ownd_log": "Gateway device type is: F454 / MyHomeServer1 / MH202 / F461 / H4890 / F450 / F459 / F460 / MH201." + }, + { + "run": "run1_lights_group_and_general", + "t": 0.651, + "step": "gateway_id", + "dir": "rx", + "session": "cmd", + "frame": "*#13**15*200##" + }, + { + "run": "run1_lights_group_and_general", + "t": 0.651, + "step": "gateway_id", + "dir": "rx", + "session": "cmd", + "frame": "*#*1##" + }, + { + "run": "run1_lights_group_and_general", + "t": 1.452, + "step": "gateway_id", + "dir": "result", + "frame": "*#13**15##", + "ack": true, + "reply_time_s": 0.15, + "replies": [ + "*#13**15*200##" + ], + "events_after": [ + "*#13**15*200##" + ], + "note": "who13_device_type_and_spontaneous_1013" + }, + { + "run": "run1_lights_group_and_general", + "t": 1.452, + "step": "gateway_id", + "dir": "tx", + "session": "cmd", + "frame": "*#13**16##", + "is_status_request": true + }, + { + "run": "run1_lights_group_and_general", + "t": 1.509, + "step": "gateway_id", + "dir": "rx", + "session": "evt", + "frame": "*#13**16*2*0*11##", + "ownd_class": "OWNGatewayEvent", + "ownd_log": "Gateway's firmware version is: 2.0.11." + }, + { + "run": "run1_lights_group_and_general", + "t": 1.564, + "step": "gateway_id", + "dir": "rx", + "session": "cmd", + "frame": "*#13**16*2*0*11##" + }, + { + "run": "run1_lights_group_and_general", + "t": 1.564, + "step": "gateway_id", + "dir": "rx", + "session": "cmd", + "frame": "*#*1##" + }, + { + "run": "run1_lights_group_and_general", + "t": 2.164, + "step": "gateway_id", + "dir": "result", + "frame": "*#13**16##", + "ack": true, + "reply_time_s": 0.111, + "replies": [ + "*#13**16*2*0*11##" + ], + "events_after": [ + "*#13**16*2*0*11##" + ], + "note": "who13_firmware_version" + }, + { + "run": "run1_lights_group_and_general", + "t": 2.164, + "step": "light_poll", + "dir": "tx", + "session": "cmd", + "frame": "*#1*06##", + "is_status_request": true + }, + { + "run": "run1_lights_group_and_general", + "t": 2.308, + "step": "light_poll", + "dir": "rx", + "session": "evt", + "frame": "*1*0*06##", + "ownd_class": "OWNLightingEvent", + "ownd_log": "Light 06 is switched off." + }, + { + "run": "run1_lights_group_and_general", + "t": 2.376, + "step": "light_poll", + "dir": "rx", + "session": "cmd", + "frame": "*1*0*06##" + }, + { + "run": "run1_lights_group_and_general", + "t": 2.376, + "step": "light_poll", + "dir": "rx", + "session": "cmd", + "frame": "*#*1##" + }, + { + "run": "run1_lights_group_and_general", + "t": 2.977, + "step": "light_poll", + "dir": "result", + "frame": "*#1*06##", + "ack": true, + "reply_time_s": 0.212, + "replies": [ + "*1*0*06##" + ], + "events_after": [ + "*1*0*06##" + ], + "note": "poll_light_06" + }, + { + "run": "run1_lights_group_and_general", + "t": 2.977, + "step": "light_poll", + "dir": "tx", + "session": "cmd", + "frame": "*#1*07##", + "is_status_request": true + }, + { + "run": "run1_lights_group_and_general", + "t": 3.115, + "step": "light_poll", + "dir": "rx", + "session": "evt", + "frame": "*1*0*07##", + "ownd_class": "OWNLightingEvent", + "ownd_log": "Light 07 is switched off." + }, + { + "run": "run1_lights_group_and_general", + "t": 3.18, + "step": "light_poll", + "dir": "rx", + "session": "cmd", + "frame": "*1*0*07##" + }, + { + "run": "run1_lights_group_and_general", + "t": 3.18, + "step": "light_poll", + "dir": "rx", + "session": "cmd", + "frame": "*#*1##" + }, + { + "run": "run1_lights_group_and_general", + "t": 3.781, + "step": "light_poll", + "dir": "result", + "frame": "*#1*07##", + "ack": true, + "reply_time_s": 0.203, + "replies": [ + "*1*0*07##" + ], + "events_after": [ + "*1*0*07##" + ], + "note": "poll_light_07" + }, + { + "run": "run1_lights_group_and_general", + "t": 3.781, + "step": "point_switch", + "dir": "tx", + "session": "cmd", + "frame": "*1*1*06##", + "is_status_request": false + }, + { + "run": "run1_lights_group_and_general", + "t": 3.9, + "step": "point_switch", + "dir": "rx", + "session": "cmd", + "frame": "*#*1##" + }, + { + "run": "run1_lights_group_and_general", + "t": 3.93, + "step": "point_switch", + "dir": "rx", + "session": "evt", + "frame": "*1*1*06##", + "ownd_class": "OWNLightingEvent", + "ownd_log": "Light 06 is switched on." + }, + { + "run": "run1_lights_group_and_general", + "t": 4.018, + "step": "point_switch", + "dir": "rx", + "session": "evt", + "frame": "*#1*#1*15*1##", + "ownd_class": "OWNLightingEvent", + "ownd_log": "Light/motion sensor #1 has sent an unknown dimension 15." + }, + { + "run": "run1_lights_group_and_general", + "t": 4.501, + "step": "point_switch", + "dir": "result", + "frame": "*1*1*06##", + "ack": true, + "reply_time_s": 0.119, + "replies": [], + "events_after": [ + "*1*1*06##", + "*#1*#1*15*1##" + ], + "note": "light_06_on_emits_group1_dim15_1" + }, + { + "run": "run1_lights_group_and_general", + "t": 4.501, + "step": "point_switch", + "dir": "tx", + "session": "cmd", + "frame": "*1*0*06##", + "is_status_request": false + }, + { + "run": "run1_lights_group_and_general", + "t": 4.619, + "step": "point_switch", + "dir": "rx", + "session": "cmd", + "frame": "*#*1##" + }, + { + "run": "run1_lights_group_and_general", + "t": 4.65, + "step": "point_switch", + "dir": "rx", + "session": "evt", + "frame": "*1*0*06##", + "ownd_class": "OWNLightingEvent", + "ownd_log": "Light 06 is switched off." + }, + { + "run": "run1_lights_group_and_general", + "t": 4.73, + "step": "point_switch", + "dir": "rx", + "session": "evt", + "frame": "*#1*#1*15*0##", + "ownd_class": "OWNLightingEvent", + "ownd_log": "Light/motion sensor #1 has sent an unknown dimension 15." + }, + { + "run": "run1_lights_group_and_general", + "t": 5.22, + "step": "point_switch", + "dir": "result", + "frame": "*1*0*06##", + "ack": true, + "reply_time_s": 0.118, + "replies": [], + "events_after": [ + "*1*0*06##", + "*#1*#1*15*0##" + ], + "note": "light_06_off_emits_group1_dim15_0" + }, + { + "run": "run1_lights_group_and_general", + "t": 5.22, + "step": "point_switch", + "dir": "tx", + "session": "cmd", + "frame": "*1*1*07##", + "is_status_request": false + }, + { + "run": "run1_lights_group_and_general", + "t": 5.3, + "step": "point_switch", + "dir": "rx", + "session": "cmd", + "frame": "*#*1##" + }, + { + "run": "run1_lights_group_and_general", + "t": 5.337, + "step": "point_switch", + "dir": "rx", + "session": "evt", + "frame": "*1*1*07##", + "ownd_class": "OWNLightingEvent", + "ownd_log": "Light 07 is switched on." + }, + { + "run": "run1_lights_group_and_general", + "t": 5.408, + "step": "point_switch", + "dir": "rx", + "session": "evt", + "frame": "*#1*#1*15*1##", + "ownd_class": "OWNLightingEvent", + "ownd_log": "Light/motion sensor #1 has sent an unknown dimension 15." + }, + { + "run": "run1_lights_group_and_general", + "t": 5.901, + "step": "point_switch", + "dir": "result", + "frame": "*1*1*07##", + "ack": true, + "reply_time_s": 0.08, + "replies": [], + "events_after": [ + "*1*1*07##", + "*#1*#1*15*1##" + ], + "note": "light_07_on_emits_group1_dim15_1" + }, + { + "run": "run1_lights_group_and_general", + "t": 5.901, + "step": "point_switch", + "dir": "tx", + "session": "cmd", + "frame": "*1*0*07##", + "is_status_request": false + }, + { + "run": "run1_lights_group_and_general", + "t": 6.01, + "step": "point_switch", + "dir": "rx", + "session": "cmd", + "frame": "*#*1##" + }, + { + "run": "run1_lights_group_and_general", + "t": 6.048, + "step": "point_switch", + "dir": "rx", + "session": "evt", + "frame": "*1*0*07##", + "ownd_class": "OWNLightingEvent", + "ownd_log": "Light 07 is switched off." + }, + { + "run": "run1_lights_group_and_general", + "t": 6.136, + "step": "point_switch", + "dir": "rx", + "session": "evt", + "frame": "*#1*#1*15*0##", + "ownd_class": "OWNLightingEvent", + "ownd_log": "Light/motion sensor #1 has sent an unknown dimension 15." + }, + { + "run": "run1_lights_group_and_general", + "t": 6.613, + "step": "point_switch", + "dir": "result", + "frame": "*1*0*07##", + "ack": true, + "reply_time_s": 0.109, + "replies": [], + "events_after": [ + "*1*0*07##", + "*#1*#1*15*0##" + ], + "note": "light_07_off_emits_group1_dim15_0" + }, + { + "run": "run1_lights_group_and_general", + "t": 6.613, + "step": "group_1", + "dir": "tx", + "session": "cmd", + "frame": "*#1*#1##", + "is_status_request": true + }, + { + "run": "run1_lights_group_and_general", + "t": 6.752, + "step": "group_1", + "dir": "rx", + "session": "evt", + "frame": "*1*0*06##", + "ownd_class": "OWNLightingEvent", + "ownd_log": "Light 06 is switched off." + }, + { + "run": "run1_lights_group_and_general", + "t": 6.807, + "step": "group_1", + "dir": "rx", + "session": "evt", + "frame": "*1*0*07##", + "ownd_class": "OWNLightingEvent", + "ownd_log": "Light 07 is switched off." + }, + { + "run": "run1_lights_group_and_general", + "t": 7.804, + "step": "group_1", + "dir": "rx", + "session": "cmd", + "frame": "*1*0*06##" + }, + { + "run": "run1_lights_group_and_general", + "t": 7.804, + "step": "group_1", + "dir": "rx", + "session": "cmd", + "frame": "*1*0*07##" + }, + { + "run": "run1_lights_group_and_general", + "t": 7.804, + "step": "group_1", + "dir": "rx", + "session": "cmd", + "frame": "*#*1##" + }, + { + "run": "run1_lights_group_and_general", + "t": 8.604, + "step": "group_1", + "dir": "result", + "frame": "*#1*#1##", + "ack": true, + "reply_time_s": 1.191, + "replies": [ + "*1*0*06##", + "*1*0*07##" + ], + "events_after": [ + "*1*0*06##", + "*1*0*07##" + ], + "note": "poll_group_1_returns_06_and_07" + }, + { + "run": "run1_lights_group_and_general", + "t": 8.605, + "step": "group_1", + "dir": "tx", + "session": "cmd", + "frame": "*1*1*#1##", + "is_status_request": false + }, + { + "run": "run1_lights_group_and_general", + "t": 8.738, + "step": "group_1", + "dir": "rx", + "session": "evt", + "frame": "*1*1*#1##", + "ownd_class": "OWNLightingEvent", + "ownd_log": "Light #1 is switched on." + }, + { + "run": "run1_lights_group_and_general", + "t": 8.738, + "step": "group_1", + "dir": "rx", + "session": "cmd", + "frame": "*#*1##" + }, + { + "run": "run1_lights_group_and_general", + "t": 8.786, + "step": "group_1", + "dir": "rx", + "session": "evt", + "frame": "*1*1*06##", + "ownd_class": "OWNLightingEvent", + "ownd_log": "Light 06 is switched on." + }, + { + "run": "run1_lights_group_and_general", + "t": 8.836, + "step": "group_1", + "dir": "rx", + "session": "evt", + "frame": "*#1*#1*15*1##", + "ownd_class": "OWNLightingEvent", + "ownd_log": "Light/motion sensor #1 has sent an unknown dimension 15." + }, + { + "run": "run1_lights_group_and_general", + "t": 8.898, + "step": "group_1", + "dir": "rx", + "session": "evt", + "frame": "*1*1*07##", + "ownd_class": "OWNLightingEvent", + "ownd_log": "Light 07 is switched on." + }, + { + "run": "run1_lights_group_and_general", + "t": 9.339, + "step": "group_1", + "dir": "result", + "frame": "*1*1*#1##", + "ack": true, + "reply_time_s": 0.133, + "replies": [], + "events_after": [ + "*1*1*#1##", + "*1*1*06##", + "*#1*#1*15*1##", + "*1*1*07##" + ], + "note": "group_1_on_emits_06_07_and_dim15_1" + }, + { + "run": "run1_lights_group_and_general", + "t": 9.339, + "step": "group_1", + "dir": "tx", + "session": "cmd", + "frame": "*1*0*#1##", + "is_status_request": false + }, + { + "run": "run1_lights_group_and_general", + "t": 9.447, + "step": "group_1", + "dir": "rx", + "session": "evt", + "frame": "*1*0*#1##", + "ownd_class": "OWNLightingEvent", + "ownd_log": "Light #1 is switched off." + }, + { + "run": "run1_lights_group_and_general", + "t": 9.449, + "step": "group_1", + "dir": "rx", + "session": "cmd", + "frame": "*#*1##" + }, + { + "run": "run1_lights_group_and_general", + "t": 9.505, + "step": "group_1", + "dir": "rx", + "session": "evt", + "frame": "*1*0*06##", + "ownd_class": "OWNLightingEvent", + "ownd_log": "Light 06 is switched off." + }, + { + "run": "run1_lights_group_and_general", + "t": 9.555, + "step": "group_1", + "dir": "rx", + "session": "evt", + "frame": "*1*0*07##", + "ownd_class": "OWNLightingEvent", + "ownd_log": "Light 07 is switched off." + }, + { + "run": "run1_lights_group_and_general", + "t": 9.606, + "step": "group_1", + "dir": "rx", + "session": "evt", + "frame": "*#1*#1*15*0##", + "ownd_class": "OWNLightingEvent", + "ownd_log": "Light/motion sensor #1 has sent an unknown dimension 15." + }, + { + "run": "run1_lights_group_and_general", + "t": 10.05, + "step": "group_1", + "dir": "result", + "frame": "*1*0*#1##", + "ack": true, + "reply_time_s": 0.11, + "replies": [], + "events_after": [ + "*1*0*#1##", + "*1*0*06##", + "*1*0*07##", + "*#1*#1*15*0##" + ], + "note": "group_1_off_emits_06_07_and_dim15_0" + }, + { + "run": "run1_lights_group_and_general", + "t": 10.05, + "step": "general_0", + "dir": "tx", + "session": "cmd", + "frame": "*1*1*0##", + "is_status_request": false + }, + { + "run": "run1_lights_group_and_general", + "t": 10.183, + "step": "general_0", + "dir": "rx", + "session": "evt", + "frame": "*1*1*0##", + "ownd_class": "OWNLightingEvent", + "ownd_log": "Light 0 is switched on." + }, + { + "run": "run1_lights_group_and_general", + "t": 10.184, + "step": "general_0", + "dir": "rx", + "session": "cmd", + "frame": "*#*1##" + }, + { + "run": "run1_lights_group_and_general", + "t": 10.245, + "step": "general_0", + "dir": "rx", + "session": "evt", + "frame": "*#1*#1*15*1##", + "ownd_class": "OWNLightingEvent", + "ownd_log": "Light/motion sensor #1 has sent an unknown dimension 15." + }, + { + "run": "run1_lights_group_and_general", + "t": 10.785, + "step": "general_0", + "dir": "result", + "frame": "*1*1*0##", + "ack": true, + "reply_time_s": 0.134, + "replies": [], + "events_after": [ + "*1*1*0##", + "*#1*#1*15*1##" + ], + "note": "general_on_emits_dim15_1_only" + }, + { + "run": "run1_lights_group_and_general", + "t": 10.785, + "step": "general_0", + "dir": "tx", + "session": "cmd", + "frame": "*#1*06##", + "is_status_request": true + }, + { + "run": "run1_lights_group_and_general", + "t": 10.91, + "step": "general_0", + "dir": "rx", + "session": "evt", + "frame": "*1*1*06##", + "ownd_class": "OWNLightingEvent", + "ownd_log": "Light 06 is switched on." + }, + { + "run": "run1_lights_group_and_general", + "t": 10.969, + "step": "general_0", + "dir": "rx", + "session": "cmd", + "frame": "*1*1*06##" + }, + { + "run": "run1_lights_group_and_general", + "t": 10.969, + "step": "general_0", + "dir": "rx", + "session": "cmd", + "frame": "*#*1##" + }, + { + "run": "run1_lights_group_and_general", + "t": 11.57, + "step": "general_0", + "dir": "result", + "frame": "*#1*06##", + "ack": true, + "reply_time_s": 0.184, + "replies": [ + "*1*1*06##" + ], + "events_after": [ + "*1*1*06##" + ], + "note": "resync_poll_06_after_general_on" + }, + { + "run": "run1_lights_group_and_general", + "t": 11.57, + "step": "general_0", + "dir": "tx", + "session": "cmd", + "frame": "*#1*07##", + "is_status_request": true + }, + { + "run": "run1_lights_group_and_general", + "t": 11.693, + "step": "general_0", + "dir": "rx", + "session": "evt", + "frame": "*1*1*07##", + "ownd_class": "OWNLightingEvent", + "ownd_log": "Light 07 is switched on." + }, + { + "run": "run1_lights_group_and_general", + "t": 11.749, + "step": "general_0", + "dir": "rx", + "session": "cmd", + "frame": "*1*1*07##" + }, + { + "run": "run1_lights_group_and_general", + "t": 11.749, + "step": "general_0", + "dir": "rx", + "session": "cmd", + "frame": "*#*1##" + }, + { + "run": "run1_lights_group_and_general", + "t": 12.349, + "step": "general_0", + "dir": "result", + "frame": "*#1*07##", + "ack": true, + "reply_time_s": 0.178, + "replies": [ + "*1*1*07##" + ], + "events_after": [ + "*1*1*07##" + ], + "note": "resync_poll_07_after_general_on" + }, + { + "run": "run1_lights_group_and_general", + "t": 12.349, + "step": "general_0", + "dir": "tx", + "session": "cmd", + "frame": "*1*0*0##", + "is_status_request": false + }, + { + "run": "run1_lights_group_and_general", + "t": 12.461, + "step": "general_0", + "dir": "rx", + "session": "evt", + "frame": "*1*0*0##", + "ownd_class": "OWNLightingEvent", + "ownd_log": "Light 0 is switched off." + }, + { + "run": "run1_lights_group_and_general", + "t": 12.463, + "step": "general_0", + "dir": "rx", + "session": "cmd", + "frame": "*#*1##" + }, + { + "run": "run1_lights_group_and_general", + "t": 12.541, + "step": "general_0", + "dir": "rx", + "session": "evt", + "frame": "*#1*#1*15*0##", + "ownd_class": "OWNLightingEvent", + "ownd_log": "Light/motion sensor #1 has sent an unknown dimension 15." + }, + { + "run": "run1_lights_group_and_general", + "t": 13.065, + "step": "general_0", + "dir": "result", + "frame": "*1*0*0##", + "ack": true, + "reply_time_s": 0.114, + "replies": [], + "events_after": [ + "*1*0*0##", + "*#1*#1*15*0##" + ], + "note": "general_off_emits_dim15_0_only" + }, + { + "run": "run1_lights_group_and_general", + "t": 13.065, + "step": "general_0", + "dir": "tx", + "session": "cmd", + "frame": "*#1*06##", + "is_status_request": true + }, + { + "run": "run1_lights_group_and_general", + "t": 13.205, + "step": "general_0", + "dir": "rx", + "session": "evt", + "frame": "*1*0*06##", + "ownd_class": "OWNLightingEvent", + "ownd_log": "Light 06 is switched off." + }, + { + "run": "run1_lights_group_and_general", + "t": 13.257, + "step": "general_0", + "dir": "rx", + "session": "cmd", + "frame": "*1*0*06##" + }, + { + "run": "run1_lights_group_and_general", + "t": 13.257, + "step": "general_0", + "dir": "rx", + "session": "cmd", + "frame": "*#*1##" + }, + { + "run": "run1_lights_group_and_general", + "t": 13.858, + "step": "general_0", + "dir": "result", + "frame": "*#1*06##", + "ack": true, + "reply_time_s": 0.192, + "replies": [ + "*1*0*06##" + ], + "events_after": [ + "*1*0*06##" + ], + "note": "resync_poll_06_after_general_off" + }, + { + "run": "run1_lights_group_and_general", + "t": 13.858, + "step": "general_0", + "dir": "tx", + "session": "cmd", + "frame": "*#1*07##", + "is_status_request": true + }, + { + "run": "run1_lights_group_and_general", + "t": 13.997, + "step": "general_0", + "dir": "rx", + "session": "evt", + "frame": "*1*0*07##", + "ownd_class": "OWNLightingEvent", + "ownd_log": "Light 07 is switched off." + }, + { + "run": "run1_lights_group_and_general", + "t": 14.052, + "step": "general_0", + "dir": "rx", + "session": "cmd", + "frame": "*1*0*07##" + }, + { + "run": "run1_lights_group_and_general", + "t": 14.052, + "step": "general_0", + "dir": "rx", + "session": "cmd", + "frame": "*#*1##" + }, + { + "run": "run1_lights_group_and_general", + "t": 14.653, + "step": "general_0", + "dir": "result", + "frame": "*#1*07##", + "ack": true, + "reply_time_s": 0.194, + "replies": [ + "*1*0*07##" + ], + "events_after": [ + "*1*0*07##" + ], + "note": "resync_poll_07_after_general_off" + }, + { + "run": "run1_lights_group_and_general", + "t": 14.653, + "step": "unpopulated_old_lights", + "dir": "tx", + "session": "cmd", + "frame": "*#1*01##", + "is_status_request": true + }, + { + "run": "run1_lights_group_and_general", + "t": 16.718, + "step": "unpopulated_old_lights", + "dir": "rx", + "session": "cmd", + "frame": "*#*0##" + }, + { + "run": "run1_lights_group_and_general", + "t": 17.319, + "step": "unpopulated_old_lights", + "dir": "result", + "frame": "*#1*01##", + "ack": false, + "reply_time_s": 2.065, + "replies": [], + "events_after": [], + "note": "unpopulated_old_light_01_nack" + }, + { + "run": "run1_lights_group_and_general", + "t": 17.319, + "step": "unpopulated_old_lights", + "dir": "tx", + "session": "cmd", + "frame": "*#1*04##", + "is_status_request": true + }, + { + "run": "run1_lights_group_and_general", + "t": 18.265, + "step": "unpopulated_old_lights", + "dir": "rx", + "session": "evt", + "frame": "*#13**#1*00*18*10*2026##", + "ownd_class": "OWNGatewayCommand", + "ownd_log": "Gateway broadcasting internal date: 2026-10-18." + }, + { + "run": "run1_lights_group_and_general", + "t": 18.313, + "step": "unpopulated_old_lights", + "dir": "rx", + "session": "evt", + "frame": "*#13**#0*22*50*00*##", + "ownd_class": "OWNGatewayCommand", + "ownd_log": "Gateway broadcasting internal time: 22:50:00." + }, + { + "run": "run1_lights_group_and_general", + "t": 19.393, + "step": "unpopulated_old_lights", + "dir": "rx", + "session": "cmd", + "frame": "*#*0##" + }, + { + "run": "run1_lights_group_and_general", + "t": 19.994, + "step": "unpopulated_old_lights", + "dir": "result", + "frame": "*#1*04##", + "ack": false, + "reply_time_s": 2.074, + "replies": [], + "events_after": [ + "*#13**#1*00*18*10*2026##", + "*#13**#0*22*50*00*##" + ], + "note": "unpopulated_old_light_04_nack" + }, + { + "run": "run2_shutter_05_travel_and_reversal", + "t": 19.994, + "step": "shutter_poll", + "dir": "tx", + "session": "cmd", + "frame": "*#2*05##", + "is_status_request": true + }, + { + "run": "run2_shutter_05_travel_and_reversal", + "t": 20.114, + "step": "shutter_poll", + "dir": "rx", + "session": "evt", + "frame": "*2*0*05##", + "ownd_class": "OWNAutomationEvent", + "ownd_log": "Cover 05 stopped." + }, + { + "run": "run2_shutter_05_travel_and_reversal", + "t": 20.171, + "step": "shutter_poll", + "dir": "rx", + "session": "cmd", + "frame": "*2*0*05##" + }, + { + "run": "run2_shutter_05_travel_and_reversal", + "t": 20.171, + "step": "shutter_poll", + "dir": "rx", + "session": "cmd", + "frame": "*#*1##" + }, + { + "run": "run2_shutter_05_travel_and_reversal", + "t": 20.772, + "step": "shutter_poll", + "dir": "result", + "frame": "*#2*05##", + "ack": true, + "reply_time_s": 0.177, + "replies": [ + "*2*0*05##" + ], + "events_after": [ + "*2*0*05##" + ], + "note": "initial_shutter_05_status" + }, + { + "run": "run2_shutter_05_travel_and_reversal", + "t": 20.772, + "step": "shutter_poll", + "dir": "tx", + "session": "cmd", + "frame": "*#2*05*10##", + "is_status_request": true + }, + { + "run": "run2_shutter_05_travel_and_reversal", + "t": 21.917, + "step": "shutter_poll", + "dir": "rx", + "session": "cmd", + "frame": "*#*1##" + }, + { + "run": "run2_shutter_05_travel_and_reversal", + "t": 22.518, + "step": "shutter_poll", + "dir": "result", + "frame": "*#2*05*10##", + "ack": true, + "reply_time_s": 1.145, + "replies": [], + "events_after": [], + "note": "shutter_05_dim10_query" + }, + { + "run": "run2_shutter_05_travel_and_reversal", + "t": 22.518, + "step": "partial_up", + "dir": "tx", + "session": "cmd", + "frame": "*2*1*05##", + "is_status_request": false + }, + { + "run": "run2_shutter_05_travel_and_reversal", + "t": 22.602, + "step": "partial_up", + "dir": "rx", + "session": "evt", + "frame": "*2*1000#0*05##", + "ownd_class": "OWNAutomationEvent", + "ownd_log": "*2*1000#0*05##" + }, + { + "run": "run2_shutter_05_travel_and_reversal", + "t": 22.67, + "step": "partial_up", + "dir": "rx", + "session": "evt", + "frame": "*2*0*05##", + "ownd_class": "OWNAutomationEvent", + "ownd_log": "Cover 05 stopped." + }, + { + "run": "run2_shutter_05_travel_and_reversal", + "t": 22.677, + "step": "partial_up", + "dir": "rx", + "session": "cmd", + "frame": "*#*1##" + }, + { + "run": "run2_shutter_05_travel_and_reversal", + "t": 22.72, + "step": "partial_up", + "dir": "rx", + "session": "evt", + "frame": "*2*1000#1*05##", + "ownd_class": "OWNAutomationEvent", + "ownd_log": "*2*1000#1*05##" + }, + { + "run": "run2_shutter_05_travel_and_reversal", + "t": 22.72, + "step": "partial_up", + "dir": "rx", + "session": "evt", + "frame": "*2*1*05##", + "ownd_class": "OWNAutomationEvent", + "ownd_log": "Cover 05 is opening." + }, + { + "run": "run2_shutter_05_travel_and_reversal", + "t": 24.68, + "step": "partial_up", + "dir": "result", + "frame": "*2*1*05##", + "ack": true, + "reply_time_s": 0.159, + "replies": [], + "events_after": [ + "*2*1000#0*05##", + "*2*0*05##", + "*2*1000#1*05##", + "*2*1*05##" + ], + "note": "raise_shutter_05_for_2s" + }, + { + "run": "run2_shutter_05_travel_and_reversal", + "t": 24.68, + "step": "partial_up", + "dir": "tx", + "session": "cmd", + "frame": "*2*0*05##", + "is_status_request": false + }, + { + "run": "run2_shutter_05_travel_and_reversal", + "t": 24.792, + "step": "partial_up", + "dir": "rx", + "session": "cmd", + "frame": "*#*1##" + }, + { + "run": "run2_shutter_05_travel_and_reversal", + "t": 24.792, + "step": "partial_up", + "dir": "rx", + "session": "evt", + "frame": "*2*1000#0*05##", + "ownd_class": "OWNAutomationEvent", + "ownd_log": "*2*1000#0*05##" + }, + { + "run": "run2_shutter_05_travel_and_reversal", + "t": 24.852, + "step": "partial_up", + "dir": "rx", + "session": "evt", + "frame": "*2*0*05##", + "ownd_class": "OWNAutomationEvent", + "ownd_log": "Cover 05 stopped." + }, + { + "run": "run2_shutter_05_travel_and_reversal", + "t": 25.392, + "step": "partial_up", + "dir": "result", + "frame": "*2*0*05##", + "ack": true, + "reply_time_s": 0.112, + "replies": [], + "events_after": [ + "*2*1000#0*05##", + "*2*0*05##" + ], + "note": "stop_shutter_05_after_2s" + }, + { + "run": "run2_shutter_05_travel_and_reversal", + "t": 25.392, + "step": "partial_up", + "dir": "tx", + "session": "cmd", + "frame": "*#2*05##", + "is_status_request": true + }, + { + "run": "run2_shutter_05_travel_and_reversal", + "t": 25.526, + "step": "partial_up", + "dir": "rx", + "session": "evt", + "frame": "*2*0*05##", + "ownd_class": "OWNAutomationEvent", + "ownd_log": "Cover 05 stopped." + }, + { + "run": "run2_shutter_05_travel_and_reversal", + "t": 25.581, + "step": "partial_up", + "dir": "rx", + "session": "cmd", + "frame": "*2*0*05##" + }, + { + "run": "run2_shutter_05_travel_and_reversal", + "t": 25.581, + "step": "partial_up", + "dir": "rx", + "session": "cmd", + "frame": "*#*1##" + }, + { + "run": "run2_shutter_05_travel_and_reversal", + "t": 26.182, + "step": "partial_up", + "dir": "result", + "frame": "*#2*05##", + "ack": true, + "reply_time_s": 0.189, + "replies": [ + "*2*0*05##" + ], + "events_after": [ + "*2*0*05##" + ], + "note": "poll_shutter_05_after_stop" + }, + { + "run": "run2_shutter_05_travel_and_reversal", + "t": 26.182, + "step": "reversal_and_autostop", + "dir": "tx", + "session": "cmd", + "frame": "*2*1*05##", + "is_status_request": false + }, + { + "run": "run2_shutter_05_travel_and_reversal", + "t": 26.275, + "step": "reversal_and_autostop", + "dir": "rx", + "session": "evt", + "frame": "*2*1000#0*05##", + "ownd_class": "OWNAutomationEvent", + "ownd_log": "*2*1000#0*05##" + }, + { + "run": "run2_shutter_05_travel_and_reversal", + "t": 26.325, + "step": "reversal_and_autostop", + "dir": "rx", + "session": "evt", + "frame": "*2*0*05##", + "ownd_class": "OWNAutomationEvent", + "ownd_log": "Cover 05 stopped." + }, + { + "run": "run2_shutter_05_travel_and_reversal", + "t": 26.358, + "step": "reversal_and_autostop", + "dir": "rx", + "session": "cmd", + "frame": "*#*1##" + }, + { + "run": "run2_shutter_05_travel_and_reversal", + "t": 26.373, + "step": "reversal_and_autostop", + "dir": "rx", + "session": "evt", + "frame": "*2*1000#1*05##", + "ownd_class": "OWNAutomationEvent", + "ownd_log": "*2*1000#1*05##" + }, + { + "run": "run2_shutter_05_travel_and_reversal", + "t": 26.421, + "step": "reversal_and_autostop", + "dir": "rx", + "session": "evt", + "frame": "*2*1*05##", + "ownd_class": "OWNAutomationEvent", + "ownd_log": "Cover 05 is opening." + }, + { + "run": "run2_shutter_05_travel_and_reversal", + "t": 27.86, + "step": "reversal_and_autostop", + "dir": "result", + "frame": "*2*1*05##", + "ack": true, + "reply_time_s": 0.175, + "replies": [], + "events_after": [ + "*2*1000#0*05##", + "*2*0*05##", + "*2*1000#1*05##", + "*2*1*05##" + ], + "note": "raise_shutter_05_before_reversal" + }, + { + "run": "run2_shutter_05_travel_and_reversal", + "t": 27.86, + "step": "reversal_and_autostop", + "dir": "tx", + "session": "cmd", + "frame": "*2*2*05##", + "is_status_request": false + }, + { + "run": "run2_shutter_05_travel_and_reversal", + "t": 27.964, + "step": "reversal_and_autostop", + "dir": "rx", + "session": "evt", + "frame": "*2*1000#0*05##", + "ownd_class": "OWNAutomationEvent", + "ownd_log": "*2*1000#0*05##" + }, + { + "run": "run2_shutter_05_travel_and_reversal", + "t": 28.014, + "step": "reversal_and_autostop", + "dir": "rx", + "session": "evt", + "frame": "*2*0*05##", + "ownd_class": "OWNAutomationEvent", + "ownd_log": "Cover 05 stopped." + }, + { + "run": "run2_shutter_05_travel_and_reversal", + "t": 28.04, + "step": "reversal_and_autostop", + "dir": "rx", + "session": "cmd", + "frame": "*#*1##" + }, + { + "run": "run2_shutter_05_travel_and_reversal", + "t": 28.072, + "step": "reversal_and_autostop", + "dir": "rx", + "session": "evt", + "frame": "*2*1000#2*05##", + "ownd_class": "OWNAutomationEvent", + "ownd_log": "*2*1000#2*05##" + }, + { + "run": "run2_shutter_05_travel_and_reversal", + "t": 28.119, + "step": "reversal_and_autostop", + "dir": "rx", + "session": "evt", + "frame": "*2*2*05##", + "ownd_class": "OWNAutomationEvent", + "ownd_log": "Cover 05 is closing." + }, + { + "run": "run2_shutter_05_travel_and_reversal", + "t": 34.341, + "step": "reversal_and_autostop", + "dir": "rx", + "session": "evt", + "frame": "*2*0*05##", + "ownd_class": "OWNAutomationEvent", + "ownd_log": "Cover 05 stopped." + }, + { + "run": "run2_shutter_05_travel_and_reversal", + "t": 34.54, + "step": "reversal_and_autostop", + "dir": "result", + "frame": "*2*2*05##", + "ack": true, + "reply_time_s": 0.18, + "replies": [], + "events_after": [ + "*2*1000#0*05##", + "*2*0*05##", + "*2*1000#2*05##", + "*2*2*05##", + "*2*0*05##" + ], + "note": "reverse_to_down_and_wait_for_5s_hardware_autostop" + }, + { + "run": "run2_shutter_05_travel_and_reversal", + "t": 34.54, + "step": "reversal_and_autostop", + "dir": "tx", + "session": "cmd", + "frame": "*#2*05##", + "is_status_request": true + }, + { + "run": "run2_shutter_05_travel_and_reversal", + "t": 34.657, + "step": "reversal_and_autostop", + "dir": "rx", + "session": "evt", + "frame": "*2*0*05##", + "ownd_class": "OWNAutomationEvent", + "ownd_log": "Cover 05 stopped." + }, + { + "run": "run2_shutter_05_travel_and_reversal", + "t": 34.718, + "step": "reversal_and_autostop", + "dir": "rx", + "session": "cmd", + "frame": "*2*0*05##" + }, + { + "run": "run2_shutter_05_travel_and_reversal", + "t": 34.718, + "step": "reversal_and_autostop", + "dir": "rx", + "session": "cmd", + "frame": "*#*1##" + }, + { + "run": "run2_shutter_05_travel_and_reversal", + "t": 35.319, + "step": "reversal_and_autostop", + "dir": "result", + "frame": "*#2*05##", + "ack": true, + "reply_time_s": 0.178, + "replies": [ + "*2*0*05##" + ], + "events_after": [ + "*2*0*05##" + ], + "note": "poll_shutter_05_after_hardware_autostop" + }, + { + "run": "run3_climate_zone2", + "t": 35.319, + "step": "zone2_poll", + "dir": "tx", + "session": "cmd", + "frame": "*#4*2##", + "is_status_request": true + }, + { + "run": "run3_climate_zone2", + "t": 35.953, + "step": "zone2_poll", + "dir": "rx", + "session": "evt", + "frame": "*#4*2*0*0249##", + "ownd_class": "OWNHeatingEvent", + "ownd_log": "Zone 2's main sensor is reporting a temperature of 24.9\u00b0C." + }, + { + "run": "run3_climate_zone2", + "t": 36.277, + "step": "zone2_poll", + "dir": "rx", + "session": "evt", + "frame": "*#4*2*12*0245*3##", + "ownd_class": "OWNHeatingEvent", + "ownd_log": "Zone 2's local target temperature is set to 24.5\u00b0C." + }, + { + "run": "run3_climate_zone2", + "t": 36.323, + "step": "zone2_poll", + "dir": "rx", + "session": "evt", + "frame": "*4*1*2##", + "ownd_class": "OWNHeatingEvent", + "ownd_log": "Zone 2's season is heating." + }, + { + "run": "run3_climate_zone2", + "t": 36.598, + "step": "zone2_poll", + "dir": "rx", + "session": "evt", + "frame": "*#4*2*14*0245*3##", + "ownd_class": "OWNHeatingEvent", + "ownd_log": "Zone 2's target temperature is set to 24.5\u00b0C." + }, + { + "run": "run3_climate_zone2", + "t": 36.691, + "step": "zone2_poll", + "dir": "rx", + "session": "evt", + "frame": "*#4*2*13*00##", + "ownd_class": "OWNHeatingEvent", + "ownd_log": "Zone 2's local offset is set to 0\u00b0C." + }, + { + "run": "run3_climate_zone2", + "t": 36.751, + "step": "zone2_poll", + "dir": "rx", + "session": "cmd", + "frame": "*#4*2*0*0249##" + }, + { + "run": "run3_climate_zone2", + "t": 36.751, + "step": "zone2_poll", + "dir": "rx", + "session": "cmd", + "frame": "*#4*2*12*0245*3##" + }, + { + "run": "run3_climate_zone2", + "t": 36.751, + "step": "zone2_poll", + "dir": "rx", + "session": "cmd", + "frame": "*4*1*2##" + }, + { + "run": "run3_climate_zone2", + "t": 36.751, + "step": "zone2_poll", + "dir": "rx", + "session": "cmd", + "frame": "*#4*2*14*0245*3##" + }, + { + "run": "run3_climate_zone2", + "t": 36.751, + "step": "zone2_poll", + "dir": "rx", + "session": "cmd", + "frame": "*#4*2*13*00##" + }, + { + "run": "run3_climate_zone2", + "t": 36.751, + "step": "zone2_poll", + "dir": "rx", + "session": "cmd", + "frame": "*#*1##" + }, + { + "run": "run3_climate_zone2", + "t": 37.352, + "step": "zone2_poll", + "dir": "result", + "frame": "*#4*2##", + "ack": true, + "reply_time_s": 1.432, + "replies": [ + "*#4*2*0*0249##", + "*#4*2*12*0245*3##", + "*4*1*2##", + "*#4*2*14*0245*3##", + "*#4*2*13*00##" + ], + "events_after": [ + "*#4*2*0*0249##", + "*#4*2*12*0245*3##", + "*4*1*2##", + "*#4*2*14*0245*3##", + "*#4*2*13*00##" + ], + "note": "zone2_full_status_5_frames" + }, + { + "run": "run3_climate_zone2", + "t": 37.352, + "step": "zone2_poll", + "dir": "tx", + "session": "cmd", + "frame": "*#4*2*0##", + "is_status_request": true + }, + { + "run": "run3_climate_zone2", + "t": 37.492, + "step": "zone2_poll", + "dir": "rx", + "session": "evt", + "frame": "*#4*2*0*0249##", + "ownd_class": "OWNHeatingEvent", + "ownd_log": "Zone 2's main sensor is reporting a temperature of 24.9\u00b0C." + }, + { + "run": "run3_climate_zone2", + "t": 37.546, + "step": "zone2_poll", + "dir": "rx", + "session": "cmd", + "frame": "*#4*2*0*0249##" + }, + { + "run": "run3_climate_zone2", + "t": 37.546, + "step": "zone2_poll", + "dir": "rx", + "session": "cmd", + "frame": "*#*1##" + }, + { + "run": "run3_climate_zone2", + "t": 38.146, + "step": "zone2_poll", + "dir": "result", + "frame": "*#4*2*0##", + "ack": true, + "reply_time_s": 0.193, + "replies": [ + "*#4*2*0*0249##" + ], + "events_after": [ + "*#4*2*0*0249##" + ], + "note": "zone2_temperature_dim0" + }, + { + "run": "run3_climate_zone2", + "t": 38.147, + "step": "zone2_poll", + "dir": "tx", + "session": "cmd", + "frame": "*#4*2*19##", + "is_status_request": true + }, + { + "run": "run3_climate_zone2", + "t": 38.325, + "step": "zone2_poll", + "dir": "rx", + "session": "evt", + "frame": "*#4*2*19*5*0##", + "ownd_class": "OWNHeatingEvent", + "ownd_log": "Zone 2's cooling fan is off; heating valve is off." + }, + { + "run": "run3_climate_zone2", + "t": 38.389, + "step": "zone2_poll", + "dir": "rx", + "session": "cmd", + "frame": "*#4*2*19*5*0##" + }, + { + "run": "run3_climate_zone2", + "t": 38.389, + "step": "zone2_poll", + "dir": "rx", + "session": "cmd", + "frame": "*#*1##" + }, + { + "run": "run3_climate_zone2", + "t": 38.989, + "step": "zone2_poll", + "dir": "result", + "frame": "*#4*2*19##", + "ack": true, + "reply_time_s": 0.242, + "replies": [ + "*#4*2*19*5*0##" + ], + "events_after": [ + "*#4*2*19*5*0##" + ], + "note": "zone2_fancoil_valve_dim19" + }, + { + "run": "run3_climate_zone2", + "t": 38.989, + "step": "zone2_poll", + "dir": "tx", + "session": "cmd", + "frame": "*#4*2*20##", + "is_status_request": true + }, + { + "run": "run3_climate_zone2", + "t": 39.14, + "step": "zone2_poll", + "dir": "rx", + "session": "evt", + "frame": "*#4*2#1*20*0##", + "ownd_class": "OWNHeatingEvent", + "ownd_log": "Zone 2's actuator 1 is off." + }, + { + "run": "run3_climate_zone2", + "t": 39.189, + "step": "zone2_poll", + "dir": "rx", + "session": "evt", + "frame": "*#4*2#2*20*5##", + "ownd_class": "OWNHeatingEvent", + "ownd_log": "Zone 2's fan is off." + }, + { + "run": "run3_climate_zone2", + "t": 39.194, + "step": "zone2_poll", + "dir": "rx", + "session": "cmd", + "frame": "*#4*2#1*20*0##" + }, + { + "run": "run3_climate_zone2", + "t": 39.194, + "step": "zone2_poll", + "dir": "rx", + "session": "cmd", + "frame": "*#*1##" + }, + { + "run": "run3_climate_zone2", + "t": 39.795, + "step": "zone2_poll", + "dir": "result", + "frame": "*#4*2*20##", + "ack": true, + "reply_time_s": 0.205, + "replies": [ + "*#4*2#1*20*0##" + ], + "events_after": [ + "*#4*2#1*20*0##", + "*#4*2#2*20*5##" + ], + "note": "zone2_actuator_dim20" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 39.795, + "step": "who1_expose_populated", + "dir": "tx", + "session": "cmd", + "frame": "*1*1*06##", + "is_status_request": true + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 39.909, + "step": "who1_expose_populated", + "dir": "rx", + "session": "cmd", + "frame": "*#*1##" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 39.94, + "step": "who1_expose_populated", + "dir": "rx", + "session": "evt", + "frame": "*1*1*06##", + "ownd_class": "OWNLightingEvent", + "ownd_log": "Light 06 is switched on." + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 40.019, + "step": "who1_expose_populated", + "dir": "rx", + "session": "evt", + "frame": "*#1*#1*15*1##", + "ownd_class": "OWNLightingEvent", + "ownd_log": "Light/motion sensor #1 has sent an unknown dimension 15." + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 40.511, + "step": "who1_expose_populated", + "dir": "result", + "frame": "*1*1*06##", + "ack": true, + "reply_time_s": 0.114, + "replies": [], + "events_after": [ + "*1*1*06##", + "*#1*#1*15*1##" + ], + "note": "expose_who1_on_populated_06" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 40.511, + "step": "who1_expose_populated", + "dir": "tx", + "session": "cmd", + "frame": "*1*0*06##", + "is_status_request": true + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 40.606, + "step": "who1_expose_populated", + "dir": "rx", + "session": "cmd", + "frame": "*#*1##" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 40.645, + "step": "who1_expose_populated", + "dir": "rx", + "session": "evt", + "frame": "*1*0*06##", + "ownd_class": "OWNLightingEvent", + "ownd_log": "Light 06 is switched off." + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 40.732, + "step": "who1_expose_populated", + "dir": "rx", + "session": "evt", + "frame": "*#1*#1*15*0##", + "ownd_class": "OWNLightingEvent", + "ownd_log": "Light/motion sensor #1 has sent an unknown dimension 15." + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 41.208, + "step": "who1_expose_populated", + "dir": "result", + "frame": "*1*0*06##", + "ack": true, + "reply_time_s": 0.095, + "replies": [], + "events_after": [ + "*1*0*06##", + "*#1*#1*15*0##" + ], + "note": "expose_who1_off_populated_06" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 41.208, + "step": "who1_expose_virtual", + "dir": "tx", + "session": "cmd", + "frame": "*1*1*08##", + "is_status_request": true + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 43.269, + "step": "who1_expose_virtual", + "dir": "rx", + "session": "cmd", + "frame": "*#*0##" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 43.87, + "step": "who1_expose_virtual", + "dir": "result", + "frame": "*1*1*08##", + "ack": false, + "reply_time_s": 2.061, + "replies": [], + "events_after": [], + "note": "expose_who1_on_virtual_08_no_retry" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 43.87, + "step": "who1_expose_virtual", + "dir": "tx", + "session": "cmd", + "frame": "*1*0*08##", + "is_status_request": true + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 45.932, + "step": "who1_expose_virtual", + "dir": "rx", + "session": "cmd", + "frame": "*#*0##" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 46.533, + "step": "who1_expose_virtual", + "dir": "result", + "frame": "*1*0*08##", + "ack": false, + "reply_time_s": 2.062, + "replies": [], + "events_after": [], + "note": "expose_who1_off_virtual_08_no_retry" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 46.533, + "step": "who1_expose_virtual", + "dir": "tx", + "session": "cmd", + "frame": "*1*5*08##", + "is_status_request": true + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 48.595, + "step": "who1_expose_virtual", + "dir": "rx", + "session": "cmd", + "frame": "*#*0##" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 49.183, + "step": "who1_expose_virtual", + "dir": "result", + "frame": "*1*5*08##", + "ack": false, + "reply_time_s": 2.062, + "replies": [], + "events_after": [], + "note": "expose_who1_dim_preset_virtual_08" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 49.184, + "step": "who1_expose_virtual", + "dir": "tx", + "session": "cmd", + "frame": "*#1*08*1*150*0##", + "is_status_request": true + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 49.238, + "step": "who1_expose_virtual", + "dir": "rx", + "session": "cmd", + "frame": "*#*0##" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 49.839, + "step": "who1_expose_virtual", + "dir": "result", + "frame": "*#1*08*1*150*0##", + "ack": false, + "reply_time_s": 0.055, + "replies": [], + "events_after": [], + "note": "expose_who1_report_brightness_virtual_08" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 49.839, + "step": "who1_expose_virtual", + "dir": "tx", + "session": "cmd", + "frame": "*#1*08*#1*150*0##", + "is_status_request": false + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 49.965, + "step": "who1_expose_virtual", + "dir": "rx", + "session": "cmd", + "frame": "*#*1##" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 50.566, + "step": "who1_expose_virtual", + "dir": "result", + "frame": "*#1*08*#1*150*0##", + "ack": true, + "reply_time_s": 0.126, + "replies": [], + "events_after": [], + "note": "expose_who1_write_brightness_virtual_08_acked" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 50.566, + "step": "who1_expose_virtual", + "dir": "tx", + "session": "cmd", + "frame": "*#1*08##", + "is_status_request": true + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 52.621, + "step": "who1_expose_virtual", + "dir": "rx", + "session": "cmd", + "frame": "*#*0##" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 53.222, + "step": "who1_expose_virtual", + "dir": "result", + "frame": "*#1*08##", + "ack": false, + "reply_time_s": 2.055, + "replies": [], + "events_after": [], + "note": "query_who1_status_virtual_08" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 53.222, + "step": "who1_expose_virtual", + "dir": "tx", + "session": "cmd", + "frame": "*#1*08*1##", + "is_status_request": true + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 55.279, + "step": "who1_expose_virtual", + "dir": "rx", + "session": "cmd", + "frame": "*#*0##" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 55.879, + "step": "who1_expose_virtual", + "dir": "result", + "frame": "*#1*08*1##", + "ack": false, + "reply_time_s": 2.056, + "replies": [], + "events_after": [], + "note": "query_who1_dim1_virtual_08" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 55.88, + "step": "who2_expose_virtual", + "dir": "tx", + "session": "cmd", + "frame": "*2*1*25##", + "is_status_request": true + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 56.186, + "step": "who2_expose_virtual", + "dir": "rx", + "session": "evt", + "frame": "*2*1000#0*25##", + "ownd_class": "OWNAutomationEvent", + "ownd_log": "*2*1000#0*25##" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 56.416, + "step": "who2_expose_virtual", + "dir": "rx", + "session": "evt", + "frame": "*2*1000#1*25##", + "ownd_class": "OWNAutomationEvent", + "ownd_log": "*2*1000#1*25##" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 56.418, + "step": "who2_expose_virtual", + "dir": "rx", + "session": "cmd", + "frame": "*#*0##" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 57.219, + "step": "who2_expose_virtual", + "dir": "result", + "frame": "*2*1*25##", + "ack": false, + "reply_time_s": 0.539, + "replies": [], + "events_after": [ + "*2*1000#0*25##", + "*2*1000#1*25##" + ], + "note": "expose_who2_raise_virtual_25_emits_premove_stop_and_up_on_evt" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 57.219, + "step": "who2_expose_virtual", + "dir": "tx", + "session": "cmd", + "frame": "*2*2*25##", + "is_status_request": true + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 57.553, + "step": "who2_expose_virtual", + "dir": "rx", + "session": "evt", + "frame": "*2*1000#0*25##", + "ownd_class": "OWNAutomationEvent", + "ownd_log": "*2*1000#0*25##" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 57.833, + "step": "who2_expose_virtual", + "dir": "rx", + "session": "evt", + "frame": "*2*1000#2*25##", + "ownd_class": "OWNAutomationEvent", + "ownd_log": "*2*1000#2*25##" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 57.835, + "step": "who2_expose_virtual", + "dir": "rx", + "session": "cmd", + "frame": "*#*0##" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 58.636, + "step": "who2_expose_virtual", + "dir": "result", + "frame": "*2*2*25##", + "ack": false, + "reply_time_s": 0.615, + "replies": [], + "events_after": [ + "*2*1000#0*25##", + "*2*1000#2*25##" + ], + "note": "expose_who2_lower_virtual_25_emits_premove_stop_and_down_on_evt" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 58.636, + "step": "who2_expose_virtual", + "dir": "tx", + "session": "cmd", + "frame": "*2*0*25##", + "is_status_request": true + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 58.946, + "step": "who2_expose_virtual", + "dir": "rx", + "session": "evt", + "frame": "*2*1000#0*25##", + "ownd_class": "OWNAutomationEvent", + "ownd_log": "*2*1000#0*25##" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 58.948, + "step": "who2_expose_virtual", + "dir": "rx", + "session": "cmd", + "frame": "*#*0##" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 59.749, + "step": "who2_expose_virtual", + "dir": "result", + "frame": "*2*0*25##", + "ack": false, + "reply_time_s": 0.312, + "replies": [], + "events_after": [ + "*2*1000#0*25##" + ], + "note": "expose_who2_stop_virtual_25_emits_stop_translation_on_evt" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 59.749, + "step": "who2_expose_virtual", + "dir": "tx", + "session": "cmd", + "frame": "*#2*25*10*10*50*0*0##", + "is_status_request": true + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 59.805, + "step": "who2_expose_virtual", + "dir": "rx", + "session": "cmd", + "frame": "*#*0##" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 60.406, + "step": "who2_expose_virtual", + "dir": "result", + "frame": "*#2*25*10*10*50*0*0##", + "ack": false, + "reply_time_s": 0.055, + "replies": [], + "events_after": [], + "note": "expose_who2_report_level_virtual_25" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 60.406, + "step": "who2_expose_virtual", + "dir": "tx", + "session": "cmd", + "frame": "*#2*25##", + "is_status_request": true + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 60.717, + "step": "who2_expose_virtual", + "dir": "rx", + "session": "cmd", + "frame": "*#*0##" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 61.317, + "step": "who2_expose_virtual", + "dir": "result", + "frame": "*#2*25##", + "ack": false, + "reply_time_s": 0.311, + "replies": [], + "events_after": [], + "note": "query_who2_status_virtual_25" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 61.317, + "step": "who2_expose_virtual", + "dir": "tx", + "session": "cmd", + "frame": "*#2*25*10##", + "is_status_request": true + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 62.456, + "step": "who2_expose_virtual", + "dir": "rx", + "session": "cmd", + "frame": "*#*1##" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 63.056, + "step": "who2_expose_virtual", + "dir": "result", + "frame": "*#2*25*10##", + "ack": true, + "reply_time_s": 1.138, + "replies": [], + "events_after": [], + "note": "query_who2_dim10_virtual_25_acked" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 63.056, + "step": "who4_expose_probe", + "dir": "tx", + "session": "cmd", + "frame": "*#4*2*0*0999##", + "is_status_request": true + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 63.229, + "step": "who4_expose_probe", + "dir": "rx", + "session": "evt", + "frame": "*#4*2*0*0249##", + "ownd_class": "OWNHeatingEvent", + "ownd_log": "Zone 2's main sensor is reporting a temperature of 24.9\u00b0C." + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 63.277, + "step": "who4_expose_probe", + "dir": "rx", + "session": "cmd", + "frame": "*#4*2*0*0249##" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 63.277, + "step": "who4_expose_probe", + "dir": "rx", + "session": "cmd", + "frame": "*#*1##" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 63.878, + "step": "who4_expose_probe", + "dir": "result", + "frame": "*#4*2*0*0999##", + "ack": true, + "reply_time_s": 0.22, + "replies": [ + "*#4*2*0*0249##" + ], + "events_after": [ + "*#4*2*0*0249##" + ], + "note": "who4_dim0_without_hash_parsed_as_read_on_zone2" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 63.878, + "step": "who4_expose_probe", + "dir": "tx", + "session": "cmd", + "frame": "*#4*50*0*0215##", + "is_status_request": true + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 66.011, + "step": "who4_expose_probe", + "dir": "rx", + "session": "cmd", + "frame": "*#*0##" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 66.612, + "step": "who4_expose_probe", + "dir": "result", + "frame": "*#4*50*0*0215##", + "ack": false, + "reply_time_s": 2.134, + "replies": [], + "events_after": [], + "note": "expose_who4_report_temp_50_nack_timeout" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 66.612, + "step": "who4_expose_probe", + "dir": "tx", + "session": "cmd", + "frame": "*#4*50*#0*0215##", + "is_status_request": false + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 66.768, + "step": "who4_expose_probe", + "dir": "rx", + "session": "cmd", + "frame": "*#*1##" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 67.369, + "step": "who4_expose_probe", + "dir": "result", + "frame": "*#4*50*#0*0215##", + "ack": true, + "reply_time_s": 0.155, + "replies": [], + "events_after": [], + "note": "expose_who4_write_temp_50_acked_156ms" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 67.369, + "step": "who4_expose_probe", + "dir": "tx", + "session": "cmd", + "frame": "*#4*000*0*0215##", + "is_status_request": true + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 69.437, + "step": "who4_expose_probe", + "dir": "rx", + "session": "cmd", + "frame": "*#*0##" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 70.037, + "step": "who4_expose_probe", + "dir": "result", + "frame": "*#4*000*0*0215##", + "ack": false, + "reply_time_s": 2.068, + "replies": [], + "events_after": [], + "note": "expose_who4_report_temp_000_nack" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 70.037, + "step": "who4_expose_probe", + "dir": "tx", + "session": "cmd", + "frame": "*#4*102*0*0215##", + "is_status_request": true + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 72.155, + "step": "who4_expose_probe", + "dir": "rx", + "session": "cmd", + "frame": "*#*0##" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 72.757, + "step": "who4_expose_probe", + "dir": "result", + "frame": "*#4*102*0*0215##", + "ack": false, + "reply_time_s": 2.118, + "replies": [], + "events_after": [], + "note": "expose_who4_report_temp_102_nack" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 72.757, + "step": "who4_expose_probe", + "dir": "tx", + "session": "cmd", + "frame": "*#4*50*0##", + "is_status_request": true + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 74.888, + "step": "who4_expose_probe", + "dir": "rx", + "session": "cmd", + "frame": "*#*0##" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 75.489, + "step": "who4_expose_probe", + "dir": "result", + "frame": "*#4*50*0##", + "ack": false, + "reply_time_s": 2.131, + "replies": [], + "events_after": [], + "note": "query_who4_dim0_virtual_50" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 75.489, + "step": "who4_expose_probe", + "dir": "tx", + "session": "cmd", + "frame": "*#4*000*0##", + "is_status_request": true + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 77.555, + "step": "who4_expose_probe", + "dir": "rx", + "session": "cmd", + "frame": "*#*0##" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 78.155, + "step": "who4_expose_probe", + "dir": "result", + "frame": "*#4*000*0##", + "ack": false, + "reply_time_s": 2.066, + "replies": [], + "events_after": [], + "note": "query_who4_dim0_virtual_000" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 78.155, + "step": "who13_expose_clock", + "dir": "tx", + "session": "cmd", + "frame": "*#13**0##", + "is_status_request": true + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 78.212, + "step": "who13_expose_clock", + "dir": "rx", + "session": "evt", + "frame": "*#13**0*22*51*00*999##", + "ownd_class": "OWNGatewayEvent", + "ownd_log": "Gateway's internal time is: 22:51:00." + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 78.266, + "step": "who13_expose_clock", + "dir": "rx", + "session": "cmd", + "frame": "*#13**0*22*51*00*999##" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 78.266, + "step": "who13_expose_clock", + "dir": "rx", + "session": "cmd", + "frame": "*#*1##" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 78.867, + "step": "who13_expose_clock", + "dir": "result", + "frame": "*#13**0##", + "ack": true, + "reply_time_s": 0.111, + "replies": [ + "*#13**0*22*51*00*999##" + ], + "events_after": [ + "*#13**0*22*51*00*999##" + ], + "note": "query_who13_time" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 78.867, + "step": "who13_expose_clock", + "dir": "tx", + "session": "cmd", + "frame": "*#13**1##", + "is_status_request": true + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 78.908, + "step": "who13_expose_clock", + "dir": "rx", + "session": "evt", + "frame": "*#13**1*00*18*10*2026##", + "ownd_class": "OWNGatewayEvent", + "ownd_log": "Gateway's internal date is: 2026-10-18." + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 78.964, + "step": "who13_expose_clock", + "dir": "rx", + "session": "cmd", + "frame": "*#13**1*00*18*10*2026##" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 78.964, + "step": "who13_expose_clock", + "dir": "rx", + "session": "cmd", + "frame": "*#*1##" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 79.565, + "step": "who13_expose_clock", + "dir": "result", + "frame": "*#13**1##", + "ack": true, + "reply_time_s": 0.097, + "replies": [ + "*#13**1*00*18*10*2026##" + ], + "events_after": [ + "*#13**1*00*18*10*2026##" + ], + "note": "query_who13_date" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 79.565, + "step": "who13_expose_clock", + "dir": "tx", + "session": "cmd", + "frame": "*#13**22##", + "is_status_request": true + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 79.619, + "step": "who13_expose_clock", + "dir": "rx", + "session": "evt", + "frame": "*#13**22*22*51*02*999*00*18*10*2026##", + "ownd_class": "OWNGatewayEvent", + "ownd_log": "Gateway's internal datetime is: 2026-10-18 22:51:02." + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 79.682, + "step": "who13_expose_clock", + "dir": "rx", + "session": "cmd", + "frame": "*#13**22*22*51*02*999*00*18*10*2026##" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 79.682, + "step": "who13_expose_clock", + "dir": "rx", + "session": "cmd", + "frame": "*#*1##" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 80.282, + "step": "who13_expose_clock", + "dir": "result", + "frame": "*#13**22##", + "ack": true, + "reply_time_s": 0.117, + "replies": [ + "*#13**22*22*51*02*999*00*18*10*2026##" + ], + "events_after": [ + "*#13**22*22*51*02*999*00*18*10*2026##" + ], + "note": "query_who13_datetime" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 80.282, + "step": "who13_expose_clock", + "dir": "tx", + "session": "cmd", + "frame": "*#13**0*22*51*00*999##", + "is_status_request": true + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 80.337, + "step": "who13_expose_clock", + "dir": "rx", + "session": "evt", + "frame": "*#13**0*22*51*02*999##", + "ownd_class": "OWNGatewayEvent", + "ownd_log": "Gateway's internal time is: 22:51:02." + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 80.398, + "step": "who13_expose_clock", + "dir": "rx", + "session": "cmd", + "frame": "*#13**0*22*51*02*999##" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 80.398, + "step": "who13_expose_clock", + "dir": "rx", + "session": "cmd", + "frame": "*#*1##" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 81.199, + "step": "who13_expose_clock", + "dir": "result", + "frame": "*#13**0*22*51*00*999##", + "ack": true, + "reply_time_s": 0.116, + "replies": [ + "*#13**0*22*51*02*999##" + ], + "events_after": [ + "*#13**0*22*51*02*999##" + ], + "note": "expose_who13_report_time_parsed_as_read" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 81.199, + "step": "who13_expose_clock", + "dir": "tx", + "session": "cmd", + "frame": "*#13**#0*22*51*00*999##", + "is_status_request": true + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 81.271, + "step": "who13_expose_clock", + "dir": "rx", + "session": "cmd", + "frame": "*#*1##" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 82.072, + "step": "who13_expose_clock", + "dir": "result", + "frame": "*#13**#0*22*51*00*999##", + "ack": true, + "reply_time_s": 0.071, + "replies": [], + "events_after": [], + "note": "expose_who13_set_time_broadcasts_on_evt" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 82.072, + "step": "who18_expose_energy", + "dir": "tx", + "session": "cmd", + "frame": "*#18*51*113*450##", + "is_status_request": false + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 83.34, + "step": "who18_expose_energy", + "dir": "rx", + "session": "cmd", + "frame": "*#*1##" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 83.941, + "step": "who18_expose_energy", + "dir": "result", + "frame": "*#18*51*113*450##", + "ack": true, + "reply_time_s": 1.268, + "replies": [], + "events_after": [], + "note": "expose_who18_active_power_dim113_acked" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 83.941, + "step": "who18_expose_energy", + "dir": "tx", + "session": "cmd", + "frame": "*#18*51*51*12345##", + "is_status_request": false + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 85.342, + "step": "who18_expose_energy", + "dir": "rx", + "session": "cmd", + "frame": "*#*1##" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 85.943, + "step": "who18_expose_energy", + "dir": "result", + "frame": "*#18*51*51*12345##", + "ack": true, + "reply_time_s": 1.401, + "replies": [], + "events_after": [], + "note": "expose_who18_total_energy_dim51_acked" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 85.943, + "step": "who18_expose_energy", + "dir": "tx", + "session": "cmd", + "frame": "*#18*51*54*320##", + "is_status_request": false + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 87.345, + "step": "who18_expose_energy", + "dir": "rx", + "session": "cmd", + "frame": "*#*1##" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 87.946, + "step": "who18_expose_energy", + "dir": "result", + "frame": "*#18*51*54*320##", + "ack": true, + "reply_time_s": 1.403, + "replies": [], + "events_after": [], + "note": "expose_who18_daily_energy_dim54_acked" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 87.946, + "step": "who18_expose_energy", + "dir": "tx", + "session": "cmd", + "frame": "*#18*51*#1200#1*1##", + "is_status_request": false + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 88.095, + "step": "who18_expose_energy", + "dir": "rx", + "session": "cmd", + "frame": "*#*1##" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 88.696, + "step": "who18_expose_energy", + "dir": "result", + "frame": "*#18*51*#1200#1*1##", + "ack": true, + "reply_time_s": 0.149, + "replies": [], + "events_after": [], + "note": "expose_who18_stream_enable_dim1200_acked" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 88.696, + "step": "who18_expose_energy", + "dir": "tx", + "session": "cmd", + "frame": "*#18*51*#1200#1*0##", + "is_status_request": false + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 88.84, + "step": "who18_expose_energy", + "dir": "rx", + "session": "cmd", + "frame": "*#*1##" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 89.441, + "step": "who18_expose_energy", + "dir": "result", + "frame": "*#18*51*#1200#1*0##", + "ack": true, + "reply_time_s": 0.144, + "replies": [], + "events_after": [], + "note": "expose_who18_stream_disable_dim1200_acked" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 89.441, + "step": "who18_expose_energy", + "dir": "tx", + "session": "cmd", + "frame": "*#18*51*113##", + "is_status_request": true + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 90.355, + "step": "who18_expose_energy", + "dir": "rx", + "session": "cmd", + "frame": "*#*1##" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 90.956, + "step": "who18_expose_energy", + "dir": "result", + "frame": "*#18*51*113##", + "ack": true, + "reply_time_s": 0.914, + "replies": [], + "events_after": [], + "note": "query_who18_dim113_acked" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 90.956, + "step": "who18_expose_energy", + "dir": "tx", + "session": "cmd", + "frame": "*#18*51*1200##", + "is_status_request": true + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 91.007, + "step": "who18_expose_energy", + "dir": "rx", + "session": "cmd", + "frame": "*#*0##" + }, + { + "run": "run4_expose_virtual_bus_verification", + "t": 91.607, + "step": "who18_expose_energy", + "dir": "result", + "frame": "*#18*51*1200##", + "ack": false, + "reply_time_s": 0.051, + "replies": [], + "events_after": [], + "note": "query_who18_dim1200_nack" + } + ] +} diff --git a/tests/test_f461_live_shutter_expose_trace.py b/tests/test_f461_live_shutter_expose_trace.py new file mode 100644 index 000000000..5975e1601 --- /dev/null +++ b/tests/test_f461_live_shutter_expose_trace.py @@ -0,0 +1,567 @@ +"""Replay and verification tests for the 2026-10-18 live BTicino F461 capture. + +Fixture: + tests/fixtures/traces/f461_live_2026-10-18/myhome_trace_F461_live_2026-10-18.json + +Covers: +1. Expanded F461 physical plant: + - BT-F411/4 lights at WHERE="06" and WHERE="07", Group #1 (*1*1*#1##, *1*0*#1##, + *#1*#1##, and aggregate status *#1*#1*15*0## / *#1*#1*15*1##), and General 0 + (*1*1*0##, *1*0*0##) with post-broadcast resync polling. + - BT-F411/2 5-second timed shutter at WHERE="05": partial 2.0 s UP + STOP, + mid-flight direction reversal (UP -> DOWN) with gateway pre-move STOP + translation (*2*1000#0*05##), and automatic 5.0 s hardware end-of-travel + STOP (*2*0*05##). + - Living Now KW4691 standalone thermostat + F430/2 + F430/4 at Zone 2. +2. Live F461 bus verification of PR #750 declarative Expose (WHO=1, 2, 4, 13, 18): + - WHO=2 virtual cover at WHERE="25": gateway emits *2*1000#0*25## + *2*1000#1/2*25## + on the event monitor session before returning NACK (~0.55 s) on the command + session; ExposeManager's _pre_move_stop_guard and _tx_echo_guard absorb both + frames without self-oscillation. + - WHO=1 populated (06) vs unpopulated virtual (08) state push & Dim #1 write. + - WHO=4 Dimension 0 read-vs-write (*#4*2*0*0999## parsed as read returning + *#4*2*0*0249## vs *#4*50*#0*0215## write ACKed in 155 ms). + - WHO=13 clock/date queries and *#13**#0*...## write vs *#13**0*...## report. + - WHO=18 virtual energy meter at WHERE="51" (Dims 113, 51, 54, and 1200 streaming). +""" + +from __future__ import annotations + +import asyncio +import json +from pathlib import Path +from types import SimpleNamespace +from unittest.mock import AsyncMock, MagicMock, patch + +import pytest +from homeassistant.components.climate import HVACMode +from homeassistant.const import CONF_NAME +from homeassistant.core import HomeAssistant +from OWNd.message import OWNCommand, OWNMessage +from OWNd.profiles import F461Profile +from pytest_homeassistant_custom_component.common import MockConfigEntry + +from custom_components.myhome.const import CONF_ENTITY, CONF_PLATFORMS, CONF_WHERE, DOMAIN +from custom_components.myhome.data import MyHOMERuntimeData +from custom_components.myhome.router import FrameRouter +from tests.conftest import attach_platform, attach_runtime + +MAC = "00:03:50:00:04:97" +FIXTURE = ( + Path(__file__).resolve().parent + / "fixtures" + / "traces" + / "f461_live_2026-10-18" + / "myhome_trace_F461_live_2026-10-18.json" +) + + +@pytest.fixture(scope="module") +def trace_payload() -> dict: + """Load the 2026-10-18 F461 live trace archive.""" + assert FIXTURE.is_file(), FIXTURE + return json.loads(FIXTURE.read_text(encoding="utf-8")) + + +@pytest.fixture(scope="module") +def rows(trace_payload: dict) -> list[dict]: + """Return the chronological session log rows.""" + return trace_payload["rows"] + + +def _result_rows(rows: list[dict], run: str, frame: str | None = None) -> list[dict]: + return [ + r + for r in rows + if r["run"] == run + and r["dir"] == "result" + and (frame is None or r["frame"] == frame) + ] + + +def _gateway(model: str = "F461") -> MagicMock: + gw = MagicMock() + gw.mac = MAC + gw.unique_id = MAC + gw.bus_group = MAC + gw.primary_gateway_mac = None + gw.delegated_whos = set() + gw.delegated_away_whos = set() + gw.log_id = f"[{model} live 2026-10-18]" + gw.model = model + gw.has_central_unit = False + gw.supports_zone_status = True + gw.initial_discovery_pending = False + gw.is_connected = True + gw.profile = F461Profile() + gw._gateway = SimpleNamespace(profile=gw.profile) + gw.sent: list[str] = [] + + async def _send(cmd, *a, **k): + gw.sent.append(str(cmd)) + fut: asyncio.Future[bool] = asyncio.get_running_loop().create_future() + fut.set_result(True) + return fut + + gw.send = AsyncMock(side_effect=_send) + gw.send_status_request = AsyncMock(side_effect=_send) + return gw + + +async def _setup_plant(hass: HomeAssistant, gw: MagicMock) -> dict[str, object]: + from custom_components.myhome.climate import async_setup_entry as climate_setup + from custom_components.myhome.cover import async_setup_entry as cover_setup + from custom_components.myhome.light import async_setup_entry as light_setup + + platforms_cfg = { + "light": { + "light_1": {CONF_WHERE: "06", CONF_NAME: "Light 1", "model": "F411"}, + "light_2": {CONF_WHERE: "07", CONF_NAME: "Light 2", "model": "F411"}, + }, + "cover": { + "shutter": { + CONF_WHERE: "05", + CONF_NAME: "Shutter", + "advanced": True, + "open_Best_Time": 5, + "close_Best_Time": 5, + }, + }, + "climate": { + "thermo_office": { + "who": "4", + "zone": "2", + "name": "Thermo office", + "heat": True, + "cool": True, + "standalone": True, + "central": False, + "manufacturer": "BTicino", + "model": "F461", + } + }, + } + hass.data = { + DOMAIN: { + MAC: { + CONF_ENTITY: gw, + CONF_PLATFORMS: platforms_cfg, + } + } + } + entry = MagicMock() + entry.data = {"mac": MAC} + entry.entry_id = "f461_live_2026_10_18" + entry.options = {} + attach_runtime(hass, entry, MAC, gw) + + lights: list = [] + covers: list = [] + climates: list = [] + with ( + patch( + "custom_components.myhome.discovery.er.async_entries_for_config_entry", + return_value=[], + ), + patch("custom_components.myhome.discovery.er.async_get", return_value=MagicMock()), + ): + await light_setup(hass, entry, lights.extend) + await cover_setup(hass, entry, covers.extend) + await climate_setup(hass, entry, climates.extend) + + for ent in lights + covers + climates: + ent.hass = hass + domain_prefix = ( + "climate" + if ent in climates + else ("cover" if ent in covers else "light") + ) + ent.entity_id = f"{domain_prefix}.{ent._device_id}" + ent.async_write_ha_state = MagicMock() + ent.async_schedule_update_ha_state = MagicMock() + attach_platform(ent, entry) + + return {ent._where: ent for ent in lights + covers + climates} + + +def test_f461_2026_10_18_trace_schema_and_ownd_parsing( + trace_payload: dict, rows: list[dict] +) -> None: + """Verify fixture metadata, RFC 5737 scrubbing, and OWNd parser coverage across all frames.""" + assert trace_payload["gateway"]["model"] == "F461" + assert trace_payload["gateway"]["firmware"] == "2.0.11" + assert trace_payload["gateway"]["ip"] == "192.0.2.92" + assert trace_payload["gateway"]["mac"] == MAC + assert trace_payload["gateway"]["identification"]["who13_code"] == "200" + assert trace_payload["gateway"]["identification"]["who1013_code"] == "134" + + frames = trace_payload["frames"] + assert len(frames) == 73 + assert len(rows) == 294 + + for item in frames: + assert item["direction"] == "rx" + assert item["session"] == "evt" + parsed = OWNMessage.parse(item["raw"]) + assert parsed is not None, f"Failed to parse evt frame: {item['raw']}" + + for r in rows: + if r["dir"] == "tx": + assert OWNCommand.parse(r["frame"]) is not None or OWNMessage.parse(r["frame"]) is not None + elif r["dir"] == "rx": + assert OWNMessage.parse(r["frame"]) is not None + + +@pytest.mark.asyncio +async def test_run1_lights_06_07_group1_dim15_and_general0_resync( + hass: HomeAssistant, rows: list[dict] +) -> None: + """Replay run1_lights_group_and_general through light entities 06 and 07 and PlatformDiscovery.""" + gw = _gateway() + entities = await _setup_plant(hass, gw) + light_06 = entities["06"] + light_07 = entities["07"] + + # Verify gateway identification frames at start of run1 + id_res = _result_rows(rows, "run1_lights_group_and_general", "*#13**15##")[0] + assert id_res["ack"] is True + assert id_res["replies"] == ["*#13**15*200##"] + fw_res = _result_rows(rows, "run1_lights_group_and_general", "*#13**16##")[0] + assert fw_res["ack"] is True + assert fw_res["replies"] == ["*#13**16*2*0*11##"] + + # Unpopulated old light addresses 01 and 04 NACK + for old_where in ("01", "04"): + old_res = _result_rows(rows, "run1_lights_group_and_general", f"*#1*{old_where}##")[0] + assert old_res["ack"] is False + + # Step-by-step replay of point switching, Group #1, and General 0 + r1_results = [ + r for r in rows if r["run"] == "run1_lights_group_and_general" and r["dir"] == "result" + ] + by_note = {r["note"]: r for r in r1_results} + + def _feed_events(ev_list: list[str]) -> None: + for raw in ev_list: + msg = OWNMessage.parse(raw) + assert msg is not None + if getattr(msg, "who", None) == 1: + w = getattr(msg, "where", None) + if w in ("06", "07"): + entities[w].handle_event(msg) + + # Initial polls: both 06 and 07 are OFF + _feed_events(by_note["poll_light_06"]["events_after"]) + _feed_events(by_note["poll_light_07"]["events_after"]) + assert light_06.is_on is False + assert light_07.is_on is False + + # Turn 06 ON -> emits *1*1*06## and Group #1 aggregate *#1*#1*15*1## + assert by_note["light_06_on_emits_group1_dim15_1"]["events_after"] == [ + "*1*1*06##", + "*#1*#1*15*1##", + ] + _feed_events(by_note["light_06_on_emits_group1_dim15_1"]["events_after"]) + assert light_06.is_on is True + assert light_07.is_on is False + + # Turn 06 OFF -> emits *1*0*06## and Group #1 aggregate *#1*#1*15*0## + assert by_note["light_06_off_emits_group1_dim15_0"]["events_after"] == [ + "*1*0*06##", + "*#1*#1*15*0##", + ] + _feed_events(by_note["light_06_off_emits_group1_dim15_0"]["events_after"]) + assert light_06.is_on is False + + # Turn 07 ON and OFF -> emits *#1*#1*15*1## then *#1*#1*15*0## + _feed_events(by_note["light_07_on_emits_group1_dim15_1"]["events_after"]) + assert light_07.is_on is True + _feed_events(by_note["light_07_off_emits_group1_dim15_0"]["events_after"]) + assert light_07.is_on is False + + # Group #1 poll (*#1*#1##) returns individual states for both 06 and 07 + assert by_note["poll_group_1_returns_06_and_07"]["replies"] == [ + "*1*0*06##", + "*1*0*07##", + ] + + # Group #1 ON (*1*1*#1##) emits individual status frames for both 06 and 07 + assert set(by_note["group_1_on_emits_06_07_and_dim15_1"]["events_after"]) == { + "*1*1*#1##", + "*1*1*06##", + "*1*1*07##", + "*#1*#1*15*1##", + } + _feed_events(by_note["group_1_on_emits_06_07_and_dim15_1"]["events_after"]) + assert light_06.is_on is True + assert light_07.is_on is True + + # Group #1 OFF (*1*0*#1##) turns both 06 and 07 OFF + _feed_events(by_note["group_1_off_emits_06_07_and_dim15_0"]["events_after"]) + assert light_06.is_on is False + assert light_07.is_on is False + + # General ON (*1*1*0##) emits only *1*1*0## and *#1*#1*15*1##; resync polls confirm 06 & 07 are ON + assert by_note["general_on_emits_dim15_1_only"]["events_after"] == [ + "*1*1*0##", + "*#1*#1*15*1##", + ] + _feed_events(by_note["resync_poll_06_after_general_on"]["events_after"]) + _feed_events(by_note["resync_poll_07_after_general_on"]["events_after"]) + assert light_06.is_on is True + assert light_07.is_on is True + + # General OFF (*1*0*0##) followed by resync polls confirms 06 & 07 are OFF + assert by_note["general_off_emits_dim15_0_only"]["events_after"] == [ + "*1*0*0##", + "*#1*#1*15*0##", + ] + _feed_events(by_note["resync_poll_06_after_general_off"]["events_after"]) + _feed_events(by_note["resync_poll_07_after_general_off"]["events_after"]) + assert light_06.is_on is False + assert light_07.is_on is False + + +@pytest.mark.asyncio +async def test_run2_shutter_05_partial_run_reversal_and_5s_hardware_autostop( + hass: HomeAssistant, rows: list[dict] +) -> None: + """Replay run2_shutter_05_travel_and_reversal through the F411/2 cover entity at WHERE=05.""" + gw = _gateway() + entities = await _setup_plant(hass, gw) + shutter = entities["05"] + + r2_results = [ + r + for r in rows + if r["run"] == "run2_shutter_05_travel_and_reversal" and r["dir"] == "result" + ] + by_note = {r["note"]: r for r in r2_results} + + # 1. Initial status is stopped (*2*0*05##) + assert by_note["initial_shutter_05_status"]["replies"] == ["*2*0*05##"] + + # 2. Raising shutter (*2*1*05##) emits pre-move STOP translation, STOP status, UP translation, UP status + up_events = by_note["raise_shutter_05_for_2s"]["events_after"] + assert up_events == [ + "*2*1000#0*05##", + "*2*0*05##", + "*2*1000#1*05##", + "*2*1*05##", + ] + for raw in up_events: + msg = OWNMessage.parse(raw) + if not getattr(msg, "is_translation", False): + shutter.handle_event(msg) + assert shutter.is_opening is True + + # 3. Manual STOP after 2s (*2*0*05##) emits *2*1000#0*05## and *2*0*05## + stop_events = by_note["stop_shutter_05_after_2s"]["events_after"] + assert stop_events == ["*2*1000#0*05##", "*2*0*05##"] + for raw in stop_events: + msg = OWNMessage.parse(raw) + if not getattr(msg, "is_translation", False): + shutter.handle_event(msg) + assert shutter.is_opening is False + assert shutter.is_closing is False + + # 4. Mid-flight reversal (UP -> DOWN) followed by 5s hardware auto-stop (*2*0*05##) + rev_events = by_note["reverse_to_down_and_wait_for_5s_hardware_autostop"]["events_after"] + assert rev_events == [ + "*2*1000#0*05##", + "*2*0*05##", + "*2*1000#2*05##", + "*2*2*05##", + "*2*0*05##", + ] + # Verify exact timing of the spontaneous 5s hardware auto-stop in the raw evt rows + r2_evt = [ + r + for r in rows + if r["run"] == "run2_shutter_05_travel_and_reversal" + and r["dir"] == "rx" + and r["session"] == "evt" + and r["step"] == "reversal_and_autostop" + ] + down_start_t = next(r["t"] for r in r2_evt if r["frame"] == "*2*2*05##") + auto_stop_t = r2_evt[-2]["t"] + assert r2_evt[-2]["frame"] == "*2*0*05##" + assert 5.0 <= (auto_stop_t - down_start_t) <= 6.5 + + for raw in rev_events[:-1]: + msg = OWNMessage.parse(raw) + if not getattr(msg, "is_translation", False): + shutter.handle_event(msg) + assert shutter.is_closing is True + + shutter.handle_event(OWNMessage.parse(rev_events[-1])) + assert shutter.is_closing is False + assert shutter.is_opening is False + + +@pytest.mark.asyncio +async def test_run3_climate_zone2_status_and_actuators( + hass: HomeAssistant, rows: list[dict] +) -> None: + """Replay run3_climate_zone2 through the standalone KW4691 climate entity at Zone 2.""" + gw = _gateway() + entities = await _setup_plant(hass, gw) + zone2 = entities["2"] + + r3_evt = [ + r + for r in rows + if r["run"] == "run3_climate_zone2" + and r["dir"] == "rx" + and r["session"] == "evt" + ] + for r in r3_evt: + msg = OWNMessage.parse(r["frame"]) + assert msg is not None + if getattr(msg, "who", None) == 4 and getattr(msg, "zone", None) == 2: + zone2.handle_event(msg) + + assert zone2.current_temperature == 24.9 + assert zone2.target_temperature == 24.5 + assert zone2.hvac_mode == HVACMode.HEAT + assert zone2.extra_state_attributes["fan_coil_cooling"] is True + assert zone2.extra_state_attributes["fan_coil_heating"] is False + + +@pytest.mark.asyncio +async def test_run4_expose_live_f461_protocol_and_echo_guard_replay( + hass: HomeAssistant, rows: list[dict] +) -> None: + """Verify the live F461 Expose run4 verdicts and replay them through ExposeManager.""" + r4_results = [ + r + for r in rows + if r["run"] == "run4_expose_virtual_bus_verification" and r["dir"] == "result" + ] + by_note = {r["note"]: r for r in r4_results} + + # 1. WHO=1 populated (06) ACKs and echoes on evt; unpopulated (08) NACKs without retry on s:, while Dim #1 write ACKs + assert by_note["expose_who1_on_populated_06"]["ack"] is True + assert "*1*1*06##" in by_note["expose_who1_on_populated_06"]["events_after"] + assert by_note["expose_who1_on_virtual_08_no_retry"]["ack"] is False + assert by_note["expose_who1_on_virtual_08_no_retry"]["reply_time_s"] < 2.5 + assert by_note["expose_who1_write_brightness_virtual_08_acked"]["ack"] is True + + # 2. WHO=2 unpopulated virtual cover (25) emits pre-move STOP (*2*1000#0*25##) + move (*2*1000#1/2*25##) on evt! + assert by_note["expose_who2_raise_virtual_25_emits_premove_stop_and_up_on_evt"]["ack"] is False + assert by_note["expose_who2_raise_virtual_25_emits_premove_stop_and_up_on_evt"]["events_after"] == [ + "*2*1000#0*25##", + "*2*1000#1*25##", + ] + assert by_note["expose_who2_lower_virtual_25_emits_premove_stop_and_down_on_evt"]["events_after"] == [ + "*2*1000#0*25##", + "*2*1000#2*25##", + ] + assert by_note["expose_who2_stop_virtual_25_emits_stop_translation_on_evt"]["events_after"] == [ + "*2*1000#0*25##", + ] + assert by_note["query_who2_dim10_virtual_25_acked"]["ack"] is True + + # 3. WHO=4: *#4*2*0*0999## (without #0) is parsed by bt_termo as a read request and returns *#4*2*0*0249##; + # *#4*50*#0*0215## (write_temperature with #0) is ACKed in ~155 ms! + assert by_note["who4_dim0_without_hash_parsed_as_read_on_zone2"]["ack"] is True + assert by_note["who4_dim0_without_hash_parsed_as_read_on_zone2"]["replies"] == [ + "*#4*2*0*0249##" + ] + assert by_note["expose_who4_report_temp_50_nack_timeout"]["ack"] is False + assert by_note["expose_who4_write_temp_50_acked_156ms"]["ack"] is True + assert by_note["expose_who4_write_temp_50_acked_156ms"]["reply_time_s"] < 0.5 + + # 4. WHO=13: clock queries ACK and echo on evt; *#13**0*...## (without #0) is parsed as read + assert by_note["query_who13_time"]["ack"] is True + assert by_note["query_who13_date"]["ack"] is True + assert by_note["query_who13_datetime"]["ack"] is True + assert by_note["expose_who13_report_time_parsed_as_read"]["ack"] is True + assert len(by_note["expose_who13_report_time_parsed_as_read"]["replies"]) == 1 + assert by_note["expose_who13_set_time_broadcasts_on_evt"]["ack"] is True + + # 5. WHO=18: active power (113), total energy (51), daily energy (54), and Dim 1200 streaming ACK + assert by_note["expose_who18_active_power_dim113_acked"]["ack"] is True + assert by_note["expose_who18_total_energy_dim51_acked"]["ack"] is True + assert by_note["expose_who18_daily_energy_dim54_acked"]["ack"] is True + assert by_note["expose_who18_stream_enable_dim1200_acked"]["ack"] is True + assert by_note["expose_who18_stream_disable_dim1200_acked"]["ack"] is True + + # 6. Replay the captured WHO=2 virtual cover (25) and WHO=1 (06) event bursts through ExposeManager + try: + from custom_components.myhome.expose import ExposeManager + except ImportError: + return + + gw = _gateway() + expose_cfg = [ + {"who": 1, "where": "06", "entity_id": "light.virtual_06", "cooldown": 0}, + {"who": 2, "where": "25", "entity_id": "cover.virtual_25", "cooldown": 0}, + ] + entry = MockConfigEntry( + domain=DOMAIN, + data={"mac": MAC, "host": "192.0.2.92", "port": 20000, "expose": expose_cfg}, + unique_id=MAC, + ) + entry.add_to_hass(hass) + entry.runtime_data = MyHOMERuntimeData( + gateway=gw, + router=FrameRouter(), + platforms={}, + entities={}, + expose=expose_cfg, + ) + + cover_calls: list[tuple[str, dict]] = [] + + async def _record_cover_service(call) -> None: + cover_calls.append((call.service, dict(call.data))) + + hass.services.async_register("cover", "open_cover", _record_cover_service) + hass.services.async_register("cover", "close_cover", _record_cover_service) + hass.services.async_register("cover", "stop_cover", _record_cover_service) + + hass.states.async_set("light.virtual_06", "off") + hass.states.async_set("cover.virtual_25", "closed") + mgr = ExposeManager(hass, entry, gw, expose_cfg) + await mgr.async_start() + + # HA transitions cover.virtual_25 -> "opening", sending *2*1*25## onto the F461 + hass.states.async_set("cover.virtual_25", "opening") + await hass.async_block_till_done() + assert gw.sent[-1] == "*2*1*25##" + + # Feed the exact 2-frame translation burst emitted by the physical F461 on evt: + # ['*2*1000#0*25##', '*2*1000#1*25##'] + for raw in by_note["expose_who2_raise_virtual_25_emits_premove_stop_and_up_on_evt"]["events_after"]: + msg = OWNMessage.parse(raw) + assert msg is not None + await mgr.handle_bus_message(msg) + await hass.async_block_till_done() + + # Both frames MUST be absorbed by _pre_move_stop_guard and _tx_echo_guard (0 service calls!) + assert cover_calls == [] + + # HA transitions cover.virtual_25 -> "closing", sending *2*2*25## + hass.states.async_set("cover.virtual_25", "closing") + await hass.async_block_till_done() + assert gw.sent[-1] == "*2*2*25##" + + for raw in by_note["expose_who2_lower_virtual_25_emits_premove_stop_and_down_on_evt"]["events_after"]: + msg = OWNMessage.parse(raw) + assert msg is not None + await mgr.handle_bus_message(msg) + await hass.async_block_till_done() + assert cover_calls == [] + + # Now an external wall control sends the same F461 sequence (*2*1000#0*25## -> *2*1000#1*25##) + # when no HA TX guard is active: pre-move STOP triggers stop_cover and UP triggers open_cover, + # while ExposeManager's confirmation echoes are guarded! + for raw in by_note["expose_who2_raise_virtual_25_emits_premove_stop_and_up_on_evt"]["events_after"]: + msg = OWNMessage.parse(raw) + assert msg is not None + await mgr.handle_bus_message(msg) + await hass.async_block_till_done() + assert [c[0] for c in cover_calls] == ["stop_cover", "open_cover"] + + await mgr.async_stop()