diff options
Diffstat (limited to 'bin/sound_node.py')
-rwxr-xr-x | bin/sound_node.py | 91 |
1 files changed, 91 insertions, 0 deletions
diff --git a/bin/sound_node.py b/bin/sound_node.py new file mode 100755 index 0000000..90e6997 --- /dev/null +++ b/bin/sound_node.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python3 +import os +import __py_include + +from typing import Optional + +from homekit.config import config +from homekit.audio import amixer +from homekit.media import MediaNodeServer, SoundRecordStorage, SoundRecorder +from homekit import http + + +# This script must be run as root as it runs arecord. +# Implements HTTP API for amixer and arecord. +# ------------------------------------------- + +def _amixer_control_response(control): + info = amixer.get(control) + caps = amixer.get_caps(control) + return http.ok({ + 'caps': caps, + 'info': info + }) + + +class SoundNodeServer(MediaNodeServer): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + self.get('/amixer/get-all/', self.amixer_get_all) + self.get('/amixer/get/{control}/', self.amixer_get) + self.get('/amixer/{op:mute|unmute|cap|nocap}/{control}/', self.amixer_set) + self.get('/amixer/{op:incr|decr}/{control}/', self.amixer_volume) + + async def amixer_get_all(self, request: http.Request): + controls_info = amixer.get_all() + return self.ok(controls_info) + + async def amixer_get(self, request: http.Request): + control = request.match_info['control'] + if not amixer.has_control(control): + raise ValueError(f'invalid control: {control}') + + return _amixer_control_response(control) + + async def amixer_set(self, request: http.Request): + op = request.match_info['op'] + control = request.match_info['control'] + if not amixer.has_control(control): + raise ValueError(f'invalid control: {control}') + + f = getattr(amixer, op) + f(control) + + return _amixer_control_response(control) + + async def amixer_volume(self, request: http.Request): + op = request.match_info['op'] + control = request.match_info['control'] + if not amixer.has_control(control): + raise ValueError(f'invalid control: {control}') + + def get_step() -> Optional[int]: + if 'step' in request.query: + step = int(request.query['step']) + if not 1 <= step <= 50: + raise ValueError('invalid step value') + return step + return None + + f = getattr(amixer, op) + f(control, step=get_step()) + + return _amixer_control_response(control) + + +if __name__ == '__main__': + if not os.getegid() == 0: + raise RuntimeError("Must be run as root.") + + config.load_app('sound_node') + + storage = SoundRecordStorage(config['node']['storage']) + + recorder = SoundRecorder(storage=storage) + recorder.start_thread() + + server = SoundNodeServer(recorder=recorder, + storage=storage, + addr=config.get_addr('node.listen')) + server.run() |