Use constants (#14647)

This commit is contained in:
Fabian Affolter 2018-05-27 20:16:30 +02:00 committed by GitHub
parent 36e8157268
commit 2f4c5f949b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -2,7 +2,7 @@
Rest API for Home Assistant. Rest API for Home Assistant.
For more details about the RESTful API, please refer to the documentation at For more details about the RESTful API, please refer to the documentation at
https://home-assistant.io/developers/api/ https://developers.home-assistant.io/docs/en/external_api_rest.html
""" """
import asyncio import asyncio
import json import json
@ -11,31 +11,34 @@ import logging
from aiohttp import web from aiohttp import web
import async_timeout import async_timeout
import homeassistant.core as ha
import homeassistant.remote as rem
from homeassistant.bootstrap import DATA_LOGGING from homeassistant.bootstrap import DATA_LOGGING
from homeassistant.const import (
EVENT_HOMEASSISTANT_STOP, EVENT_TIME_CHANGED,
HTTP_BAD_REQUEST, HTTP_CREATED, HTTP_NOT_FOUND,
MATCH_ALL, URL_API, URL_API_COMPONENTS,
URL_API_CONFIG, URL_API_DISCOVERY_INFO, URL_API_ERROR_LOG,
URL_API_EVENTS, URL_API_SERVICES,
URL_API_STATES, URL_API_STATES_ENTITY, URL_API_STREAM, URL_API_TEMPLATE,
__version__)
from homeassistant.exceptions import TemplateError
from homeassistant.helpers.state import AsyncTrackStates
from homeassistant.helpers.service import async_get_all_descriptions
from homeassistant.helpers import template
from homeassistant.components.http import HomeAssistantView from homeassistant.components.http import HomeAssistantView
from homeassistant.const import (
EVENT_HOMEASSISTANT_STOP, EVENT_TIME_CHANGED, HTTP_BAD_REQUEST,
HTTP_CREATED, HTTP_NOT_FOUND, MATCH_ALL, URL_API, URL_API_COMPONENTS,
URL_API_CONFIG, URL_API_DISCOVERY_INFO, URL_API_ERROR_LOG, URL_API_EVENTS,
URL_API_SERVICES, URL_API_STATES, URL_API_STATES_ENTITY, URL_API_STREAM,
URL_API_TEMPLATE, __version__)
import homeassistant.core as ha
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
_LOGGER = logging.getLogger(__name__)
ATTR_BASE_URL = 'base_url'
ATTR_LOCATION_NAME = 'location_name'
ATTR_REQUIRES_API_PASSWORD = 'requires_api_password'
ATTR_VERSION = 'version'
DOMAIN = 'api' DOMAIN = 'api'
DEPENDENCIES = ['http'] DEPENDENCIES = ['http']
STREAM_PING_PAYLOAD = "ping" STREAM_PING_PAYLOAD = 'ping'
STREAM_PING_INTERVAL = 50 # seconds STREAM_PING_INTERVAL = 50 # seconds
_LOGGER = logging.getLogger(__name__)
def setup(hass, config): def setup(hass, config):
"""Register the API with the HTTP interface.""" """Register the API with the HTTP interface."""
@ -62,19 +65,19 @@ class APIStatusView(HomeAssistantView):
"""View to handle Status requests.""" """View to handle Status requests."""
url = URL_API url = URL_API
name = "api:status" name = 'api:status'
@ha.callback @ha.callback
def get(self, request): def get(self, request):
"""Retrieve if API is running.""" """Retrieve if API is running."""
return self.json_message('API running.') return self.json_message("API running.")
class APIEventStream(HomeAssistantView): class APIEventStream(HomeAssistantView):
"""View to handle EventStream requests.""" """View to handle EventStream requests."""
url = URL_API_STREAM url = URL_API_STREAM
name = "api:stream" name = 'api:stream'
async def get(self, request): async def get(self, request):
"""Provide a streaming interface for the event bus.""" """Provide a streaming interface for the event bus."""
@ -95,7 +98,7 @@ class APIEventStream(HomeAssistantView):
if restrict and event.event_type not in restrict: if restrict and event.event_type not in restrict:
return return
_LOGGER.debug('STREAM %s FORWARDING %s', id(stop_obj), event) _LOGGER.debug("STREAM %s FORWARDING %s", id(stop_obj), event)
if event.event_type == EVENT_HOMEASSISTANT_STOP: if event.event_type == EVENT_HOMEASSISTANT_STOP:
data = stop_obj data = stop_obj
@ -111,7 +114,7 @@ class APIEventStream(HomeAssistantView):
unsub_stream = hass.bus.async_listen(MATCH_ALL, forward_events) unsub_stream = hass.bus.async_listen(MATCH_ALL, forward_events)
try: try:
_LOGGER.debug('STREAM %s ATTACHED', id(stop_obj)) _LOGGER.debug("STREAM %s ATTACHED", id(stop_obj))
# Fire off one message so browsers fire open event right away # Fire off one message so browsers fire open event right away
await to_write.put(STREAM_PING_PAYLOAD) await to_write.put(STREAM_PING_PAYLOAD)
@ -126,25 +129,25 @@ class APIEventStream(HomeAssistantView):
break break
msg = "data: {}\n\n".format(payload) msg = "data: {}\n\n".format(payload)
_LOGGER.debug('STREAM %s WRITING %s', id(stop_obj), _LOGGER.debug(
msg.strip()) "STREAM %s WRITING %s", id(stop_obj), msg.strip())
await response.write(msg.encode("UTF-8")) await response.write(msg.encode('UTF-8'))
except asyncio.TimeoutError: except asyncio.TimeoutError:
await to_write.put(STREAM_PING_PAYLOAD) await to_write.put(STREAM_PING_PAYLOAD)
except asyncio.CancelledError: except asyncio.CancelledError:
_LOGGER.debug('STREAM %s ABORT', id(stop_obj)) _LOGGER.debug("STREAM %s ABORT", id(stop_obj))
finally: finally:
_LOGGER.debug('STREAM %s RESPONSE CLOSED', id(stop_obj)) _LOGGER.debug("STREAM %s RESPONSE CLOSED", id(stop_obj))
unsub_stream() unsub_stream()
class APIConfigView(HomeAssistantView): class APIConfigView(HomeAssistantView):
"""View to handle Config requests.""" """View to handle Configuration requests."""
url = URL_API_CONFIG url = URL_API_CONFIG
name = "api:config" name = 'api:config'
@ha.callback @ha.callback
def get(self, request): def get(self, request):
@ -153,22 +156,22 @@ class APIConfigView(HomeAssistantView):
class APIDiscoveryView(HomeAssistantView): class APIDiscoveryView(HomeAssistantView):
"""View to provide discovery info.""" """View to provide Discovery information."""
requires_auth = False requires_auth = False
url = URL_API_DISCOVERY_INFO url = URL_API_DISCOVERY_INFO
name = "api:discovery" name = 'api:discovery'
@ha.callback @ha.callback
def get(self, request): def get(self, request):
"""Get discovery info.""" """Get discovery information."""
hass = request.app['hass'] hass = request.app['hass']
needs_auth = hass.config.api.api_password is not None needs_auth = hass.config.api.api_password is not None
return self.json({ return self.json({
'base_url': hass.config.api.base_url, ATTR_BASE_URL: hass.config.api.base_url,
'location_name': hass.config.location_name, ATTR_LOCATION_NAME: hass.config.location_name,
'requires_api_password': needs_auth, ATTR_REQUIRES_API_PASSWORD: needs_auth,
'version': __version__ ATTR_VERSION: __version__,
}) })
@ -187,8 +190,8 @@ class APIStatesView(HomeAssistantView):
class APIEntityStateView(HomeAssistantView): class APIEntityStateView(HomeAssistantView):
"""View to handle EntityState requests.""" """View to handle EntityState requests."""
url = "/api/states/{entity_id}" url = '/api/states/{entity_id}'
name = "api:entity-state" name = 'api:entity-state'
@ha.callback @ha.callback
def get(self, request, entity_id): def get(self, request, entity_id):
@ -196,7 +199,7 @@ class APIEntityStateView(HomeAssistantView):
state = request.app['hass'].states.get(entity_id) state = request.app['hass'].states.get(entity_id)
if state: if state:
return self.json(state) return self.json(state)
return self.json_message('Entity not found', HTTP_NOT_FOUND) return self.json_message("Entity not found.", HTTP_NOT_FOUND)
async def post(self, request, entity_id): async def post(self, request, entity_id):
"""Update state of entity.""" """Update state of entity."""
@ -204,13 +207,13 @@ class APIEntityStateView(HomeAssistantView):
try: try:
data = await request.json() data = await request.json()
except ValueError: except ValueError:
return self.json_message('Invalid JSON specified', return self.json_message(
HTTP_BAD_REQUEST) "Invalid JSON specified.", HTTP_BAD_REQUEST)
new_state = data.get('state') new_state = data.get('state')
if new_state is None: if new_state is None:
return self.json_message('No state specified', HTTP_BAD_REQUEST) return self.json_message("No state specified.", HTTP_BAD_REQUEST)
attributes = data.get('attributes') attributes = data.get('attributes')
force_update = data.get('force_update', False) force_update = data.get('force_update', False)
@ -232,15 +235,15 @@ class APIEntityStateView(HomeAssistantView):
def delete(self, request, entity_id): def delete(self, request, entity_id):
"""Remove entity.""" """Remove entity."""
if request.app['hass'].states.async_remove(entity_id): if request.app['hass'].states.async_remove(entity_id):
return self.json_message('Entity removed') return self.json_message("Entity removed.")
return self.json_message('Entity not found', HTTP_NOT_FOUND) return self.json_message("Entity not found.", HTTP_NOT_FOUND)
class APIEventListenersView(HomeAssistantView): class APIEventListenersView(HomeAssistantView):
"""View to handle EventListeners requests.""" """View to handle EventListeners requests."""
url = URL_API_EVENTS url = URL_API_EVENTS
name = "api:event-listeners" name = 'api:event-listeners'
@ha.callback @ha.callback
def get(self, request): def get(self, request):
@ -252,7 +255,7 @@ class APIEventView(HomeAssistantView):
"""View to handle Event requests.""" """View to handle Event requests."""
url = '/api/events/{event_type}' url = '/api/events/{event_type}'
name = "api:event" name = 'api:event'
async def post(self, request, event_type): async def post(self, request, event_type):
"""Fire events.""" """Fire events."""
@ -260,12 +263,12 @@ class APIEventView(HomeAssistantView):
try: try:
event_data = json.loads(body) if body else None event_data = json.loads(body) if body else None
except ValueError: except ValueError:
return self.json_message('Event data should be valid JSON', return self.json_message(
HTTP_BAD_REQUEST) "Event data should be valid JSON.", HTTP_BAD_REQUEST)
if event_data is not None and not isinstance(event_data, dict): if event_data is not None and not isinstance(event_data, dict):
return self.json_message('Event data should be a JSON object', return self.json_message(
HTTP_BAD_REQUEST) "Event data should be a JSON object", HTTP_BAD_REQUEST)
# Special case handling for event STATE_CHANGED # Special case handling for event STATE_CHANGED
# We will try to convert state dicts back to State objects # We will try to convert state dicts back to State objects
@ -276,8 +279,8 @@ class APIEventView(HomeAssistantView):
if state: if state:
event_data[key] = state event_data[key] = state
request.app['hass'].bus.async_fire(event_type, event_data, request.app['hass'].bus.async_fire(
ha.EventOrigin.remote) event_type, event_data, ha.EventOrigin.remote)
return self.json_message("Event {} fired.".format(event_type)) return self.json_message("Event {} fired.".format(event_type))
@ -286,7 +289,7 @@ class APIServicesView(HomeAssistantView):
"""View to handle Services requests.""" """View to handle Services requests."""
url = URL_API_SERVICES url = URL_API_SERVICES
name = "api:services" name = 'api:services'
async def get(self, request): async def get(self, request):
"""Get registered services.""" """Get registered services."""
@ -297,8 +300,8 @@ class APIServicesView(HomeAssistantView):
class APIDomainServicesView(HomeAssistantView): class APIDomainServicesView(HomeAssistantView):
"""View to handle DomainServices requests.""" """View to handle DomainServices requests."""
url = "/api/services/{domain}/{service}" url = '/api/services/{domain}/{service}'
name = "api:domain-services" name = 'api:domain-services'
async def post(self, request, domain, service): async def post(self, request, domain, service):
"""Call a service. """Call a service.
@ -310,8 +313,8 @@ class APIDomainServicesView(HomeAssistantView):
try: try:
data = json.loads(body) if body else None data = json.loads(body) if body else None
except ValueError: except ValueError:
return self.json_message('Data should be valid JSON', return self.json_message(
HTTP_BAD_REQUEST) "Data should be valid JSON.", HTTP_BAD_REQUEST)
with AsyncTrackStates(hass) as changed_states: with AsyncTrackStates(hass) as changed_states:
await hass.services.async_call(domain, service, data, True) await hass.services.async_call(domain, service, data, True)
@ -323,7 +326,7 @@ class APIComponentsView(HomeAssistantView):
"""View to handle Components requests.""" """View to handle Components requests."""
url = URL_API_COMPONENTS url = URL_API_COMPONENTS
name = "api:components" name = 'api:components'
@ha.callback @ha.callback
def get(self, request): def get(self, request):
@ -332,10 +335,10 @@ class APIComponentsView(HomeAssistantView):
class APITemplateView(HomeAssistantView): class APITemplateView(HomeAssistantView):
"""View to handle requests.""" """View to handle Template requests."""
url = URL_API_TEMPLATE url = URL_API_TEMPLATE
name = "api:template" name = 'api:template'
async def post(self, request): async def post(self, request):
"""Render a template.""" """Render a template."""
@ -344,30 +347,29 @@ class APITemplateView(HomeAssistantView):
tpl = template.Template(data['template'], request.app['hass']) tpl = template.Template(data['template'], request.app['hass'])
return tpl.async_render(data.get('variables')) return tpl.async_render(data.get('variables'))
except (ValueError, TemplateError) as ex: except (ValueError, TemplateError) as ex:
return self.json_message('Error rendering template: {}'.format(ex), return self.json_message(
HTTP_BAD_REQUEST) "Error rendering template: {}".format(ex), HTTP_BAD_REQUEST)
class APIErrorLog(HomeAssistantView): class APIErrorLog(HomeAssistantView):
"""View to fetch the error log.""" """View to fetch the API error log."""
url = URL_API_ERROR_LOG url = URL_API_ERROR_LOG
name = "api:error_log" name = 'api:error_log'
async def get(self, request): async def get(self, request):
"""Retrieve API error log.""" """Retrieve API error log."""
return web.FileResponse( return web.FileResponse(request.app['hass'].data[DATA_LOGGING])
request.app['hass'].data[DATA_LOGGING])
async def async_services_json(hass): async def async_services_json(hass):
"""Generate services data to JSONify.""" """Generate services data to JSONify."""
descriptions = await async_get_all_descriptions(hass) descriptions = await async_get_all_descriptions(hass)
return [{"domain": key, "services": value} return [{'domain': key, 'services': value}
for key, value in descriptions.items()] for key, value in descriptions.items()]
def async_events_json(hass): def async_events_json(hass):
"""Generate event data to JSONify.""" """Generate event data to JSONify."""
return [{"event": key, "listener_count": value} return [{'event': key, 'listener_count': value}
for key, value in hass.bus.async_listeners().items()] for key, value in hass.bus.async_listeners().items()]