Skip to content

Commit a7dc7b9

Browse files
authored
Merge pull request #13 from Darknetzz/master
Added unique_id
2 parents 6c33912 + 5d4bba9 commit a7dc7b9

File tree

3 files changed

+82
-58
lines changed

3 files changed

+82
-58
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
**/.DS_Store
2+
.history

README.md

Lines changed: 44 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ This is a HomeAssistant component for tracking birthdays, where the state of eac
1717

1818
## Set up
1919
Set up the component:
20-
~~~~
20+
```yaml
2121
# Example configuration.yaml entry
2222
birthdays:
2323
- name: 'Frodo Baggins'
@@ -27,17 +27,41 @@ birthdays:
2727
- name: Elvis
2828
date_of_birth: 1935-01-08
2929
icon: 'mdi:music'
30-
~~~~
30+
```
31+
32+
You can also add a custom `unique_id` and attributes to each birthday, for instance to add an icon or other metadata.
33+
```yaml
34+
- unique_id: bond_james_bond
35+
name: James Bond
36+
date_of_birth: 1920-05-25
37+
icon: 'mdi:pistol'
38+
attributes:
39+
occupation: "Agent"
40+
license_to_kill: "Yes"
41+
- unique_id: einstein
42+
name: 'Albert Einstein'
43+
date_of_birth: 1879-03-14
44+
icon: 'mdi:lightbulb-on'
45+
attributes:
46+
occupation: 'Theoretical physicist'
47+
iq: 'Genius level'
48+
sense_of_humor: 'Einsteinian'
49+
```
3150
Restart homeassistant
3251

