aboutsummaryrefslogtreecommitdiff
path: root/bin/sound_node.py
diff options
context:
space:
mode:
Diffstat (limited to 'bin/sound_node.py')
-rwxr-xr-xbin/sound_node.py91
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()