Astrology: Planetary Hours sensor and automations (with Sonos)

Hi everyone,

This is my first serious post. I thought I’d share a small project I just worked on for my astrologer partner: a sensor that calculates the current Planetary Hour.

The first challenge was calculating the length of each planetary hour on the current day, since there are 12 planetary hours regardless of daylength. Because the inbuilt sun.sun component only has a time for the following sunrise and sunset, but we need to know the daylength of the current day, I made use of @pnbruckner’s enhanced sun component:

Firstly the sensors to calculate planetary hour length, the current planetary hour’s number and name, in sensors.yaml:

# Planetary hour calculations
- platform: template
      friendly_name: "Planetary hour length today"
      unit_of_measurement: "seconds"
      value_template: >
        {{ states('sensor.daylight') | float / 12 * 3600 }}
      friendly_name: "Planetary hour number"
      entity_id: sensor.time
      value_template: >
        {% if states.sensor.sunrise.state is defined
           and states.sensor.plan_hr_len.state is defined
           and states.sensor.plan_hr_len.state | int > 0 %}
        {{ ( as_timestamp(now()) - as_timestamp(states('sensor.sunrise') )
           / ( states('sensor.plan_hr_len') | float ) )
           | round (0, "ceil") }}
        {% else %}
        {% endif%}
      friendly_name: "Planetary hour name"
      entity_id: sensor.time
      value_template: >-
        {% set plan_order = ['Moon', 'Saturn', 'Jupiter', 'Mars',
                             'Sun', 'Venus', 'Mercury'] %}
        {% set day_planets = [0, 3, 6, 2,
                              5, 1, 4] %}
        {% set hour = (( day_planets[now().weekday() | int] )
                         + ( states('sensor.plan_hr_no') | int) - 1 )
                         % plan_order | length  %}
        {{ plan_order[hour] }}

This allows me to create a lovelace card for the current planetary hour, but also to integrate the changeover of each PH into an automation, to play a custom chime for each planet at the start of that planet’s hour. First a custom script to send the correct sound file to a Sonos speaker to play (in scripts.yaml). The script will send a URL filename such as Jupiter.mp3 to the speaker:

# Script to play sound file with Sonos
  alias: "Play Planetary hour chime"
    - service: sonos.snapshot
        entity_id: "{{ sonos_entity }}"
    - service: sonos.unjoin
        entity_id: "{{ sonos_entity }}"
    - service: media_player.volume_set
        entity_id: "{{ sonos_entity }}"
        volume_level: "{{ volume }}"
    - service: media_player.play_media
        entity_id: "{{ sonos_entity }}"
        media_content_id: '{{ states("sensor.plan_hr_name") }}.mp3'
        media_content_type: 'music'
    - delay: "{{ delay }}"
    - wait_template: "{{ not is_state(sonos_entity, 'playing') }}"
      timeout: '00:06:00'
    - service: sonos.restore
        entity_id: "{{ sonos_entity }}"

Finally an automation to play the correct chime when a planet’s hour starts (in automations.yaml):

- id: 'some_big_number_XXXXX'
  alias: Planetary Hour
  description: 'Chime Planetary Hour on Change'
  - entity_id: sensor.plan_hr_name
    platform: state
  - after: 07:00
    before: '21:05'
    condition: time
  - condition: state
    entity_id: group.inhabitants
    state: home
  - after: sunrise
    before: sunset
    before_offset: 0:15
    condition: sun
  - data:
      delay: 00:00:6
      sonos_entity: media_player.room_1, media_player.room_2
      volume: 0.6
    service: script.planetary_hour_chime

If you’re astrologically inclined and this is useful, please let me know!

Very nice! I’m glad you found my custom integration useful. :smiley:

Just a few suggestions for minor improvements:

  • Typically (I think) "sec" would be used for the unit_of_measurement as opposed to "seconds". But it really doesn’t matter I guess.
  • You can use “state_attr('sensor.daylight', 'today')” instead of “states('sensor.daylight') | float”. The former is already a float and is not rounded. It’s meant for use in automations, template sensors, etc.
  • You can use “states('sensor.plan_hr_len') | int > 0” instead of “states.sensor.plan_hr_len.state is defined and states.sensor.plan_hr_len.state | int > 0”. That’s because “states('sensor.plan_hr_len')” will return 'unknown' when sensor.plan_hr_len doesn’t exist, and “| int” will return zero when it can’t convert its input to an integer.
  • You can use “now().weekday()” instead of “now().weekday() | int” because weekday() returns an int.

Thanks @pnbruckner, that all looks great! I’ll have a play when I get home.

1 Like

I just had a bit of time to add your optimisations, thanks @pnbruckner.
One thing I’d noticed was that the change of planetary hour was coming on a couple of minutes late. I wondered if this was because both plan_hr_no and plan_hr_name use sensor.time to update, and so I think the number was updating up to one minute late, then the name a minute later.

I’ve modified plan_hr_name to use entity_id: plan_hr_no. I’ve uploaded the updated script as a Github Gist:

One question: in order to keep the timing as accurate as possible, would I be better off using an automation that runs every planetary hour length (plan_hr_len, in seconds) from sunrise to manually update the state of plan_hr_no and plan_hr_name, rather than reay on sensor.time?

Feedback on the code in the link provided above:

  • sensor.plan_hr_len: Don’t specify any value for entity_id. Just remove it entirely. The sensor will automatically update right after sensor.daylight does, which is at midnight. If you feel you have to use entity_id, then use sensor.daylight as its value.
  • sensor.plan_hr_no: Since the template uses now(), and you want it to update periodically, then yes, using entity_id (e.g., with sensor.time) is necessary. However, since you’d like it to update when sensor.sunrise or sensor.plan_hr_len update, then add them to the entity_id parameter. E.g., “entity_id: [sensor.time, sensor.sunrise, sensor.plan_hr_len]”.
  • sensor.plan_hr_name: As with sensor.plan_hr_len it doesn’t require the use of the entity_id option. It will figure out it needs to update whenever sensor.plan_hr_no does automatically. Also, even though you’re using now() in the template, that’s not a problem in this case, because now().weekday() will change at midnight, but so will sensor.daylight, which will cause sensor.plan_hr_len to update, which will cause sensor.plan_hr_no to update, which will automatically cause this sensor to update.

So, to answer your question, if you make these changes, then no.

1 Like

Thanks Phil!

I removed the entity_id entries from sensor.plan_hr_len and sensor.plan_hr_name, and added [sensor.time, sensor.sunrise, sensor.plan_hr_len] to sensor.plan_hr_no. They all seem to update as expected.

Thanks again for all the suggestions! I’ve updated the Gist.

1 Like