summaryrefslogtreecommitdiff
path: root/src/web_api.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/web_api.py')
-rwxr-xr-xsrc/web_api.py239
1 files changed, 0 insertions, 239 deletions
diff --git a/src/web_api.py b/src/web_api.py
deleted file mode 100755
index 0ddc6bd..0000000
--- a/src/web_api.py
+++ /dev/null
@@ -1,239 +0,0 @@
-#!/usr/bin/env python3
-import asyncio
-import json
-import os
-
-from datetime import datetime, timedelta
-
-from aiohttp import web
-from home import http
-from home.config import config, is_development_mode
-from home.database import BotsDatabase, SensorsDatabase, InverterDatabase
-from home.database.inverter_time_formats import *
-from home.api.types import BotType, TemperatureSensorLocation, SoundSensorLocation
-from home.media import SoundRecordStorage
-
-
-def strptime_auto(s: str) -> datetime:
- e = None
- for fmt in (FormatTime, FormatDate):
- try:
- return datetime.strptime(s, fmt)
- except ValueError as _e:
- e = _e
- raise e
-
-
-class AuthError(Exception):
- def __init__(self, message: str):
- super().__init__()
- self.message = message
-
-
-class WebAPIServer(http.HTTPServer):
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
-
- self.app.middlewares.append(self.validate_auth)
-
- self.get('/', self.get_index)
- self.get('/sensors/data/', self.GET_sensors_data)
- self.get('/sound_sensors/hits/', self.GET_sound_sensors_hits)
- self.post('/sound_sensors/hits/', self.POST_sound_sensors_hits)
-
- self.post('/log/bot_request/', self.POST_bot_request_log)
- self.post('/log/openwrt/', self.POST_openwrt_log)
-
- self.get('/inverter/consumed_energy/', self.GET_consumed_energy)
- self.get('/inverter/grid_consumed_energy/', self.GET_grid_consumed_energy)
-
- self.get('/recordings/list/', self.GET_recordings_list)
-
- @staticmethod
- @web.middleware
- async def validate_auth(req: http.Request, handler):
- def get_token() -> str:
- name = 'X-Token'
- if name in req.headers:
- return req.headers[name]
-
- return req.query['token']
-
- try:
- token = get_token()
- except KeyError:
- raise AuthError('no token')
-
- if token != config['api']['token']:
- raise AuthError('invalid token')
-
- return await handler(req)
-
- @staticmethod
- async def get_index(req: http.Request):
- message = "nothing here, keep lurking"
- if is_development_mode():
- message += ' (dev mode)'
- return http.Response(text=message, content_type='text/plain')
-
- async def GET_sensors_data(self, req: http.Request):
- try:
- hours = int(req.query['hours'])
- if hours < 1 or hours > 24:
- raise ValueError('invalid hours value')
- except KeyError:
- hours = 1
-
- sensor = TemperatureSensorLocation(int(req.query['sensor']))
-
- dt_to = datetime.now()
- dt_from = dt_to - timedelta(hours=hours)
-
- db = SensorsDatabase()
- data = db.get_temperature_recordings(sensor, (dt_from, dt_to))
- return self.ok(data)
-
- async def GET_sound_sensors_hits(self, req: http.Request):
- location = SoundSensorLocation(int(req.query['location']))
-
- after = int(req.query['after'])
- kwargs = {}
- if after is None:
- last = int(req.query['last'])
- if last is None:
- raise ValueError('you must pass `after` or `last` params')
- else:
- if not 0 < last < 100:
- raise ValueError('invalid last value: must be between 0 and 100')
- kwargs['last'] = last
- else:
- kwargs['after'] = datetime.fromtimestamp(after)
-
- data = BotsDatabase().get_sound_hits(location, **kwargs)
- return self.ok(data)
-
- async def POST_sound_sensors_hits(self, req: http.Request):
- hits = []
- data = await req.post()
- for hit, count in json.loads(data['hits']):
- if not hasattr(SoundSensorLocation, hit.upper()):
- raise ValueError('invalid sensor location')
- if count < 1:
- raise ValueError(f'invalid count: {count}')
- hits.append((SoundSensorLocation[hit.upper()], count))
-
- BotsDatabase().add_sound_hits(hits, datetime.now())
- return self.ok()
-
- async def POST_bot_request_log(self, req: http.Request):
- data = await req.post()
-
- try:
- user_id = int(data['user_id'])
- except KeyError:
- user_id = 0
-
- try:
- message = data['message']
- except KeyError:
- message = ''
-
- bot = BotType(int(data['bot']))
-
- # validate message
- if message.strip() == '':
- raise ValueError('message can\'t be empty')
-
- # add record to the database
- BotsDatabase().add_request(bot, user_id, message)
-
- return self.ok()
-
- async def POST_openwrt_log(self, req: http.Request):
- data = await req.post()
-
- try:
- logs = data['logs']
- ap = int(data['ap'])
- except KeyError:
- logs = ''
- ap = 0
-
- # validate it
- logs = json.loads(logs)
- assert type(logs) is list, "invalid json data (list expected)"
-
- lines = []
- for line in logs:
- assert type(line) is list, "invalid line type (list expected)"
- assert len(line) == 2, f"expected 2 items in line, got {len(line)}"
- assert type(line[0]) is int, "invalid line[0] type (int expected)"
- assert type(line[1]) is str, "invalid line[1] type (str expected)"
-
- lines.append((
- datetime.fromtimestamp(line[0]),
- line[1]
- ))
-
- BotsDatabase().add_openwrt_logs(lines, ap)
- return self.ok()
-
- async def GET_recordings_list(self, req: http.Request):
- data = await req.post()
-
- try:
- extended = bool(int(data['extended']))
- except KeyError:
- extended = False
-
- node = data['node']
-
- root = os.path.join(config['recordings']['directory'], node)
- if not os.path.isdir(root):
- raise ValueError(f'invalid node {node}: no such directory')
-
- storage = SoundRecordStorage(root)
- files = storage.getfiles(as_objects=extended)
- if extended:
- files = list(map(lambda file: file.__dict__(), files))
-
- return self.ok(files)
-
- @staticmethod
- def _get_inverter_from_to(req: http.Request):
- s_from = req.query['from']
- s_to = req.query['to']
-
- dt_from = strptime_auto(s_from)
-
- if s_to == 'now':
- dt_to = datetime.now()
- else:
- dt_to = strptime_auto(s_to)
-
- return dt_from, dt_to
-
- async def GET_consumed_energy(self, req: http.Request):
- dt_from, dt_to = self._get_inverter_from_to(req)
- wh = InverterDatabase().get_consumed_energy(dt_from, dt_to)
- return self.ok(wh)
-
- async def GET_grid_consumed_energy(self, req: http.Request):
- dt_from, dt_to = self._get_inverter_from_to(req)
- wh = InverterDatabase().get_grid_consumed_energy(dt_from, dt_to)
- return self.ok(wh)
-
-
-# start of the program
-# --------------------
-
-if __name__ == '__main__':
- _app_name = 'web_api'
- if is_development_mode():
- _app_name += '_dev'
- config.load(_app_name)
-
- loop = asyncio.get_event_loop()
-
- server = WebAPIServer(config.get_addr('server.listen'))
- server.run()