Moved stuff away from core. Made component interface more uniform.

This commit is contained in:
Paulus Schoutsen 2014-01-23 22:03:13 -08:00
parent b387f9d9d7
commit 0fc3d359cb
13 changed files with 302 additions and 158 deletions

View file

@ -20,16 +20,9 @@ MATCH_ALL = '*'
DOMAIN = "homeassistant"
STATE_ON = "on"
STATE_OFF = "off"
STATE_NOT_HOME = 'device_not_home'
STATE_HOME = 'device_home'
SERVICE_TURN_ON = "turn_on"
SERVICE_TURN_OFF = "turn_off"
SERVICE_HOMEASSISTANT_STOP = "stop"
EVENT_HOMEASSISTANT_START = "homeassistant.start"
EVENT_HOMEASSISTANT_START = "homeassistant_start"
EVENT_STATE_CHANGED = "state_changed"
EVENT_TIME_CHANGED = "time_changed"
@ -78,21 +71,6 @@ def _matcher(subject, pattern):
return MATCH_ALL == pattern or subject in pattern
def split_entity_id(entity_id):
""" Splits a state entity_id into domain, object_id. """
return entity_id.split(".", 1)
def filter_entity_ids(entity_ids, domain_filter=None, strip_domain=False):
""" Filter a list of entities based on domain. Setting strip_domain
will only return the object_ids. """
return [
split_entity_id(entity_id)[1] if strip_domain else entity_id
for entity_id in entity_ids if
not domain_filter or entity_id.startswith(domain_filter)
]
def track_state_change(bus, entity_id, action, from_state=None, to_state=None):
""" Helper method to track specific state changes. """
from_state = _process_match_param(from_state)

View file

@ -134,7 +134,7 @@ def from_config_file(config_path):
add_status("Downloader", downloader.setup(
bus, get_opt("downloader", "download_dir")))
add_status("General", general.setup(bus, statemachine))
add_status("General", general.setup(bus))
if has_section('browser'):
add_status("Browser", load_module('browser').setup(bus))

View file

