Python_script for countdown to birthdays/anniversaries/significant dates

have you got

nextOccur = datetime.date(thisYear , dateMonth , dateDay)

in the python script

and did you add to the hass.states.set()

hass.states.set(sensorName , numberOfDays ,
  {
    "icon" : "mdi:calendar-star" ,
    "unit_of_measurement" : "days" ,
    "friendly_name" : "{}'s {}".format(name, type) ,
    "nextoccur" : "{}/{}/{}".format(nextOccur.day,nextOccur.month,nextOccur.year) ,
    "years" : years
  }
)

you need to add it to the hass.state.set()

hass.states.set(sensorName , numberOfDays ,
  {
    "icon" : "mdi:calendar-star" ,
    "unit_of_measurement" : "days" ,
    "friendly_name" : "{}'s {}".format(name, type) ,
    "original_date" : dateStr ,
    "nextoccur" : "{}/{}/{}".format(nextOccur.day,nextOccur.month,nextOccur.year) ,
    "years" : years
  }
)

that should work as it a string but if not

    "original_date" : "{}/{}/{}".format(dateStr.day,dateStr.month,dateStr.year) ,

still get head aound this python

I think the time format could be wrong did you try

'10:00:00'

I did add it to the hass.state.set(), as shown above.
Why wouldn’t it be possible to simply use the dateStr, or date?

I’ ve also tried your bottom suggestion but that won’t work, and renders None-None-None :wink: But, that’s already progress, and the system stays alive.

I’ve changed it into:

    "original_date" : "{}-{}-{}".format(dateDay,dateMonth,dateYear)

and hurray, I see the original dates in the sensors attributes! At last.
Don’t understand why, but it works for now, so thats really nice, thanks for helping me out!

I leaning Python so im wing it

but what I do know
is we are playing with a String and and datetime value which are differance

also
id you did this

"original_date" : "{}".Format(dateStr) ,

mite of fix your problem

good to see you got it working

1 Like

Thanks for the reply StePhan. No I didn’t. The format I put was from the example so figured it was right.

Thanks.

JR

just for reference, this is what I ended up with now:

hass.states.set(sensorName , numberOfDays ,
  {
    #"icon" : "mdi:calendar-star" ,
    "entity_picture": "/local/family/{}.jpg".format(name.lower()),
    "unit_of_measurement": "days",
    "friendly_name": "{} turning {} in:".format(name, years),
    "persoon": name,
    "type": "{}".format(type.title()),
    "years": years,
    "original_date" : "{}-{}-{}".format(dateDay,dateMonth,dateYear)
  }
)

so I can use a decluttering template:

card:
  type: 'custom:secondaryinfo-entity-row'
  entity: '[[entity]]'
  secondary_info: '[[ {entity}.attributes.type ]]: [[ {entity}.attributes.original_date ]]'

and card config:

type: entities
style: |
  ha-card {
    background: url('/local/images/party.png');
    background-size: cover;
    background-color: var(--paper-card-background-color);
  }
title: Celebrations
show_header_toggle: false
entities:
  - type: custom:decluttering-card
    template: event_date
    variables:
      - entity: sensor.event1

  - type: custom:decluttering-card
    template: event_date
    variables:
      - entity: sensor.event2

  - type: custom:decluttering-card
    template: event_date
    variables:
      - entity: sensor.event3

  - type: custom:decluttering-card
    template: event_date
    variables:
      - entity: sensor.event4

etcetc

nice.

1 Like

Version 3 is now in HACS - adds the ability to use a future date and only uses the apostrophe for birthdays.

HI Marc,
do you have it on a GitHub repo also? Don’t use HACS.

Yes sir…

1 Like

cool Mark,
now, another question: could the day (of the next occurence) be incorporated easily into the script? Is something I always need when checking for upcoming parties :wink:

Hey, just to let you know, your script has a no-no and there’s some simplifying that you can do. By no-no i mean you’re overriding the built in type method.

The optimization would just be a personal preference.

You can also build in some ‘safety’ in case people don’t fully configure the thing.

Also, I added the awesome and far superior US date style.

today = datetime.datetime.now().date()

name = data.get('name', 'special') # defaults to special
eventtype = data.get('type', 'event') # defaults to event
region = data.get('region', 'EU') # defaults to dirty european date format.
dateStr = data.get('date', r'01/01/{}'.format(today.year)) # defaults to jan 1st this year.

sensorName = "sensor.{}_{}".format(eventtype.lower(), name.replace(" " , "_").lower())

fmat = '%d/%m/%Y' if region == 'EU' else '%m/%d/%Y'

origin = datetime.datetime.strptime(dateStr, fmat).date()
event = origin.replace(year=today.year)
event = event if event > today else event.replace(year=event.year+1)

friendly_name = "{}'s {}".format(name, eventtype) if eventtype=="birthday" else "{} {}".format(name, eventtype)

