mirror of
https://github.com/home-assistant/core.git
synced 2025-04-26 18:27:51 +00:00
DeviceTracker can now reload tracked devices without restart
This commit is contained in:
parent
f07622e0d7
commit
05e68c3e1e
@ -239,6 +239,19 @@ class StateMachine(object):
|
|||||||
""" List of categories which states are being tracked. """
|
""" List of categories which states are being tracked. """
|
||||||
return self.states.keys()
|
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):
|
def set_state(self, category, new_state, attributes=None):
|
||||||
""" Set the state of a category, add category if it does not exist.
|
""" 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):
|
def get_state(self, category):
|
||||||
""" Returns a dict (state,last_changed, attributes) describing
|
""" Returns a dict (state,last_changed, attributes) describing
|
||||||
the state of the specified category. """
|
the state of the specified category. """
|
||||||
if category not in self.states:
|
try:
|
||||||
return None
|
|
||||||
else:
|
|
||||||
# Make a copy so people won't accidently mutate the state
|
# Make a copy so people won't accidently mutate the state
|
||||||
return dict(self.states[category])
|
return dict(self.states[category])
|
||||||
|
|
||||||
|
except KeyError:
|
||||||
|
# If category does not exist
|
||||||
|
return None
|
||||||
|
|
||||||
def is_state(self, category, state):
|
def is_state(self, category, state):
|
||||||
""" Returns True if category exists and is specified state. """
|
""" Returns True if category exists and is specified state. """
|
||||||
cur_state = self.get_state(category)
|
cur_state = self.get_state(category)
|
||||||
|
@ -19,6 +19,8 @@ import requests
|
|||||||
|
|
||||||
import homeassistant as ha
|
import homeassistant as ha
|
||||||
|
|
||||||
|
EVENT_DEVICE_TRACKER_RELOAD = "device_tracker.reload_devices_csv"
|
||||||
|
|
||||||
STATE_CATEGORY_SUN = "weather.sun"
|
STATE_CATEGORY_SUN = "weather.sun"
|
||||||
STATE_ATTRIBUTE_NEXT_SUN_RISING = "next_rising"
|
STATE_ATTRIBUTE_NEXT_SUN_RISING = "next_rising"
|
||||||
STATE_ATTRIBUTE_NEXT_SUN_SETTING = "next_setting"
|
STATE_ATTRIBUTE_NEXT_SUN_SETTING = "next_setting"
|
||||||
@ -109,74 +111,23 @@ class DeviceTracker(object):
|
|||||||
# Did we encounter a valid known devices file
|
# Did we encounter a valid known devices file
|
||||||
self.invalid_known_devices_file = False
|
self.invalid_known_devices_file = False
|
||||||
|
|
||||||
# Read known devices if file exists
|
self._read_known_devices_file()
|
||||||
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))
|
|
||||||
|
|
||||||
ha.track_time_change(eventbus,
|
ha.track_time_change(eventbus,
|
||||||
lambda time:
|
lambda time:
|
||||||
self.update_devices(
|
self.update_devices(
|
||||||
device_scanner.scan_devices()))
|
device_scanner.scan_devices()))
|
||||||
|
|
||||||
|
eventbus.listen(EVENT_DEVICE_TRACKER_RELOAD,
|
||||||
|
lambda event: self._read_known_devices_file())
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def device_state_categories(self):
|
def device_state_categories(self):
|
||||||
""" Returns a list containing all categories
|
""" Returns a set containing all categories
|
||||||
that are maintained for devices. """
|
that are maintained for devices. """
|
||||||
return [self.known_devices[device]['category'] for device
|
return set([self.known_devices[device]['category'] for device
|
||||||
in self.known_devices if self.known_devices[device]['track']]
|
in self.known_devices
|
||||||
|
if self.known_devices[device]['track']])
|
||||||
|
|
||||||
def update_devices(self, found_devices):
|
def update_devices(self, found_devices):
|
||||||
""" Update device states based on the found devices. """
|
""" Update device states based on the found devices. """
|
||||||
@ -261,6 +212,92 @@ class DeviceTracker(object):
|
|||||||
|
|
||||||
self.lock.release()
|
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):
|
class TomatoDeviceScanner(object):
|
||||||
""" This class queries a wireless router running Tomato firmware
|
""" This class queries a wireless router running Tomato firmware
|
||||||
|
Loading…
x
Reference in New Issue
Block a user