This may seem like a trivial project. But I found it to be more challenging that I expected, and it touches on some odd syntax which I haven’t seen many examples of, so I suspect others might find it helpful. Also, some of you might be able to point out mistakes or better ways of doing this.
I like to know the phase of the moon. It helps me internalize the times of tides better than trying to memorize the exact times of the highs and lows for the coming days. What works best is when I know how many days we are past the last full or new moon. Text like “Waxing gibbous” isn’t specific enough.
Far better would be to see something like “3 days since the last full moon.”
The first data point I need to track is the previous quarter phase (new, full and first and last quarters.)
Next I need to keep a count of how many days it’s been since then. From what I’ve read, the best way to store persistent variables in HA is to use input_text
and input_number
. Here’s what I ended up with (in configuration.yaml):
input_text:
# Store days since last moon phase strings
moon_last_phase:
name: Last Moon Phase
input_number:
# Store days since last moon phase
moon_days_elapsed:
name: "Days Since Last Moon"
min: 0
max: 8
step: 1
unit_of_measurement: 'Days'
I don’t really need the min:
, max:
or step:
, but they were in the sample I copied so I figured I’d leave them in as an example for other projects.
Next I need a way to populate these values. My logic goes like this: the sensor.moon
state changes eight times per lunar month. For four of those, I want to change the moon_last_phase input text, and reset the moon_days_elapsed counter to zero.
For the other four state changes, I want to just increment the moon_days_elapsed counter.
Here’s the automation I came up with.
#
# Update days since last moon phase
#
- id: moon_days
alias: Moon Days Since Phase
description: Set days since last moon phase
mode: single
trigger:
- platform: state
entity_id:
- sensor.moon
action:
- choose:
# If we're at one of the four primary phases
# Change the saved phase and reset the elapsed days
- conditions:
- condition: template
value_template: "{{ states('sensor.moon') in ['full_moon', 'new_moon', 'first_quarter', 'last_quarter'] }}"
sequence:
- service: input_number.set_value
data:
entity_id: input_number.moon_days_elapsed
value: 0
- service: input_text.set_value
data:
entity_id: input_text.moon_last_phase
value: "{{ states('sensor.moon') }}"
# If we're at one of the in-between phases
# Increment the elapsed days
- conditions:
- condition: template
value_template: "{{ states('sensor.moon') in ['waxing_crescent', 'waxing_gibbous', 'waning_crescent', 'waning_gibbous'] }}"
sequence:
- service: input_number.set_value
data:
entity_id: input_number.moon_days_elapsed
value: "{{ states('input_number.moon_days_elapsed') | int + 1 }}"
So far I’ve only tested this through one phase change. There could be syntax errors or typos which will show up in future changes.
Also, I’m not 100% certain that this automation will only run once for each change. If it somehow gets triggered at any other time, that will mess up the whole thing.
The last piece was to display the results as a phrase. For this I used a template:
# Moon phase string
- name: "Moon Phase String"
unique_id: "moon_phase_string"
state: "{{states('input_number.moon_days_elapsed') | int }} days since {{states('input_text.moon_last_phase') | regex_replace(find='_', replace=' ', ignorecase=False) }}"
I used a regular expression to replace the hyphen ("-") in the state with a space. I never did figure out why, when you display the sate of sensor.moon in a dashboard it prints it in mixed case with no hyphen, like “Waxing gibbous.” I’m OK with just removing the hyphen.
When I displayed this template, I noticed it included an icon. Of course I wanted the icon to match the text. So I added some logic to the template. Here’s the final result:
# Moon phase string
- name: "Moon Phase String"
unique_id: "moon_phase_string"
state: "{{states('input_number.moon_days_elapsed') | int }} days since {{states('input_text.moon_last_phase') | regex_replace(find='_', replace=' ', ignorecase=False) }}"
icon: >
{% if is_state("sensor.moon", "full_moon") %}
mdi:moon-new
{% elif is_state("sensor.moon", "waning_gibbous") %}
mdi:moon-waning-gibbous
{% elif is_state("sensor.moon", "last_quarter") %}
mdi:moon-last-quarter
{% elif is_state("sensor.moon", "waning_crescent") %}
mdi:moon-waning-crescent
{% elif is_state("sensor.moon", "new_moon") %}
mdi:moon-new
{% elif is_state("sensor.moon", "waxing_crescent") %}
mdi:moon-waxing-crescent
{% elif is_state("sensor.moon", "first_quarter") %}
mdi:moon-first-quarter
{% elif is_state("sensor.moon", "waxing_gibbous") %}
mdi:moon-waxing-gibbous
{% endif %}
As it happens, today the moon was in its last quarter, and the automation did its job. My Tides card looked like this today:
It does look a little odd with the zero there, but I know what it means.
That’s it. Feel free to use, improve or criticize it.