Covers & Shutters (WHO = 2)¶
The MyHOME integration provides full control for motorized shutters, blinds, venetian blinds, and curtains operating on OpenWebNet WHO = 2.
In v2, setup and management are 100% UI-first: entities are automatically discovered from the SCS bus, and position calibration is handled natively through Home Assistant buttons and services without requiring manual YAML configuration files.
π Auto-Discovery¶
When your gateway connects to Home Assistant:
- Dynamic Bus Discovery: The integration listens to OpenWebNet
WHO = 2frames and scans the bus. - Device Creation: Each physical shutter actuator (
WHERE = 1..99or area/point addresses) is registered as a Home Assistantcoverdevice linked to your MyHOME Gateway. - UI Customization: You can rename the entity, assign it to an Area (e.g. Living Room, Master Bedroom), or change its icon directly in the Home Assistant UI (Settings β Devices & Services β Entities).
ποΈ Cover Types: Standard vs. Advanced¶
The integration distinguishes between two types of MyHOME covers:
| Cover Type | Supported Hardware | Position Control (set_cover_position) |
How Position is Handled |
|---|---|---|---|
| Advanced Covers | Legrand CΓ©liane 67557, Axolute H4661M2, Livinglight LN4661M2, BTicino F401 (advanced mode) |
β Native | Actuator hardware reports exact physical position back to the bus (Dimension = 10). |
| Standard (Timed) Covers | Standard relay actuators (F411/2, F411U2, standard F401, older flush-mount units) |
β Estimated | Actuator has no position feedback. The v2 runtime estimates position from the elapsed share of the travel time (travel_time_down and travel_time_up). |
Note
Every discovered cover starts as a timed cover. Position-reporting hardware is not detected automatically: set advanced_shutter: true for that cover in /config/myhome.yaml (see Troubleshooting β Cover position is wrong). Advanced covers refuse myhome.calibrate_cover and travel times in myhome.set_cover_travel_time, as they do not need a travel time (a Venetian blind with slat_tilt still accepts slat_time there).
Choosing the cover model (strategy)¶
Each WHO=2 cover runs one of four position models. Without strategy, the flags pick it: advanced_shutter: true gives native, slat_tilt: true gives venetian, travel_time: 0 gives simple, and anything else gives timed. Set strategy in /config/myhome.yaml to name it explicitly:
strategy |
Position | Use for |
|---|---|---|
timed |
Estimated from the travel time | Standard relay actuators (the default). Needs a positive travel_time. |
venetian |
Estimated, plus slat tilt | Venetian blinds; implies slat_tilt: true and needs a positive travel_time (or advanced_shutter). |
native |
Reported by the actuator (Dimension = 10) |
Advanced actuators; implies advanced_shutter: true. Can be combined with slat_tilt. |
simple |
None (open / close / stop only) | Relays with no measured travel time. The entity has no position slider and an assumed state: closed after a closing run, open after an opening one. |
An explicit strategy cannot be combined with a flag that selects another model (advanced_shutter with anything but native, slat_tilt with timed or simple, travel_time: 0 with timed): the configuration is rejected. A cover set up with travel_time: 0 becomes timed once it is calibrated (myhome.calibrate_cover or myhome.set_cover_travel_time), and simple again after myhome.reset_cover_travel_time. An explicit strategy: simple stays simple even when travel times are stored.
β±οΈ Timed Cover Position Engine¶
For standard covers without hardware position feedback, the integration provides a software estimator enabling full open_cover, close_cover, stop_cover, and slider-based set_cover_position support:
- Direction-Aware Travel Times: Because gravity and motor friction cause shutters to fall faster than they rise, the v2 engine tracks separate
travel_time_downandtravel_time_updurations (default:25.0s). - Frame Anchor Timing: The run timer starts when the direction frame is written to the gateway, not when Home Assistant queues it.
- Echo Suppression: The gateway relays a momentary stop status (~0.1 s) followed by translation and the actual motor start (~0.55 s). The v2 engine recognizes these as command echoes, re-anchoring the timer to the true motor start rather than falsely treating them as manual stop commands.
- Motor Start-Up Floor (
min_motor_pulse): A tubular motor needs a moment after the relay closes before the curtain moves. On an F454 with Somfy Ilmo 50 WT motors (20.4 s travel), 1 % steps (~0.2 s) clicked the relay without moving the curtain, while 2 % steps (~0.4 s) moved it (#466). A position change that would run shorter than the configured floor runs formin_motor_pulseseconds (default:0.4 s, configurable per cover via YAMLmin_motor_pulseormyhome.set_cover_travel_time;0.0disables), and the estimate books the movement of that longer run: on a 5 s cover with the default 0.4 s floor, 50 % β 51 % ends at 58 %. Other motors may need more or less. - Stop Status Re-Anchoring: When a timed run is stopped, the estimate is provisionally frozen at the stop write. If the gateway relays the stop status (
*2*0*<WHERE>##) within the echo window (measured at 34β47 ms, median 42 ms on a MyHOMEServer1 and 64β120 ms on an F461), the final position andlast_run_secondsare re-anchored tostop status - start anchor(#705). Gateways that do not relay the stop status keep the write-time freeze. - 0 % and 100 % Are Full Runs: Setting position to 0 % or 100 % sends a plain close / open, exactly like the buttons, and lets the motor stop on its limit switch instead of timing a stop. This also happens when the cover already reports 0 % or 100 %, so it is the way to put a drifted estimate back in step. A short move whose run would reach an end becomes a full run to that end too. No stop frame is sent, so the entity reports
opening/closinguntil the actuator's own stop status arrives, which can be long after the curtain reached the end (about 60 s on one MyHOMEServer1) or never on a gateway that does not relay it. The integration therefore settles on 0 % / 100 % once the stored travel time plus 2 s has passed, without a stop frame. That can be a few seconds before a slow curtain physically arrives; automations waiting foropen/closedsee the state change at that point. The open and close buttons are unchanged and still wait for the actuator's status. - Resynchronization: Running a cover to its full travel limit (fully open or fully closed) automatically resets any minor timing drift to 0% or 100%.
Note
Small moves can still drift. The estimate assumes the curtain moves for the whole run. A motor's start-up time (measured only on the Somfy Ilmo 50 WT in #466; another installation measured none on runs of about 0.76 s) is lost on every run, and the slat phase of a roller shutter with slats is booked as travel (one report: a 40 % request from fully closed lifted the curtain only about 18 % of its travel). A series of short moves can therefore book more travel than the curtain made. Send the cover to 0 % or 100 % now and then to re-sync it.
Venetian blinds are handled as a plain lift cover: lift only, no tilt, and no fine positioning.
Warning
Half the time is not half the height. The estimate is linear: 50 % means the motor ran for half of the stored travel time. A roller shutter does not move at constant speed β coming down from the top the curtain runs fast on a full roll and is well past the middle at half time; going up from the bottom the first seconds go into gathering the slats and the curtain barely moves. Measured on a 107 cm shutter with exact travel times: set_cover_position: 50 stopped at 27 cm from the sill coming down and at 37 cm going up, against a true midpoint of about 53 cm, while Home Assistant reported 50 % both times. The end positions (0 % / 100 %) are exact; intermediate positions are approximate and differ by direction. Treat the slider as "roughly there", not as a measurement β this applies to calibrated and manually set times alike.
Advanced covers track their own level, but the actuator turns a level change into a timed run the same way, so a 1 % change is just as likely to click without moving. The integration widens a change of less than 2 % to 2 % (50 % β 51 % is sent as 52 %), except while the cover is moving (it then reports the level it started from, so the request goes out as asked), and sends 0 % and 100 % as plain close / open full runs. The 2 % comes from the 20.4 s motor above; a faster motor may need more, and the behaviour of the level command for small changes is inferred from the step commands in that trace, not captured.
π Calibrating Travel Time (Zero YAML)¶
You never need to edit YAML files to calibrate travel times in v2.
Important
Actuator Requirements for Automated Calibration
Automated on-bus calibration relies entirely on the actuator emitting a stop frame (*2*0*<WHERE>##) at the moment the shutter physically reaches its end stop.
- Trimmed Actuators: If the installer trimmed the actuator's mechanical or potentiometer run-time limit to match the physical shutter, automated calibration will measure your shutter travel times accurately down to tenths of a second.
- Factory 60 s Cutoff Actuators: If the actuator run-time was left at the factory default cutoff (~60 s), the actuator will not stop when the shutter reaches the bottom; it will continue running until the 60 s hardware timer expires. The integration detects this condition and safely rejects the calibration to prevent saving inaccurate 60 s run times. For these actuators, use Method 3: Set Explicit Travel Time via Service Action instead.
How to spot your case: press the button once and watch the first (opening) run. If the motor keeps running for about a minute after the shutter is fully up, your actuator is at its factory limit and cannot be measured over the bus β the button will refuse every time. You will not see the refusal in the UI (the button does not wait for the result): it is logged, and the myhome_cover_calibration event reports phase: failed. Nothing is stored.
Which actuators (from datasheets and user reports, awaiting full hardware traces under #466): the F411/2, F411/4 and F411U2 have a configurable timed stop β the physical M configurator, or 1β60 s in MyHOME_Suite. Setting it a couple of seconds above the real travel allows these to be measured; a run that ends at ~60 s indicates it was left at its factory default M=0. The F401 and the H4661M2 / LN4661M2 family learn the real travel themselves (Push&Learn), which is the case where the end stop on the bus is genuine.
Method 1: Automated Calibration Button on Device Page¶
Every cover device in Home Assistant includes a dedicated configuration button entity (on a position-reporting cover a press is refused, as there is nothing to measure):
- Entity:
button.<name>_calibrate_travel_time - Icon:
mdi:ruler-square-compass
When you click Calibrate Travel Time, the integration performs a fully automated 3-step calibration sequence on the bus:
- Full Open (Reference Sync): The shutter is driven fully UP to reach the mechanical top limit, establishing a reliable reference position.
- Full Close (Timed): After a brief settling pause, the shutter is driven fully DOWN. The integration monitors the OpenWebNet bus to precisely record
travel_time_down(from motor start to actuator stop frame). - Full Open (Timed): After another pause, the shutter is driven fully UP back to the top limit, recording
travel_time_up. - Saved Automatically: Both measured times are persisted into Home Assistant's config entry (
cover_travel_times) and shown as the cover'stravel_time_down/travel_time_upattributes, withcalibration_source: measuredand acalibrated_attimestamp. The shutter ends in the 100% open position. Progress is published on the event bus asmyhome_cover_calibration(phase:queued,start,run,done,failed), so you can follow or automate on it.
Note
- Do not press the button a second time: The sequence is fully automated. A second press while the run is active is refused ("β¦ is already being calibrated"); the button fires the action without waiting for it, so the refusal only shows up in the Home Assistant log, not in the UI. Each run also gives up with no stop status from the actuator if no stop frame arrives within 180 s.
- Do NOT stop the shutter during calibration β neither from the cover entity nor from the wall: A stop press from either place puts a plain stop frame (
*2*0*<WHERE>##) on the bus, which is the very same frame the actuator emits when the shutter reaches its end stop. The integration cannot tell them apart, so it treats the stop as the end of the run and stores the partial elapsed time (e.g. 5β6 seconds) as the calibrated travel time. - How to Abort Safely: The one safe way out is the
myhome.stop_cover_calibrationaction in Developer Tools β Actions: it marks the run as interrupted and stores nothing. Call it while the shutter is moving β a call that lands in the one-second pause between two runs is not seen and the next run starts. (Driving the shutter in the opposite direction from the wall is also recognised as an interruption, because it arrives as an explicit open/close command rather than a stop β but prefer the action.)
Method 2: Calibrate All Covers Sequentially¶
On your gateway device page, click Calibrate All Covers (button.<gateway name>_calibrate_all_covers, e.g. button.f454_gateway_calibrate_all_covers). It calls myhome.calibrate_cover for every enabled cover of that gateway; a per-gateway lock runs them one at a time, because two motors moving on one paced session would make the timings meaningless. Covers still waiting report phase: queued. Position-reporting (advanced) covers are refused with an error in the log and the others continue. myhome.stop_cover_calibration stops the running cover and drops the whole queue.
Method 3: Set Explicit Travel Time via Service Action¶
If your actuators enforce the 60-second factory cutoff, if your gateway is single-session (such as MH200 / MH200N), or if you already know the exact run duration (e.g. from a handheld stopwatch or technical datasheet), set it directly using the myhome.set_cover_travel_time action in Developer Tools β Actions.
You rarely need a handheld stopwatch: the integration times every run of a timed cover, calibration or not, and exposes the result as plain entity attributes β no card required.
- Open the cover in Home Assistant (or use its wall switch) and press Close.
- Press Stop (entity or wall switch) the moment the shutter reaches the bottom end stop.
- Go to Developer Tools β States, filter on the cover entity:
last_run_secondsis the motor-start-to-stop time of the run you just made andlast_run_directionsaysclose. The same attributes are listed under Attributes in the cover's more-info dialog. - Repeat with Open for the up time, then pass both numbers to the action (or put the attributes and a Save button on your dashboard with Lovelace Recipe 6):
action: myhome.set_cover_travel_time
target:
entity_id: cover.living_room_shutter
data:
travel_time_down: 22.5
travel_time_up: 24.0
min_motor_pulse: 0.4
When setting travel times, the result is stored like a measured calibration, with calibration_source: manual, and overwrites whatever a previous calibration attempt may have saved (also visible under cover_travel_times in the integration's diagnostics download). When only min_motor_pulse is set, existing travel times, source, and calibration timestamps remain untouched. When the numbers you pass were measured on an identical shutter, add copied_from: cover.<other_shutter> to record where they came from; calibration_source then reads copied and the entity is exposed as a copied_from attribute.
To forget the measured or manual times and return to the travel_time from myhome.yaml (or the 25 s default when none is configured):
πͺ Venetian Blinds & Slat Tilt Positioning (Timed Slat Inching)¶
For Venetian blinds, motorized louvres, and external venetian blinds (EVBs/BSO) with adjustable slat tilt angles, the integration provides timed slat travel (inching):
- Supported Features:
SET_TILT_POSITION,OPEN_TILT(100%),CLOSE_TILT(0%), andSTOP_TILT. - Device Class: Sets the entity device class to
blind(CoverDeviceClass.BLIND). - Attributes: Exposes
current_cover_tilt_position(0β100%),slat_tilt: true, andslat_time: <seconds>.
Physical Architecture & Slat Travel Time¶
Physical BTicino / Legrand MyHOME Venetian blind actuators (such as the F411U2 configured in Venetian mode) control slat tilt angle mechanically through brief motor pulses: when the motor runs, the slats first rotate through their full angular range (typically 1.5β2.5 seconds) before linear curtain lifting or lowering commences.
The integration models this behavior with timed travel:
* When a tilt command is issued, the shutter motor runs in the required direction for a calculated duration:
$$\text{duration} = \frac{|\text{target_tilt} - \text{current_tilt}|}{100} \times \text{slat_time}$$
* The pulse is timed from the motor start, not from the frame write: from the direction status the gateway relays (*2*1*<WHERE>## / *2*2*<WHERE>##), or, when the gateway relays none, from the write plus the measured 0.55 s motor-start delay (see Runtime behaviour β Timed covers). Timed from the write, a pulse shorter than about half a second would end before the motor started. Nothing is counted while the frame still waits in the send queue.
* When the target duration elapses, an automatic stop command (*2*0*<WHERE>##) halts the motor. The angle is settled when that stop is written, not when it was due: the send queue is first-in first-out, so behind other frames (several blinds tilted at once, a busy gateway) the stop leaves late and the slats turn on until then. The angle follows the real motor time at one full rotation per slat_time, also past the target, up to 0 % / 100 %. Motor time past a full rotation moves the curtain, and a timed cover's position estimate moves by that time over its curtain travel time (travel_time - slat_time; a position-reporting cover reports its own level).
* A new tilt target while the previous pulse's stop still waits in the queue waits for that stop to be written, so it starts from the real angle; the new direction frame could not leave before that stop anyway. Tilt requests run one at a time, and a newer target replaces one still waiting, so a dragged slider sends only where it ended.
* A new target in the same direction while the pulse still runs moves the pulse's stop and sends nothing; only a change of direction sends a new direction frame.
* Like any command, a pulse opens an echo window: the stop status the gateway relays right after our direction frame is ignored, so it cannot cancel the pulse's stop and leave the motor running.
* Steps shorter than the cover's min_motor_pulse (default 0.4 s, or 20 % at slat_time: 2.0) are skipped: the motor would barely answer while the angle was recorded as changed. This floor has not been measured on a Venetian actuator yet.
* A tilt command while the curtain itself is running is refused ("β¦ is moving; stop it before tilting the slats"). The run turns the slats anyway, and a pulse would leave the curtain's travel estimate behind. Opening, closing or setting the position during a pulse ends the pulse at the angle it reached; the new command takes the motor over, and the pulse's own stop is dropped. Setting the position the blind is already at, while a pulse or a run is going, stops the motor there.
* During a curtain run the slats turn first at one full rotation per slat_time (towards 100 % when opening, towards 0 % when closing), and only the remaining motor time moves the curtain estimate over travel_time - slat_time. A stop status with no run behind it (such as the relay of a pulse's own stop) leaves the angle alone, so slats tilted open on a lowered blind stay open.
Configuration¶
Slat tilt and travel duration can be declared in /config/myhome.yaml:
cover:
living_room_venetian:
where: '31'
name: Living Room Venetian Blind
slat_tilt: true
slat_time: 2.0
| Parameter | Type | Default | Description |
|---|---|---|---|
slat_tilt |
boolean | false |
Enables Venetian blind slat tilt controls and sets device class to blind. |
slat_time |
float | 2.0 |
Full slat rotation travel time in seconds (allowed range: 0.5β10.0 s). A value set with myhome.set_cover_travel_time is stored and wins over this one after a restart, until myhome.reset_cover_travel_time. |
Higher tilt is assumed to be open (an opening pulse raises the angle). This, and the slat time of real actuators, still awaits a keypad / app capture of a Venetian actuator (#492).
Protocol Note: Dimension 11 is Shutter Level¶
In OpenWebNet WHO 2, Dimension 11 (*#2*<WHERE>*#11#PRIORITY*LEVEL##) represents an absolute Go to level / Shutter position command, not slat tilt angle. Gateway firmwares (MH200N, MyHomeServer1) reject query frames (*#2*<WHERE>*11##) with a NACK because Dimension 11 is write-only.
When an actuator or external controller broadcasts a Dimension 11 event (such as *#2*31*#11#001#1*40## indicating 40% level), the integration ignores it: the motor needs time to get there and a keypad can stop it midway, so the position only follows the actuator's own status reports (Dimension 10 / status frames).
Travel Time Calibration & Slat Rotation¶
End-to-end travel time (travel_time_down / travel_time_up) includes one full slat rotation (slat_time) at the start of the run:
* On a slat blind, linear curtain travel is estimated over travel_time_down - slat_time and travel_time_up - slat_time after the slats finish rotating.
* Mid-tilt stops do not corrupt or reset linear curtain position estimation.
* Tilt commands are safely rejected if the cover is actively running automated travel-time calibration.
π General, Area and Group Covers¶
A cover declared in /config/myhome.yaml on a general (where: '0'), area (where: '1', '00', '100') or group (where: '#3') address is one button for many shutters: open, close and stop send a single frame to that address, like the general button on a keypad.
Its state comes from the shutters it moves, not from the bus. At the end of a run every actuator reports its own stop (*2*0*11##, *2*0*12##, β¦) and none arrives for the general or area address (WHO_2 specification Β§3.0.1). So the cover shows opening / closing while any of its shutters is moving, closed when all are closed, and the average of their positions:
| Address | Shutters it follows |
|---|---|
General 0 |
every point-to-point cover of the gateway |
Area 1β¦9, 00, 100 |
the covers of that area on the same bus: area 1 is 11β¦19 and 0110β¦0115 |
Group #1β¦#255 |
the covers listed under members:; group membership is programmed in the actuators and cannot be read from the bus |
cover:
all_shutters:
where: '1' # area 1: follows covers 11-19 on its own
name: All shutters
bedrooms:
where: '#3'
name: Bedrooms
members: ['11', '13']
Behind an F422 interface (bus_interface: '02', logical #4# addressing), physical actuators reside on an isolated secondary SCS bus segment (e.g. 11#4#02 β¦ 22#4#02).
ποΈ The BTicino Way & Real-World Plant Discovery¶
In large MyHOME installations, F422 interfaces physically segment the SCS bus (e.g., separating ground floor from upper floors or main house from outbuildings/curtain lines). How commands are routed across an F422 is fundamental to reliability:
1. Why Naive Fan-Out Overwhelms F422 Hardware Buffers¶
In real-world plant operation with an MH200 gateway and F422 interface, an automation triggering 10 individual shutters at sunset (action: cover.close_cover targeting 10 individual entities) encountered reproducible dropped frames:
- Home Assistant dispatched 10 concurrent requests via asyncio.gather().
- When fanning out 10 individual point-to-point OpenWebNet commands (*2*2*14#4#02##, *2*2*19#4#02##, *2*2*18#4#02##, β¦), even with the gateway's worker queue pacing (e.g., 0.15 s per frame), the burst overwhelmed the physical F422 interface's internal FIFO buffer and secondary SCS bus arbitration.
- By the 6th or 7th consecutive command, the F422 buffer overflowed. The gateway returned NACK (*#*0##), causing OWNd to cancel delivery and abort motion on the un-acknowledged curtain (e.g. 13#4#02, "Oost 1"). The shutter remained stuck open while the rest closed.
- Compounding this, a common legacy automation pattern of placing a delay: 45s followed by an explicit cover.stop_cover sent *2*0*13#4#02## 45 seconds later when the bus was quiet again. The actuator acknowledged the stop frame, permanently cementing the stuck shutter in its open position and masking the failure.
2. Native Broadcast: The BTicino Protocol Architecture¶
BTicino physical keypads and scenario units (such as LN4660M2 or CEN/CEN+ controls programmed with A=GEN or Area A=1..9) never send individual point commands. They emit native OpenWebNet broadcasts:
- Main Bus General: *2*WHAT*0##
- Secondary Bus General via F422: *2*WHAT*0#4#<interface>## (e.g., *2*2*0#4#02## for General Down on interface 02, *2*1*0#4#02## for General Up, *2*0*0#4#02## for General Stop)
- Secondary Bus Area via F422: *2*WHAT*<area>#4#<interface>## (e.g., *2*2*1#4#02##)
The gateway routes the single broadcast frame through the F422 interface onto the secondary bus once. Every actuator on that bus reads the frame off the physical wire simultaneously. All relays energize in unison with zero queue latency, zero interface congestion, and 100% mechanical synchronization.
βοΈ How the MyHOME Integration Handles Scope Covers¶
The MyHOME integration implements a dual-layer architecture adhering to both Home Assistant standards and BTicino protocol engineering:
- Native Broadcast for General Scope Covers (
where: '0'): Whenopen_cover,close_cover, orstop_coveris called on a General cover behind an interface (where: '0',bus_interface: '02'), the integration sends the native OpenWebNet broadcast frame directly (*2*WHAT*0#4#<bus>##). - Actuators on the interface bus respond simultaneously without dropping frames.
- When the gateway echoes
*2*WHAT*0#4#<bus>##, the integration scopes the incoming frame strictly to entities on that interface (interface == "02"), preserving total bus isolation. -
When individual actuators reach their end stops and report status frames (
*2*0*11#4#02##, etc.), Home Assistant tracks each entity and aggregates the General cover's position automatically. -
Paced Member Fan-Out (
PACED_FANOUT_DELAY = 0.3s) for Percentage Positioning: OpenWebNet has no broadcast protocol for percentage positioning (Dimension 10 positioning*#2*WHERE*#10*LEVEL##is strictly point-to-point; there is no broadcast level command). Whenset_cover_positionis called on a scope/group cover, or when non-general scopes must address individual actuators, the integration staggers member commands with a safety interval of 0.3 s (300 ms). This gives the F422 hardware FIFO buffer adequate time to serialize frames onto the secondary bus, completely eliminating frame loss during multi-cover repositioning. -
Keypad Broadcast Reflection: A general or area command from a physical wall switch or scenario unit also moves the individual covers in Home Assistant. A group or area cover whose shutters are not known yet ends its run after its own
travel_time, so it never stays stuck on opening (#433).
π‘ Automation Best Practices: The Home Assistant & BTicino Way¶
To ensure maximum reliability and preserve bus health:
Recommended: Target the General Scope Cover¶
Declare a General scope cover in /config/myhome.yaml for each bus interface, and target that single entity in your automations:
# /config/myhome.yaml
00:03:50:XX:XX:XX:
cover:
all_shutters_oost:
where: "0"
bus_interface: "02"
name: "All Shutters East"
In your automation:
alias: Close Windows at Sunset
triggers:
- trigger: sun
event: sunset
offset: "00:30:00"
actions:
- action: cover.close_cover
target:
entity_id: cover.all_shutters_oost
Retire the Legacy delay: 45s + stop_cover Anti-Pattern¶
In legacy configurations, users often placed a delay: 45s followed by cover.stop_cover to ensure motors stopped. This pattern is unnecessary and counterproductive:
- BTicino actuators (F411, F401, LN4661M2) incorporate hardware limit switches and configurable travel timers (e.g. configurator M or MyHOME_Suite cutoff).
- The MyHOME integration automatically calculates direction-aware travel times (travel_time_down / travel_time_up) and handles stop detection.
- Sending a delayed cover.stop_cover floods the bus with redundant frames and can accidentally cement a stalled or out-of-sync shutter. Let the hardware and integration manage motion termination naturally.
πͺ Slat Tilt on Scope Covers¶
Slat tilt on a scope cover (with slat_tilt: true): on the main bus one scope pulse turns the slats of every member with slat_tilt. The members follow that pulse (its direction and stop frames turn their slats and do not count as a curtain run), and the scope cover times and settles it for all of them. A scope tilt is refused while one of its covers is running. Behind an F422 each blind gets its own pulse, one after the other: queued together, every stop would wait behind the other blinds' direction frames and the slats would turn on. The last blind therefore moves a few seconds after the first.
πͺ Impulse Covers: Motorized Gates & Garage Doors (WHO = 1)¶
In BTicino MyHOME installations, motorized garage doors and sliding/swing entrance gates are controlled via monostable impulse relays on WHO = 1 (such as F411/2 or F411U2).
Because monostable relays only send a momentary pulse (open / stop / close / stop) with no inherent directional knowledge on the bus, Home Assistant implements the specialized MyHOMEImpulseCover entity.
By default the cover is a plain hardware interface. open_cover, close_cover and stop_cover pulse the relay straight away, from a dashboard, a wall tablet, a button, a script or a voice assistant; nobody has to approve anything on a phone. The integration only does what belongs to the hardware:
- It sends the pulse (hardware-timed WHAT 18 where the installed OWNd has it, ON followed by OFF otherwise) and keeps a deadband (
min_cycle_time) so a double tap cannot send two pulses. A pulse that was not sent raises an error. - With a
state_sensorit knows the real position:open_coveron an open or opening door andclose_coveron a closed or closing door send nothing, because a pulse would only undo the request. Without a sensor the position is assumed and every pulse goes out as asked. stop_coveris never withheld.- If the OFF that ends a software pulse cannot be delivered it is retried, and the relay is released before the next pulse.
Who may operate the door, from where and with which confirmation is a policy of your installation. Build it with automations or blueprints, or switch on the optional supervised workflow below with an access: block.
π‘οΈ Safety Architecture & Threat Model¶
Caution
HOME ASSISTANT IS A SUPERVISORY AUTOMATION SYSTEM, NOT A CERTIFIED SAFETY SYSTEM
Primary entrapment and crush protection (e.g. European Standard EN 12453 / EN 12445 or North American UL 325) must be provided by the motorized gate or garage door hardware itself: certified infrared photocells, safety contact edges, and mechanical force limiters built into the motor control board.
The optional AccessController is designed exclusively to prevent Home Assistant from causing an unintended, accidental, or unattended movement.
π Optional Human-in-the-Loop Access Policy (access:)¶
Everything in this part applies only to a cover that has an access: block. Without it the cover pulses directly, as described above.
[ User / Dashboard / Widget / Siri / Alexa ]
β
β 1. cover.open_cover / close_cover
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β MyHOME AccessController β
β β
β [ Check User & Lockout ] ββ( unauthorized )ββ> β AccessDenied β
β β (authorized) β
β βΌ β
β [ Classify State Sensor ] β
β ββ Sensor = Closed βββββββββ> Effect: OPEN β
β ββ Sensor = Open / Unknown ββ> Effect: MAY_CLOSE β
β β β
β [ Safety Verification ] β
β - Photocells valid (<31d)? β
β - User at home / Camera ok? β
β - Close-block switch off? β
β β (all pass) β
β βββββββββββββββββββββββββββββββ β
β βΌ β
β [ Generate 128-bit CSPRNG Nonce ] β
β - Single-use token: secrets.token_urlsafe(16) β
β - Bound to: user_id + entity_id + effect + 60s timeout β
βββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββ
β 2. Push Notification
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Companion App (iOS / Android) β
β β
β π "Security Request: Approve garage_door_1 close?" β
β [ β
Area Clear - Close ] (authenticationRequired: true) β
β β
β π User unlocks phone via FaceID / Fingerprint / Biometrics β
βββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββ
β 3. mobile_app_notification_action
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β MyHOME AccessController β
β β
β [ Validate Nonce & User Context (constant-time HMAC) ] β
β β (valid) β
β βΌ β
β [ Pre-Warning Phase (optional 5s flasher) ] β
β ββ( wall switch pressed? )ββ> β Cancel movement β
β β (clear) β
β βΌ β
β [ Bus Impulse ] ββ> *1*1*WHERE## (WHO 1 Relay Monostable Pulse)β
β β β
β βΌ β
β [ Watchdog Timer (travel_time + margin) ] β
β ββ Sensor confirms target state ββ> β
IDLE (Success) β
β ββ Sensor fails to reach state ββ> β οΈ FAULT (Latched) β
β (Zero auto-retry) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
π Authentic BTicino Hardware Sequence (MH200 Trace Capture)¶
Entrance Panel (PE1) MH200 Gateway Handset (74) Gate/Garage Relay (71/72)
β β β β
β 1. Bell Pressed β β β
βββββββββββββββββββββββββββ>β β β
β *#16*81*1*1##..88## β β β
β (Audio matrix routing) β β β
β β β β
β *8*1#1#4*74## β β β
β (WHO 8 Video Call) β β β
β ββββββββ Ring βββββββββ>β β
β β β β
β β<βββ Pick Up / Talk ββββ β
β β *8*9#1#4*73## β β
β β (Caller address) β β
β β β β
β β<ββ Hang Up / End ββββββ β
β β *6*9## β β
β β (Camera OFF) β β
β β β β
β β β 2. Gate Button Pressed β
β β<βββββββββββββββββββββββ β
β β *1*1*72## β β
β β (WHO 1 Pulse) β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββ>β
β β β [ Gate Moves ]
Key Principles of the Optional Human-in-the-Loop Policy (with access:):¶
- Zero Direct Movement on Open and Close Calls:
Calls to
cover.open_coverorclose_cover(whether from dashboards, mobile widgets, voice assistants, or automations) never pulse the relay directly. Instead, they create an authenticated approval request for an authorized person. Automations and scripts are refused because they have no person behind them.stop_coveris the exception: while a request is pending, the pre-warning runs or the door moves under supervision, it cancels the request or pulses the relay to stop the door at once, for anyone, without approval. - Cryptographic Single-Use Nonce & User Binding:
Each approval request generates a 128-bit CSPRNG token (
secrets.token_urlsafe(16)), tightly bound to the requestinguser_id, target entity, and physical direction. Replayed, stale, or forged approval actions are discarded. - Biometric Phone Unlock Requirement:
Actionable push notifications to the Companion App enforce
authenticationRequired: true(iOS FaceID/TouchID, Android biometrics/screen lock). The phone must be actively unlocked by the authorized approver. - Fail-Closed Sensor Direction Checks:
A pulse is classified as a pure OPEN only when the ground-truth state sensor (
state_sensor, a binary sensor that isoffwhen the door is closed andonotherwise) proves the door is closed. In all other states, the pulse is classified as MAY_CLOSE, requiring: - Verified active safety devices within
safety_check_days. - The user to be on site (
personentity reportshome) or visual confirmation via a camera snapshot. - Any external close-block switch to report
off. - Any missing, unavailable, or non-binary sensor state immediately fails closed.
- Audible / Visual Pre-Warning & Watchdog:
Before a close pulse, an optional pre-warning flasher (
prewarn_light) triggers forprewarn_seconds. After the pulse, an anti-stuck watchdog monitors motion: if the expected state is not reached withintravel_time + watchdog_margin, a latching fault is asserted with zero automatic retries. A latched fault survives a restart or reload of Home Assistant and can only be cleared on site by an authorized user using themyhome.acknowledge_cover_faultaction; an in-flight request or pre-warning can be cancelled at any time usingmyhome.cancel_cover_request(see Services). - A Wall Button Starts the Gate, It Does Not Stop It:
A press of the wall button (or any other keypad on the bus) closes the relay and energizes the motor by itself; Home Assistant only sees the resulting
*1*1*WHERE##frame afterwards. When such a frame arrives during an approval request or the pre-warning countdown, Home Assistant therefore drops its own pending request so that no second pulse follows and stops or reverses the gate. The movement the button started is not aborted. Use the hardware's own stop input or the remote control for an emergency stop.
βοΈ Configuring an Impulse Cover¶
An impulse cover needs who: 1 and a single point-to-point where. The minimal configuration is a hardware interface with no approval step:
# /config/myhome.yaml
00:03:50:XX:XX:XX:
cover:
garage_door:
who: 1
where: "71"
name: Garage door
device_class: garage
state_sensor: binary_sensor.garage_door_closed # optional: off = closed, on = not closed
Add an access: block to opt in to the supervised workflow (phone approval, PIN, presence, camera, pre-warning, safety audit). With access: an empty allowed_users refuses every open and close request with AccessDenied: that policy is fail-closed. The full example:
# /config/myhome.yaml
00:03:50:XX:XX:XX:
cover:
garage_door:
who: 1
type: impulse_relay # optional; only valid together with who: 1
where: "71" # one relay, never a general, area or group address
name: Garage door
device_class: garage # garage or gate (default gate)
state_sensor: binary_sensor.garage_door_closed # off = closed, on = not closed
travel_time: 25 # seconds, for the dashboard estimate and the watchdog
pulse_duration: 0.5 # seconds the relay is closed (0.2 - 2.0)
min_cycle_time: 5 # seconds between two pulses (1 - 120)
pin_code: "1234" # optional second factor, asked on the phone; needs access:
access:
allowed_users: # Home Assistant user ids
- 1a2b3c4d5e6f47a8b9c0d1e2f3a4b5c6
approvers: # one notify service per allowed user
1a2b3c4d5e6f47a8b9c0d1e2f3a4b5c6: notify.mobile_app_phone
remote_close: at_home # never (default), at_home or with_camera
camera: camera.driveway # needed for remote_close: with_camera
safety_devices_verified: 2026-09-01 # date you last tested photocells and force limiter
safety_check_days: 31
prewarn_light: light.driveway_flasher
prewarn_seconds: 5
close_block_entity: input_boolean.gate_close_block
watchdog_margin: 10 # seconds added to travel_time before a fault is latched
approval_timeout: 60
| Key | Meaning |
|---|---|
state_sensor |
Binary sensor of the ground-truth position. Without it the position is assumed and, with access:, every pulse is treated as one that can close the door. |
pulse_duration |
Relay pulse in seconds. 0.5 uses the timed WHAT 18 command (*1*18*WHERE##, specified in WHO_1.pdf for 0.5 s timed ON and verified on physical F461 + BT-F411/4 hardware in tests/fixtures/traces/issue_667/myhome_trace_F461_impulse_intercom_2026-10-19.json, where the actuator emits *1*1*WHERE## and autonomously turns off with *1*0*WHERE## after ~0.53 s; not yet replayed in the QEMU firmware oracle) where the installed OWNd exposes switch_on_timed, and ON (*1*1*WHERE##) followed by OFF (*1*0*WHERE##) otherwise. |
min_cycle_time |
Deadband after a pulse that reached the bus (must be strictly greater than pulse_duration). A failed send does not start it. |
pin_code |
Digit-only PIN asked in the approval notification; only valid together with access:. A reply that carries no text is refused without counting as a wrong attempt. |
access.* |
Optional. Turns on the supervised workflow, see the flow above: allowed_users, approvers, remote_close, camera, safety_devices_verified, safety_check_days, prewarn_light, prewarn_seconds, close_block_entity, watchdog_margin, approval_timeout. |
Note
type: impulse_relay together with who: 2 (and type: shutter or standard together with who: 1) is rejected when the configuration is validated; it used to fall back to an ordinary shutter silently.
π Configuring a Door Strike Lock¶
00:03:50:XX:XX:XX:
lock:
front_door:
who: 6 # the only value; WHO 1 relays are covers
where: "4001" # 1-99 or 4000-4099 endpoints; append #2 on a riser installation: "4001#2"
name: Front door
code: "1234" # optional digit-only PIN; asked on every unlock
pulse_duration: 5.0 # seconds until the lock shows locked again (default 5.0 s)
Locks exist only when they are configured here (and are restored from the registry on restart with their YAML configuration intact). Neither a lock release seen on the bus nor a stale registry entry removed from myhome.yaml creates a lock entity, because such an entity would carry no code.
β οΈ Strict Distinction: Covers vs. Locks¶
βββββββββββββββββββββββββββββββββββββββββ βββββββββββββββββββββββββββββββββββββββββ
β HEAVY MACHINERY (KINETIC RISK) β β PEDESTRIAN ACCESS (LOW MASS) β
β β β β
β - Motorized Sliding / Swing Gates β β - Front Door Latch / Strike β
β - Sectional / Roller Garage Doors β β - Pedestrian Wicket Gate Buzzer β
β β β - Elettroserratura (12V AC/DC) β
β β β β β β
β βΌ β β βΌ β
β PLATFORM: cover (impulse) β β PLATFORM: lock β
β β β β β β
β - Optional AccessController β β - Direct momentary pulse (1-3s) β
β - Dual binary sensor feedback β β - Optional PIN authentication β
β - Biometric challenge-response nonce β β - Automatic re-lock state machine β
β - 5s pre-warning flasher β β - Doorbell one-tap unlock safe β
β - Anti-stuck watchdog (latched fault)β β β
β - Certified EN 12453 safety check β β β
βββββββββββββββββββββββββββββββββββββββββ βββββββββββββββββββββββββββββββββββββββββ
| Physical Device | Required Entity Platform | Why |
|---|---|---|
| Motorized Gates & Garage Doors | cover (type: impulse_relay) |
Heavy kinetic machinery with kinetic entrapment/crush risk. Use a state_sensor, and the optional access: workflow where you want supervision. Never configure a motorized gate or garage as a lock: its door lock-style unlock would carry no sensor feedback. |
| Pedestrian Door Strikes | lock: block, who: 6 only |
Low-mass momentary electric door buzzers (elettroserrature) on a WHO 6 entrance panel that release a pedestrian latch. A lock cannot be a WHO 1 relay: a motorized gate needs the cover's sensor feedback and stop handling, so who: 1 is rejected there. |
π Legacy YAML Note¶
Note
If you are upgrading from legacy v0.9 installations and still have manual cover: blocks in /config/myhome.yaml, please refer to the v0.9.4 Legacy Cover Documentation or the Legacy YAML Migration Guide. In v2, all covers are managed dynamically via Home Assistant's native registry.