---
title: "Light brightness as state"
description: "## The problem"
section: home-assistant
raw: "03-light-brightness-state.md"
source: ai-generated
tags: home-assistant, home-automation
last-updated: 2026-09-15
---

# 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:

- A **motion sensor (PIR)** — the light should come on, but if nobody is
  staying, it should turn itself off after a fixed delay.
- A **physical button** (e.g. an RF wall switch) — the light should stay on
  until someone deliberately turns it off.

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

| Trigger | Brightness set | Later behaviour |
|---------|----------------|-----------------|
| PIR / motion | **252** (≈ 99 %) | Auto-turns off after a fixed delay |
| Physical button | **255** (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:

- brightness **252** = "on by motion, may be auto-offed"
- brightness **255** = "on by a human, leave alone"
- anything **below** = normal dimming, no flag meaning

> 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

```yaml
# 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.

---

## Related

- [Automating with Google Home & HomeKit](/home-assistant/04-assistant-automations/)

---

## Source Disclaimer

- [x] AI Generated
- [ ] Human Generated
- [ ] AI Edited
- [ ] Human Edited