@ -6,46 +6,46 @@ Provides functionality to interact with Chromecasts.
"""
import logging
from homeassistant.external import pychromecast
import homeassistant as ha
import homeassistant.util as util
from homeassistant.components import general
DOMAIN = 'chromecast'
DOMAIN = "chromecast"
SERVICE_YOUTUBE_VIDEO = "play_youtube_video"
SERVICE_YOUTUBE_VIDEO = 'play_youtube_video'
ENTITY_ID_FORMAT = DOMAIN + '.{}'
STATE_NO_APP = "none"
STATE_NO_APP = 'no_app'
ATTR_FRIENDLY_NAME = "friendly_name"
ATTR_HOST = "host"
ATTR_STATE = "state"
ATTR_OPTIONS = "options"
ATTR_FRIENDLY_NAME = 'friendly_name'
ATTR_HOST = 'host'
ATTR_STATE = 'state'
ATTR_OPTIONS = 'options'
def turn_off(statemachine, cc_id=None):
""" Exits any running app on the specified ChromeCast and shows
idle screen. Will quit all ChromeCasts if nothing specified. """
def is_on(statemachine, entity_id=None):
""" Returns true if specified ChromeCast entity_id is on.
Will check all chromecasts if no entity_id specified. """
entity_ids = [ENTITY_ID_FORMAT.format(cc_id)] if cc_id \
else ha.filter_entity_ids(statemachine.entity_ids, DOMAIN)
entity_ids = [entity_id] if entity_id \
else util.filter_entity_ids(statemachine.entity_ids, DOMAIN)
for entity_id in entity_ids:
state = statemachine.get_state(entity_id)
if (state and
(state.state != STATE_NO_APP or
state.state != pychromecast.APP_ID_HOME)):
pychromecast.quit_app(state.attributes[ATTR_HOST])
return any(not statemachine.is_state(entity_id, STATE_NO_APP)
for entity_id in entity_ids)
def setup(bus, statemachine, host):
""" Listen for chromecast events. """
logger = logging.getLogger(__name__)
try:
from homeassistant.external import pychromecast
except ImportError:
logger.exception(("Failed to import pychromecast. "
"Did you maybe not cloned the git submodules?"))
return False
logger.info("Getting device status")
device = pychromecast.get_device_status(host)
@ -53,13 +53,30 @@ def setup(bus, statemachine, host):
logger.error("Could not find Chromecast")
return False
entity = ENTITY_ID_FORMAT.format(util.slugify(
device.friendly_name))
entity = ENTITY_ID_FORMAT.format(util.slugify(device.friendly_name))
bus.register_service(DOMAIN, ha.SERVICE_TURN_OFF,
lambda service:
turn_off(statemachine,
service.data.get("cc_id", None)))
if not bus.has_service(DOMAIN, general.SERVICE_TURN_OFF):
def _turn_off_service(service):
""" Service to exit any running app on the specified ChromeCast and
shows idle screen. Will quit all ChromeCasts if nothing specified.
"""
entity_id = service.data.get(general.ATTR_ENTITY_ID)
entity_ids = [entity_id] if entity_id \
else util.filter_entity_ids(statemachine.entity_ids, DOMAIN)
for entity_id in entity_ids:
state = statemachine.get_state(entity_id)
try:
pychromecast.quit_app(state.attributes[ATTR_HOST])
except (AttributeError, KeyError):
# AttributeError: state returned None
# KeyError: ATTR_HOST did not exist
pass
bus.register_service(DOMAIN, general.SERVICE_TURN_OFF,
_turn_off_service)
bus.register_service(DOMAIN, "start_fireplace",
lambda service:
@ -74,7 +91,7 @@ def setup(bus, statemachine, host):
pychromecast.play_youtube_video(
host, service.data['video']))
def update_chromecast_state(time): # pylint: disable=unused-argument
def _update_chromecast_state(time): # pylint: disable=unused-argument
""" Retrieve state of Chromecast and update statemachine. """
logger.info("Updating app status")
status = pychromecast.get_app_status(host)
@ -92,8 +109,8 @@ def setup(bus, statemachine, host):
else:
statemachine.set_state(entity, STATE_NO_APP, {ATTR_HOST: host})
ha.track_time_change(bus, update_chromecast_state)
ha.track_time_change(bus, _update_chromecast_state)
update_chromecast_state(None)
_update_chromecast_state(None)
return True

View file

@ -9,6 +9,7 @@ import logging
from datetime import datetime, timedelta
import homeassistant as ha
import homeassistant.util as util
from . import light, sun, device_tracker, general, group
@ -22,7 +23,7 @@ def setup(bus, statemachine, light_group=None):
logger = logging.getLogger(__name__)
device_entity_ids = ha.filter_entity_ids(statemachine.entity_ids,
device_entity_ids = util.filter_entity_ids(statemachine.entity_ids,
device_tracker.DOMAIN)
if not device_entity_ids:
@ -33,7 +34,7 @@ def setup(bus, statemachine, light_group=None):
light_group = light_group or light.GROUP_NAME_ALL_LIGHTS
# Get the light IDs from the specified group
light_ids = ha.filter_entity_ids(
light_ids = util.filter_entity_ids(
group.get_entity_ids(statemachine, light_group), light.DOMAIN)
if not light_ids:
@ -55,7 +56,7 @@ def setup(bus, statemachine, light_group=None):
def turn_light_on_before_sunset(light_id):
""" Helper function to turn on lights slowly if there
are devices home and the light is not on yet. """
if (device_tracker.is_home(statemachine) and
if (device_tracker.is_on(statemachine) and
not light.is_on(statemachine, light_id)):
light.turn_on(bus, light_id, LIGHT_TRANSITION_TIME.seconds)
@ -80,18 +81,18 @@ def setup(bus, statemachine, light_group=None):
# If the sun is already above horizon
# schedule the time-based pre-sun set event
if sun.is_up(statemachine):
if sun.is_on(statemachine):
handle_sun_rising(None, None, None)
def handle_device_state_change(entity, old_state, new_state):
def _handle_device_state_change(entity, old_state, new_state):
""" Function to handle tracked device state changes. """
lights_are_on = group.is_on(statemachine, light_group)
light_needed = not (lights_are_on or sun.is_up(statemachine))
light_needed = not (lights_are_on or sun.is_on(statemachine))
# Specific device came home ?
if (entity != device_tracker.ENTITY_ID_ALL_DEVICES and
new_state.state == ha.STATE_HOME):
new_state.state == general.STATE_HOME):
# These variables are needed for the elif check
now = datetime.now()
@ -104,6 +105,8 @@ def setup(bus, statemachine, light_group=None):
"Home coming event for {}. Turning lights on".
format(entity))
# Turn on lights directly instead of calling group.turn_on
# So we skip fetching the entity ids again.
for light_id in light_ids:
light.turn_on(bus, light_id)
@ -127,21 +130,21 @@ def setup(bus, statemachine, light_group=None):
# Did all devices leave the house?
elif (entity == device_tracker.ENTITY_ID_ALL_DEVICES and
new_state.state == ha.STATE_NOT_HOME and lights_are_on):
new_state.state == general.STATE_NOT_HOME and lights_are_on):
logger.info(
"Everyone has left but there are devices on. Turning them off")
general.shutdown_devices(bus, statemachine)
light.turn_off(bus, statemachine)
# Track home coming of each seperate device
for entity in device_entity_ids:
ha.track_state_change(bus, entity, handle_device_state_change,
ha.STATE_NOT_HOME, ha.STATE_HOME)
ha.track_state_change(bus, entity, _handle_device_state_change,
general.STATE_NOT_HOME, general.STATE_HOME)
# Track when all devices are gone to shut down lights
ha.track_state_change(bus, device_tracker.ENTITY_ID_ALL_DEVICES,
handle_device_state_change, ha.STATE_HOME,
ha.STATE_NOT_HOME)
_handle_device_state_change, general.STATE_HOME,
general.STATE_NOT_HOME)
return True

View file

@ -16,9 +16,7 @@ import requests
import homeassistant as ha
import homeassistant.util as util
import homeassistant.components.group as group
import homeassistant.external.pynetgear as pynetgear
from homeassistant.components import general, group
DOMAIN = "device_tracker"
@ -41,12 +39,11 @@ MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5)
KNOWN_DEVICES_FILE = "known_devices.csv"
def is_home(statemachine, device_id=None):
def is_on(statemachine, entity_id=None):
""" Returns if any or specified device is home. """
entity = ENTITY_ID_FORMAT.format(device_id) if device_id \
else ENTITY_ID_ALL_DEVICES
entity = entity_id or ENTITY_ID_ALL_DEVICES
return statemachine.is_state(entity, ha.STATE_HOME)
return statemachine.is_state(entity, general.STATE_HOME)
# pylint: disable=too-many-instance-attributes
@ -100,30 +97,30 @@ class DeviceTracker(object):
now = datetime.now()
temp_tracking_devices = [device for device in self.known_devices
if self.known_devices[device]['track']]
known_dev = self.known_devices
temp_tracking_devices = [device for device in known_dev
if known_dev[device]['track']]
for device in found_devices:
# Are we tracking this device?
if device in temp_tracking_devices:
temp_tracking_devices.remove(device)
self.known_devices[device]['last_seen'] = now
known_dev[device]['last_seen'] = now
self.statemachine.set_state(
self.known_devices[device]['entity_id'], ha.STATE_HOME)
known_dev[device]['entity_id'], general.STATE_HOME)
# 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
# Because we do not want to have stuff happening when the device does
# not show up for 1 scan beacuse of reboot etc
for device in temp_tracking_devices:
if (now - self.known_devices[device]['last_seen'] >
self.error_scanning):
if (now - known_dev[device]['last_seen'] > self.error_scanning):
self.statemachine.set_state(
self.known_devices[device]['entity_id'],
ha.STATE_NOT_HOME)
self.statemachine.set_state(known_dev[device]['entity_id'],
general.STATE_NOT_HOME)
# If we come along any unknown devices we will write them to the
# known devices file but only if we did not encounter an invalid
@ -131,7 +128,7 @@ class DeviceTracker(object):
if not self.invalid_known_devices_file:
unknown_devices = [device for device in found_devices
if device not in self.known_devices]
if device not in known_dev]
if unknown_devices:
try:
@ -151,13 +148,12 @@ class DeviceTracker(object):
for device in unknown_devices:
# See if the device scanner knows the name
temp_name = \
self.device_scanner.get_device_name(device)
name = temp_name if temp_name else "unknown_device"
# else defaults to unknown device
name = (self.device_scanner.get_device_name(device)
or "unknown_device")
writer.writerow((device, name, 0))
self.known_devices[device] = {'name': name,
known_dev[device] = {'name': name,
'track': False}
except IOError:
@ -380,17 +376,28 @@ class NetgearDeviceScanner(object):
""" This class queries a Netgear wireless router using the SOAP-api. """
def __init__(self, host, username, password):
self._api = pynetgear.Netgear(host, username, password)
self.logger = logging.getLogger(__name__)
self.lock = threading.Lock()
self.date_updated = None
self.last_results = []
try:
import homeassistant.external.pynetgear as pynetgear
except ImportError:
self.logger.exception(
("Netgear:Failed to import pynetgear. "
"Did you maybe not cloned the git submodules?"))
self.success_init = False
return
self._api = pynetgear.Netgear(host, username, password)
self.lock = threading.Lock()
self.logger.info("Netgear:Logging in")
if self._api.login():
self.success_init = self._update_info()
self.success_init = True
self._update_info()
else:
self.logger.error("Netgear:Failed to Login")
@ -423,6 +430,8 @@ class NetgearDeviceScanner(object):
def _update_info(self):
""" Retrieves latest information from the Netgear router.
Returns boolean if scanning successful. """
if not self.success_init:
return
with self.lock:
# if date_updated is None or the date is too old we scan for
@ -436,7 +445,7 @@ class NetgearDeviceScanner(object):
self.date_updated = datetime.now()
return True
return
else:
return True
return

View file

@ -31,7 +31,7 @@ def setup(bus, download_path):
return False
def download_file(service):
def _download_file(service):
""" Downloads file specified in the url. """
try:
@ -78,6 +78,6 @@ def setup(bus, download_path):
format(service.data['url']))
bus.register_service(DOMAIN, SERVICE_DOWNLOAD_FILE,
download_file)
_download_file)
return True

View file

@ -4,23 +4,100 @@ homeassistant.components.general
This component contains a service to shut down all devices.
"""
import importlib
import homeassistant as ha
from . import chromecast, light
import homeassistant.util as util
SERVICE_SHUTDOWN_DEVICES = "shutdown_devices"
ATTR_ENTITY_ID = 'entity_id'
STATE_ON = "on"
STATE_OFF = "off"
STATE_NOT_HOME = 'device_not_home'
STATE_HOME = 'device_home'
SERVICE_TURN_ON = "turn_on"
SERVICE_TURN_OFF = "turn_off"
_LOADED_MOD = {}
def shutdown_devices(bus, statemachine):
""" Tries to shutdown all devices that are currently on. """
chromecast.turn_off(statemachine)
light.turn_off(bus)
def _get_module(module):
""" Helper function to load a module. """
try:
return _LOADED_MOD[module]
except KeyError:
# if module key did not exist in loaded dict
try:
module = _LOADED_MOD[module] = importlib.import_module(
'homeassistant.components.'+module)
return module
except ImportError:
# If module does not exist
return None
def setup(bus, statemachine):
""" Setup services related to homeassistant. """
def is_on(statemachine, entity_id=None):
""" Loads up the module to call the turn_on method.
If there is no entity id given we will check all. """
entity_ids = [entity_id] if entity_id else statemachine.entity_ids
bus.register_service(ha.DOMAIN, SERVICE_SHUTDOWN_DEVICES,
lambda service: shutdown_devices(bus, statemachine))
for entity_id in entity_ids:
domain = util.split_entity_id(entity_id)[0]
try:
if _get_module(domain).is_on(statemachine, entity_id):
return True
except AttributeError:
# method is_on does not exist within module
pass
return False
def turn_on(bus, entity_id=None):
""" Turns specified entity on if possible. """
# If there is no entity_id we do not know which domain to call.
if not entity_id:
return
domain = util.split_entity_id(entity_id)[0]
try:
bus.call_service(domain, SERVICE_TURN_ON, {ATTR_ENTITY_ID: entity_id})
except ha.ServiceDoesNotExistError:
# turn_on service does not exist
pass
def turn_off(bus, entity_id=None):
""" Turns specified entity off. """
# If there is no entity_id we do not know which domain to call.
if not entity_id:
return
domain = util.split_entity_id(entity_id)[0]
try:
bus.call_service(domain, SERVICE_TURN_OFF, {ATTR_ENTITY_ID: entity_id})
except ha.ServiceDoesNotExistError:
# turn_off service does not exist
pass
def setup(bus):
""" Setup general services related to homeassistant. """
bus.register_service(ha.DOMAIN, SERVICE_TURN_OFF,
lambda service:
turn_off(bus, service.data.get(ATTR_ENTITY_ID)))
bus.register_service(ha.DOMAIN, SERVICE_TURN_ON,
lambda service:
turn_on(bus, service.data.get(ATTR_ENTITY_ID)))
return True

