docs.na.id.au

The problem

Light brightness as state

A trick for giving a light “memory” of how it was turned on, by encoding a flag in the brightness value.

The problem

A hallway or passage light usually has two trigger sources:

The delayed auto-off has to distinguish “the light I switched on 15 minutes ago, nobody has touched it since” from “the light someone deliberately left on with the button”. Lights only have on/off plus brightness — so the brightest, least visible place to stash a flag is the brightness itself.

The 252 / 255 contract

TriggerBrightness setLater behaviour
PIR / motion252 (≈ 99 %)Auto-turns off after a fixed delay
Physical button255 (100 %)Stays on until switched off

252 and 255 are imperceptibly different to the eye, but the automation can read the difference back with state_attr(light.x, 'brightness'):

  1. PIR automation turns the light on at brightness 252, then waits the fixed delay. At the end of the delay it turns the light off only if the brightness is still < 255 — i.e. it is still the light it switched on and nobody has since pressed the button.
  2. Button automation toggles using the same flag in reverse: a light at 252 counts as “on” for the user, so the button press turns it off and clears the flag. A light at 255 (button-turned-on) or fully off toggles in the normal way, setting 255 when it turns on.

The contract is:

When editing these automations, keep the threshold intact. The on-automation, the delayed-off, and the matching button automation all depend on the exact same value — change one without the others and the light either never auto-offs or auto-offs while someone is in the room.

Sketch

# PIR: on at 252, then delayed conditional off
trigger:
  - trigger: state
    entity_id: binary_sensor.passage_pir
    from: 'off'
    to: 'on'
action:
  - action: light.turn_on
    target:
      entity_id: light.passage_lights
    data:
      brightness: 252
  - delay: "15:00"
  - if:
      - condition: template
        value_template: >-
          {{ state_attr('light.passage_lights', 'brightness') is not none
             and state_attr('light.passage_lights', 'brightness') < 255 }}
    then:
      - action: light.turn_off
        target:
          entity_id: light.passage_lights

Why not a helper?

You could add an input_boolean or template sensor per light to track the source. That works, but it doubles the entities, adds a second thing to keep in sync with the light, and fails quietly if the helper and the light drift apart (a scene or an assistant voice command can change the light without touching the helper). Storing the flag in the light’s own state means the source of truth is always the light itself — whatever turns it on, the flag travels with it.

The same trick generalises: any light attribute (brightness, colour temperature, hue) can carry a small amount of state that other automations read back, as long as you document the contract.



Source Disclaimer