core/homeassistant/remote.py

223 lines
7.2 KiB
Python
Raw Normal View History

"""
homeassistant.remote
~~~~~~~~~~~~~~~~~~~~
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.
"""
import threading
import logging
import json
2013-10-29 07:22:38 +00:00
import urlparse
import requests
import homeassistant as ha
2013-10-29 07:22:38 +00:00
import homeassistant.httpinterface as hah
2013-10-29 07:22:38 +00:00
METHOD_GET = "get"
METHOD_POST = "post"
2013-11-11 00:46:48 +00:00
2013-10-29 07:22:38 +00:00
def _setup_call_api(host, port, api_password):
""" Helper method to setup a call api method. """
2013-10-29 07:22:38 +00:00
port = port or hah.SERVER_PORT
2013-10-29 07:22:38 +00:00
base_url = "http://{}:{}".format(host, port)
2013-10-29 07:22:38 +00:00
def _call_api(method, path, data=None):
""" Makes a call to the Home Assistant api. """
data = data or {}
data['api_password'] = api_password
2013-10-29 07:22:38 +00:00
url = urlparse.urljoin(base_url, path)
if method == METHOD_GET:
return requests.get(url, params=data)
else:
return requests.request(method, url, data=data)
return _call_api
class EventBus(ha.EventBus):
""" Drop-in replacement for a normal eventbus that will forward events to
a remote eventbus.
"""
def __init__(self, host, api_password, port=None):
ha.EventBus.__init__(self)
2013-10-29 07:22:38 +00:00
self._call_api = _setup_call_api(host, port, api_password)
self.logger = logging.getLogger(__name__)
@property
def listeners(self):
""" List of events that is being listened for. """
try:
req = self._call_api(METHOD_GET, hah.URL_API_EVENTS)
if req.status_code == 200:
data = req.json()
return data['listeners']
else:
raise ha.HomeAssistantException(
2013-11-11 00:46:48 +00:00
"Got unexpected result (3): {}.".format(req.text))
except requests.exceptions.ConnectionError:
self.logger.exception("EventBus:Error connecting to server")
raise ha.HomeAssistantException("Error connecting to server")
2013-11-11 00:46:48 +00:00
except ValueError: # If req.json() can't parse the json
self.logger.exception("EventBus:Got unexpected result")
raise ha.HomeAssistantException(
2013-11-11 00:46:48 +00:00
"Got unexpected result: {}".format(req.text))
2013-11-11 00:46:48 +00:00
except KeyError: # If not all expected keys are in the returned JSON
self.logger.exception("EventBus:Got unexpected result (2)")
raise ha.HomeAssistantException(
2013-11-11 00:46:48 +00:00
"Got unexpected result (2): {}".format(req.text))
def fire(self, event_type, event_data=None):
""" Fire an event. """
2013-10-29 07:22:38 +00:00
data = {'event_data': json.dumps(event_data)} if event_data else None
try:
2013-10-29 07:22:38 +00:00
req = self._call_api(METHOD_POST,
hah.URL_API_EVENTS_EVENT.format(event_type),
data)
if req.status_code != 200:
error = "Error firing event: {} - {}".format(
2013-11-11 00:46:48 +00:00
req.status_code, req.text)
self.logger.error("EventBus:{}".format(error))
raise ha.HomeAssistantException(error)
except requests.exceptions.ConnectionError:
self.logger.exception("EventBus:Error connecting to server")
def listen(self, event_type, listener):
""" Not implemented for remote eventbus.
Will throw NotImplementedError. """
raise NotImplementedError
def remove_listener(self, event_type, listener):
""" Not implemented for remote eventbus.
Will throw NotImplementedError. """
raise NotImplementedError
2013-11-11 00:46:48 +00:00
class StateMachine(ha.StateMachine):
""" Drop-in replacement for a normal statemachine that communicates with a
remote statemachine.
"""
def __init__(self, host, api_password, port=None):
ha.StateMachine.__init__(self, None)
2013-10-29 07:22:38 +00:00
self._call_api = _setup_call_api(host, port, api_password)
self.lock = threading.Lock()
self.logger = logging.getLogger(__name__)
@property
def categories(self):
""" List of categories which states are being tracked. """
try:
2013-10-29 07:22:38 +00:00
req = self._call_api(METHOD_GET, hah.URL_API_STATES)
return req.json()['categories']
except requests.exceptions.ConnectionError:
self.logger.exception("StateMachine:Error connecting to server")
return []
2013-11-11 00:46:48 +00:00
except ValueError: # If req.json() can't parse the json
self.logger.exception("StateMachine:Got unexpected result")
return []
2013-11-11 00:46:48 +00:00
except KeyError: # If 'categories' key not in parsed json
self.logger.exception("StateMachine:Got unexpected result (2)")
return []
def set_state(self, category, new_state, attributes=None):
""" Set the state of a category, add category if it does not exist.
Attributes is an optional dict to specify attributes of this state. """
attributes = attributes or {}
self.lock.acquire()
2013-10-29 07:22:38 +00:00
data = {'new_state': new_state,
'attributes': json.dumps(attributes)}
try:
2013-10-29 07:22:38 +00:00
req = self._call_api(METHOD_POST,
hah.URL_API_STATES_CATEGORY.format(category),
data)
2013-10-29 07:22:38 +00:00
if req.status_code != 201:
error = "Error changing state: {} - {}".format(
2013-11-11 00:46:48 +00:00
req.status_code, req.text)
self.logger.error("StateMachine:{}".format(error))
raise ha.HomeAssistantException(error)
except requests.exceptions.ConnectionError:
self.logger.exception("StateMachine:Error connecting to server")
raise ha.HomeAssistantException("Error connecting to server")
finally:
self.lock.release()
def get_state(self, category):
""" Returns a dict (state,last_changed, attributes) describing
the state of the specified category. """
try:
2013-10-29 07:22:38 +00:00
req = self._call_api(METHOD_GET,
hah.URL_API_STATES_CATEGORY.format(category))
2013-11-01 18:34:43 +00:00
if req.status_code == 200:
data = req.json()
2013-11-11 00:46:48 +00:00
return ha.create_state(data['state'], data['attributes'],
ha.str_to_datetime(
data['last_changed']))
2013-11-01 18:34:43 +00:00
elif req.status_code == 422:
# Category does not exist
return None
else:
raise ha.HomeAssistantException(
2013-11-11 00:46:48 +00:00
"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")
2013-11-11 00:46:48 +00:00
except ValueError: # If req.json() can't parse the json
self.logger.exception("StateMachine:Got unexpected result")
raise ha.HomeAssistantException(
2013-11-11 00:46:48 +00:00
"Got unexpected result: {}".format(req.text))
2013-11-11 00:46:48 +00:00
except KeyError: # If not all expected keys are in the returned JSON
self.logger.exception("StateMachine:Got unexpected result (2)")
raise ha.HomeAssistantException(
2013-11-11 00:46:48 +00:00
"Got unexpected result (2): {}".format(req.text))