Repository navigation
feat(message,profiles): add declarative expose frame builders, query helpers, and profile flags - #129
GreenGrassBlueOcean wants to merge 1 commit into
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
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(bt_luci&bt_termo): On physicalF461(2.0.11), incoming un-hashed response frames (*#1*08*1*150*0##,*#2*25*10*10*50*001*0##,*#4*50*0*0215##) on the TCP command session are either rejected withNACK(*#*0##) or parsed as dimension reads (*#4*2*0*0999##returns Zone 2's measured*#4*2*0*0249##), whereas#DIMwrite frames (*#1*08*#1*150*0##,*#2*25*#11#001*50##,*#4*50*#0*0215##) are ACKed in ~83–126 ms and forwarded to the SCS bus. Furthermore,F453andMH200NrejectWHO=4Dimension0/#0temperature injection entirely (nack+silent).WHO=18(bt_energy/bt_supervisione): Conversely,WHO=18does accept un-hashed dimension response frames (*#18*51*113*450##,51..54,1200#1), which are ACKed in 140 ms – 1.7 s on physicalF461(and supported on all gateways withWHO=18exceptMH200/MH200N).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_zoneand rejecting naivedatetime/timeinputs whentime_zone is None.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):supports_dimension_response_injection: bool = FalseonGatewayProfile(since real BTicino gateways reject un-hashed*#1*W*1*...##,*#2*W*10*...##, and*#4*W*0*...##frames on the TCP command session or parse them as dimension reads, requiring#DIMwrite framesset_brightness,set_shutter_level, andwrite_temperatureby default).supports_who4_command_injection: bool = TrueonGatewayProfile, overridden toFalseonF453ProfileandMH200NProfile(the two gateway families whosebt_devfirmware rejects*#4*WHERE*0*TTTT##and*#4*WHERE*#0*TTTT##withnack+silent).supports_who18_response_injection: bool = TrueonGatewayProfile, overridden toFalseonMH200ProfileandMH200NProfile(sincebt_energy/bt_supervisioneaccepts un-hashed*#18*WHERE*DIM*VAL##response frames).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 == False\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 == True\n-> report_active_power / report_total_energy / report_streaming_state (*#18*W*DIM*VAL##) ProfileGate --> InjectClockReply: WHO == 13\n-> set_datetime (*#13**#22*...##) or report_datetime (*#13**22*...##) ProfileGate --> SkipUnsupported: WHO=4 on F453 / MH200N (supports_who4_command_injection == False)\nor WHO=18 on MH200 / MH200N (supports_who18_response_injection == False) InjectDimWrite --> [*] InjectWho18Reply --> [*] InjectClockReply --> [*] 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, and empty/malformedWHEREaddresses.tests/test_profiles.py):test_profile_injection_capability_flags: verifiessupports_dimension_response_injection(Falseacross all profiles),supports_who4_command_injection(FalseonF453andMH200N;Trueon all others), andsupports_who18_response_injection(FalseonMH200andMH200N;Trueon all others).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-18and2026-10-20live physical BTicino F461 captures inMyHOME#753andMyHOME#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,828 passedacross Python 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-18&2026-10-20)f461_live_2026-10-18/myhome_trace_F461_live_2026-10-18.json(OpenWebNet-HA/MyHOME#753&MyHOME#750issuecomment-6277050571)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 (['*2*1000#0*25##', '*2*1000#1/2*25##']beforeNACKoncmd),set_shutter_level("25", 50)(*#2*25*#11#001*50##ACKed in 83 ms + echoed onevtas*#2*25*#11#001#1*50##) vsreport_shutter_level(*#2*25*10*10*50*001*0##NACKed in 82 ms);WHO=4write_temperature("50", 21.5)(*#4*50*#0*0215##ACKed in 84–155 ms) vsreport_temperature(*#4*2*0*0999##parsed by F461bt_termoas read returning*#4*2*0*0249##);WHO=1virtual08(*#1*08*#1*150*0##ACKed in 88–126 ms vs*#1*08*1*150*0##NACKed in 5 ms,*1*1/0/5*08##NACK after 2.06 s) and Group#1Dim 15 (*#1*#1*15*1/0##);WHO=13(*#13**#0*...##ACKed);WHO=18(*#18*51*113*450##,51..54,#1200#1*1/0ACKed)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 84–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)In addition to the community plant captures above, command-session injection behavior across all 12 emulated gateway firmwares is pinned in
tests/golden/firmware_oracle.json(sourced fromown-firmware-oracle/results/<PRODUCT>/<VERSION>/oracle/full/*.tsv):WHO=1Lighting (lighting-ts10.tsv):*#1*21*1##,*#1*21*4##,*#1*21*#1*150*3##across all 12 gateways.WHO=2Automation (automation-ts10.tsv,trace-corpus.tsv):*#2*31*10##,*#2*31*#11#001*40##*#2*31*#11#001#1*40##,*#2*31*10*10*50*0*0##,*#2*31*10*11*100*0*0##,*#2*31*10*12*0*0*0##across all 12 gateways.WHO=4Thermoregulation (thermo-ts10.tsv,thermo-probes.tsv):*#4*1*0##,*#4*1*0*0215##,*#4*1*#0*0215##,*#4*000*0##,*#4*101*0##across all 12 gateways.WHO=13Gateway Clock (gateway-ts10.tsv):*#13**0##,*#13**1##,*#13**22##,*#13**#0*...##,*#13**#1*...##,*#13**#22*...##across all 12 gateways.WHO=18Energy Management (energy-ts10.tsv):*#18*51*113##,*#18*51*51##,*#18*51*52#24#9##,*#18*51*53##,*#18*51*54##,*#18*51*#1200#1*1##,*#18*51*#1200#1*255##,*#18*51*#1200#1*0##across all 12 gateways.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 rejected withNACKin 5 ms, whereas*#1*08*#1*150*0##(set_brightness#1write form) is ACKed in 88–126 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),*#2*25*10*10*50*001*0##(report form) is rejected withNACKin 82 ms, whereas*#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 in 84–155 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(F454,F455,F461(MyHOME#753),MH200,MH200N,MH201,MH202, andMyHomeServer1captures.WHO=18Dimensions51,52,53,54,113,1200: Unlikebt_luciandbt_termo,bt_energy/bt_supervisionedoes accept un-hashed dimension response frames (*#18*51*113*450##,51..54,1200#1), which are ACKed in 140 ms – 1.7 s on physicalF461(supports_who18_response_injection = True).own-firmware-oracle):supports_dimension_response_injection,supports_who4_command_injection,supports_who18_response_injection):supports_dimension_response_injectiondefaults toFalseacross all profiles soWHO=1,WHO=2, andWHO=4use#DIMwrite frames (set_brightness,set_shutter_level,write_temperature) by default.*#4*1*#0*0215##on a command session producesverdict: out(bus frame$120001600000D7\r) onF453AV,F454,F459,F460,F461,H4684,MH200,MH202, andMyHomeServer1, but producesreply: nack, verdict: silent(0 bus frames) onF453(010100) andMH200N(010108). HenceF453ProfileandMH200NProfilesetsupports_who4_command_injection = False.supports_who18_response_injectiondefaults toTrueand is set toFalseonMH200ProfileandMH200NProfile.