Some code cleanup

This commit is contained in:
Paulus Schoutsen 2014-01-04 13:48:17 -08:00
parent 328b9a84c0
commit 367433acb2
6 changed files with 117 additions and 114 deletions

View file

@ -47,13 +47,10 @@ def start_home_assistant(bus):
bus.fire_event(EVENT_HOMEASSISTANT_START) bus.fire_event(EVENT_HOMEASSISTANT_START)
while True: while not request_shutdown.isSet():
try: try:
time.sleep(1) time.sleep(1)
if request_shutdown.isSet():
break
except KeyboardInterrupt: except KeyboardInterrupt:
break break
@ -356,10 +353,10 @@ class StateMachine(object):
self.lock.release() self.lock.release()
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. """
try: try:
# Make a copy so people won't accidently mutate the state # Make a copy so people won't mutate the state
return dict(self.states[category]) return dict(self.states[category])
except KeyError: except KeyError:
@ -393,16 +390,19 @@ class Timer(threading.Thread):
last_fired_on_second = -1 last_fired_on_second = -1
while True: while True:
# Sleep till it is the next time that we have to fire an event.
# Aim for halfway through the second that matches TIMER_INTERVAL.
# So if TIMER_INTERVAL is 10 fire at .5, 10.5, 20.5, etc seconds.
# This will yield the best results because time.sleep() is not
# 100% accurate because of non-realtime OS's
now = datetime.now() now = datetime.now()
if now.second % TIMER_INTERVAL > 0 or \ # First check checks if we are not on a second matching the
# timer interval. Second check checks if we did not already fire
# this interval.
if now.second % TIMER_INTERVAL or \
now.second == last_fired_on_second: now.second == last_fired_on_second:
# Sleep till it is the next time that we have to fire an event.
# Aim for halfway through the second that fits TIMER_INTERVAL.
# If TIMER_INTERVAL is 10 fire at .5, 10.5, 20.5, etc seconds.
# This will yield the best results because time.sleep() is not
# 100% accurate because of non-realtime OS's
slp_seconds = TIMER_INTERVAL - now.second % TIMER_INTERVAL + \ slp_seconds = TIMER_INTERVAL - now.second % TIMER_INTERVAL + \
.5 - now.microsecond/1000000.0 .5 - now.microsecond/1000000.0

View file

@ -12,11 +12,13 @@ from homeassistant.components import (general, chromecast,
browser, httpinterface) browser, httpinterface)
# pylint: disable=too-many-branches # pylint: disable=too-many-branches,too-many-locals,too-many-statements
def from_config_file(config_path): def from_config_file(config_path):
""" Starts home assistant with all possible functionality """ Starts home assistant with all possible functionality
based on a config file. """ based on a config file. """
logger = logging.getLogger(__name__)
statusses = [] statusses = []
# Read config # Read config
@ -27,104 +29,113 @@ def from_config_file(config_path):
bus = ha.Bus() bus = ha.Bus()
statemachine = ha.StateMachine(bus) statemachine = ha.StateMachine(bus)
has_opt = config.has_option
get_opt = config.get
has_section = config.has_section
add_status = lambda name, result: statusses.append((name, result))
# Device scanner # Device scanner
if config.has_option('tomato', 'host') and \ dev_scan = None
config.has_option('tomato', 'username') and \
config.has_option('tomato', 'password') and \
config.has_option('tomato', 'http_id'):
device_scanner = device.TomatoDeviceScanner( try:
config.get('tomato', 'host'), # For the error message if not all option fields exist
config.get('tomato', 'username'), opt_fields = "host, username, password"
config.get('tomato', 'password'),
config.get('tomato', 'http_id'))
statusses.append(("Device Scanner - Tomato", if has_section('tomato'):
device_scanner.success_init)) dev_scan_name = "Tomato"
opt_fields += ", http_id"
elif config.has_option('netgear', 'host') and \ dev_scan = device.TomatoDeviceScanner(
config.has_option('netgear', 'username') and \ get_opt('tomato', 'host'),
config.has_option('netgear', 'password'): get_opt('tomato', 'username'),
get_opt('tomato', 'password'),
get_opt('tomato', 'http_id'))
device_scanner = device.NetgearDeviceScanner( elif has_section('netgear'):
config.get('netgear', 'host'), dev_scan_name = "Netgear"
config.get('netgear', 'username'),
config.get('netgear', 'password'))
statusses.append(("Device Scanner - Netgear", dev_scan = device.NetgearDeviceScanner(
device_scanner.success_init)) get_opt('netgear', 'host'),
get_opt('netgear', 'username'),
get_opt('netgear', 'password'))
else: except ConfigParser.NoOptionError:
device_scanner = None # If one of the options didn't exist
logger.exception(("Error initializing {}DeviceScanner, "
"could not find one of the following config "
"options: {}".format(dev_scan_name, opt_fields)))
if device_scanner and not device_scanner.success_init: add_status("Device Scanner - {}".format(dev_scan_name), False)
device_scanner = None
if dev_scan:
add_status("Device Scanner - {}".format(dev_scan_name),
dev_scan.success_init)
if not dev_scan.success_init:
dev_scan = None
# Device Tracker # Device Tracker
if device_scanner: if dev_scan:
device.DeviceTracker(bus, statemachine, device_scanner) device.DeviceTracker(bus, statemachine, dev_scan)
statusses.append(("Device Tracker", True)) add_status("Device Tracker", True)
# Sun tracker # Sun tracker
if config.has_option("common", "latitude") and \ if has_opt("common", "latitude") and \
config.has_option("common", "longitude"): has_opt("common", "longitude"):
statusses.append(("Weather - Ephem", add_status("Weather - Ephem",
sun.setup( sun.setup(
bus, statemachine, bus, statemachine,
config.get("common", "latitude"), get_opt("common", "latitude"),
config.get("common", "longitude")))) get_opt("common", "longitude")))
# Chromecast # Chromecast
if config.has_option("chromecast", "host"): if has_opt("chromecast", "host"):
chromecast_started = chromecast.setup(bus, statemachine, chromecast_started = chromecast.setup(bus, statemachine,
config.get("chromecast", "host")) get_opt("chromecast", "host"))
statusses.append(("Chromecast", chromecast_started)) add_status("Chromecast", chromecast_started)
else: else:
chromecast_started = False chromecast_started = False
# Light control # Light control
if config.has_section("hue"): if has_section("hue"):
if config.has_option("hue", "host"): if has_opt("hue", "host"):
light_control = light.HueLightControl(config.get("hue", "host")) light_control = light.HueLightControl(get_opt("hue", "host"))
else: else:
light_control = light.HueLightControl() light_control = light.HueLightControl()
statusses.append(("Light Control - Hue", light_control.success_init)) add_status("Light Control - Hue", light_control.success_init)
light.setup(bus, statemachine, light_control)
else: else:
light_control = None light_control = None
# Light trigger # Light trigger
if light_control: if light_control:
light.setup(bus, statemachine, light_control) add_status("Light Trigger",
device_sun_light_trigger.setup(bus, statemachine))
statusses.append(("Light Trigger", device_sun_light_trigger.setup( if has_opt("downloader", "download_dir"):
bus, statemachine))) add_status("Downloader", downloader.setup(
bus, get_opt("downloader", "download_dir")))
if config.has_option("downloader", "download_dir"):
statusses.append(("Downloader", downloader.setup(
bus, config.get("downloader", "download_dir"))))
# Currently only works with Chromecast or Light_Control # Currently only works with Chromecast or Light_Control
if chromecast_started or light_control: if chromecast_started or light_control:
statusses.append(("General", general.setup(bus, statemachine))) add_status("General", general.setup(bus, statemachine))
statusses.append(("Browser", browser.setup(bus))) add_status("Browser", browser.setup(bus))
statusses.append(("Media Buttons", keyboard.setup(bus))) add_status("Media Buttons", keyboard.setup(bus))
# Init HTTP interface # Init HTTP interface
if config.has_option("httpinterface", "api_password"): if has_opt("httpinterface", "api_password"):
httpinterface.HTTPInterface( httpinterface.HTTPInterface(
bus, statemachine, bus, statemachine,
config.get("httpinterface", "api_password")) get_opt("httpinterface", "api_password"))
statusses.append(("HTTPInterface", True)) add_status("HTTPInterface", True)
logger = logging.getLogger(__name__)
for component, success_init in statusses: for component, success_init in statusses:
status = "initialized" if success_init else "Failed to initialize" status = "initialized" if success_init else "Failed to initialize"

View file

@ -94,7 +94,7 @@ def setup(bus, statemachine, host):
ATTR_STATE: status.state, ATTR_STATE: status.state,
ATTR_OPTIONS: status.options}) ATTR_OPTIONS: status.options})
else: else:
statemachine.set_state(category, STATE_NO_APP) statemachine.set_state(category, STATE_NO_APP, {ATTR_HOST: host})
ha.track_time_change(bus, update_chromecast_state) ha.track_time_change(bus, update_chromecast_state)

View file

@ -64,10 +64,13 @@ def is_home(statemachine, device_id=None):
class DeviceTracker(object): class DeviceTracker(object):
""" Class that tracks which devices are home and which are not. """ """ Class that tracks which devices are home and which are not. """
def __init__(self, bus, statemachine, device_scanner): def __init__(self, bus, statemachine, device_scanner, error_scanning=None):
self.statemachine = statemachine self.statemachine = statemachine
self.bus = bus self.bus = bus
self.device_scanner = device_scanner self.device_scanner = device_scanner
self.error_scanning = error_scanning or TIME_SPAN_FOR_ERROR_IN_SCANNING
self.logger = logging.getLogger(__name__) self.logger = logging.getLogger(__name__)
self.lock = threading.Lock() self.lock = threading.Lock()
@ -75,7 +78,7 @@ class DeviceTracker(object):
# Dictionary to keep track of known devices and devices we track # Dictionary to keep track of known devices and devices we track
self.known_devices = {} self.known_devices = {}
# Did we encounter a valid known devices file # Did we encounter an invalid known devices file
self.invalid_known_devices_file = False self.invalid_known_devices_file = False
self._read_known_devices_file() self._read_known_devices_file()
@ -124,7 +127,7 @@ class DeviceTracker(object):
# not show up for 1 scan beacuse of reboot etc # not show up for 1 scan beacuse of reboot etc
for device in temp_tracking_devices: for device in temp_tracking_devices:
if (now - self.known_devices[device]['last_seen'] > if (now - self.known_devices[device]['last_seen'] >
TIME_SPAN_FOR_ERROR_IN_SCANNING): self.error_scanning):
self.statemachine.set_state( self.statemachine.set_state(
self.known_devices[device]['category'], self.known_devices[device]['category'],
@ -149,7 +152,7 @@ class DeviceTracker(object):
unknown_devices = [device for device in found_devices unknown_devices = [device for device in found_devices
if device not in self.known_devices] if device not in self.known_devices]
if len(unknown_devices) > 0: if unknown_devices:
try: try:
# If file does not exist we will write the header too # If file does not exist we will write the header too
is_new_file = not os.path.isfile(KNOWN_DEVICES_FILE) is_new_file = not os.path.isfile(KNOWN_DEVICES_FILE)
@ -215,26 +218,23 @@ class DeviceTracker(object):
name = util.slugify(row['name']) if row['name'] \ name = util.slugify(row['name']) if row['name'] \
else "unnamed_device" else "unnamed_device"
tries = 0 category = STATE_CATEGORY_FORMAT.format(name)
suffix = "" tries = 1
while True:
while category in used_categories:
tries += 1 tries += 1
if tries > 1: suffix = "_{}".format(tries)
suffix = "_{}".format(tries)
category = STATE_CATEGORY_FORMAT.format( category = STATE_CATEGORY_FORMAT.format(
name + suffix) name + suffix)
if category not in used_categories:
break
row['category'] = category row['category'] = category
used_categories.append(category) used_categories.append(category)
known_devices[device] = row known_devices[device] = row
if len(known_devices) == 0: if not known_devices:
self.logger.warning( self.logger.warning(
"No devices to track. Please update {}.".format( "No devices to track. Please update {}.".format(
KNOWN_DEVICES_FILE)) KNOWN_DEVICES_FILE))
@ -247,7 +247,9 @@ class DeviceTracker(object):
for category in \ for category in \
self.device_state_categories - new_categories: self.device_state_categories - new_categories:
print "Removing ", category self.logger.info(
"DeviceTracker:Removing category {}".format(
category))
self.statemachine.remove_category(category) self.statemachine.remove_category(category)
# File parsed, warnings given if necessary # File parsed, warnings given if necessary
@ -313,7 +315,7 @@ class TomatoDeviceScanner(object):
filter_named = [item[0] for item in self.last_results['dhcpd_lease'] filter_named = [item[0] for item in self.last_results['dhcpd_lease']
if item[2] == device] if item[2] == device]
if len(filter_named) == 0 or filter_named[0] == "": if not filter_named or not filter_named[0]:
return None return None
else: else:
return filter_named[0] return filter_named[0]
@ -399,7 +401,6 @@ class NetgearDeviceScanner(object):
def __init__(self, host, username, password): def __init__(self, host, username, password):
self._api = pynetgear.Netgear(host, username, password) self._api = pynetgear.Netgear(host, username, password)
self.logger = logging.getLogger(__name__) self.logger = logging.getLogger(__name__)
self.lock = threading.Lock() self.lock = threading.Lock()
@ -426,32 +427,26 @@ class NetgearDeviceScanner(object):
filter_named = [device.name for device in self.last_results filter_named = [device.name for device in self.last_results
if device.mac == mac] if device.mac == mac]
if len(filter_named) == 0: if filter_named:
return None
else:
return filter_named[0] return filter_named[0]
else:
return None
def _update_info(self): def _update_info(self):
""" Retrieves latest information from the Netgear router. """ Retrieves latest information from the Netgear router.
Returns boolean if scanning successful. """ Returns boolean if scanning successful. """
self.lock.acquire() with self.lock:
# if date_updated is None or the date is too old we scan for
# new data
if (not self.date_updated or datetime.now() - self.date_updated >
MIN_TIME_BETWEEN_SCANS):
# if date_updated is None or the date is too old we scan for new data self.logger.info("Netgear:Scanning")
if (not self.date_updated or datetime.now() - self.date_updated >
MIN_TIME_BETWEEN_SCANS):
self.logger.info("Netgear:Scanning") self.last_results = self._api.get_attached_devices()
self.last_results = self._api.get_attached_devices() return True
self.lock.release() else:
return True
return True
else:
# We acquired the lock before the IF check,
# release it before we return True
self.lock.release()
return True

View file

@ -59,15 +59,12 @@ def setup(bus, download_path):
filename)) filename))
# If file exist append a number. We test filename, filename_2.. # If file exist append a number. We test filename, filename_2..
tries = 0 tries = 1
while True: final_path = path + ext
while os.path.isfile(final_path):
tries += 1 tries += 1
name_suffix = "" if tries == 1 else "_{}".format(tries) final_path = path + "_{}".format(tries) + ext
final_path = path + name_suffix + ext
if not os.path.isfile(final_path):
break
logger.info("FileDownloader:{} -> {}".format( logger.info("FileDownloader:{} -> {}".format(
service.data['url'], final_path)) service.data['url'], final_path))

View file

@ -145,8 +145,8 @@ class HueLightControl(object):
def is_light_on(self, light_id=None): def is_light_on(self, light_id=None):
""" Returns if specified or all light are on. """ """ Returns if specified or all light are on. """
if not light_id: if not light_id:
return sum( return any(
[1 for light in self._light_map.values() if light.on]) > 0 [True for light in self._light_map.values() if light.on])
else: else:
return self._bridge.get_light(self._convert_id(light_id), 'on') return self._bridge.get_light(self._convert_id(light_id), 'on')