hass.states.set(sensorName , (event-today).days,
  {
    "icon" : "mdi:calendar-star" ,
    "unit_of_measurement" : "days" ,
    "friendly_name" : friendly_name,
    "nextoccur" : event.strftime(fmat),
    "years" : (event-origin).days//365,
  }
)

uses

automation:
  - alias: Reminder - Refresh date countdown sensors
    initial_state: on
    trigger:
      - platform: time
        at: '00:00:01'
      - platform: homeassistant
        event: start
    action:
      - service: python_script.date_countdown
        data:
          name: John
          type: birthday
          date: 17/08/1971
      - service: python_script.date_countdown
        data:
          name: Our wedding
          type: anniversary
          date: 14/02/1994
      - service: python_script.date_countdown
        data:
          name: Superior Date Style
          type: birthday
          date: 11/22/2019
          region: US
1 Like

above yields:

Log Details (ERROR)
Fri Nov 22 2019 16:10:56 GMT+0100 (CET)

Error executing script: name 'dateStr' is not defined
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/components/python_script/__init__.py", line 195, in execute
    exec(compiled.code, restricted_globals, local)
  File "event_date.py", line 12, in <module>
NameError: name 'dateStr' is not defined

Oops, fixed typo

… still some left. I think we need to quote the ‘birthday’ because it errors out on not defined otherwise.

friendly_name = "{}'s {}".format(name, eventtype) if eventtype=='birthday' else "{} {}".format(name, eventtype)

more importantly:

Fri Nov 22 2019 16:22:05 GMT+0100 (CET)

Error executing script: '__import__'
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/components/python_script/__init__.py", line 195, in execute
    exec(compiled.code, restricted_globals, local)
  File "event_date.py", line 24, in <module>
KeyError: '__import__'

the ever present import issues in the HA Python sandbox. Line 24 is

    "nextoccur" : event.strftime(fmat),

Nothing to import though. You can remove the strftime. I can’t test til I get home

ok, and o dear…

taking that out gives:

hass.states.set(sensorName , (event-today).days,
  {
    "icon" : "mdi:calendar-star" ,
    "unit_of_measurement" : "days" ,
    "person" : name,
    "friendly_name" : friendly_name,
    "nextoccur" : event,
    "years" : (event-origin).days//365,
    "origin" : origin,
  }
)

and it takes down my HA instance once more, as reported above.

Unable to serialize to JSON: Object of type date is not JSON serializable

and then followed by the full config in the log for all entities and all their attributes…

must have to do with the 'next_occurandorigin` I added, because when I simply used dateString in the original script I got the same errors, and had to use:

    "original_date" : "{}-{}-{}".format(dateDay,dateMonth,dateYear)

I mean if you just want it working you can split it out. or just cast it to a string but it will be year-month-day format

hass.states.set(sensorName , (event-today).days,
  {
    "icon" : "mdi:calendar-star" ,
    "unit_of_measurement" : "days" ,
    "person" : name,
    "friendly_name" : friendly_name,
    "nextoccur" : str(event),
    "years" : (event-origin).days//365,
    "origin" : str(origin),
  }
)

But i can’t believe that strptime() works and strftime(fmat) doesn’t inside that environment.

hass.states.set(sensorName , (event-today).days,
  {
    "icon" : "mdi:calendar-star" ,
    "unit_of_measurement" : "days" ,
    "person" : name,
    "friendly_name" : friendly_name,
    "nextoccur" : event.strftime(fmat),
    "years" : (event-origin).days//365,
    "origin" : origin.strftime(fmat),
  }
)

EDIT, also just so you know, any date() object (I.E. event, origin), you can get the day, month, year out of it with simply:

event.day
event.month
event.year

yes, affirmative.
this works:

today = datetime.datetime.now().date()

name = data.get('name', 'special') # defaults to special
eventtype = data.get('type', 'event') # defaults to event
region = data.get('region', 'EU') # defaults to dirty european date format.
dateStr = data.get('date', r'01/01/{}'.format(today.year)) # defaults to jan 1st this year.

sensorName = "sensor.event_{}_{}".format(eventtype.lower(), name.replace(" " , "_").lower())

fmat = '%d/%m/%Y' if region == 'EU' else '%m/%d/%Y'

origin = datetime.datetime.strptime(dateStr, fmat).date()
event = origin.replace(year=today.year)
event = event if event > today else event.replace(year=event.year+1)

friendly_name = "{}'s {}".format(name, eventtype) if eventtype=='birthday' else "{} {}".format(name, eventtype)

hass.states.set(sensorName , (event-today).days,
  {
    #"icon" : "mdi:calendar-star" ,
    "entity_picture": "/local/family/{}.jpg".format(name.lower()),
    "unit_of_measurement" : "days" ,
    "person" : name,
    "friendly_name" : friendly_name,
    "years" : (event-origin).days//365,
    "origin": "{}-{}-{}".format(origin.day,origin.month,origin.year),
    "next_event":"{}-{}-{}".format(event.day,event.month,event.year)
  }
)

Thanks for your response. it seams that i didn’t saved the file since i couldn’t find your script on my python file. All works good now.
Thanks for your help.