View file

@ -8,6 +8,7 @@ Provides functionality to group devices that can be turned on or off.
import logging
import homeassistant as ha
from homeassistant.components import general as gen
DOMAIN = "group"
@ -16,8 +17,8 @@ ENTITY_ID_FORMAT = DOMAIN + ".{}"
STATE_ATTR_ENTITY_IDS = "entity_ids"
_GROUP_TYPES = {
"on_off": (ha.STATE_ON, ha.STATE_OFF),
"home_not_home": (ha.STATE_HOME, ha.STATE_NOT_HOME)
"on_off": (gen.STATE_ON, gen.STATE_OFF),
"home_not_home": (gen.STATE_HOME, gen.STATE_NOT_HOME)
}
@ -30,9 +31,9 @@ def _get_group_type(state):
return None
def is_on(statemachine, group):
def is_on(statemachine, entity_id):
""" Returns if the group state is in its ON-state. """
state = statemachine.get_state(group)
state = statemachine.get_state(entity_id)
if state:
group_type = _get_group_type(state.state)
@ -46,17 +47,18 @@ def is_on(statemachine, group):
return False
def get_entity_ids(statemachine, group):
def get_entity_ids(statemachine, entity_id):
""" Get the entity ids that make up this group. """
try:
return statemachine.get_state(group).attributes[STATE_ATTR_ENTITY_IDS]
return \
statemachine.get_state(entity_id).attributes[STATE_ATTR_ENTITY_IDS]
except (AttributeError, KeyError):
# AttributeError if state did not exist
# KeyError if key did not exist in attributes
return []
# pylint: disable=too-many-branches
# pylint: disable=too-many-branches, too-many-locals
def setup(bus, statemachine, name, entity_ids):
""" Sets up a group state that is the combined state of
several states. Supports ON/OFF and DEVICE_HOME/DEVICE_NOT_HOME. """
@ -136,6 +138,33 @@ def setup(bus, statemachine, name, entity_ids):
for entity_id in entity_ids:
ha.track_state_change(bus, entity_id, _update_group_state)
# group.setup is called to setup each group. Only the first time will we
# register a turn_on and turn_off method for groups.
if not bus.has_service(DOMAIN, gen.SERVICE_TURN_ON):
def _turn_group_on_service(service):
""" Call general.turn_on for each entity_id from this group. """
for entity_id in get_entity_ids(statemachine,
service.data.get(
gen.ATTR_ENTITY_ID)):
gen.turn_on(bus, entity_id)
bus.register_service(DOMAIN, gen.SERVICE_TURN_ON,
_turn_group_on_service)
if not bus.has_service(DOMAIN, gen.SERVICE_TURN_OFF):
def _turn_group_off_service(service):
""" Call general.turn_off for each entity_id from this group. """
for entity_id in get_entity_ids(statemachine,
service.data.get(
gen.ATTR_ENTITY_ID)):
gen.turn_off(bus, entity_id)
bus.register_service(DOMAIN, gen.SERVICE_TURN_OFF,
_turn_group_off_service)
statemachine.set_state(group_entity_id, group_state, state_attr)
return True