3352
## Entities
34-
All entities are exposed using the format `birthdays.{name}`. Any character that does not fit the pattern `a-z`, `A-Z`, `0-9`, or `_` will be changed. For instance `Frodo Baggins` will get entity_id `frodo_baggins`, and Swedish names like [`Sven-Göran Eriksson`](https://sv.wikipedia.org/wiki/Sven-G%C3%B6ran_Eriksson) will get entity_id `sven_goran_eriksson`.
53+
All entities that do not have a specified `unique_id` are exposed using the format `birthdays.{name}`. Any character that does not fit the pattern `a-z`, `A-Z`, `0-9`, or `_` will be changed. For instance `Frodo Baggins` will get entity_id `frodo_baggins`, and Swedish names like [`Sven-Göran Eriksson`](https://sv.wikipedia.org/wiki/Sven-G%C3%B6ran_Eriksson) will get entity_id `sven_goran_eriksson`.
54+
55+
## Custom attributes
56+
You can add a unique id and custom attributes to each birthday, for instance to add an icon or other metadata.
57+
To do this, add a dictionary under the `attributes` key in the configuration (see example above). The dictionary can contain any key-value pairs you want, and will be exposed as attributes on the entity.
58+
Fetching the attributes can be done using `state_attr` in a template, for instance `{{ state_attr('birthdays.einstein', 'occupation') }}` will return `Theoretical physicist`.
3559

3660
## Automation
3761
All birthdays are updated at midnight, and when a birthday occurs an event is sent on the HA bus that can be used for automations. The event is called `birthday` and contains the data `name` and `age`. Note that there will be two events fired if two persons have the same birthday.
3862

3963
Sending a push notification for each birthday (with PushBullet) looks like this:
40-
~~~
64+
```yaml
4165
automation:
4266
trigger:
4367
platform: event
@@ -47,10 +71,10 @@ automation:
4771
data_template:
4872
title: 'Birthday!'
4973
message: "{{ trigger.event.data.name }} turns {{ trigger.event.data.age }} today!"
50-
~~~
74+
```
5175

5276
If you want to trigger an automation based on a specific name or age, you can use the following:
53-
~~~
77+
```yaml
5478
automation:
5579
trigger:
5680
platform: event
@@ -63,15 +87,17 @@ automation:
6387
data_template:
6488
title: 'Birthday!'
6589
message: "{{ trigger.event.data.name }} turns {{ trigger.event.data.age }} today!"
66-
~~~
90+
```
6791

6892
If you want to have a notification sent to you at a specific time (instead of midnight), you can use a custom templated sensor and a time trigger.
6993
Create the sensor:
7094
~~~
71-
template:
72-
- sensor:
73-
- name: "Next birthday"
74-
state: >
95+
sensor:
96+
- platform: template
97+
sensors:
98+
next_birthday:
99+
friendly_name: "Next birthday"
100+
value_template: >
75101
{%- set ns = namespace(days=365) -%}
76102
{%- for birthday in states.birthdays -%}
77103
{%- set daysLeft = birthday.state | int -%}
@@ -105,33 +131,10 @@ template:
105131
{%- set ns.days = daysLeft -%}
106132
{%- endif -%}
107133
{%- endfor -%}
108-
{%- for birthday in states.birthdays -%}
109-
{%- set daysLeft = birthday.state | int -%}
110-
{%- if daysLeft == ns.days -%}
111-
{%- set ns.ages = ns.ages + [birthday.attributes.age_at_next_birthday] -%}
112-
{%- endif -%}
113-
{%- endfor -%}
114-
115-
{{ns.ages | join(', ')}}
116-
birthday_message: >
117-
{%- set ns = namespace(days=365, messages=[]) -%}
118-
{%- for birthday in states.birthdays -%}
119-
{%- set daysLeft = birthday.state | int -%}
120-
{%- if daysLeft < ns.days -%}
121-
{%- set ns.days = daysLeft -%}
122-
{%- endif -%}
123-
{%- endfor -%}
124-
{%- for birthday in states.birthdays -%}
125-
{%- set daysLeft = birthday.state | int -%}
126-
{%- if daysLeft == ns.days -%}
127-
{%- set ns.messages = ns.messages + [birthday.attributes.friendly_name + ' turns ' + (birthday.attributes.age_at_next_birthday | string) + ' today!'] -%}
128-
{%- endif -%}
129-
{%- endfor -%}
130-
131-
{{ns.messages | join('\n')}}
134+
{{ ns.age }}
132135
~~~
133136
and the automation:
134-
~~~
137+
```yaml
135138
automation:
136139
alias: Happy birthday
137140
trigger:
@@ -144,13 +147,13 @@ automation:
144147
action:
145148
- service: persistent_notification.create
146149
data_template:
147-
title: Birthday!
148-
message: '{{ state_attr(''sensor.next_birthday'', ''birthday_message'') }}'
149-
~~~
150+
title: 'Birthday!'
151+
message: "{{ state_attr('sensor.next_birthday', 'name') }} turns {{ state_attr('sensor.next_birthday', 'age') }} today!"
152+
```
150153

151154
## Lovelace UI
152155
I use the birthdays as a simple entity list in lovelace, given the above example I use:
153-
~~~
156+
```yaml
154157
# Example use in lovelace
155158
- type: entities
156159
title: Birthdays
@@ -159,4 +162,4 @@ I use the birthdays as a simple entity list in lovelace, given the above example
159162
- birthdays.frodo_baggins
160163
- birthdays.bilbo_baggins
161164
- birthdays.elvis
162-
~~~
165+
```

custom_components/birthdays/__init__.py

Lines changed: 37 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,20 @@
1313

1414
_LOGGER = logging.getLogger(__name__)
1515

16+
CONF_UNIQUE_ID = 'unique_id'
1617
CONF_NAME = 'name'
1718
CONF_DATE_OF_BIRTH = 'date_of_birth'
1819
CONF_ICON = 'icon'
20+
CONF_ATTRIBUTES = 'attributes'
21+
CONF_AGE_AT_NEXT_BIRTHDAY = 'age_at_next_birthday'
1922
DOMAIN = 'birthdays'
2023

2124
BIRTHDAY_CONFIG_SCHEMA = vol.Schema({
22-
vol.Required(CONF_NAME): cv.string,
23-
vol.Required(CONF_DATE_OF_BIRTH): cv.date,
24-
vol.Optional(CONF_ICON, default='mdi:cake'): cv.string,
25+
vol.Optional(CONF_UNIQUE_ID): cv.string,
26+
vol.Required(CONF_NAME) : cv.string,
27+
vol.Required(CONF_DATE_OF_BIRTH) : cv.date,
28+
vol.Optional(CONF_ICON, default = 'mdi:cake'): cv.string,
29+
vol.Optional(CONF_ATTRIBUTES, default = {}) : vol.Schema({cv.string: cv.string}),
2530
})
2631

2732
CONFIG_SCHEMA = vol.Schema({
@@ -33,10 +38,12 @@ async def async_setup(hass, config):
3338
devices = []
3439

3540
for birthday_data in config[DOMAIN]:
41+
unique_id = birthday_data.get(CONF_UNIQUE_ID)
3642
name = birthday_data[CONF_NAME]
3743
date_of_birth = birthday_data[CONF_DATE_OF_BIRTH]
3844
icon = birthday_data[CONF_ICON]
39-
devices.append(BirthdayEntity(name, date_of_birth, icon, hass))
45+
attributes = birthday_data[CONF_ATTRIBUTES]
46+
devices.append(BirthdayEntity(unique_id, name, date_of_birth, icon, attributes, hass))
4047

4148
component = EntityComponent(_LOGGER, DOMAIN, hass)
4249
await component.async_add_entities(devices)
@@ -45,28 +52,41 @@ async def async_setup(hass, config):
4552
tasks = [asyncio.create_task(device.update_data()) for device in devices]
4653
await asyncio.wait(tasks)
4754

55+
_LOGGER.debug(devices)
56+
4857
return True
4958

5059

5160
class BirthdayEntity(Entity):
5261

53-
def __init__(self, name, date_of_birth, icon, hass):
62+
def __init__(self, unique_id, name, date_of_birth, icon, attributes, hass):
5463
self._name = name
55-
self._date_of_birth = date_of_birth
56-
self._icon = icon
57-
self._age_at_next_birthday = 0
64+
65+
if unique_id is not None:
66+
self._unique_id = slugify(unique_id)
67+
else:
68+
self._unique_id = slugify(name)
69+
5870
self._state = None
59-
name_in_entity_id = slugify(name)
60-
self.entity_id = 'birthday.{}'.format(name_in_entity_id)
71+
self._icon = icon
72+
self._date_of_birth = date_of_birth
6173
self.hass = hass
6274

75+
self._extra_state_attributes = {
76+
CONF_DATE_OF_BIRTH: str(self._date_of_birth),
77+
}
78+
79+
if len(attributes) > 0 and attributes is not None:
80+
for k,v in attributes.items():
81+
self._extra_state_attributes[k] = v
82+
6383
@property
6484
def name(self):
6585
return self._name
6686

6787
@property
6888
def unique_id(self):
69-
return '{}.{}'.format(self.entity_id, slugify(self._date_of_birth.strftime("%Y%m%d")))
89+
return self._unique_id
7090

7191
@property
7292
def state(self):
@@ -83,10 +103,7 @@ def icon(self):
83103

84104
@property
85105
def extra_state_attributes(self):
86-
return {
87-
CONF_DATE_OF_BIRTH: str(self._date_of_birth),
88-
'age_at_next_birthday': self._age_at_next_birthday,
89-
}
106+
return self._extra_state_attributes
90107

91108
@property
92109
def unit_of_measurement(self):
@@ -115,12 +132,15 @@ async def update_data(self, *_):
115132

116133
days_until_next_birthday = (next_birthday-today).days
117134

118-
self._age_at_next_birthday = next_birthday.year - self._date_of_birth.year
135+
age = next_birthday.year - self._date_of_birth.year
136+
self._extra_state_attributes[CONF_AGE_AT_NEXT_BIRTHDAY] = age
137+
138+
119139
self._state = days_until_next_birthday
120140

121141
if days_until_next_birthday == 0:
122142
# Fire event if birthday is today
123-
self.hass.bus.async_fire(event_type='birthday', event_data={'name': self._name, 'age': self._age_at_next_birthday})
143+
self.hass.bus.async_fire(event_type='birthday', event_data={'name': self._name, 'age': age})
124144

125145
self.async_write_ha_state()
126146
async_call_later(self.hass, self._get_seconds_until_midnight(), self.update_data)

0 commit comments

Comments
 (0)