# Analytics snippet placeholder — HearthNodes (gatherer.top) # Paste the measurement snippet here (server.js inlines any content that contains # a markup tag into on every page). No GA4 property is assigned to this # site yet, so leave this file as comments only and the build emits no tracking # code.
HearthNodesgatherer.top live 11/28 EN 中文

Lux-adaptive blinds that hold 300 lx on the desk, not a fixed percentage

A closed-loop controller reads a desk lux sensor every 90 seconds and moves the blind to the position that keeps the desk between 250 and 400 lx. On an overcast day it opens fully; on a bright February noon it sits at 22 % open.

protocol · matter latency · 90000 ms power · mains published · 2026-08-25 3 devices
Automation YAML light-lux-adaptive-blinds.yaml
# HearthNodes recipe 06 - lux adaptive blinds
alias: "Light - closed loop blinds holding 300 lx"
id: hn_light_lux_adaptive_blinds
description: "Desk illuminance loop, 90 s cycle, 12 point maximum step"
mode: single
max_exceeded: silent

trigger:
  - platform: numeric_state
    entity_id: sensor.desk_illuminance
    below: 250
    for: "00:01:30"
  - platform: numeric_state
    entity_id: sensor.desk_illuminance
    above: 400
    for: "00:01:30"

variables:
  target_lx: 300
  deadband_lx: 60
  measured: "{{ states('sensor.desk_illuminance') | float(300) }}"
  current: "{{ state_attr('cover.study_blind','current_position') | int(50) }}"
  error: "{{ measured - target_lx }}"
  raw_step: "{{ (-error / 18) | round(0) }}"
  clamped_step: "{{ [[raw_step, -12] | max, 12] | min }}"
  computed_position: "{{ [[current + clamped_step, 0] | max, 100] | min }}"

condition:
  - condition: numeric_state
    entity_id: sun.sun
    attribute: elevation
    above: 5
  - condition: state
    entity_id: binary_sensor.blind_manual_override
    state: "off"
  - condition: state
    entity_id: binary_sensor.study_window_contact
    state: "off"
  - condition: template
    value_template: "{{ (measured - target_lx) | abs > deadband_lx }}"

action:
  - service: cover.set_cover_position
    target:
      entity_id: cover.study_blind
    data:
      position: "{{ computed_position }}"
  - service: counter.increment
    target:
      entity_id: counter.blind_moves_today

# Stale lux (>20 min without a report) hands the loop to a sun-position model.
# A manual wall-switch press suppresses the loop for 45 minutes via
# binary_sensor.blind_manual_override, which matters more than any tuning value.
Trigger · numeric_state

sensor.desk_illuminance

Desk illuminance leaves the 250–400 lx band, sustained for 90 seconds.

Lux-adaptive blinds that hold 300 lx on the desk, not a fixed percentage topology

Step-by-step

  1. cover.study_blindMove the blind to the computed position in 4 % steps, capped at 12 % change per cycle so the slats do not visibly hunt. set_cover_position · {{ computed_position }}
  2. input_number.blind_deadbandKeep a 60 lx deadband around the 300 lx target so small cloud edges do not move the blind. hold at 60 lx
  3. counter.blind_moves_todayCount moves per day; anything above 25 means the deadband or the cycle time is wrong. increment
Step-by-step
Aim the lux sensor at what you actually read on

Mount the sensor at the height of your eyes when seated, angled slightly up toward the ceiling. A sensor lying flat on the desk reads direct sun in winter and reports 900 lx when the paper in front of you is at 200 lx. Match the sensor's field of view to the task, not to the window.

Sample every 90 s and cap the step

Faster sampling makes the blind hunt on every cloud edge; slower sampling lets the room go dark for minutes. 90 s with a 12 % maximum step is the compromise that keeps the slats visually still while still tracking a moving sun. Log the move count for a week before deciding it is tuned.

Give the wall switch 45 minutes of authority

Anyone who physically moves the blind has expressed a clear preference. Suppressing the loop for 45 minutes after a manual press is what makes the automation feel helpful rather than argumentative.

Hardware

ModelProtocolLinkRole in this chain
Somfy Sonesse 30 with a Matter bridge matter wired Mains-powered blind motor; Matter gives a real position percentage rather than the timed-press approximation that RF-only motors use.
Aqara T1 illuminance sensor zigbee wireless Desk-height lux sensor aimed at the ceiling to read reflected light rather than direct sun.
Window contact (any reed sensor) zigbee wireless Blocks the loop while the window is open, when the blind would hit the frame.

When it fails

If the lux sensor goes stale for more than 20 minutes, the loop hands control to a sun-position model (elevation and azimuth) that approximates the same target. If that also reports implausible values, the blind stays where it is and a notification names the stale sensor rather than opening fully at midday.

Recipes on the same trigger source

Terms Informational only. These recipes control mains electricity, water and physical access. You are responsible for local electrical and plumbing codes, for fusing and isolation, and for testing every fail-safe yourself. Nothing here is professional engineering, legal or safety advice.