From 5e51a1f9aa735d9c965a46d3f946108aa1c2425b Mon Sep 17 00:00:00 2001 From: Joshua King Date: Sat, 17 Oct 2026 15:33:27 -0400 Subject: [PATCH] Add Home Assistant controls for resin curing timer --- helpers/timer.yaml | 12 +-- packages/resin_curing_timer_ha.yaml | 117 ++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+), 6 deletions(-) create mode 100644 packages/resin_curing_timer_ha.yaml diff --git a/helpers/timer.yaml b/helpers/timer.yaml index 67c3952..1400ed7 100644 --- a/helpers/timer.yaml +++ b/helpers/timer.yaml @@ -1,6 +1,6 @@ -# Timer helpers -# Example: -# bedtime_timer: -# name: Bedtime Timer -# duration: "00:30:00" -# icon: mdi:timer +# Used by esphome/resin-curing-timer.yaml and its companion HA package. +resin_curing: + name: Resin Curing + duration: "00:30:00" + icon: mdi:timer-outline + restore: true diff --git a/packages/resin_curing_timer_ha.yaml b/packages/resin_curing_timer_ha.yaml new file mode 100644 index 0000000..d969775 --- /dev/null +++ b/packages/resin_curing_timer_ha.yaml @@ -0,0 +1,117 @@ +# Companion to esphome/resin-curing-timer.yaml. +# Keep the outlet and controller IDs in sync with that file's substitutions. +# The timer helper is defined in helpers/timer.yaml. +template: + - sensor: + - name: Resin Curing Deadline + default_entity_id: sensor.resin_curing_deadline + unique_id: resin_curing_deadline + icon: mdi:timer-sand + # The ESPHome display expects Unix epoch seconds, rather than an ISO date. + state: >- + {{ as_timestamp(state_attr('timer.resin_curing', 'finishes_at'), 0) | int + if is_state('timer.resin_curing', 'active') else 0 }} + +automation: + - id: resin_curing_timer_control + alias: Resin Curing Timer - Control UV Light + description: >- + Handle the CYD presets and STOP button, turn off the UV outlet when the + timer ends, and reconcile the timer and outlet after restart or recovery. + triggers: + - trigger: event + event_type: esphome.resin_curing_timer + event_data: + controller: resin-curing-timer + id: command + - trigger: state + entity_id: timer.resin_curing + to: + - idle + - paused + id: timer_stopped + - trigger: event + event_type: timer.finished + event_data: + entity_id: timer.resin_curing + id: timer_finished + - trigger: state + entity_id: switch.resin_uv_light + to: + - "on" + - "off" + id: outlet_changed + - trigger: homeassistant + event: start + id: startup + - trigger: event + event_type: automation_reloaded + id: reload + # Retry a missed shutoff once the outlet recovers, including after downtime. + - trigger: time_pattern + seconds: "/30" + id: reconcile + actions: + - choose: + - conditions: + - condition: template + value_template: >- + {{ trigger.id == 'command' + and trigger.event.data.get('command') == 'start' + and trigger.event.data.get('minutes') | string + in ['30', '45', '60', '90'] + and states('switch.resin_uv_light') in ['on', 'off'] }} + sequence: + # Arm the shutoff before requesting power. Each preset replaces + # the remaining time, matching the duration shown on the CYD. + - action: timer.start + target: + entity_id: timer.resin_curing + data: + duration: "{{ (trigger.event.data.minutes | int) * 60 }}" + - action: switch.turn_on + target: + entity_id: switch.resin_uv_light + - conditions: + - condition: template + value_template: >- + {{ trigger.id == 'command' + and trigger.event.data.get('command') == 'stop' }} + sequence: + - action: switch.turn_off + continue_on_error: true + target: + entity_id: switch.resin_uv_light + - action: timer.cancel + target: + entity_id: timer.resin_curing + default: + # Ignore malformed commands. Recheck current state when a queued + # event runs so an old expiry cannot stop a newly started session. + - condition: template + value_template: "{{ trigger.id != 'command' }}" + - choose: + - conditions: + - condition: template + value_template: >- + {{ not is_state('timer.resin_curing', 'active') + and not is_state('switch.resin_uv_light', 'off') }} + sequence: + - action: switch.turn_off + continue_on_error: true + target: + entity_id: switch.resin_uv_light + - conditions: + - condition: state + entity_id: timer.resin_curing + state: active + - condition: state + entity_id: switch.resin_uv_light + state: "off" + sequence: + # A physical/HA outlet shutoff ends the displayed countdown. + - action: timer.cancel + target: + entity_id: timer.resin_curing + mode: queued + max: 20