openhasp mods and file shuffling
This commit is contained in:
@@ -0,0 +1,809 @@
|
||||
#:########################################################################################:#
|
||||
# Jobs Tracker Job List Package #
|
||||
#:########################################################################################:#
|
||||
#
|
||||
# TITLE:
|
||||
# Jobs Tracker Job List
|
||||
#
|
||||
# FILE:
|
||||
# jobs_tracker_jobs.yaml
|
||||
#
|
||||
# VERSION:
|
||||
# V1.4 2026-06-20
|
||||
#
|
||||
# PURPOSE:
|
||||
# Defines the job list, MQTT entities, completion triggers and summary output
|
||||
# for the Jobs Tracker system.
|
||||
#
|
||||
# DEPENDS ON:
|
||||
# jobs_tracker_processor.yaml V1.3.1 or newer
|
||||
#
|
||||
# MQTT TOPICS:
|
||||
# viewroad-status/jobs_tracker/summary
|
||||
# viewroad-status/jobs_tracker/dog_feeding_morning
|
||||
# viewroad-status/jobs_tracker/dog_feeding_evening
|
||||
# viewroad-status/jobs_tracker/dog_depooping
|
||||
# viewroad-status/jobs_tracker/dishwasher_unpacked
|
||||
# viewroad-status/jobs_tracker/rubbish_removed
|
||||
#
|
||||
# JOB STATES:
|
||||
# Not Due
|
||||
# Due
|
||||
# Overdue
|
||||
# Complete
|
||||
#
|
||||
# VERSION HISTORY:
|
||||
# V1.4 2026-06-20
|
||||
# - Updated shared dog-fed button behaviour.
|
||||
# - If either dog-feeding job is Due or Overdue, the shared physical button
|
||||
# completes every dog-feeding job that is currently Due or Overdue.
|
||||
# - If neither dog-feeding job is Due or Overdue, the shared physical button
|
||||
# falls back to time-based early completion.
|
||||
#
|
||||
# V1.3 2026-06-20
|
||||
# - Added Not Due support for daily jobs before configured due time.
|
||||
# - Added grouped MQTT Jobs Tracker device.
|
||||
#
|
||||
# NOTES:
|
||||
# - due_weekday uses Python numbering:
|
||||
# Monday=0, Tuesday=1, Wednesday=2, Thursday=3,
|
||||
# Friday=4, Saturday=5, Sunday=6
|
||||
#
|
||||
# - event.dog_fed_button_xbt14_action is shared by:
|
||||
# Dog Feeding Morning
|
||||
# Dog Feeding Evening
|
||||
#
|
||||
# - sensor.jobs_done marks both:
|
||||
# Dishwasher Unpacked
|
||||
# Rubbish Removed
|
||||
#
|
||||
#:########################################################################################:#
|
||||
|
||||
#:########################################################################################:#
|
||||
# MANUAL TEST BUTTONS #
|
||||
#:########################################################################################:#
|
||||
|
||||
input_button:
|
||||
jobs_tracker_complete_dog_feeding_morning:
|
||||
name: "Jobs Tracker Complete Dog Feeding Morning"
|
||||
icon: mdi:dog-side
|
||||
|
||||
jobs_tracker_complete_dog_feeding_evening:
|
||||
name: "Jobs Tracker Complete Dog Feeding Evening"
|
||||
icon: mdi:dog-side
|
||||
|
||||
jobs_tracker_complete_dog_depooping:
|
||||
name: "Jobs Tracker Complete Dog Depooping"
|
||||
icon: mdi:emoticon-poop
|
||||
|
||||
jobs_tracker_complete_dishwasher_unpacked:
|
||||
name: "Jobs Tracker Complete Dishwasher Unpacked"
|
||||
icon: mdi:dishwasher
|
||||
|
||||
jobs_tracker_complete_rubbish_removed:
|
||||
name: "Jobs Tracker Complete Rubbish Removed"
|
||||
icon: mdi:trash-can
|
||||
|
||||
#:########################################################################################:#
|
||||
# MQTT SENSORS #
|
||||
#:########################################################################################:#
|
||||
|
||||
mqtt:
|
||||
sensor:
|
||||
#:####################################################################################:#
|
||||
# Summary sensor #
|
||||
#:####################################################################################:#
|
||||
|
||||
- name: "Jobs Tracker - Summary"
|
||||
unique_id: jobs_tracker_summary
|
||||
state_topic: "viewroad-status/jobs_tracker/summary"
|
||||
value_template: "{{ value_json.state | default('unknown') }}"
|
||||
json_attributes_topic: "viewroad-status/jobs_tracker/summary"
|
||||
icon: mdi:clipboard-list
|
||||
device:
|
||||
identifiers:
|
||||
- viewroad_jobs_tracker
|
||||
name: "Jobs Tracker"
|
||||
manufacturer: "View Road Home Assistant"
|
||||
model: "Shared household jobs tracker"
|
||||
sw_version: "1.4"
|
||||
|
||||
#:####################################################################################:#
|
||||
# Individual job sensors #
|
||||
#:####################################################################################:#
|
||||
|
||||
- name: "Jobs Tracker - Dog Feeding Morning"
|
||||
unique_id: jobs_tracker_dog_feeding_morning
|
||||
state_topic: "viewroad-status/jobs_tracker/dog_feeding_morning"
|
||||
value_template: "{{ value_json.state | default('unknown') }}"
|
||||
json_attributes_topic: "viewroad-status/jobs_tracker/dog_feeding_morning"
|
||||
icon: mdi:dog-side
|
||||
device:
|
||||
identifiers:
|
||||
- viewroad_jobs_tracker
|
||||
name: "Jobs Tracker"
|
||||
manufacturer: "View Road Home Assistant"
|
||||
model: "Shared household jobs tracker"
|
||||
sw_version: "1.4"
|
||||
|
||||
- name: "Jobs Tracker - Dog Feeding Evening"
|
||||
unique_id: jobs_tracker_dog_feeding_evening
|
||||
state_topic: "viewroad-status/jobs_tracker/dog_feeding_evening"
|
||||
value_template: "{{ value_json.state | default('unknown') }}"
|
||||
json_attributes_topic: "viewroad-status/jobs_tracker/dog_feeding_evening"
|
||||
icon: mdi:dog-side
|
||||
device:
|
||||
identifiers:
|
||||
- viewroad_jobs_tracker
|
||||
name: "Jobs Tracker"
|
||||
manufacturer: "View Road Home Assistant"
|
||||
model: "Shared household jobs tracker"
|
||||
sw_version: "1.4"
|
||||
|
||||
- name: "Jobs Tracker - Dog Depooping"
|
||||
unique_id: jobs_tracker_dog_depooping
|
||||
state_topic: "viewroad-status/jobs_tracker/dog_depooping"
|
||||
value_template: "{{ value_json.state | default('unknown') }}"
|
||||
json_attributes_topic: "viewroad-status/jobs_tracker/dog_depooping"
|
||||
icon: mdi:emoticon-poop
|
||||
device:
|
||||
identifiers:
|
||||
- viewroad_jobs_tracker
|
||||
name: "Jobs Tracker"
|
||||
manufacturer: "View Road Home Assistant"
|
||||
model: "Shared household jobs tracker"
|
||||
sw_version: "1.4"
|
||||
|
||||
- name: "Jobs Tracker - Dishwasher Unpacked"
|
||||
unique_id: jobs_tracker_dishwasher_unpacked
|
||||
state_topic: "viewroad-status/jobs_tracker/dishwasher_unpacked"
|
||||
value_template: "{{ value_json.state | default('unknown') }}"
|
||||
json_attributes_topic: "viewroad-status/jobs_tracker/dishwasher_unpacked"
|
||||
icon: mdi:dishwasher
|
||||
device:
|
||||
identifiers:
|
||||
- viewroad_jobs_tracker
|
||||
name: "Jobs Tracker"
|
||||
manufacturer: "View Road Home Assistant"
|
||||
model: "Shared household jobs tracker"
|
||||
sw_version: "1.4"
|
||||
|
||||
- name: "Jobs Tracker - Rubbish Removed"
|
||||
unique_id: jobs_tracker_rubbish_removed
|
||||
state_topic: "viewroad-status/jobs_tracker/rubbish_removed"
|
||||
value_template: "{{ value_json.state | default('unknown') }}"
|
||||
json_attributes_topic: "viewroad-status/jobs_tracker/rubbish_removed"
|
||||
icon: mdi:trash-can
|
||||
device:
|
||||
identifiers:
|
||||
- viewroad_jobs_tracker
|
||||
name: "Jobs Tracker"
|
||||
manufacturer: "View Road Home Assistant"
|
||||
model: "Shared household jobs tracker"
|
||||
sw_version: "1.4"
|
||||
|
||||
#:########################################################################################:#
|
||||
# TEMPLATE SENSORS #
|
||||
#:########################################################################################:#
|
||||
|
||||
template:
|
||||
- binary_sensor:
|
||||
- name: "Job Complete - Dog Feeding Morning"
|
||||
unique_id: job_complete_dog_feeding_morning
|
||||
icon: mdi:dog-side
|
||||
state: "{{ is_state('sensor.jobs_tracker_dog_feeding_morning', 'Complete') }}"
|
||||
|
||||
- name: "Job Complete - Dog Feeding Evening"
|
||||
unique_id: job_complete_dog_feeding_evening
|
||||
icon: mdi:dog-side
|
||||
state: "{{ is_state('sensor.jobs_tracker_dog_feeding_evening', 'Complete') }}"
|
||||
|
||||
- name: "Job Complete - Dog Depooping"
|
||||
unique_id: job_complete_dog_depooping
|
||||
icon: mdi:emoticon-poop
|
||||
state: "{{ is_state('sensor.jobs_tracker_dog_depooping', 'Complete') }}"
|
||||
|
||||
- name: "Job Complete - Dishwasher Unpacked"
|
||||
unique_id: job_complete_dishwasher_unpacked
|
||||
icon: mdi:dishwasher
|
||||
state: "{{ is_state('sensor.jobs_tracker_dishwasher_unpacked', 'Complete') }}"
|
||||
|
||||
- name: "Job Complete - Rubbish Removed"
|
||||
unique_id: job_complete_rubbish_removed
|
||||
icon: mdi:trash-can
|
||||
state: "{{ is_state('sensor.jobs_tracker_rubbish_removed', 'Complete') }}"
|
||||
|
||||
- sensor:
|
||||
- name: "Jobs Tracker - Complete Count"
|
||||
unique_id: jobs_tracker_complete_count
|
||||
icon: mdi:clipboard-check
|
||||
state: >-
|
||||
{{
|
||||
[
|
||||
states('sensor.jobs_tracker_dog_feeding_morning'),
|
||||
states('sensor.jobs_tracker_dog_feeding_evening'),
|
||||
states('sensor.jobs_tracker_dog_depooping'),
|
||||
states('sensor.jobs_tracker_dishwasher_unpacked'),
|
||||
states('sensor.jobs_tracker_rubbish_removed')
|
||||
] | select('eq', 'Complete') | list | count
|
||||
}}
|
||||
|
||||
- name: "Jobs Tracker - Not Due Count"
|
||||
unique_id: jobs_tracker_not_due_count
|
||||
icon: mdi:clipboard-clock-outline
|
||||
state: >-
|
||||
{{
|
||||
[
|
||||
states('sensor.jobs_tracker_dog_feeding_morning'),
|
||||
states('sensor.jobs_tracker_dog_feeding_evening'),
|
||||
states('sensor.jobs_tracker_dog_depooping'),
|
||||
states('sensor.jobs_tracker_dishwasher_unpacked'),
|
||||
states('sensor.jobs_tracker_rubbish_removed')
|
||||
] | select('eq', 'Not Due') | list | count
|
||||
}}
|
||||
|
||||
- name: "Jobs Tracker - Due Count"
|
||||
unique_id: jobs_tracker_due_count
|
||||
icon: mdi:clipboard-alert
|
||||
state: >-
|
||||
{{
|
||||
[
|
||||
states('sensor.jobs_tracker_dog_feeding_morning'),
|
||||
states('sensor.jobs_tracker_dog_feeding_evening'),
|
||||
states('sensor.jobs_tracker_dog_depooping'),
|
||||
states('sensor.jobs_tracker_dishwasher_unpacked'),
|
||||
states('sensor.jobs_tracker_rubbish_removed')
|
||||
] | select('eq', 'Due') | list | count
|
||||
}}
|
||||
|
||||
- name: "Jobs Tracker - Overdue Count"
|
||||
unique_id: jobs_tracker_overdue_count
|
||||
icon: mdi:clipboard-alert-outline
|
||||
state: >-
|
||||
{{
|
||||
[
|
||||
states('sensor.jobs_tracker_dog_feeding_morning'),
|
||||
states('sensor.jobs_tracker_dog_feeding_evening'),
|
||||
states('sensor.jobs_tracker_dog_depooping'),
|
||||
states('sensor.jobs_tracker_dishwasher_unpacked'),
|
||||
states('sensor.jobs_tracker_rubbish_removed')
|
||||
] | select('eq', 'Overdue') | list | count
|
||||
}}
|
||||
|
||||
#:########################################################################################:#
|
||||
# SCRIPTS #
|
||||
#:########################################################################################:#
|
||||
|
||||
script:
|
||||
jobs_tracker_dispatch:
|
||||
alias: "Jobs Tracker - Dispatch"
|
||||
description: "Runs the generic processor against either all jobs or one completed job."
|
||||
mode: queued
|
||||
max: 20
|
||||
|
||||
fields:
|
||||
update_all:
|
||||
description: "True to refresh all jobs."
|
||||
example: true
|
||||
|
||||
job_id:
|
||||
description: "Job ID to mark complete."
|
||||
example: "dog_feeding_morning"
|
||||
|
||||
sequence:
|
||||
- variables:
|
||||
jobs_tracker_jobs:
|
||||
- job_id: dog_feeding_morning
|
||||
job_name: "Dog Feeding Morning"
|
||||
repeat_type: daily
|
||||
due_time: "06:00"
|
||||
due_weekday: 0
|
||||
due_day: 1
|
||||
mqtt_topic: "viewroad-status/jobs_tracker/dog_feeding_morning"
|
||||
state_entity: "sensor.jobs_tracker_dog_feeding_morning"
|
||||
|
||||
- job_id: dog_feeding_evening
|
||||
job_name: "Dog Feeding Evening"
|
||||
repeat_type: daily
|
||||
due_time: "17:00"
|
||||
due_weekday: 0
|
||||
due_day: 1
|
||||
mqtt_topic: "viewroad-status/jobs_tracker/dog_feeding_evening"
|
||||
state_entity: "sensor.jobs_tracker_dog_feeding_evening"
|
||||
|
||||
- job_id: dog_depooping
|
||||
job_name: "Dog De-pooping"
|
||||
repeat_type: weekly
|
||||
due_time: "08:00"
|
||||
due_weekday: 4
|
||||
due_day: 1
|
||||
mqtt_topic: "viewroad-status/jobs_tracker/dog_depooping"
|
||||
state_entity: "sensor.jobs_tracker_dog_depooping"
|
||||
|
||||
- job_id: dishwasher_unpacked
|
||||
job_name: "Dishwasher Unpacked"
|
||||
repeat_type: daily
|
||||
due_time: "15:30"
|
||||
due_weekday: 0
|
||||
due_day: 1
|
||||
mqtt_topic: "viewroad-status/jobs_tracker/dishwasher_unpacked"
|
||||
state_entity: "sensor.jobs_tracker_dishwasher_unpacked"
|
||||
|
||||
- job_id: rubbish_removed
|
||||
job_name: "Rubbish Removed"
|
||||
repeat_type: daily
|
||||
due_time: "15:30"
|
||||
due_weekday: 0
|
||||
due_day: 1
|
||||
mqtt_topic: "viewroad-status/jobs_tracker/rubbish_removed"
|
||||
state_entity: "sensor.jobs_tracker_rubbish_removed"
|
||||
|
||||
- choose:
|
||||
#:################################################################################:#
|
||||
# Refresh all jobs #
|
||||
#:################################################################################:#
|
||||
|
||||
- conditions:
|
||||
- condition: template
|
||||
value_template: "{{ update_all | default(false) | bool }}"
|
||||
sequence:
|
||||
- repeat:
|
||||
for_each: "{{ jobs_tracker_jobs }}"
|
||||
sequence:
|
||||
- action: script.jobs_tracker_update_job
|
||||
data:
|
||||
job_id: "{{ repeat.item.job_id }}"
|
||||
job_name: "{{ repeat.item.job_name }}"
|
||||
repeat_type: "{{ repeat.item.repeat_type }}"
|
||||
due_time: "{{ repeat.item.due_time }}"
|
||||
due_weekday: "{{ repeat.item.due_weekday }}"
|
||||
due_day: "{{ repeat.item.due_day }}"
|
||||
mqtt_topic: "{{ repeat.item.mqtt_topic }}"
|
||||
state_entity: "{{ repeat.item.state_entity }}"
|
||||
mark_complete: false
|
||||
|
||||
#:##################################################################################:#
|
||||
# Mark one job complete #
|
||||
#:##################################################################################:#
|
||||
|
||||
default:
|
||||
- repeat:
|
||||
for_each: "{{ jobs_tracker_jobs }}"
|
||||
sequence:
|
||||
- if:
|
||||
- condition: template
|
||||
value_template: "{{ repeat.item.job_id == job_id }}"
|
||||
then:
|
||||
- action: script.jobs_tracker_update_job
|
||||
data:
|
||||
job_id: "{{ repeat.item.job_id }}"
|
||||
job_name: "{{ repeat.item.job_name }}"
|
||||
repeat_type: "{{ repeat.item.repeat_type }}"
|
||||
due_time: "{{ repeat.item.due_time }}"
|
||||
due_weekday: "{{ repeat.item.due_weekday }}"
|
||||
due_day: "{{ repeat.item.due_day }}"
|
||||
mqtt_topic: "{{ repeat.item.mqtt_topic }}"
|
||||
state_entity: "{{ repeat.item.state_entity }}"
|
||||
mark_complete: true
|
||||
|
||||
# Give the MQTT sensors a moment to update from the retained job payloads
|
||||
# before building the summary payload.
|
||||
- delay: "00:00:01"
|
||||
|
||||
- action: script.jobs_tracker_publish_summary
|
||||
|
||||
jobs_tracker_publish_summary:
|
||||
alias: "Jobs Tracker - Publish Summary"
|
||||
description: "Publishes retained MQTT JSON summary for all tracked jobs."
|
||||
mode: queued
|
||||
max: 5
|
||||
|
||||
sequence:
|
||||
- variables:
|
||||
complete_count: >-
|
||||
{{
|
||||
[
|
||||
states('sensor.jobs_tracker_dog_feeding_morning'),
|
||||
states('sensor.jobs_tracker_dog_feeding_evening'),
|
||||
states('sensor.jobs_tracker_dog_depooping'),
|
||||
states('sensor.jobs_tracker_dishwasher_unpacked'),
|
||||
states('sensor.jobs_tracker_rubbish_removed')
|
||||
] | select('eq', 'Complete') | list | count
|
||||
}}
|
||||
|
||||
not_due_count: >-
|
||||
{{
|
||||
[
|
||||
states('sensor.jobs_tracker_dog_feeding_morning'),
|
||||
states('sensor.jobs_tracker_dog_feeding_evening'),
|
||||
states('sensor.jobs_tracker_dog_depooping'),
|
||||
states('sensor.jobs_tracker_dishwasher_unpacked'),
|
||||
states('sensor.jobs_tracker_rubbish_removed')
|
||||
] | select('eq', 'Not Due') | list | count
|
||||
}}
|
||||
|
||||
due_count: >-
|
||||
{{
|
||||
[
|
||||
states('sensor.jobs_tracker_dog_feeding_morning'),
|
||||
states('sensor.jobs_tracker_dog_feeding_evening'),
|
||||
states('sensor.jobs_tracker_dog_depooping'),
|
||||
states('sensor.jobs_tracker_dishwasher_unpacked'),
|
||||
states('sensor.jobs_tracker_rubbish_removed')
|
||||
] | select('eq', 'Due') | list | count
|
||||
}}
|
||||
|
||||
overdue_count: >-
|
||||
{{
|
||||
[
|
||||
states('sensor.jobs_tracker_dog_feeding_morning'),
|
||||
states('sensor.jobs_tracker_dog_feeding_evening'),
|
||||
states('sensor.jobs_tracker_dog_depooping'),
|
||||
states('sensor.jobs_tracker_dishwasher_unpacked'),
|
||||
states('sensor.jobs_tracker_rubbish_removed')
|
||||
] | select('eq', 'Overdue') | list | count
|
||||
}}
|
||||
|
||||
unknown_count: >-
|
||||
{{
|
||||
[
|
||||
states('sensor.jobs_tracker_dog_feeding_morning'),
|
||||
states('sensor.jobs_tracker_dog_feeding_evening'),
|
||||
states('sensor.jobs_tracker_dog_depooping'),
|
||||
states('sensor.jobs_tracker_dishwasher_unpacked'),
|
||||
states('sensor.jobs_tracker_rubbish_removed')
|
||||
]
|
||||
| reject('in', ['Complete', 'Not Due', 'Due', 'Overdue'])
|
||||
| list
|
||||
| count
|
||||
}}
|
||||
|
||||
summary_state: >-
|
||||
{% if overdue_count | int(0) > 0 and due_count | int(0) > 0 %}
|
||||
{{ overdue_count }} Overdue, {{ due_count }} Due
|
||||
{% elif overdue_count | int(0) > 0 %}
|
||||
{{ overdue_count }} Overdue
|
||||
{% elif due_count | int(0) > 0 %}
|
||||
{{ due_count }} Due
|
||||
{% elif not_due_count | int(0) > 0 and complete_count | int(0) > 0 %}
|
||||
{{ complete_count }} Complete, {{ not_due_count }} Not Due
|
||||
{% elif not_due_count | int(0) > 0 %}
|
||||
{{ not_due_count }} Not Due
|
||||
{% elif complete_count | int(0) == 5 %}
|
||||
All Complete
|
||||
{% elif unknown_count | int(0) > 0 %}
|
||||
{{ unknown_count }} Unknown
|
||||
{% else %}
|
||||
Unknown
|
||||
{% endif %}
|
||||
|
||||
- action: mqtt.publish
|
||||
data:
|
||||
topic: "viewroad-status/jobs_tracker/summary"
|
||||
retain: true
|
||||
qos: 1
|
||||
payload: >-
|
||||
{{
|
||||
{
|
||||
"state": summary_state | trim,
|
||||
"complete_count": complete_count | int(0),
|
||||
"not_due_count": not_due_count | int(0),
|
||||
"due_count": due_count | int(0),
|
||||
"overdue_count": overdue_count | int(0),
|
||||
"unknown_count": unknown_count | int(0),
|
||||
"total_count": 5,
|
||||
"jobs": [
|
||||
{
|
||||
"job_id": "dog_feeding_morning",
|
||||
"job_name": "Dog Feeding Morning",
|
||||
"state": states('sensor.jobs_tracker_dog_feeding_morning'),
|
||||
"next_due_human": state_attr('sensor.jobs_tracker_dog_feeding_morning', 'next_due_human'),
|
||||
"last_completed_human": state_attr('sensor.jobs_tracker_dog_feeding_morning', 'last_completed_human'),
|
||||
"minutes_since_required": state_attr('sensor.jobs_tracker_dog_feeding_morning', 'minutes_since_required'),
|
||||
"hours_since_required": state_attr('sensor.jobs_tracker_dog_feeding_morning', 'hours_since_required'),
|
||||
"hours_since_completed": state_attr('sensor.jobs_tracker_dog_feeding_morning', 'hours_since_completed')
|
||||
},
|
||||
{
|
||||
"job_id": "dog_feeding_evening",
|
||||
"job_name": "Dog Feeding Evening",
|
||||
"state": states('sensor.jobs_tracker_dog_feeding_evening'),
|
||||
"next_due_human": state_attr('sensor.jobs_tracker_dog_feeding_evening', 'next_due_human'),
|
||||
"last_completed_human": state_attr('sensor.jobs_tracker_dog_feeding_evening', 'last_completed_human'),
|
||||
"minutes_since_required": state_attr('sensor.jobs_tracker_dog_feeding_evening', 'minutes_since_required'),
|
||||
"hours_since_required": state_attr('sensor.jobs_tracker_dog_feeding_evening', 'hours_since_required'),
|
||||
"hours_since_completed": state_attr('sensor.jobs_tracker_dog_feeding_evening', 'hours_since_completed')
|
||||
},
|
||||
{
|
||||
"job_id": "dog_depooping",
|
||||
"job_name": "Dog De-pooping",
|
||||
"state": states('sensor.jobs_tracker_dog_depooping'),
|
||||
"next_due_human": state_attr('sensor.jobs_tracker_dog_depooping', 'next_due_human'),
|
||||
"last_completed_human": state_attr('sensor.jobs_tracker_dog_depooping', 'last_completed_human'),
|
||||
"minutes_since_required": state_attr('sensor.jobs_tracker_dog_depooping', 'minutes_since_required'),
|
||||
"hours_since_required": state_attr('sensor.jobs_tracker_dog_depooping', 'hours_since_required'),
|
||||
"hours_since_completed": state_attr('sensor.jobs_tracker_dog_depooping', 'hours_since_completed')
|
||||
},
|
||||
{
|
||||
"job_id": "dishwasher_unpacked",
|
||||
"job_name": "Dishwasher Unpacked",
|
||||
"state": states('sensor.jobs_tracker_dishwasher_unpacked'),
|
||||
"next_due_human": state_attr('sensor.jobs_tracker_dishwasher_unpacked', 'next_due_human'),
|
||||
"last_completed_human": state_attr('sensor.jobs_tracker_dishwasher_unpacked', 'last_completed_human'),
|
||||
"minutes_since_required": state_attr('sensor.jobs_tracker_dishwasher_unpacked', 'minutes_since_required'),
|
||||
"hours_since_required": state_attr('sensor.jobs_tracker_dishwasher_unpacked', 'hours_since_required'),
|
||||
"hours_since_completed": state_attr('sensor.jobs_tracker_dishwasher_unpacked', 'hours_since_completed')
|
||||
},
|
||||
{
|
||||
"job_id": "rubbish_removed",
|
||||
"job_name": "Rubbish Removed",
|
||||
"state": states('sensor.jobs_tracker_rubbish_removed'),
|
||||
"next_due_human": state_attr('sensor.jobs_tracker_rubbish_removed', 'next_due_human'),
|
||||
"last_completed_human": state_attr('sensor.jobs_tracker_rubbish_removed', 'last_completed_human'),
|
||||
"minutes_since_required": state_attr('sensor.jobs_tracker_rubbish_removed', 'minutes_since_required'),
|
||||
"hours_since_required": state_attr('sensor.jobs_tracker_rubbish_removed', 'hours_since_required'),
|
||||
"hours_since_completed": state_attr('sensor.jobs_tracker_rubbish_removed', 'hours_since_completed')
|
||||
}
|
||||
],
|
||||
"dog_feeding_morning": states('sensor.jobs_tracker_dog_feeding_morning'),
|
||||
"dog_feeding_evening": states('sensor.jobs_tracker_dog_feeding_evening'),
|
||||
"dog_depooping": states('sensor.jobs_tracker_dog_depooping'),
|
||||
"dishwasher_unpacked": states('sensor.jobs_tracker_dishwasher_unpacked'),
|
||||
"rubbish_removed": states('sensor.jobs_tracker_rubbish_removed'),
|
||||
"last_updated_local": now().strftime('%Y-%m-%d %H:%M:%S')
|
||||
} | tojson
|
||||
}}
|
||||
|
||||
#:########################################################################################:#
|
||||
# AUTOMATIONS #
|
||||
#:########################################################################################:#
|
||||
|
||||
automation:
|
||||
- id: jobs_tracker_refresh_all_jobs
|
||||
alias: "Jobs Tracker - Refresh All Jobs"
|
||||
description: "Refreshes derived due/overdue values for every job."
|
||||
mode: single
|
||||
|
||||
triggers:
|
||||
- trigger: homeassistant
|
||||
event: start
|
||||
id: ha_start
|
||||
|
||||
- trigger: time_pattern
|
||||
minutes: "/1"
|
||||
id: tick
|
||||
|
||||
actions:
|
||||
# Let retained MQTT job messages restore before the first refresh.
|
||||
- if:
|
||||
- condition: template
|
||||
value_template: "{{ trigger.id == 'ha_start' }}"
|
||||
then:
|
||||
- delay: "00:01:00"
|
||||
|
||||
- action: script.jobs_tracker_dispatch
|
||||
data:
|
||||
update_all: true
|
||||
|
||||
- id: jobs_tracker_completion_actions
|
||||
alias: "Jobs Tracker - Completion Actions"
|
||||
description: "Marks jobs complete when physical/action/manual buttons are pressed."
|
||||
mode: queued
|
||||
max: 20
|
||||
|
||||
triggers:
|
||||
#:####################################################################################:#
|
||||
# Physical / action entities #
|
||||
#:####################################################################################:#
|
||||
|
||||
- trigger: state
|
||||
entity_id: event.dog_fed_button_xbt14_action
|
||||
id: dog_fed_button
|
||||
|
||||
- trigger: state
|
||||
entity_id: event.dog_depooped_button_xbt15_action
|
||||
id: dog_depooped_button
|
||||
|
||||
- trigger: state
|
||||
entity_id: sensor.jobs_done
|
||||
id: jobs_done_sensor
|
||||
|
||||
#:####################################################################################:#
|
||||
# Manual test buttons #
|
||||
#:####################################################################################:#
|
||||
|
||||
- trigger: state
|
||||
entity_id: input_button.jobs_tracker_complete_dog_feeding_morning
|
||||
id: manual_dog_feeding_morning
|
||||
|
||||
- trigger: state
|
||||
entity_id: input_button.jobs_tracker_complete_dog_feeding_evening
|
||||
id: manual_dog_feeding_evening
|
||||
|
||||
- trigger: state
|
||||
entity_id: input_button.jobs_tracker_complete_dog_depooping
|
||||
id: manual_dog_depooping
|
||||
|
||||
- trigger: state
|
||||
entity_id: input_button.jobs_tracker_complete_dishwasher_unpacked
|
||||
id: manual_dishwasher_unpacked
|
||||
|
||||
- trigger: state
|
||||
entity_id: input_button.jobs_tracker_complete_rubbish_removed
|
||||
id: manual_rubbish_removed
|
||||
|
||||
conditions:
|
||||
- condition: template
|
||||
value_template: >-
|
||||
{{
|
||||
trigger.to_state is not none
|
||||
and trigger.to_state.state not in ['unknown', 'unavailable']
|
||||
}}
|
||||
|
||||
actions:
|
||||
- choose:
|
||||
#:################################################################################:#
|
||||
# Dog fed physical button #
|
||||
#:################################################################################:#
|
||||
#
|
||||
# Shared button behaviour:
|
||||
#
|
||||
# If any dog feeding jobs are already Due or Overdue:
|
||||
# - Complete every linked dog-feeding job that is currently Due or Overdue.
|
||||
#
|
||||
# If no dog feeding jobs are currently Due or Overdue:
|
||||
# - Fall back to time-based behaviour so early completion still works.
|
||||
#
|
||||
# Examples:
|
||||
#
|
||||
# 07:00, morning Due, evening Not Due:
|
||||
# - Completes morning only.
|
||||
#
|
||||
# 23:30, morning Overdue, evening Overdue:
|
||||
# - Completes morning and evening.
|
||||
#
|
||||
# 16:30, morning Complete, evening Not Due:
|
||||
# - Completes evening early.
|
||||
#
|
||||
#:################################################################################:#
|
||||
|
||||
- conditions:
|
||||
- condition: template
|
||||
value_template: "{{ trigger.id == 'dog_fed_button' }}"
|
||||
sequence:
|
||||
- variables:
|
||||
dog_feeding_morning_state: "{{ states('sensor.jobs_tracker_dog_feeding_morning') }}"
|
||||
dog_feeding_evening_state: "{{ states('sensor.jobs_tracker_dog_feeding_evening') }}"
|
||||
|
||||
dog_feeding_any_due_or_overdue: >-
|
||||
{{
|
||||
dog_feeding_morning_state in ['Due', 'Overdue']
|
||||
or dog_feeding_evening_state in ['Due', 'Overdue']
|
||||
}}
|
||||
|
||||
#:############################################################################:#
|
||||
# Complete all linked dog feeding jobs that are already Due or Overdue #
|
||||
#:############################################################################:#
|
||||
|
||||
- if:
|
||||
- condition: template
|
||||
value_template: "{{ dog_feeding_morning_state in ['Due', 'Overdue'] }}"
|
||||
then:
|
||||
- action: script.jobs_tracker_dispatch
|
||||
data:
|
||||
job_id: dog_feeding_morning
|
||||
|
||||
- if:
|
||||
- condition: template
|
||||
value_template: "{{ dog_feeding_evening_state in ['Due', 'Overdue'] }}"
|
||||
then:
|
||||
- action: script.jobs_tracker_dispatch
|
||||
data:
|
||||
job_id: dog_feeding_evening
|
||||
|
||||
#:############################################################################:#
|
||||
# Fallback for early completion #
|
||||
#:############################################################################:#
|
||||
#
|
||||
# Only used when neither dog feeding job is currently Due or Overdue.
|
||||
# This keeps useful behaviour such as feeding early before the due time.
|
||||
# It will not overwrite an already Complete job.
|
||||
|
||||
- if:
|
||||
- condition: template
|
||||
value_template: >-
|
||||
{{
|
||||
not (dog_feeding_any_due_or_overdue | bool)
|
||||
and now().hour < 12
|
||||
and dog_feeding_morning_state != 'Complete'
|
||||
}}
|
||||
then:
|
||||
- action: script.jobs_tracker_dispatch
|
||||
data:
|
||||
job_id: dog_feeding_morning
|
||||
|
||||
- if:
|
||||
- condition: template
|
||||
value_template: >-
|
||||
{{
|
||||
not (dog_feeding_any_due_or_overdue | bool)
|
||||
and now().hour >= 12
|
||||
and dog_feeding_evening_state != 'Complete'
|
||||
}}
|
||||
then:
|
||||
- action: script.jobs_tracker_dispatch
|
||||
data:
|
||||
job_id: dog_feeding_evening
|
||||
|
||||
#:################################################################################:#
|
||||
# Dog de-pooped physical button #
|
||||
#:################################################################################:#
|
||||
|
||||
- conditions:
|
||||
- condition: template
|
||||
value_template: "{{ trigger.id == 'dog_depooped_button' }}"
|
||||
sequence:
|
||||
- action: script.jobs_tracker_dispatch
|
||||
data:
|
||||
job_id: dog_depooping
|
||||
|
||||
#:################################################################################:#
|
||||
# Shared jobs_done sensor #
|
||||
#:################################################################################:#
|
||||
#
|
||||
# One state change marks both jobs complete.
|
||||
|
||||
- conditions:
|
||||
- condition: template
|
||||
value_template: "{{ trigger.id == 'jobs_done_sensor' }}"
|
||||
sequence:
|
||||
- action: script.jobs_tracker_dispatch
|
||||
data:
|
||||
job_id: dishwasher_unpacked
|
||||
|
||||
- action: script.jobs_tracker_dispatch
|
||||
data:
|
||||
job_id: rubbish_removed
|
||||
|
||||
#:################################################################################:#
|
||||
# Manual test buttons #
|
||||
#:################################################################################:#
|
||||
|
||||
- conditions:
|
||||
- condition: template
|
||||
value_template: "{{ trigger.id == 'manual_dog_feeding_morning' }}"
|
||||
sequence:
|
||||
- action: script.jobs_tracker_dispatch
|
||||
data:
|
||||
job_id: dog_feeding_morning
|
||||
|
||||
- conditions:
|
||||
- condition: template
|
||||
value_template: "{{ trigger.id == 'manual_dog_feeding_evening' }}"
|
||||
sequence:
|
||||
- action: script.jobs_tracker_dispatch
|
||||
data:
|
||||
job_id: dog_feeding_evening
|
||||
|
||||
- conditions:
|
||||
- condition: template
|
||||
value_template: "{{ trigger.id == 'manual_dog_depooping' }}"
|
||||
sequence:
|
||||
- action: script.jobs_tracker_dispatch
|
||||
data:
|
||||
job_id: dog_depooping
|
||||
|
||||
- conditions:
|
||||
- condition: template
|
||||
value_template: "{{ trigger.id == 'manual_dishwasher_unpacked' }}"
|
||||
sequence:
|
||||
- action: script.jobs_tracker_dispatch
|
||||
data:
|
||||
job_id: dishwasher_unpacked
|
||||
|
||||
- conditions:
|
||||
- condition: template
|
||||
value_template: "{{ trigger.id == 'manual_rubbish_removed' }}"
|
||||
sequence:
|
||||
- action: script.jobs_tracker_dispatch
|
||||
data:
|
||||
job_id: rubbish_removed
|
||||
@@ -0,0 +1,316 @@
|
||||
################################################################################
|
||||
# Jobs Tracker Processor Package
|
||||
################################################################################
|
||||
#
|
||||
# TITLE:
|
||||
# Jobs Tracker Processor
|
||||
#
|
||||
# FILE:
|
||||
# jobs_tracker_processor.yaml
|
||||
#
|
||||
# VERSION:
|
||||
# V1.3.1 2026-06-20
|
||||
#
|
||||
# CHANGELOG:
|
||||
# V1.3.1:
|
||||
# - Corrected script field definitions so Home Assistant reliably creates
|
||||
# script.jobs_tracker_update_job.
|
||||
#
|
||||
# V1.3:
|
||||
# - Daily jobs reset at 00:00 but do not become Due until their configured
|
||||
# due time.
|
||||
# - Added "Not Due" state.
|
||||
# - Completion is recalculated from retained last_completed_ts after restart.
|
||||
#
|
||||
################################################################################
|
||||
|
||||
script:
|
||||
jobs_tracker_update_job:
|
||||
alias: "Jobs Tracker - Update Job"
|
||||
description: "Updates one job's retained MQTT JSON state."
|
||||
mode: queued
|
||||
max: 20
|
||||
|
||||
fields:
|
||||
job_id:
|
||||
description: "Machine-safe job ID."
|
||||
example: "dog_feeding_morning"
|
||||
job_name:
|
||||
description: "Human-readable job name."
|
||||
example: "Dog Feeding Morning"
|
||||
repeat_type:
|
||||
description: "daily, weekly or monthly."
|
||||
example: "daily"
|
||||
due_time:
|
||||
description: "Time the job is due, HH:MM."
|
||||
example: "06:00"
|
||||
due_weekday:
|
||||
description: "For weekly jobs only. Monday=0, Friday=4, Sunday=6."
|
||||
example: 4
|
||||
due_day:
|
||||
description: "For monthly jobs only. Use 1-28."
|
||||
example: 1
|
||||
mqtt_topic:
|
||||
description: "MQTT topic used for retained job state."
|
||||
example: "viewroad-status/jobs_tracker/dog_feeding_morning"
|
||||
state_entity:
|
||||
description: "MQTT sensor entity that reads this job's retained JSON."
|
||||
example: "sensor.jobs_tracker_dog_feeding_morning"
|
||||
mark_complete:
|
||||
description: "Set true when a human has completed the job."
|
||||
example: true
|
||||
|
||||
sequence:
|
||||
- variables:
|
||||
mark_complete_bool: "{{ mark_complete | default(false) | bool }}"
|
||||
repeat_type_safe: "{{ repeat_type | default('daily') | lower }}"
|
||||
due_time_safe: "{{ due_time | default('00:00') }}"
|
||||
due_hour: "{{ due_time_safe.split(':')[0] | int(0) }}"
|
||||
due_minute: "{{ due_time_safe.split(':')[1] | int(0) }}"
|
||||
due_seconds: "{{ ((due_hour | int) * 3600) + ((due_minute | int) * 60) }}"
|
||||
due_weekday_safe: "{{ due_weekday | default(0) | int(0) }}"
|
||||
due_day_safe: "{{ due_day | default(1) | int(1) }}"
|
||||
now_ts: "{{ as_timestamp(now()) }}"
|
||||
today_midnight_ts: "{{ as_timestamp(today_at('00:00')) }}"
|
||||
topic_safe: "{{ mqtt_topic | default('viewroad-status/jobs_tracker/' ~ job_id) }}"
|
||||
|
||||
- variables:
|
||||
current_period_start_ts: >-
|
||||
{% if repeat_type_safe == 'daily' %}
|
||||
{{ today_midnight_ts | float(0) }}
|
||||
{% elif repeat_type_safe == 'weekly' %}
|
||||
{{ (today_midnight_ts | float(0)) - ((now().weekday() | int) * 86400) }}
|
||||
{% elif repeat_type_safe == 'monthly' %}
|
||||
{{ as_timestamp(now().replace(day=1, hour=0, minute=0, second=0, microsecond=0)) }}
|
||||
{% else %}
|
||||
{{ today_midnight_ts | float(0) }}
|
||||
{% endif %}
|
||||
|
||||
next_period_start_ts: >-
|
||||
{% if repeat_type_safe == 'daily' %}
|
||||
{{ (today_midnight_ts | float(0)) + 86400 }}
|
||||
{% elif repeat_type_safe == 'weekly' %}
|
||||
{{ ((today_midnight_ts | float(0)) - ((now().weekday() | int) * 86400)) + 604800 }}
|
||||
{% elif repeat_type_safe == 'monthly' %}
|
||||
{% set next_month = (now().replace(day=28) + timedelta(days=4)).replace(day=1, hour=0, minute=0, second=0, microsecond=0) %}
|
||||
{{ as_timestamp(next_month) }}
|
||||
{% else %}
|
||||
{{ (today_midnight_ts | float(0)) + 86400 }}
|
||||
{% endif %}
|
||||
|
||||
- variables:
|
||||
current_due_ts: >-
|
||||
{% if repeat_type_safe == 'daily' %}
|
||||
{{ (current_period_start_ts | float(0)) + (due_seconds | int) }}
|
||||
{% elif repeat_type_safe == 'weekly' %}
|
||||
{{ (current_period_start_ts | float(0)) + ((due_weekday_safe | int) * 86400) + (due_seconds | int) }}
|
||||
{% elif repeat_type_safe == 'monthly' %}
|
||||
{{ as_timestamp(now().replace(day=(due_day_safe | int), hour=(due_hour | int), minute=(due_minute | int), second=0, microsecond=0)) }}
|
||||
{% else %}
|
||||
{{ (current_period_start_ts | float(0)) + (due_seconds | int) }}
|
||||
{% endif %}
|
||||
|
||||
next_due_ts: >-
|
||||
{% if repeat_type_safe == 'daily' %}
|
||||
{{ (next_period_start_ts | float(0)) + (due_seconds | int) }}
|
||||
{% elif repeat_type_safe == 'weekly' %}
|
||||
{{ (next_period_start_ts | float(0)) + ((due_weekday_safe | int) * 86400) + (due_seconds | int) }}
|
||||
{% elif repeat_type_safe == 'monthly' %}
|
||||
{% set next_month = (now().replace(day=28) + timedelta(days=4)).replace(day=1, hour=0, minute=0, second=0, microsecond=0) %}
|
||||
{{ as_timestamp(next_month.replace(day=(due_day_safe | int), hour=(due_hour | int), minute=(due_minute | int), second=0, microsecond=0)) }}
|
||||
{% else %}
|
||||
{{ (next_period_start_ts | float(0)) + (due_seconds | int) }}
|
||||
{% endif %}
|
||||
|
||||
overdue_after_minutes: >-
|
||||
{% if repeat_type_safe == 'daily' %}
|
||||
120
|
||||
{% elif repeat_type_safe == 'weekly' %}
|
||||
2880
|
||||
{% elif repeat_type_safe == 'monthly' %}
|
||||
20160
|
||||
{% else %}
|
||||
120
|
||||
{% endif %}
|
||||
|
||||
- variables:
|
||||
stored_last_completed_ts: >-
|
||||
{% set v = state_attr(state_entity, 'last_completed_ts') %}
|
||||
{{ 0 if v in [none, '', 'unknown', 'unavailable'] else v | float(0) }}
|
||||
|
||||
last_completed_ts: >-
|
||||
{{ now_ts if mark_complete_bool else stored_last_completed_ts }}
|
||||
|
||||
- variables:
|
||||
complete_bool: >-
|
||||
{{ (last_completed_ts | float(0)) >= (current_period_start_ts | float(0)) }}
|
||||
|
||||
due_started_bool: >-
|
||||
{% if repeat_type_safe == 'daily' %}
|
||||
{{ (now_ts | float(0)) >= (current_due_ts | float(0)) }}
|
||||
{% else %}
|
||||
true
|
||||
{% endif %}
|
||||
|
||||
overdue_started_bool: >-
|
||||
{{ (now_ts | float(0)) >= ((current_due_ts | float(0)) + ((overdue_after_minutes | int(0)) * 60)) }}
|
||||
|
||||
minutes_since_required: >-
|
||||
{% if (now_ts | float(0)) <= (current_due_ts | float(0)) %}
|
||||
0
|
||||
{% else %}
|
||||
{{ (((now_ts | float(0)) - (current_due_ts | float(0))) / 60) | round(0) | int }}
|
||||
{% endif %}
|
||||
|
||||
hours_since_required: >-
|
||||
{% if (now_ts | float(0)) <= (current_due_ts | float(0)) %}
|
||||
0
|
||||
{% else %}
|
||||
{{ (((now_ts | float(0)) - (current_due_ts | float(0))) / 3600) | round(0) | int }}
|
||||
{% endif %}
|
||||
|
||||
- variables:
|
||||
job_status: >-
|
||||
{% if complete_bool | bool %}
|
||||
Complete
|
||||
{% elif not (due_started_bool | bool) %}
|
||||
Not Due
|
||||
{% elif overdue_started_bool | bool %}
|
||||
Overdue
|
||||
{% else %}
|
||||
Due
|
||||
{% endif %}
|
||||
|
||||
display_due_ts: >-
|
||||
{% if complete_bool | bool %}
|
||||
{{ next_due_ts | float(0) }}
|
||||
{% else %}
|
||||
{{ current_due_ts | float(0) }}
|
||||
{% endif %}
|
||||
|
||||
overdue_minutes: >-
|
||||
{% if job_status | trim == 'Overdue' %}
|
||||
{{ minutes_since_required | int(0) }}
|
||||
{% else %}
|
||||
0
|
||||
{% endif %}
|
||||
|
||||
overdue_hours: >-
|
||||
{% if job_status | trim == 'Overdue' %}
|
||||
{{ hours_since_required | int(0) }}
|
||||
{% else %}
|
||||
0
|
||||
{% endif %}
|
||||
|
||||
hours_since_completed: >-
|
||||
{% if (last_completed_ts | float(0)) > 0 %}
|
||||
{{ (((now_ts | float(0)) - (last_completed_ts | float(0))) / 3600) | round(0) | int }}
|
||||
{% else %}
|
||||
unknown
|
||||
{% endif %}
|
||||
|
||||
last_completed_time: >-
|
||||
{% if (last_completed_ts | float(0)) > 0 %}
|
||||
{{ (last_completed_ts | float(0)) | timestamp_custom('%H:%M', true) }}
|
||||
{% else %}
|
||||
Never
|
||||
{% endif %}
|
||||
|
||||
last_completed_human: >-
|
||||
{% set ts = last_completed_ts | float(0) %}
|
||||
{% if ts <= 0 %}
|
||||
Never
|
||||
{% else %}
|
||||
{% set d = ts | timestamp_custom('%Y-%m-%d', true) %}
|
||||
{% set t = ts | timestamp_custom('%H:%M', true) %}
|
||||
{% set today = now().strftime('%Y-%m-%d') %}
|
||||
{% set yesterday = (as_timestamp(today_at('00:00')) - 86400) | timestamp_custom('%Y-%m-%d', true) %}
|
||||
{% set completed_midnight = as_timestamp(strptime(d ~ ' 00:00:00', '%Y-%m-%d %H:%M:%S')) %}
|
||||
{% set days_ago = ((as_timestamp(today_at('00:00')) - completed_midnight) / 86400) | round(0) | int %}
|
||||
{% if d == today %}
|
||||
Today @ {{ t }}
|
||||
{% elif d == yesterday %}
|
||||
Yesterday @ {{ t }}
|
||||
{% elif days_ago < 7 %}
|
||||
Last {{ ts | timestamp_custom('%A', true) }} @ {{ t }}
|
||||
{% else %}
|
||||
{{ days_ago }} days ago @ {{ t }}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
next_due_human: >-
|
||||
{% set ts = display_due_ts | float(0) %}
|
||||
{% set d = ts | timestamp_custom('%Y-%m-%d', true) %}
|
||||
{% set t = ts | timestamp_custom('%H:%M', true) %}
|
||||
{% set today = now().strftime('%Y-%m-%d') %}
|
||||
{% set tomorrow = (as_timestamp(today_at('00:00')) + 86400) | timestamp_custom('%Y-%m-%d', true) %}
|
||||
{% set yesterday = (as_timestamp(today_at('00:00')) - 86400) | timestamp_custom('%Y-%m-%d', true) %}
|
||||
{% set due_midnight = as_timestamp(strptime(d ~ ' 00:00:00', '%Y-%m-%d %H:%M:%S')) %}
|
||||
{% set days_diff = ((due_midnight - as_timestamp(today_at('00:00'))) / 86400) | round(0) | int %}
|
||||
{% if d == today %}
|
||||
Today @ {{ t }}
|
||||
{% elif d == tomorrow %}
|
||||
Tomorrow @ {{ t }}
|
||||
{% elif d == yesterday %}
|
||||
Yesterday @ {{ t }}
|
||||
{% elif days_diff > 1 and days_diff <= 7 %}
|
||||
Next {{ ts | timestamp_custom('%A', true) }} @ {{ t }}
|
||||
{% elif days_diff < 0 and days_diff > -7 %}
|
||||
Last {{ ts | timestamp_custom('%A', true) }} @ {{ t }}
|
||||
{% elif days_diff > 7 %}
|
||||
In {{ days_diff }} days @ {{ t }}
|
||||
{% elif days_diff < -7 %}
|
||||
{{ 0 - days_diff }} days ago @ {{ t }}
|
||||
{% else %}
|
||||
{{ ts | timestamp_custom('%Y-%m-%d @ %H:%M', true) }}
|
||||
{% endif %}
|
||||
|
||||
- action: mqtt.publish
|
||||
data:
|
||||
topic: "{{ topic_safe }}"
|
||||
retain: true
|
||||
qos: 1
|
||||
payload: >-
|
||||
{{
|
||||
{
|
||||
"job_id": job_id,
|
||||
"job_name": job_name,
|
||||
"repeat": repeat_type_safe,
|
||||
"due_time": due_time_safe,
|
||||
"due_weekday": due_weekday_safe | int,
|
||||
"due_day": due_day_safe | int,
|
||||
|
||||
"state": job_status | trim,
|
||||
"complete": complete_bool | bool,
|
||||
|
||||
"last_completed_ts": last_completed_ts | float(0),
|
||||
"last_completed_time": last_completed_time,
|
||||
"last_completed_human": last_completed_human,
|
||||
|
||||
"completed_for_period_ts": current_period_start_ts | float(0) if complete_bool | bool else 0,
|
||||
"completed_for_due_ts": current_period_start_ts | float(0) if complete_bool | bool else 0,
|
||||
|
||||
"current_period_start_ts": current_period_start_ts | float(0),
|
||||
"next_period_start_ts": next_period_start_ts | float(0),
|
||||
|
||||
"current_due_ts": current_due_ts | float(0),
|
||||
"next_due_ts": next_due_ts | float(0),
|
||||
"display_due_ts": display_due_ts | float(0),
|
||||
"next_due_human": next_due_human,
|
||||
|
||||
"due_started": due_started_bool | bool,
|
||||
"overdue_started": overdue_started_bool | bool,
|
||||
|
||||
"minutes_since_required": minutes_since_required | int(0),
|
||||
"hours_since_required": hours_since_required | int(0),
|
||||
|
||||
"overdue_after_minutes": overdue_after_minutes | int(0),
|
||||
"overdue_minutes": overdue_minutes | int(0),
|
||||
"overdue_hours": overdue_hours | int(0),
|
||||
|
||||
"hours_since_completed": hours_since_completed,
|
||||
|
||||
"last_updated_local": (now_ts | float(0)) | timestamp_custom('%Y-%m-%d %H:%M:%S', true),
|
||||
"mqtt_topic": topic_safe
|
||||
} | tojson
|
||||
}}
|
||||
Reference in New Issue
Block a user