Add url validators
This commit is contained in:
parent
4d7555957c
commit
5aa0158761
5 changed files with 92 additions and 31 deletions
|
@ -214,7 +214,7 @@ class APIStatesView(HomeAssistantView):
|
|||
class APIEntityStateView(HomeAssistantView):
|
||||
"""View to handle EntityState requests."""
|
||||
|
||||
url = "/api/states/<entity_id>"
|
||||
url = "/api/states/<entity(exist=False):entity_id>"
|
||||
name = "api:entity-state"
|
||||
|
||||
def get(self, request, entity_id):
|
||||
|
|
|
@ -147,7 +147,7 @@ class CameraView(HomeAssistantView):
|
|||
class CameraImageView(CameraView):
|
||||
"""Camera view to serve an image."""
|
||||
|
||||
url = "/api/camera_proxy/<entity_id>"
|
||||
url = "/api/camera_proxy/<entity(domain=camera):entity_id>"
|
||||
name = "api:camera:image"
|
||||
|
||||
def get(self, request, entity_id):
|
||||
|
@ -168,7 +168,7 @@ class CameraImageView(CameraView):
|
|||
class CameraMjpegStream(CameraView):
|
||||
"""Camera View to serve an MJPEG stream."""
|
||||
|
||||
url = "/api/camera_proxy_stream/<entity_id>"
|
||||
url = "/api/camera_proxy_stream/<entity(domain=camera):entity_id>"
|
||||
name = "api:camera:stream"
|
||||
|
||||
def get(self, request, entity_id):
|
||||
|
|
|
@ -165,7 +165,7 @@ def setup(hass, config):
|
|||
class Last5StatesView(HomeAssistantView):
|
||||
"""Handle last 5 state view requests."""
|
||||
|
||||
url = '/api/history/entity/<entity_id>/recent_states'
|
||||
url = '/api/history/entity/<entity:entity_id>/recent_states'
|
||||
name = 'api:history:entity-recent-states'
|
||||
|
||||
def get(self, request, entity_id):
|
||||
|
@ -178,7 +178,7 @@ class HistoryPeriodView(HomeAssistantView):
|
|||
|
||||
url = '/api/history/period'
|
||||
name = 'api:history:entity-recent-states'
|
||||
extra_urls = ['/api/history/period/<date>']
|
||||
extra_urls = ['/api/history/period/<date:date>']
|
||||
|
||||
def get(self, request, date=None):
|
||||
"""Return history over a period of time."""
|
||||
|
|
|
@ -10,6 +10,8 @@ import homeassistant.core as ha
|
|||
import homeassistant.remote as rem
|
||||
from homeassistant import util
|
||||
from homeassistant.const import SERVER_PORT, HTTP_HEADER_HA_AUTH
|
||||
from homeassistant.helpers.entity import valid_entity_id, split_entity_id
|
||||
import homeassistant.util.dt as dt_util
|
||||
|
||||
DOMAIN = "http"
|
||||
REQUIREMENTS = ("eventlet==0.18.4", "static3==0.6.1", "Werkzeug==0.11.5",)
|
||||
|
@ -77,6 +79,85 @@ def setup(hass, config):
|
|||
# return app(environ, start_response)
|
||||
|
||||
|
||||
def request_class():
|
||||
"""Generate request class.
|
||||
|
||||
Done in method because of imports."""
|
||||
from werkzeug.exceptions import BadRequest
|
||||
from werkzeug.wrappers import BaseRequest, AcceptMixin
|
||||
from werkzeug.utils import cached_property
|
||||
|
||||
class Request(BaseRequest, AcceptMixin):
|
||||
"""Base class for incoming requests."""
|
||||
|
||||
@cached_property
|
||||
def json(self):
|
||||
"""Get the result of json.loads if possible."""
|
||||
if not self.data:
|
||||
return None
|
||||
# elif 'json' not in self.environ.get('CONTENT_TYPE', ''):
|
||||
# raise BadRequest('Not a JSON request')
|
||||
try:
|
||||
return json.loads(self.data.decode(
|
||||
self.charset, self.encoding_errors))
|
||||
except (TypeError, ValueError):
|
||||
raise BadRequest('Unable to read JSON request')
|
||||
|
||||
return Request
|
||||
|
||||
|
||||
def routing_map(hass):
|
||||
"""Generate empty routing map with HA validators."""
|
||||
from werkzeug.routing import Map, BaseConverter, ValidationError
|
||||
|
||||
class EntityValidator(BaseConverter):
|
||||
"""Validate entity_id in urls."""
|
||||
regex = r"(\w+)\.(\w+)"
|
||||
|
||||
def __init__(self, url_map, exist=True, domain=None):
|
||||
"""Initilalize entity validator."""
|
||||
super().__init__(url_map)
|
||||
self._exist = exist
|
||||
self._domain = domain
|
||||
|
||||
def to_python(self, value):
|
||||
"""Validate entity id."""
|
||||
if self._exist and hass.states.get(value) is None:
|
||||
raise ValidationError()
|
||||
if self._domain is not None and \
|
||||
split_entity_id(value)[0] != self._domain:
|
||||
raise ValidationError()
|
||||
|
||||
return value
|
||||
|
||||
def to_url(self, value):
|
||||
"""Convert entity_id for a url."""
|
||||
return value
|
||||
|
||||
class DateValidator(BaseConverter):
|
||||
"""Validate dates in urls."""
|
||||
|
||||
regex = r'\d{4}-(0[1-9])|(1[012])-((0[1-9])|([12]\d)|(3[01]))'
|
||||
|
||||
def to_python(self, value):
|
||||
"""Validate and convert date."""
|
||||
parsed = dt_util.parse_date(value)
|
||||
|
||||
if value is None:
|
||||
raise ValidationError()
|
||||
|
||||
return parsed
|
||||
|
||||
def to_url(self, value):
|
||||
"""Convert date to url value."""
|
||||
return value.isoformat()
|
||||
|
||||
return Map(converters={
|
||||
'entity': EntityValidator,
|
||||
'date': DateValidator,
|
||||
})
|
||||
|
||||
|
||||
class HomeAssistantWSGI(object):
|
||||
"""WSGI server for Home Assistant."""
|
||||
|
||||
|
@ -86,33 +167,13 @@ class HomeAssistantWSGI(object):
|
|||
def __init__(self, hass, development, api_password, ssl_certificate,
|
||||
ssl_key, server_host, server_port):
|
||||
"""Initilalize the WSGI Home Assistant server."""
|
||||
from werkzeug.exceptions import BadRequest
|
||||
from werkzeug.wrappers import BaseRequest, AcceptMixin
|
||||
from werkzeug.routing import Map
|
||||
from werkzeug.utils import cached_property
|
||||
from werkzeug.wrappers import Response
|
||||
|
||||
class Request(BaseRequest, AcceptMixin):
|
||||
"""Base class for incoming requests."""
|
||||
|
||||
@cached_property
|
||||
def json(self):
|
||||
"""Get the result of json.loads if possible."""
|
||||
if not self.data:
|
||||
return None
|
||||
# elif 'json' not in self.environ.get('CONTENT_TYPE', ''):
|
||||
# raise BadRequest('Not a JSON request')
|
||||
try:
|
||||
return json.loads(self.data.decode(
|
||||
self.charset, self.encoding_errors))
|
||||
except (TypeError, ValueError):
|
||||
raise BadRequest('Unable to read JSON request')
|
||||
|
||||
Response.mimetype = 'text/html'
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
self.Request = Request
|
||||
self.url_map = Map()
|
||||
self.Request = request_class()
|
||||
self.url_map = routing_map(hass)
|
||||
self.views = {}
|
||||
self.hass = hass
|
||||
self.extra_apps = {}
|
||||
|
@ -340,13 +401,13 @@ class HomeAssistantView(object):
|
|||
from werkzeug.exceptions import NotFound
|
||||
|
||||
if isinstance(fil, str):
|
||||
if mimetype is None:
|
||||
mimetype = mimetypes.guess_type(fil)[0]
|
||||
|
||||
try:
|
||||
fil = open(fil)
|
||||
except IOError:
|
||||
raise NotFound()
|
||||
|
||||
if mimetype is None:
|
||||
mimetype = mimetypes.guess_type(fil)[0]
|
||||
|
||||
return self.Response(wrap_file(request.environ, fil),
|
||||
mimetype=mimetype, direct_passthrough=True)
|
||||
|
|
|
@ -89,7 +89,7 @@ class LogbookView(HomeAssistantView):
|
|||
|
||||
url = '/api/logbook'
|
||||
name = 'api:logbook'
|
||||
extra_urls = ['/api/logbook/<date>']
|
||||
extra_urls = ['/api/logbook/<date:date>']
|
||||
|
||||
def get(self, request, date=None):
|
||||
"""Retrieve logbook entries."""
|
||||
|
|
Loading…
Add table
Reference in a new issue