]> git.0d.be Git - django-panik-nonstop.git/blob - nonstop/management/commands/switch-jack.py
add JACK_SWITCH_SEND_UDP_NOTIFICATIONS setting
[django-panik-nonstop.git] / nonstop / management / commands / switch-jack.py
1 import asyncio
2 import json
3 import logging
4
5 import aiohttp
6 from django.core.management.base import BaseCommand, CommandError
7
8 from nonstop.app_settings import app_settings
9
10 try:
11     import jack
12 except ImportError:
13     jack = None
14
15 logger = logging.getLogger('switch-jack')
16
17
18 class Command(BaseCommand):
19     help = 'jack source switch'
20
21     def handle(self, verbosity, **options):
22         if jack is None:
23             raise CommandError('missing jack module (install python3-jack-client?)')
24         self.verbosity = verbosity
25         asyncio.run(self.main())
26
27     async def main(self):
28         if not app_settings.SWITCH_WS_URL:
29             raise CommandError('missing switch_ws_url')
30         currently_active = None
31         sleep_duration = 0.2
32         while True:
33             try:
34                 async with aiohttp.ClientSession() as session:
35                     async with session.ws_connect(app_settings.SWITCH_WS_URL) as ws:
36                         logger.info('waiting for messages')
37                         sleep_duration = 0.2  # reset sleep duration to baseline
38                         async for msg in ws:
39                             if msg.type == aiohttp.WSMsgType.TEXT:
40                                 try:
41                                     msg = json.loads(msg.data)
42                                 except ValueError:
43                                     continue
44                                 if msg.get('active') != currently_active:
45                                     currently_active = msg.get('active')
46                                     self.update_jack_connections(currently_active)
47                             elif msg.type == aiohttp.WSMsgType.ERROR:
48                                 break
49                     logger.debug('lost websocket connection')
50             except aiohttp.ClientError as e:
51                 logger.warning('websocket error (%s)' % e)
52                 sleep_duration *= 2
53                 await asyncio.sleep(min(sleep_duration, 5))
54             except asyncio.CancelledError:
55                 # most probably because of a keyboard interrupt, quit silently
56                 return
57
58     def update_jack_connections(self, active):
59         if active not in app_settings.SWITCH_IN_PORTS:
60             logger.info('unsupported source: %s', active)
61             return
62         logger.info('setting source: %s', active)
63         out_ports = app_settings.SWITCH_OUT_PORTS
64         with jack.Client('switch-jack') as client:
65             known_ports = {x.name for x in client.get_ports(is_audio=True)}
66             for dports in out_ports.values():
67                 if any(x not in known_ports for x in dports):
68                     logger.error('unavailable destination ports %r', dports)
69                     continue
70                 for port_id, port_names in app_settings.SWITCH_IN_PORTS.items():
71                     if any(x not in known_ports for x in port_names):
72                         logger.error('unavailable source ports %r', port_names)
73                         continue
74                     try:
75                         if port_id == active:
76                             self.jack_connect(client, port_names[0], dports[0])
77                             self.jack_connect(client, port_names[1], dports[1])
78                         else:
79                             self.jack_disconnect(client, port_names[0], dports[0])
80                             self.jack_disconnect(client, port_names[1], dports[1])
81                     except jack.JackError as e:
82                         logger.error('jack error: %s' % e)
83
84     def jack_connect(self, client, in_port, out_port):
85         connections = [x.name for x in client.get_all_connections(in_port)]
86         if out_port not in connections:
87             if self.verbosity:
88                 logger.info('connecting %s and %s', in_port, out_port)
89             client.connect(in_port, out_port)
90
91     def jack_disconnect(self, client, in_port, out_port):
92         connections = [x.name for x in client.get_all_connections(in_port)]
93         if out_port in connections:
94             if self.verbosity:
95                 logger.info('disconnecting %s and %s', in_port, out_port)
96             client.disconnect(in_port, out_port)