A verifiable method of control and actioning of the Somfy blinds was required because they often fail to respond to commands from the Somfy App via its Connectivity Kit .
The Dashboard not only shows the current status of the blinds as depicted by the Roller Blinds icon, but also controls opening and closing either by clicking on the icon, or via the Blinds schedule using @cgigh/node-red-dashboard-2-scheduler ui_scheduler. The status of the Blinds is determined using the Ikea MYGGBETT door/window sensor connected by matter over thread to an Aqara Hub M100 on the home network. When the blinds are in the process of being opened/closed then three pulsating vertical dots appear next to the icon.
The status of the Blinds is determined using matterreadattr of @sammachin/node-red-matter-controller.The Somfy Blinds are operated via their specific app through Alexa and controlled via Blind_UpDown the mattercontactsensor of @sammachin/node-red-matter-bridge. a maximum of 6 attempts to operate the blinds are made. Each attempt is shown as a LOOP number, if successful the interface reports DONE otherwise it displays FAIL.
This Node-RED flow automates a smart blind system via Matter, using UI scheduling and manual switches to set target states while providing visual, animated feedback during operation. It incorporates a verification loop that checks the blind's status, triggering up to six retries with a five-second delay if the physical position does not match the requested state.
The mattersubscribe (MYGGBETT 1 stateValue) node detects open/close events broadcast by Ikea's MYGGBETT. However, this method of detection of a successful operation of the blinds was initially found to be unreliable, hence preferred method of using matterreadattr as above. Nevertheless, it has been used to maintain synchronisation between MYGGBETT device and the mattercontactsensor (Blind_UpDown) node.
Set Switches according to Blind Status
Detect Page Load ui-event for My Dashboard[/dashboard], $pageview - emits whenever a user views a page and so acts as a "Refresh" button that triggers automatically whenever the dashboard is accessed'
ui-event (Detect Page Load): This node listens for any interaction with your Dashboard 2.0 interface. Because includeClientData is enabled in the ui-base, it sends a message whenever a user connects.
switch (Is Pageview?): It filters the incoming events. It only allows the message to pass if the msg.topic is exactly $pageview (meaning a user just opened the URL).
matterreadattr (Blind STATUS): acts as a "request." to ask the MYGGBETT directly: "What is your current stateValue (Cluster 69) right now?"
The Result: The output will contain the real-time status of the blind, ensuring the dashboard doesn't show old info just because the blind has been controlled by e.g. a voice command and not the dashboard.
The blinds ui-switch node DOES NOT have message arriving set to pass through, so that it can be flipped without effect.
When did Blinds open/close using mattersubscribe though initially found unreliable is included to show the operation and time of an open/close event as well as, updating the setting the blinds ui-switch icon status following Somefy manual or Alexa audio control of the motorised blinds.
The function node
msg.payload=(msg.payload!=false)?"closing":"opening";
return msg;
The change node usines JSONATA expression to append date and time information to the payload.
msg.time set
$moment().local().format('dddd DD/MM/YYYY h:mma')
followed by msg.payload set
" " & $string(payload) & " " & $string(time)
Automatically adjusting for Daylight Saving Time is achieved using the JSONATA $moment().local()expression.
The Trigger: The flow starts when you either the Blinds Switch is flipped on the dashboard or when the Blinds Schedule hits a pre-set time (like sunrise).The Switch icons (On Icon = mdi-blinds-open; Off Icon = mdi-roller-shade-closed) given </> Class large-switch and assigned in the busy dots ui-template.
<template>
<div class="animation-wrapper">
<transition name="fade">
<div v-if="isBusy" class="busy-dots">
●<br>●<br>●
</div>
</transition>
</div>
</template>
<script>
export default {
data() {
return {
isBusy: false
}
},
watch: {
msg: {
handler(msg) {
// If topic is 'busy' (from switch) OR state is 'busy' (from external)
if (msg?.topic === 'busy' || msg?.state === 'busy') {
this.isBusy = true
}
// Stop if explicitly told to be 'idle'
else if (msg?.state === 'idle') {
this.isBusy = false
}
},
immediate: true
}
}
}
</script>
<style>
/* 1. Resize Switch Icon */
.large-switch .v-icon {
font-size: 48px !important;
width: 48px !important;
height: 48px !important;
}
/* 2. Pull animation closer by removing margins/padding */
.animation-wrapper {
height: 48px;
display: flex;
align-items: center;
/*margin-left: -12px; /* Pulls dots toward the switch icon */
}
.busy-dots {
display: flex;
flex-direction: column;
line-height: 0.6; /* Tightens vertical dot spacing */
font-size: 1.1rem;
color: var(--nrdb-primary-color);
animation: pulse 0.8s infinite;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.2; }
}
.fade-enter-active, .fade-leave-active { transition: opacity 0.3s; }
.fade-enter-from, .fade-leave-to { opacity: 0; }
</style>
Setting the Goal: Once triggered, the flow saves your request (e.g., "Open") and sets the system status to "busy." The Loop label characteristics and initial retryCount are set.
Visual Feedback: This "busy" status triggers a "Busy Dots" animation on the dashboard. This lets you know the command is being processed even if the blinds haven't finished moving yet.
Checking Reality: The flow talks to your blind (via Matter) to ask for its current physical position.
The Logic Loop:
Success: If the blind’s position matches your request, the status changes to "idle," the dots disappear, and the flow stops.
Try Again: If they don't match, the flow waits e.g. 30 seconds, adds 1 to a "retry count," and sends the command again.
Safety Cut-off: To prevent the motor from running forever if something is stuck, the flow will give up after 6 failed attempts.
The Change nodes (FAILED and DONE ) in the flow before Loop Count set the characteristics of ui-text in which Apply Style has been enabled
Alexa Routine for Blind_UpDown the mattercontactsensor
"Syncing" no longer a priority:
By using the "Flicker" method to force an Alexa trigger during retries, the mattercontactsensor is treated as a stateless command trigger rather than a synchronized state mirror.
The Command is King: The flow now cares more about forcing a transition to trigger the Alexa Routine than it does about making sure the virtual icon in Alexa matches the real world.
Decoupled Logic: Since MYGGBETT (the IKEA sensor) is the "Source of Truth" for the Node-RED Dashboard, it doesn't matter if the Alexa app's virtual sensor is technically out of sync for a few minutes. The "Flicker" will "un-stick" it the moment the next command or retry is sent.
What stays "Synced":
The only synchronization that remains critical is:
MYGGBETT -> UI Switch: So one knows the blinds are open/closed when looking at your dashboard.
MYGGBETT -> UI-template (Busy Dots): So one knows when the blinds opening/closing has completed.
MYGGBETT -> Retry Loop: So the flow knows whether to trigger the Flicker or stop
The "Cleaned Up" Architecture:
Manual/Schedule Trigger: Sends command -> Flicker Node -> mattercontactsensor.
The Loop: matterreadattr checks MYGGBETT.
If Fail: Change Node (+1 count) -> Flicker Node (Resets Alexa).
If Success: Stop.
If Change Node (count > 6): Stop
The Dashboard: mattersubscribe (or the poll) updates the UI Switch icon directly.