diff options
author | Evgeny Zinoviev <me@ch1p.io> | 2023-06-10 23:02:34 +0300 |
---|---|---|
committer | Evgeny Zinoviev <me@ch1p.io> | 2023-06-10 23:02:34 +0300 |
commit | b0bf43e6a272d42a55158e657bd937cb82fc3d8d (patch) | |
tree | f1bc13253bc028abcaed9c88882f5aee384a269c /src/home/soundsensor | |
parent | f3b9d50496257d87757802dfb472b5ffae11962c (diff) |
move files, rename home package to homekit
Diffstat (limited to 'src/home/soundsensor')
-rw-r--r-- | src/home/soundsensor/__init__.py | 22 | ||||
-rw-r--r-- | src/home/soundsensor/__init__.pyi | 8 | ||||
-rw-r--r-- | src/home/soundsensor/node.py | 75 | ||||
-rw-r--r-- | src/home/soundsensor/server.py | 128 | ||||
-rw-r--r-- | src/home/soundsensor/server_client.py | 38 |
5 files changed, 0 insertions, 271 deletions
diff --git a/src/home/soundsensor/__init__.py b/src/home/soundsensor/__init__.py deleted file mode 100644 index 30052f8..0000000 --- a/src/home/soundsensor/__init__.py +++ /dev/null @@ -1,22 +0,0 @@ -import importlib - -__all__ = [ - 'SoundSensorNode', - 'SoundSensorHitHandler', - 'SoundSensorServer', - 'SoundSensorServerGuardClient' -] - - -def __getattr__(name): - if name in __all__: - if name == 'SoundSensorNode': - file = 'node' - elif name == 'SoundSensorServerGuardClient': - file = 'server_client' - else: - file = 'server' - module = importlib.import_module(f'.{file}', __name__) - return getattr(module, name) - - raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/src/home/soundsensor/__init__.pyi b/src/home/soundsensor/__init__.pyi deleted file mode 100644 index cb34972..0000000 --- a/src/home/soundsensor/__init__.pyi +++ /dev/null @@ -1,8 +0,0 @@ -from .server import ( - SoundSensorHitHandler as SoundSensorHitHandler, - SoundSensorServer as SoundSensorServer, -) -from .server_client import ( - SoundSensorServerGuardClient as SoundSensorServerGuardClient -) -from .node import SoundSensorNode as SoundSensorNode diff --git a/src/home/soundsensor/node.py b/src/home/soundsensor/node.py deleted file mode 100644 index 292452f..0000000 --- a/src/home/soundsensor/node.py +++ /dev/null @@ -1,75 +0,0 @@ -import logging -import threading - -from typing import Optional -from time import sleep -from ..util import stringify, send_datagram, Addr - -from pyA20.gpio import gpio -from pyA20.gpio import port as gpioport - -logger = logging.getLogger(__name__) - - -class SoundSensorNode: - def __init__(self, - name: str, - pinname: str, - server_addr: Optional[Addr], - threshold: int = 1, - delay=0.005): - - if not hasattr(gpioport, pinname): - raise ValueError(f'invalid pin {pinname}') - - self.pin = getattr(gpioport, pinname) - self.name = name - self.delay = delay - self.threshold = threshold - - self.server_addr = server_addr - - self.hits = 0 - self.hitlock = threading.Lock() - - self.interrupted = False - - def run(self): - try: - t = threading.Thread(target=self.sensor_reader) - t.daemon = True - t.start() - - while True: - with self.hitlock: - hits = self.hits - self.hits = 0 - - if hits >= self.threshold: - try: - if self.server_addr is not None: - send_datagram(stringify([self.name, hits]), self.server_addr) - else: - logger.debug(f'server reporting disabled, skipping reporting {hits} hits') - except OSError as exc: - logger.exception(exc) - - sleep(1) - - except (KeyboardInterrupt, SystemExit) as e: - self.interrupted = True - logger.info(str(e)) - - def sensor_reader(self): - gpio.init() - gpio.setcfg(self.pin, gpio.INPUT) - gpio.pullup(self.pin, gpio.PULLUP) - - while not self.interrupted: - state = gpio.input(self.pin) - sleep(self.delay) - - if not state: - with self.hitlock: - logger.debug('got a hit') - self.hits += 1 diff --git a/src/home/soundsensor/server.py b/src/home/soundsensor/server.py deleted file mode 100644 index a627390..0000000 --- a/src/home/soundsensor/server.py +++ /dev/null @@ -1,128 +0,0 @@ -import asyncio -import json -import logging -import threading - -from ..database.sqlite import SQLiteBase -from ..config import config -from .. import http - -from typing import Type -from ..util import Addr - -logger = logging.getLogger(__name__) - - -class SoundSensorHitHandler(asyncio.DatagramProtocol): - def datagram_received(self, data, addr): - try: - data = json.loads(data) - except json.JSONDecodeError as e: - logger.error('failed to parse json datagram') - logger.exception(e) - return - - try: - name, hits = data - except (ValueError, IndexError) as e: - logger.error('failed to unpack data') - logger.exception(e) - return - - self.handler(name, hits) - - def handler(self, name: str, hits: int): - pass - - -class Database(SQLiteBase): - SCHEMA = 1 - - def __init__(self): - super().__init__(dbname='sound_sensor_server') - - def schema_init(self, version: int) -> None: - cursor = self.cursor() - - if version < 1: - cursor.execute("CREATE TABLE IF NOT EXISTS status (guard_enabled INTEGER NOT NULL)") - cursor.execute("INSERT INTO status (guard_enabled) VALUES (-1)") - - self.commit() - - def get_guard_enabled(self) -> int: - cur = self.cursor() - cur.execute("SELECT guard_enabled FROM status LIMIT 1") - return int(cur.fetchone()[0]) - - def set_guard_enabled(self, enabled: bool) -> None: - cur = self.cursor() - cur.execute("UPDATE status SET guard_enabled=?", (int(enabled),)) - self.commit() - - -class SoundSensorServer: - def __init__(self, - addr: Addr, - handler_impl: Type[SoundSensorHitHandler]): - self.addr = addr - self.impl = handler_impl - self.db = Database() - - self._recording_lock = threading.Lock() - self._recording_enabled = True - - if self.guard_control_enabled(): - current_status = self.db.get_guard_enabled() - if current_status == -1: - self.set_recording(config['server']['guard_recording_default'] - if 'guard_recording_default' in config['server'] - else False, - update=False) - else: - self.set_recording(bool(current_status), update=False) - - @staticmethod - def guard_control_enabled() -> bool: - return 'guard_control' in config['server'] and config['server']['guard_control'] is True - - def set_recording(self, enabled: bool, update=True): - with self._recording_lock: - self._recording_enabled = enabled - if update: - self.db.set_guard_enabled(enabled) - - def is_recording_enabled(self) -> bool: - with self._recording_lock: - return self._recording_enabled - - def run(self): - if self.guard_control_enabled(): - t = threading.Thread(target=self.run_guard_server) - t.daemon = True - t.start() - - loop = asyncio.get_event_loop() - t = loop.create_datagram_endpoint(self.impl, local_addr=self.addr) - loop.run_until_complete(t) - loop.run_forever() - - def run_guard_server(self): - routes = http.routes() - - @routes.post('/guard/enable') - async def guard_enable(request): - self.set_recording(True) - return http.ok() - - @routes.post('/guard/disable') - async def guard_disable(request): - self.set_recording(False) - return http.ok() - - @routes.get('/guard/status') - async def guard_status(request): - return http.ok({'enabled': self.is_recording_enabled()}) - - asyncio.set_event_loop(asyncio.new_event_loop()) # need to create new event loop in new thread - http.serve(self.addr, routes, handle_signals=False) # handle_signals=True doesn't work in separate thread diff --git a/src/home/soundsensor/server_client.py b/src/home/soundsensor/server_client.py deleted file mode 100644 index 7eef996..0000000 --- a/src/home/soundsensor/server_client.py +++ /dev/null @@ -1,38 +0,0 @@ -import requests -import logging - -from ..util import Addr -from ..api.errors import ApiResponseError - - -class SoundSensorServerGuardClient: - def __init__(self, addr: Addr): - self.endpoint = f'http://{addr[0]}:{addr[1]}' - self.logger = logging.getLogger(self.__class__.__name__) - - def guard_enable(self): - return self._call('guard/enable', is_post=True) - - def guard_disable(self): - return self._call('guard/disable', is_post=True) - - def guard_status(self): - return self._call('guard/status') - - def _call(self, - method: str, - is_post=False): - - url = f'{self.endpoint}/{method}' - self.logger.debug(f'calling {url}') - - r = requests.get(url) if not is_post else requests.post(url) - - if r.status_code != 200: - response = r.json() - raise ApiResponseError(status_code=r.status_code, - error_type=response['error'], - error_message=response['message'] or None, - error_stacktrace=response['stacktrace'] if 'stacktrace' in response else None) - - return r.json()['response'] |