diff --git a/ansible/roles/podman/files/hass/automations.yaml b/ansible/roles/podman/files/hass/automations.yaml index fabd5c6..13969e0 100644 --- a/ansible/roles/podman/files/hass/automations.yaml +++ b/ansible/roles/podman/files/hass/automations.yaml @@ -192,7 +192,8 @@ mode: single - id: '1762116115638' alias: Light - TV On - description: '' + description: TV mode on; once it's dark, dim the living room and turn off the + lights that glare on the TV triggers: - type: turned_on device_id: 18a9bb7a2a32be4371da447767ef50a9 @@ -204,28 +205,32 @@ - action: input_boolean.turn_on target: entity_id: input_boolean.tv_mode - - action: light.turn_on - metadata: {} - data: - brightness_pct: 5 - target: - area_id: living_room - - type: turn_off - device_id: 03a12d2360d9954aed19c2449070725a - entity_id: 7c1e7db73799cc3f90948b5118596985 - domain: light - - type: turn_off - device_id: 800eddbeeda071225f181a14cb9527e0 - entity_id: 521a92ddd8be76c7eddfc544f81f6020 - domain: light - - type: turn_off - device_id: 3f7f65571d9bb0833433996f1f6725bd - entity_id: 7407afe14783543252c666d5ff7c5d5c - domain: light + - if: + - condition: or + conditions: + - condition: sun + after: sunset + after_offset: "-01:00:00" + - condition: sun + before: sunrise + then: + - action: light.turn_on + data: + brightness_pct: 5 + target: + area_id: living_room + - action: light.turn_off + target: + entity_id: + - light.kitchen_wall_light + - light.dining_hall + - light.bathroom_hallway mode: single - id: new_tv_off alias: Light - TV Off - Restore - description: Restores appropriate lighting level when TV turns off based on time + description: Evening (sunset -1h to 23:30) brings the lights back to the + scheduled level; late night (23:30 to sunrise) only turns off the TV glow; + daytime leaves the lights alone triggers: - type: turned_off device_id: 18a9bb7a2a32be4371da447767ef50a9 @@ -239,399 +244,156 @@ entity_id: input_boolean.tv_mode - choose: - conditions: + - condition: sun + after: sunset + after_offset: "-01:00:00" - condition: time - after: '22:30:00' - before: '23:45:00' + before: '23:30:00' sequence: - # Late dim levels - - type: turn_on - device_id: f31e4f9bf8fa3687a07aeb4430eaef38 - entity_id: b79934d97f3bb9d8a3da47c76d03ded4 - domain: light - brightness_pct: 1 - - type: turn_on - device_id: 03a12d2360d9954aed19c2449070725a - entity_id: 7c1e7db73799cc3f90948b5118596985 - domain: light - brightness_pct: 1 - - type: turn_on - device_id: 800eddbeeda071225f181a14cb9527e0 - entity_id: 521a92ddd8be76c7eddfc544f81f6020 - domain: light - brightness_pct: 25 - - type: turn_on - device_id: 3f7f65571d9bb0833433996f1f6725bd - entity_id: 7407afe14783543252c666d5ff7c5d5c - domain: light - brightness_pct: 10 - - type: turn_on - device_id: 21eb2bd28aba2ee361a22af92e8b2d16 - entity_id: 81c486d682afcc94e98e377475cc92fc - domain: light - brightness_pct: 10 + - action: script.evening_lights_apply + data: + apply: force - conditions: - - condition: time - after: '21:30:00' - before: '22:30:00' + - condition: or + conditions: + - condition: time + after: '23:30:00' + - condition: sun + before: sunrise sequence: - # Mid dim levels - - type: turn_on - device_id: f31e4f9bf8fa3687a07aeb4430eaef38 - entity_id: b79934d97f3bb9d8a3da47c76d03ded4 - domain: light - brightness_pct: 25 - - type: turn_on - device_id: 03a12d2360d9954aed19c2449070725a - entity_id: 7c1e7db73799cc3f90948b5118596985 - domain: light - brightness_pct: 25 - - type: turn_on - device_id: 800eddbeeda071225f181a14cb9527e0 - entity_id: 521a92ddd8be76c7eddfc544f81f6020 - domain: light - brightness_pct: 50 - - type: turn_on - device_id: 3f7f65571d9bb0833433996f1f6725bd - entity_id: 7407afe14783543252c666d5ff7c5d5c - domain: light - brightness_pct: 25 - - type: turn_on - device_id: 21eb2bd28aba2ee361a22af92e8b2d16 - entity_id: 81c486d682afcc94e98e377475cc92fc - domain: light - brightness_pct: 25 - - conditions: - - condition: time - after: '21:00:00' - before: '21:30:00' - sequence: - # Early dim levels - - type: turn_on - device_id: 03a12d2360d9954aed19c2449070725a - entity_id: 7c1e7db73799cc3f90948b5118596985 - domain: light - brightness_pct: 50 - - type: turn_on - device_id: f31e4f9bf8fa3687a07aeb4430eaef38 - entity_id: b79934d97f3bb9d8a3da47c76d03ded4 - domain: light - brightness_pct: 50 - - type: turn_on - device_id: 3f7f65571d9bb0833433996f1f6725bd - entity_id: 7407afe14783543252c666d5ff7c5d5c - domain: light - brightness_pct: 50 - - type: turn_on - device_id: 21eb2bd28aba2ee361a22af92e8b2d16 - entity_id: 81c486d682afcc94e98e377475cc92fc - domain: light - brightness_pct: 50 - default: - # Full brightness (before 21:00 after sunset) - - type: turn_on - device_id: 800eddbeeda071225f181a14cb9527e0 - entity_id: 521a92ddd8be76c7eddfc544f81f6020 - domain: light - brightness_pct: 25 - - type: turn_on - device_id: f31e4f9bf8fa3687a07aeb4430eaef38 - entity_id: b79934d97f3bb9d8a3da47c76d03ded4 - domain: light - brightness_pct: 100 - - type: turn_on - device_id: 03a12d2360d9954aed19c2449070725a - entity_id: 7c1e7db73799cc3f90948b5118596985 - domain: light - brightness_pct: 100 - - type: turn_on - device_id: 3f7f65571d9bb0833433996f1f6725bd - entity_id: 7407afe14783543252c666d5ff7c5d5c - domain: light - brightness_pct: 75 - - type: turn_on - device_id: 21eb2bd28aba2ee361a22af92e8b2d16 - entity_id: 81c486d682afcc94e98e377475cc92fc - domain: light - brightness_pct: 100 + - action: light.turn_off + target: + area_id: living_room mode: single -- id: 'sunset_lights_on' - alias: Lights - Sunset On - description: Turn on lights 1 hour before sunset +- id: driveway_lights_on + alias: Driveway String Lights On + description: On 1 hour before sunset whether or not the TV is on. Also catches + up if Home Assistant restarts or the switch reconnects before the 23:00 off triggers: - trigger: sun event: sunset offset: "-01:00:00" + id: sunset + - trigger: homeassistant + event: start + - trigger: state + entity_id: switch.driveway_string_lights + from: unavailable + to: 'off' conditions: - condition: state - entity_id: input_boolean.tv_mode + entity_id: switch.driveway_string_lights state: 'off' + - condition: or + conditions: + - condition: trigger + id: sunset + - condition: and + conditions: + - condition: sun + after: sunset + after_offset: "-01:00:00" + - condition: time + before: '23:00:00' actions: - - type: turn_on - device_id: 1fa1aca8f90daf94a2a7baf8a3abc158 - entity_id: 58d101e63456fd8e088d3a3b63f3a0f9 - domain: switch - - type: turn_on - device_id: 800eddbeeda071225f181a14cb9527e0 - entity_id: 521a92ddd8be76c7eddfc544f81f6020 - domain: light - brightness_pct: 25 - - type: turn_on - device_id: f31e4f9bf8fa3687a07aeb4430eaef38 - entity_id: b79934d97f3bb9d8a3da47c76d03ded4 - domain: light - brightness_pct: 100 - - type: turn_on - device_id: 03a12d2360d9954aed19c2449070725a - entity_id: 7c1e7db73799cc3f90948b5118596985 - domain: light - brightness_pct: 100 - - type: turn_on - device_id: 3f7f65571d9bb0833433996f1f6725bd - entity_id: 7407afe14783543252c666d5ff7c5d5c - domain: light - brightness_pct: 75 - - type: turn_on - device_id: 21eb2bd28aba2ee361a22af92e8b2d16 - entity_id: 81c486d682afcc94e98e377475cc92fc - domain: light - brightness_pct: 100 + - action: switch.turn_on + target: + entity_id: switch.driveway_string_lights mode: single -- id: 'evening_dim_2100' - alias: Lights - Evening Dim (21:00) - description: Dim lights at 21:00 - only affects lights that are ON +- id: 'sunset_lights_on' + alias: Lights - Sunset On + description: Turn on lights 1 hour before sunset. If the TV is on, the living + room gets the TV glow and the lights that glare on the TV stay off triggers: - - trigger: time - at: "21:00:00" - conditions: - - condition: state - entity_id: input_boolean.tv_mode - state: 'off' - actions: - - if: - - condition: device - device_id: 03a12d2360d9954aed19c2449070725a - domain: light - entity_id: 7c1e7db73799cc3f90948b5118596985 - type: is_on - then: - - type: turn_on - device_id: 03a12d2360d9954aed19c2449070725a - entity_id: 7c1e7db73799cc3f90948b5118596985 - domain: light - brightness_pct: 50 - - if: - - condition: device - device_id: f31e4f9bf8fa3687a07aeb4430eaef38 - domain: light - entity_id: b79934d97f3bb9d8a3da47c76d03ded4 - type: is_on - then: - - type: turn_on - device_id: f31e4f9bf8fa3687a07aeb4430eaef38 - entity_id: b79934d97f3bb9d8a3da47c76d03ded4 - domain: light - brightness_pct: 50 - - if: - - condition: device - device_id: 3f7f65571d9bb0833433996f1f6725bd - domain: light - entity_id: 7407afe14783543252c666d5ff7c5d5c - type: is_on - then: - - type: turn_on - device_id: 3f7f65571d9bb0833433996f1f6725bd - entity_id: 7407afe14783543252c666d5ff7c5d5c - domain: light - brightness_pct: 50 - - if: - - condition: device - device_id: 21eb2bd28aba2ee361a22af92e8b2d16 - domain: light - entity_id: 81c486d682afcc94e98e377475cc92fc - type: is_on - then: - - type: turn_on - device_id: 21eb2bd28aba2ee361a22af92e8b2d16 - entity_id: 81c486d682afcc94e98e377475cc92fc - domain: light - brightness_pct: 50 - mode: single -- id: 'mid_dim_2130' - alias: Lights - Mid Dim (21:30) - description: Dim lights at 21:30 - only affects lights that are ON - triggers: - - trigger: time - at: "21:30:00" - conditions: - - condition: state - entity_id: input_boolean.tv_mode - state: 'off' - actions: - - if: - - condition: device - device_id: f31e4f9bf8fa3687a07aeb4430eaef38 - domain: light - entity_id: b79934d97f3bb9d8a3da47c76d03ded4 - type: is_on - then: - - type: turn_on - device_id: f31e4f9bf8fa3687a07aeb4430eaef38 - entity_id: b79934d97f3bb9d8a3da47c76d03ded4 - domain: light - brightness_pct: 25 - - if: - - condition: device - device_id: 03a12d2360d9954aed19c2449070725a - domain: light - entity_id: 7c1e7db73799cc3f90948b5118596985 - type: is_on - then: - - type: turn_on - device_id: 03a12d2360d9954aed19c2449070725a - entity_id: 7c1e7db73799cc3f90948b5118596985 - domain: light - brightness_pct: 25 - - if: - - condition: device - device_id: 800eddbeeda071225f181a14cb9527e0 - domain: light - entity_id: 521a92ddd8be76c7eddfc544f81f6020 - type: is_on - then: - - type: turn_on - device_id: 800eddbeeda071225f181a14cb9527e0 - entity_id: 521a92ddd8be76c7eddfc544f81f6020 - domain: light - brightness_pct: 50 - - if: - - condition: device - device_id: 3f7f65571d9bb0833433996f1f6725bd - domain: light - entity_id: 7407afe14783543252c666d5ff7c5d5c - type: is_on - then: - - type: turn_on - device_id: 3f7f65571d9bb0833433996f1f6725bd - entity_id: 7407afe14783543252c666d5ff7c5d5c - domain: light - brightness_pct: 25 - - if: - - condition: device - device_id: 21eb2bd28aba2ee361a22af92e8b2d16 - domain: light - entity_id: 81c486d682afcc94e98e377475cc92fc - type: is_on - then: - - type: turn_on - device_id: 21eb2bd28aba2ee361a22af92e8b2d16 - entity_id: 81c486d682afcc94e98e377475cc92fc - domain: light - brightness_pct: 25 - mode: single -- id: 'late_dim_2230' - alias: Lights - Late Dim (22:30) - description: Dim lights at 22:30 - only affects lights that are ON - triggers: - - trigger: time - at: "22:30:00" - conditions: - - condition: state - entity_id: input_boolean.tv_mode - state: 'off' - actions: - - if: - - condition: device - device_id: f31e4f9bf8fa3687a07aeb4430eaef38 - domain: light - entity_id: b79934d97f3bb9d8a3da47c76d03ded4 - type: is_on - then: - - type: turn_on - device_id: f31e4f9bf8fa3687a07aeb4430eaef38 - entity_id: b79934d97f3bb9d8a3da47c76d03ded4 - domain: light - brightness_pct: 1 - - if: - - condition: device - device_id: 03a12d2360d9954aed19c2449070725a - domain: light - entity_id: 7c1e7db73799cc3f90948b5118596985 - type: is_on - then: - - type: turn_on - device_id: 03a12d2360d9954aed19c2449070725a - entity_id: 7c1e7db73799cc3f90948b5118596985 - domain: light - brightness_pct: 1 - - if: - - condition: device - device_id: 800eddbeeda071225f181a14cb9527e0 - domain: light - entity_id: 521a92ddd8be76c7eddfc544f81f6020 - type: is_on - then: - - type: turn_on - device_id: 800eddbeeda071225f181a14cb9527e0 - entity_id: 521a92ddd8be76c7eddfc544f81f6020 - domain: light - brightness_pct: 25 - - if: - - condition: device - device_id: 3f7f65571d9bb0833433996f1f6725bd - domain: light - entity_id: 7407afe14783543252c666d5ff7c5d5c - type: is_on - then: - - type: turn_on - device_id: 3f7f65571d9bb0833433996f1f6725bd - entity_id: 7407afe14783543252c666d5ff7c5d5c - domain: light - brightness_pct: 10 - - if: - - condition: device - device_id: 21eb2bd28aba2ee361a22af92e8b2d16 - domain: light - entity_id: 81c486d682afcc94e98e377475cc92fc - type: is_on - then: - - type: turn_on - device_id: 21eb2bd28aba2ee361a22af92e8b2d16 - entity_id: 81c486d682afcc94e98e377475cc92fc - domain: light - brightness_pct: 10 - mode: single -- id: 'lights_out_2345' - alias: Lights - Out (23:45) - description: Turn off all lights at 23:45 - triggers: - - trigger: time - at: "23:45:00" + - trigger: sun + event: sunset + offset: "-01:00:00" conditions: [] actions: - - type: turn_off - device_id: 3f7f65571d9bb0833433996f1f6725bd - entity_id: 7407afe14783543252c666d5ff7c5d5c - domain: light - - type: turn_off - device_id: f31e4f9bf8fa3687a07aeb4430eaef38 - entity_id: b79934d97f3bb9d8a3da47c76d03ded4 - domain: light - - type: turn_off - device_id: 03a12d2360d9954aed19c2449070725a - entity_id: 7c1e7db73799cc3f90948b5118596985 - domain: light - - type: turn_off - device_id: 800eddbeeda071225f181a14cb9527e0 - entity_id: 521a92ddd8be76c7eddfc544f81f6020 - domain: light - - type: turn_off - device_id: 03eb359bf2344a58bebfe1e9c5bcfadd - entity_id: a30b2da3cd80a5b4c927e1608b91eb65 - domain: light - - type: turn_off - device_id: 21eb2bd28aba2ee361a22af92e8b2d16 - entity_id: 81c486d682afcc94e98e377475cc92fc - domain: light + - action: script.evening_lights_apply + data: + apply: force + - if: + - condition: state + entity_id: input_boolean.tv_mode + state: 'on' + then: + - action: light.turn_on + data: + brightness_pct: 5 + target: + area_id: living_room + mode: single +- id: evening_dim_ramp + alias: Lights - Evening Dim Ramp + description: Every 5 minutes from 20:30 to 23:30, ease the lights that are on + toward the schedule in script.evening_lights_apply + triggers: + - trigger: time_pattern + minutes: /5 + conditions: + - condition: time + after: '20:30:00' + before: '23:30:00' + actions: + - action: script.evening_lights_apply + data: + apply: ramp + mode: single +- id: 'lights_out_2345' + alias: Lights - Out (23:30) + description: Turn off all lights at 23:30. While the TV is on the living room + is left as it is + triggers: + - trigger: time + at: "23:30:00" + conditions: [] + actions: + - action: light.turn_off + target: + entity_id: + - light.kitchen_lights + - light.kitchen_wall_light + - light.dining_hall + - light.dining_room + - light.bathroom_hallway + - if: + - condition: state + entity_id: input_boolean.tv_mode + state: 'off' + then: + - action: light.turn_off + target: + entity_id: light.living_room + mode: single +- id: lights_sweep_0100 + alias: Lights - Sweep (01:00) + description: Catch anything turned back on after lights-out. While the TV is + on the living room is left as it is + triggers: + - trigger: time + at: "01:00:00" + conditions: [] + actions: + - action: light.turn_off + target: + entity_id: + - light.kitchen_lights + - light.kitchen_wall_light + - light.dining_hall + - light.dining_room + - light.bathroom_hallway + - action: switch.turn_off + target: + entity_id: switch.driveway_string_lights + - if: + - condition: state + entity_id: input_boolean.tv_mode + state: 'off' + then: + - action: light.turn_off + target: + entity_id: light.living_room mode: single - id: '1768862300896' alias: Bedroom On diff --git a/ansible/roles/podman/files/hass/configuration.yaml b/ansible/roles/podman/files/hass/configuration.yaml index 04c73ae..a25d51e 100644 --- a/ansible/roles/podman/files/hass/configuration.yaml +++ b/ansible/roles/podman/files/hass/configuration.yaml @@ -23,6 +23,7 @@ homeassistant: media: /share automation: !include automations.yaml +script: !include scripts.yaml input_boolean: tv_mode: diff --git a/ansible/roles/podman/files/hass/scripts.yaml b/ansible/roles/podman/files/hass/scripts.yaml new file mode 100644 index 0000000..83cbd32 --- /dev/null +++ b/ansible/roles/podman/files/hass/scripts.yaml @@ -0,0 +1,76 @@ +evening_lights_apply: + alias: Evening Lights - Apply Schedule + description: >- + Sets each evening light to its scheduled brightness for the current time, + blending linearly between the points in `schedule`. apply=force turns the + lights on (sunset, TV off); apply=ramp only eases lights that are already + on, and leaves alone any light someone has changed by hand. While TV mode + is on the living room and the lights that glare on the TV are left alone. + mode: queued + fields: + apply: + description: "force: turn lights on at the target. ramp: only adjust lights that are already on." + example: ramp + selector: + select: + options: + - force + - ramp + variables: + # [minute of day, brightness %] - 1230 = 20:30, 1260 = 21:00, + # 1290 = 21:30, 1350 = 22:30. Before the first point a light sits at the + # first value; after the last it holds the last value until lights-out. + schedule: + light.kitchen_lights: [[1230, 100], [1260, 50], [1290, 25], [1350, 1]] + light.kitchen_wall_light: [[1230, 100], [1260, 50], [1290, 25], [1350, 1]] + light.bathroom_hallway: [[1230, 75], [1260, 50], [1290, 25], [1350, 10]] + light.living_room: [[1230, 100], [1260, 50], [1290, 25], [1350, 10]] + light.dining_hall: [[1290, 25], [1350, 15]] + tv_mode_lights: + - light.living_room + - light.kitchen_wall_light + - light.dining_hall + - light.bathroom_hallway + apply_mode: "{{ apply | default('ramp') }}" + sequence: + - repeat: + for_each: "{{ schedule.keys() | list }}" + sequence: + - variables: + light: "{{ repeat.item }}" + # [target now, target 5 minutes ago - what the last ramp tick set] + levels: >- + {%- macro at(pts, t) -%} + {%- if t <= pts[0][0] -%}{{ pts[0][1] }} + {%- elif t >= pts[-1][0] -%}{{ pts[-1][1] }} + {%- else -%} + {%- for i in range(pts | length - 1) if pts[i][0] <= t < pts[i + 1][0] -%} + {{ (pts[i][1] + (pts[i + 1][1] - pts[i][1]) * (t - pts[i][0]) / (pts[i + 1][0] - pts[i][0])) | round(0) | int }} + {%- endfor -%} + {%- endif -%} + {%- endmacro -%} + {%- set t = now().hour * 60 + now().minute -%} + {{ [at(schedule[repeat.item], t) | int, at(schedule[repeat.item], t - 5) | int] }} + current: "{{ ((state_attr(repeat.item, 'brightness') or 0) / 2.55) | round(0) | int }}" + skip: "{{ is_state('input_boolean.tv_mode', 'on') and repeat.item in tv_mode_lights }}" + - choose: + - conditions: "{{ not skip and apply_mode == 'force' }}" + sequence: + - action: light.turn_on + target: + entity_id: "{{ light }}" + data: + brightness_pct: "{{ levels[0] }}" + transition: 2 + # A light more than 10 points off the last tick was set by hand; the + # biggest scheduled change in 5 minutes is ~8 + - conditions: >- + {{ not skip and apply_mode == 'ramp' and is_state(light, 'on') + and (current - levels[1]) | abs <= 10 and current != levels[0] }} + sequence: + - action: light.turn_on + target: + entity_id: "{{ light }}" + data: + brightness_pct: "{{ levels[0] }}" + transition: 60 diff --git a/ansible/roles/podman/tasks/containers/home/hass.yml b/ansible/roles/podman/tasks/containers/home/hass.yml index d47cfbd..786c4e8 100644 --- a/ansible/roles/podman/tasks/containers/home/hass.yml +++ b/ansible/roles/podman/tasks/containers/home/hass.yml @@ -25,6 +25,7 @@ loop: - configuration.yaml - automations.yaml + - scripts.yaml - name: flush handlers ansible.builtin.meta: flush_handlers diff --git a/ansible/roles/podman/tasks/main.yml b/ansible/roles/podman/tasks/main.yml index 28a0c9a..16f8815 100644 --- a/ansible/roles/podman/tasks/main.yml +++ b/ansible/roles/podman/tasks/main.yml @@ -39,7 +39,7 @@ - import_tasks: containers/home/hass.yml vars: - image: ghcr.io/home-assistant/home-assistant:2026.8.3 + image: ghcr.io/home-assistant/home-assistant:2026.9.3 tags: hass - import_tasks: containers/home/partsy.yml