Skip to content

Commit

Permalink
Merge branch 'master' of github.com:CarlFK/voctomix-outcasts
Browse files Browse the repository at this point in the history
  • Loading branch information
CarlFK committed Nov 19, 2017
2 parents 408b404 + def84e2 commit d04afac
Show file tree
Hide file tree
Showing 3 changed files with 234 additions and 2 deletions.
8 changes: 8 additions & 0 deletions configs/voctolight.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[server]
host=localhost

[light]
driver=rpi
cam=cam2
gpio_red=11
gpios=11,12,13
2 changes: 0 additions & 2 deletions ingest.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,10 +120,8 @@ def mk_video_src(args, videocaps):
elif args.video_source == 'png':
video_src = """
multifilesrc {attribs}
loop=0
caps="image/png" !
pngdec !
imagefreeze !
videoscale !
{monitor}
videoconvert !
Expand Down
226 changes: 226 additions & 0 deletions voctolight.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
#!/usr/bin/env python3
# Based upon:
# https://github.com/voc/voctomix/tree/voctopanel/example-scripts/voctolight

from enum import Enum
import argparse
import asyncio
import configparser
import json
import os.path


class Config(configparser.ConfigParser, object):
config_files = [
os.path.join(os.path.dirname(os.path.realpath(__file__)),
'default-config.ini'),
os.path.join(os.path.dirname(os.path.realpath(__file__)),
'config.ini'),
'/etc/voctomix/voctolight.ini',
'/etc/voctolight.ini',
os.path.expanduser('~/.voctolight.ini'),
]

def __init__(self, cmd_line_config=None):
super().__init__()
self.cmd_line_config = cmd_line_config
self._read_config()

def _read_config(self):
self.read(self.config_files)
if self.cmd_line_config:
self.read_file(self.cmd_line_config)
self.cmd_line_config.seek(0)

def setup_with_server_config(self, server_config):
self.clear()
self._read_config()
self.read_dict(server_config)

def getlist(self, section, option):
return [x.strip() for x in self.get(section, option).split(',')]


class Connection(asyncio.Protocol):
def __init__(self, interpreter):
self.interpreter = interpreter

def connect(self, host, port=9999):
self.loop = asyncio.get_event_loop()
self.conn = self.loop.create_connection(lambda: self, host, port)
self.loop.run_until_complete(conn.conn)

def send(self, message):
self.transport.write(message.encode())
self.transport.write('\n'.encode())

def connection_made(self, transport):
self.transport = transport

def data_received(self, data):
# print('data received: {}'.format(data.decode()))
lines = data.decode().rstrip('\n').split('\n')
for line in lines:
interpreter.handler(line)

def connection_lost(self, exc):
print('server closed the connection')
asyncio.get_event_loop().stop()


# FIXME: Duplicate from videomix.py
class CompositeModes(Enum):
fullscreen = 0
side_by_side_equal = 1
side_by_side_preview = 2
picture_in_picture = 3


class Interpreter(object):
a_or_b = False
primary = False
composite_mode = CompositeModes.fullscreen

def __init__(self, actor, config, debug=False):
self.config = config
self.actor = actor
self.debug = debug
actor.reset_led()
if self.debug:
print('LED has been reset to off')

def compute_state(self):
if self.composite_mode == CompositeModes.fullscreen:
return self.primary
else:
return self.a_or_b

def handler(self, response):
words = response.split()
signal = words[0]
args = words[1:]
try:
handler = getattr(self, 'handle_{}'.format(signal))
except AttributeError:
print('Ignoring signal', signal)
else:
handler(args)
enable = interpreter.compute_state()
actor.enable_tally(enable)
if self.debug:
print('LED has been set to {}.'.format(
'on' if enable else 'off'))

def handle_video_status(self, cams):
mycam = self.config.get('light', 'cam')
if mycam in cams:
self.a_or_b = True
else:
self.a_or_b = False

self.primary = (cams[0] == mycam)
# print ('Is primary?', self.primary)

def handle_composite_mode(self, mode_list):
mode = mode_list[0]
if mode == 'fullscreen':
self.composite_mode = CompositeModes.fullscreen
elif mode == 'side_by_side_equal':
self.composite_mode = CompositeModes.side_by_side_equal
elif mode == 'side_by_side_preview':
self.composite_mode = CompositeModes.side_by_side_preview
elif mode == 'picture_in_picture':
self.composite_mode = CompositeModes.picture_in_picture
else:
print('Cannot handle', mode, 'of type', type(mode))

def handle_server_config(self, args):
server_config_json = ' '.join(args)
server_config = json.loads(server_config_json)
self.config.setup_with_server_config(server_config)


class RPiGPIODriver:
def __init__(self, config):
self._import()
self.gpios = config.get('light', 'gpios').split(',')
self.gpio_red = int(config.get('light', 'gpio_red'))
self.reset_led()

def _import(self):
global GPIO
import RPi.GPIO as GPIO

def reset_led(self):
GPIO.setmode(GPIO.BOARD)
GPIO.setwarnings(False)
for gpio in self.gpios:
gpio = int(gpio)
GPIO.setup(gpio, GPIO.OUT)
GPIO.output(gpio, GPIO.HIGH)

def enable_tally(self, enable):
if enable:
GPIO.output(self.gpio_red, GPIO.LOW)
else:
GPIO.output(self.gpio_red, GPIO.HIGH)


class DummyDriver:
def __init__(self, config):
pass

def reset_led(self):
pass

def enable_tally(self, enable):
pass


class SerialDTRDriver:
def __init__(self, config):
self.fn = config.get('light', 'port')
self.fd = None

def reset_led(self):
if self.fd:
self.fd.close()
self.fd = None

def enable_tally(self, enable):
if enable:
self.fd = open(self.fn)
else:
self.reset_led()


if __name__ == '__main__':
parser = argparse.ArgumentParser(
description='Tallylight controlling daemon for voctomix.')
parser.add_argument(
'--config', type=open, help='Use a specific config file')
parser.add_argument(
'--debug', action='store_true',
help='Show what would be done instead of toggling lights')
args = parser.parse_args()
config = Config(cmd_line_config=args.config)
driver = config.get('light', 'driver')
if driver == 'dummy':
actor = DummyDriver(config)
elif driver == 'rpi':
actor = RPiGPIODriver(config)
elif driver == 'serial':
actor = SerialDTRDriver(config)
else:
raise Exception('Unknown driver: ' + driver)
interpreter = Interpreter(actor, config, debug=args.debug)
conn = Connection(interpreter)
conn.connect(config.get('server', 'host'))
conn.send('get_config')
conn.send('get_composite_mode')
conn.send('get_video')
try:
conn.loop.run_forever()
except KeyboardInterrupt:
print('Quitting (Ctrl-C)')
actor.reset_led()

0 comments on commit d04afac

Please sign in to comment.