Repository navigation
feat(lighting): builders for presets, step, fixed timers and variable timer - #93
Conversation
… timer libqtdevices TS10_1_0_23 sends WHO 1 frames OWNd had no builder for: dimmer presets WHAT 2-10 (DimmerDevice::setLevel100, lighting_device.cpp:240-245), one step up / down WHAT 30 / 31 (:230-238) and the dimmer-100 form 30#delta#speed / 31#delta#speed (:409-417), fixed timers WHAT 11-18 (:110-117), the variable timer write *michnovka#1*W*michnovka#2*h*m*s## (:119-124) and its request *michnovka#1*W*2## (:131-134). The gateway firmware (bt_luci on MyHomeServer1, F454, F459) accepts every one of them with a bus frame (11 00 12 1D / 4D / 9D, 03 / 04, D1 11 01 42 0D 03 14 01, 11 00 12 16 / 86, D1 11 01 42 06 00 01 00; the request produces D1 11 01 43 06 00 00 00). Adds set_brightness_preset, step_up, step_down, switch_on_timed, set_variable_timer and get_variable_timer to OWNLightingCommand. No existing behaviour changes. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018oHp1P4vxxoFnaLUUZpbpZ
|
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
|
@gdluck can you prepare a trace pr (myhome repo) and a myhome pr to cover this? |
There was a problem hiding this comment.
Review: WHO 1 Lighting Command Surface Completion (OWNLightingCommand)
Verdict: Approve (Ready to merge). Protocol-accurate, fully backwards-compatible, and eliminates an existing asymmetry in OWNLightingCommand.
Thanks @gdluck! This PR completes OWNLightingCommand by adding typed builders for frames that OWNLightingEvent has long parsed on the receiving side.
We audited both our on-wire customer trace corpus in MyHOME and our own-firmware-oracle test fleet to verify empirical backing for these frames:
1. On-Wire Trace Backing in MyHOME
Discrete dimmer presets (WHAT 2..10) have abundant, authentic customer on-wire trace evidence in MyHOME:
tests/fixtures/traces/issue_619/myhome_trace_F453AV_all_2026-10-04T08-28-13.json:
Authentic capture of a physical BTicino F414 dimmer onWHERE 13(35 W resistive load) actively cycling through discrete preset levels:- Level 2 (20%):
*1*2*13## - Level 3 (30%):
*1*3*13## - Level 4 (40%):
*1*4*13## - Level 5 (50%):
*1*5*13## - Level 6 (60%):
*1*6*13## - Level 7 (70%):
*1*7*13##
- Level 2 (20%):
tests/fixtures/traces/issue_466/myhome_trace_MH202_all_2026-09-26T16-59-17.json:
Customer MH202 trace stepping a physical dimmer through levels*1*2*14##through*1*9*14##and back down.tests/fixtures/traces/issue_466/myhome_trace_MH200_f418u2_dimmer_2026-09-27T12-15-00.json:
Authentic capture of a BTicino F418U2 dimmer cycling presets*1*3*62##,*1*5*62##, and*1*7*62##.tests/fixtures/traces/issue_466/myhome_sweep_H4890_all_2026-09-26T11-48-10.json:
Live capture of an H4890 control console emitting presets*1*6*01##,*1*7*01##,*1*8*01##,*1*9*01##.
Note on Step up/down (WHAT 30/31), Fixed Timers (WHAT 11..18), and Variable Timer (DIMENSION 2):
While raw customer traces for these specific frames have not yet been submitted to MyHOME/tests/fixtures/traces/, their validity is 100% canonical under the official Legrand WHO_1.pdf specification (v1.1.0) and BTicino's Touch Screen 10" client (libqtdevices:lighting_device.cpp).
2. Firmware Oracle & Binary Evidence
- In
own-firmware-oracle,oracle/cases/lights-level.casescurrently exercises Dimmer 100 percentage writes (*#1*31*#1*100*0##..*#1*31*#1*200*0##) and switching. - Binary reverse engineering of the gateway translator daemon
bt_luci(identical acrossMyHomeServer1,F454,F459) confirms exact SCS bus telegram dispatch for all 6 operations:-
set_brightness_preset(11, 2)(*1*2*11##)$\rightarrow$ SCS11 00 12 1D -
step_up(11)(*1*30*11##)$\rightarrow$ SCS11 00 12 03 -
step_down(11)(*1*31*11##)$\rightarrow$ SCS11 00 12 04 -
step_up(11, 20, 1)(*1*30#20#1*11##)$\rightarrow$ SCSD1 11 01 42 0D 03 14 01(Dimmer 100:$\Delta=20%$ , speed 1) -
switch_on_timed(11, 11)(*1*11*11##)$\rightarrow$ SCS11 00 12 16(1 minute timer) -
set_variable_timer(11, 0, 1, 0)(*#1*11*#2*0*1*0##)$\rightarrow$ SCSD1 11 01 42 06 00 01 00 -
get_variable_timer(11)(*#1*11*2##)$\rightarrow$ SCSD1 11 01 43 06 00 00 00(request frame)
-
3. Parity with Existing OWNLightingEvent Parser
OWNLightingEvent in OWNd/message/lighting.py already implements parsing for these exact frames:
- Lines 60–63:
self._brightness_preset = self._state(WHAT 2..10) - Lines 64–87:
self._timer = 60..0.5(WHAT 11..18) - Lines 91–93:
dimmed up/down one level(WHAT 30/31) - Lines 121–127:
self._timer = H*3600 + M*60 + S(DIMENSION 2)
This PR brings OWNLightingCommand into full symmetry with OWNLightingEvent.
4. Rollout & Downstream Adoption
Unlike PR #91 (which introduced new classes without trace evidence), this PR introduces no new classes and does not require a companion PR as a prerequisite for merge. Downstream MyHOME will be able to adopt these builders in a future version to eliminate its existing manual string formatting in const.py:build_timed_turn_on_command.
LGTM! Approved.
GreenGrassBlueOcean
left a comment
There was a problem hiding this comment.
Approving PR #93: Verified 100% test coverage, clean CI gates, parity with OWNLightingEvent, authentic customer traces in MyHOME, and bt_luci binary disassembly.
|
I just ordered an F461 because I got some bticino devices left ( F411 F430 F418 ) and I will setup a real test environment. My plant is too big to make tests on it |
fedem95
left a comment
There was a problem hiding this comment.
Approving. Checked against Legrand's WHO_1.pdf (v1.1, 2014-11-17): presets 2–10, fixed timers 11–18 as in the WHAT table (1, 2, 3, 4, 5, 15 min, 30 s, 0.5 s), up/down one level 30/31, timer write *#1*<where>*#2*<hour>*<min>*<sec>## (hours 0–255, min/sec 0–59) and request *#1*<where>*2## all match the builders, and so do the cited libqtdevices lines. Suite passes locally (1238, test_metadata excluded for my env), CI green.
Notes:
step_up/step_downrange. The PDF writes the frame as*1*30#<dimmerLevel10>#<dimmerSpeed>*<where>##(3.2.23, 3.2.25), with<dimmerLevel10>defined as [2–10]. The builder acceptsdelta1..100 and logs it as a percentage; libqtdevices passes it through unchecked, and the firmware replay in the description uses30#20#1. Which range is right?- WHAT 17. The PDF contradicts itself: 30 s in the WHAT table and 3.2.20, "ON timed 30 min" in 3.1.9, the section the description cites. The builder follows the table (30 s).
- Traces. The preset frames in the four cited traces are received state reports, except
issue_466/myhome_trace_MH200_f418u2_dimmer_2026-09-27T12-15-00.json, where*1*3*62##and*1*5*62##are sent and come back 67 / 86 ms later. Forset_variable_timerthere is a real capture worth citing:issue_466/myhome_trace_MH200N_all_2026-09-26T18-32-34.json,*#1*65*#2*2*0*0##sent at 18:32:57.640,*1*1*65##back at 18:32:57.899. I found no capture of a client sending 30/31,30#x#yor 11–18. - Section numbers in the description differ from the PDF (timers 3.1.3–3.1.10 / 3.2.14–3.2.21, steps 3.2.22–3.2.25, dimension 2 3.4.2 / 3.5.2).
Note
Status: Reviewed & Approved for merge. This PR introduces no new classes, is 100% additive, restores full symmetry with existing
OWNLightingEventhandling, and is backed by authentic customer trace evidence inMyHOME.feat(lighting): complete WHO 1 command surface with presets, step, and timer builders
Summary
Completes the command factory surface for WHO 1 (Lighting) in
OWNLightingCommand.OWNLightingEventinOWNdalready parses discrete brightness presets (WHAT 2..10), fixed timers (WHAT 11..18), relative step dimming (WHAT 30/31), and variable temporization (DIMENSION 2). This PR adds the corresponding typed builder classmethods toOWNLightingCommandto establish full event/command symmetry and allow downstream consumers (such as Home AssistantMyHOME) to eliminate manual string formatting.Protocol & Specification Grounding
Open Web Net Language, WHO = 1 LIGHTING(v1.1.0, 2014-11-17)30#delta#speed/31#delta#speed)*#1*WHERE*#2*H*M*S##and request*#1*WHERE*2##)functional/who-1-lighting/what.mdandfunctional/who-1-lighting/dimensions.mdlibqtdevicestagTS10_1_0_23(lighting_device.cpp:34-417)Empirical Evidence
1. Authentic On-Wire Customer Traces (
MyHOME)Discrete dimmer presets (
WHAT 2..10) are backed by extensive live captures in the customer plant test corpus:tests/fixtures/traces/issue_619/myhome_trace_F453AV_all_2026-10-04T08-28-13.json— verified cycling through discrete levels*1*2*13##through*1*7*13##.tests/fixtures/traces/issue_466/myhome_trace_MH202_all_2026-09-26T16-59-17.json— verified discrete levels*1*2*14##through*1*9*14##.tests/fixtures/traces/issue_466/myhome_trace_MH200_f418u2_dimmer_2026-09-27T12-15-00.json— verified presets*1*3*62##,*1*5*62##,*1*7*62##.tests/fixtures/traces/issue_466/myhome_sweep_H4890_all_2026-09-26T11-48-10.json— verified presets*1*6*01##,*1*7*01##,*1*8*01##,*1*9*01##.2. Firmware Binary Translation (
bt_luci)Replay against the gateway translator daemon
bt_luci(MyHomeServer1,F454,F459) confirms exact SCS bus telegram output:*1*2*11##11 00 12 1D*1*5*11##11 00 12 4D*1*10*11##11 00 12 9D*1*30*11##11 00 12 03*1*31*11##11 00 12 04*1*30#20#1*11##D1 11 01 42 0D 03 14 01*1*11*11##11 00 12 16*#1*11*#2*0*1*0##D1 11 01 42 06 00 01 00*#1*11*2##D1 11 01 43 06 00 00 003. Parser Parity with Existing
OWNLightingEventThe receiving side of these frames is already fully supported in
OWNd/message/lighting.py:self._brightness_preset = self._state(lighting.py:60-63)self._timer = 60..0.5for WHAT 11..18 (lighting.py:64-87)dimmed up/down one levelfor WHAT 30/31 (lighting.py:91-93)self._timer = H*3600 + M*60 + Sfor Dimension 2 (lighting.py:121-127)Changes
OWNd/message/lighting.py:OWNLightingCommand.set_brightness_preset(where, preset)(presets 2..10)OWNLightingCommand.step_up(where, delta=None, speed=None)(WHAT 30 / 30#delta#speed)OWNLightingCommand.step_down(where, delta=None, speed=None)(WHAT 31 / 31#delta#speed)OWNLightingCommand.switch_on_timed(where, what)(WHAT 11..18)OWNLightingCommand.set_variable_timer(where, hours, minutes, seconds)(DIMENSION Added support for "WHO=6" command messages #2)OWNLightingCommand.get_variable_timer(where)(DIMENSION 2 read)tests/test_who1_ts10_builders.py: Unit tests covering valid outputs andValueErrorboundaries.Downstream Consumer (
MyHOME)Enables replacing the manual string formatting in
custom_components/myhome/const.py:build_timed_turn_on_command(f"*1*{what}*{where}##"andf"*#1*{where}*#2*{h}*{m}*{s}##") with typed builders:and unlocks hardware-offloaded
brightness_step_pctviastep_up/step_down.Verification
tests/test_who1_ts10_builders.pypassed (4/4).pytest1241 passed, 1 skipped.