forked from home-assistant/core
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathentity.py
250 lines (184 loc) · 6.94 KB
/
entity.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
"""
homeassistant.helpers.entity.
Provides ABC for entities in HA.
"""
from collections import defaultdict
import re
from homeassistant.exceptions import NoEntitySpecifiedError
from homeassistant.util import ensure_unique_string, slugify
from homeassistant.const import (
ATTR_FRIENDLY_NAME, ATTR_HIDDEN, ATTR_UNIT_OF_MEASUREMENT, ATTR_ICON,
DEVICE_DEFAULT_NAME, STATE_ON, STATE_OFF, STATE_UNKNOWN, STATE_UNAVAILABLE,
TEMP_CELCIUS, TEMP_FAHRENHEIT, ATTR_ASSUMED_STATE)
# Dict mapping entity_id to a boolean that overwrites the hidden property
_OVERWRITE = defaultdict(dict)
# Pattern for validating entity IDs (format: <domain>.<entity>)
ENTITY_ID_PATTERN = re.compile(r"^(\w+)\.(\w+)$")
def generate_entity_id(entity_id_format, name, current_ids=None, hass=None):
"""Generate a unique entity ID based on given entity IDs or used ids."""
name = (name or DEVICE_DEFAULT_NAME).lower()
if current_ids is None:
if hass is None:
raise RuntimeError("Missing required parameter currentids or hass")
current_ids = hass.states.entity_ids()
return ensure_unique_string(
entity_id_format.format(slugify(name.lower())), current_ids)
def split_entity_id(entity_id):
"""Split a state entity_id into domain, object_id."""
return entity_id.split(".", 1)
def valid_entity_id(entity_id):
"""Test if an entity ID is a valid format."""
return ENTITY_ID_PATTERN.match(entity_id) is not None
class Entity(object):
"""ABC for Home Assistant entities."""
# pylint: disable=no-self-use
# SAFE TO OVERWRITE
# The properties and methods here are safe to overwrite when inherting this
# class. These may be used to customize the behavior of the entity.
@property
def should_poll(self):
"""
Return True if entity has to be polled for state.
False if entity pushes its state to HA.
"""
return True
@property
def unique_id(self):
"""Return a unique id."""
return "{}.{}".format(self.__class__, id(self))
@property
def name(self):
"""Return the name of the entity."""
return None
@property
def state(self):
"""Return the state of the entity."""
return STATE_UNKNOWN
@property
def state_attributes(self):
"""
Return the state attributes.
Implemented by component base class.
"""
return None
@property
def device_state_attributes(self):
"""
Return device specific state attributes.
Implemented by platform classes.
"""
return None
@property
def unit_of_measurement(self):
"""Return the unit of measurement of this entity, if any."""
return None
@property
def icon(self):
"""Return the icon to use in the frontend, if any."""
return None
@property
def hidden(self):
"""Return True if the entity should be hidden from UIs."""
return False
@property
def available(self):
"""Return True if entity is available."""
return True
@property
def assumed_state(self):
"""Return True if unable to access real state of entity."""
return False
def update(self):
"""Retrieve latest state."""
pass
entity_id = None
# DO NOT OVERWRITE
# These properties and methods are either managed by Home Assistant or they
# are used to perform a very specific function. Overwriting these may
# produce undesirable effects in the entity's operation.
hass = None
def update_ha_state(self, force_refresh=False):
"""
Update Home Assistant with current state of entity.
If force_refresh == True will update entity before setting state.
"""
if self.hass is None:
raise RuntimeError("Attribute hass is None for {}".format(self))
if self.entity_id is None:
raise NoEntitySpecifiedError(
"No entity id specified for entity {}".format(self.name))
if force_refresh:
self.update()
state = STATE_UNKNOWN if self.state is None else str(self.state)
attr = self.state_attributes or {}
device_attr = self.device_state_attributes
if device_attr is not None:
attr.update(device_attr)
if ATTR_UNIT_OF_MEASUREMENT not in attr and \
self.unit_of_measurement is not None:
attr[ATTR_UNIT_OF_MEASUREMENT] = str(self.unit_of_measurement)
if not self.available:
state = STATE_UNAVAILABLE
attr = {}
if self.name is not None:
attr[ATTR_FRIENDLY_NAME] = str(self.name)
if self.icon is not None:
attr[ATTR_ICON] = str(self.icon)
if self.hidden:
attr[ATTR_HIDDEN] = bool(self.hidden)
if self.assumed_state:
attr[ATTR_ASSUMED_STATE] = bool(self.assumed_state)
# overwrite properties that have been set in the config file
attr.update(_OVERWRITE.get(self.entity_id, {}))
# remove hidden property if false so it won't show up
if not attr.get(ATTR_HIDDEN, True):
attr.pop(ATTR_HIDDEN)
# Convert temperature if we detect one
if attr.get(ATTR_UNIT_OF_MEASUREMENT) in (TEMP_CELCIUS,
TEMP_FAHRENHEIT):
state, attr[ATTR_UNIT_OF_MEASUREMENT] = \
self.hass.config.temperature(
state, attr[ATTR_UNIT_OF_MEASUREMENT])
state = str(state)
return self.hass.states.set(self.entity_id, state, attr)
def __eq__(self, other):
return (isinstance(other, Entity) and
other.unique_id == self.unique_id)
def __repr__(self):
return "<Entity {}: {}>".format(self.name, self.state)
@staticmethod
def overwrite_attribute(entity_id, attrs, vals):
"""
Overwrite any attribute of an entity.
This function should receive a list of attributes and a
list of values. Set attribute to None to remove any overwritten
value in place.
"""
for attr, val in zip(attrs, vals):
if val is None:
_OVERWRITE[entity_id.lower()].pop(attr, None)
else:
_OVERWRITE[entity_id.lower()][attr] = val
class ToggleEntity(Entity):
"""ABC for entities that can be turned on and off."""
# pylint: disable=no-self-use
@property
def state(self):
"""Return the state."""
return STATE_ON if self.is_on else STATE_OFF
@property
def is_on(self):
"""True if entity is on."""
return False
def turn_on(self, **kwargs):
"""Turn the entity on."""
pass
def turn_off(self, **kwargs):
"""Turn the entity off."""
pass
def toggle(self, **kwargs):
"""Toggle the entity off."""
if self.is_on:
self.turn_off(**kwargs)
else:
self.turn_on(**kwargs)