2828
2929_LOGGER = logging .getLogger (__name__ )
3030
31- __version__ = '1.10.0 '
31+ __version__ = '1.10.1 '
3232
3333CONF_TIME_AS = 'time_as'
3434CONF_REQ_MOVEMENT = 'require_movement'
4646ATTR_TIME_ZONE = 'time_zone'
4747
4848WARNED = 'warned'
49+ SEEN = 'seen'
4950SOURCE_TYPE = ATTR_SOURCE_TYPE
50- STATE = ATTR_STATE
51+ DATA = 'data'
5152
5253SOURCE_TYPE_BINARY_SENSOR = BS_DOMAIN
5354STATE_BINARY_SENSOR_HOME = STATE_ON
@@ -79,8 +80,9 @@ def __init__(self, hass, config, see):
7980 for entity_id in entities :
8081 self ._entities [entity_id ] = {
8182 WARNED : False ,
83+ SEEN : None ,
8284 SOURCE_TYPE : None ,
83- STATE : None }
85+ DATA : None }
8486 self ._dev_id = config [CONF_NAME ]
8587 self ._entity_id = ENTITY_ID_FORMAT .format (self ._dev_id )
8688 self ._time_as = config [CONF_TIME_AS ]
@@ -90,15 +92,20 @@ def __init__(self, hass, config, see):
9092 self ._req_movement = config [CONF_REQ_MOVEMENT ]
9193 self ._lock = threading .Lock ()
9294 self ._prev_seen = None
95+ self ._init_complete = False
9396
9497 self ._remove = track_state_change (
9598 hass , entities , self ._update_info )
9699
97100 for entity_id in entities :
98- self ._update_info (entity_id , None , hass .states .get (entity_id ),
99- init = True )
101+ self ._update_info (entity_id , None , hass .states .get (entity_id ))
100102
101- def _bad_entity (self , entity_id , message , init ):
103+ def init_complete (event ):
104+ self ._init_complete = True
105+
106+ hass .bus .listen_once (EVENT_HOMEASSISTANT_START , init_complete )
107+
108+ def _bad_entity (self , entity_id , message ):
102109 msg = '{} {}' .format (entity_id , message )
103110 # Has there already been a warning for this entity?
104111 if self ._entities [entity_id ][WARNED ]:
@@ -109,16 +116,19 @@ def _bad_entity(self, entity_id, message, init):
109116 if len (self ._entities ):
110117 self ._remove = track_state_change (
111118 self ._hass , self ._entities .keys (), self ._update_info )
112- else :
119+ # Don't warn during init.
120+ elif self ._init_complete :
113121 _LOGGER .warning (msg )
114- # Don't count warnings during init.
115- self ._entities [entity_id ][WARNED ] = not init
122+ self ._entities [entity_id ][WARNED ] = True
123+ else :
124+ _LOGGER .debug (msg )
116125
117- def _good_entity (self , entity_id , source_type , state ):
126+ def _good_entity (self , entity_id , seen , source_type , data ):
118127 self ._entities [entity_id ].update ({
119128 WARNED : False ,
129+ SEEN : seen ,
120130 SOURCE_TYPE : source_type ,
121- STATE : state })
131+ DATA : data })
122132
123133 def _use_non_gps_data (self , state ):
124134 if state == STATE_HOME :
@@ -127,7 +137,7 @@ def _use_non_gps_data(self, state):
127137 if any (entity [SOURCE_TYPE ] == SOURCE_TYPE_GPS
128138 for entity in entities ):
129139 return False
130- return all (entity [STATE ] != STATE_HOME
140+ return all (entity [DATA ] != STATE_HOME
131141 for entity in entities
132142 if entity [SOURCE_TYPE ] in SOURCE_TYPE_NON_GPS )
133143
@@ -138,7 +148,7 @@ def _dt_attr_from_utc(self, utc, tz):
138148 return dt_util .as_local (utc )
139149 return utc
140150
141- def _update_info (self , entity_id , old_state , new_state , init = False ):
151+ def _update_info (self , entity_id , old_state , new_state ):
142152 if new_state is None :
143153 return
144154
@@ -157,13 +167,9 @@ def _update_info(self, entity_id, old_state, new_state, init=False):
157167 except (TypeError , ValueError ):
158168 last_seen = new_state .last_updated
159169
160- # Is this newer info than last update?
161- if self ._prev_seen and last_seen <= self ._prev_seen :
162- _LOGGER .debug (
163- 'For {} skipping update from {}: '
164- 'last_seen not newer than previous update ({} <= {})'
165- .format (self ._entity_id , entity_id , last_seen ,
166- self ._prev_seen ))
170+ old_last_seen = self ._entities [entity_id ][SEEN ]
171+ if old_last_seen and last_seen < old_last_seen :
172+ self ._bad_entity (entity_id , 'last_seen went backwards' )
167173 return
168174
169175 # Try to get GPS and battery data.
@@ -191,30 +197,29 @@ def _update_info(self, entity_id, old_state, new_state, init=False):
191197 if source_type == SOURCE_TYPE_GPS :
192198 # GPS coordinates and accuracy are required.
193199 if gps is None :
194- self ._bad_entity (entity_id ,
195- 'missing gps attributes' , init )
200+ self ._bad_entity (entity_id , 'missing gps attributes' )
196201 return
197202 if gps_accuracy is None :
198203 self ._bad_entity (entity_id ,
199- 'missing gps_accuracy attribute' , init )
204+ 'missing gps_accuracy attribute' )
200205 return
201- if self ._req_movement and old_state is not None :
202- try :
203- old_lat = old_state .attributes [ATTR_LATITUDE ]
204- old_lon = old_state .attributes [ATTR_LONGITUDE ]
205- old_acc = old_state .attributes [ATTR_GPS_ACCURACY ]
206- except KeyError :
207- self ._bad_entity (entity_id ,
208- 'old_state missing gps data' , init )
206+
207+ new_data = gps , gps_accuracy
208+ old_data = self ._entities [entity_id ][DATA ]
209+ if old_data :
210+ if last_seen == old_last_seen and new_data == old_data :
209211 return
210- if (distance (gps [0 ], gps [1 ], old_lat , old_lon ) <=
212+ old_gps , old_acc = old_data
213+ self ._good_entity (entity_id , last_seen , source_type , new_data )
214+
215+ if (self ._req_movement and old_data and
216+ distance (gps [0 ], gps [1 ], old_gps [0 ], old_gps [1 ]) <=
211217 gps_accuracy + old_acc ):
212- _LOGGER .debug (
213- 'For {} skipping update from {}: '
214- 'not enough movement'
215- .format (self ._entity_id , entity_id ))
216- return
217- self ._good_entity (entity_id , SOURCE_TYPE_GPS , state )
218+ _LOGGER .debug (
219+ 'For {} skipping update from {}: '
220+ 'not enough movement'
221+ .format (self ._entity_id , entity_id ))
222+ return
218223
219224 elif source_type in SOURCE_TYPE_NON_GPS :
220225 # Convert 'on'/'off' state of binary_sensor
@@ -225,8 +230,8 @@ def _update_info(self, entity_id, old_state, new_state, init=False):
225230 else :
226231 state = STATE_NOT_HOME
227232
228- self ._good_entity (
229- entity_id , source_type , state )
233+ self ._good_entity (entity_id , last_seen , source_type , state )
234+
230235 if not self ._use_non_gps_data (state ):
231236 return
232237
@@ -274,8 +279,16 @@ def _update_info(self, entity_id, old_state, new_state, init=False):
274279 else :
275280 self ._bad_entity (
276281 entity_id ,
277- 'unsupported source_type: {}' .format (source_type ),
278- init )
282+ 'unsupported source_type: {}' .format (source_type ))
283+ return
284+
285+ # Is this newer info than last update?
286+ if self ._prev_seen and last_seen <= self ._prev_seen :
287+ _LOGGER .debug (
288+ 'For {} skipping update from {}: '
289+ 'last_seen not newer than previous update ({} <= {})'
290+ .format (self ._entity_id , entity_id , last_seen ,
291+ self ._prev_seen ))
279292 return
280293
281294 _LOGGER .debug ('Updating %s from %s' , self ._entity_id , entity_id )
0 commit comments