mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 21:27:38 +00:00
Cleaning up code
This commit is contained in:
parent
90c343d16b
commit
7636092c7d
@ -16,70 +16,64 @@ STATE_CATEGORY_ALL_DEVICES = 'device.alldevices'
|
|||||||
STATE_CATEGORY_DEVICE_FORMAT = 'device.{}'
|
STATE_CATEGORY_DEVICE_FORMAT = 'device.{}'
|
||||||
|
|
||||||
|
|
||||||
class DeviceTracker:
|
class DeviceTracker(object):
|
||||||
|
|
||||||
def __init__(self, eventbus, statemachine, device_scanner):
|
def __init__(self, eventbus, statemachine, device_scanner):
|
||||||
self.statemachine = statemachine
|
self.statemachine = statemachine
|
||||||
self.eventbus = eventbus
|
self.eventbus = eventbus
|
||||||
self.device_scanner = device_scanner
|
|
||||||
|
|
||||||
default_last_seen = datetime(1990, 1, 1)
|
default_last_seen = datetime(1990, 1, 1)
|
||||||
|
|
||||||
temp_devices_to_track = device_scanner.get_devices_to_track()
|
temp_devices_to_track = device_scanner.get_devices_to_track()
|
||||||
|
|
||||||
self.devices_to_track = { device: { 'name': temp_devices_to_track[device],
|
self.devices_to_track = { device: { 'name': temp_devices_to_track[device],
|
||||||
'last_seen': default_last_seen,
|
'last_seen': default_last_seen,
|
||||||
'category': STATE_CATEGORY_DEVICE_FORMAT.format(temp_devices_to_track[device]) }
|
'category': STATE_CATEGORY_DEVICE_FORMAT.format(temp_devices_to_track[device]) }
|
||||||
for device in temp_devices_to_track }
|
for device in temp_devices_to_track }
|
||||||
|
|
||||||
self.all_devices_state = STATE_DEVICE_DEFAULT
|
# Add categories to state machine
|
||||||
|
statemachine.add_category(STATE_CATEGORY_ALL_DEVICES, STATE_DEVICE_DEFAULT)
|
||||||
|
|
||||||
# Add categories to state machine
|
for device in self.devices_to_track:
|
||||||
statemachine.add_category(STATE_CATEGORY_ALL_DEVICES, STATE_DEVICE_DEFAULT)
|
self.statemachine.add_category(self.devices_to_track[device]['category'], STATE_DEVICE_DEFAULT)
|
||||||
|
|
||||||
for device in self.devices_to_track:
|
track_time_change(eventbus, lambda time: self.update_devices(device_scanner.scan_devices()))
|
||||||
self.statemachine.add_category(self.devices_to_track[device]['category'], STATE_DEVICE_DEFAULT)
|
|
||||||
|
|
||||||
track_time_change(eventbus, lambda time: self.update_devices(device_scanner.scan_devices()))
|
|
||||||
|
|
||||||
|
|
||||||
def device_state_categories(self):
|
def device_state_categories(self):
|
||||||
for device in self.devices_to_track:
|
for device in self.devices_to_track:
|
||||||
yield self.devices_to_track[device]['category']
|
yield self.devices_to_track[device]['category']
|
||||||
|
|
||||||
|
|
||||||
def set_state(self, device, state):
|
def set_state(self, device, state):
|
||||||
if state == STATE_DEVICE_HOME:
|
if state == STATE_DEVICE_HOME:
|
||||||
self.devices_to_track[device]['last_seen'] = datetime.now()
|
self.devices_to_track[device]['last_seen'] = datetime.now()
|
||||||
|
|
||||||
self.statemachine.set_state(self.devices_to_track[device]['category'], state)
|
self.statemachine.set_state(self.devices_to_track[device]['category'], state)
|
||||||
|
|
||||||
|
|
||||||
def update_devices(self, found_devices):
|
def update_devices(self, found_devices):
|
||||||
# Keep track of devices that are home, all that are not will be marked not home
|
# Keep track of devices that are home, all that are not will be marked not home
|
||||||
temp_tracking_devices = self.devices_to_track.keys()
|
temp_tracking_devices = self.devices_to_track.keys()
|
||||||
|
|
||||||
for device in found_devices:
|
for device in found_devices:
|
||||||
# Are we tracking this device?
|
# Are we tracking this device?
|
||||||
if device in temp_tracking_devices:
|
if device in temp_tracking_devices:
|
||||||
temp_tracking_devices.remove(device)
|
temp_tracking_devices.remove(device)
|
||||||
|
|
||||||
self.set_state(device, STATE_DEVICE_HOME)
|
self.set_state(device, STATE_DEVICE_HOME)
|
||||||
|
|
||||||
# For all devices we did not find, set state to NH
|
# For all devices we did not find, set state to NH
|
||||||
# But only if they have been gone for longer then the error time span
|
# But only if they have been gone for longer then the error time span
|
||||||
# Because we do not want to have stuff happening when the device does
|
# Because we do not want to have stuff happening when the device does
|
||||||
# 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 self.statemachine.get_state(self.devices_to_track[device]['category']).state == STATE_DEVICE_HOME and \
|
if datetime.now() - self.devices_to_track[device]['last_seen'] > TIME_SPAN_FOR_ERROR_IN_SCANNING:
|
||||||
datetime.now() - self.devices_to_track[device]['last_seen'] > TIME_SPAN_FOR_ERROR_IN_SCANNING:
|
self.set_state(device, STATE_DEVICE_NOT_HOME)
|
||||||
|
|
||||||
self.set_state(device, STATE_DEVICE_NOT_HOME)
|
# Get the set of currently used statuses
|
||||||
|
states_of_devices = [self.statemachine.get_state(self.devices_to_track[device]['category']).state for device in self.devices_to_track]
|
||||||
|
|
||||||
|
all_devices_state = STATE_DEVICE_HOME if STATE_DEVICE_HOME in states_of_devices else STATE_DEVICE_NOT_HOME
|
||||||
|
|
||||||
# Get the set of currently used statuses
|
self.statemachine.set_state(STATE_CATEGORY_ALL_DEVICES, all_devices_state)
|
||||||
states_of_devices = [self.statemachine.get_state(self.devices_to_track[device]['category']).state for device in self.devices_to_track]
|
|
||||||
|
|
||||||
self.all_devices_state = STATE_DEVICE_HOME if STATE_DEVICE_HOME in states_of_devices else STATE_DEVICE_NOT_HOME
|
|
||||||
|
|
||||||
self.statemachine.set_state(STATE_CATEGORY_ALL_DEVICES, self.all_devices_state)
|
|
||||||
|
@ -6,59 +6,58 @@ from threading import Thread, RLock
|
|||||||
|
|
||||||
ALL_EVENTS = '*'
|
ALL_EVENTS = '*'
|
||||||
|
|
||||||
class EventBus:
|
class EventBus(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.listeners = defaultdict(list)
|
self.listeners = defaultdict(list)
|
||||||
self.lock = RLock()
|
self.lock = RLock()
|
||||||
self.logger =logging.getLogger(__name__)
|
self.logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
def fire(self, event):
|
|
||||||
assert isinstance(event, Event), "event needs to be an instance of Event"
|
|
||||||
|
|
||||||
# We dont want the eventbus to be blocking,
|
def fire(self, event):
|
||||||
# We dont want the eventbus to crash when one of its listeners throws an Exception
|
assert isinstance(event, Event), "event needs to be an instance of Event"
|
||||||
# So run in a thread
|
|
||||||
def run():
|
|
||||||
self.lock.acquire()
|
|
||||||
|
|
||||||
self.logger.info("{} event received: {}".format(event.eventType, event.data))
|
# We dont want the eventbus to be blocking,
|
||||||
|
# We dont want the eventbus to crash when one of its listeners throws an Exception
|
||||||
|
# So run in a thread
|
||||||
|
def run():
|
||||||
|
self.lock.acquire()
|
||||||
|
|
||||||
for callback in chain(self.listeners[ALL_EVENTS], self.listeners[event.eventType]):
|
self.logger.info("{} event received: {}".format(event.event_type, event.data))
|
||||||
callback(event)
|
|
||||||
|
|
||||||
if event.removeListener:
|
for callback in chain(self.listeners[ALL_EVENTS], self.listeners[event.event_type]):
|
||||||
if callback in self.listeners[ALL_EVENTS]:
|
callback(event)
|
||||||
self.listeners[ALL_EVENTS].remove(callback)
|
|
||||||
|
|
||||||
if callback in self.listeners[event.eventType]:
|
if event.remove_listener:
|
||||||
self.listeners[event.eventType].remove(callback)
|
if callback in self.listeners[ALL_EVENTS]:
|
||||||
|
self.listeners[ALL_EVENTS].remove(callback)
|
||||||
|
|
||||||
event.removeListener = False
|
if callback in self.listeners[event.event_type]:
|
||||||
|
self.listeners[event.event_type].remove(callback)
|
||||||
|
|
||||||
if event.stopPropegating:
|
event.remove_listener = False
|
||||||
break
|
|
||||||
|
|
||||||
self.lock.release()
|
if event.stop_propegating:
|
||||||
|
break
|
||||||
|
|
||||||
Thread(target=run).start()
|
self.lock.release()
|
||||||
|
|
||||||
def listen(self, event_type, callback):
|
Thread(target=run).start()
|
||||||
self.lock.acquire()
|
|
||||||
|
|
||||||
self.listeners[event_type].append(callback)
|
def listen(self, event_type, callback):
|
||||||
|
self.lock.acquire()
|
||||||
|
|
||||||
self.logger.info("New listener added for event {}. Total: {}".format(event_type, len(self.listeners[event_type])))
|
self.listeners[event_type].append(callback)
|
||||||
|
|
||||||
self.lock.release()
|
self.logger.info("New listener added for event {}. Total: {}".format(event_type, len(self.listeners[event_type])))
|
||||||
|
|
||||||
|
self.lock.release()
|
||||||
|
|
||||||
|
|
||||||
class Event:
|
class Event(object):
|
||||||
def __init__(self, eventType, data):
|
def __init__(self, event_type, data):
|
||||||
self.eventType = eventType
|
self.event_type = event_type
|
||||||
self.data = data
|
self.data = data
|
||||||
self.stopPropegating = False
|
self.stop_propegating = False
|
||||||
self.removeListener = False
|
self.remove_listener = False
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return str([self.eventType, self.data])
|
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return str([self.event_type, self.data])
|
||||||
|
@ -4,103 +4,97 @@ import time
|
|||||||
from app.StateMachine import StateMachine
|
from app.StateMachine import StateMachine
|
||||||
from app.EventBus import EventBus
|
from app.EventBus import EventBus
|
||||||
from app.DeviceTracker import DeviceTracker
|
from app.DeviceTracker import DeviceTracker
|
||||||
from HttpInterface import HttpInterface
|
from app.HttpInterface import HttpInterface
|
||||||
|
|
||||||
from app.observer.WeatherWatcher import WeatherWatcher
|
from app.observer.WeatherWatcher import WeatherWatcher
|
||||||
from app.observer.TomatoDeviceScanner import TomatoDeviceScanner
|
|
||||||
from app.observer.Timer import Timer
|
from app.observer.Timer import Timer
|
||||||
|
|
||||||
from app.actor.HueTrigger import HueTrigger
|
from app.actor.HueTrigger import HueTrigger
|
||||||
|
|
||||||
class HomeAssistant:
|
class HomeAssistant(object):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.config = None
|
self.config = None
|
||||||
self.eventbus = None
|
self.eventbus = None
|
||||||
self.statemachine = None
|
self.statemachine = None
|
||||||
|
|
||||||
self.timer = None
|
self.timer = None
|
||||||
self.weatherwatcher = None
|
self.weatherwatcher = None
|
||||||
self.devicetracker = None
|
self.devicetracker = None
|
||||||
|
|
||||||
self.huetrigger = None
|
self.huetrigger = None
|
||||||
self.httpinterface = None
|
self.httpinterface = None
|
||||||
|
|
||||||
def get_config(self):
|
def get_config(self):
|
||||||
if self.config is None:
|
if self.config is None:
|
||||||
self.config = SafeConfigParser()
|
self.config = SafeConfigParser()
|
||||||
self.config.read("home-assistant.conf")
|
self.config.read("home-assistant.conf")
|
||||||
|
|
||||||
return self.config
|
return self.config
|
||||||
|
|
||||||
|
|
||||||
def get_event_bus(self):
|
def get_event_bus(self):
|
||||||
if self.eventbus is None:
|
if self.eventbus is None:
|
||||||
self.eventbus = EventBus()
|
self.eventbus = EventBus()
|
||||||
|
|
||||||
return self.eventbus
|
return self.eventbus
|
||||||
|
|
||||||
|
|
||||||
def get_state_machine(self):
|
def get_state_machine(self):
|
||||||
if self.statemachine is None:
|
if self.statemachine is None:
|
||||||
self.statemachine = StateMachine(self.get_event_bus())
|
self.statemachine = StateMachine(self.get_event_bus())
|
||||||
|
|
||||||
return self.statemachine
|
return self.statemachine
|
||||||
|
|
||||||
|
|
||||||
def setup_timer(self):
|
def setup_timer(self):
|
||||||
if self.timer is None:
|
if self.timer is None:
|
||||||
self.timer = Timer(self.get_event_bus())
|
self.timer = Timer(self.get_event_bus())
|
||||||
|
|
||||||
return self.timer
|
return self.timer
|
||||||
|
|
||||||
def setup_weather_watcher(self):
|
def setup_weather_watcher(self):
|
||||||
if self.weatherwatcher is None:
|
if self.weatherwatcher is None:
|
||||||
self.weatherwatcher = WeatherWatcher(self.get_config(), self.get_event_bus(), self.get_state_machine())
|
self.weatherwatcher = WeatherWatcher(self.get_config(), self.get_event_bus(), self.get_state_machine())
|
||||||
|
|
||||||
return self.weatherwatcher
|
return self.weatherwatcher
|
||||||
|
|
||||||
|
|
||||||
def setup_device_tracker(self, device_scanner):
|
def setup_device_tracker(self, device_scanner):
|
||||||
if self.devicetracker is None:
|
if self.devicetracker is None:
|
||||||
self.devicetracker = DeviceTracker(self.get_event_bus(), self.get_state_machine(), device_scanner)
|
self.devicetracker = DeviceTracker(self.get_event_bus(), self.get_state_machine(), device_scanner)
|
||||||
|
|
||||||
return self.devicetracker
|
return self.devicetracker
|
||||||
|
|
||||||
|
|
||||||
def setup_hue_trigger(self):
|
def setup_hue_trigger(self):
|
||||||
if self.huetrigger is None:
|
if self.huetrigger is None:
|
||||||
assert self.devicetracker is not None, "Cannot setup Hue Trigger without a device tracker being setup"
|
assert self.devicetracker is not None, "Cannot setup Hue Trigger without a device tracker being setup"
|
||||||
|
|
||||||
self.huetrigger = HueTrigger(self.get_config(), self.get_event_bus(), self.get_state_machine(), self.devicetracker, self.setup_weather_watcher())
|
self.huetrigger = HueTrigger(self.get_config(), self.get_event_bus(), self.get_state_machine(), self.devicetracker, self.setup_weather_watcher())
|
||||||
|
|
||||||
return self.huetrigger
|
return self.huetrigger
|
||||||
|
|
||||||
|
|
||||||
def setup_http_interface(self):
|
def setup_http_interface(self):
|
||||||
self.httpinterface = HttpInterface(self.get_event_bus(), self.get_state_machine())
|
self.httpinterface = HttpInterface(self.get_event_bus(), self.get_state_machine())
|
||||||
self.httpinterface.start()
|
self.httpinterface.start()
|
||||||
|
|
||||||
return self.httpinterface
|
return self.httpinterface
|
||||||
|
|
||||||
def start(self):
|
|
||||||
self.setup_timer().start()
|
|
||||||
|
|
||||||
while True:
|
|
||||||
try:
|
|
||||||
time.sleep(1)
|
|
||||||
|
|
||||||
except:
|
|
||||||
print ""
|
|
||||||
print "Interrupt received. Wrapping up and quiting.."
|
|
||||||
self.timer.stop()
|
|
||||||
|
|
||||||
if self.httpinterface is not None:
|
|
||||||
self.httpinterface.stop()
|
|
||||||
|
|
||||||
break
|
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
self.setup_timer().start()
|
||||||
|
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print ""
|
||||||
|
print "Interrupt received. Wrapping up and quiting.."
|
||||||
|
self.timer.stop()
|
||||||
|
|
||||||
|
if self.httpinterface is not None:
|
||||||
|
self.httpinterface.stop()
|
||||||
|
|
||||||
|
break
|
||||||
|
@ -5,85 +5,85 @@ import requests
|
|||||||
|
|
||||||
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
|
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
|
||||||
|
|
||||||
SERVER_HOST= '127.0.0.1'
|
SERVER_HOST = '127.0.0.1'
|
||||||
SERVER_PORT = 8080
|
SERVER_PORT = 8080
|
||||||
|
|
||||||
class RequestHandler(BaseHTTPRequestHandler):
|
class RequestHandler(BaseHTTPRequestHandler):
|
||||||
|
|
||||||
#Handler for the GET requests
|
|
||||||
def do_GET(self):
|
|
||||||
if self.path == "/":
|
|
||||||
self.send_response(200)
|
|
||||||
self.send_header('Content-type','text/html')
|
|
||||||
self.end_headers()
|
|
||||||
|
|
||||||
write = self.wfile.write
|
|
||||||
|
|
||||||
# Describe state machine:
|
#Handler for the GET requests
|
||||||
categories = []
|
def do_GET(self):
|
||||||
|
if self.path == "/":
|
||||||
|
self.send_response(200)
|
||||||
|
self.send_header('Content-type','text/html')
|
||||||
|
self.end_headers()
|
||||||
|
|
||||||
write("<table>")
|
write = self.wfile.write
|
||||||
write("<tr><th>Name</th><th>State</th><th>Last Changed</th></tr>")
|
|
||||||
|
|
||||||
for category, state, last_changed in self.server.statemachine.get_states():
|
# Describe state machine:
|
||||||
categories.append(category)
|
categories = []
|
||||||
|
|
||||||
write("<tr><td>{}</td><td>{}</td><td>{}</td></tr>".format(category, state, last_changed.strftime("%H:%M:%S %d-%m-%Y")))
|
write("<table>")
|
||||||
|
write("<tr><th>Name</th><th>State</th><th>Last Changed</th></tr>")
|
||||||
|
|
||||||
write("</table>")
|
for category, state, last_changed in self.server.statemachine.get_states():
|
||||||
|
categories.append(category)
|
||||||
|
|
||||||
# Small form to change the state
|
write("<tr><td>{}</td><td>{}</td><td>{}</td></tr>".format(category, state, last_changed.strftime("%H:%M:%S %d-%m-%Y")))
|
||||||
write("<br />Change state:<br />")
|
|
||||||
write("<form action='change_state' method='POST'>")
|
|
||||||
write("<select name='category'>")
|
|
||||||
|
|
||||||
for category in categories:
|
write("</table>")
|
||||||
write("<option>{}</option>".format(category))
|
|
||||||
|
|
||||||
write("</select>")
|
# Small form to change the state
|
||||||
|
write("<br />Change state:<br />")
|
||||||
|
write("<form action='change_state' method='POST'>")
|
||||||
|
write("<select name='category'>")
|
||||||
|
|
||||||
write("<input name='new_state' />")
|
for category in categories:
|
||||||
write("<input type='submit' value='set state' />")
|
write("<option>{}</option>".format(category))
|
||||||
write("</form>")
|
|
||||||
|
|
||||||
else:
|
write("</select>")
|
||||||
self.send_response(404)
|
|
||||||
|
write("<input name='new_state' />")
|
||||||
|
write("<input type='submit' value='set state' />")
|
||||||
|
write("</form>")
|
||||||
|
|
||||||
|
else:
|
||||||
|
self.send_response(404)
|
||||||
|
|
||||||
|
|
||||||
def do_POST(self):
|
def do_POST(self):
|
||||||
length = int(self.headers['Content-Length'])
|
length = int(self.headers['Content-Length'])
|
||||||
post_data = urlparse.parse_qs(self.rfile.read(length))
|
post_data = urlparse.parse_qs(self.rfile.read(length))
|
||||||
|
|
||||||
if self.path == "/change_state":
|
if self.path == "/change_state":
|
||||||
self.server.statemachine.set_state(post_data['category'][0], post_data['new_state'][0])
|
self.server.statemachine.set_state(post_data['category'][0], post_data['new_state'][0])
|
||||||
|
|
||||||
self.send_response(301)
|
self.send_response(301)
|
||||||
self.send_header("Location", "/")
|
self.send_header("Location", "/")
|
||||||
self.end_headers()
|
self.end_headers()
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.send_response(404)
|
self.send_response(404)
|
||||||
|
|
||||||
|
|
||||||
class HttpInterface(threading.Thread):
|
class HttpInterface(threading.Thread):
|
||||||
|
|
||||||
def __init__(self, eventbus, statemachine):
|
def __init__(self, eventbus, statemachine):
|
||||||
threading.Thread.__init__(self)
|
threading.Thread.__init__(self)
|
||||||
|
|
||||||
self.server = HTTPServer((SERVER_HOST, SERVER_PORT), RequestHandler)
|
self.server = HTTPServer((SERVER_HOST, SERVER_PORT), RequestHandler)
|
||||||
|
|
||||||
self.server.eventbus = eventbus
|
self.server.eventbus = eventbus
|
||||||
self.server.statemachine = statemachine
|
self.server.statemachine = statemachine
|
||||||
|
|
||||||
self._stop = threading.Event()
|
self._stop = threading.Event()
|
||||||
|
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
while not self._stop.is_set():
|
while not self._stop.is_set():
|
||||||
self.server.handle_request()
|
self.server.handle_request()
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
self._stop.set()
|
self._stop.set()
|
||||||
|
|
||||||
# Trigger a fake request to get the server to quit
|
# Trigger a fake request to get the server to quit
|
||||||
requests.get("http://{}:{}".format(SERVER_HOST, SERVER_PORT))
|
requests.get("http://{}:{}".format(SERVER_HOST, SERVER_PORT))
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from collections import defaultdict, namedtuple
|
from collections import namedtuple
|
||||||
from threading import RLock
|
from threading import RLock
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
@ -7,54 +7,53 @@ from app.util import ensure_list, matcher
|
|||||||
|
|
||||||
EVENT_STATE_CHANGED = "state_changed"
|
EVENT_STATE_CHANGED = "state_changed"
|
||||||
|
|
||||||
state = namedtuple("State", ['state','last_changed'])
|
State = namedtuple("State", ['state','last_changed'])
|
||||||
|
|
||||||
class StateMachine:
|
class StateMachine(object):
|
||||||
|
|
||||||
def __init__(self, eventBus):
|
def __init__(self, eventbus):
|
||||||
self.states = dict()
|
self.states = dict()
|
||||||
self.eventBus = eventBus
|
self.eventbus = eventbus
|
||||||
self.lock = RLock()
|
self.lock = RLock()
|
||||||
|
|
||||||
def add_category(self, category, initialState):
|
def add_category(self, category, initial_state):
|
||||||
self.states[category] = state(initialState, datetime.now())
|
self.states[category] = State(initial_state, datetime.now())
|
||||||
|
|
||||||
def set_state(self, category, newState):
|
def set_state(self, category, new_state):
|
||||||
self.lock.acquire()
|
self.lock.acquire()
|
||||||
|
|
||||||
assert category in self.states, "Category does not exist: {}".format(category)
|
assert category in self.states, "Category does not exist: {}".format(category)
|
||||||
|
|
||||||
oldState = self.states[category]
|
|
||||||
|
|
||||||
if oldState.state != newState:
|
old_state = self.states[category]
|
||||||
self.states[category] = state(newState, datetime.now())
|
|
||||||
|
|
||||||
self.eventBus.fire(Event(EVENT_STATE_CHANGED, {'category':category, 'oldState':oldState, 'newState':self.states[category]}))
|
if old_state.state != new_state:
|
||||||
|
self.states[category] = State(new_state, datetime.now())
|
||||||
|
|
||||||
self.lock.release()
|
self.eventbus.fire(Event(EVENT_STATE_CHANGED, {'category':category, 'old_state':old_state, 'new_state':self.states[category]}))
|
||||||
|
|
||||||
def get_state(self, category):
|
self.lock.release()
|
||||||
assert category in self.states, "Category does not exist: {}".format(category)
|
|
||||||
|
|
||||||
return self.states[category]
|
def get_state(self, category):
|
||||||
|
assert category in self.states, "Category does not exist: {}".format(category)
|
||||||
|
|
||||||
def get_states(self):
|
return self.states[category]
|
||||||
for category in sorted(self.states.keys()):
|
|
||||||
yield category, self.states[category].state, self.states[category].last_changed
|
def get_states(self):
|
||||||
|
for category in sorted(self.states.keys()):
|
||||||
|
yield category, self.states[category].state, self.states[category].last_changed
|
||||||
|
|
||||||
|
|
||||||
def track_state_change(eventBus, category, fromState, toState, action):
|
def track_state_change(eventbus, category, from_state, to_state, action):
|
||||||
fromState = ensure_list(fromState)
|
from_state = ensure_list(from_state)
|
||||||
toState = ensure_list(toState)
|
to_state = ensure_list(to_state)
|
||||||
|
|
||||||
def listener(event):
|
def listener(event):
|
||||||
assert isinstance(event, Event), "event needs to be of Event type"
|
assert isinstance(event, Event), "event needs to be of Event type"
|
||||||
|
|
||||||
if category == event.data['category'] and \
|
if category == event.data['category'] and \
|
||||||
matcher(event.data['oldState'].state, fromState) and \
|
matcher(event.data['old_state'].state, from_state) and \
|
||||||
matcher(event.data['newState'].state, toState):
|
matcher(event.data['new_state'].state, to_state):
|
||||||
|
|
||||||
action(event.data['category'], event.data['oldState'], event.data['newState'])
|
|
||||||
|
|
||||||
eventBus.listen(EVENT_STATE_CHANGED, listener)
|
action(event.data['category'], event.data['old_state'], event.data['new_state'])
|
||||||
|
|
||||||
|
eventbus.listen(EVENT_STATE_CHANGED, listener)
|
||||||
|
@ -10,87 +10,85 @@ from app.observer.Timer import track_time_change
|
|||||||
|
|
||||||
LIGHTS_TURNING_ON_BEFORE_SUN_SET_PERIOD = timedelta(minutes=30)
|
LIGHTS_TURNING_ON_BEFORE_SUN_SET_PERIOD = timedelta(minutes=30)
|
||||||
|
|
||||||
class HueTrigger:
|
class HueTrigger(object):
|
||||||
def __init__(self, config, eventbus, statemachine, device_tracker, weather):
|
def __init__(self, config, eventbus, statemachine, device_tracker, weather):
|
||||||
self.eventbus = eventbus
|
self.eventbus = eventbus
|
||||||
self.statemachine = statemachine
|
self.statemachine = statemachine
|
||||||
self.weather = weather
|
self.weather = weather
|
||||||
|
|
||||||
self.bridge = Bridge(config.get("hue","host"))
|
self.bridge = Bridge(config.get("hue","host"))
|
||||||
self.lights = self.bridge.get_light_objects()
|
self.lights = self.bridge.get_light_objects()
|
||||||
self.logger = logging.getLogger("HueTrigger")
|
self.logger = logging.getLogger("HueTrigger")
|
||||||
|
|
||||||
# Track home coming of each seperate device
|
# Track home coming of each seperate device
|
||||||
for category in device_tracker.device_state_categories():
|
for category in device_tracker.device_state_categories():
|
||||||
track_state_change(eventbus, category, STATE_DEVICE_NOT_HOME, STATE_DEVICE_HOME, self.handle_device_state_change)
|
track_state_change(eventbus, category, STATE_DEVICE_NOT_HOME, STATE_DEVICE_HOME, self.handle_device_state_change)
|
||||||
|
|
||||||
# Track when all devices are gone to shut down lights
|
# Track when all devices are gone to shut down lights
|
||||||
track_state_change(eventbus, STATE_CATEGORY_ALL_DEVICES, STATE_DEVICE_HOME, STATE_DEVICE_NOT_HOME, self.handle_device_state_change)
|
track_state_change(eventbus, STATE_CATEGORY_ALL_DEVICES, STATE_DEVICE_HOME, STATE_DEVICE_NOT_HOME, self.handle_device_state_change)
|
||||||
|
|
||||||
# Track every time sun rises so we can schedule a time-based pre-sun set event
|
# Track every time sun rises so we can schedule a time-based pre-sun set event
|
||||||
track_state_change(eventbus, STATE_CATEGORY_SUN, SUN_STATE_BELOW_HORIZON, SUN_STATE_ABOVE_HORIZON, self.handle_sun_rising)
|
track_state_change(eventbus, STATE_CATEGORY_SUN, SUN_STATE_BELOW_HORIZON, SUN_STATE_ABOVE_HORIZON, self.handle_sun_rising)
|
||||||
|
|
||||||
# If the sun is already above horizon schedule the time-based pre-sun set event
|
# If the sun is already above horizon schedule the time-based pre-sun set event
|
||||||
if statemachine.get_state(STATE_CATEGORY_SUN) == SUN_STATE_ABOVE_HORIZON:
|
if statemachine.get_state(STATE_CATEGORY_SUN) == SUN_STATE_ABOVE_HORIZON:
|
||||||
self.handle_sun_rising(None, None, None)
|
self.handle_sun_rising(None, None, None)
|
||||||
|
|
||||||
|
|
||||||
def get_lights_status(self):
|
def get_lights_status(self):
|
||||||
lights_are_on = sum([1 for light in self.lights if light.on]) > 0
|
lights_are_on = sum([1 for light in self.lights if light.on]) > 0
|
||||||
|
|
||||||
light_needed = not lights_are_on and self.statemachine.get_state(STATE_CATEGORY_SUN).state == SUN_STATE_BELOW_HORIZON
|
light_needed = not lights_are_on and self.statemachine.get_state(STATE_CATEGORY_SUN).state == SUN_STATE_BELOW_HORIZON
|
||||||
|
|
||||||
return lights_are_on, light_needed
|
return lights_are_on, light_needed
|
||||||
|
|
||||||
|
|
||||||
def turn_lights_on(self, transitiontime=None):
|
def turn_lights_on(self, transitiontime=None):
|
||||||
command = {'on': True, 'xy': [0.5119, 0.4147], 'bri':164}
|
command = {'on': True, 'xy': [0.5119, 0.4147], 'bri':164}
|
||||||
|
|
||||||
if transitiontime is not None:
|
if transitiontime is not None:
|
||||||
command['transitiontime'] = transitiontime
|
command['transitiontime'] = transitiontime
|
||||||
|
|
||||||
self.bridge.set_light([1,2,3], command)
|
self.bridge.set_light([1, 2, 3], command)
|
||||||
|
|
||||||
|
|
||||||
def turn_lights_off(self, transitiontime=None):
|
def turn_lights_off(self, transitiontime=None):
|
||||||
command = {'on': False}
|
command = {'on': False}
|
||||||
|
|
||||||
if transitiontime is not None:
|
if transitiontime is not None:
|
||||||
command['transitiontime'] = transitiontime
|
command['transitiontime'] = transitiontime
|
||||||
|
|
||||||
self.bridge.set_light([1,2,3], command)
|
self.bridge.set_light([1, 2, 3], command)
|
||||||
|
|
||||||
|
|
||||||
def handle_sun_rising(self, category, oldState, newState):
|
def handle_sun_rising(self, category, old_state, new_state):
|
||||||
# Schedule an event X minutes prior to sun setting
|
# Schedule an event X minutes prior to sun setting
|
||||||
track_time_change(self.eventbus, self.handle_sun_setting, datetime=self.weather.next_sun_setting()-LIGHTS_TURNING_ON_BEFORE_SUN_SET_PERIOD)
|
track_time_change(self.eventbus, self.handle_sun_setting, point_in_time=self.weather.next_sun_setting()-LIGHTS_TURNING_ON_BEFORE_SUN_SET_PERIOD)
|
||||||
|
|
||||||
|
|
||||||
# Gets called when darkness starts falling in, slowly turn on the lights
|
# Gets called when darkness starts falling in, slowly turn on the lights
|
||||||
def handle_sun_setting(self, now):
|
def handle_sun_setting(self, now):
|
||||||
lights_are_on, light_needed = self.get_lights_status()
|
lights_are_on, light_needed = self.get_lights_status()
|
||||||
|
|
||||||
if not lights_are_on and self.statemachine.get_state(STATE_CATEGORY_ALL_DEVICES).state == STATE_DEVICE_HOME:
|
if not lights_are_on and self.statemachine.get_state(STATE_CATEGORY_ALL_DEVICES).state == STATE_DEVICE_HOME:
|
||||||
self.logger.info("Sun setting and devices home. Turning on lights.")
|
self.logger.info("Sun setting and devices home. Turning on lights.")
|
||||||
|
|
||||||
# We will start the lights now and by the time the sun sets
|
# We will start the lights now and by the time the sun sets
|
||||||
# the lights will be at full brightness
|
# the lights will be at full brightness
|
||||||
transitiontime = (self.weather.next_sun_setting() - datetime.now()).seconds * 10
|
transitiontime = (self.weather.next_sun_setting() - datetime.now()).seconds * 10
|
||||||
|
|
||||||
self.turn_lights_on(transitiontime)
|
self.turn_lights_on(transitiontime)
|
||||||
|
|
||||||
|
|
||||||
def handle_device_state_change(self, category, oldState, newState):
|
def handle_device_state_change(self, category, old_state, new_state):
|
||||||
lights_are_on, light_needed = self.get_lights_status()
|
lights_are_on, light_needed = self.get_lights_status()
|
||||||
|
|
||||||
# Specific device came home ?
|
|
||||||
if category != STATE_CATEGORY_ALL_DEVICES and newState.state == STATE_DEVICE_HOME and light_needed:
|
|
||||||
self.logger.info("Home coming event for {}. Turning lights on".format(category))
|
|
||||||
self.turn_lights_on()
|
|
||||||
|
|
||||||
# Did all devices leave the house?
|
|
||||||
elif category == STATE_CATEGORY_ALL_DEVICES and newState.state == STATE_DEVICE_NOT_HOME and lights_are_on:
|
|
||||||
self.logger.info("Everyone has left. Turning lights off")
|
|
||||||
self.turn_lights_off()
|
|
||||||
|
|
||||||
|
# Specific device came home ?
|
||||||
|
if category != STATE_CATEGORY_ALL_DEVICES and new_state.state == STATE_DEVICE_HOME and light_needed:
|
||||||
|
self.logger.info("Home coming event for {}. Turning lights on".format(category))
|
||||||
|
self.turn_lights_on()
|
||||||
|
|
||||||
|
# Did all devices leave the house?
|
||||||
|
elif category == STATE_CATEGORY_ALL_DEVICES and new_state.state == STATE_DEVICE_NOT_HOME and lights_are_on:
|
||||||
|
self.logger.info("Everyone has left. Turning lights off")
|
||||||
|
self.turn_lights_off()
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from datetime import datetime, timedelta
|
from datetime import datetime
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
|
|
||||||
@ -12,53 +12,52 @@ assert 60 % TIME_INTERVAL == 0, "60 % TIME_INTERVAL should be 0!"
|
|||||||
EVENT_TIME_CHANGED = "time_changed"
|
EVENT_TIME_CHANGED = "time_changed"
|
||||||
|
|
||||||
class Timer(threading.Thread):
|
class Timer(threading.Thread):
|
||||||
def __init__(self, eventbus):
|
def __init__(self, eventbus):
|
||||||
threading.Thread.__init__(self)
|
threading.Thread.__init__(self)
|
||||||
|
|
||||||
self.eventbus = eventbus
|
self.eventbus = eventbus
|
||||||
self._stop = threading.Event()
|
self._stop = threading.Event()
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
self._stop.set()
|
self._stop.set()
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
now = datetime.now()
|
now = datetime.now()
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
if self._stop.isSet():
|
if self._stop.isSet():
|
||||||
break
|
break
|
||||||
|
|
||||||
self.eventbus.fire(Event(EVENT_TIME_CHANGED, {'now':now}))
|
self.eventbus.fire(Event(EVENT_TIME_CHANGED, {'now':now}))
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
now = datetime.now()
|
now = datetime.now()
|
||||||
|
|
||||||
if self._stop.isSet() or now.second % TIME_INTERVAL == 0:
|
if self._stop.isSet() or now.second % TIME_INTERVAL == 0:
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
||||||
def track_time_change(eventBus, action, year='*', month='*', day='*', hour='*', minute='*', second='*', datetime=None, listen_once=False):
|
def track_time_change(eventBus, action, year='*', month='*', day='*', hour='*', minute='*', second='*', point_in_time=None, listen_once=False):
|
||||||
year, month, day = ensure_list(year), ensure_list(month), ensure_list(day)
|
year, month, day = ensure_list(year), ensure_list(month), ensure_list(day)
|
||||||
hour, minute, second = ensure_list(hour), ensure_list(minute), ensure_list(second)
|
hour, minute, second = ensure_list(hour), ensure_list(minute), ensure_list(second)
|
||||||
|
|
||||||
def listener(event):
|
def listener(event):
|
||||||
assert isinstance(event, Event), "event needs to be of Event type"
|
assert isinstance(event, Event), "event needs to be of Event type"
|
||||||
|
|
||||||
if (datetime is not None and event.data['now'] > datetime) or \
|
if (point_in_time is not None and event.data['now'] > point_in_time) or \
|
||||||
datetime is None and \
|
point_in_time is None and \
|
||||||
matcher(event.data['now'].year, year) and \
|
matcher(event.data['now'].year, year) and \
|
||||||
matcher(event.data['now'].month, month) and \
|
matcher(event.data['now'].month, month) and \
|
||||||
matcher(event.data['now'].day, day) and \
|
matcher(event.data['now'].day, day) and \
|
||||||
matcher(event.data['now'].hour, hour) and \
|
matcher(event.data['now'].hour, hour) and \
|
||||||
matcher(event.data['now'].minute, minute) and \
|
matcher(event.data['now'].minute, minute) and \
|
||||||
matcher(event.data['now'].second, second):
|
matcher(event.data['now'].second, second):
|
||||||
|
|
||||||
# datetime are exact points in time so we always remove it after fire
|
# point_in_time are exact points in time so we always remove it after fire
|
||||||
event.removeListener = listen_once or datetime is not None
|
event.remove_listener = listen_once or point_in_time is not None
|
||||||
|
|
||||||
action(event.data['now'])
|
action(event.data['now'])
|
||||||
|
|
||||||
eventBus.listen(EVENT_TIME_CHANGED, listener)
|
|
||||||
|
|
||||||
|
eventBus.listen(EVENT_TIME_CHANGED, listener)
|
||||||
|
@ -10,93 +10,94 @@ MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5)
|
|||||||
|
|
||||||
KNOWN_DEVICES_FILE = "tomato_known_devices.csv"
|
KNOWN_DEVICES_FILE = "tomato_known_devices.csv"
|
||||||
|
|
||||||
class TomatoDeviceScanner:
|
class TomatoDeviceScanner(object):
|
||||||
# self.logger
|
# self.logger
|
||||||
|
|
||||||
def __init__(self, config):
|
def __init__(self, config):
|
||||||
self.config = config
|
self.config = config
|
||||||
self.logger = logging.getLogger(__name__)
|
self.logger = logging.getLogger(__name__)
|
||||||
self.lock = Lock()
|
self.lock = Lock()
|
||||||
self.date_updated = None
|
self.date_updated = None
|
||||||
self.last_results = None
|
self.last_results = None
|
||||||
|
|
||||||
# Read known devices
|
# Read known devices
|
||||||
if os.path.isfile(KNOWN_DEVICES_FILE):
|
if os.path.isfile(KNOWN_DEVICES_FILE):
|
||||||
with open(KNOWN_DEVICES_FILE) as inp:
|
with open(KNOWN_DEVICES_FILE) as inp:
|
||||||
known_devices = { row['mac']: row for row in csv.DictReader(inp) }
|
known_devices = { row['mac']: row for row in csv.DictReader(inp) }
|
||||||
|
|
||||||
# Update known devices csv file for future use
|
# Update known devices csv file for future use
|
||||||
with open(KNOWN_DEVICES_FILE, 'a') as outp:
|
with open(KNOWN_DEVICES_FILE, 'a') as outp:
|
||||||
writer = csv.writer(outp)
|
writer = csv.writer(outp)
|
||||||
|
|
||||||
# Query for new devices
|
# Query for new devices
|
||||||
exec(self.tomato_request("devlist"))
|
exec(self.tomato_request("devlist"))
|
||||||
|
|
||||||
for name, _, mac, _ in dhcpd_lease:
|
for name, _, mac, _ in dhcpd_lease:
|
||||||
if mac not in known_devices:
|
if mac not in known_devices:
|
||||||
writer.writerow((mac, name, 0))
|
writer.writerow((mac, name, 0))
|
||||||
|
|
||||||
# Create a dict with ID: NAME of the devices to track
|
# Create a dict with ID: NAME of the devices to track
|
||||||
self.devices_to_track = dict()
|
self.devices_to_track = dict()
|
||||||
|
|
||||||
for mac in [mac for mac in known_devices if known_devices[mac]['track'] == '1']:
|
for mac in known_devices:
|
||||||
self.devices_to_track[mac] = known_devices[mac]['name']
|
if known_devices[mac]['track'] == '1':
|
||||||
|
self.devices_to_track[mac] = known_devices[mac]['name']
|
||||||
# Quicker way of the previous statement but it doesn't go together with exec:
|
|
||||||
# unqualified exec is not allowed in function '__init__' it contains a nested function with free variables
|
# Quicker way of the previous statement but it doesn't go together with exec:
|
||||||
# self.devices_to_track = {mac: known_devices[mac]['name'] for mac in known_devices if known_devices[mac]['track'] == '1'}
|
# unqualified exec is not allowed in function '__init__' it contains a nested function with free variables
|
||||||
|
# self.devices_to_track = {mac: known_devices[mac]['name'] for mac in known_devices if known_devices[mac]['track'] == '1'}
|
||||||
|
|
||||||
|
|
||||||
def get_devices_to_track(self):
|
def get_devices_to_track(self):
|
||||||
return self.devices_to_track
|
return self.devices_to_track
|
||||||
|
|
||||||
def scan_devices(self):
|
def scan_devices(self):
|
||||||
self.lock.acquire()
|
self.lock.acquire()
|
||||||
|
|
||||||
# We don't want to hammer the router. Only update if MIN_TIME_BETWEEN_SCANS has passed
|
# We don't want to hammer the router. Only update if MIN_TIME_BETWEEN_SCANS has passed
|
||||||
if self.date_updated is None or datetime.now() - self.date_updated > MIN_TIME_BETWEEN_SCANS:
|
if self.date_updated is None or datetime.now() - self.date_updated > MIN_TIME_BETWEEN_SCANS:
|
||||||
self.logger.info("Scanning for new devices")
|
self.logger.info("Scanning for new devices")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Query for new devices
|
# Query for new devices
|
||||||
exec(self.tomato_request("devlist"))
|
exec(self.tomato_request("devlist"))
|
||||||
|
|
||||||
self.last_results = [mac for iface, mac, rssi, tx, rx, quality, unknown_num in wldev]
|
self.last_results = [mac for iface, mac, rssi, tx, rx, quality, unknown_num in wldev]
|
||||||
|
|
||||||
except:
|
except Exception:
|
||||||
self.logger.exception("Scanning failed")
|
self.logger.exception("Scanning failed")
|
||||||
|
|
||||||
|
|
||||||
self.lock.release()
|
|
||||||
return self.last_results
|
|
||||||
|
|
||||||
def tomato_request(self, action):
|
self.lock.release()
|
||||||
# Get router info
|
return self.last_results
|
||||||
r = requests.post('http://{}/update.cgi'.format(self.config.get('tomato','host')),
|
|
||||||
data={'_http_id':self.config.get('tomato','http_id'), 'exec':action},
|
|
||||||
auth=requests.auth.HTTPBasicAuth(self.config.get('tomato','username'), self.config.get('tomato','password')))
|
|
||||||
|
|
||||||
return r.text
|
def tomato_request(self, action):
|
||||||
|
# Get router info
|
||||||
|
req = requests.post('http://{}/update.cgi'.format(self.config.get('tomato','host')),
|
||||||
|
data={'_http_id':self.config.get('tomato','http_id'), 'exec':action},
|
||||||
|
auth=requests.auth.HTTPBasicAuth(self.config.get('tomato','username'), self.config.get('tomato','password')))
|
||||||
|
|
||||||
|
return req.text
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
for ip, mac, iface in arplist:
|
for ip, mac, iface in arplist:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# print wlnoise
|
# print wlnoise
|
||||||
|
|
||||||
# print dhcpd_static
|
# print dhcpd_static
|
||||||
|
|
||||||
for iface, mac, rssi, tx, rx, quality, unknown_num in wldev:
|
for iface, mac, rssi, tx, rx, quality, unknown_num in wldev:
|
||||||
print mac, quality
|
print mac, quality
|
||||||
|
|
||||||
print ""
|
print ""
|
||||||
|
|
||||||
for name, ip, mac, lease in dhcpd_lease:
|
for name, ip, mac, lease in dhcpd_lease:
|
||||||
if name:
|
if name:
|
||||||
print name, ip
|
print name, ip
|
||||||
|
|
||||||
else:
|
else:
|
||||||
print ip
|
print ip
|
||||||
"""
|
"""
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
import logging
|
import logging
|
||||||
from datetime import datetime, timedelta
|
from datetime import timedelta
|
||||||
|
|
||||||
import ephem
|
import ephem
|
||||||
|
|
||||||
from app.EventBus import Event
|
|
||||||
|
|
||||||
from app.observer.Timer import track_time_change
|
from app.observer.Timer import track_time_change
|
||||||
|
|
||||||
STATE_CATEGORY_SUN = "weather.sun"
|
STATE_CATEGORY_SUN = "weather.sun"
|
||||||
@ -12,46 +10,44 @@ STATE_CATEGORY_SUN = "weather.sun"
|
|||||||
SUN_STATE_ABOVE_HORIZON = "above_horizon"
|
SUN_STATE_ABOVE_HORIZON = "above_horizon"
|
||||||
SUN_STATE_BELOW_HORIZON = "below_horizon"
|
SUN_STATE_BELOW_HORIZON = "below_horizon"
|
||||||
|
|
||||||
class WeatherWatcher:
|
class WeatherWatcher(object):
|
||||||
def __init__(self, config, eventbus, statemachine):
|
def __init__(self, config, eventbus, statemachine):
|
||||||
self.logger = logging.getLogger(__name__)
|
self.logger = logging.getLogger(__name__)
|
||||||
self.config = config
|
self.config = config
|
||||||
self.eventbus = eventbus
|
self.eventbus = eventbus
|
||||||
self.statemachine = statemachine
|
self.statemachine = statemachine
|
||||||
|
|
||||||
self.observer = ephem.Observer()
|
self.observer = ephem.Observer()
|
||||||
self.observer.lat = self.config.get('common','latitude')
|
self.observer.lat = self.config.get('common','latitude')
|
||||||
self.observer.long = self.config.get('common','longitude')
|
self.observer.long = self.config.get('common','longitude')
|
||||||
|
|
||||||
self.sun = ephem.Sun()
|
self.sun = ephem.Sun()
|
||||||
|
|
||||||
statemachine.add_category(STATE_CATEGORY_SUN, SUN_STATE_BELOW_HORIZON)
|
statemachine.add_category(STATE_CATEGORY_SUN, SUN_STATE_BELOW_HORIZON)
|
||||||
|
|
||||||
self.update_sun_state()
|
self.update_sun_state()
|
||||||
|
|
||||||
def next_sun_rising(self):
|
def next_sun_rising(self):
|
||||||
return ephem.localtime(self.observer.next_rising(self.sun))
|
return ephem.localtime(self.observer.next_rising(self.sun))
|
||||||
|
|
||||||
def next_sun_setting(self):
|
def next_sun_setting(self):
|
||||||
return ephem.localtime(self.observer.next_setting(self.sun))
|
return ephem.localtime(self.observer.next_setting(self.sun))
|
||||||
|
|
||||||
def update_sun_state(self, now=None):
|
def update_sun_state(self, now=None):
|
||||||
next_rising = ephem.localtime(self.observer.next_rising(self.sun))
|
next_rising = ephem.localtime(self.observer.next_rising(self.sun))
|
||||||
next_setting = ephem.localtime(self.observer.next_setting(self.sun))
|
next_setting = ephem.localtime(self.observer.next_setting(self.sun))
|
||||||
|
|
||||||
if next_rising > next_setting:
|
if next_rising > next_setting:
|
||||||
new_state = SUN_STATE_ABOVE_HORIZON
|
new_state = SUN_STATE_ABOVE_HORIZON
|
||||||
next_change = next_setting
|
next_change = next_setting
|
||||||
|
|
||||||
else:
|
else:
|
||||||
new_state = SUN_STATE_BELOW_HORIZON
|
new_state = SUN_STATE_BELOW_HORIZON
|
||||||
next_change = next_rising
|
next_change = next_rising
|
||||||
|
|
||||||
self.logger.info("Updating sun state to {}. Next change: {}".format(new_state, next_change))
|
self.logger.info("Updating sun state to {}. Next change: {}".format(new_state, next_change))
|
||||||
|
|
||||||
self.statemachine.set_state(STATE_CATEGORY_SUN, new_state)
|
self.statemachine.set_state(STATE_CATEGORY_SUN, new_state)
|
||||||
|
|
||||||
# +10 seconds to be sure that the change has occured
|
|
||||||
track_time_change(self.eventbus, self.update_sun_state, datetime=next_change + timedelta(seconds=10))
|
|
||||||
|
|
||||||
|
|
||||||
|
# +10 seconds to be sure that the change has occured
|
||||||
|
track_time_change(self.eventbus, self.update_sun_state, point_in_time=next_change + timedelta(seconds=10))
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
def ensure_list(parameter):
|
def ensure_list(parameter):
|
||||||
return parameter if isinstance(parameter, list) else [parameter]
|
return parameter if isinstance(parameter, list) else [parameter]
|
||||||
|
|
||||||
def matcher(subject, pattern):
|
def matcher(subject, pattern):
|
||||||
return '*' in pattern or subject in pattern
|
return '*' in pattern or subject in pattern
|
||||||
|
Loading…
x
Reference in New Issue
Block a user