Setting up a RESTful Sensor to Find a Specific Future Google Calendar Event

Using this post as a starting point, I’ve created a RESTful sensor that looks into the future for a specific event on a Google Calendar. Perhaps this would be helpful for others to adapt for their own needs.

For my use case, I wanted to find the next full moon date. I already had the Google calendar integration and Google provides a nice moon phase calendar that you can add to your account.

The only events in the calendar are full moon, first quarter, last quarter, and new moon. Setting a date range of 30 days into the future will always find 4 events, one of which will be the full moon event. My first attempt at this sensor was using regex to find the full moon event, but this proved simpler (for me). Unfortunately, it doesn’t seem possible to set json_attributes for multiple root elements of the same name. I would love to be corrected on that if I am wrong.

The value template evaluates the 4 events to find the full moon event, then populates the date as the state.

I am not an engineer, programmer, or developer, nor a Jinja2 ninja, so it’s possible/likely that my templating can be improved.

rest:
  - resource: !secret calendar_rest_resource
    method: GET
    scan_interval: 3600
    headers:
      authorization: !secret calendar_rest_token
    params:
      start: >
        {{ utcnow().strftime('%Y-%m-%dT%H:%M:%S.000Z') }}
      end: >
        {{ (utcnow() + timedelta(days=30)).strftime('%Y-%m-%dT%H:%M:%S.000Z') }}
    sensor: 
    - name: Next Full Moon Date
      unique_id: next_full_moon_date
      json_attributes_path: "$."
      value_template: >
        {% set phase0 = value_json[0] | string %}
        {% set phase1 = value_json[1] | string %}
        {% set phase2 = value_json[2] | string %}        
        {% set phase3 = value_json[3] | string %}
        {% if 'Full moon' in phase0 %}
          {% set phase0json = value_json[0] %}
          {{ phase0json.start.date }}
        {% elif 'Full moon' in phase1 %}
          {% set phase1json = value_json[1] %}
          {{ phase1json.start.date }}
        {% elif 'Full moon' in phase2 %}
          {% set phase2json = value_json[2] %}
          {{ phase2json.start.date }}
        {% elif 'Full moon' in phase3 %}
          {% set phase3json = value_json[3] %}
          {{ phase3json.start.date }}
        {% else %}
          MOON GONE
        {% endif %}

Example JSON response:

[
   {
      "start":{
         "date":"2023-04-20"
      },
      "end":{
         "date":"2023-04-21"
      },
      "summary":"New moon 12:12am",
      "description":"None",
      "location":"None",
      "uid":"[email protected]",
      "recurrence_id":"None",
      "rrule":"None"
   },
   {
      "start":{
         "date":"2023-04-27"
      },
      "end":{
         "date":"2023-04-28"
      },
      "summary":"First quarter 5:20pm",
      "description":"None",
      "location":"None",
      "uid":"[email protected]",
      "recurrence_id":"None",
      "rrule":"None"
   },
   {
      "start":{
         "date":"2023-05-05"
      },
      "end":{
         "date":"2023-05-06"
      },
      "summary":"Full moon 1:34pm",
      "description":"None",
      "location":"None",
      "uid":"[email protected]",
      "recurrence_id":"None",
      "rrule":"None"
   },
   {
      "start":{
         "date":"2023-05-12"
      },
      "end":{
         "date":"2023-05-13"
      },
      "summary":"Last quarter 10:28am",
      "description":"None",
      "location":"None",
      "uid":"[email protected]",
      "recurrence_id":"None",
      "rrule":"None"
   }
]

I paired this with a template sensor to calculate the number of days until the next full moon. (Based on this post.)

template:
  - sensor:
    - name: Days Until Next Full Moon
      unique_id: days_until_next_full_moon
      state: >
        {% set midnight = today_at() %}
        {% set event = iif( states('sensor.next_full_moon_date') in ['unavailable', 'unknown', 'none'], today_at() | string, states('sensor.next_full_moon_date'), today_at() | string) | as_datetime | as_local %}
        {% set delta = event - midnight %}
        {% if delta.days < 1 %}
          0
        {% elif delta.days >= 1 %}
          {{ delta.days}}
        {% endif %}

Together, I end up with these two sensors which serve my need well.

Nice work!