Repository navigation
feat(message,profiles): add declarative expose frame builders, query helpers, and profile flags - #129
Conversation
|
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
5219b77 to
4113a2f
Compare
fedem95
left a comment
There was a problem hiding this comment.
I found one validation gap: finite fractional values are silently truncated by int() instead of being rejected. For example, report_brightness(..., 50.9) builds a frame with level 50; the same issue affects integer-valued parameters in the other builders. Since these fields represent protocol integers, please reject non-integral values and add regression tests before approval.
|
Thanks for catching that, @fedem95! Fixed in d478495:
|
|
Follow-up update (@fedem95): Also pushed 42280af on this branch to address a few additional review findings:
|
…helpers, and profile flags (#129)
c68ffd5 to
30410c6
Compare
fedem95
left a comment
There was a problem hiding this comment.
Thanks for d478495 and the rebase: report_brightness('21', 50.9) and True now raise, 50.0 still builds *#1*21*1*150*0##. Tested 30410c6: 1893 passed, CI green. The only change from c68ffd5 is the gateway.py conflict resolution, which keeps master's _resolve_tzinfo, and the set_*_to_now builders work with both str and tzinfo. The F461 2026-10-18 trace backs the main points: *#1*08*#1*150*0## ACK in 0.126 s, *#4*2*0*0999## answered as a read with *#4*2*0*0249##, *#4*50*#0*0215## ACK in 0.155 s, WHO 18 reports ACKed in 0.14-1.40 s. Before approving:
- The F453/MH200N
supports_who4_command_injection=Falseand MH200/MH200Nsupports_who18_response_injection=Falseflags are credited to the oracle, but I can't find the evidence.*#4*1*#0*0215##isn't intests/golden/firmware_oracle.json, and neither is any*#4*W*#0*TTTT##,*#4*W*0*TTTT##or WHO 18 report frame. The 88 F453/MH200/MH200N/F461 TSVs on oraclemainhave none either (the only0215is*#4*#23#1*#14*0215*1##). Where are those verdicts? If they aren't published yet, I'd keep the defaults and leave the overrides out until they are. - Figures that don't match the 2026-10-18 trace:
*#1*08*1*150*0##is NACKed in 0.055 s (not 5 ms), and the WHO 2 report there is*#2*25*10*10*50*0*0##(priority0, NACK in 0.055 s), not…*001*0##in 82 ms. If those come from the 2026-10-20 capture, could it be added to the corpus? - On the F461, WHO 13 report frames are answered as reads too (
*#13**0*22*51*00*999##→ ACK plus the real time), like WHO 4; the diagram listsreport_datetimeas a valid injection. MH201Profileinheritssupports_who4_command_injection=Trueandsupports_who18_response_injection=Truewith no MH201 evidence. Maybe say "unverified" for profiles without data rather than defaulting to True?
Non-blocking: a half-hour offset is still truncated silently (Asia/Kolkata → TZ 005); rejecting it might be safer.
…t sub-hour TZ offsets Review follow-up (#129, pullrequestreview-5586521653): - supports_dimension_response_injection, supports_who4_command_injection and supports_who18_response_injection are now bool | None, None meaning no capture or published oracle verdict exists. Only F461Profile sets them, from the live 2026-10-18 capture and the MyHOME#750 plant check: reply forms not injected (WHO 1/2 NACK, WHO 4/13 answered as reads), *#4*W*#0*TTTT## ACKed, WHO 18 reply forms ACKed. - Drop the F453/MH200N WHO 4 and MH200/MH200N WHO 18 overrides: no published oracle verdict backs them. - _encode_tz_offset raises ValueError for offsets that are not whole hours (Asia/Kolkata, Asia/Kathmandu, America/St_Johns) instead of truncating. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Thanks, all five points checked against the F461 trace and the oracle checkout; fixed in e374f3c and in the description.
Non-blocking: done. 1894 passed locally; ruff and mypy clean. |
fedem95
left a comment
There was a problem hiding this comment.
Thanks, all five points are addressed in e374f3c. Merged with current master (a2ce7fb): no conflicts, 1893 passed, CI green. The flags now default to None, only F461Profile sets them, and its docstring matches the 2026-10-18 trace (*#4*50*#0*0215## ACK in 0.155 s, the WHO 18 reports ACKed in 1.27-1.40 s with no event after). MH201Profile stays None on all three. Non-whole-hour offsets now raise, also through set_time_to_now. Approving.
One last fix to the description, no code change needed: it says the 0HH/1HH TZ field is "confirmed across physical … MH201 … captures", but every MH201 capture we have leaves it empty: my 3.6.27 answers *#13**0*21*31*40*## and *#13**22*21*31*43**03*14*10*2026##, the mh201_physical_plant fixture has *#13**22*00*22*43**04*17*09*2026##, and the 3.3.6 trace in issue_466 has *#13**0*21*07*30*##. The F461 in the same list answers 999 (unconfigured) in the 2026-10-18 trace.
…penWebNet-HA#132, OpenWebNet-HA#133 to the changelog Brings in OpenWebNet-HA#132 (typed exceptions, fast send reconnect), OpenWebNet-HA#133 (discovery shim removal, MH201 retry notes) and OpenWebNet-HA#129 (expose builders, query helpers, tri-state profile flags). OWNd/__init__.py keeps master's exception re-exports with __version__ = "2.0.0b11". Changelog dated 2026-10-20. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
What is the problem
Downstream virtual-device emulation and bus-state injection (the
expose:engine inOpenWebNet-HA/MyHOME#750, verified on physical hardware inOpenWebNet-HA/MyHOME#753andissuecomment-6277050571) require four protocol-layer capabilities that were previously missing or incomplete inOWNd:report_*) and dimension-write (write_*/set_*) frame builders:OWNdpreviously provided actuator command and request builders (switch_on,set_brightness,raise_shutter,set_temperature,get_total_consumption,request_datetime), but lacked builders for dimension response frames (*#WHO*WHERE*DIM*VAL...##) and WHO=4 Dimension#0temperature writes (*#4*WHERE*#0*TTTT##) needed when acting as a virtual responder or injecting external state onto the SCS bus acrossWHO=1(Lighting),WHO=2(Automation),WHO=4(Thermoregulation),WHO=13(Gateway Clock), andWHO=18(Energy Management).OWNMessageandOWN*Command:*#WHO*WHERE##), dimension requests (*#WHO*WHERE*DIM##), dimension writes (*#WHO*WHERE*#DIM*VAL##), dimension replies (*#WHO*WHERE*DIM*VAL##), and pushbutton command-translation frames (*WHO*1000#WHAT*WHERE##) without ad-hoc string regexes in downstream application code.Report Formvs.#DIMWrite Form):*#WHO*WHERE*DIM*VAL##) vs.#DIMwrite frames (*#WHO*WHERE*#DIM*VAL##) differently across subsystems and hardware families:WHO=1,WHO=2,WHO=4,WHO=13: On physicalF461(2.0.11), un-hashed response frames on the TCP command session are either NACKed (*#1*08*1*150*0##,*#2*25*10*10*50*0*0##) or answered as dimension reads (*#4*2*0*0999##returns Zone 2's measured*#4*2*0*0249##;*#13**0*22*51*00*999##returns the gateway's own time*#13**0*22*51*02*999##). The#DIMwrite frames (*#1*08*#1*150*0##,*#2*25*#11#001*50##,*#4*50*#0*0215##,*#13**#0*…##) are ACKed. Timings and sources are in the trace section below.WHO=18: Report-form frames (*#18*51*113*450##,*#18*51*51*12345##,*#18*51*54*320##) are ACKed on physicalF461in 1.27–1.40 s, with no event after the ACK, so the value is not shown to reach the bus. The plain read*#18*51*113##is ACKed in 0.91 s without a reply too.exposebuilders (extendingOpenWebNet-HA/OWNd#128):OWNd#128: rejecting empty or*-pollutedWHEREaddresses,boolvalues (True/Falsedisguised asint/float), non-finite floats (NaN,±inf, raisingValueErrorrather thanOverflowError), finite fractional floats on protocol-integer fields (e.g.50.9rather than silently truncating viaint()), naivedatetime/timeinputs lacking an explicittime_zone, and out-of-range hardware parameters before constructing frames.(Note: The command-session post-write retry guard for
is_status_request=Truewas split into standalone PROpenWebNet-HA/OWNd#131.)How is it solved
OWNd/message/base.py):is_status_request(*#WHO*WHERE##),is_dimension_request(*#WHO*WHERE*DIM##),is_dimension_writing(*#WHO*WHERE*#DIM*...##),is_dimension_reply(*#WHO*WHERE*DIM*VAL...##), andeffective_what(unwrapping1000#WHATtranslation frames to integerWHAT) onOWNMessage.OWNd/message/):WHO=1(OWNLightingCommand):is_status_query(*#1*WHERE##) andis_brightness_query(*#1*WHERE*1##or*#1*WHERE*4##).report_brightness(where, level, transition=0)*#1*WHERE*1*LEVEL*SPEED##(encoding0..100%into OpenWebNet100..200, where100= OFF).WHO=2(OWNAutomationCommand):is_status_query(*#2*WHERE##) andis_shutter_status_query(*#2*WHERE*10##).report_shutter_level(where, level, state=10, priority=0, info=0)*#2*WHERE*10*STATE*LEVEL*PRIORITY*INFO##(supporting0..100%or255unknown, states10..15, and both integer0..255and"001"/"000").WHO=4(OWNHeatingCommand&OWNHeatingEvent):is_temperature_query(*#4*WHERE*0##).encode_temperature(temperature)0TTTfor1TTTfor[-99.9, +99.9]°C, usingDecimalwithROUND_HALF_UPand avoiding-0.0sign artifacts).report_temperature(where, temperature)*#4*WHERE*0*TTTT##andwrite_temperature(where, temperature)*#4*WHERE*#0*TTTT##.OWNHeatingEvent: addsprobe_temperatureproperty (returning the scalar float temperature for 3-digit external probes101–899alongsidesecondary_temperature == [sensor, temp]) while preserving existingunique_idsemantics ("4-#0"forWHERE="000"/zone == 0,"4-101"forWHERE="101").WHO=13(OWNGatewayCommand):is_clock_query(*#13**0##,*#13**1##,*#13**22##).set_datetime(dt, time_zone=None)(*#13**#22*...##),report_datetime(dt, time_zone=None)(*#13**22*...##),set_date(d, time_zone=None)(*#13**#1*...##),report_date(d, time_zone=None)(*#13**1*...##),set_time(t, time_zone=None)(*#13**#0*...##),report_time(t, time_zone=None)(*#13**0*...##), acceptingdatetime.tzinfo | str | Nonefortime_zone, rejecting naivedatetime/timeinputs whentime_zone is None, and rejecting UTC offsets that are not whole hours (Asia/Kolkata,Asia/Kathmandu,America/St_Johns), which the0HH/1HHfield cannot carry. Thereport_*forms are for answering on the event side. On the F461 command session they are answered as reads, so injection usesset_*.WHO=18(OWNEnergyCommand):is_dimension_query(*#18*WHERE*DIM##),is_auto_update_command(*#18*WHERE*#1200#1*TIME##), andstream_duration(0..255minutes).report_active_power(where, watts)(*#18*WHERE*113*W##),report_total_energy(where, wh)(*#18*WHERE*51*Wh##),report_current_month_energy(where, wh, year=None, month=None)(*#18*WHERE*52[#YY#M]*Wh##),report_partial_month_energy(where, wh, month=None)(*#18*WHERE*53[#M]*Wh##),report_partial_day_energy(where, wh, day=None)(*#18*WHERE*54[#D]*Wh##), andreport_streaming_state(where, duration, energy_type=1)(*#18*WHERE*1200#TYPE*TIME##).OWNd/profiles.py):GatewayProfile, allbool | None = None.Nonemeans unverified: no capture or published oracle verdict exists for that family. Callers decide what to do withNone.supports_dimension_response_injection: whether reply-form frames (*#1*W*1*…##,*#2*W*10*…##,*#4*W*0*T##,*#13**D*…##) inject state on the command session.supports_who4_command_injection: whether*#4*W*#0*TTTT##is accepted.supports_who18_response_injection: whether*#18*W*DIM*VAL##is acknowledged. This is not proof that the value reaches the bus.F461Profilesets them (False,True,True), from the live captures cited in its docstring. Every other profile,MH201Profileincluded, staysNone.OWNd/message/{lighting,automation,heating,energy,gateway}.py):require_non_empty_where(where)acrossreport_brightness,report_shutter_level,report_temperature,write_temperature, and allOWNEnergyCommand.report_*builders.bool, non-finite floats (NaN,±inf), and finite fractional floats (isinstance(x, float) and x % 1 != 0, e.g.50.9) withValueErrorrather than silently truncating viaint()across all protocol-integer builder parameters, and enforces domain bounds:report_brightness(0 <= level <= 100,0 <= transition <= 255),set_shutter_level(0 <= level <= 100),report_shutter_level(level in 0..100 or 255,10 <= state <= 15,0 <= priority, info <= 255, preservingpriority),encode_temperature(math.isfinite,-99.9 <= temperature <= 99.9,ROUND_HALF_UP),OWNEnergyCommand.report_*(_validate_non_negative_energy, paired integral2000 <= year <= 2099and1 <= month <= 12, integral1 <= day <= 31, integral1 <= energy_type <= 4, integral0 <= duration <= 255), andOWNGatewayCommand(datetime.datetime/datetime.date/datetime.timetype checks, explicittime_zonerequired for naivedatetime/time, and2000 <= year <= 2099).State diagram
stateDiagram-v2 direction TB state "Bus Query / Virtual-Device Injection Lifecycle" as ExposeFlow { [*] --> EventMonitor: Frame received on Event Session EventMonitor --> QueryDetected: is_status_query / is_brightness_query / is_shutter_status_query / is_temperature_query / is_clock_query / is_dimension_query EventMonitor --> StreamDetected: is_auto_update_command (*#18*W*#1200#1*T##) EventMonitor --> TranslationDetected: is_translation (*WHO*1000#WHAT*WHERE##) -> effective_what QueryDetected --> ProfileGate: Check GatewayProfile capabilities StreamDetected --> ProfileGate ProfileGate --> InjectDimWrite: WHO in {1, 2, 4} & supports_dimension_response_injection is not True\n-> set_brightness (*#1*W*#1*L*S##) / set_shutter_level (*#2*W*#11#001*P##) / write_temperature (*#4*W*#0*TTTT##) ProfileGate --> InjectWho18Reply: WHO == 18 & supports_who18_response_injection is not False\n-> report_active_power / report_total_energy / report_streaming_state (*#18*W*DIM*VAL##), ACK only ProfileGate --> InjectClockWrite: WHO == 13\n-> set_datetime (*#13**#22*...##) / set_time / set_date (report_* forms are answered as reads on F461) ProfileGate --> SkipUnsupported: flag is False (none set today) InjectDimWrite --> [*] InjectWho18Reply --> [*] InjectClockWrite --> [*] SkipUnsupported --> [*] }How is it tested
tests/test_message_protocol.py):test_own_message_helper_properties_and_effective_what: verifiesis_status_request,is_dimension_request,is_dimension_writing,is_dimension_reply,is_translation,effective_what, and allWHO=1,WHO=2,WHO=4,WHO=13, andWHO=18builders and query detectors (including 3-digit priority bitfield strings likepriority="001"onOWNAutomationCommand.report_shutter_level,datetime.tzinfoand IANA string timezones, and aware vs naivedatetime.time).test_expose_builders_infeasible_inputs: exhaustively tests rejection (ValueError) ofboolinputs (True/False),NaN,±inf, finite fractional floats (50.9,0.5,10.5,2026.5,18.5,60.5,1.5) across all integer-valued builder parameters, out-of-range brightness/shutter levels, out-of-range temperatures (< -99.9or> 99.9),ROUND_HALF_UPhalf-way rounding (20.25 -> 0203,20.35 -> 0204), negative energy values, invalid years/months/days, naivedatetime/timewithouttime_zone, invalidtime_zonetypes, half- and quarter-hour UTC offsets, and empty/malformedWHEREaddresses.tests/test_profiles.py):test_profile_injection_capability_flags:F461isFalse/True/True; every other profile and the generic fallback areNoneon all three flags.tests/test_golden_conformance.py,tests/golden/frames/*.yaml):community-plant-capturefixtures acrosswho01_lighting.yaml,who02_automation.yaml,who04_thermo.yaml,who13_gateway.yaml, andwho18_energy.yaml(bringing the corpus to 164 validated frames, including the2026-10-18live F461 capture fromMyHOME#753and the2026-10-19F461 plant-check table inMyHOME#750issuecomment-6277050571) withbuilder:blocks pinningOWNLightingCommand.report_brightness,OWNAutomationCommand.set_shutter_level/report_shutter_level,OWNHeatingCommand.report_temperature/write_temperature,OWNGatewayCommand.report_time/set_time/report_date/set_date/report_datetime/set_datetime, andOWNEnergyCommand.report_active_power/report_total_energy/report_partial_month_energy/report_partial_day_energy/report_streaming_statebyte-for-byte against real hardware captures.1,894 passed, 1 skippedlocally on Python 3.14 (CI covers 3.11–3.14) with 100.0% statement and branch coverage on all modified modules (automation.py,base.py,energy.py,gateway.py,heating.py,lighting.py,profiles.py),mypy OWNd tests/typingclean (0 issues),ruff checkclean.Where are the traces
1. Real Physical Bus Captures by Community Contributors (
community-plant-capture)Every frame shape built or classified in this PR is backed by authentic physical bus captures contributed by the community and pinned with
builder:blocks intests/golden/frames/(provenance recorded intests/golden/SOURCE.yaml):MyHOME/tests/fixtures/traces/&OWNd/tests/golden/frames/)F461(2.0.11)Expose(WHO=1, 2, 4, 13, 18),F411/4Lights &F411/25 s Shutter (2026-10-18capture,2026-10-19plant check)f461_live_2026-10-18/myhome_trace_F461_live_2026-10-18.json(OpenWebNet-HA/MyHOME#753)Plant-check table in
MyHOME#750issuecomment-6277050571(text only, no capture file)OWNd/tests/golden/frames/who02_automation.yamlOWNd/tests/golden/frames/who04_thermo.yaml@gdluck&@GreenGrassBlueOcean(MyHOME#753&MyHOME#750) — BTicinoF461(FW2.0.11,WHO=13code200,WHO=1013134.15.5.0) +BT-F411/4(06, 07),BT-F411/25 s shutter (05),KW4691+F430/2+F430/4(Zone 2)05(*2*1000#0*05##*2*0*05##*2*1000#1/2*05##*2*1/2*05##*2*0*05##); virtual cover25event-session translation bursts beforeNACKoncmd.2026-10-18 trace:
*#1*08*1*150*0##NACK 0.055 s vs*#1*08*#1*150*0##ACK 0.126 s;*#2*25*10*10*50*0*0##(priority0) NACK 0.055 s;*#4*2*0*0999##answered as a read with*#4*2*0*0249##(0.22 s);*#4*50*0*0215##NACK after 2.13 s vs*#4*50*#0*0215##ACK 0.155 s;*#13**0*22*51*00*999##answered as a read with*#13**0*22*51*02*999##(0.116 s) vs*#13**#0*22*51*00*999##ACK 0.071 s;*#18*51*113*450##/*#18*51*51*12345##/*#18*51*54*320##ACK 1.27–1.40 s,*#18*51*#1200#1*1/0##ACK 0.14–0.15 s, no events after.2026-10-19 plant check (comment table):
*#1*08*1*150*0##NACK 6–10 ms vs#1write ACK 83 ms;*#2*25*10*10*50*001*0##NACK 10 ms vs*#2*25*#11#001*50##ACK 83 ms, echoed*#2*25*#11#001#1*50##;*#4*50*0*0215##read, NACK after 4.2 s vs*#4*50*#0*0215##ACK 88 ms;*#18*51*113*450##ACK 1.2–1.7 s; the WHO 13 write frame sets the gateway clock.WHO=1Lighting (Dim 1report_brightness&1000#WHATtranslation)issue_466/myhome_trace_MH202_all_2026-09-26T16-59-17.jsonissue_466/myhome_trace_F454_f418u2_dimmer_2026-09-27T08-58-54.jsonissue_466/myhome_trace_MH202_f418u2_dimmer_2026-09-27T09-43-45.jsonOWNd/tests/golden/frames/who01_lighting.yaml@anotherjulien(MyHOME#466comments5849027587,5854469497,5854805415) — BTicinoMH202(FW 2.6.13) +F454(FW 2.0.5) +F418U22-ch dimmer (WHERE 14, 32)*1*1000#1*14##,*1*1000#0*14##,*1*1000#30*14##,*1*1000#31*14##(effective_what) and Dim 1 replies*#1*32*1*100*0##(OFF),*#1*32*1*130*5##,*#1*14*1*130*5##..*#1*14*1*200*5##(report_brightness)WHO=2Automation (Dim 10report_shutter_level, Dim 11set_shutter_level&1000#WHATtranslation)issue_304/myhome_trace_MyHomeServer1_who2_2026-10-08T10-49-50.jsonissue_304/myhome_trace_MyHomeServer1_who2_2026-10-08T10-49-12.jsonissue_466/myhome_trace_MH202_all_2026-09-26T16-59-17.jsonissue_466/myhome_trace_MyHomeServer1_scenarios_cover_diagnostic_2026-09-27T13-47-33.jsonf461_live_2026-10-18/myhome_trace_F461_live_2026-10-18.jsonOWNd/tests/golden/frames/who02_automation.yaml@f18m(MyHOME#304comment6060208135—MyHomeServer1FW 2.53.16 +LN-4660M2shutters91..95),@anotherjulien(MH202+F454, shutters15, 22, 31),@TheDarkWizard(MyHOME#466comment5857411555),@gdluck&@GreenGrassBlueOcean(F461FW2.0.11+BT-F411/205,MyHOME#753&MyHOME#750)*#2*95*#11#001*29##*2*1000#0*95##*#2*95*10*12*40*000*0##(closing)*#2*95*10*10*29*000*0##(stopped at 29%);*#2*25*#11#001*50##*#2*25*#11#001#1*50##(F461monitor translation);*#2*31*10*10*40*001*0##(priority="001"preserved);*#2*15*10*11*0*000*0##(opening);*#2*44*10*10*100*000*0##; physicalF411/205(*2*1000#0*05##,*2*1000#1*05##,*2*1000#2*05##)WHO=4Heating (Dim 0report_temperature,write_temperature& 3-digit external probes)issue_466/myhome_sweep_MH200N_all_2026-09-25T14-40-10.jsonissue_466/myhome_trace_MyHomeServer1_all_2026-09-29T17-57-37.jsonissue_466/myhome_trace_MyHomeServer1_scenarios_cover_diagnostic_2026-09-27T13-47-33.jsonf461_live_2026-10-18/myhome_trace_F461_live_2026-10-18.jsonOWNd/tests/golden/frames/who04_thermo.yaml@caiosweet(MH200NFW 1.1.8,MyHOME#466comment5834435606),@gdluck(MyHomeServer1FW 3.87.13 &F461FW 2.0.11,MyHOME#753),@TheDarkWizard(MyHomeServer1FW 2.38.9)*#4*105*0*0296##(29.6 °C on probe105) and*#4*169*0*...##(probe169), zone temperatures*#4*2*0*0271##(27.1 °C) and*#4*2*0*0249##(24.9 °C onF461KW4691), and Dimension#0write*#4*50*#0*0215##(write_temperature("50", 21.5)ACKed in 88 ms / 155 ms onF461)WHO=13Gateway Clock (Dims0,1,22set_*&report_*)issue_466/config_entry-myhome_F455.jsonissue_466/myhome_trace_MH202_all_2026-09-26T16-59-17.jsonissue_466/config_entry-myhome_F454_step_commands.jsonf461_live_2026-10-15/myhome_trace_F461_live_2026-10-15.jsonf461_live_2026-10-18/myhome_trace_F461_live_2026-10-18.jsonissue_466/myhome_sweep_MH200N_all_2026-09-25T14-40-10.jsonOWNd/tests/golden/frames/who13_gateway.yaml@lionelser(F455),@anotherjulien(F454+MH202),@caiosweet(MH200N),@gdluck&@GreenGrassBlueOcean(F461FW 2.0.11 &MH200FW 2.1.0,MyHOME#753)*#13**0##*#13**0*20*54*00*002##,*#13**#0*11*30*00*002##,*#13**1*04*15*10*2026##,*#13**#1*03*07*10*2026##,*#13**22*16*37*03*001*05*25*09*2026##,*#13**#22*17*33*54*002*05*11*09*2026##WHO=18Energy (Dim1200stream lifecycle + Dims113,51,53,54)issue_466/myhome_trace_F454_who18_2026-10-08T19-30-16.jsonissue_466/myhome_trace_MyHomeServer1_all_2026-10-01T07-59-11.jsonissue_466/myhome_sweep_F454_all_2026-09-26T16-59-13.jsonissue_466/myhome_sweep_MH200N_all_2026-09-25T14-40-10.jsonf461_live_2026-10-18/myhome_trace_F461_live_2026-10-18.jsonOWNd/tests/golden/frames/who18_energy.yamlOWNd/tests/test_who18_auto_update_reply.py@xtimmy86x(MyHOME#661comment6067730589,F454FW 2.0 +F52051..511),@Interstellar0verdrive(MyHomeServer1+F52051, 52),@anotherjulien(F454+F52052),@caiosweet(MH200N+F52051..57),@gdluck&@GreenGrassBlueOcean(F461FW 2.0.11,MyHOME#753),MyHOME#669(7X#0)TX *#18*56*#1200#1*2##RX *#18*56*1200#1*2##(~235 ms)*#18*56*113*9##..*#18*51*113*572##RX *#18*56*1200#1*0##(~126.18 s);*#18*51*113*450##,*#18*51*51*12345##,*#18*51*54*320##,*#18*51*#1200#1*1##,*#18*51*#1200#1*0##ACKed on liveF461;*#18*51*51*23791364##(Dim 51);*#18*52*53*1##(Dim 53);*#18*51*54*13165##(Dim 54);*#18*77#0*113*161##2. Emulated Gateway Firmware Oracle Traces (
own-firmware-oracle)tests/golden/firmware_oracle.jsonpins only these frames related to this PR:*#2*31*10##,*#2*31*#11#001*40##,*#4*1*0##,*#18*51*113##,*#18*51*51##,*#18*51*53##,*#18*51*54##,*#18*51*#1200#1*255##,*#18*51*#1200#1*0##. These are queries and writes, not report-form injection. No published oracle verdict exists for*#4*W*#0*TTTT##,*#4*W*0*TTTT##or any WHO 18 report frame, so no profile flag rests on the oracle. An earlier revision of this PR set F453/MH200N/MH200 overrides from an unpublished local replay; those overrides were removed ine374f3c.What does the Encyclopedia / Firmware Oracle / Live F461 Plant say
OpenWebNet-Encyclopedia+ Community Hardware Captures):WHO=1Dimension 1 (*#1*WHERE*1*LEVEL*SPEED##vs*#1*WHERE*#1*LEVEL*SPEED##):LEVELon the wire is100..200corresponding to0%..100%(100= OFF,200= 100%). On physicalF461(2.0.11),*#1*08*1*150*0##(report form) is NACKed (0.055 s in the 2026-10-18 trace, 6–10 ms in the 2026-10-19 plant check), whereas*#1*08*#1*150*0##(set_brightness#1write form) is ACKed (0.126 s / 83 ms).WHO=2Dimension 10 (*#2*WHERE*10*STATE*LEVEL*PRIORITY*INFO##), Dimension 11 (*#2*WHERE*#11#001*LEVEL##) & Command Translation (*2*1000#0/1/2*WHERE##):STATEis10(stopped),11(opening),12(closing),13(step-up),14(step-down),15(PUL);LEVELis0..100(%) or255(unknown position);PRIORITYis a 3-digit bitfield (001/000) on physical actuators or integer0..255. On physicalF461(2.0.11), the report form is NACKed:*#2*25*10*10*50*0*0##in 0.055 s (2026-10-18 trace) and*#2*25*10*10*50*001*0##in 10 ms (2026-10-19 plant check). By contrast,*#2*25*#11#001*50##(set_shutter_level#11#001write form) is ACKed in 83 ms and echoed onevtas*#2*25*#11#001#1*50##(@gdluck,MyHOME#750issuecomment-6277050571).WHO=4Dimension 0 (*#4*WHERE*0*TTTT##vs*#4*WHERE*#0*TTTT##): 4-digit signed tenths of a degree Celsius (0TTTfor1TTTforWHERE = 000(mapped to4-#0) or 3-digitpZZ(101–899). On physicalF461(2.0.11),bt_termoparses*#4*W*0*TTTT##without#0as a Dimension 0 read request returning Zone 2's*#4*2*0*0249##, whileOWNHeatingCommand.write_temperature("50", 21.5)(*#4*50*#0*0215##) is ACKed (0.155 s / 88 ms).WHO=13Dimensions0,1,22: Time (H*M*S*TZ), Date (0W*D*M*Y), and Combined Datetime (H*M*S*TZ*0W*D*M*Y), whereTZis0HH(1HH(0HH:F453AV(001),F454(001,002),MH200(000,001,002),MH200N(001) andMH202(000,002).MH201(3.6.27*#13**0*21*31*40*##and*#13**22*21*31*43**03*14*10*2026##, themh201_physical_plantfixture*#13**22*00*22*43**04*17*09*2026##, and the 3.3.6issue_466trace*#13**0*21*07*30*##), andF455.999(unconfigured):F461in the 2026-10-15 and 2026-10-18 traces, and oneF454(issue_297).MyHomeServer1has no clock frame in the fixtures, and no plant capture shows a1HHvalue; the only one is the openwebnet4j fixture*#13**22*09*37*30*102*03*01*05*2019##.F461,*#13**0*22*51*00*999##is answered as a read with the gateway's real time, like WHO 4, while*#13**#0*…##is ACKed and sets the clock.WHO=18Dimensions51,52,53,54,113,1200: On physicalF461, report-form frames (*#18*51*113*450##,*#18*51*51*12345##,*#18*51*54*320##) are ACKed in 1.27–1.40 s (1.2–1.7 s in the plant check) with no event after, and the plain read*#18*51*113##is ACKed in 0.91 s without a reply. So the ACK does not show the value reaching the bus (supports_who18_response_injection = TrueonF461means acknowledged).own-firmware-oracle):F461Profilesets them, from the live captures; every other profile isNone(unverified) until a capture or a published verdict exists.