summaryrefslogtreecommitdiff
path: root/src/home/soundsensor
diff options
context:
space:
mode:
authorEvgeny Zinoviev <me@ch1p.io>2023-06-10 23:02:34 +0300
committerEvgeny Zinoviev <me@ch1p.io>2023-06-10 23:02:34 +0300
commitb0bf43e6a272d42a55158e657bd937cb82fc3d8d (patch)
treef1bc13253bc028abcaed9c88882f5aee384a269c /src/home/soundsensor
parentf3b9d50496257d87757802dfb472b5ffae11962c (diff)
move files, rename home package to homekit
Diffstat (limited to 'src/home/soundsensor')
-rw-r--r--src/home/soundsensor/__init__.py22
-rw-r--r--src/home/soundsensor/__init__.pyi8
-rw-r--r--src/home/soundsensor/node.py75
-rw-r--r--src/home/soundsensor/server.py128
-rw-r--r--src/home/soundsensor/server_client.py38
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']