2019-01-08 Fred Gleason <fredg@paravelsystems.com>

* Changed the PyPAD namespace from 'PyPAD' to 'pypad'.
	* Changed the installation location of PyPAD scripts from
	'@libdir@/rivendell/PyPAD/' to '@libdir@/rivendell/pypad/'.
This commit is contained in:
Fred Gleason
2019-01-08 15:27:44 -05:00
parent 31596cbaca
commit 087f3b811b
46 changed files with 425 additions and 425 deletions

View File

@@ -0,0 +1,138 @@
## makefile.am
##
## Makefile.am for Rivendell pypad/examples
##
## (C) Copyright 2018 Fred Gleason <fredg@paravelsystems.com>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as
## published by the Free Software Foundation; either version 2 of
## the License, or (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public
## License along with this program; if not, write to the Free Software
## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
##
## Use automake to process this into a Makefile.in
install-exec-am:
mkdir -p $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad
../../../helpers/install_python.sh pypad_ando.py $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_ando.py
cp pypad_ando.exemplar $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_ando.exemplar
../../../helpers/install_python.sh pypad_filewrite.py $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_filewrite.py
cp pypad_filewrite.exemplar $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_filewrite.exemplar
../../../helpers/install_python.sh pypad_icecast2.py $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_icecast2.py
cp pypad_icecast2.exemplar $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_icecast2.exemplar
../../../helpers/install_python.sh pypad_inno713.py $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_inno713.py
cp pypad_inno713.exemplar $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_inno713.exemplar
../../../helpers/install_python.sh pypad_live365.py $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_live365.py
cp pypad_live365.exemplar $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_live365.exemplar
../../../helpers/install_python.sh pypad_liqcomp.py $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_liqcomp.py
cp pypad_liqcomp.exemplar $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_liqcomp.exemplar
../../../helpers/install_python.sh pypad_serial.py $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_serial.py
cp pypad_serial.exemplar $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_serial.exemplar
../../../helpers/install_python.sh pypad_shoutcast1.py $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_shoutcast1.py
cp pypad_shoutcast1.exemplar $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_shoutcast1.exemplar
../../../helpers/install_python.sh pypad_spinitron.py $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_spinitron.py
cp pypad_spinitron.exemplar $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_spinitron.exemplar
../../../helpers/install_python.sh pypad_spottrap.py $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_spottrap.py
cp pypad_spottrap.exemplar $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_spottrap.exemplar
../../../helpers/install_python.sh pypad_tunein.py $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_tunein.py
cp pypad_tunein.exemplar $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_tunein.exemplar
../../../helpers/install_python.sh pypad_udp.py $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_udp.py
cp pypad_udp.exemplar $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_udp.exemplar
../../../helpers/install_python.sh pypad_urlwrite.py $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_urlwrite.py
cp pypad_urlwrite.exemplar $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_urlwrite.exemplar
../../../helpers/install_python.sh pypad_walltime.py $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_walltime.py
cp pypad_walltime.exemplar $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_walltime.exemplar
../../../helpers/install_python.sh pypad_xds.py $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_xds.py
cp pypad_xds.exemplar $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_xds.exemplar
../../../helpers/install_python.sh pypad_xmpad.py $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_xmpad.py
cp pypad_xmpad.exemplar $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_xmpad.exemplar
uninstall-local:
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_ando.exemplar
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_ando.py
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_filewrite.exemplar
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_filewrite.py
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_icecast2.exemplar
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_icecast2.py
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_inno713.exemplar
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_inno713.py
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_live365.exemplar
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_live365.py
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_liqcomp.exemplar
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_liqcomp.py
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_serial.exemplar
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_serial.py
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_shoutcast1.exemplar
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_shoutcast1.py
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_spinitron.exemplar
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_spinitron.py
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_spottrap.exemplar
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_spottrap.py
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_tunein.exemplar
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_tunein.py
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_udp.exemplar
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_udp.py
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_urlwrite.exemplar
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_urlwrite.py
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_walltime.exemplar
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_walltime.py
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_xds.exemplar
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_xds.py
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_xmpad.exemplar
rm -f $(DESTDIR)$(prefix)/@RD_LIB_PATH@/rivendell/pypad/pypad_xmpad.py
EXTRA_DIST = pypad_ando.exemplar\
pypad_ando.py\
pypad_filewrite.exemplar\
pypad_filewrite.py\
pypad_icecast2.exemplar\
pypad_icecast2.py\
pypad_inno713.exemplar\
pypad_inno713.py\
pypad_liqcomp.exemplar\
pypad_liqcomp.py\
pypad_live365.exemplar\
pypad_live365.py\
pypad_serial.exemplar\
pypad_serial.py\
pypad_shoutcast1.exemplar\
pypad_shoutcast1.py\
pypad_spinitron.exemplar\
pypad_spinitron.py\
pypad_spottrap.exemplar\
pypad_spottrap.py\
pypad_tunein.exemplar\
pypad_tunein.py\
pypad_udp.exemplar\
pypad_udp.py\
pypad_urlwrite.exemplar\
pypad_urlwrite.py\
pypad_walltime.exemplar\
pypad_walltime.py\
pypad_xds.exemplar\
pypad_xds.py\
pypad_xmpad.exemplar\
pypad_xmpad.py
CLEANFILES = *~\
*.idb\
*ilk\
*.obj\
*.pdb\
*.qm\
moc_*
MAINTAINERCLEANFILES = *~\
*.tar.gz\
aclocal.m4\
configure\
Makefile.in\
moc_*

View File

@@ -0,0 +1,99 @@
; This is the configuration for the 'pypad_ando.py' script for
; Rivendell, which can be used to output Now & Next data to one or more
; ANDO Media Streaming systems, using one of the following formats:
;
; ^<artist>~<title>~<duration>~<group>~<album>~<cartnum>|
; ^<artist>~<title>~<duration>~<group>~<cartnum>~<album>~<label>|
; Section Header
;
; One section per remote ANDO system is configured, starting with 'System1' and
; working up consecutively
[System1]
; IP Address
;
; The IP address of the remote ANDO port, in dotted-quad notation.
IpAddress=192.168.10.29
; UDP Port
;
; The UDP port number of the remote ANDO system, in the range 0 - 65,535.
UdpPort=5273
; Field Definitions. The string to use to populate the <title>, <artist>,
; <album> and <label> fields sent to ANDO each time RDAirPlay changes play
; state. These can include wildcards as placeholders for metadata values.
; For the list of supported wildcards. see the 'Metadata Wildcards' section
; of the Rivendell Operations Guide.
;
; The <label> field is optional, and should be left blank unless you know
; that your specific ANDO configuration requires it.
;
Title=%t
Artist=%a
Album=%l
Label=
; Log Selection
;
; Set the status for each log to 'Yes', 'No' or 'Onair' to indicate whether
; state changes on that log should be output on this udp port. If set
; to 'Onair', then output will be generated only if RDAirPlay's OnAir flag
; is active.
MasterLog=Yes
Aux1Log=Yes
Aux2Log=Yes
VLog101=No
VLog102=No
VLog103=No
VLog104=No
VLog105=No
VLog106=No
VLog107=No
VLog108=No
VLog109=No
VLog110=No
VLog111=No
VLog112=No
VLog113=No
VLog114=No
VLog115=No
VLog116=No
VLog117=No
VLog118=No
VLog119=No
VLog120=No
; Additional ANDO systems can be configured by adding new sections...
;[System2]
;IpAddress=192.168.10.22
;UdpPort=6789
;Title=%u
;Artist=%a
;Album=%p
;Label=
;MasterLog=Yes
;Aux1Log=No
;Aux2Log=Onair
;VLog101=No
;VLog102=No
;VLog103=No
;VLog104=No
;VLog105=No
;VLog106=No
;VLog107=No
;VLog108=No
;VLog109=No
;VLog110=No
;VLog111=No
;VLog112=No
;VLog113=No
;VLog114=No
;VLog115=No
;VLog116=No
;VLog117=No
;VLog118=No
;VLog119=No
;VLog120=No

View File

