diff --git a/docs/source/api/homeassistant.rst b/docs/source/api/homeassistant.rst index f5ff069451d..599f5fb8019 100644 --- a/docs/source/api/homeassistant.rst +++ b/docs/source/api/homeassistant.rst @@ -60,14 +60,6 @@ loader module :undoc-members: :show-inheritance: -remote module ---------------------------- - -.. automodule:: homeassistant.remote - :members: - :undoc-members: - :show-inheritance: - Module contents --------------- diff --git a/homeassistant/components/api.py b/homeassistant/components/api.py index de28eeff5ca..0fbb4de39f1 100644 --- a/homeassistant/components/api.py +++ b/homeassistant/components/api.py @@ -24,7 +24,7 @@ from homeassistant.exceptions import TemplateError from homeassistant.helpers import template from homeassistant.helpers.service import async_get_all_descriptions from homeassistant.helpers.state import AsyncTrackStates -import homeassistant.remote as rem +from homeassistant.helpers.json import JSONEncoder _LOGGER = logging.getLogger(__name__) @@ -102,7 +102,7 @@ class APIEventStream(HomeAssistantView): if event.event_type == EVENT_HOMEASSISTANT_STOP: data = stop_obj else: - data = json.dumps(event, cls=rem.JSONEncoder) + data = json.dumps(event, cls=JSONEncoder) await to_write.put(data) diff --git a/homeassistant/components/http/view.py b/homeassistant/components/http/view.py index 22ef34de54a..b3b2587fc45 100644 --- a/homeassistant/components/http/view.py +++ b/homeassistant/components/http/view.py @@ -11,10 +11,10 @@ import logging from aiohttp import web from aiohttp.web_exceptions import HTTPUnauthorized, HTTPInternalServerError -import homeassistant.remote as rem from homeassistant.components.http.ban import process_success_login from homeassistant.core import Context, is_callback from homeassistant.const import CONTENT_TYPE_JSON +from homeassistant.helpers.json import JSONEncoder from .const import KEY_AUTHENTICATED, KEY_REAL_IP @@ -44,7 +44,7 @@ class HomeAssistantView: """Return a JSON response.""" try: msg = json.dumps( - result, sort_keys=True, cls=rem.JSONEncoder).encode('UTF-8') + result, sort_keys=True, cls=JSONEncoder).encode('UTF-8') except TypeError as err: _LOGGER.error('Unable to serialize to JSON: %s\n%s', err, result) raise HTTPInternalServerError diff --git a/homeassistant/components/mqtt_eventstream.py b/homeassistant/components/mqtt_eventstream.py index ea4463f5c23..0e01310115f 100644 --- a/homeassistant/components/mqtt_eventstream.py +++ b/homeassistant/components/mqtt_eventstream.py @@ -17,7 +17,7 @@ from homeassistant.const import ( EVENT_STATE_CHANGED, EVENT_TIME_CHANGED, MATCH_ALL) from homeassistant.core import EventOrigin, State import homeassistant.helpers.config_validation as cv -from homeassistant.remote import JSONEncoder +from homeassistant.helpers.json import JSONEncoder DOMAIN = 'mqtt_eventstream' DEPENDENCIES = ['mqtt'] diff --git a/homeassistant/components/mqtt_statestream.py b/homeassistant/components/mqtt_statestream.py index 205a638c574..592e31cbff1 100644 --- a/homeassistant/components/mqtt_statestream.py +++ b/homeassistant/components/mqtt_statestream.py @@ -15,7 +15,7 @@ from homeassistant.core import callback from homeassistant.components.mqtt import valid_publish_topic from homeassistant.helpers.entityfilter import generate_filter from homeassistant.helpers.event import async_track_state_change -from homeassistant.remote import JSONEncoder +from homeassistant.helpers.json import JSONEncoder import homeassistant.helpers.config_validation as cv CONF_BASE_TOPIC = 'base_topic' diff --git a/homeassistant/components/notify/aws_lambda.py b/homeassistant/components/notify/aws_lambda.py index 46ac2f89d33..8a3cb900f4b 100644 --- a/homeassistant/components/notify/aws_lambda.py +++ b/homeassistant/components/notify/aws_lambda.py @@ -15,7 +15,7 @@ from homeassistant.const import ( from homeassistant.components.notify import ( ATTR_TARGET, PLATFORM_SCHEMA, BaseNotificationService) import homeassistant.helpers.config_validation as cv -from homeassistant.remote import JSONEncoder +from homeassistant.helpers.json import JSONEncoder REQUIREMENTS = ['boto3==1.4.7'] diff --git a/homeassistant/components/notify/html5.py b/homeassistant/components/notify/html5.py index 1ed50472004..fa93cc4ba4d 100644 --- a/homeassistant/components/notify/html5.py +++ b/homeassistant/components/notify/html5.py @@ -132,17 +132,6 @@ def _load_config(filename): return {} -class JSONBytesDecoder(json.JSONEncoder): - """JSONEncoder to decode bytes objects to unicode.""" - - # pylint: disable=method-hidden, arguments-differ - def default(self, obj): - """Decode object if it's a bytes object, else defer to base class.""" - if isinstance(obj, bytes): - return obj.decode() - return json.JSONEncoder.default(self, obj) - - class HTML5PushRegistrationView(HomeAssistantView): """Accepts push registrations from a browser.""" diff --git a/homeassistant/components/recorder/models.py b/homeassistant/components/recorder/models.py index b8b777990f7..700dd57eacf 100644 --- a/homeassistant/components/recorder/models.py +++ b/homeassistant/components/recorder/models.py @@ -11,7 +11,7 @@ from sqlalchemy.ext.declarative import declarative_base import homeassistant.util.dt as dt_util from homeassistant.core import ( Context, Event, EventOrigin, State, split_entity_id) -from homeassistant.remote import JSONEncoder +from homeassistant.helpers.json import JSONEncoder # SQLAlchemy Schema # pylint: disable=invalid-name diff --git a/homeassistant/components/splunk.py b/homeassistant/components/splunk.py index a5b42eb9b5a..28863f6a436 100644 --- a/homeassistant/components/splunk.py +++ b/homeassistant/components/splunk.py @@ -15,7 +15,7 @@ from homeassistant.const import ( CONF_SSL, CONF_HOST, CONF_NAME, CONF_PORT, CONF_TOKEN, EVENT_STATE_CHANGED) from homeassistant.helpers import state as state_helper import homeassistant.helpers.config_validation as cv -from homeassistant.remote import JSONEncoder +from homeassistant.helpers.json import JSONEncoder _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/websocket_api.py b/homeassistant/components/websocket_api.py index c25f4418263..1ba0e20d553 100644 --- a/homeassistant/components/websocket_api.py +++ b/homeassistant/components/websocket_api.py @@ -20,7 +20,7 @@ from homeassistant.const import ( __version__) from homeassistant.core import Context, callback from homeassistant.loader import bind_hass -from homeassistant.remote import JSONEncoder +from homeassistant.helpers.json import JSONEncoder from homeassistant.helpers import config_validation as cv from homeassistant.helpers.service import async_get_all_descriptions from homeassistant.components.http import HomeAssistantView diff --git a/homeassistant/helpers/json.py b/homeassistant/helpers/json.py new file mode 100644 index 00000000000..c28ee8c5c2c --- /dev/null +++ b/homeassistant/helpers/json.py @@ -0,0 +1,27 @@ +"""Helpers to help with encoding Home Assistant objects in JSON.""" +from datetime import datetime +import json +import logging + +from typing import Any + +_LOGGER = logging.getLogger(__name__) + + +class JSONEncoder(json.JSONEncoder): + """JSONEncoder that supports Home Assistant objects.""" + + # pylint: disable=method-hidden + def default(self, o: Any) -> Any: + """Convert Home Assistant objects. + + Hand other objects to the original method. + """ + if isinstance(o, datetime): + return o.isoformat() + if isinstance(o, set): + return list(o) + if hasattr(o, 'as_dict'): + return o.as_dict() + + return json.JSONEncoder.default(self, o) diff --git a/homeassistant/remote.py b/homeassistant/remote.py deleted file mode 100644 index 16c37f210e7..00000000000 --- a/homeassistant/remote.py +++ /dev/null @@ -1,317 +0,0 @@ -""" -Support for an interface to work with a remote instance of Home Assistant. - -If a connection error occurs while communicating with the API a -HomeAssistantError will be raised. - -For more details about the Python API, please refer to the documentation at -https://home-assistant.io/developers/python_api/ -""" -from datetime import datetime -import enum -import json -import logging -import urllib.parse - -from typing import Optional, Dict, Any, List - -from aiohttp.hdrs import METH_GET, METH_POST, METH_DELETE, CONTENT_TYPE -import requests - -from homeassistant import core as ha -from homeassistant.const import ( - URL_API, SERVER_PORT, URL_API_CONFIG, URL_API_EVENTS, URL_API_STATES, - URL_API_SERVICES, CONTENT_TYPE_JSON, HTTP_HEADER_HA_AUTH, - URL_API_EVENTS_EVENT, URL_API_STATES_ENTITY, URL_API_SERVICES_SERVICE) -from homeassistant.exceptions import HomeAssistantError - -_LOGGER = logging.getLogger(__name__) - - -class APIStatus(enum.Enum): - """Representation of an API status.""" - - OK = "ok" - INVALID_PASSWORD = "invalid_password" - CANNOT_CONNECT = "cannot_connect" - UNKNOWN = "unknown" - - def __str__(self) -> str: - """Return the state.""" - return self.value # type: ignore - - -class API: - """Object to pass around Home Assistant API location and credentials.""" - - def __init__(self, host: str, api_password: Optional[str] = None, - port: Optional[int] = SERVER_PORT, - use_ssl: bool = False) -> None: - """Init the API.""" - _LOGGER.warning('This class is deprecated and will be removed in 0.77') - self.host = host - self.port = port - self.api_password = api_password - - if host.startswith(("http://", "https://")): - self.base_url = host - elif use_ssl: - self.base_url = "https://{}".format(host) - else: - self.base_url = "http://{}".format(host) - - if port is not None: - self.base_url += ':{}'.format(port) - - self.status = None # type: Optional[APIStatus] - self._headers = {CONTENT_TYPE: CONTENT_TYPE_JSON} - - if api_password is not None: - self._headers[HTTP_HEADER_HA_AUTH] = api_password - - def validate_api(self, force_validate: bool = False) -> bool: - """Test if we can communicate with the API.""" - if self.status is None or force_validate: - self.status = validate_api(self) - - return self.status == APIStatus.OK - - def __call__(self, method: str, path: str, data: Optional[Dict] = None, - timeout: int = 5) -> requests.Response: - """Make a call to the Home Assistant API.""" - if data is None: - data_str = None - else: - data_str = json.dumps(data, cls=JSONEncoder) - - url = urllib.parse.urljoin(self.base_url, path) - - try: - if method == METH_GET: - return requests.get( - url, params=data_str, timeout=timeout, - headers=self._headers) - - return requests.request( - method, url, data=data_str, timeout=timeout, - headers=self._headers) - - except requests.exceptions.ConnectionError: - _LOGGER.exception("Error connecting to server") - raise HomeAssistantError("Error connecting to server") - - except requests.exceptions.Timeout: - error = "Timeout when talking to {}".format(self.host) - _LOGGER.exception(error) - raise HomeAssistantError(error) - - def __repr__(self) -> str: - """Return the representation of the API.""" - return "".format( - self.base_url, 'yes' if self.api_password is not None else 'no') - - -class JSONEncoder(json.JSONEncoder): - """JSONEncoder that supports Home Assistant objects.""" - - # pylint: disable=method-hidden - def default(self, o: Any) -> Any: - """Convert Home Assistant objects. - - Hand other objects to the original method. - """ - if isinstance(o, datetime): - return o.isoformat() - if isinstance(o, set): - return list(o) - if hasattr(o, 'as_dict'): - return o.as_dict() - - return json.JSONEncoder.default(self, o) - - -def validate_api(api: API) -> APIStatus: - """Make a call to validate API.""" - try: - req = api(METH_GET, URL_API) - - if req.status_code == 200: - return APIStatus.OK - - if req.status_code == 401: - return APIStatus.INVALID_PASSWORD - - return APIStatus.UNKNOWN - - except HomeAssistantError: - return APIStatus.CANNOT_CONNECT - - -def get_event_listeners(api: API) -> Dict: - """List of events that is being listened for.""" - try: - req = api(METH_GET, URL_API_EVENTS) - - return req.json() if req.status_code == 200 else {} # type: ignore - - except (HomeAssistantError, ValueError): - # ValueError if req.json() can't parse the json - _LOGGER.exception("Unexpected result retrieving event listeners") - - return {} - - -def fire_event(api: API, event_type: str, data: Optional[Dict] = None) -> None: - """Fire an event at remote API.""" - try: - req = api(METH_POST, URL_API_EVENTS_EVENT.format(event_type), data) - - if req.status_code != 200: - _LOGGER.error("Error firing event: %d - %s", - req.status_code, req.text) - - except HomeAssistantError: - _LOGGER.exception("Error firing event") - - -def get_state(api: API, entity_id: str) -> Optional[ha.State]: - """Query given API for state of entity_id.""" - try: - req = api(METH_GET, URL_API_STATES_ENTITY.format(entity_id)) - - # req.status_code == 422 if entity does not exist - - return ha.State.from_dict(req.json()) \ - if req.status_code == 200 else None - - except (HomeAssistantError, ValueError): - # ValueError if req.json() can't parse the json - _LOGGER.exception("Error fetching state") - - return None - - -def get_states(api: API) -> List[ha.State]: - """Query given API for all states.""" - try: - req = api(METH_GET, - URL_API_STATES) - - return [ha.State.from_dict(item) for - item in req.json()] - - except (HomeAssistantError, ValueError, AttributeError): - # ValueError if req.json() can't parse the json - _LOGGER.exception("Error fetching states") - - return [] - - -def remove_state(api: API, entity_id: str) -> bool: - """Call API to remove state for entity_id. - - Return True if entity is gone (removed/never existed). - """ - try: - req = api(METH_DELETE, URL_API_STATES_ENTITY.format(entity_id)) - - if req.status_code in (200, 404): - return True - - _LOGGER.error("Error removing state: %d - %s", - req.status_code, req.text) - return False - except HomeAssistantError: - _LOGGER.exception("Error removing state") - - return False - - -def set_state(api: API, entity_id: str, new_state: str, - attributes: Optional[Dict] = None, force_update: bool = False) \ - -> bool: - """Tell API to update state for entity_id. - - Return True if success. - """ - attributes = attributes or {} - - data = {'state': new_state, - 'attributes': attributes, - 'force_update': force_update} - - try: - req = api(METH_POST, URL_API_STATES_ENTITY.format(entity_id), data) - - if req.status_code not in (200, 201): - _LOGGER.error("Error changing state: %d - %s", - req.status_code, req.text) - return False - - return True - - except HomeAssistantError: - _LOGGER.exception("Error setting state") - - return False - - -def is_state(api: API, entity_id: str, state: str) -> bool: - """Query API to see if entity_id is specified state.""" - cur_state = get_state(api, entity_id) - - return bool(cur_state and cur_state.state == state) - - -def get_services(api: API) -> Dict: - """Return a list of dicts. - - Each dict has a string "domain" and a list of strings "services". - """ - try: - req = api(METH_GET, URL_API_SERVICES) - - return req.json() if req.status_code == 200 else {} # type: ignore - - except (HomeAssistantError, ValueError): - # ValueError if req.json() can't parse the json - _LOGGER.exception("Got unexpected services result") - - return {} - - -def call_service(api: API, domain: str, service: str, - service_data: Optional[Dict] = None, - timeout: int = 5) -> None: - """Call a service at the remote API.""" - try: - req = api(METH_POST, - URL_API_SERVICES_SERVICE.format(domain, service), - service_data, timeout=timeout) - - if req.status_code != 200: - _LOGGER.error("Error calling service: %d - %s", - req.status_code, req.text) - - except HomeAssistantError: - _LOGGER.exception("Error calling service") - - -def get_config(api: API) -> Dict: - """Return configuration.""" - try: - req = api(METH_GET, URL_API_CONFIG) - - if req.status_code != 200: - return {} - - result = req.json() - if 'components' in result: - result['components'] = set(result['components']) - return result # type: ignore - - except (HomeAssistantError, ValueError): - # ValueError if req.json() can't parse the JSON - _LOGGER.exception("Got unexpected configuration results") - - return {} diff --git a/tests/components/device_tracker/test_init.py b/tests/components/device_tracker/test_init.py index e9e4650e41a..97a4ea7c067 100644 --- a/tests/components/device_tracker/test_init.py +++ b/tests/components/device_tracker/test_init.py @@ -20,7 +20,7 @@ from homeassistant.const import ( STATE_HOME, STATE_NOT_HOME, CONF_PLATFORM, ATTR_ICON) import homeassistant.components.device_tracker as device_tracker from homeassistant.exceptions import HomeAssistantError -from homeassistant.remote import JSONEncoder +from homeassistant.helpers.json import JSONEncoder from tests.common import ( get_test_home_assistant, fire_time_changed, diff --git a/tests/components/recorder/models_original.py b/tests/components/recorder/models_original.py index 990414d7713..7096e84c82b 100644 --- a/tests/components/recorder/models_original.py +++ b/tests/components/recorder/models_original.py @@ -14,7 +14,7 @@ from sqlalchemy.ext.declarative import declarative_base import homeassistant.util.dt as dt_util from homeassistant.core import Event, EventOrigin, State, split_entity_id -from homeassistant.remote import JSONEncoder +from homeassistant.helpers.json import JSONEncoder # SQLAlchemy Schema # pylint: disable=invalid-name diff --git a/tests/components/test_demo.py b/tests/components/test_demo.py index 258e3d96297..b0b2524180f 100644 --- a/tests/components/test_demo.py +++ b/tests/components/test_demo.py @@ -7,7 +7,7 @@ import pytest from homeassistant.setup import async_setup_component from homeassistant.components import demo, device_tracker -from homeassistant.remote import JSONEncoder +from homeassistant.helpers.json import JSONEncoder @pytest.fixture(autouse=True) diff --git a/tests/components/test_mqtt_eventstream.py b/tests/components/test_mqtt_eventstream.py index a6881672339..1613198e4ce 100644 --- a/tests/components/test_mqtt_eventstream.py +++ b/tests/components/test_mqtt_eventstream.py @@ -6,7 +6,7 @@ from homeassistant.setup import setup_component import homeassistant.components.mqtt_eventstream as eventstream from homeassistant.const import EVENT_STATE_CHANGED from homeassistant.core import State, callback -from homeassistant.remote import JSONEncoder +from homeassistant.helpers.json import JSONEncoder import homeassistant.util.dt as dt_util from tests.common import ( diff --git a/tests/helpers/test_json.py b/tests/helpers/test_json.py new file mode 100644 index 00000000000..1d6e7eb6ede --- /dev/null +++ b/tests/helpers/test_json.py @@ -0,0 +1,21 @@ +"""Test Home Assistant remote methods and classes.""" +import pytest + +from homeassistant import core +from homeassistant.helpers.json import JSONEncoder +from homeassistant.util import dt as dt_util + + +def test_json_encoder(hass): + """Test the JSON Encoder.""" + ha_json_enc = JSONEncoder() + state = core.State('test.test', 'hello') + + assert ha_json_enc.default(state) == state.as_dict() + + # Default method raises TypeError if non HA object + with pytest.raises(TypeError): + ha_json_enc.default(1) + + now = dt_util.utcnow() + assert ha_json_enc.default(now) == now.isoformat() diff --git a/tests/test_remote.py b/tests/test_remote.py deleted file mode 100644 index 9aa730d6eb6..00000000000 --- a/tests/test_remote.py +++ /dev/null @@ -1,205 +0,0 @@ -"""Test Home Assistant remote methods and classes.""" -# pylint: disable=protected-access -import unittest - -from homeassistant import remote, setup, core as ha -import homeassistant.components.http as http -from homeassistant.const import HTTP_HEADER_HA_AUTH, EVENT_STATE_CHANGED -import homeassistant.util.dt as dt_util - -from tests.common import ( - get_test_instance_port, get_test_home_assistant) - -API_PASSWORD = 'test1234' -MASTER_PORT = get_test_instance_port() -BROKEN_PORT = get_test_instance_port() -HTTP_BASE_URL = 'http://127.0.0.1:{}'.format(MASTER_PORT) - -HA_HEADERS = {HTTP_HEADER_HA_AUTH: API_PASSWORD} - -broken_api = remote.API('127.0.0.1', "bladybla", port=get_test_instance_port()) -hass, master_api = None, None - - -def _url(path=''): - """Helper method to generate URLs.""" - return HTTP_BASE_URL + path - - -# pylint: disable=invalid-name -def setUpModule(): - """Initialization of a Home Assistant server instance.""" - global hass, master_api - - hass = get_test_home_assistant() - - hass.bus.listen('test_event', lambda _: _) - hass.states.set('test.test', 'a_state') - - setup.setup_component( - hass, http.DOMAIN, - {http.DOMAIN: {http.CONF_API_PASSWORD: API_PASSWORD, - http.CONF_SERVER_PORT: MASTER_PORT}}) - - setup.setup_component(hass, 'api') - - hass.start() - - master_api = remote.API('127.0.0.1', API_PASSWORD, MASTER_PORT) - - -# pylint: disable=invalid-name -def tearDownModule(): - """Stop the Home Assistant server.""" - hass.stop() - - -class TestRemoteMethods(unittest.TestCase): - """Test the homeassistant.remote module.""" - - def tearDown(self): - """Stop everything that was started.""" - hass.block_till_done() - - def test_validate_api(self): - """Test Python API validate_api.""" - self.assertEqual(remote.APIStatus.OK, remote.validate_api(master_api)) - - self.assertEqual( - remote.APIStatus.INVALID_PASSWORD, - remote.validate_api( - remote.API('127.0.0.1', API_PASSWORD + 'A', MASTER_PORT))) - - self.assertEqual( - remote.APIStatus.CANNOT_CONNECT, remote.validate_api(broken_api)) - - def test_get_event_listeners(self): - """Test Python API get_event_listeners.""" - local_data = hass.bus.listeners - remote_data = remote.get_event_listeners(master_api) - - for event in remote_data: - self.assertEqual(local_data.pop(event["event"]), - event["listener_count"]) - - self.assertEqual(len(local_data), 0) - - self.assertEqual({}, remote.get_event_listeners(broken_api)) - - def test_fire_event(self): - """Test Python API fire_event.""" - test_value = [] - - @ha.callback - def listener(event): - """Helper method that will verify our event got called.""" - test_value.append(1) - - hass.bus.listen("test.event_no_data", listener) - remote.fire_event(master_api, "test.event_no_data") - hass.block_till_done() - self.assertEqual(1, len(test_value)) - - # Should not trigger any exception - remote.fire_event(broken_api, "test.event_no_data") - - def test_get_state(self): - """Test Python API get_state.""" - self.assertEqual( - hass.states.get('test.test'), - remote.get_state(master_api, 'test.test')) - - self.assertEqual(None, remote.get_state(broken_api, 'test.test')) - - def test_get_states(self): - """Test Python API get_state_entity_ids.""" - self.assertEqual(hass.states.all(), remote.get_states(master_api)) - self.assertEqual([], remote.get_states(broken_api)) - - def test_remove_state(self): - """Test Python API set_state.""" - hass.states.set('test.remove_state', 'set_test') - - self.assertIn('test.remove_state', hass.states.entity_ids()) - remote.remove_state(master_api, 'test.remove_state') - self.assertNotIn('test.remove_state', hass.states.entity_ids()) - - def test_set_state(self): - """Test Python API set_state.""" - remote.set_state(master_api, 'test.test', 'set_test') - - state = hass.states.get('test.test') - - self.assertIsNotNone(state) - self.assertEqual('set_test', state.state) - - self.assertFalse(remote.set_state(broken_api, 'test.test', 'set_test')) - - def test_set_state_with_push(self): - """Test Python API set_state with push option.""" - events = [] - hass.bus.listen(EVENT_STATE_CHANGED, lambda ev: events.append(ev)) - - remote.set_state(master_api, 'test.test', 'set_test_2') - remote.set_state(master_api, 'test.test', 'set_test_2') - hass.block_till_done() - self.assertEqual(1, len(events)) - - remote.set_state( - master_api, 'test.test', 'set_test_2', force_update=True) - hass.block_till_done() - self.assertEqual(2, len(events)) - - def test_is_state(self): - """Test Python API is_state.""" - self.assertTrue( - remote.is_state(master_api, 'test.test', - hass.states.get('test.test').state)) - - self.assertFalse( - remote.is_state(broken_api, 'test.test', - hass.states.get('test.test').state)) - - def test_get_services(self): - """Test Python API get_services.""" - local_services = hass.services.services - - for serv_domain in remote.get_services(master_api): - local = local_services.pop(serv_domain["domain"]) - - self.assertEqual(local, serv_domain["services"]) - - self.assertEqual({}, remote.get_services(broken_api)) - - def test_call_service(self): - """Test Python API services.call.""" - test_value = [] - - @ha.callback - def listener(service_call): - """Helper method that will verify that our service got called.""" - test_value.append(1) - - hass.services.register("test_domain", "test_service", listener) - - remote.call_service(master_api, "test_domain", "test_service") - - hass.block_till_done() - - self.assertEqual(1, len(test_value)) - - # Should not raise an exception - remote.call_service(broken_api, "test_domain", "test_service") - - def test_json_encoder(self): - """Test the JSON Encoder.""" - ha_json_enc = remote.JSONEncoder() - state = hass.states.get('test.test') - - self.assertEqual(state.as_dict(), ha_json_enc.default(state)) - - # Default method raises TypeError if non HA object - self.assertRaises(TypeError, ha_json_enc.default, 1) - - now = dt_util.utcnow() - self.assertEqual(now.isoformat(), ha_json_enc.default(now))