1010from uuid import uuid4
1111from time import time
1212
13- from indoktrinator .db import with_session
1413from indoktrinator .manager .schema import schema
1514from indoktrinator .manager .scheduler import *
1615from indoktrinator .manager .store import *
@@ -37,6 +36,7 @@ def __init__(self, db, notifier, router, media_path, url,
3736 self .power_down_gap = int (power_down_gap )
3837
3938 # Collection of devices we have come into contact with.
39+ # Written only from the reactor thread; HTTP threads read via dict.copy().
4040 self .devices = {}
4141
4242 # Store for database objects received via notifications.
@@ -158,8 +158,9 @@ def on_program_change(self, uuid, program):
158158 else :
159159 log .msg ('Program {} deleted.' .format (uuid ))
160160
161- if uuid in self .plans :
162- self .plans .pop (uuid )
161+ str_uuid = str (uuid )
162+ if str_uuid in self .plans :
163+ del self .plans [str_uuid ]
163164
164165 self .sync_devices ()
165166
@@ -178,14 +179,18 @@ def update_plans(self):
178179
179180 def on_device_change (self , id , device ):
180181 """Device has changed in the database."""
181- self .sync_device (id )
182+ if device is None :
183+ # Device deleted — remove runtime state.
184+ self .devices .pop (id , None )
185+ else :
186+ self .sync_device (id )
182187
183188 def sync_devices (self ):
184189 """
185190 Synchronize all devices plans as needed.
186191 """
187192
188- for id in self .devices :
193+ for id in list ( self .devices ) :
189194 self .sync_device (id )
190195
191196 def sync_device (self , id ):
@@ -211,7 +216,12 @@ def sync_device(self, id):
211216 return
212217
213218 # Find plan for device program.
214- plan = self .plans [str (device ['program' ])]
219+ plan = self .plans .get (str (device ['program' ]))
220+
221+ if plan is None :
222+ # Plan not computed yet (e.g. during startup or after DB change).
223+ self .send (id , 'plan' , EMPTY_PLAN )
224+ return
215225
216226 # Check that device plan is up to date.
217227 if status ['plan' ] != plan ['id' ]:
0 commit comments