Skip to content

Automations

Automations are organized into feature packages under packages/. Each package owns its own automations, scripts, and helper entities.


Entertainment state machine

The media package implements a state machine for three entertainment modes: TV, Movie, and Hygge. Only one mode can be active at a time. The "Master Orchestrator" automation manages transitions between modes based on what's playing and on which device.

Dynamic lighting during playback:

  • When a video starts playing, lights dim to a minimum
  • When playback pauses, lights brighten slightly so it's easy to move around — but only after sunset
  • When all entertainment modes are turned off, lighting returns to the normal adaptive state

The logic tracks both the TV's device state and the Plex client state to handle edge cases reliably.

Sonos night sound is also managed here: enabled automatically at 21:00 and disabled at 07:00.

Master Orchestrator

The automation listens to several triggers and routes to the correct action using choose. Mode is restart — if a new trigger fires while the automation is still running, it starts over from the beginning.

- alias: "Viihde: Master Orchestrator"
  triggers:
    - trigger: state
      entity_id: media_player.olohuoneen_tv
      to: "off"
      for:
        seconds: 3
      id: tv_off
    - trigger: state
      entity_id: media_player.olohuoneen_tv
      attribute: source
      to: "Live TV"
      id: source_live
    - trigger: state
      entity_id: media_player.shield
      id: shield_change
    - trigger: state
      entity_id: media_player.plex_shield
      id: shield_change
    - trigger: state
      entity_id: input_boolean.hyggetila_paalla
      to: "on"
      id: hygge_on
    - trigger: state
      entity_id: input_boolean.elokuvatila_paalla
      to: "on"
      id: movie_mode_on
  conditions:
    # Shield fires state changes constantly while playing (timeline updates etc.).
    # Only continue if the Shield trigger actually changed state (playing → paused etc.),
    # not just updated attributes.
    - condition: template
      value_template: >
        {{ trigger.id != 'shield_change' or
           trigger.to_state.state != trigger.from_state.state }}
  actions:
    - choose:
        - conditions:
            - condition: trigger
              id: tv_off
          sequence:
            - action: script.reset_entertainment_modes
            - action: script.restore_normal_lighting

        - conditions:
            - condition: trigger
              id: hygge_on
          sequence:
            - action: input_boolean.turn_off
              target:
                entity_id:
                  - input_boolean.tv_tila_paalla
                  - input_boolean.elokuvatila_paalla
            - action: script.hyggetila

        - conditions:
            - condition: trigger
              id: movie_mode_on
          sequence:
            - action: input_boolean.turn_off
              target:
                entity_id:
                  - input_boolean.tv_tila_paalla
                  - input_boolean.hyggetila_paalla
            - action: script.elokuvatila

        # Shield starts playing → activate movie mode
        - conditions:
            - condition: trigger
              id: shield_change
            - condition: state
              entity_id: input_boolean.elokuvatila_paalla
              state: "off"
            - condition: or
              conditions:
                - condition: state
                  entity_id: media_player.shield
                  state: "playing"
                - condition: state
                  entity_id: media_player.plex_shield
                  state: "playing"
          sequence:
            - action: input_boolean.turn_on
              target:
                entity_id: input_boolean.elokuvatila_paalla

        # Shield state changes while movie mode is already on
        - conditions:
            - condition: trigger
              id: shield_change
            - condition: state
              entity_id: input_boolean.elokuvatila_paalla
              state: "on"
          sequence:
            - action: script.handle_movie_mode_state_change
  mode: restart

Plex vs. Shield priority

During movie mode, playback state is read from both the Shield media player and the dedicated Plex integration. Plex takes priority: if the Plex entity says paused, that wins even if the Shield entity reports playing. This avoids a false "watching" state when Plex is paused but the Shield hasn't updated yet.

- choose:
    # Plex playing → dim lights
    - conditions:
        - condition: state
          entity_id: media_player.plex_shield
          state: playing
      sequence:
        - action: script.apply_movie_viewing_lighting

    # Plex paused → brighten lights
    - conditions:
        - condition: state
          entity_id: media_player.plex_shield
          state: paused
      sequence:
        - action: script.apply_movie_paused_lighting

    # Shield playing, but NOT via Plex → dim lights
    - conditions:
        - condition: state
          entity_id: media_player.shield
          state: playing
        - condition: not
          conditions:
            - condition: state
              entity_id: media_player.shield
              attribute: app_name
              state: "Plex"
      sequence:
        - action: script.apply_movie_viewing_lighting

Adaptive Lighting

The Adaptive Lighting custom component adjusts brightness and colour temperature throughout the day, following the sun. Activating an entertainment mode disables adaptive lighting for the duration, giving full manual control over the scene.

Lights are grouped into Zigbee groups where possible to avoid the "popcorn effect" (lights turning on one by one with a delay).


Air quality

Two Apollo Air-1 sensors monitor CO2, PM2.5, and VOC levels — one in the bedroom and one in the office. When CO2 exceeds 1000 ppm for more than 5 minutes:

  • A notification is sent
  • The sensor's RGB LED turns orange as a local visual indicator

The alert only fires when someone is home and the room isn't in sleep mode.


Wake-up routine

A circadian wake-up routine gradually brings up the lights and starts music before the alarm time. It reads the next alarm from the Android Companion App.

One important quirk: the Companion App must be configured with an allow list in its sensor settings, permitting only the clock app (com.google.android.deskclock). Without this, other apps (calendar events, Tasker) can trigger the wake-up routine unintentionally.

The routine also has a quiet hours guard (22:00–06:00) to prevent it from firing if the sensor sends an incorrect alarm time.