mirror of
https://github.com/home-assistant/core.git
synced 2025-07-10 06:47:09 +00:00
Code according to PEP8 standard
This commit is contained in:
parent
9c9b00c2d4
commit
483546a31d
@ -28,6 +28,7 @@ assert 60 % TIMER_INTERVAL == 0, "60 % TIMER_INTERVAL should be 0!"
|
|||||||
|
|
||||||
DATE_STR_FORMAT = "%H:%M:%S %d-%m-%Y"
|
DATE_STR_FORMAT = "%H:%M:%S %d-%m-%Y"
|
||||||
|
|
||||||
|
|
||||||
def start_home_assistant(eventbus):
|
def start_home_assistant(eventbus):
|
||||||
""" Start home assistant. """
|
""" Start home assistant. """
|
||||||
Timer(eventbus)
|
Timer(eventbus)
|
||||||
@ -41,6 +42,7 @@ def start_home_assistant(eventbus):
|
|||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
||||||
def datetime_to_str(dattim):
|
def datetime_to_str(dattim):
|
||||||
""" Converts datetime to a string format.
|
""" Converts datetime to a string format.
|
||||||
|
|
||||||
@ -48,6 +50,7 @@ def datetime_to_str(dattim):
|
|||||||
"""
|
"""
|
||||||
return dattim.strftime(DATE_STR_FORMAT)
|
return dattim.strftime(DATE_STR_FORMAT)
|
||||||
|
|
||||||
|
|
||||||
def str_to_datetime(dt_str):
|
def str_to_datetime(dt_str):
|
||||||
""" Converts a string to a datetime object.
|
""" Converts a string to a datetime object.
|
||||||
|
|
||||||
@ -55,6 +58,7 @@ def str_to_datetime(dt_str):
|
|||||||
"""
|
"""
|
||||||
return datetime.strptime(dt_str, DATE_STR_FORMAT)
|
return datetime.strptime(dt_str, DATE_STR_FORMAT)
|
||||||
|
|
||||||
|
|
||||||
def ensure_list(parameter):
|
def ensure_list(parameter):
|
||||||
""" Wraps parameter in a list if it is not one and returns it.
|
""" Wraps parameter in a list if it is not one and returns it.
|
||||||
|
|
||||||
@ -62,6 +66,7 @@ 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):
|
||||||
""" Returns True if subject matches the pattern.
|
""" Returns True if subject matches the pattern.
|
||||||
|
|
||||||
@ -70,6 +75,7 @@ def matcher(subject, pattern):
|
|||||||
"""
|
"""
|
||||||
return '*' in pattern or subject in pattern
|
return '*' in pattern or subject in pattern
|
||||||
|
|
||||||
|
|
||||||
def create_state(state, attributes=None, last_changed=None):
|
def create_state(state, attributes=None, last_changed=None):
|
||||||
""" Creates a new state and initializes defaults where necessary. """
|
""" Creates a new state and initializes defaults where necessary. """
|
||||||
attributes = attributes or {}
|
attributes = attributes or {}
|
||||||
@ -79,6 +85,7 @@ def create_state(state, attributes=None, last_changed=None):
|
|||||||
'attributes': attributes,
|
'attributes': attributes,
|
||||||
'last_changed': datetime_to_str(last_changed)}
|
'last_changed': datetime_to_str(last_changed)}
|
||||||
|
|
||||||
|
|
||||||
def track_state_change(eventbus, category, from_state, to_state, action):
|
def track_state_change(eventbus, category, from_state, to_state, action):
|
||||||
""" Helper method to track specific state changes. """
|
""" Helper method to track specific state changes. """
|
||||||
from_state = ensure_list(from_state)
|
from_state = ensure_list(from_state)
|
||||||
@ -96,6 +103,7 @@ def track_state_change(eventbus, category, from_state, to_state, action):
|
|||||||
|
|
||||||
eventbus.listen(EVENT_STATE_CHANGED, listener)
|
eventbus.listen(EVENT_STATE_CHANGED, listener)
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=too-many-arguments
|
# pylint: disable=too-many-arguments
|
||||||
def track_time_change(eventbus, action,
|
def track_time_change(eventbus, action,
|
||||||
year='*', month='*', day='*',
|
year='*', month='*', day='*',
|
||||||
@ -111,12 +119,12 @@ def track_time_change(eventbus, action,
|
|||||||
now = str_to_datetime(event.data['now'])
|
now = str_to_datetime(event.data['now'])
|
||||||
|
|
||||||
if (point_in_time and now > point_in_time) or \
|
if (point_in_time and now > point_in_time) or \
|
||||||
(not point_in_time and \
|
(not point_in_time and
|
||||||
matcher(now.year, year) and \
|
matcher(now.year, year) and
|
||||||
matcher(now.month, month) and \
|
matcher(now.month, month) and
|
||||||
matcher(now.day, day) and \
|
matcher(now.day, day) and
|
||||||
matcher(now.hour, hour) and \
|
matcher(now.hour, hour) and
|
||||||
matcher(now.minute, minute) and \
|
matcher(now.minute, minute) and
|
||||||
matcher(now.second, second)):
|
matcher(now.second, second)):
|
||||||
|
|
||||||
# point_in_time are exact points in time
|
# point_in_time are exact points in time
|
||||||
@ -128,8 +136,10 @@ def track_time_change(eventbus, action,
|
|||||||
|
|
||||||
eventbus.listen(EVENT_TIME_CHANGED, listener)
|
eventbus.listen(EVENT_TIME_CHANGED, listener)
|
||||||
|
|
||||||
|
|
||||||
Event = namedtuple("Event", ["eventbus", "event_type", "data"])
|
Event = namedtuple("Event", ["eventbus", "event_type", "data"])
|
||||||
|
|
||||||
|
|
||||||
class EventBus(object):
|
class EventBus(object):
|
||||||
""" Class that allows code to listen for- and fire events. """
|
""" Class that allows code to listen for- and fire events. """
|
||||||
|
|
||||||
@ -206,6 +216,7 @@ class EventBus(object):
|
|||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class StateMachine(object):
|
class StateMachine(object):
|
||||||
""" Helper class that tracks the state of different categories. """
|
""" Helper class that tracks the state of different categories. """
|
||||||
|
|
||||||
@ -263,6 +274,7 @@ class StateMachine(object):
|
|||||||
|
|
||||||
return cur_state and cur_state['state'] == state
|
return cur_state and cur_state['state'] == state
|
||||||
|
|
||||||
|
|
||||||
class Timer(threading.Thread):
|
class Timer(threading.Thread):
|
||||||
""" Timer will sent out an event every TIMER_INTERVAL seconds. """
|
""" Timer will sent out an event every TIMER_INTERVAL seconds. """
|
||||||
|
|
||||||
@ -304,7 +316,6 @@ class Timer(threading.Thread):
|
|||||||
self.eventbus.fire(EVENT_TIME_CHANGED,
|
self.eventbus.fire(EVENT_TIME_CHANGED,
|
||||||
{'now': datetime_to_str(now)})
|
{'now': datetime_to_str(now)})
|
||||||
|
|
||||||
|
|
||||||
class HomeAssistantException(Exception):
|
class HomeAssistantException(Exception):
|
||||||
""" General Home Assistant exception occured. """
|
""" General Home Assistant exception occured. """
|
||||||
|
|
||||||
|
|
||||||
|
@ -35,6 +35,7 @@ EVENT_KEYBOARD_MEDIA_PLAY_PAUSE = "keyboard.media_play_pause"
|
|||||||
EVENT_KEYBOARD_MEDIA_NEXT_TRACK = "keyboard.media_next_track"
|
EVENT_KEYBOARD_MEDIA_NEXT_TRACK = "keyboard.media_next_track"
|
||||||
EVENT_KEYBOARD_MEDIA_PREV_TRACK = "keyboard.media_prev_track"
|
EVENT_KEYBOARD_MEDIA_PREV_TRACK = "keyboard.media_prev_track"
|
||||||
|
|
||||||
|
|
||||||
def _hue_process_transition_time(transition_seconds):
|
def _hue_process_transition_time(transition_seconds):
|
||||||
""" Transition time is in 1/10th seconds
|
""" Transition time is in 1/10th seconds
|
||||||
and cannot exceed MAX_TRANSITION_TIME. """
|
and cannot exceed MAX_TRANSITION_TIME. """
|
||||||
@ -56,7 +57,7 @@ class LightTrigger(object):
|
|||||||
self.logger = logging.getLogger(__name__)
|
self.logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
# 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:
|
||||||
ha.track_state_change(eventbus, category,
|
ha.track_state_change(eventbus, category,
|
||||||
DEVICE_STATE_NOT_HOME, DEVICE_STATE_HOME,
|
DEVICE_STATE_NOT_HOME, DEVICE_STATE_HOME,
|
||||||
self._handle_device_state_change)
|
self._handle_device_state_change)
|
||||||
@ -107,8 +108,8 @@ class LightTrigger(object):
|
|||||||
|
|
||||||
for index, light_id in enumerate(self.light_control.light_ids):
|
for index, light_id in enumerate(self.light_control.light_ids):
|
||||||
ha.track_time_change(self.eventbus, turn_on(light_id),
|
ha.track_time_change(self.eventbus, turn_on(light_id),
|
||||||
point_in_time=start_point +
|
point_in_time=(start_point +
|
||||||
index * LIGHT_TRANSITION_TIME)
|
index * LIGHT_TRANSITION_TIME))
|
||||||
|
|
||||||
def _turn_light_on_before_sunset(self, light_id=None):
|
def _turn_light_on_before_sunset(self, light_id=None):
|
||||||
""" Helper function to turn on lights slowly if there
|
""" Helper function to turn on lights slowly if there
|
||||||
@ -162,7 +163,6 @@ class LightTrigger(object):
|
|||||||
# will all the following then, break.
|
# will all the following then, break.
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
||||||
# Did all devices leave the house?
|
# Did all devices leave the house?
|
||||||
elif (category == STATE_CATEGORY_ALL_DEVICES and
|
elif (category == STATE_CATEGORY_ALL_DEVICES and
|
||||||
new_state['state'] == DEVICE_STATE_NOT_HOME and lights_are_on):
|
new_state['state'] == DEVICE_STATE_NOT_HOME and lights_are_on):
|
||||||
@ -195,8 +195,8 @@ class HueLightControl(object):
|
|||||||
try:
|
try:
|
||||||
import phue
|
import phue
|
||||||
except ImportError:
|
except ImportError:
|
||||||
logging.getLogger(__name__).exception(("HueLightControl:"
|
logging.getLogger(__name__).exception(
|
||||||
"Error while importing dependency phue."))
|
"HueLightControl: Error while importing dependency phue.")
|
||||||
|
|
||||||
self.success_init = False
|
self.success_init = False
|
||||||
|
|
||||||
@ -208,7 +208,6 @@ class HueLightControl(object):
|
|||||||
|
|
||||||
self.success_init = True
|
self.success_init = True
|
||||||
|
|
||||||
|
|
||||||
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:
|
||||||
@ -217,7 +216,6 @@ class HueLightControl(object):
|
|||||||
else:
|
else:
|
||||||
return self.bridge.get_light(light_id, 'on')
|
return self.bridge.get_light(light_id, 'on')
|
||||||
|
|
||||||
|
|
||||||
def turn_light_on(self, light_id=None, transition_seconds=None):
|
def turn_light_on(self, light_id=None, transition_seconds=None):
|
||||||
""" Turn the specified or all lights on. """
|
""" Turn the specified or all lights on. """
|
||||||
if not light_id:
|
if not light_id:
|
||||||
@ -226,12 +224,11 @@ class HueLightControl(object):
|
|||||||
command = {'on': True, 'xy': [0.5119, 0.4147], 'bri': 164}
|
command = {'on': True, 'xy': [0.5119, 0.4147], 'bri': 164}
|
||||||
|
|
||||||
if transition_seconds:
|
if transition_seconds:
|
||||||
command['transitiontime'] = _hue_process_transition_time(
|
command['transitiontime'] = \
|
||||||
transition_seconds)
|
_hue_process_transition_time(transition_seconds)
|
||||||
|
|
||||||
self.bridge.set_light(light_id, command)
|
self.bridge.set_light(light_id, command)
|
||||||
|
|
||||||
|
|
||||||
def turn_light_off(self, light_id=None, transition_seconds=None):
|
def turn_light_off(self, light_id=None, transition_seconds=None):
|
||||||
""" Turn the specified or all lights off. """
|
""" Turn the specified or all lights off. """
|
||||||
if not light_id:
|
if not light_id:
|
||||||
@ -240,8 +237,8 @@ class HueLightControl(object):
|
|||||||
command = {'on': False}
|
command = {'on': False}
|
||||||
|
|
||||||
if transition_seconds:
|
if transition_seconds:
|
||||||
command['transitiontime'] = _hue_process_transition_time(
|
command['transitiontime'] = \
|
||||||
transition_seconds)
|
_hue_process_transition_time(transition_seconds)
|
||||||
|
|
||||||
self.bridge.set_light(light_id, command)
|
self.bridge.set_light(light_id, command)
|
||||||
|
|
||||||
@ -309,11 +306,11 @@ def setup_file_downloader(eventbus, download_path):
|
|||||||
logger.exception("FileDownloader:ConnectionError occured for {}".
|
logger.exception("FileDownloader:ConnectionError occured for {}".
|
||||||
format(event.data['url']))
|
format(event.data['url']))
|
||||||
|
|
||||||
|
|
||||||
eventbus.listen(EVENT_DOWNLOAD_FILE, download_file)
|
eventbus.listen(EVENT_DOWNLOAD_FILE, download_file)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def setup_webbrowser(eventbus):
|
def setup_webbrowser(eventbus):
|
||||||
""" Listen for browse_url events and open
|
""" Listen for browse_url events and open
|
||||||
the url in the default webbrowser. """
|
the url in the default webbrowser. """
|
||||||
@ -325,28 +322,33 @@ def setup_webbrowser(eventbus):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def setup_chromecast(eventbus, host):
|
def setup_chromecast(eventbus, host):
|
||||||
""" Listen for chromecast events. """
|
""" Listen for chromecast events. """
|
||||||
from homeassistant.packages import pychromecast
|
from homeassistant.packages import pychromecast
|
||||||
|
|
||||||
eventbus.listen("start_fireplace",
|
eventbus.listen("start_fireplace",
|
||||||
lambda event: pychromecast.play_youtube_video(host, "eyU3bRy2x44"))
|
lambda event:
|
||||||
|
pychromecast.play_youtube_video(host, "eyU3bRy2x44"))
|
||||||
|
|
||||||
eventbus.listen("start_epic_sax",
|
eventbus.listen("start_epic_sax",
|
||||||
lambda event: pychromecast.play_youtube_video(host, "kxopViU98Xo"))
|
lambda event:
|
||||||
|
pychromecast.play_youtube_video(host, "kxopViU98Xo"))
|
||||||
|
|
||||||
eventbus.listen(EVENT_CHROMECAST_YOUTUBE_VIDEO,
|
eventbus.listen(EVENT_CHROMECAST_YOUTUBE_VIDEO,
|
||||||
lambda event: pychromecast.play_youtube_video(host, event.data['video']))
|
lambda event:
|
||||||
|
pychromecast.play_youtube_video(host, event.data['video']))
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def setup_media_buttons(eventbus):
|
def setup_media_buttons(eventbus):
|
||||||
""" Listen for keyboard events. """
|
""" Listen for keyboard events. """
|
||||||
try:
|
try:
|
||||||
import pykeyboard
|
import pykeyboard
|
||||||
except ImportError:
|
except ImportError:
|
||||||
logging.getLogger(__name__).exception(("MediaButtons:"
|
logging.getLogger(__name__).exception(
|
||||||
"Error while importing dependency PyUserInput."))
|
"MediaButtons: Error while importing dependency PyUserInput.")
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -354,21 +356,27 @@ def setup_media_buttons(eventbus):
|
|||||||
keyboard.special_key_assignment()
|
keyboard.special_key_assignment()
|
||||||
|
|
||||||
eventbus.listen(EVENT_KEYBOARD_VOLUME_UP,
|
eventbus.listen(EVENT_KEYBOARD_VOLUME_UP,
|
||||||
lambda event: keyboard.tap_key(keyboard.volume_up_key))
|
lambda event:
|
||||||
|
keyboard.tap_key(keyboard.volume_up_key))
|
||||||
|
|
||||||
eventbus.listen(EVENT_KEYBOARD_VOLUME_DOWN,
|
eventbus.listen(EVENT_KEYBOARD_VOLUME_DOWN,
|
||||||
lambda event: keyboard.tap_key(keyboard.volume_down_key))
|
lambda event:
|
||||||
|
keyboard.tap_key(keyboard.volume_down_key))
|
||||||
|
|
||||||
eventbus.listen(EVENT_KEYBOARD_VOLUME_MUTE,
|
eventbus.listen(EVENT_KEYBOARD_VOLUME_MUTE,
|
||||||
lambda event: keyboard.tap_key(keyboard.volume_mute_key))
|
lambda event:
|
||||||
|
keyboard.tap_key(keyboard.volume_mute_key))
|
||||||
|
|
||||||
eventbus.listen(EVENT_KEYBOARD_MEDIA_PLAY_PAUSE,
|
eventbus.listen(EVENT_KEYBOARD_MEDIA_PLAY_PAUSE,
|
||||||
lambda event: keyboard.tap_key(keyboard.media_play_pause_key))
|
lambda event:
|
||||||
|
keyboard.tap_key(keyboard.media_play_pause_key))
|
||||||
|
|
||||||
eventbus.listen(EVENT_KEYBOARD_MEDIA_NEXT_TRACK,
|
eventbus.listen(EVENT_KEYBOARD_MEDIA_NEXT_TRACK,
|
||||||
lambda event: keyboard.tap_key(keyboard.media_next_track_key))
|
lambda event:
|
||||||
|
keyboard.tap_key(keyboard.media_next_track_key))
|
||||||
|
|
||||||
eventbus.listen(EVENT_KEYBOARD_MEDIA_PREV_TRACK,
|
eventbus.listen(EVENT_KEYBOARD_MEDIA_PREV_TRACK,
|
||||||
lambda event: keyboard.tap_key(keyboard.media_prev_track_key))
|
lambda event:
|
||||||
|
keyboard.tap_key(keyboard.media_prev_track_key))
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
@ -10,6 +10,7 @@ import homeassistant.observers as observers
|
|||||||
import homeassistant.actors as actors
|
import homeassistant.actors as actors
|
||||||
import homeassistant.httpinterface as httpinterface
|
import homeassistant.httpinterface as httpinterface
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=too-many-branches
|
# pylint: disable=too-many-branches
|
||||||
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
|
||||||
@ -49,28 +50,27 @@ def from_config_file(config_path):
|
|||||||
else:
|
else:
|
||||||
device_scanner = None
|
device_scanner = None
|
||||||
|
|
||||||
|
|
||||||
# Device Tracker
|
# Device Tracker
|
||||||
if device_scanner:
|
if device_scanner:
|
||||||
device_tracker = observers.DeviceTracker(eventbus, statemachine,
|
device_tracker = observers.DeviceTracker(
|
||||||
device_scanner)
|
eventbus, statemachine, device_scanner)
|
||||||
|
|
||||||
statusses.append(("Device Tracker", True))
|
statusses.append(("Device Tracker", True))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
device_tracker = None
|
device_tracker = None
|
||||||
|
|
||||||
|
|
||||||
# Sun tracker
|
# Sun tracker
|
||||||
if config.has_option("common", "latitude") and \
|
if config.has_option("common", "latitude") and \
|
||||||
config.has_option("common", "longitude"):
|
config.has_option("common", "longitude"):
|
||||||
|
|
||||||
statusses.append(("Weather - Ephem",
|
statusses.append(("Weather - Ephem",
|
||||||
observers.track_sun(eventbus, statemachine,
|
observers.track_sun(
|
||||||
|
eventbus, statemachine,
|
||||||
config.get("common", "latitude"),
|
config.get("common", "latitude"),
|
||||||
config.get("common", "longitude"))))
|
config.get("common", "longitude"))))
|
||||||
|
|
||||||
|
# --------------------------
|
||||||
# Init actors
|
# Init actors
|
||||||
# Light control
|
# Light control
|
||||||
if config.has_section("hue"):
|
if config.has_section("hue"):
|
||||||
@ -84,7 +84,6 @@ def from_config_file(config_path):
|
|||||||
else:
|
else:
|
||||||
light_control = None
|
light_control = None
|
||||||
|
|
||||||
|
|
||||||
# Light trigger
|
# Light trigger
|
||||||
if light_control:
|
if light_control:
|
||||||
actors.LightTrigger(eventbus, statemachine,
|
actors.LightTrigger(eventbus, statemachine,
|
||||||
@ -92,27 +91,25 @@ def from_config_file(config_path):
|
|||||||
|
|
||||||
statusses.append(("Light Trigger", True))
|
statusses.append(("Light Trigger", True))
|
||||||
|
|
||||||
|
|
||||||
if config.has_option("chromecast", "host"):
|
if config.has_option("chromecast", "host"):
|
||||||
statusses.append(("Chromecast", actors.setup_chromecast(eventbus,
|
statusses.append(("Chromecast",
|
||||||
config.get("chromecast", "host"))))
|
actors.setup_chromecast(
|
||||||
|
eventbus, config.get("chromecast", "host"))))
|
||||||
|
|
||||||
if config.has_option("downloader", "download_dir"):
|
if config.has_option("downloader", "download_dir"):
|
||||||
result = actors.setup_file_downloader(eventbus,
|
result = actors.setup_file_downloader(
|
||||||
config.get("downloader", "download_dir"))
|
eventbus, config.get("downloader", "download_dir"))
|
||||||
|
|
||||||
statusses.append(("Downloader", result))
|
statusses.append(("Downloader", result))
|
||||||
|
|
||||||
|
|
||||||
statusses.append(("Webbrowser", actors.setup_webbrowser(eventbus)))
|
statusses.append(("Webbrowser", actors.setup_webbrowser(eventbus)))
|
||||||
|
|
||||||
statusses.append(("Media Buttons", actors.setup_media_buttons(eventbus)))
|
statusses.append(("Media Buttons", actors.setup_media_buttons(eventbus)))
|
||||||
|
|
||||||
|
|
||||||
# Init HTTP interface
|
# Init HTTP interface
|
||||||
if config.has_option("httpinterface", "api_password"):
|
if config.has_option("httpinterface", "api_password"):
|
||||||
httpinterface.HTTPInterface(eventbus, statemachine,
|
httpinterface.HTTPInterface(
|
||||||
|
eventbus, statemachine,
|
||||||
config.get("httpinterface", "api_password"))
|
config.get("httpinterface", "api_password"))
|
||||||
|
|
||||||
statusses.append(("HTTPInterface", True))
|
statusses.append(("HTTPInterface", True))
|
||||||
|
@ -100,6 +100,7 @@ URL_API_EVENTS_EVENT = "/api/events/{}"
|
|||||||
|
|
||||||
URL_STATIC = "/static/{}"
|
URL_STATIC = "/static/{}"
|
||||||
|
|
||||||
|
|
||||||
class HTTPInterface(threading.Thread):
|
class HTTPInterface(threading.Thread):
|
||||||
""" Provides an HTTP interface for Home Assistant. """
|
""" Provides an HTTP interface for Home Assistant. """
|
||||||
|
|
||||||
@ -133,6 +134,7 @@ class HTTPInterface(threading.Thread):
|
|||||||
|
|
||||||
self.server.serve_forever()
|
self.server.serve_forever()
|
||||||
|
|
||||||
|
|
||||||
class RequestHandler(BaseHTTPRequestHandler):
|
class RequestHandler(BaseHTTPRequestHandler):
|
||||||
""" Handles incoming HTTP requests """
|
""" Handles incoming HTTP requests """
|
||||||
|
|
||||||
@ -201,7 +203,6 @@ class RequestHandler(BaseHTTPRequestHandler):
|
|||||||
# pylint: disable=maybe-no-member
|
# pylint: disable=maybe-no-member
|
||||||
path_match = t_path.match(url.path)
|
path_match = t_path.match(url.path)
|
||||||
|
|
||||||
|
|
||||||
if path_match and method == t_method:
|
if path_match and method == t_method:
|
||||||
# Call the method
|
# Call the method
|
||||||
handle_request_method = getattr(self, t_handler)
|
handle_request_method = getattr(self, t_handler)
|
||||||
@ -210,7 +211,6 @@ class RequestHandler(BaseHTTPRequestHandler):
|
|||||||
elif path_match:
|
elif path_match:
|
||||||
path_matched_but_not_method = True
|
path_matched_but_not_method = True
|
||||||
|
|
||||||
|
|
||||||
# Did we find a handler for the incoming request?
|
# Did we find a handler for the incoming request?
|
||||||
if handle_request_method:
|
if handle_request_method:
|
||||||
|
|
||||||
@ -241,8 +241,9 @@ class RequestHandler(BaseHTTPRequestHandler):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
elif self.use_json:
|
elif self.use_json:
|
||||||
self._message("API password missing or incorrect.",
|
self._message(
|
||||||
HTTP_UNAUTHORIZED)
|
"API password missing or incorrect.", HTTP_UNAUTHORIZED)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.send_response(HTTP_OK)
|
self.send_response(HTTP_OK)
|
||||||
self.send_header('Content-type', 'text/html')
|
self.send_header('Content-type', 'text/html')
|
||||||
@ -297,8 +298,9 @@ class RequestHandler(BaseHTTPRequestHandler):
|
|||||||
|
|
||||||
# Flash message support
|
# Flash message support
|
||||||
if self.server.flash_message:
|
if self.server.flash_message:
|
||||||
write(("<div class='row'><div class='alert alert-success'>"
|
write(("<div class='row'><div class='col-xs-12'>"
|
||||||
"{}</div></div>").format(self.server.flash_message))
|
"<div class='alert alert-success'>"
|
||||||
|
"{}</div></div></div>").format(self.server.flash_message))
|
||||||
|
|
||||||
self.server.flash_message = None
|
self.server.flash_message = None
|
||||||
|
|
||||||
@ -332,13 +334,12 @@ class RequestHandler(BaseHTTPRequestHandler):
|
|||||||
|
|
||||||
write(("<tr>"
|
write(("<tr>"
|
||||||
"<td>{}</td><td>{}</td><td>{}</td><td>{}</td>"
|
"<td>{}</td><td>{}</td><td>{}</td><td>{}</td>"
|
||||||
"</tr>").
|
"</tr>").format(
|
||||||
format(category,
|
category,
|
||||||
state['state'],
|
state['state'],
|
||||||
attributes,
|
attributes,
|
||||||
state['last_changed']))
|
state['last_changed']))
|
||||||
|
|
||||||
|
|
||||||
# Change state form
|
# Change state form
|
||||||
write(("<tr><td><input name='category' class='form-control' "
|
write(("<tr><td><input name='category' class='form-control' "
|
||||||
"placeholder='Category'></td>"
|
"placeholder='Category'></td>"
|
||||||
@ -354,8 +355,6 @@ class RequestHandler(BaseHTTPRequestHandler):
|
|||||||
|
|
||||||
"</div></div>"))
|
"</div></div>"))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Describe event bus:
|
# Describe event bus:
|
||||||
write(("<div class='row'>"
|
write(("<div class='row'>"
|
||||||
"<div class='col-xs-6'>"
|
"<div class='col-xs-6'>"
|
||||||
@ -365,7 +364,8 @@ class RequestHandler(BaseHTTPRequestHandler):
|
|||||||
"<table class='table'>"
|
"<table class='table'>"
|
||||||
"<tr><th>Event Type</th><th>Listeners</th></tr>"))
|
"<tr><th>Event Type</th><th>Listeners</th></tr>"))
|
||||||
|
|
||||||
for event_type, count in sorted(self.server.eventbus.listeners.items()):
|
for event_type, count in sorted(
|
||||||
|
self.server.eventbus.listeners.items()):
|
||||||
write("<tr><td>{}</td><td>{}</td></tr>".format(event_type, count))
|
write("<tr><td>{}</td><td>{}</td></tr>".format(event_type, count))
|
||||||
|
|
||||||
write(("</table></div></div>"
|
write(("</table></div></div>"
|
||||||
@ -388,7 +388,6 @@ class RequestHandler(BaseHTTPRequestHandler):
|
|||||||
"</div>"
|
"</div>"
|
||||||
"</div>"
|
"</div>"
|
||||||
|
|
||||||
|
|
||||||
"<div class='form-group'>"
|
"<div class='form-group'>"
|
||||||
"<label for='event_data' class='col-xs-3 control-label'>"
|
"<label for='event_data' class='col-xs-3 control-label'>"
|
||||||
"Event data</label>"
|
"Event data</label>"
|
||||||
@ -405,12 +404,10 @@ class RequestHandler(BaseHTTPRequestHandler):
|
|||||||
"Fire Event</button>"
|
"Fire Event</button>"
|
||||||
"</div>"
|
"</div>"
|
||||||
"</div>"
|
"</div>"
|
||||||
|
|
||||||
"</form>"
|
"</form>"
|
||||||
"</div></div></div>"
|
"</div></div></div>"
|
||||||
"</div>").format(self.server.api_password))
|
"</div>").format(self.server.api_password))
|
||||||
|
|
||||||
|
|
||||||
write("</div></body></html>")
|
write("</div></body></html>")
|
||||||
|
|
||||||
# pylint: disable=invalid-name
|
# pylint: disable=invalid-name
|
||||||
@ -448,20 +445,21 @@ class RequestHandler(BaseHTTPRequestHandler):
|
|||||||
state['category'] = category
|
state['category'] = category
|
||||||
|
|
||||||
self._write_json(state, status_code=HTTP_CREATED,
|
self._write_json(state, status_code=HTTP_CREATED,
|
||||||
location=URL_API_STATES_CATEGORY.format(category))
|
location=
|
||||||
|
URL_API_STATES_CATEGORY.format(category))
|
||||||
else:
|
else:
|
||||||
self._message("State of {} changed to {}".format(
|
self._message(
|
||||||
category, new_state))
|
"State of {} changed to {}".format(category, new_state))
|
||||||
|
|
||||||
except KeyError:
|
except KeyError:
|
||||||
# If new_state don't exist in post data
|
# If new_state don't exist in post data
|
||||||
self._message("No new_state submitted.",
|
self._message(
|
||||||
HTTP_BAD_REQUEST)
|
"No new_state submitted.", HTTP_BAD_REQUEST)
|
||||||
|
|
||||||
except ValueError:
|
except ValueError:
|
||||||
# Occurs during error parsing json
|
# Occurs during error parsing json
|
||||||
self._message("Invalid JSON for attributes",
|
self._message(
|
||||||
HTTP_UNPROCESSABLE_ENTITY)
|
"Invalid JSON for attributes", HTTP_UNPROCESSABLE_ENTITY)
|
||||||
|
|
||||||
# pylint: disable=invalid-name
|
# pylint: disable=invalid-name
|
||||||
def _handle_fire_event(self, path_match, data):
|
def _handle_fire_event(self, path_match, data):
|
||||||
@ -494,8 +492,8 @@ class RequestHandler(BaseHTTPRequestHandler):
|
|||||||
|
|
||||||
except ValueError:
|
except ValueError:
|
||||||
# Occurs during error parsing json
|
# Occurs during error parsing json
|
||||||
self._message("Invalid JSON for event_data",
|
self._message(
|
||||||
HTTP_UNPROCESSABLE_ENTITY)
|
"Invalid JSON for event_data", HTTP_UNPROCESSABLE_ENTITY)
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
# pylint: disable=unused-argument
|
||||||
def _handle_get_api_states(self, path_match, data):
|
def _handle_get_api_states(self, path_match, data):
|
||||||
@ -560,8 +558,11 @@ class RequestHandler(BaseHTTPRequestHandler):
|
|||||||
def _redirect(self, location):
|
def _redirect(self, location):
|
||||||
""" Helper method to redirect caller. """
|
""" Helper method to redirect caller. """
|
||||||
self.send_response(HTTP_MOVED_PERMANENTLY)
|
self.send_response(HTTP_MOVED_PERMANENTLY)
|
||||||
self.send_header("Location", "{}?api_password={}".
|
|
||||||
format(location, self.server.api_password))
|
self.send_header(
|
||||||
|
"Location", "{}?api_password={}".format(
|
||||||
|
location, self.server.api_password))
|
||||||
|
|
||||||
self.end_headers()
|
self.end_headers()
|
||||||
|
|
||||||
def _write_json(self, data=None, status_code=HTTP_OK, location=None):
|
def _write_json(self, data=None, status_code=HTTP_OK, location=None):
|
||||||
|
@ -72,8 +72,9 @@ def track_sun(eventbus, statemachine, latitude, longitude):
|
|||||||
new_state = SUN_STATE_BELOW_HORIZON
|
new_state = SUN_STATE_BELOW_HORIZON
|
||||||
next_change = next_rising
|
next_change = next_rising
|
||||||
|
|
||||||
logger.info("Sun:{}. Next change: {}".
|
logger.info(
|
||||||
format(new_state, next_change.strftime("%H:%M")))
|
"Sun:{}. Next change: {}".format(new_state,
|
||||||
|
next_change.strftime("%H:%M")))
|
||||||
|
|
||||||
state_attributes = {
|
state_attributes = {
|
||||||
STATE_ATTRIBUTE_NEXT_SUN_RISING: ha.datetime_to_str(next_rising),
|
STATE_ATTRIBUTE_NEXT_SUN_RISING: ha.datetime_to_str(next_rising),
|
||||||
@ -90,6 +91,7 @@ def track_sun(eventbus, statemachine, latitude, longitude):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
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. """
|
||||||
|
|
||||||
@ -154,18 +156,22 @@ class DeviceTracker(object):
|
|||||||
|
|
||||||
except KeyError:
|
except KeyError:
|
||||||
self.invalid_known_devices_file = False
|
self.invalid_known_devices_file = False
|
||||||
self.logger.warning(("Invalid {} found. "
|
self.logger.warning((
|
||||||
|
"Invalid {} found. "
|
||||||
"We won't update it with new found devices.").
|
"We won't update it with new found devices.").
|
||||||
format(KNOWN_DEVICES_FILE))
|
format(KNOWN_DEVICES_FILE))
|
||||||
|
|
||||||
if len(self.device_state_categories()) == 0:
|
if len(self.device_state_categories) == 0:
|
||||||
self.logger.warning("No devices to track. Please update {}.".
|
self.logger.warning(
|
||||||
format(KNOWN_DEVICES_FILE))
|
"No devices to track. Please update {}.".format(
|
||||||
|
KNOWN_DEVICES_FILE))
|
||||||
|
|
||||||
ha.track_time_change(eventbus,
|
ha.track_time_change(eventbus,
|
||||||
lambda time: self.update_devices(device_scanner.scan_devices()))
|
lambda time:
|
||||||
|
self.update_devices(
|
||||||
|
device_scanner.scan_devices()))
|
||||||
|
|
||||||
|
@property
|
||||||
def device_state_categories(self):
|
def device_state_categories(self):
|
||||||
""" Returns a list containing all categories
|
""" Returns a list containing all categories
|
||||||
that are maintained for devices. """
|
that are maintained for devices. """
|
||||||
@ -203,7 +209,7 @@ class DeviceTracker(object):
|
|||||||
|
|
||||||
# Get the currently used statuses
|
# Get the currently used statuses
|
||||||
states_of_devices = [self.statemachine.get_state(category)['state']
|
states_of_devices = [self.statemachine.get_state(category)['state']
|
||||||
for category in self.device_state_categories()]
|
for category in self.device_state_categories]
|
||||||
|
|
||||||
# Update the all devices category
|
# Update the all devices category
|
||||||
all_devices_state = (DEVICE_STATE_HOME if DEVICE_STATE_HOME
|
all_devices_state = (DEVICE_STATE_HOME if DEVICE_STATE_HOME
|
||||||
@ -226,7 +232,8 @@ class DeviceTracker(object):
|
|||||||
is_new_file = not os.path.isfile(KNOWN_DEVICES_FILE)
|
is_new_file = not os.path.isfile(KNOWN_DEVICES_FILE)
|
||||||
|
|
||||||
with open(KNOWN_DEVICES_FILE, 'a') as outp:
|
with open(KNOWN_DEVICES_FILE, 'a') as outp:
|
||||||
self.logger.info(("DeviceTracker:Found {} new devices,"
|
self.logger.info((
|
||||||
|
"DeviceTracker:Found {} new devices,"
|
||||||
" updating {}").format(len(unknown_devices),
|
" updating {}").format(len(unknown_devices),
|
||||||
KNOWN_DEVICES_FILE))
|
KNOWN_DEVICES_FILE))
|
||||||
|
|
||||||
@ -237,8 +244,8 @@ class DeviceTracker(object):
|
|||||||
|
|
||||||
for device in unknown_devices:
|
for device in unknown_devices:
|
||||||
# See if the device scanner knows the name
|
# See if the device scanner knows the name
|
||||||
temp_name = self.device_scanner.get_device_name(
|
temp_name = \
|
||||||
device)
|
self.device_scanner.get_device_name(device)
|
||||||
|
|
||||||
name = temp_name if temp_name else "unknown_device"
|
name = temp_name if temp_name else "unknown_device"
|
||||||
|
|
||||||
@ -247,9 +254,10 @@ class DeviceTracker(object):
|
|||||||
'track': False}
|
'track': False}
|
||||||
|
|
||||||
except IOError:
|
except IOError:
|
||||||
self.logger.exception(("DeviceTracker:Error updating {}"
|
self.logger.exception((
|
||||||
"with {} new devices").format(KNOWN_DEVICES_FILE,
|
"DeviceTracker:Error updating {}"
|
||||||
len(unknown_devices)))
|
"with {} new devices").format(
|
||||||
|
KNOWN_DEVICES_FILE, len(unknown_devices)))
|
||||||
|
|
||||||
self.lock.release()
|
self.lock.release()
|
||||||
|
|
||||||
@ -298,7 +306,6 @@ 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 len(filter_named) == 0 or filter_named[0] == "":
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
@ -321,15 +328,16 @@ class TomatoDeviceScanner(object):
|
|||||||
|
|
||||||
# Calling and parsing the Tomato api here. We only need the
|
# Calling and parsing the Tomato api here. We only need the
|
||||||
# wldev and dhcpd_lease values. For API description see:
|
# wldev and dhcpd_lease values. For API description see:
|
||||||
# http://paulusschoutsen.nl/blog/2013/10/tomato-api-documentation/
|
# http://paulusschoutsen.nl/
|
||||||
|
# blog/2013/10/tomato-api-documentation/
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
|
|
||||||
for param, value in self.parse_api_pattern.findall(
|
for param, value in \
|
||||||
response.text):
|
self.parse_api_pattern.findall(response.text):
|
||||||
|
|
||||||
if param == 'wldev' or param == 'dhcpd_lease':
|
if param == 'wldev' or param == 'dhcpd_lease':
|
||||||
self.last_results[param] = json.loads(value.
|
self.last_results[param] = \
|
||||||
replace("'",'"'))
|
json.loads(value.replace("'", '"'))
|
||||||
|
|
||||||
self.date_updated = datetime.now()
|
self.date_updated = datetime.now()
|
||||||
|
|
||||||
@ -337,7 +345,8 @@ class TomatoDeviceScanner(object):
|
|||||||
|
|
||||||
elif response.status_code == 401:
|
elif response.status_code == 401:
|
||||||
# Authentication error
|
# Authentication error
|
||||||
self.logger.exception(("Tomato:Failed to authenticate, "
|
self.logger.exception((
|
||||||
|
"Tomato:Failed to authenticate, "
|
||||||
"please check your username and password"))
|
"please check your username and password"))
|
||||||
|
|
||||||
return False
|
return False
|
||||||
@ -345,7 +354,8 @@ class TomatoDeviceScanner(object):
|
|||||||
except requests.exceptions.ConnectionError:
|
except requests.exceptions.ConnectionError:
|
||||||
# We get this if we could not connect to the router or
|
# We get this if we could not connect to the router or
|
||||||
# an invalid http_id was supplied
|
# an invalid http_id was supplied
|
||||||
self.logger.exception(("Tomato:Failed to connect to the router"
|
self.logger.exception((
|
||||||
|
"Tomato:Failed to connect to the router"
|
||||||
" or invalid http_id supplied"))
|
" or invalid http_id supplied"))
|
||||||
|
|
||||||
return False
|
return False
|
||||||
@ -353,15 +363,15 @@ class TomatoDeviceScanner(object):
|
|||||||
except requests.exceptions.Timeout:
|
except requests.exceptions.Timeout:
|
||||||
# We get this if we could not connect to the router or
|
# We get this if we could not connect to the router or
|
||||||
# an invalid http_id was supplied
|
# an invalid http_id was supplied
|
||||||
self.logger.exception(("Tomato:Connection to the router "
|
self.logger.exception(
|
||||||
"timed out"))
|
"Tomato:Connection to the router timed out")
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
except ValueError:
|
except ValueError:
|
||||||
# If json decoder could not parse the response
|
# If json decoder could not parse the response
|
||||||
self.logger.exception(("Tomato:Failed to parse response "
|
self.logger.exception(
|
||||||
"from router"))
|
"Tomato:Failed to parse response from router")
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@ import homeassistant.httpinterface as hah
|
|||||||
METHOD_GET = "get"
|
METHOD_GET = "get"
|
||||||
METHOD_POST = "post"
|
METHOD_POST = "post"
|
||||||
|
|
||||||
|
|
||||||
def _setup_call_api(host, port, api_password):
|
def _setup_call_api(host, port, api_password):
|
||||||
""" Helper method to setup a call api method. """
|
""" Helper method to setup a call api method. """
|
||||||
port = port or hah.SERVER_PORT
|
port = port or hah.SERVER_PORT
|
||||||
@ -117,6 +118,7 @@ class EventBus(ha.EventBus):
|
|||||||
|
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
class StateMachine(ha.StateMachine):
|
class StateMachine(ha.StateMachine):
|
||||||
""" Drop-in replacement for a normal statemachine that communicates with a
|
""" Drop-in replacement for a normal statemachine that communicates with a
|
||||||
remote statemachine.
|
remote statemachine.
|
||||||
@ -193,9 +195,9 @@ class StateMachine(ha.StateMachine):
|
|||||||
if req.status_code == 200:
|
if req.status_code == 200:
|
||||||
data = req.json()
|
data = req.json()
|
||||||
|
|
||||||
return ha.create_state(data['state'],
|
return ha.create_state(data['state'], data['attributes'],
|
||||||
data['attributes'],
|
ha.str_to_datetime(
|
||||||
ha.str_to_datetime(data['last_changed']))
|
data['last_changed']))
|
||||||
|
|
||||||
elif req.status_code == 422:
|
elif req.status_code == 422:
|
||||||
# Category does not exist
|
# Category does not exist
|
||||||
|
@ -19,14 +19,17 @@ API_PASSWORD = "test1234"
|
|||||||
|
|
||||||
HTTP_BASE_URL = "http://127.0.0.1:{}".format(hah.SERVER_PORT)
|
HTTP_BASE_URL = "http://127.0.0.1:{}".format(hah.SERVER_PORT)
|
||||||
|
|
||||||
|
|
||||||
def _url(path=""):
|
def _url(path=""):
|
||||||
""" Helper method to generate urls. """
|
""" Helper method to generate urls. """
|
||||||
return HTTP_BASE_URL + path
|
return HTTP_BASE_URL + path
|
||||||
|
|
||||||
|
|
||||||
class HAHelper(object): # pylint: disable=too-few-public-methods
|
class HAHelper(object): # pylint: disable=too-few-public-methods
|
||||||
""" Helper class to keep track of current running HA instance. """
|
""" Helper class to keep track of current running HA instance. """
|
||||||
core = None
|
core = None
|
||||||
|
|
||||||
|
|
||||||
def ensure_homeassistant_started():
|
def ensure_homeassistant_started():
|
||||||
""" Ensures home assistant is started. """
|
""" Ensures home assistant is started. """
|
||||||
|
|
||||||
@ -49,6 +52,7 @@ def ensure_homeassistant_started():
|
|||||||
|
|
||||||
return HAHelper.core['eventbus'], HAHelper.core['statemachine']
|
return HAHelper.core['eventbus'], HAHelper.core['statemachine']
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=too-many-public-methods
|
# pylint: disable=too-many-public-methods
|
||||||
class TestHTTPInterface(unittest.TestCase):
|
class TestHTTPInterface(unittest.TestCase):
|
||||||
""" Test the HTTP debug interface and API. """
|
""" Test the HTTP debug interface and API. """
|
||||||
@ -69,7 +73,6 @@ class TestHTTPInterface(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertNotEqual(without_pw.text, with_pw.text)
|
self.assertNotEqual(without_pw.text, with_pw.text)
|
||||||
|
|
||||||
|
|
||||||
def test_api_password(self):
|
def test_api_password(self):
|
||||||
""" Test if we get access denied if we omit or provide
|
""" Test if we get access denied if we omit or provide
|
||||||
a wrong api password. """
|
a wrong api password. """
|
||||||
@ -84,7 +87,6 @@ class TestHTTPInterface(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertEqual(req.status_code, 401)
|
self.assertEqual(req.status_code, 401)
|
||||||
|
|
||||||
|
|
||||||
def test_debug_change_state(self):
|
def test_debug_change_state(self):
|
||||||
""" Test if we can change a state from the debug interface. """
|
""" Test if we can change a state from the debug interface. """
|
||||||
self.statemachine.set_state("test.test", "not_to_be_set_state")
|
self.statemachine.set_state("test.test", "not_to_be_set_state")
|
||||||
@ -120,8 +122,6 @@ class TestHTTPInterface(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertEqual(len(test_value), 1)
|
self.assertEqual(len(test_value), 1)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def test_api_list_state_categories(self):
|
def test_api_list_state_categories(self):
|
||||||
""" Test if the debug interface allows us to list state categories. """
|
""" Test if the debug interface allows us to list state categories. """
|
||||||
req = requests.get(_url(hah.URL_API_STATES),
|
req = requests.get(_url(hah.URL_API_STATES),
|
||||||
@ -132,7 +132,6 @@ class TestHTTPInterface(unittest.TestCase):
|
|||||||
self.assertEqual(self.statemachine.categories,
|
self.assertEqual(self.statemachine.categories,
|
||||||
data['categories'])
|
data['categories'])
|
||||||
|
|
||||||
|
|
||||||
def test_api_get_state(self):
|
def test_api_get_state(self):
|
||||||
""" Test if the debug interface allows us to get a state. """
|
""" Test if the debug interface allows us to get a state. """
|
||||||
req = requests.get(
|
req = requests.get(
|
||||||
@ -230,7 +229,6 @@ class TestHTTPInterface(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertEqual(len(test_value), 1)
|
self.assertEqual(len(test_value), 1)
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=invalid-name
|
# pylint: disable=invalid-name
|
||||||
def test_api_fire_event_with_invalid_json(self):
|
def test_api_fire_event_with_invalid_json(self):
|
||||||
""" Test if the API allows us to fire an event. """
|
""" Test if the API allows us to fire an event. """
|
||||||
@ -247,7 +245,6 @@ class TestHTTPInterface(unittest.TestCase):
|
|||||||
data={"event_data": 'not json',
|
data={"event_data": 'not json',
|
||||||
"api_password": API_PASSWORD})
|
"api_password": API_PASSWORD})
|
||||||
|
|
||||||
|
|
||||||
# It shouldn't but if it fires, allow the event to take place
|
# It shouldn't but if it fires, allow the event to take place
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
@ -263,6 +260,7 @@ class TestHTTPInterface(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertEqual(data['listeners'], self.eventbus.listeners)
|
self.assertEqual(data['listeners'], self.eventbus.listeners)
|
||||||
|
|
||||||
|
|
||||||
class TestRemote(unittest.TestCase):
|
class TestRemote(unittest.TestCase):
|
||||||
""" Test the homeassistant.remote module. """
|
""" Test the homeassistant.remote module. """
|
||||||
|
|
||||||
@ -283,7 +281,6 @@ class TestRemote(unittest.TestCase):
|
|||||||
self.assertEqual(self.statemachine.categories,
|
self.assertEqual(self.statemachine.categories,
|
||||||
self.remote_sm.categories)
|
self.remote_sm.categories)
|
||||||
|
|
||||||
|
|
||||||
def test_remote_sm_get_state(self):
|
def test_remote_sm_get_state(self):
|
||||||
""" Test if the debug interface allows us to list state categories. """
|
""" Test if the debug interface allows us to list state categories. """
|
||||||
remote_state = self.remote_sm.get_state("test")
|
remote_state = self.remote_sm.get_state("test")
|
||||||
@ -294,7 +291,6 @@ class TestRemote(unittest.TestCase):
|
|||||||
self.assertEqual(remote_state['last_changed'], state['last_changed'])
|
self.assertEqual(remote_state['last_changed'], state['last_changed'])
|
||||||
self.assertEqual(remote_state['attributes'], state['attributes'])
|
self.assertEqual(remote_state['attributes'], state['attributes'])
|
||||||
|
|
||||||
|
|
||||||
def test_remote_sm_get_non_existing_state(self):
|
def test_remote_sm_get_non_existing_state(self):
|
||||||
""" Test if the debug interface allows us to list state categories. """
|
""" Test if the debug interface allows us to list state categories. """
|
||||||
self.assertEqual(self.remote_sm.get_state("test_does_not_exist"), None)
|
self.assertEqual(self.remote_sm.get_state("test_does_not_exist"), None)
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
|
||||||
def sanitize_filename(filename):
|
def sanitize_filename(filename):
|
||||||
""" Sanitizes a filename by removing .. / and \\. """
|
""" Sanitizes a filename by removing .. / and \\. """
|
||||||
return re.sub(r"(~|(\.\.)|/|\+)", "", filename)
|
return re.sub(r"(~|(\.\.)|/|\+)", "", filename)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user