summaryrefslogtreecommitdiff
path: root/src/sound_sensor_server.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/sound_sensor_server.py')
-rwxr-xr-xsrc/sound_sensor_server.py199
1 files changed, 0 insertions, 199 deletions
diff --git a/src/sound_sensor_server.py b/src/sound_sensor_server.py
deleted file mode 100755
index aa62608..0000000
--- a/src/sound_sensor_server.py
+++ /dev/null
@@ -1,199 +0,0 @@
-#!/usr/bin/env python3
-import logging
-import threading
-
-from time import sleep
-from typing import Optional, List, Dict, Tuple
-from functools import partial
-from home.config import config
-from home.util import parse_addr
-from home.api import WebAPIClient, RequestParams
-from home.api.types import SoundSensorLocation
-from home.soundsensor import SoundSensorServer, SoundSensorHitHandler
-from home.media import MediaNodeType, SoundRecordClient, CameraRecordClient, RecordClient
-
-interrupted = False
-logger = logging.getLogger(__name__)
-server: SoundSensorServer
-
-
-def get_related_nodes(node_type: MediaNodeType,
- sensor_name: str) -> List[str]:
- try:
- if sensor_name not in config[f'sensor_to_{node_type.name.lower()}_nodes_relations']:
- raise ValueError(f'unexpected sensor name {sensor_name}')
- return config[f'sensor_to_{node_type.name.lower()}_nodes_relations'][sensor_name]
- except KeyError:
- return []
-
-
-def get_node_config(node_type: MediaNodeType,
- name: str) -> Optional[dict]:
- if name in config[f'{node_type.name.lower()}_nodes']:
- cfg = config[f'{node_type.name.lower()}_nodes'][name]
- if 'min_hits' not in cfg:
- cfg['min_hits'] = 1
- return cfg
- else:
- return None
-
-
-class HitCounter:
- def __init__(self):
- self.sensors = {}
- self.lock = threading.Lock()
- self._reset_sensors()
-
- def _reset_sensors(self):
- for loc in SoundSensorLocation:
- self.sensors[loc.name.lower()] = 0
-
- def add(self, name: str, hits: int):
- if name not in self.sensors:
- raise ValueError(f'sensor {name} not found')
-
- with self.lock:
- self.sensors[name] += hits
-
- def get_all(self) -> List[Tuple[str, int]]:
- vals = []
- with self.lock:
- for name, hits in self.sensors.items():
- if hits > 0:
- vals.append((name, hits))
- self._reset_sensors()
- return vals
-
-
-class HitHandler(SoundSensorHitHandler):
- def handler(self, name: str, hits: int):
- if not hasattr(SoundSensorLocation, name.upper()):
- logger.error(f'invalid sensor name: {name}')
- return
-
- should_continue = False
- for node_type in MediaNodeType:
- try:
- nodes = get_related_nodes(node_type, name)
- except ValueError:
- logger.error(f'config for {node_type.name.lower()} node {name} not found')
- return
-
- for node in nodes:
- node_config = get_node_config(node_type, node)
- if node_config is None:
- logger.error(f'config for {node_type.name.lower()} node {node} not found')
- continue
- if hits < node_config['min_hits']:
- continue
- should_continue = True
-
- if not should_continue:
- return
-
- hc.add(name, hits)
-
- if not server.is_recording_enabled():
- return
- for node_type in MediaNodeType:
- try:
- nodes = get_related_nodes(node_type, name)
- for node in nodes:
- node_config = get_node_config(node_type, node)
- if node_config is None:
- logger.error(f'node config for {node_type.name.lower()} node {node} not found')
- continue
-
- durations = node_config['durations']
- dur = durations[1] if hits > node_config['min_hits'] else durations[0]
- record_clients[node_type].record(node, dur*60, {'node': node})
-
- except ValueError as exc:
- logger.exception(exc)
-
-
-def hits_sender():
- while not interrupted:
- all_hits = hc.get_all()
- if all_hits:
- api.add_sound_sensor_hits(all_hits)
- sleep(5)
-
-
-api: Optional[WebAPIClient] = None
-hc: Optional[HitCounter] = None
-record_clients: Dict[MediaNodeType, RecordClient] = {}
-
-
-# record callbacks
-# ----------------
-
-def record_error(type: MediaNodeType,
- info: dict,
- userdata: dict):
- node = userdata['node']
- logger.error('recording ' + str(dict) + f' from {type.name.lower()} node ' + node + ' failed')
-
- record_clients[type].forget(node, info['id'])
-
-
-def record_finished(type: MediaNodeType,
- info: dict,
- fn: str,
- userdata: dict):
- logger.debug(f'{type.name.lower()} record finished: ' + str(info))
-
- # audio could have been requested by other user (telegram bot, for example)
- # so we shouldn't 'forget' it here
-
- # node = userdata['node']
- # record.forget(node, info['id'])
-
-
-# api client callbacks
-# --------------------
-
-def api_error_handler(exc, name, req: RequestParams):
- logger.error(f'api call ({name}, params={req.params}) failed, exception below')
- logger.exception(exc)
-
-
-if __name__ == '__main__':
- config.load('sound_sensor_server')
-
- hc = HitCounter()
- api = WebAPIClient(timeout=(10, 60))
- api.enable_async(error_handler=api_error_handler)
-
- t = threading.Thread(target=hits_sender)
- t.daemon = True
- t.start()
-
- sound_nodes = {}
- if 'sound_nodes' in config:
- for nodename, nodecfg in config['sound_nodes'].items():
- sound_nodes[nodename] = parse_addr(nodecfg['addr'])
-
- camera_nodes = {}
- if 'camera_nodes' in config:
- for nodename, nodecfg in config['camera_nodes'].items():
- camera_nodes[nodename] = parse_addr(nodecfg['addr'])
-
- if sound_nodes:
- record_clients[MediaNodeType.SOUND] = SoundRecordClient(sound_nodes,
- error_handler=partial(record_error, MediaNodeType.SOUND),
- finished_handler=partial(record_finished, MediaNodeType.SOUND))
-
- if camera_nodes:
- record_clients[MediaNodeType.CAMERA] = CameraRecordClient(camera_nodes,
- error_handler=partial(record_error, MediaNodeType.CAMERA),
- finished_handler=partial(record_finished, MediaNodeType.CAMERA))
-
- try:
- server = SoundSensorServer(config.get_addr('server.listen'), HitHandler)
- server.run()
- except KeyboardInterrupt:
- interrupted = True
- for c in record_clients.values():
- c.stop()
- logging.info('keyboard interrupt, exiting...')