@@ -0,0 +1,88 @@
#!%PYTHON_BANGPATH%
# pypad_ando.py
#
# Send PAD updates to an Ando AdInjector
#
# (C) Copyright 2018 Fred Gleason <fredg@paravelsystems.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
import sys
import socket
import configparser
import pypad
last_updates={}
def eprint(*args,**kwargs):
print(*args,file=sys.stderr,**kwargs)
def ProcessTimer(config):
n=1
while(True):
section='System'+str(n)
try:
send_sock.sendto('HB'.encode('utf-8'),
(config.get(section,'IpAddress'),int(config.get(section,'UdpPort'))))
n=n+1
except configparser.NoSectionError:
return
def ProcessPad(update):
try:
last_updates[update.machine()]
except KeyError:
last_updates[update.machine()]=None
n=1
while(True):
section='System'+str(n)
try:
if update.shouldBeProcessed(section) and update.hasPadType(pypad.TYPE_NOW) and (last_updates[update.machine()] != update.startDateTimeString(pypad.TYPE_NOW)):
last_updates[update.machine()]=update.startDateTimeString(pypad.TYPE_NOW)
title=update.resolvePadFields(update.config().get(section,'Title'),pypad.ESCAPE_NONE)
artist=update.resolvePadFields(update.config().get(section,'Artist'),pypad.ESCAPE_NONE)
album=update.resolvePadFields(update.config().get(section,'Album'),pypad.ESCAPE_NONE)
label=update.resolvePadFields(update.config().get(section,'Label'),pypad.ESCAPE_NONE)
secs=update.padField(pypad.TYPE_NOW,pypad.FIELD_LENGTH)
duration=('%02d:' % (secs//60000))+('%02d' % ((secs%60000)//1000))
group=update.padField(pypad.TYPE_NOW,pypad.FIELD_GROUP_NAME)
if update.config().get(section,'Label')=='':
msg='^'+artist+'~'+title+'~'+duration+'~'+group+'~'+album+'~'+str(update.padField(pypad.TYPE_NOW,pypad.FIELD_CART_NUMBER))+'|'
else:
msg='^'+artist+'~'+title+'~'+duration+'~'+group+'~'+str(update.padField(pypad.TYPE_NOW,pypad.FIELD_CART_NUMBER))+'~'+album+'~'+label+'|'
send_sock.sendto(msg.encode('utf-8'),
(update.config().get(section,'IpAddress'),int(update.config().get(section,'UdpPort'))))
n=n+1
except configparser.NoSectionError:
return
#
# 'Main' function
#
# Create Send Socket
#
send_sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
rcvr=pypad.Receiver()
try:
rcvr.setConfigFile(sys.argv[3])
except IndexError:
eprint('pypad_ando.py: USAGE: cmd <hostname> <port> <config>')
sys.exit(1)
rcvr.setCallback(ProcessPad)
rcvr.setTimerCallback(30,ProcessTimer)
rcvr.start(sys.argv[1],int(sys.argv[2]))

View File

@@ -0,0 +1,101 @@
; This is the sample configuration for the 'pypad_filewrite.py' PyPAD script
; for Rivendell, which can be used to write one or more files on the local
; system using Now & Next data.
;
; Section Header
;
; One section per file to be written should be configured, starting with
; 'File1' and working up consecutively
[File1]
; Filename
;
; The full path to the file to be written. The filename may contain filepath
; wildcards as defined in Appendix C of the Rivendell Operations and
; Administration Guide. The user running RDAirPlay must have write
; permissions for this location.
Filename=/tmp/rlm_filewrite.txt
; Append Mode
;
; If set to '0', the file will be completely overwritten with the contents
; of each PAD update. If set to '1', each update will be appended to the
; existing contents of the file.
Append=0
; Format String. The string to be output each time RDAirPlay changes
; play state, including any wildcards as placeholders for metadata values.
;
; The list of available wildcards can be found in the 'metadata_wildcards.txt'
; file in the Rivendell documentation directory.
;
FormatString=NOW: %d(ddd MMM d hh:mm:ss yyyy): %t - %a\nNEXT: %D(ddd MMM d hh:mm:ss yyyy): %T - %A\n
; Encoding. Defines the set of escapes to be applied to the PAD fields.
; The following options are available:
;
; 0 - Perform no character escaping.
; 1 - "XML" escaping: Escape reserved characters as per XML-v1.0
; 2 - "Web" escaping: Escape reserved characters as per RFC 2396 Section 2.4
Encoding=0
; Log Selection
;
; Set the status for each log to 'Yes', 'No' or 'Onair' to indicate whether
; state changes on that log should be output. If set to 'Onair', then
; output will be generated only if RDAirPlays OnAir flag is active.
MasterLog=Yes
Aux1Log=Yes
Aux2Log=Yes
VLog101=No
VLog102=No
VLog103=No
VLog104=No
VLog105=No
VLog106=No
VLog107=No
VLog108=No
VLog109=No
VLog110=No
VLog111=No
VLog112=No
VLog113=No
VLog114=No
VLog115=No
VLog116=No
VLog117=No
VLog118=No
VLog119=No
VLog120=No
; Additional files can be written by adding new sections...
;
;[File2]
;Filename=/home/rd/foo2.txt
;Append=1
;FormatString=%t by %a\r\n
;MasterLog=Yes
;Aux1Log=No
;Aux2Log=Onair
;VLog101=No
;VLog102=No
;VLog103=No
;VLog104=No
;VLog105=No
;VLog106=No
;VLog107=No
;VLog108=No
;VLog109=No
;VLog110=No
;VLog111=No
;VLog112=No
;VLog113=No
;VLog114=No
;VLog115=No
;VLog116=No
;VLog117=No
;VLog118=No
;VLog119=No
;VLog120=No

View File

@@ -0,0 +1,58 @@
#!%PYTHON_BANGPATH%
# pypad_filewrite.py
#
# Write PAD updates to files
#
# (C) Copyright 2018 Fred Gleason <fredg@paravelsystems.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
import sys
import configparser
import pypad
def eprint(*args,**kwargs):
print(*args,file=sys.stderr,**kwargs)
def ProcessPad(update):
n=1
try:
while(True):
section='File'+str(n)
if update.shouldBeProcessed(section):
fmtstr=update.config().get(section,'FormatString')
mode='w'
if update.config().get(section,'Append')=='1':
mode='a'
f=open(update.resolveFilepath(update.config().get(section,'Filename'),update.dateTime()),mode)
f.write(update.resolvePadFields(fmtstr,int(update.config().get(section,'Encoding'))))
f.close()
n=n+1
except configparser.NoSectionError:
return
#
# 'Main' function
#
rcvr=pypad.Receiver()
try:
rcvr.setConfigFile(sys.argv[3])
except IndexError:
eprint('pypad_filewrite.py: USAGE: cmd <hostname> <port> <config>')
sys.exit(1)
rcvr.setCallback(ProcessPad)
rcvr.start(sys.argv[1],int(sys.argv[2]))

View File

@@ -0,0 +1,106 @@
; This is the sample configuration for the 'pypad_icecast2.py' PyPAD script
; for Rivendell, which can be used to update the metadata on an Icecast2
; mountpoint using Now & Next data.
;
; Section Header
;
; One section per Icecast2 mountpoint is configured, starting with
; 'Icecast1' and working up consecutively
[Icecast1]
; User Name
;
; The username of the Icecast2 account to which to send updates.
Username=source
; Password
;
; The password of the Icecast2 account to which to send updates.
Password=hackme
; Host Name
;
; The fully-qualified domain name or IP address of the Icecast2 server
Hostname=icecast.example.com
; Host Port
;
; The TCP port number of the Icecast2 server
Tcpport=8000
; Mountpoint
;
; The Icecast2 mountpoint
Mountpoint=/audio.mp3
; Format String. The metadata to be sent each time RDAirPlay changes
; play state, including any wildcards as placeholders for metadata values.
;
; The list of available wildcards can be found in the 'metadata_wildcards.txt'
; file in the Rivendell documentation directory.
;
FormatString=%a - %t
; Log Selection
;
; Set the status for each log to 'Yes', 'No' or 'Onair' to indicate whether
; state changes on that log should be output to this account. If set
; to 'Onair', then output will be generated only if RDAirPlays OnAir flag
; is active.
MasterLog=Yes
Aux1Log=Yes
Aux2Log=Yes
VLog101=No
VLog102=No
VLog103=No
VLog104=No
VLog105=No
VLog106=No
VLog107=No
VLog108=No
VLog109=No
VLog110=No
VLog111=No
VLog112=No
VLog113=No
VLog114=No
VLog115=No
VLog116=No
VLog117=No
VLog118=No
VLog119=No
VLog120=No
; Additional Icecast2 mountpoints can be configured by adding new sections...
;[Icecast2]
;Username=source
;Password=letmein
;Hostname=anotherone.example.com
;Tcpport=80
;Mountpoint=moreaudio.mp3
;FormatString=%t by %a
;MasterLog=Yes
;Aux1Log=No
;Aux2Log=Onair
;VLog101=No
;VLog102=No
;VLog103=No
;VLog104=No
;VLog105=No
;VLog106=No
;VLog107=No
;VLog108=No
;VLog109=No
;VLog110=No
;VLog111=No
;VLog112=No
;VLog113=No
;VLog114=No
;VLog115=No
;VLog116=No
;VLog117=No
;VLog118=No
;VLog119=No
;VLog120=No

View File

@@ -0,0 +1,98 @@
#!%PYTHON_BANGPATH%
# pypad_icecast2.py
#
# Send PAD updates to Icecast2 mountpoint
#
# (C) Copyright 2018 Fred Gleason <fredg@paravelsystems.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
import os
import sys
import socket
import requests
from requests.auth import HTTPBasicAuth
import xml.etree.ElementTree as ET
import syslog
import pypad
import configparser
def eprint(*args,**kwargs):
print(pypad_name+': ',file=sys.stderr,end='')
print(*args,file=sys.stderr)
syslog.syslog(syslog.LOG_ERR,*args)
def iprint(*args,**kwargs):
print(pypad_name+': ',file=sys.stdout,end='')
print(*args,file=sys.stdout)
syslog.syslog(syslog.LOG_INFO,*args)
def isTrue(string):
l=['Yes','On','True','1']
return string.lower() in map(str.lower,l)
def ProcessPad(update):
if update.hasPadType(pypad.TYPE_NOW):
n=1
while(True):
section='Icecast'+str(n)
try:
values={}
values['mount']=update.config().get(section,'Mountpoint')
values['song']=update.resolvePadFields(update.config().get(section,'FormatString'),pypad.ESCAPE_NONE)
values['mode']='updinfo'
iprint('Updating '+update.config().get(section,'Hostname')+': song='+values['song'])
url="http://%s:%s/admin/metadata" % (update.config().get(section,'Hostname'),update.config().get(section,'Tcpport'))
try:
response=requests.get(url,auth=HTTPBasicAuth(update.config().get(section,'Username'),update.config().get(section,'Password')),params=values)
response.raise_for_status()
except requests.exceptions.RequestException as e:
eprint(str(e))
n=n+1
except configparser.NoSectionError:
if(n==1):
eprint('No icecast config found')
return
#
# Program Name
#
pypad_name=os.path.basename(__file__)
#
# Open Syslog
#
syslog.openlog(pypad_name,logoption=syslog.LOG_PID,facility=syslog.LOG_DAEMON)
#
# Create Send Socket
#
send_sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
#
# Start Receiver
#
rcvr=pypad.Receiver()
try:
rcvr.setConfigFile(sys.argv[3])
except IndexError:
eprint('pypad_icecast2: USAGE: cmd <hostname> <port> <config>')
sys.exit(1)
rcvr.setCallback(ProcessPad)
iprint('Started')
rcvr.start(sys.argv[1],int(sys.argv[2]))
iprint('Stopped')

View File

@@ -0,0 +1,117 @@
; This is the configuration for the 'rlm_inno713.py' script for
; Rivendell, which can be used to output Now & Next data to one or more
; Innovonics model 713 RDS encoders.
; Section Header
;
; One section per remote RDS unit is configured, starting with 'Rds1' and
; working up consecutively
[Rds1]
; Two methods of connecting to the unit are supported: TCP/IP or serial.
;
; *****************************************************************************
; TCP/IP Connection Settings
; IP Address
;
; The IP address of the UDP port to send updates to, in dotted-quad notation.
; If using a serial connection, leave this entry blank!
IpAddress=127.0.0.1
; UDP Port
;
; The UDP port number to send updates to, in the range 0 - 65,535.
UdpPort=10001
; *****************************************************************************
; *****************************************************************************
; Serial Connection Settings
;
; The device file that corresponds to the serial device that is connected
; to the unit. If using a TCP/IP connection, leave this entry blank!
;Device=/dev/ttyS0
; Serial Baud Rate (in bps)
Speed=9600
; Parity (0=none, 1=even, 2=odd)
Parity=0
; Number of bits per data 'word'.
WordSize=8
; *****************************************************************************
; Output Strings. The PAD data to output each time RDAirPlay changes
; play state, including any wildcards as placeholders for metadata values.
;
; For the list of supported wildcards. see the 'Metadata Wildcards' section
; of the Rivendell Operations Guide.
PsString=
DynamicPsString=%t
RadiotextString=%a
; Log Selection
;
; Set the status for each log to 'Yes', 'No' or 'Onair' to indicate whether
; state changes on that log should be output on this udp port. If set
; to 'Onair', then output will be generated only if RDAirPlays OnAir flag
; is active.
MasterLog=Yes
Aux1Log=Yes
Aux2Log=Yes
VLog101=No
VLog102=No
VLog103=No
VLog104=No
VLog105=No
VLog106=No
VLog107=No
VLog108=No
VLog109=No
VLog110=No
VLog111=No
VLog112=No
VLog113=No
VLog114=No
VLog115=No
VLog116=No
VLog117=No
VLog118=No
VLog119=No
VLog120=No
; Additional RDS encoders can be configured by adding new sections...
;[Rds2]
;IpAddress=192.168.10.22
;UdpPort=6789
;Device=/dev/ttyS0
;Speed=9600
;Parity=0
;WordSize=8
;PsString=
;DynamicPsString=%t
;RadiotextString=%a
;MasterLog=Yes
;Aux1Log=No
;Aux2Log=Onair
;VLog101=No
;VLog102=No
;VLog103=No
;VLog104=No
;VLog105=No
;VLog106=No
;VLog107=No
;VLog108=No
;VLog109=No
;VLog110=No
;VLog111=No
;VLog112=No
;VLog113=No
;VLog114=No
;VLog115=No
;VLog116=No
;VLog117=No
;VLog118=No
;VLog119=No
;VLog120=No

View File

@@ -0,0 +1,99 @@
#!%PYTHON_BANGPATH%
# pypad_inno713.py
#
# Send Now & Next updates to an Innovonics 713 RDS encoder
#
# (C) Copyright 2018 Fred Gleason <fredg@paravelsystems.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
import sys
import socket
import configparser
import serial
import pypad
def eprint(*args,**kwargs):
print(*args,file=sys.stderr,**kwargs)
def ProcessPad(update):
n=1
while(True):
section='Rds'+str(n)
try:
if update.shouldBeProcessed(section) and update.hasPadType(pypad.TYPE_NOW):
dps=''
if(len(update.config().get(section,'DynamicPsString'))!=0):
dps='DPS='+update.resolvePadFields(update.config().get(section,'DynamicPsString'),pypad.ESCAPE_NONE)+'\r\n'
ps=''
if(len(update.config().get(section,'PsString'))!=0):
ps='PS='+update.resolvePadFields(update.config().get(section,'PsString'),pypad.ESCAPE_NONE)+'\r\n'
text=''
if(len(update.config().get(section,'RadiotextString'))!=0):
text='TEXT='+update.resolvePadFields(update.config().get(section,'RadiotextString'),pypad.ESCAPE_NONE)+'\r\n'
try:
#
# Use serial output
#
tty_dev=update.config().get(section,'Device')
speed=int(update.config().get(section,'Speed'))
parity=serial.PARITY_NONE
if int(update.config().get(section,'Parity'))==1:
parity=serial.PARITY_EVEN
if int(update.config().get(section,'Parity'))==2:
parity=serial.PARITY_ODD
bytesize=int(update.config().get(section,'WordSize'))
dev=serial.Serial(tty_dev,speed,parity=parity,bytesize=bytesize)
if(len(dps)!=0):
dev.write(dps.encode('utf-8'))
if(len(ps)!=0):
dev.write(ps.encode('utf-8'))
if(len(text)!=0):
dev.write(text.encode('utf-8'))
dev.close()
except configparser.NoOptionError:
#
# Use UDP output
#
ipaddr=update.config().get(section,'IpAddress')
port=int(update.config().get(section,'UdpPort'))
if(len(dps)!=0):
send_sock.sendto(dps.encode('utf-8'),(ipaddr,port))
if(len(ps)!=0):
send_sock.sendto(ps.encode('utf-8'),(ipaddr,port))
if(len(text)!=0):
send_sock.sendto(text.encode('utf-8'),(ipaddr,port))
n=n+1
except configparser.NoSectionError:
return
#
# 'Main' function
#
# Create Send Socket
#
send_sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
rcvr=pypad.Receiver()
try:
rcvr.setConfigFile(sys.argv[3])
except IndexError:
eprint('pypad_inno713.py: USAGE: cmd <hostname> <port> <config>')
sys.exit(1)
rcvr.setCallback(ProcessPad)
rcvr.start(sys.argv[1],int(sys.argv[2]))

View File

@@ -0,0 +1,93 @@
; This is the configuration for the 'pypad_liqcomp.py' module for
; Rivendell, which can be used to output Now & Next data to one or more
; Liquid Compass Internet streaming encoders, using the following format:
;
; |<title>|<artist>|<cart-num>|<length>|<group>|<album>|<label>|<lf>
; Section Header
;
; One section per remote encoder is configured, starting with 'System1' and
; working up consecutively
[System1]
; IP Address
;
; The IP address of the remote encoder, in dotted-quad notation.
IpAddress=192.168.10.22
; UDP Port
;
; The UDP port number of the remote encoder, in the range 0 - 65,535.
UdpPort=5273
; Field Definitions. The string to use to populate the <title>, <artist>,
; <album> and <label> fields sent to ANDO each time RDAirPlay changes play
; state. These can include wildcards as placeholders for metadata values.
; For the list of supported wildcards. see the 'Metadata Wildcards' section
; of the Rivendell Operations Guide.
Title=%t
Artist=%a
Album=%l
Label=%b
; Log Selection
;
; Set the status for each log to 'Yes', 'No' or 'Onair' to indicate whether
; state changes on that log should be output on this udp port. If set
; to 'Onair', then output will be generated only if the RDAirPlay OnAir flag
; is active.
MasterLog=Yes
Aux1Log=Yes
Aux2Log=Yes
VLog101=No
VLog102=No
VLog103=No
VLog104=No
VLog105=No
VLog106=No
VLog107=No
VLog108=No
VLog109=No
VLog110=No
VLog111=No
VLog112=No
VLog113=No
VLog114=No
VLog115=No
VLog116=No
VLog117=No
VLog118=No
VLog119=No
VLog120=No
; Additional encoders can be configured by adding new sections...
;[System2]
;IpAddress=192.168.10.22
;UdpPort=6789
;Title=%u
;Artist=%a
;Album=%p
;MasterLog=Yes
;Aux1Log=No
;Aux2Log=Onair
;VLog101=No
;VLog102=No
;VLog103=No
;VLog104=No
;VLog105=No
;VLog106=No
;VLog107=No
;VLog108=No
;VLog109=No
;VLog110=No
;VLog111=No
;VLog112=No
;VLog113=No
;VLog114=No
;VLog115=No
;VLog116=No
;VLog117=No
;VLog118=No
;VLog119=No
;VLog120=No

View File

@@ -0,0 +1,72 @@
#!%PYTHON_BANGPATH%
# pypad_liqcomp.py
#
# Send PAD updates to a Liquid Compass stream encoder
#
# (C) Copyright 2018 Fred Gleason <fredg@paravelsystems.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
import sys
import socket
import configparser
import pypad
last_updates={}
def eprint(*args,**kwargs):
print(*args,file=sys.stderr,**kwargs)
def ProcessPad(update):
try:
last_updates[update.machine()]
except KeyError:
last_updates[update.machine()]=None
n=1
while(True):
section='System'+str(n)
try:
if update.shouldBeProcessed(section) and update.hasPadType(pypad.TYPE_NOW) and (last_updates[update.machine()] != update.startDateTimeString(pypad.TYPE_NOW)):
last_updates[update.machine()]=update.startDateTimeString(pypad.TYPE_NOW)
title=update.resolvePadFields(update.config().get(section,'Title'),pypad.ESCAPE_NONE)
artist=update.resolvePadFields(update.config().get(section,'Artist'),pypad.ESCAPE_NONE)
album=update.resolvePadFields(update.config().get(section,'Album'),pypad.ESCAPE_NONE)
label=update.resolvePadFields(update.config().get(section,'Label'),pypad.ESCAPE_NONE)
secs=update.padField(pypad.TYPE_NOW,pypad.FIELD_LENGTH)
group=update.padField(pypad.TYPE_NOW,pypad.FIELD_GROUP_NAME)
msg='|'+title+'|'+artist+'|'+str(update.padField(pypad.TYPE_NOW,pypad.FIELD_CART_NUMBER))+'|'+str(secs)+'|'+group+'|'+album+'|'+label+'|\n'
send_sock.sendto(msg.encode('utf-8'),
(update.config().get(section,'IpAddress'),int(update.config().get(section,'UdpPort'))))
n=n+1
except configparser.NoSectionError:
return
#
# 'Main' function
#
# Create Send Socket
#
send_sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
rcvr=pypad.Receiver()
try:
rcvr.setConfigFile(sys.argv[3])
except IndexError:
eprint('pypad_liqcomp.py: USAGE: cmd <hostname> <port> <config>')
sys.exit(1)
rcvr.setCallback(ProcessPad)
rcvr.start(sys.argv[1],int(sys.argv[2]))

View File

@@ -0,0 +1,97 @@
; This is the configuration for the 'pypad_live365.py' script for
; Rivendell, which can be used to update the metadata on a Live 365 channel
; using Now & Next data.
; Section Header
;
; One section per Live365 station is configured, starting with
; 'Station1' and working up consecutively
[Station1]
; MemberName
;
; The member name of the Live365 station to which to send updates.
MemberName=my_station
; Password
;
; The password of the Live365 station to which to send updates.
Password=changeme
; Metadata String. The metadata fields to be sent each time RDAirPlay changes
; play state, including any wildcards as placeholders for metadata values.
;
; The list of available wildcards can be found in the 'metadata_wildcards.txt'
; file in the Rivendell documentation directory.
;
TitleString=%t
ArtistString=%a
AlbumString=%l
; Log Selection
;
; Set the status for each log to 'Yes', 'No' or 'Onair' to indicate whether
; state changes on that log should be output to this station. If set
; to 'Onair', then output will be generated only if RDAirPlays OnAir flag
; is active.
MasterLog=Yes
Aux1Log=Yes
Aux2Log=Yes
VLog101=No
VLog102=No
VLog103=No
VLog104=No
VLog105=No
VLog106=No
VLog107=No
VLog108=No
VLog109=No
VLog110=No
VLog111=No
VLog112=No
VLog113=No
VLog114=No
VLog115=No
VLog116=No
VLog117=No
VLog118=No
VLog119=No
VLog120=No
; Additional Live365 stations can be configured by adding new
; sections...
;
;[Station2]
;MemberName=your_station
;Password=changeme
;TitleString=%t
;ArtistString=%a
;AlbumString=%l
;MasterLog=No
;Aux1Log=Yes
;Aux2Log=No
;VLog101=No
;VLog102=No
;VLog103=No
;VLog104=No
;VLog105=No
;VLog106=No
;VLog107=No
;VLog108=No
;VLog109=No
;VLog110=No
;VLog111=No
;VLog112=No
;VLog113=No
;VLog114=No
;VLog115=No
;VLog116=No
;VLog117=No
;VLog118=No
;VLog119=No
;VLog120=No

View File

@@ -0,0 +1,76 @@
#!%PYTHON_BANGPATH%
# pypad_live365.py
#
# Write PAD updates to Live365 stations
#
# (C) Copyright 2018 Fred Gleason <fredg@paravelsystems.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
import sys
import syslog
import configparser
import pycurl
import pypad
from io import BytesIO
def eprint(*args,**kwargs):
print(*args,file=sys.stderr,**kwargs)
def ProcessPad(update):
n=1
try:
while(True):
section='Station'+str(n)
if update.shouldBeProcessed(section) and update.hasPadType(pypad.TYPE_NOW):
member=update.escape(update.config().get(section,'MemberName'),pypad.ESCAPE_URL)
password=update.escape(update.config().get(section,'Password'),pypad.ESCAPE_URL)
title=update.resolvePadFields(update.config().get(section,'TitleString'),pypad.ESCAPE_URL)
artist=update.resolvePadFields(update.config().get(section,'ArtistString'),pypad.ESCAPE_URL)
album=update.resolvePadFields(update.config().get(section,'AlbumString'),pypad.ESCAPE_URL)
seconds=str(update.padField(pypad.TYPE_NOW,pypad.FIELD_LENGTH)//1000)
buf=BytesIO()
curl=pycurl.Curl()
url='http://www.live365.com/cgi-bin/add_song.cgi?member_name='+member+'&password='+password+'&version=2&filename=Rivendell&seconds='+seconds+'&title='+title+'&artist='+artist+'&album='+album
curl.setopt(curl.URL,url)
curl.setopt(curl.WRITEDATA,buf)
curl.setopt(curl.FOLLOWLOCATION,True)
try:
curl.perform()
code=curl.getinfo(pycurl.RESPONSE_CODE)
if (code<200) or (code>=300):
syslog.syslog(syslog.LOG_WARNING,'['+section+'] returned response code '+str(code))
except pycurl.error:
syslog.syslog(syslog.LOG_WARNING,'['+section+'] failed: '+curl.errstr())
curl.close()
n=n+1
except configparser.NoSectionError:
return
#
# 'Main' function
#
syslog.openlog(sys.argv[0].split('/')[-1])
rcvr=pypad.Receiver()
try:
rcvr.setConfigFile(sys.argv[3])
except IndexError:
eprint('pypad_live365.py: USAGE: cmd <hostname> <port> <config>')
sys.exit(1)
rcvr.setCallback(ProcessPad)
rcvr.start(sys.argv[1],int(sys.argv[2]))

View File

@@ -0,0 +1,116 @@
; This is the configuration for the 'pypad_serial.py' script for
; Rivendell, which can be used to output Now & Next data via one or more
; serial ports.
;
; NOTE: The serial ports configured here have NOTHING TO DO with the
; ports configured in RDAdmin! These ports are used strictly by the
; 'pypad_serial.py' plugin, and will not be usable by any other Rivendell
; component.
; Section Header
;
; One per serial device to be configured, starting with 'Serial1' and
; working up consecutively
[Serial1]
; Serial Device
;
; The device file that corresponds to the serial device.
Device=/dev/ttyS0
; Serial Baud Rate (in bps)
Speed=9600
; Parity (0=none, 1=even, 2=odd)
Parity=0
; Number of bits per data 'word'.
WordSize=8
; Format String. The string to be output each time RDAirPlay changes
; play state, including any wildcards as placeholders for metadata values.
;
; The list of available wildcards can be found in the 'metadata_wildcards.txt'
; file in the Rivendell documentation directory.
;
FormatString=NOW: %d(ddd MMM d hh:mm:ss yyyy): %t - %a\r\nNEXT: %D(ddd MMM d hh:mm:ss yyyy): %T - %A\r\n
; Encoding. Defines the set of escapes to be applied to the PAD fields.
; The following options are available:
;
; 0 - Perform no character escaping.
; 1 - "XML" escaping: Escape reserved characters as per XML-v1.0
; 2 - "Web" escaping: Escape reserved characters as per RFC 2396 Section 2.4
Encoding=0
; Null Update Handling. Defines how 'null' updates --i.e. those with a cart
; number of '0' -- should be handled.
;
; 0 - Process all updates regardless of cart values.
; 1 - Process update only if the 'now' cart is not null.
; 2 - Process update only if the 'next' cart is not null.
; 3 - Process update only if both the 'now' and 'next' carts are not null.
ProcessNullUpdates=0
; Log Selection
;
; Set the status for each log to 'Yes', 'No' or 'Onair' to indicate whether
; state changes on that log should be output on this serial port. If set
; to 'Onair', then output will be generated only if RDAirPlay's OnAir flag
; is active.
MasterLog=Yes
Aux1Log=Yes
Aux2Log=Yes
VLog101=No
VLog102=No
VLog103=No
VLog104=No
VLog105=No
VLog106=No
VLog107=No
VLog108=No
VLog109=No
VLog110=No
VLog111=No
VLog112=No
VLog113=No
VLog114=No
VLog115=No
VLog116=No
VLog117=No
VLog118=No
VLog119=No
VLog120=No
; Additional serial ports can be configured by adding new sections...
;[Serial2]
;Device=/dev/ttyS1
;Speed=9600
;Parity=0
;WordSize=8
;FormatString=%t
;ProcessNullUpdates=0
;MasterLog=Yes
;Aux1Log=No
;Aux2Log=Onair
;VLog101=No
;VLog102=No
;VLog103=No
;VLog104=No
;VLog105=No
;VLog106=No
;VLog107=No
;VLog108=No
;VLog109=No
;VLog110=No
;VLog111=No
;VLog112=No
;VLog113=No
;VLog114=No
;VLog115=No
;VLog116=No
;VLog117=No
;VLog118=No
;VLog119=No
;VLog120=No

View File

@@ -0,0 +1,69 @@
#!%PYTHON_BANGPATH%
# pypad_serial.py
#
# Write PAD updates to arbitrary URLs
#
# (C) Copyright 2018 Fred Gleason <fredg@paravelsystems.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
import sys
import syslog
import configparser
import pypad
import serial
from io import BytesIO
def eprint(*args,**kwargs):
print(*args,file=sys.stderr,**kwargs)
def ProcessPad(update):
n=1
try:
while(True):
section='Serial'+str(n)
if update.shouldBeProcessed(section):
devname=update.config().get(section,'Device')
speed=int(update.config().get(section,'Speed'))
parity=serial.PARITY_NONE
if int(update.config().get(section,'Parity'))==1:
parity=serial.PARITY_EVEN
if int(update.config().get(section,'Parity'))==2:
parity=serial.PARITY_ODD
bytesize=int(update.config().get(section,'WordSize'))
dev=serial.Serial(devname,speed,parity=parity,bytesize=bytesize)
fmtstr=update.config().get(section,'FormatString')
esc=int(update.config().get(section,'Encoding'))
dev.write(update.resolvePadFields(fmtstr,esc).encode('utf-8'))
dev.close()
n=n+1
except configparser.NoSectionError:
return
#
# 'Main' function
#
syslog.openlog(sys.argv[0].split('/')[-1])
rcvr=pypad.Receiver()
try:
rcvr.setConfigFile(sys.argv[3])
except IndexError:
eprint('pypad_serial.py: USAGE: cmd <hostname> <port> <config>')
sys.exit(1)
rcvr.setCallback(ProcessPad)
rcvr.start(sys.argv[1],int(sys.argv[2]))

View File

@@ -0,0 +1,96 @@
; This is the sample configuration for the 'pypad_shoutcast1.py' script for
; Rivendell, which can be used to update the metadata on an Shoutcast 1.x
; server using Now & Next data.
; Section Header
;
; One section per Shoutcast server instance is configured, starting with
; 'Shoutcast1' and working up consecutively
[Shoutcast1]
; Password
;
; The password of the Shoutcast server instance to which to send updates.
Password=changeme
; Host Name
;
; The fully-qualified domain name or IP address of the Shoutcast server
Hostname=shoutcast.example.com
; Host Port
;
; The TCP port number of the Shoutcast server
Tcpport=8000
; Format String. The metadata to be sent each time RDAirPlay changes
; play state, including any wildcards as placeholders for metadata values.
;
; The list of available wildcards can be found in the 'metadata_wildcards.txt'
; file in the Rivendell documentation directory.
;
FormatString=%a%20-%20%t
; Log Selection
;
; Set the status for each log to 'Yes', 'No' or 'Onair' to indicate whether
; state changes on that log should be output to this account. If set
; to 'Onair', then output will be generated only if RDAirPlays OnAir flag
; is active.
MasterLog=Yes
Aux1Log=Yes
Aux2Log=Yes
VLog101=No
VLog102=No
VLog103=No
VLog104=No
VLog105=No
VLog106=No
VLog107=No
VLog108=No
VLog109=No
VLog110=No
VLog111=No
VLog112=No
VLog113=No
VLog114=No
VLog115=No
VLog116=No
VLog117=No
VLog118=No
VLog119=No
VLog120=No
; Additional Shoutcast server instances can be configured by adding new
; sections...
;
;[Shoutcast2]
;Password=letmein
;Hostname=anotherone.example.com
;Tcpport=80
;FormatString=%t by %a
;MasterLog=Yes
;Aux1Log=No
;Aux2Log=Onair
;VLog101=No
;VLog102=No
;VLog103=No
;VLog104=No
;VLog105=No
;VLog106=No
;VLog107=No
;VLog108=No
;VLog109=No
;VLog110=No
;VLog111=No
;VLog112=No
;VLog113=No
;VLog114=No
;VLog115=No
;VLog116=No
;VLog117=No
;VLog118=No
;VLog119=No
;VLog120=No

View File

@@ -0,0 +1,83 @@
#!%PYTHON_BANGPATH%
# pypad_shoutcast1.py
#
# Write PAD updates to a Shoutcast 1 instance
#
# (C) Copyright 2018 Fred Gleason <fredg@paravelsystems.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
import sys
import syslog
import configparser
import pycurl
import pypad
from io import BytesIO
last_updates={}
def eprint(*args,**kwargs):
print(*args,file=sys.stderr,**kwargs)
def ProcessPad(update):
try:
last_updates[update.machine()]
except KeyError:
last_updates[update.machine()]=None
n=1
try:
while(True):
section='Shoutcast'+str(n)
if update.shouldBeProcessed(section) and update.hasPadType(pypad.TYPE_NOW) and (last_updates[update.machine()] != update.startDateTimeString(pypad.TYPE_NOW)):
last_updates[update.machine()]=update.startDateTimeString(pypad.TYPE_NOW)
song=update.resolvePadFields(update.config().get(section,'FormatString'),pypad.ESCAPE_URL)
url='http://'+update.config().get(section,'Hostname')+':'+str(update.config().get(section,'Tcpport'))+'/admin.cgi?pass='+update.escape(update.config().get(section,'Password'),pypad.ESCAPE_URL)+'&mode=updinfo&song='+song
curl=pycurl.Curl()
curl.setopt(curl.URL,url)
headers=[]
#
# D.N.A.S v1.9.8 refuses to process updates with the default
# CURL user-agent value, hence we lie to it.
#
headers.append('User-Agent: '+'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.2) Gecko/20070219 Firefox/2.0.0.2')
curl.setopt(curl.HTTPHEADER,headers);
try:
curl.perform()
code=curl.getinfo(pycurl.RESPONSE_CODE)
if (code<200) or (code>=300):
syslog.syslog(syslog.LOG_WARNING,'['+section+'] returned response code '+str(code))
except pycurl.error:
syslog.syslog(syslog.LOG_WARNING,'['+section+'] failed: '+curl.errstr())
curl.close()
n=n+1
except configparser.NoSectionError:
return
#
# 'Main' function
#
syslog.openlog(sys.argv[0].split('/')[-1])
rcvr=pypad.Receiver()
try:
rcvr.setConfigFile(sys.argv[3])
except IndexError:
eprint('pypad_shoutcast1.py: USAGE: cmd <hostname> <port> <config>')
sys.exit(1)
rcvr.setCallback(ProcessPad)
rcvr.start(sys.argv[1],int(sys.argv[2]))

View File

@@ -0,0 +1,132 @@
; This is the configuration for the 'pypad_spinitron' script
; for Rivendell, which can be used log Now & Next data to the Spinitron
; online playlist service [http://www.spinitron.com].
; Section Header
;
; One section per Spinitron account is configured, starting with
; 'Spinitron1' and working up consecutively
[Spinitron1]
; APIKey
;
; API key for the Spinitron v2 account to which to log the play-out.
; (This setting is only needed for Spinitron major version 2).
APIKey=change_me_please
; PlaylistMode
;
; Set the Spinitron playlist mode to use when sending updates.
;
; (For a discussion of the implications of this setting on your
; Spinitron playlists, see Section 3.2 of the 'Spinitron Automation
; Integration' document, available from Spinitron).
;
; The following options are recognized:
;
; Full - Always use the Spinitron 'Full Automation' mode.
; Assist - Always use the Spinitron 'Live Assist' mode.
; Follow - Use the Spinitron 'Full Automation' mode when RDAirPlay
; is in Automatic, otherwise use the Spinitron 'Live Assist' mode.
PlaylistMode=Follow
; Title. The string to be sent as the 'Title' field for each update,
; including any wildcards as placeholders for metadata values. For the
; list of supported wildcards. see the 'Metadata Wildcards' section of the
; Rivendell Operations Guide.
Title=%t
; Artist. The string to be sent as the 'Artist' field for each update,
; including any wildcards as placeholders for metadata values.
Artist=%a
; Album. The string to be sent as the 'Album' field for each update,
; including any wildcards as placeholders for metadata values.
Album=%l
; Label. The string to be sent as the 'Label' field for each update,
; including any wildcards as placeholders for metadata values.
Label=%b
; Composer. The string to be sent as the 'Composer' field for each update,
; including any wildcards as placeholders for metadata values.
Composer=%m
; Conductor. The string to be sent as the 'Conductor' field for each update,
; including any wildcards as placeholders for metadata values.
Conductor=%r
; Notes. The string to be sent as the 'Notes' field for each update,
; including any wildcards as placeholders for metadata values.
Notes=%u
; Log Selection
;
; Set the status for each log to 'Yes', 'No' or 'Onair' to indicate whether
; state changes on that log should be output to this account. If set
; to 'Onair', then output will be generated only if RDAirPlays OnAir flag
; is active.
MasterLog=Yes
Aux1Log=Yes
Aux2Log=Yes
VLog101=No
VLog102=No
VLog103=No
VLog104=No
VLog105=No
VLog106=No
VLog107=No
VLog108=No
VLog109=No
VLog110=No
VLog111=No
VLog112=No
VLog113=No
VLog114=No
VLog115=No
VLog116=No
VLog117=No
VLog118=No
VLog119=No
VLog120=No
; Additional Spinitron instances can be configured by adding new
; sections...
;
;[Spinitron2]
;MajorVersion=2
;Station=abcd
;Username=metoo
;Password=letmein
;APIKey=some_different_key
;Title=%t
;Artist=%a
;Album=%l
;Label=%b
;Composer=%m
;Conductor=%r
;Notes=%u
;MasterLog=Yes
;Aux1Log=No
;Aux2Log=Onair
;VLog101=No
;VLog102=No
;VLog103=No
;VLog104=No
;VLog105=No
;VLog106=No
;VLog107=No
;VLog108=No
;VLog109=No
;VLog110=No
;VLog111=No
;VLog112=No
;VLog113=No
;VLog114=No
;VLog115=No
;VLog116=No
;VLog117=No
;VLog118=No
;VLog119=No
;VLog120=No

View File

@@ -0,0 +1,128 @@
#!%PYTHON_BANGPATH%
# pypad_spinitron.py
#
# Write PAD updates to the Spinitron Playlist Service
#
# (C) Copyright 2018 Fred Gleason <fredg@paravelsystems.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
import sys
import syslog
import configparser
import pycurl
import pypad
from io import BytesIO
last_updates={}
def eprint(*args,**kwargs):
print(*args,file=sys.stderr,**kwargs)
def JsonField(update,tag,value,is_last=False):
if (value==None) or (value==''):
ret=' "'+tag+'": null'
else:
ret=' "'+tag+'": "'+update.escape(value,pypad.ESCAPE_JSON)+'"'
if not is_last:
ret+=','
return ret+'\r\n'
def ProcessPad(update):
try:
last_updates[update.machine()]
except KeyError:
last_updates[update.machine()]=None
n=1
try:
while(True):
section='Spinitron'+str(n)
if update.shouldBeProcessed(section) and update.hasPadType(pypad.TYPE_NOW) and (last_updates[update.machine()] != update.startDateTimeString(pypad.TYPE_NOW)):
last_updates[update.machine()]=update.startDateTimeString(pypad.TYPE_NOW)
title=update.resolvePadFields(update.config().get(section,'Title'),pypad.ESCAPE_JSON)
artist=update.resolvePadFields(update.config().get(section,'Artist'),pypad.ESCAPE_JSON)
album=update.resolvePadFields(update.config().get(section,'Album'),pypad.ESCAPE_JSON)
label=update.resolvePadFields(update.config().get(section,'Label'),pypad.ESCAPE_JSON)
composer=update.resolvePadFields(update.config().get(section,'Composer'),pypad.ESCAPE_JSON)
conductor=update.resolvePadFields(update.config().get(section,'Conductor'),pypad.ESCAPE_JSON)
notes=update.resolvePadFields(update.config().get(section,'Notes'),pypad.ESCAPE_JSON)
json='{\r\n'
pmode=update.config().get(section,'PlaylistMode')
if pmode=='Full':
json+=' "live": false\r\n'
if pmode=='Assist':
json+=' "live:" true\r\n'
if pmode=='Follow':
if update.mode()=='Automatic':
json+=' "live": false,\r\n'
else:
json+=' "live": true,\r\n'
duration=str(update.padField(pypad.TYPE_NOW,pypad.FIELD_LENGTH)//1000)
year=update.padField(pypad.TYPE_NOW,pypad.FIELD_YEAR)
if year==None:
json+=' "released": null,\r\n'
else:
json+=' "released": '+str(year)+',\r\n'
json+=' "duration": '+duration+',\r\n'
json+=JsonField(update,'artist',artist)
json+=JsonField(update,'release',album)
json+=JsonField(update,'label',label)
json+=JsonField(update,'song',title)
json+=JsonField(update,'composer',composer)
json+=JsonField(update,'conductor',conductor)
json+=JsonField(update,'note',notes)
json+=JsonField(update,'isrc',update.padField(pypad.TYPE_NOW,pypad.FIELD_ISRC),True)
json+='}\r\n'
send_buf=BytesIO(json.encode('utf-8'))
recv_buf=BytesIO()
curl=pycurl.Curl()
curl.setopt(curl.URL,'https://spinitron.com/api/spins')
headers=[]
headers.append('Authorization: Bearer '+update.config().get(section,'APIKey'))
headers.append('Content-Type: application/json')
headers.append('Content-Length: '+str(len(json.encode('utf-8'))))
curl.setopt(curl.HTTPHEADER,headers);
curl.setopt(curl.POST,True)
curl.setopt(curl.READDATA,send_buf)
curl.setopt(curl.WRITEDATA,recv_buf)
try:
curl.perform()
code=curl.getinfo(pycurl.RESPONSE_CODE)
if (code<200) or (code>=300):
syslog.syslog(syslog.LOG_WARNING,'['+section+'] returned response code '+str(code))
except pycurl.error:
syslog.syslog(syslog.LOG_WARNING,'['+section+'] failed: '+curl.errstr())
curl.close()
n=n+1
except configparser.NoSectionError:
return
#
# 'Main' function
#
syslog.openlog(sys.argv[0].split('/')[-1])
rcvr=pypad.Receiver()
try:
rcvr.setConfigFile(sys.argv[3])
except IndexError:
eprint('pypad_spinitron.py: USAGE: cmd <hostname> <port> <config>')
sys.exit(1)
rcvr.setCallback(ProcessPad)
rcvr.start(sys.argv[1],int(sys.argv[2]))

View File

@@ -0,0 +1,123 @@
; This is the configuration for the 'pypad_spottrap.script' script for
; Rivendell, which can be used to output Now & Next data to one or more
; remote UDP ports on the basis of Group and Length.
;
; Module configuration consists of a list of rules, each of which consists
; in turn with a set of filter parameters and actions. Each PAD update is
; compared against the filter parameters of each rule. If the filter matches,
; then the rule actions are performed.
;
; Rule Header
;
; One section per rule is configured, starting with 'Rule1' and
; working up consecutively
[Rule1]
; FILTER PARAMETERS
;
; Group Name
;
; The name of the Rivendell group to match.
GroupName=BEDS
; Minimum Length
;
; The minimum length, in milliseconds. A PAD update with a Length of less
; than this value will not be matched.
MinimumLength=0
; Maximum Length
; The maximum length, in milliseconds. A PAD update with a Length of more
; than this value will not be matched.
MaximumLength=10000000
; ACTION PARAMETERS
;
; IP Address
;
; The IP address of the remote UDP port, in dotted-quad notation.
IpAddress=192.168.10.30
; UDP Port
;
; The UDP port number of the remote UDP port, in the range 0 - 65,535.
UdpPort=5859
; Format String. The string to be output each time RDAirPlay changes
; play state, including any wildcards as placeholders for metadata values.
;
; The list of available wildcards can be found in the 'metadata_wildcards.txt'
; file in the Rivendell documentation directory.
;
FormatString=DX 1 %h!
; Default Format String. Similar to the 'FormatString=' parameter described
; above, but this string is output when the rule *doesn't* match. All of
; the wildcards described in the 'FormatString=' section apply here as well.
DefaultFormatString=DX 1 0!
; Log Selection
;
; Set the status for each log to 'Yes', 'No' or 'Onair' to indicate whether
; state changes on that log should be output on this udp port. If set
; to 'Onair', then output will be generated only if RDAirPlay's OnAir flag
; is active.
MasterLog=Yes
Aux1Log=Yes
Aux2Log=Yes
VLog101=No
VLog102=No
VLog103=No
VLog104=No
VLog105=No
VLog106=No
VLog107=No
VLog108=No
VLog109=No
VLog110=No
VLog111=No
VLog112=No
VLog113=No
VLog114=No
VLog115=No
VLog116=No
VLog117=No
VLog118=No
VLog119=No
VLog120=No
; Additional Rules can be configured by adding new sections...
;[Rule2]
;GroupName=TEST
;MinimumLength=0
;MaximumLength=1000000
;FormatString=Artist: %a%r
;DefaultFormatString=Artist: %a%r
;IpAddress=192.168.10.30
;UdpPort=6789
;MasterLog=Yes
;Aux1Log=No
;Aux2Log=Onair
;VLog101=No
;VLog102=No
;VLog103=No
;VLog104=No
;VLog105=No
;VLog106=No
;VLog107=No
;VLog108=No
;VLog109=No
;VLog110=No
;VLog111=No
;VLog112=No
;VLog113=No
;VLog114=No
;VLog115=No
;VLog116=No
;VLog117=No
;VLog118=No
;VLog119=No
;VLog120=No

View File

@@ -0,0 +1,71 @@
#!%PYTHON_BANGPATH%
# pypad_spottrap.py
#
# Output Now & Next data on the basis of Group and Length.
#
# (C) Copyright 2018 Fred Gleason <fredg@paravelsystems.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
import sys
import socket
import configparser
import pypad
last_updates={}
def eprint(*args,**kwargs):
print(*args,file=sys.stderr,**kwargs)
def ProcessPad(update):
try:
last_updates[update.machine()]
except KeyError:
last_updates[update.machine()]=None
n=1
while(True):
section='Rule'+str(n)
try:
if update.shouldBeProcessed(section) and update.hasPadType(pypad.TYPE_NOW) and (last_updates[update.machine()] != update.startDateTimeString(pypad.TYPE_NOW)):
last_updates[update.machine()]=update.startDateTimeString(pypad.TYPE_NOW)
length=update.padField(pypad.TYPE_NOW,pypad.FIELD_LENGTH)
if update.padField(pypad.TYPE_NOW,pypad.FIELD_GROUP_NAME)==update.config().get(section,'GroupName') and length>=int(update.config().get(section,'MinimumLength')) and length<=int(update.config().get(section,'MaximumLength')):
msg=update.resolvePadFields(update.config().get(section,'FormatString'),pypad.ESCAPE_NONE)
else:
msg=update.resolvePadFields(update.config().get(section,'DefaultFormatString'),pypad.ESCAPE_NONE)
send_sock.sendto(msg.encode('utf-8'),
(update.config().get(section,'IpAddress'),int(update.config().get(section,'UdpPort'))))
n=n+1
except configparser.NoSectionError:
return
#
# 'Main' function
#
# Create Send Socket
#
send_sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
rcvr=pypad.Receiver()
try:
rcvr.setConfigFile(sys.argv[3])
except IndexError:
eprint('pypad_spottrap.py: USAGE: cmd <hostname> <port> <config>')
sys.exit(1)
rcvr.setCallback(ProcessPad)
rcvr.start(sys.argv[1],int(sys.argv[2]))

View File

@@ -0,0 +1,105 @@
; This is the sample configuration for the 'pypad_tunein.py' PyPAD script for
; Rivendell, which can be used to update the metadata at TuneIn server
; using Now & Next data.
;
; Section Header
;
; One section per TuneIn station is configured, starting with
; 'Station1' and working up consecutively
[Station1]
; StationID
;
; The Station ID of the TuneIn station to which to send updates.
StationID=s123456
; PartnerID
;
; The Partner ID of the TuneIn station to which to send updates.
PartnerID=changeme
; PartnerKey
;
; The Partner Key of the TuneIn station to which to send updates.
PartnerKey=changeme
; Metadata String. The metadata fields to be sent each time RDAirPlay changes
; play state, including any wildcards as placeholders for metadata values.
;
; The list of available wildcards can be found in the 'metadata_wildcards.txt'
; file in the Rivendell documentation directory.
;
TitleString=%t
ArtistString=%a
AlbumString=%l
; Log Selection
;
; Set the status for each log to 'Yes', 'No' or 'Onair' to indicate whether
; state changes on that log should be output to this station. If set
; to 'Onair', then output will be generated only if RDAirPlays OnAir flag
; is active.
MasterLog=Yes
Aux1Log=Yes
Aux2Log=Yes
VLog101=No
VLog102=No
VLog103=No
VLog104=No
VLog105=No
VLog106=No
VLog107=No
VLog108=No
VLog109=No
VLog110=No
VLog111=No
VLog112=No
VLog113=No
VLog114=No
VLog115=No
VLog116=No
VLog117=No
VLog118=No
VLog119=No
VLog120=No
; Additional TuneIn stations can be configured by adding new
; sections...
;
;[Station2]
;StationID=s654321
;PartnerID=changeme
;PartnerKey=changeme
;TitleString=%t
;ArtistString=%a
;AlbumString=%l
;MasterLog=No
;Aux1Log=Yes
;Aux2Log=No
;VLog101=No
;VLog102=No
;VLog103=No
;VLog104=No
;VLog105=No
;VLog106=No
;VLog107=No
;VLog108=No
;VLog109=No
;VLog110=No
;VLog111=No
;VLog112=No
;VLog113=No
;VLog114=No
;VLog115=No
;VLog116=No
;VLog117=No
;VLog118=No
;VLog119=No
;VLog120=No

View File

@@ -0,0 +1,104 @@
#!%PYTHON_BANGPATH%
# pypad_tunein.py
#
# Send PAD updates to TuneIn
#
# (C) Copyright 2018 Fred Gleason <fredg@paravelsystems.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
import os
import sys
import socket
import requests
import xml.etree.ElementTree as ET
import syslog
import pypad
import configparser
def eprint(*args,**kwargs):
print(pypad_name+': ',file=sys.stderr,end='')
print(*args,file=sys.stderr)
syslog.syslog(syslog.LOG_ERR,*args)
def iprint(*args,**kwargs):
print(pypad_name+': ',file=sys.stdout,end='')
print(*args,file=sys.stdout)
syslog.syslog(syslog.LOG_INFO,*args)
def isTrue(string):
l=['Yes','On','True','1']
return string.lower() in map(str.lower,l)
def ProcessPad(update):
if update.hasPadType(pypad.TYPE_NOW):
n=1
while(True):
section='Station'+str(n)
try:
values={}
values['id']=update.config().get(section,'StationID')
values['partnerId']=update.config().get(section,'PartnerID')
values['partnerKey']=update.config().get(section,'PartnerKey')
values['title']=update.resolvePadFields(update.config().get(section,'TitleString'),pypad.ESCAPE_NONE)
values['artist']=update.resolvePadFields(update.config().get(section,'ArtistString'),pypad.ESCAPE_NONE)
values['album']=update.resolvePadFields(update.config().get(section,'AlbumString'),pypad.ESCAPE_NONE)
iprint('Updating TuneIn: artist='+values['artist']+' title='+values['title']+' album='+values['album'])
try:
response=requests.get('http://air.radiotime.com/Playing.ashx',params=values)
response.raise_for_status()
except requests.exceptions.RequestException as e:
eprint(str(e))
else:
xml=ET.fromstring(response.text)
status=xml.find('./head/status')
if(status.text!='200'):
eprint('Update Failed: '+xml.find('./head/fault').text)
n=n+1
except configparser.NoSectionError:
if(n==1):
eprint('No station config found')
return
#
# Program Name
#
pypad_name=os.path.basename(__file__)
#
# Open Syslog
#
syslog.openlog(pypad_name,logoption=syslog.LOG_PID,facility=syslog.LOG_DAEMON)
#
# Create Send Socket
#
send_sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
#
# Start Receiver
#
rcvr=pypad.Receiver()
try:
rcvr.setConfigFile(sys.argv[3])
except IndexError:
eprint('pypad_tunein.py: USAGE: cmd <hostname> <port> <config>')
sys.exit(1)
rcvr.setCallback(ProcessPad)
iprint('Started')
rcvr.start(sys.argv[1],int(sys.argv[2]))
iprint('Stopped')

View File

@@ -0,0 +1,105 @@
; This is the sample configuration for the 'pypad_udp.py' PyPAD script for
; Rivendell, which can be used to output Now & Next data to one or more
; remote UDP ports.
; Section Header
;
; One section per remote UDP port is configured, starting with 'Udp1' and
; working up consecutively
[Udp1]
; IP Address
;
; The IP address of the remote UDP port, in dotted-quad notation.
IpAddress=127.0.0.1
; UDP Port
;
; The UDP port number of the remote UDP port, in the range 0 - 65,535.
UdpPort=1234
; Format String. The string to be output each time RDAirPlay changes
; play state, including any wildcards as placeholders for metadata values.
;
; The list of available wildcards can be found in the 'metadata_wildcards.txt'
; file in the Rivendell documentation directory.
;
FormatString=NOW: %d(ddd MMM d hh:mm:ss yyyy): %t - %a\nNEXT: %D(ddd MMM d hh:mm:ss yyyy): %T - %A\n
; Encoding. Defines the set of escapes to be applied to the PAD fields.
; The following options are available:
;
; 0 - Perform no character escaping.
; 1 - "XML" escaping: Escape reserved characters as per XML-v1.0
; 2 - "Web" escaping: Escape reserved characters as per RFC 2396 Section 2.4
Encoding=0
; Null Update Handling. Defines how 'null' updates --i.e. those with a cart
; number of '0' -- should be handled.
;
; 0 - Process all updates regardless of cart values.
; 1 - Process update only if the 'now' cart is not null.
; 2 - Process update only if the 'next' cart is not null.
; 3 - Process update only if both the 'now' and 'next' carts are not null.
ProcessNullUpdates=0
; Log Selection
;
; Set the status for each log to 'Yes', 'No' or 'Onair' to indicate whether
; state changes on that log should be output on this udp port. If set
; to 'Onair', then output will be generated only if RDAirPlays OnAir flag
; is active.
MasterLog=Yes
Aux1Log=Yes
Aux2Log=Yes
VLog101=No
VLog102=No
VLog103=No
VLog104=No
VLog105=No
VLog106=No
VLog107=No
VLog108=No
VLog109=No
VLog110=No
VLog111=No
VLog112=No
VLog113=No
VLog114=No
VLog115=No
VLog116=No
VLog117=No
VLog118=No
VLog119=No
VLog120=No
; Additional UDP destinations can be configured by adding new sections...
;[Udp2]
;FormatString=Artist: %a%r
;IpAddress=192.168.10.22
;UdpPort=6789
;ProcessNullUpdates=0
;MasterLog=Yes
;Aux1Log=No
;Aux2Log=Onair
;VLog101=No
;VLog102=No
;VLog103=No
;VLog104=No
;VLog105=No
;VLog106=No
;VLog107=No
;VLog108=No
;VLog109=No
;VLog110=No
;VLog111=No
;VLog112=No
;VLog113=No
;VLog114=No
;VLog115=No
;VLog116=No
;VLog117=No
;VLog118=No
;VLog119=No
;VLog120=No

58
apis/pypad/scripts/pypad_udp.py Executable file
View File

@@ -0,0 +1,58 @@
#!%PYTHON_BANGPATH%
# pypad_udp.py
#
# Send PAD updates via UDP
#
# (C) Copyright 2018 Fred Gleason <fredg@paravelsystems.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
import sys
import socket
import configparser
import pypad
def eprint(*args,**kwargs):
print(*args,file=sys.stderr,**kwargs)
def ProcessPad(update):
n=1
while(True):
section='Udp'+str(n)
try:
if update.shouldBeProcessed(section):
fmtstr=update.config().get(section,'FormatString')
send_sock.sendto(update.resolvePadFields(fmtstr,int(update.config().get(section,'Encoding'))).encode('utf-8'),
(update.config().get(section,'IpAddress'),int(update.config().get(section,'UdpPort'))))
n=n+1
except configparser.NoSectionError:
return
#
# 'Main' function
#
# Create Send Socket
#
send_sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
rcvr=pypad.Receiver()
try:
rcvr.setConfigFile(sys.argv[3])
except IndexError:
eprint('pypad_udp.py: USAGE: cmd <hostname> <port> <config>')
sys.exit(1)
rcvr.setCallback(ProcessPad)
rcvr.start(sys.argv[1],int(sys.argv[2]))

View File

@@ -0,0 +1,113 @@
; This is the configuration for the 'pypad_urlwrite' script for
; Rivendell, which can be used to send one or more files to a local or
; remote system using Now & Next data.
; Section Header
;
; One section per file to be written should be configured, starting with
; 'Url1' and working up consecutively
[Url1]
; Url
;
; The URL to which to send the file, which may contain filepath
; wildcards as defined in Appendix C of the Rivendell Operations and
; Administration Guide.
;
; The following URL schemes are recognized:
; file://
; ftp://
; http:// (uses the PUT method)
; sftp://
;
Url=sftp://server.example.com/myfile.txt
; Username
;
; The username to be used when authenticating to the remote server.
;
Username=someuser
; Password
;
; The password to be used when authenticating to the remote server.
Password=hackme
; Format String. The string to be sent each time RDAirPlay changes
; play state, including any wildcards as placeholders for metadata values.
;
; The list of available wildcards can be found in the 'Metadata Wildcards'
; appendix of the Rivendell Operations Guide.
;
FormatString=NOW: %d(ddd MMM d hh:mm:ss yyyy): %t - %a\nNEXT: %D(ddd MMM d hh:mm:ss yyyy): %T - %A\n
; Encoding. Defines the set of escapes to be applied to the PAD fields.
; The following options are available:
;
; 0 - Perform no character escaping.
; 1 - "XML" escaping: Escape reserved characters as per XML-v1.0
; 2 - "URL" escaping: Escape reserved characters as per RFC 2396 Section 2.4
; 3 - "JSON" escaping: Escape reserved characters as per ECMA-404
Encoding=0
; Log Selection
;
; Set the status for each log to 'Yes', 'No' or 'Onair' to indicate whether
; state changes on that log should be output. If set to 'Onair', then
; output will be generated only if RDAirPlays OnAir flag is active.
MasterLog=Yes
Aux1Log=Yes
Aux2Log=Yes
VLog101=No
VLog102=No
VLog103=No
VLog104=No
VLog105=No
VLog106=No
VLog107=No
VLog108=No
VLog109=No
VLog110=No
VLog111=No
VLog112=No
VLog113=No
VLog114=No
VLog115=No
VLog116=No
VLog117=No
VLog118=No
VLog119=No
VLog120=No
; Additional files can be written by adding new sections...
;
;[Url2]
;Url=file:///home/rd/somefile.txt
;Username=janedoe
;Password=crackme
;FormatString=%t by %a\r\n
;Encoding=0
;MasterLog=Yes
;Aux1Log=No
;Aux2Log=Onair
;VLog101=No
;VLog102=No
;VLog103=No
;VLog104=No
;VLog105=No
;VLog106=No
;VLog107=No
;VLog108=No
;VLog109=No
;VLog110=No
;VLog111=No
;VLog112=No
;VLog113=No
;VLog114=No
;VLog115=No
;VLog116=No
;VLog117=No
;VLog118=No
;VLog119=No
;VLog120=No

View File

@@ -0,0 +1,69 @@
#!%PYTHON_BANGPATH%
# pypad_urlwrite.py
#
# Write PAD updates to arbitrary URLs
#
# (C) Copyright 2018 Fred Gleason <fredg@paravelsystems.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
import sys
import syslog
import configparser
import pycurl
import pypad
from io import BytesIO
def eprint(*args,**kwargs):
print(*args,file=sys.stderr,**kwargs)
def ProcessPad(update):
n=1
try:
while(True):
section='Url'+str(n)
if update.shouldBeProcessed(section):
fmtstr=update.config().get(section,'FormatString')
buf=BytesIO(update.resolvePadFields(fmtstr,int(update.config().get(section,'Encoding'))).encode('utf-8'))
curl=pycurl.Curl()
curl.setopt(curl.URL,update.resolveFilepath(update.config().get(section,'Url'),update.dateTime()))
curl.setopt(curl.USERNAME,update.config().get(section,'Username'))
curl.setopt(curl.PASSWORD,update.config().get(section,'Password'))
curl.setopt(curl.UPLOAD,True)
curl.setopt(curl.READDATA,buf)
try:
curl.perform()
except pycurl.error:
syslog.syslog(syslog.LOG_WARNING,'['+section+'] failed: '+curl.errstr())
curl.close()
n=n+1
except configparser.NoSectionError:
return
#
# 'Main' function
#
syslog.openlog(sys.argv[0].split('/')[-1])
rcvr=pypad.Receiver()
try:
rcvr.setConfigFile(sys.argv[3])
except IndexError:
eprint('pypad_urlwrite.py: USAGE: cmd <hostname> <port> <config>')
sys.exit(1)
rcvr.setCallback(ProcessPad)
rcvr.start(sys.argv[1],int(sys.argv[2]))

View File

@@ -0,0 +1,89 @@
; This is the configuration for the 'pypad_walltime.py' script for
; Rivendell, which can be used to output Now & Next data to one or more
; Walltime clocks.
; Section Header
;
; One section per WallTime unit is configured, starting with 'Walltime1' and
; working up consecutively
[Walltime1]
; IP Address
;
; The IP address of the Walltime clock, in dotted-quad notation.
IpAddress=192.168.21.100
; Password
;
; The password to use when sending HTML updates.
Password=crackme
; Format String. The template for the HTML sent to the Walltime web display
; each time RDAirPlay changes play state, including any wildcards as
; placeholders for metadata values.
;
; The list of available wildcards can be found in the 'Metadata Wildcards'
; appendix in the Rivendell Operations Guide.
;
FormatString=<body bgcolor="#000000"><table cellspacing="2" cellpadding="2" border="0"><tr><td>&nbsp;</td></tr><tr><td>&nbsp;</td></tr><tr><td><font size="6" color="white">%t</font></td></tr><tr><td>&nbsp;</td></tr><tr><td><font size="5" color="red">%a</font><td></tr></table></body>
; Log Selection
;
; Set the status for each log to 'Yes', 'No' or 'Onair' to indicate whether
; state changes on that log should be output on this udp port. If set
; to 'Onair', then output will be generated only if RDAirPlays OnAir flag
; is active.
;
MasterLog=Yes
Aux1Log=No
Aux2Log=No
VLog101=No
VLog102=No
VLog103=No
VLog104=No
VLog105=No
VLog106=No
VLog107=No
VLog108=No
VLog109=No
VLog110=No
VLog111=No
VLog112=No
VLog113=No
VLog114=No
VLog115=No
VLog116=No
VLog117=No
VLog118=No
VLog119=No
VLog120=No
; Additional WallTime units can be configured by adding new sections...
;[Walltime2]
;FormatString=Artist: %a
;IpAddress=192.168.10.22
;UdpPort=6060
;MasterLog=Yes
;Aux1Log=No
;Aux2Log=Onair
;VLog101=No
;VLog102=No
;VLog103=No
;VLog104=No
;VLog105=No
;VLog106=No
;VLog107=No
;VLog108=No
;VLog109=No
;VLog110=No
;VLog111=No
;VLog112=No
;VLog113=No
;VLog114=No
;VLog115=No
;VLog116=No
;VLog117=No
;VLog118=No
;VLog119=No
;VLog120=No

View File

@@ -0,0 +1,69 @@
#!%PYTHON_BANGPATH%
# pypad_walltime.py
#
# Write PAD updates a WallTime text widget.
#
# (C) Copyright 2018 Fred Gleason <fredg@paravelsystems.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
import sys
import syslog
import configparser
import pycurl
import pypad
from io import BytesIO
def eprint(*args,**kwargs):
print(*args,file=sys.stderr,**kwargs)
def ProcessPad(update):
n=1
try:
while(True):
section='Walltime'+str(n)
if update.shouldBeProcessed(section):
fmtstr=update.config().get(section,'FormatString')
buf=BytesIO(update.resolvePadFields(fmtstr,pypad.ESCAPE_NONE).encode('utf-8'))
curl=pycurl.Curl()
curl.setopt(curl.URL,'http://'+update.config().get(section,'IpAddress')+'/webwidget');
curl.setopt(curl.USERNAME,'user')
curl.setopt(curl.PASSWORD,update.config().get(section,'Password'))
curl.setopt(curl.UPLOAD,True)
curl.setopt(curl.READDATA,buf)
try:
curl.perform()
except pycurl.error:
syslog.syslog(syslog.LOG_WARNING,'['+section+'] failed: '+curl.errstr())
curl.close()
n=n+1
except configparser.NoSectionError:
return
#
# 'Main' function
#
syslog.openlog(sys.argv[0].split('/')[-1])
rcvr=pypad.Receiver()
try:
rcvr.setConfigFile(sys.argv[3])
except IndexError:
eprint('pypad_walltime.py: USAGE: cmd <hostname> <port> <config>')
sys.exit(1)
rcvr.setCallback(ProcessPad)
rcvr.start(sys.argv[1],int(sys.argv[2]))

View File

@@ -0,0 +1,92 @@
; This is the configuration for the 'pypad_xds' script for
; Rivendell, which can be used to output ISCI data for a Citidel X-Digital
; Vantive system via UDP or serial.
; Section Header
;
; One section per Vantive UDP port is configured, starting with 'Udp1' and
; working up consecutively
[Udp1]
; IP Address
;
; The IP address of the remote UDP port, in dotted-quad notation.
IpAddress=127.0.0.1
; UDP Port
;
; The UDP port number of the remote UDP port, in the range 0 - 65,535.
UdpPort=1234
; TTY Device
;
; Use the specified TTY device instead of UDP to send CICs.
;TtyDevice=/dev/ttyS0
;TtySpeed=19200
; ISCI Prefix
;
; The string to prepend to transmitted ISCI code strings.
;
IsciPrefix=TEST_
; Log Selection
;
; Set the status for each log to 'Yes', 'No' or 'Onair' to indicate whether
; state changes on that log should be output on this port. If set
; to 'Onair', then output will be generated only if RDAirPlays OnAir flag
; is active.
MasterLog=Yes
Aux1Log=Yes
Aux2Log=Yes
VLog101=No
VLog102=No
VLog103=No
VLog104=No
VLog105=No
VLog106=No
VLog107=No
VLog108=No
VLog109=No
VLog110=No
VLog111=No
VLog112=No
VLog113=No
VLog114=No
VLog115=No
VLog116=No
VLog117=No
VLog118=No
VLog119=No
VLog120=No
; Additional Vantive destinations can be configured by adding new sections...
;[Udp2]
;IpAddress=192.168.10.22
;UdpPort=6789
;TtyDevice=/dev/ttyS1
;TtySpeed=19200
;MasterLog=Yes
;Aux1Log=No
;Aux2Log=Onair
;VLog101=No
;VLog102=No
;VLog103=No
;VLog104=No
;VLog105=No
;VLog106=No
;VLog107=No
;VLog108=No
;VLog109=No
;VLog110=No
;VLog111=No
;VLog112=No
;VLog113=No
;VLog114=No
;VLog115=No
;VLog116=No
;VLog117=No
;VLog118=No
;VLog119=No
;VLog120=No

104
apis/pypad/scripts/pypad_xds.py Executable file
View File

@@ -0,0 +1,104 @@
#!%PYTHON_BANGPATH%
# pypad_xds.py
#
# Send CICs via UDP or serial
#
# (C) Copyright 2018 Fred Gleason <fredg@paravelsystems.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
import sys
import socket
import configparser
import serial
import pypad
def eprint(*args,**kwargs):
print(*args,file=sys.stderr,**kwargs)
def FilterField(string):
string=string.replace(' ','_')
string=string.replace(',','_')
string=string.replace('.','_')
string=string.replace('"',"'")
string=string.replace('%','-')
string=string.replace('*','-')
string=string.replace('+','-')
string=string.replace('/','-')
string=string.replace(':','-')
string=string.replace(';','-')
string=string.replace('<','-')
string=string.replace('=','-')
string=string.replace('>','-')
string=string.replace('?','-')
string=string.replace('@','-')
string=string.replace('[','-')
string=string.replace('\\','-')
string=string.replace(']','-')
string=string.replace('^','-')
string=string.replace('{','-')
string=string.replace('|','-')
string=string.replace('}','-')
return string
def ProcessPad(update):
n=1
while(True):
section='Udp'+str(n)
try:
if update.shouldBeProcessed(section) and update.hasPadType(pypad.TYPE_NOW):
packet='0:'+update.serviceProgramCode()+':'+update.config().get(section,'IsciPrefix')+FilterField(update.padField(pypad.TYPE_NOW,pypad.FIELD_EXTERNAL_EVENT_ID))+':*'
try:
#
# Use serial output
#
tty_dev=update.config().get(section,'TtyDevice')
speed=int(update.config().get(section,'TtySpeed'))
parity=serial.PARITY_NONE
dev=serial.Serial(tty_dev,speed,parity=parity,bytesize=8)
dev.write(packet.encode('utf-8'))
dev.close()
except configparser.NoOptionError:
#
# Use UDP output
#
send_sock.sendto(packet.encode('utf-8'),
(update.config().get(section,'IpAddress'),int(update.config().get(section,'UdpPort'))))
n=n+1
except configparser.NoSectionError:
return
#
# 'Main' function
#
# Create Send Socket
#
send_sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
rcvr=pypad.Receiver()
try:
rcvr.setConfigFile(sys.argv[3])
except IndexError:
eprint('pypad_xds.py: USAGE: cmd <hostname> <port> <config>')
sys.exit(1)
rcvr.setCallback(ProcessPad)
rcvr.start(sys.argv[1],int(sys.argv[2]))

View File

@@ -0,0 +1,112 @@
; This is the configuration for the 'pypad_xmpad.py' script for
; Rivendell, which can be used to output Now & Next data for an XM
; Satellite Radio channel.
;
; NOTE: The serial ports configured here have NOTHING TO DO with the
; ports configured in rdadmin(1)! These ports are used strictly by this
; script, and will not be usable by any other Rivendell component.
; Section Header
;
; One per serial device to be configured, starting with 'Serial1' and
; working up consecutively
[Serial1]
; Serial Device
;
; The device file that corresponds to the serial device.
;Device=/dev/ttyS0
Device=/dev/ttyUSB0
; Serial Baud Rate (in bps)
Speed=4800
; Parity (0=none, 1=even, 2=odd)
Parity=0
; Number of bits per data 'word'.
WordSize=8
; Program ID
; A unique integer value, assigned by XM
ProgramID=1000000005
; Format Strings. There is one for each line of PAD data (total=2).
; The string is output each time RDAirPlay changes
; play state, including any wildcards as placeholders for metadata values.
;
; The list of available wildcards can be found in the 'Metadata Wildcards'
; appendix in the Rivendell Operations Guide.
;
FormatString1=%t
FormatString2=%a
; Display Size. The maximum length of text to be sent for each line.
DisplaySize1=8
DisplaySize2=10
; Record Flag. Set to 'Yes' to allow recording, or 'No' to disable.
Recording=Yes
; Log Selection
;
; Set the status for each log to 'Yes', 'No' or 'Onair' to indicate whether
; state changes on that log should be output on this serial port. If set
; to 'Onair', then output will be generated only if RDAirPlay's OnAir flag
; is active.
MasterLog=Yes
Aux1Log=Yes
Aux2Log=Yes
VLog101=No
VLog102=No
VLog103=No
VLog104=No
VLog105=No
VLog106=No
VLog107=No
VLog108=No
VLog109=No
VLog110=No
VLog111=No
VLog112=No
VLog113=No
VLog114=No
VLog115=No
VLog116=No
VLog117=No
VLog118=No
VLog119=No
VLog120=No
; Additional serial ports can be configured by adding new sections...
;[Serial2]
;Device=/dev/ttyS1
;Speed=4800
;Parity=0
;WordSize=8
;FormatString1=%t
;FormatString2=%a
;MasterLog=Yes
;Aux1Log=No
;Aux2Log=Onair
;VLog101=No
;VLog102=No
;VLog103=No
;VLog104=No
;VLog105=No
;VLog106=No
;VLog107=No
;VLog108=No
;VLog109=No
;VLog110=No
;VLog111=No
;VLog112=No
;VLog113=No
;VLog114=No
;VLog115=No
;VLog116=No
;VLog117=No
;VLog118=No
;VLog119=No
;VLog120=No

184
apis/pypad/scripts/pypad_xmpad.py Executable file
View File

@@ -0,0 +1,184 @@
#!%PYTHON_BANGPATH%
# pypad_xmpad.py
#
# Write PAD updates to a Sirius/XM channel
#
# (C) Copyright 2018 Fred Gleason <fredg@paravelsystems.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
import sys
import syslog
import configparser
import serial
import pypad
XMPAD_DELIMITER='\02'
# XMPAD_DELIMITER='\x7c' #(|)
def eprint(*args,**kwargs):
print(*args,file=sys.stderr,**kwargs)
def MakeA4(update,section):
a4='A4'
a4+=XMPAD_DELIMITER+'1'
field1=update.resolvePadFields(update.config().get(section,'FormatString1'),
pypad.ESCAPE_NONE)
a4+=XMPAD_DELIMITER+field1[0:16]
a4+='\n'
return a4
def MakeA5(update,section):
a5='A5'
a5+=XMPAD_DELIMITER+'1'
field2=update.resolvePadFields(update.config().get(section,'FormatString2'),
pypad.ESCAPE_NONE)
a5+=XMPAD_DELIMITER+field2[0:16]
a5+='\n'
return a5
def MakeB4(update,section):
b4='B-4'
#
# Recording flag
#
recording='1'
if update.config().get(section,'Recording').lower()=='yes':
recording='0'
b4+=XMPAD_DELIMITER+recording
b4+=XMPAD_DELIMITER+'1'
#
# Event duration
#
b4+=XMPAD_DELIMITER+str(update.padField(pypad.TYPE_NOW,pypad.FIELD_LENGTH)//432)
b4+=XMPAD_DELIMITER+'0'
#
# Display field sizes
#
disp1size=update.config().get(section,'DisplaySize1')
mask1=''
for i in range(int(disp1size)):
mask1+='1'
disp2size=update.config().get(section,'DisplaySize2')
mask2=''
for i in range(int(disp2size)):
mask2+='1'
disp2size=update.config().get(section,'DisplaySize2')
b4+=XMPAD_DELIMITER+disp1size
b4+=XMPAD_DELIMITER+disp2size
b4+=XMPAD_DELIMITER+mask1
b4+=XMPAD_DELIMITER+mask2
#
# Display field values
#
field1=update.resolvePadFields(update.config().get(section,'FormatString1'),
pypad.ESCAPE_NONE)
field2=update.resolvePadFields(update.config().get(section,'FormatString2'),
pypad.ESCAPE_NONE)
b4+=XMPAD_DELIMITER+field2[0:16]
b4+=XMPAD_DELIMITER+mask1
b4+=XMPAD_DELIMITER+mask2
b4+=XMPAD_DELIMITER+field1[0:16]
#
# Program ID
#
b4+=XMPAD_DELIMITER+update.config().get(section,'ProgramID')
#
# EOM
#
b4+='\n'
return b4
def OpenSerialDevice(config,section):
devname=config.get(section,'Device')
speed=int(config.get(section,'Speed'))
parity=serial.PARITY_NONE
if int(config.get(section,'Parity'))==1:
parity=serial.PARITY_EVEN
if int(config.get(section,'Parity'))==2:
parity=serial.PARITY_ODD
bytesize=int(config.get(section,'WordSize'))
return serial.Serial(devname,speed,parity=parity,bytesize=bytesize)
def ProcessTimer(config):
n=1
try:
while(True):
section='Serial'+str(n)
dev=OpenSerialDevice(config,section)
dev.write('H0\n'.encode('utf-8'))
dev.close()
n=n+1
except configparser.NoSectionError:
return
def ProcessPad(update):
n=1
try:
while(True):
section='Serial'+str(n)
if update.shouldBeProcessed(section) and update.hasPadType(pypad.TYPE_NOW):
dev=OpenSerialDevice(update.config(),section)
b4=MakeB4(update,section)
a4=MakeA4(update,section)
a5=MakeA5(update,section)
dev.write(b4.encode('utf-8'))
dev.write(b4.encode('utf-8'))
dev.write(b4.encode('utf-8'))
dev.write(a4.encode('utf-8'))
dev.write(a5.encode('utf-8'))
dev.close()
n=n+1
except configparser.NoSectionError:
return
#
# 'Main' function
#
syslog.openlog(sys.argv[0].split('/')[-1])
rcvr=pypad.Receiver()
try:
rcvr.setConfigFile(sys.argv[3])
except IndexError:
eprint('pypad_xmpad.py: USAGE: cmd <hostname> <port> <config>')
sys.exit(1)
rcvr.setCallback(ProcessPad)
rcvr.setTimerCallback(30,ProcessTimer)
rcvr.start(sys.argv[1],int(sys.argv[2]))