DeviceTracker can now reload tracked devices without restart

This commit is contained in:
Paulus Schoutsen 2013-11-18 22:45:19 -08:00
parent f07622e0d7
commit 05e68c3e1e
2 changed files with 114 additions and 62 deletions

View File

@ -239,6 +239,19 @@ class StateMachine(object):
""" List of categories which states are being tracked. """
return self.states.keys()
def remove_category(self, category):
""" Removes a category from the state machine.
Returns boolean to indicate if a category was removed. """
try:
del self.states[category]
return True
except KeyError:
# if category does not exist
return False
def set_state(self, category, new_state, attributes=None):
""" Set the state of a category, add category if it does not exist.
@ -271,12 +284,14 @@ class StateMachine(object):
def get_state(self, category):
""" Returns a dict (state,last_changed, attributes) describing
the state of the specified category. """
if category not in self.states:
return None
else:
try:
# Make a copy so people won't accidently mutate the state
return dict(self.states[category])
except KeyError:
# If category does not exist
return None
def is_state(self, category, state):
""" Returns True if category exists and is specified state. """
cur_state = self.get_state(category)

View File

@ -19,6 +19,8 @@ import requests
import homeassistant as ha
EVENT_DEVICE_TRACKER_RELOAD = "device_tracker.reload_devices_csv"
STATE_CATEGORY_SUN = "weather.sun"
STATE_ATTRIBUTE_NEXT_SUN_RISING = "next_rising"
STATE_ATTRIBUTE_NEXT_SUN_SETTING = "next_setting"
@ -109,74 +111,23 @@ class DeviceTracker(object):
# Did we encounter a valid known devices file
self.invalid_known_devices_file = False
# Read known devices if file exists
if os.path.isfile(KNOWN_DEVICES_FILE):
with open(KNOWN_DEVICES_FILE) as inp:
default_last_seen = datetime(1990, 1, 1)
# Temp variable to keep track of which categories we use
# so we can ensure we have unique categories.
used_categories = []
try:
for row in csv.DictReader(inp):
device = row['device']
row['track'] = True if row['track'] == '1' else False
# If we track this device setup tracking variables
if row['track']:
row['last_seen'] = default_last_seen
# Make sure that each device is mapped
# to a unique category name
name = row['name']
if not name:
name = "unnamed_device"
tries = 0
suffix = ""
while True:
tries += 1
if tries > 1:
suffix = "_{}".format(tries)
category = STATE_CATEGORY_DEVICE_FORMAT.format(
name + suffix)
if category not in used_categories:
break
row['category'] = category
used_categories.append(category)
self.known_devices[device] = row
except KeyError:
self.invalid_known_devices_file = False
self.logger.warning((
"Invalid {} found. "
"We won't update it with new found devices.").
format(KNOWN_DEVICES_FILE))
if len(self.device_state_categories) == 0:
self.logger.warning(
"No devices to track. Please update {}.".format(
KNOWN_DEVICES_FILE))
self._read_known_devices_file()
ha.track_time_change(eventbus,
lambda time:
self.update_devices(
device_scanner.scan_devices()))
eventbus.listen(EVENT_DEVICE_TRACKER_RELOAD,
lambda event: self._read_known_devices_file())
@property
def device_state_categories(self):
""" Returns a list containing all categories
""" Returns a set containing all categories
that are maintained for devices. """
return [self.known_devices[device]['category'] for device
in self.known_devices if self.known_devices[device]['track']]
return set([self.known_devices[device]['category'] for device
in self.known_devices
if self.known_devices[device]['track']])
def update_devices(self, found_devices):
""" Update device states based on the found devices. """
@ -261,6 +212,92 @@ class DeviceTracker(object):
self.lock.release()
def _read_known_devices_file(self):
""" Parse and process the known devices file. """
# Read known devices if file exists
if os.path.isfile(KNOWN_DEVICES_FILE):
self.lock.acquire()
known_devices = {}
with open(KNOWN_DEVICES_FILE) as inp:
default_last_seen = datetime(1990, 1, 1)
# Temp variable to keep track of which categories we use
# so we can ensure we have unique categories.
used_categories = []
try:
for row in csv.DictReader(inp):
device = row['device']
row['track'] = True if row['track'] == '1' else False
# If we track this device setup tracking variables
if row['track']:
row['last_seen'] = default_last_seen
# Make sure that each device is mapped
# to a unique category name
name = row['name']
if not name:
name = "unnamed_device"
tries = 0
suffix = ""
while True:
tries += 1
if tries > 1:
suffix = "_{}".format(tries)
category = STATE_CATEGORY_DEVICE_FORMAT.format(
name + suffix)
if category not in used_categories:
break
row['category'] = category
used_categories.append(category)
known_devices[device] = row
if len(known_devices) == 0:
self.logger.warning(
"No devices to track. Please update {}.".format(
KNOWN_DEVICES_FILE))
# Remove categories that are no longer maintained
new_categories = set([known_devices[device]['category']
for device in known_devices
if known_devices[device]['track']])
for category in \
self.device_state_categories - new_categories:
print "Removing ", category
self.statemachine.remove_category(category)
# File parsed, warnings given if necessary
# categories cleaned up, make it available
self.known_devices = known_devices
self.logger.info(
"DeviceTracker:Loaded devices from {}".format(
KNOWN_DEVICES_FILE))
except KeyError:
self.invalid_known_devices_file = True
self.logger.warning((
"Invalid {} found. "
"We won't update it with new found devices.").
format(KNOWN_DEVICES_FILE))
finally:
self.lock.release()
class TomatoDeviceScanner(object):
""" This class queries a wireless router running Tomato firmware