From bf125a73b11d641b2716ae620f5863666ecc5e1f Mon Sep 17 00:00:00 2001 From: Sophie Schiller Date: Thu, 23 Dec 2021 13:48:44 +0100 Subject: [PATCH 1/5] infobeamer-cms: initial commit --- bundles/infobeamer-cms/files/settings.cfg | 47 +++++++++++++ bundles/infobeamer-cms/items.py | 57 +++++++++++++++ bundles/infobeamer-cms/metadata.py | 86 +++++++++++++++++++++++ nodes/voc/infobeamer-cms.py | 43 ++++++++++++ 4 files changed, 233 insertions(+) create mode 100644 bundles/infobeamer-cms/files/settings.cfg create mode 100644 bundles/infobeamer-cms/items.py create mode 100644 bundles/infobeamer-cms/metadata.py create mode 100644 nodes/voc/infobeamer-cms.py diff --git a/bundles/infobeamer-cms/files/settings.cfg b/bundles/infobeamer-cms/files/settings.cfg new file mode 100644 index 0000000..a0deee5 --- /dev/null +++ b/bundles/infobeamer-cms/files/settings.cfg @@ -0,0 +1,47 @@ +<% +from json import dumps +%> +# This github OAuth client will be used for authentication. Create +# one at https://github.com/organizations/ACCOUNT/settings/applications +GITHUB_CLIENT_ID = '${GITHUB_CLIENT_ID}' +GITHUB_CLIENT_SECRET = '${GITHUB_CLIENT_SECRET}' + +SESSION_COOKIE_NAME = '${SESSION_COOKIE_NAME}' + +PREFERRED_URL_SCHEME = '${PREFERRED_URL_SCHEME}' + +# info-beamer API keys. +# This key needs access to setups and assets as well as the +# node-message calls. If you consider the host running this +# code trusted, you might use a key with full account access. +# The keys given out to users are adhoc keys based on this key. +HOSTED_API_KEY = '${HOSTED_API_KEY}' + +# Maximum uploads per github user +MAX_UPLOADS = ${MAX_UPLOADS} + +# Setup IDs using the scheduled player setup. One of the +# playlists must be named 'User Content'. Its pages will be +# autogenerated by the CMS when calling /sync +SETUP_IDS = ${SETUP_IDS} + +# Generate some random string. It's used for signing +# urls send to moderators. +URL_KEY = '${URL_KEY}' + +# Push notifications for moderation requests user pushover. +# Specify a target key and the app key to use. +PUSHOVER_TARGET = '${PUSHOVER_TARGET}' +PUSHOVER_APP_KEY = '${PUSHOVER_APP_KEY}' + +# Unix timestamp allows for specifying start/end time +# of uploaded content +TIME_MIN = ${TIME_MIN} +TIME_MAX = ${TIME_MAX} + +# change this to invalidate cached static files. +VERSION = ${VERSION} + +# rooms. This is both used for the /last view as well +# as the interruption control on /interrupt. +ROOMS = ${dumps(node.metadata.get('infobeamer-cms/config/ROOMS', {}))} diff --git a/bundles/infobeamer-cms/items.py b/bundles/infobeamer-cms/items.py new file mode 100644 index 0000000..7b572bb --- /dev/null +++ b/bundles/infobeamer-cms/items.py @@ -0,0 +1,57 @@ +git_deploy = { + '/opt/infobeamer-cms': { + 'rev': 'MASTER', + 'repo': 'https://github.com/sophieschi/36c3-cms.git', + 'needs': { + 'directory:/opt/infobeamer-cms', + }, + 'triggers': { + 'svc_systemd:infobeamer-cms:restart', + }, + }, +} + +directories = { + '/opt/infobeamer-cms': {}, +} + +files = { + '/opt/infobeamer-cms/settings.cfg': { + 'content_type': 'mako', + 'context': node.metadata.get('infobeamer-cms/config'), + 'needs': { + 'git_deploy:/opt/infobeamer-cms', + }, + 'triggers': { + 'svc_systemd:infobeamer-cms:restart', + }, + }, + '/etc/systemd/system/infobeamer-cms.service': { + 'triggers': { + 'action:systemd-reload', + 'svc_systemd:infobeamer-cms:restart', + }, + } +} + +pkg_pip = { + 'github-flask': { + 'needed_by': { + 'git_deploy:/opt/infobeamer-cms', + }, + }, +} + +svc_systemd = { + 'infobeamer-cms': { + 'needs': { + 'file:/opt/infobeamer-cms/settings.cfg', + }, + }, +} + +users = { + 'infobeamer-cms': { + 'home': '/opt/infobeamer-cms', + }, +} diff --git a/bundles/infobeamer-cms/metadata.py b/bundles/infobeamer-cms/metadata.py new file mode 100644 index 0000000..b10e0fb --- /dev/null +++ b/bundles/infobeamer-cms/metadata.py @@ -0,0 +1,86 @@ +defaults = { + 'apt': { + 'packages': { + 'gunicorn': {}, + 'python3-gevent': {}, + 'python3-flask': {}, + 'python3-redis': {}, + 'python3-virtualenv': {}, + 'python3-requests': {}, + 'python3-iso8601': {}, + }, + }, + 'infobeamer-cms': { + 'config': { + 'GITHUB_CLIENT_ID': 'encrypted1', + 'GITHUB_CLIENT_SECRET': 'encryted2', + 'SESSION_COOKIE_NAME': '__Host-sess', + 'PREFERRED_URL_SCHEME': 'https', + 'HOSTED_API_KEY': 'encrypted3', + 'MAX_UPLOADS': 5, + 'SETUP_IDS': '[123456]', + 'URL_KEY': 'encrypted4', + 'PUSHOVER_TARGET': 'encrypted5', + 'PUSHOVER_APP_KEY': 'encrypted6', + 'TIME_MIN': 1640039559, + 'TIME_MAX': 1640905200, + 'VERSION': 1, + 'ROOMS': [ + { + 'name': 'Ada', + 'device_id': 5919, + 'interrupts': [ + { + 'name': 'Signal', + 'data': 'signal', + }, + ], + }, + { + 'name': 'Borg', + 'device_id': 5920, + 'interrupts': [ + { + 'name': 'Signal', + 'data': 'signal', + }, + ], + }, + ], + 'unused': 'foobar', + }, + }, +} + +@metadata_reactor.provides( + 'nginx/vhosts', +) +def nginx(metadata): + if not node.has_bundle('nginx'): + raise DoNotRunAgain + + locations = { + '/': { + 'target': 'http://127.0.0.1:8000', + }, + '/twitter/': { + 'alias': '/export/twitter/', + }, + '/static/': { + 'alias': '/opt/infobeamer-cms/static', + }, + } + + vhosts = { + 'infobeamer-cms': { + 'locations': locations, + 'website_check_path': '/', + 'website_check_string': '', + }, + } + + return { + 'nginx': { + 'vhosts': vhosts + }, + } diff --git a/nodes/voc/infobeamer-cms.py b/nodes/voc/infobeamer-cms.py new file mode 100644 index 0000000..ae0d911 --- /dev/null +++ b/nodes/voc/infobeamer-cms.py @@ -0,0 +1,43 @@ +nodes['voc.infobeamer-cms'] = { + 'hostname': 'pretalx.c3voc.de', + 'bundles': { + 'infobeamer-cms', + 'redis', + }, + 'groups': { + 'debian-buster', + 'webserver', + }, + 'metadata': { + 'apt': { + # Move patchday to somewhere where the possibility of + # clashing with actual events is less likely. + 'unattended_upgrades': { + 'day': 1, + }, + }, + 'backups': { + 'exclude_from_backups': True, + }, + 'interfaces': { + 'default': { + 'ips': { + '31.172.33.106/28', + '2a01:a700:48d1::106/64', + }, + 'gateway4': '31.172.33.110', + 'gateway6': '2a01:a700:48d1::1', + }, + }, + 'icinga_options': { + 'pretty_name': 'infobeamer-cms.c3voc.de', + }, + 'nginx': { + 'vhosts': { + 'infobeamer-cms': { + 'domain': 'infobeamer-cms.c3voc.de', + }, + }, + }, + }, +} -- 2.39.2 From bac2a369c461b01c80bf9ca5a0290074f70abcb6 Mon Sep 17 00:00:00 2001 From: Sophie Schiller Date: Thu, 23 Dec 2021 19:36:50 +0100 Subject: [PATCH 2/5] infobeamer-cms: make usable --- .../files/infobeamer-cms-runperiodic.service | 10 ++ .../files/infobeamer-cms-runperiodic.timer | 9 + .../files/infobeamer-cms.service | 18 ++ bundles/infobeamer-cms/files/settings.cfg | 2 +- bundles/infobeamer-cms/items.py | 33 +++- bundles/infobeamer-cms/metadata.py | 154 ++++++++++++++++-- 6 files changed, 209 insertions(+), 17 deletions(-) create mode 100644 bundles/infobeamer-cms/files/infobeamer-cms-runperiodic.service create mode 100644 bundles/infobeamer-cms/files/infobeamer-cms-runperiodic.timer create mode 100644 bundles/infobeamer-cms/files/infobeamer-cms.service diff --git a/bundles/infobeamer-cms/files/infobeamer-cms-runperiodic.service b/bundles/infobeamer-cms/files/infobeamer-cms-runperiodic.service new file mode 100644 index 0000000..1e7ddba --- /dev/null +++ b/bundles/infobeamer-cms/files/infobeamer-cms-runperiodic.service @@ -0,0 +1,10 @@ +[Unit] +Description=infobeamer-cms sync +After=network.target +Requires=infobeamer-cms.service + +[Service] +User=infobeamer-cms +Group=infobeamer-cms +WorkingDirectory=/opt/infobeamer-cms +ExecStart=curl http://127.0.0.1:8000/sync diff --git a/bundles/infobeamer-cms/files/infobeamer-cms-runperiodic.timer b/bundles/infobeamer-cms/files/infobeamer-cms-runperiodic.timer new file mode 100644 index 0000000..48b52f4 --- /dev/null +++ b/bundles/infobeamer-cms/files/infobeamer-cms-runperiodic.timer @@ -0,0 +1,9 @@ +[Unit] +Description=Run infobeamer-cms sync + +[Timer] +OnCalendar=*:0/5 +Persistent=true + +[Install] +WantedBy=timers.target diff --git a/bundles/infobeamer-cms/files/infobeamer-cms.service b/bundles/infobeamer-cms/files/infobeamer-cms.service new file mode 100644 index 0000000..56423ee --- /dev/null +++ b/bundles/infobeamer-cms/files/infobeamer-cms.service @@ -0,0 +1,18 @@ +[Unit] +Description=infobeamer-cms - upload custom info pages to info-beamer +After=network.target + +[Service] +Type=exec +Environment=SETTINGS=settings.cfg +Restart=always +RestartSec=5s +ExecStart=/usr/bin/gunicorn frontend:app -b 127.0.0.1:${PORT} -w ${WORKERS} -t 120 -k gevent --max-requests=1000 + +# You may have to adjust these settings +User=infobeamer-cms +Group=infobeamer-cms +WorkingDirectory=/opt/infobeamer-cms + +[Install] +WantedBy=multi-user.target diff --git a/bundles/infobeamer-cms/files/settings.cfg b/bundles/infobeamer-cms/files/settings.cfg index a0deee5..435a2d4 100644 --- a/bundles/infobeamer-cms/files/settings.cfg +++ b/bundles/infobeamer-cms/files/settings.cfg @@ -27,7 +27,7 @@ SETUP_IDS = ${SETUP_IDS} # Generate some random string. It's used for signing # urls send to moderators. -URL_KEY = '${URL_KEY}' +URL_KEY = b'${URL_KEY}' # Push notifications for moderation requests user pushover. # Specify a target key and the app key to use. diff --git a/bundles/infobeamer-cms/items.py b/bundles/infobeamer-cms/items.py index 7b572bb..e1f812c 100644 --- a/bundles/infobeamer-cms/items.py +++ b/bundles/infobeamer-cms/items.py @@ -1,12 +1,20 @@ +actions = { + 'infobeamer-cms_set_directory_permissions': { + 'triggered': True, + 'command': 'chown -R infobeamer-cms:infobeamer-cms /opt/infobeamer-cms/static/' + }, +} + git_deploy = { '/opt/infobeamer-cms': { - 'rev': 'MASTER', + 'rev': 'master', 'repo': 'https://github.com/sophieschi/36c3-cms.git', 'needs': { 'directory:/opt/infobeamer-cms', }, 'triggers': { 'svc_systemd:infobeamer-cms:restart', + 'action:infobeamer-cms_set_directory_permissions', }, }, } @@ -27,11 +35,24 @@ files = { }, }, '/etc/systemd/system/infobeamer-cms.service': { + 'content_type': 'mako', + 'context': node.metadata.get('infobeamer-cms/config'), 'triggers': { 'action:systemd-reload', 'svc_systemd:infobeamer-cms:restart', }, - } + }, + '/etc/systemd/system/infobeamer-cms-runperiodic.timer': { + 'triggers': { + 'action:systemd-reload', + 'svc_systemd:infobeamer-cms-runperiodic.timer:restart', + }, + }, + '/etc/systemd/system/infobeamer-cms-runperiodic.service': { + 'triggers': { + 'action:systemd-reload', + }, + }, } pkg_pip = { @@ -46,6 +67,14 @@ svc_systemd = { 'infobeamer-cms': { 'needs': { 'file:/opt/infobeamer-cms/settings.cfg', + 'file:/etc/systemd/system/infobeamer-cms.service', + 'git_deploy:/opt/infobeamer-cms', + }, + }, + 'infobeamer-cms-runperiodic.timer': { + 'needs': { + 'file:/etc/systemd/system/infobeamer-cms-runperiodic.timer', + 'file:/etc/systemd/system/infobeamer-cms-runperiodic.service', }, }, } diff --git a/bundles/infobeamer-cms/metadata.py b/bundles/infobeamer-cms/metadata.py index b10e0fb..d8d983b 100644 --- a/bundles/infobeamer-cms/metadata.py +++ b/bundles/infobeamer-cms/metadata.py @@ -4,7 +4,9 @@ defaults = { 'gunicorn': {}, 'python3-gevent': {}, 'python3-flask': {}, + 'python3-jinja2': {}, 'python3-redis': {}, + 'python3-oauth2client': {}, 'python3-virtualenv': {}, 'python3-requests': {}, 'python3-iso8601': {}, @@ -12,23 +14,23 @@ defaults = { }, 'infobeamer-cms': { 'config': { - 'GITHUB_CLIENT_ID': 'encrypted1', - 'GITHUB_CLIENT_SECRET': 'encryted2', + 'GITHUB_CLIENT_ID': repo.vault.decrypt('encrypt$gAAAAABhxJT3JG3Qb1X-gjtBxwOXZmF-GVPjNbDkYo0Eke5Ly4CAKiussQ8Lld-4zoIWnIVBgndfPPGFDV2RlAHgb-_RY5r7jQcAlgsR0RUw4as0jEhiKlQ='), + 'GITHUB_CLIENT_SECRET': repo.vault.decrypt('encrypt$gAAAAABhxJUgYzLIm5Efbn9-sEPpQRRiskHKa7fSqNoUxgonpcn7b9e6S_WbNHH_CLGrkKi7oE3pYUticDaLLLSM0bv74lswwElNvkhUuOM-RSeEuyKEhPP-fX-NXIa_AkRkFPVVBLnw'), 'SESSION_COOKIE_NAME': '__Host-sess', 'PREFERRED_URL_SCHEME': 'https', - 'HOSTED_API_KEY': 'encrypted3', + 'HOSTED_API_KEY': repo.vault.decrypt('encrypt$gAAAAABhxJPH2sIGMAibU2Us1HoCVlNfF0SQQnVl0eiod48Zu8webL_-xk3wDw3yXw1Hkglj-2usl-D3Yd095yTSq0vZMCv2fh-JWwSPdJewQ45x9Ai4vXVD4CNz5vuJBESKS9xQWXTc'), 'MAX_UPLOADS': 5, - 'SETUP_IDS': '[123456]', - 'URL_KEY': 'encrypted4', - 'PUSHOVER_TARGET': 'encrypted5', - 'PUSHOVER_APP_KEY': 'encrypted6', + 'SETUP_IDS': '[212947]', + 'URL_KEY': repo.vault.password_for(f'{node.name} infobeamer-cms url key'), + 'PUSHOVER_TARGET': repo.vault.decrypt('encrypt$gAAAAABhxKJJrjd-wBezp1Bl8Lu_8BrMm4jiZvOub_XBnPTXE6mAHCTDCRGqH2-Z---hhuJh50MXroNzzIuA_9uAtwXhtkXkrsR344bcZh-idG6V0tDgzds='), + 'PUSHOVER_APP_KEY': repo.vault.decrypt('encrypt$gAAAAABhxJFqFafr8pZhQPn3HXGx0plLNQpnTDtJhM4PnlWYDBKjMZxUVh0Ol4631ZBTO5Sj8SIq79O2-Lx2eu0cGIZuzPQsZIBFuxv-30d81r6ljmLh8RQ='), 'TIME_MIN': 1640039559, 'TIME_MAX': 1640905200, 'VERSION': 1, 'ROOMS': [ { - 'name': 'Ada', - 'device_id': 5919, + 'name': 'Chaos-West TV', + 'device_id': 4157489434, 'interrupts': [ { 'name': 'Signal', @@ -37,8 +39,8 @@ defaults = { ], }, { - 'name': 'Borg', - 'device_id': 5920, + 'name': 'Chaosstudio Hamburg', + 'device_id': 157382517, 'interrupts': [ { 'name': 'Signal', @@ -46,7 +48,130 @@ defaults = { }, ], }, + { + 'name': 'ChaosZone TV', + 'device_id': 3, + 'interrupts': [ + { + 'name': 'Signal', + 'data': 'signal', + }, + ], + }, + { + 'name': 'r3s - Monheim/Rhein', + 'device_id': 3987908073, + 'interrupts': [ + { + 'name': 'Signal', + 'data': 'signal', + }, + ], + }, + { + 'name': 'franconian.net Livestream', + 'device_id': 246100657, + 'interrupts': [ + { + 'name': 'Signal', + 'data': 'signal', + }, + ], + }, + { + 'name': 'about:future stage', + 'device_id': 246100658, + 'interrupts': [ + { + 'name': 'Signal', + 'data': 'signal', + }, + ], + }, + { + 'name': 'Sendezentrum Bühne', + 'device_id': 246100659, + 'interrupts': [ + { + 'name': 'Signal', + 'data': 'signal', + }, + ], + }, + { + 'name': 'Haecksen Stream', + 'device_id': 246100660, + 'interrupts': [ + { + 'name': 'Signal', + 'data': 'signal', + }, + ], + }, + { + 'name': 'xHain Mainhall', + 'device_id': 246100661, + 'interrupts': [ + { + 'name': 'Signal', + 'data': 'signal', + }, + ], + }, + { + 'name': 'xHain Workshop-Area', + 'device_id': 246100662, + 'interrupts': [ + { + 'name': 'Signal', + 'data': 'signal', + }, + ], + }, + { + 'name': 'c-base', + 'device_id': 246100663, + 'interrupts': [ + { + 'name': 'Signal', + 'data': 'signal', + }, + ], + }, + { + 'name': 'about:future Kitchen', + 'device_id': 246100664, + 'interrupts': [ + { + 'name': 'Signal', + 'data': 'signal', + }, + ], + }, + { + 'name': 'Haecksen Zur schönen Mary', + 'device_id': 246100665, + 'interrupts': [ + { + 'name': 'Signal', + 'data': 'signal', + }, + ], + }, + { + 'name': 'Haecksen Zur magischen Margaret', + 'device_id': 246100666, + 'interrupts': [ + { + 'name': 'Signal', + 'data': 'signal', + }, + ], + }, + ], + 'PORT': 8000, + 'WORKERS': 4, 'unused': 'foobar', }, }, @@ -63,10 +188,11 @@ def nginx(metadata): '/': { 'target': 'http://127.0.0.1:8000', }, - '/twitter/': { - 'alias': '/export/twitter/', + '/sync': { + 'return': 'forbidden', + 'mode': 403, }, - '/static/': { + '/static': { 'alias': '/opt/infobeamer-cms/static', }, } -- 2.39.2 From 2e4cccea00cfbd13abe7ef8a90d30a5ab4ba74a6 Mon Sep 17 00:00:00 2001 From: Sophie Schiller Date: Thu, 23 Dec 2021 19:37:46 +0100 Subject: [PATCH 3/5] voc.infobeamer-cms: update network config --- nodes/voc/infobeamer-cms.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/nodes/voc/infobeamer-cms.py b/nodes/voc/infobeamer-cms.py index ae0d911..7062005 100644 --- a/nodes/voc/infobeamer-cms.py +++ b/nodes/voc/infobeamer-cms.py @@ -1,11 +1,11 @@ nodes['voc.infobeamer-cms'] = { - 'hostname': 'pretalx.c3voc.de', + 'hostname': 'infobeamer-cms.c3voc.de', 'bundles': { 'infobeamer-cms', 'redis', }, 'groups': { - 'debian-buster', + 'debian-bullseye', 'webserver', }, 'metadata': { @@ -20,13 +20,13 @@ nodes['voc.infobeamer-cms'] = { 'exclude_from_backups': True, }, 'interfaces': { - 'default': { + 'ens18': { 'ips': { - '31.172.33.106/28', - '2a01:a700:48d1::106/64', + '185.106.84.31/26', + '2001:67c:20a0:e::31/64', }, - 'gateway4': '31.172.33.110', - 'gateway6': '2a01:a700:48d1::1', + 'gateway4': '185.106.84.1', + 'gateway6': '2001:67c:20a0:e::1', }, }, 'icinga_options': { -- 2.39.2 From 18e30178a4b80594a11ace4aaf8e5d6f4f1a3008 Mon Sep 17 00:00:00 2001 From: Sophie Schiller Date: Thu, 23 Dec 2021 19:38:21 +0100 Subject: [PATCH 4/5] letsencrypt: add openssl package --- bundles/letsencrypt/metadata.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/bundles/letsencrypt/metadata.py b/bundles/letsencrypt/metadata.py index d5b6d1d..a36b58c 100644 --- a/bundles/letsencrypt/metadata.py +++ b/bundles/letsencrypt/metadata.py @@ -1,6 +1,11 @@ defaults = { 'apt': { 'packages': { + 'openssl': { + 'needed_by': { + 'action:letsencrypt_update_certificates', + }, + }, 'dehydrated': { 'needed_by': { 'action:letsencrypt_update_certificates', -- 2.39.2 From 678f558f4a3af6160ac3c2c0dc832fc9b63094fa Mon Sep 17 00:00:00 2001 From: Sophie Schiller Date: Thu, 23 Dec 2021 19:45:24 +0100 Subject: [PATCH 5/5] infobeamer-cms: WHITESPACE --- bundles/infobeamer-cms/metadata.py | 44 ++++++++++++++++++------------ 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/bundles/infobeamer-cms/metadata.py b/bundles/infobeamer-cms/metadata.py index d8d983b..5b74877 100644 --- a/bundles/infobeamer-cms/metadata.py +++ b/bundles/infobeamer-cms/metadata.py @@ -30,23 +30,23 @@ defaults = { 'ROOMS': [ { 'name': 'Chaos-West TV', - 'device_id': 4157489434, + 'device_id': 17968, 'interrupts': [ { 'name': 'Signal', 'data': 'signal', }, - ], + ], }, { 'name': 'Chaosstudio Hamburg', - 'device_id': 157382517, + 'device_id': 3284, 'interrupts': [ { 'name': 'Signal', 'data': 'signal', }, - ], + ], }, { 'name': 'ChaosZone TV', @@ -56,17 +56,17 @@ defaults = { 'name': 'Signal', 'data': 'signal', }, - ], + ], }, { 'name': 'r3s - Monheim/Rhein', - 'device_id': 3987908073, + 'device_id': 22460, 'interrupts': [ { 'name': 'Signal', 'data': 'signal', }, - ], + ], }, { 'name': 'franconian.net Livestream', @@ -76,7 +76,7 @@ defaults = { 'name': 'Signal', 'data': 'signal', }, - ], + ], }, { 'name': 'about:future stage', @@ -86,7 +86,7 @@ defaults = { 'name': 'Signal', 'data': 'signal', }, - ], + ], }, { 'name': 'Sendezentrum Bühne', @@ -96,7 +96,7 @@ defaults = { 'name': 'Signal', 'data': 'signal', }, - ], + ], }, { 'name': 'Haecksen Stream', @@ -106,7 +106,7 @@ defaults = { 'name': 'Signal', 'data': 'signal', }, - ], + ], }, { 'name': 'xHain Mainhall', @@ -116,7 +116,7 @@ defaults = { 'name': 'Signal', 'data': 'signal', }, - ], + ], }, { 'name': 'xHain Workshop-Area', @@ -126,7 +126,7 @@ defaults = { 'name': 'Signal', 'data': 'signal', }, - ], + ], }, { 'name': 'c-base', @@ -136,7 +136,7 @@ defaults = { 'name': 'Signal', 'data': 'signal', }, - ], + ], }, { 'name': 'about:future Kitchen', @@ -146,7 +146,7 @@ defaults = { 'name': 'Signal', 'data': 'signal', }, - ], + ], }, { 'name': 'Haecksen Zur schönen Mary', @@ -156,7 +156,7 @@ defaults = { 'name': 'Signal', 'data': 'signal', }, - ], + ], }, { 'name': 'Haecksen Zur magischen Margaret', @@ -166,7 +166,17 @@ defaults = { 'name': 'Signal', 'data': 'signal', }, - ], + ], + }, + { + 'name': 'infobeamer stream', + 'device_id': 15586, + 'interrupts': [ + { + 'name': 'Signal', + 'data': 'signal', + }, + ], }, ], -- 2.39.2