View file

@ -592,6 +592,10 @@ class RequestHandler(BaseHTTPRequestHandler):
self._message("Service {}/{} called.".format(domain, service))
except ha.ServiceDoesNotExistError:
# If the service does not exist
self._message('Service does not exist', HTTP_BAD_REQUEST)
except KeyError:
# Occurs if domain or service does not exist in data
self._message("No domain or service received.", HTTP_BAD_REQUEST)

View file

@ -10,7 +10,7 @@ from datetime import datetime, timedelta
import homeassistant as ha
import homeassistant.util as util
import homeassistant.components.group as group
from homeassistant.components import general, group
DOMAIN = "light"
@ -27,33 +27,35 @@ def is_on(statemachine, entity_id=None):
""" Returns if the lights are on based on the statemachine. """
entity_id = entity_id or ENTITY_ID_ALL_LIGHTS
return statemachine.is_state(entity_id, ha.STATE_ON)
return statemachine.is_state(entity_id, general.STATE_ON)
# pylint: disable=unused-argument
def turn_on(bus, entity_id=None, transition_seconds=None):
""" Turns all or specified light on. """
data = {}
if entity_id:
data["light_id"] = ha.split_entity_id(entity_id)[1]
data[general.ATTR_ENTITY_ID] = entity_id
if transition_seconds:
data["transition_seconds"] = transition_seconds
bus.call_service(DOMAIN, ha.SERVICE_TURN_ON, data)
bus.call_service(DOMAIN, general.SERVICE_TURN_ON, data)
# pylint: disable=unused-argument
def turn_off(bus, entity_id=None, transition_seconds=None):
""" Turns all or specified light off. """
data = {}
if entity_id:
data["light_id"] = ha.split_entity_id(entity_id)[1]
data[general.ATTR_ENTITY_ID] = entity_id
if transition_seconds:
data["transition_seconds"] = transition_seconds
bus.call_service(DOMAIN, ha.SERVICE_TURN_OFF, data)
bus.call_service(DOMAIN, general.SERVICE_TURN_OFF, data)
def setup(bus, statemachine, light_control):
@ -61,6 +63,13 @@ def setup(bus, statemachine, light_control):
logger = logging.getLogger(__name__)
entity_ids = {light_id: ENTITY_ID_FORMAT.format(light_id) for light_id
in light_control.light_ids}
if not entity_ids:
logger.error("Light:Found no lights to track")
return
def update_light_state(time): # pylint: disable=unused-argument
""" Track the state of the lights. """
try:
@ -79,39 +88,36 @@ def setup(bus, statemachine, light_control):
for light_id in light_control.light_ids}
for light_id, state in status.items():
entity_id = ENTITY_ID_FORMAT.format(light_id)
new_state = general.STATE_ON if state else general.STATE_OFF
new_state = ha.STATE_ON if state else ha.STATE_OFF
statemachine.set_state(entity_id, new_state)
statemachine.set_state(entity_ids[light_id], new_state)
ha.track_time_change(bus, update_light_state, second=[0, 30])
update_light_state(None)
# Track the all lights state
entity_ids = [ENTITY_ID_FORMAT.format(light_id) for light_id
in light_control.light_ids]
group.setup(bus, statemachine, GROUP_NAME_ALL_LIGHTS, entity_ids)
group.setup(bus, statemachine, GROUP_NAME_ALL_LIGHTS, entity_ids.values())
def handle_light_service(service):
""" Hande a turn light on or off service call. """
light_id = service.data.get("light_id", None)
entity_id = service.data.get(general.ATTR_ENTITY_ID, None)
transition_seconds = service.data.get("transition_seconds", None)
if service.service == ha.SERVICE_TURN_ON:
light_control.turn_light_on(light_id, transition_seconds)
object_id = util.split_entity_id(entity_id)[1] if entity_id else None
if service.service == general.SERVICE_TURN_ON:
light_control.turn_light_on(object_id, transition_seconds)
else:
light_control.turn_light_off(light_id, transition_seconds)
light_control.turn_light_off(object_id, transition_seconds)
update_light_state(None)
# Listen for light on and light off events
bus.register_service(DOMAIN, ha.SERVICE_TURN_ON,
bus.register_service(DOMAIN, general.SERVICE_TURN_ON,
handle_light_service)
bus.register_service(DOMAIN, ha.SERVICE_TURN_OFF,
bus.register_service(DOMAIN, general.SERVICE_TURN_OFF,
handle_light_service)
return True

View file

@ -10,7 +10,7 @@ from datetime import timedelta
import homeassistant as ha
import homeassistant.util as util
ENTITY_ID = "weather.sun"
ENTITY_ID = "sun.sun"
STATE_ABOVE_HORIZON = "above_horizon"
STATE_BELOW_HORIZON = "below_horizon"
@ -19,9 +19,11 @@ STATE_ATTR_NEXT_RISING = "next_rising"
STATE_ATTR_NEXT_SETTING = "next_setting"
def is_up(statemachine):
def is_on(statemachine, entity_id=None):
""" Returns if the sun is currently up based on the statemachine. """
return statemachine.is_state(ENTITY_ID, STATE_ABOVE_HORIZON)
entity_id = entity_id or ENTITY_ID
return statemachine.is_state(entity_id, STATE_ABOVE_HORIZON)
def next_setting(statemachine):

View file

@ -6,7 +6,7 @@ A module containing drop in replacements for core parts that will interface
with a remote instance of home assistant.
If a connection error occurs while communicating with the API a
HomeAssistantException will be raised.
HomeAssistantError will be raised.
"""
import threading
@ -44,7 +44,7 @@ def _setup_call_api(host, port, api_password):
except requests.exceptions.ConnectionError:
logging.getLogger(__name__).exception("Error connecting to server")
raise ha.HomeAssistantException("Error connecting to server")
raise ha.HomeAssistantError("Error connecting to server")
return _call_api
@ -85,17 +85,17 @@ class Bus(ha.Bus):
return data['services']
else:
raise ha.HomeAssistantException(
raise ha.HomeAssistantError(
"Got unexpected result (3): {}.".format(req.text))
except ValueError: # If req.json() can't parse the json
self.logger.exception("Bus:Got unexpected result")
raise ha.HomeAssistantException(
raise ha.HomeAssistantError(
"Got unexpected result: {}".format(req.text))
except KeyError: # If not all expected keys are in the returned JSON
self.logger.exception("Bus:Got unexpected result (2)")
raise ha.HomeAssistantException(
raise ha.HomeAssistantError(
"Got unexpected result (2): {}".format(req.text))
@property
@ -110,17 +110,17 @@ class Bus(ha.Bus):
return data['event_listeners']
else:
raise ha.HomeAssistantException(
raise ha.HomeAssistantError(
"Got unexpected result (3): {}.".format(req.text))
except ValueError: # If req.json() can't parse the json
self.logger.exception("Bus:Got unexpected result")
raise ha.HomeAssistantException(
raise ha.HomeAssistantError(
"Got unexpected result: {}".format(req.text))
except KeyError: # If not all expected keys are in the returned JSON
self.logger.exception("Bus:Got unexpected result (2)")
raise ha.HomeAssistantException(
raise ha.HomeAssistantError(
"Got unexpected result (2): {}".format(req.text))
def call_service(self, domain, service, service_data=None):
@ -141,7 +141,11 @@ class Bus(ha.Bus):
req.status_code, req.text)
self.logger.error("Bus:{}".format(error))
raise ha.HomeAssistantException(error)
if req.status_code == 400:
raise ha.ServiceDoesNotExistError(error)
else:
raise ha.HomeAssistantError(error)
def register_service(self, domain, service, service_callback):
""" Not implemented for remote bus.
@ -166,7 +170,7 @@ class Bus(ha.Bus):
req.status_code, req.text)
self.logger.error("Bus:{}".format(error))
raise ha.HomeAssistantException(error)
raise ha.HomeAssistantError(error)
def listen_event(self, event_type, listener):
""" Not implemented for remote bus.
@ -251,11 +255,11 @@ class StateMachine(ha.StateMachine):
req.status_code, req.text)
self.logger.error("StateMachine:{}".format(error))
raise ha.HomeAssistantException(error)
raise ha.HomeAssistantError(error)
except requests.exceptions.ConnectionError:
self.logger.exception("StateMachine:Error connecting to server")
raise ha.HomeAssistantException("Error connecting to server")
raise ha.HomeAssistantError("Error connecting to server")
finally:
self.lock.release()
@ -277,19 +281,19 @@ class StateMachine(ha.StateMachine):
return None
else:
raise ha.HomeAssistantException(
raise ha.HomeAssistantError(
"Got unexpected result (3): {}.".format(req.text))
except requests.exceptions.ConnectionError:
self.logger.exception("StateMachine:Error connecting to server")
raise ha.HomeAssistantException("Error connecting to server")
raise ha.HomeAssistantError("Error connecting to server")
except ValueError: # If req.json() can't parse the json
self.logger.exception("StateMachine:Got unexpected result")
raise ha.HomeAssistantException(
raise ha.HomeAssistantError(
"Got unexpected result: {}".format(req.text))
except KeyError: # If not all expected keys are in the returned JSON
self.logger.exception("StateMachine:Got unexpected result (2)")
raise ha.HomeAssistantException(
raise ha.HomeAssistantError(
"Got unexpected result (2): {}".format(req.text))

View file

@ -38,3 +38,18 @@ def str_to_datetime(dt_str):
return datetime.datetime.strptime(dt_str, DATE_STR_FORMAT)
except ValueError: # If dt_str did not match our format
return None
def split_entity_id(entity_id):
""" Splits a state entity_id into domain, object_id. """
return entity_id.split(".", 1)
def filter_entity_ids(entity_ids, domain_filter=None, strip_domain=False):
""" Filter a list of entities based on domain. Setting strip_domain
will only return the object_ids. """
return [
split_entity_id(entity_id)[1] if strip_domain else entity_id
for entity_id in entity_ids if
not domain_filter or entity_id.startswith(domain_filter)
]