Skip to content

Instantly share code, notes, and snippets.

@wd5gnr
Created February 11, 2025 15:34
Show Gist options
  • Save wd5gnr/62bce94b7097414d094d1e81bf46cfe2 to your computer and use it in GitHub Desktop.
Save wd5gnr/62bce94b7097414d094d1e81bf46cfe2 to your computer and use it in GitHub Desktop.
Simple Custom WPSD remote setup
# This wouldn't exist if/when this feature is pulled into mainstream
#!/bin/bash
DIR=/usr/local/sbin
FBASE=pistar-remote
echo Checking gnrcmd setup
if diff "$DIR/$FBASE" "$DIR/$FBASE.wd5gnr" >/dev/null
then
echo GNR is present
exit 0
fi
echo GNR not present
if diff "$DIR/$FBASE" "$DIR/$FBASE.original" >/dev/null
then
echo No changes detected on original
if ! diff "$DIR/$FBASE.wd5gnr" "$DIR/$FBASE.wd5gnr.backup" >/dev/null
then
echo Warning: Backup is not the same!
echo Resolve this before relinking
exit 1
fi
if [ "$1" == "fix" ]
then
echo Fixing
ln -sf "$DIR/$FBASE.wd5gnr" "$DIR/$FBASE"
sudo systemctl restart pistar-remote
exit 0
fi
echo issue: ln -sf \"$DIR/$FBASE.wd5gnr\" \"$DIR/$FBASE\"
echo then: systemctl restart pistar-remote
fi
exit 1

You need to do the following:

  1. Copy pistar-remote.wd5gnr to /usr/local/sbin/pistar-remote.wd5gnr AND /usr/local/sbin/pistar-remote.wd5gnr.backup (important!)
  2. Copy the files (remote-user-hook-example0, bm_tg.py, gnrcheck) for /usr/local/bin to your /usr/local/bin directory
  3. Edit (don't copy) your /etc/pistar-remote to look like the one below
  4. Copy and Edit /home/pi-star/user-hook-data to fit your needs
  5. Make all the files except user-hook-data executable (e.g., chmod +x gnrcheck)

Commands

Assuming you set the prefix to 77:

  • 7700002 - Deletes static talkgroup #2 (slot 1; as defined in user-hook-data
  • 7710002 - Adds static talkgroup #2 (slot 1)
  • 7720003 - Disables DMR network #3
  • 7730003 - Enables DMR network #3

Reinstalling

Every time WPSD updates, this script will end. Run gnrcheck to fix things. It will tell you if you need to fix it and then will show you the instructions to fix it. However, if you run gnrcheck fix it will try to fix things for you as long as the .wd5gnr and the .wd5gnr.backup files match.

TODO

Right now, this only works for DMR but it is easy to add the other modes which I will be doing shortly.

# NOTE: You only need to change what's in [enable] to suit your purpose
[banner]
# Pi-Star Remote config file
# This config file is desiged for the Pi-Star Keeper remote control
# The remote control system is designed to give repeater keepers an
# RF KillSwitch for their repeaters.
[enable]
# Is the Pi-Star Remote Enabled? (true|false)
enabled=true
local_prefix=77
local_script=/usr/local/bin/remote-user-hook-example0
scan_rate=15 # scan_rate is how many seconds between looking for commands (default was 30)
[keeper]
# Keepers Information
callsign=WD5GNR
[d-star]
# UR fields
#svckill=SVCKILL
#svcrestart=SVCRSTRT
#reboot=REBOOTPI
#shutdown=SHUTDOWN
#8Ball=8BALL
[dmr]
# TG commands
svckill=7999999
svcrestart=7999998
reboot=7999997
shutdown=7999996
gnrcmd=77
[ysf]
# ROOM commands
#svckill=99999
#svcrestart=99998
#reboot=99997
#shutdown=99996
[p25]
# P25 Talkgroups are limited to 1->65535
#svckill=65531
#svcrestart=65532
#reboot=65533
#shutdown=65534
[m17]
# M17 destination fields
#svckill=SVCKILL
#svcrestart=SVCRSTRT
#reboot=REBOOTPI
#shutdown=SHUTDOWN
#!/usr/bin/python
###########################################################################
# #
# WPSD RF Remote Control #
# #
# Refactored for Py3 - W0CHP #
# #
###########################################################################
# WD5GNR version
import datetime
import time
import linecache
import os
import subprocess
import configparser
import random
import re
# Read the config;
config = configparser.RawConfigParser()
config.read('/etc/pistar-remote')
# Read the MMDVMHost config;
mmdvmConfig = configparser.RawConfigParser()
mmdvmConfig.read('/etc/mmdvmhost')
# Read the YSFGateway config;
ysfGatewayConfig = configparser.RawConfigParser()
ysfGatewayConfig.read('/etc/ysfgateway')
# If not enabled, die;
isEnabled = config.get('enable', 'enabled')
if (isEnabled != 'true'):
quit()
# Substitute variables from config
mmdvmLogPath = mmdvmConfig.get('Log', 'FilePath')
mmdvmFileRoot = mmdvmConfig.get('Log', 'FileRoot')
ysfGatewayLogPath = ysfGatewayConfig.get('Log', 'FilePath')
ysfGatewayFileRoot = ysfGatewayConfig.get('Log', 'FileRoot')
keeperCall = config.get('keeper', 'callsign')
local_prefix=config.get('enable','local_prefix',fallback='999999999999')
local_script=config.get('enable','local_script',fallback='')
scan_rate=config.getint('enable','scan_rate',fallback=30)
# DMR Control Options
if config.has_option('dmr', 'svckill'):
dmrstop = config.get('dmr', 'svckill')
else:
dmrstop = str(999999999999)
if config.has_option('dmr', 'svcrestart'):
dmrrestart = config.get('dmr', 'svcrestart')
else:
dmrrestart = str(999999999999)
if config.has_option('dmr', 'reboot'):
dmrreboot = config.get('dmr', 'reboot')
else:
dmrreboot = str(999999999999)
if config.has_option('dmr', 'shutdown'):
dmrshutdown = config.get('dmr', 'shutdown')
else:
dmrshutdown = str(999999999999)
if config.has_option('dmr', 'hostfiles'):
dmrhostfiles = config.get('dmr', 'hostfiles')
else:
dmrhostfiles = str(999999999999)
if config.has_option('dmr', 'reconnect'):
dmrreconnect = config.get('dmr', 'reconnect')
else:
dmrreconnect = str(999999999999)
# D-Star Control Options
if config.has_option('d-star', 'svckill'):
dstarstop = config.get('d-star', 'svckill')
else:
dstarstop = str(999999999999)
if config.has_option('d-star', 'svcrestart'):
dstarrestart = config.get('d-star', 'svcrestart')
else:
dstarrestart = str(999999999999)
if config.has_option('d-star', 'reboot'):
dstarreboot = config.get('d-star', 'reboot')
else:
dstarreboot = str(999999999999)
if config.has_option('d-star', 'shutdown'):
dstarshutdown = config.get('d-star', 'shutdown')
else:
dstarshutdown = str(999999999999)
if config.has_option('d-star', 'hostfiles'):
dstarhostfiles = config.get('d-star', 'hostfiles')
else:
dstarhostfiles = str(999999999999)
if config.has_option('d-star', 'getip'):
dstargetip = config.get('d-star', 'getip')
else:
dstargetip = str(999999999999)
if config.has_option('d-star', 'wifissid'):
dstargetwifissid = config.get('d-star', 'wifissid')
else:
dstargetwifissid = str(999999999999)
if config.has_option('d-star', 'wifirssi'):
dstargetwifirssi = config.get('d-star', 'wifirssi')
else:
dstargetwifirssi = str(999999999999)
if config.has_option('d-star', '8Ball'):
dstar8ball = config.get('d-star', '8Ball')
else:
dstar8ball = str(999999999999)
dstarmodule = mmdvmConfig.get('General', 'Callsign').ljust(
7) + mmdvmConfig.get('D-Star', 'Module')
# YSF Control Options
if config.has_option('ysf', 'svckill'):
ysfstop = config.get('ysf', 'svckill')
else:
ysfstop = str(999999999999)
if config.has_option('ysf', 'svcrestart'):
ysfrestart = config.get('ysf', 'svcrestart')
else:
ysfrestart = str(999999999999)
if config.has_option('ysf', 'reboot'):
ysfreboot = config.get('ysf', 'reboot')
else:
ysfreboot = str(999999999999)
if config.has_option('ysf', 'shutdown'):
ysfshutdown = config.get('ysf', 'shutdown')
else:
ysfshutdown = str(999999999999)
if config.has_option('ysf', 'hostfiles'):
ysfhostfiles = config.get('ysf', 'hostfiles')
else:
ysfhostfiles = str(999999999999)
# P25 Control Options
if config.has_option('p25', 'svckill'):
p25stop = config.get('p25', 'svckill')
else:
p25stop = str(999999999999)
if config.has_option('p25', 'svcrestart'):
p25restart = config.get('p25', 'svcrestart')
else:
p25restart = str(999999999999)
if config.has_option('p25', 'reboot'):
p25reboot = config.get('p25', 'reboot')
else:
p25reboot = str(999999999999)
if config.has_option('p25', 'shutdown'):
p25shutdown = config.get('p25', 'shutdown')
else:
p25shutdown = str(999999999999)
if config.has_option('p25', 'hostfiles'):
p25hostfiles = config.get('p25', 'hostfiles')
else:
p25hostfiles = str(999999999999)
# M17 Control Options
if config.has_option('m17', 'svckill'):
m17stop = config.get('m17', 'svckill')
else:
m17stop = str(999999999999)
if config.has_option('m17', 'svcrestart'):
m17restart = config.get('m17', 'svcrestart')
else:
m17restart = str(999999999999)
if config.has_option('m17', 'reboot'):
m17reboot = config.get('m17', 'reboot')
else:
m17reboot = str(999999999999)
if config.has_option('m17', 'shutdown'):
m17shutdown = config.get('m17', 'shutdown')
else:
m17shutdown = str(999999999999)
if config.has_option('m17', 'hostfiles'):
m17hostfiles = config.get('m17', 'hostfiles')
else:
m17hostfiles = str(999999999999)
# 8-Ball answers
magic8ball = [
'It is certain',
'It is decidedly so',
'Without a doubt',
'Yes definitely',
'You may rely on it',
'As I see it, yes',
'Most likely',
'Outlook good',
'Yes',
'Signs point to yes',
'Reply hazy try agn',
'Ask again later',
'Tell you later',
'Cannot predict now',
'Concentrate, ask agn',
'Dont count on it',
'My reply is no',
'My sources say no',
'Outlook not so good',
'Very doubtful'
]
# Some Variables that are important later
txtTransmitOldBin = '/usr/local/bin/texttransmit'
txtTransmitNewBin = '/usr/local/bin/texttransmitd'
if os.path.isfile(txtTransmitOldBin):
txtTransmitBin = '/usr/local/bin/texttransmit'
if os.path.isfile(txtTransmitNewBin):
txtTransmitBin = '/usr/local/bin/texttransmitd'
# Now run the loop
while True:
# Check that the process is running, if its not there is no point in trying to stop it.
checkproc = subprocess.Popen(
'pgrep' + ' MMDVMHost', shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
if checkproc.stdout.readlines():
# This is the main loop that keeps waiting, we dont want to hammer the logs too often, every 30 secs should be enough.
utcnow = datetime.datetime.utcnow()
datenow = utcnow.strftime('%Y-%m-%d')
dateminus60sec = datetime.datetime.utcnow() - datetime.timedelta(seconds=scan_rate)
logstampnow = utcnow.strftime('%Y-%m-%d %H:%M:%S')
logstampnowminus60sec = dateminus60sec.strftime('%Y-%m-%d %H:%M:%S')
currentLog = mmdvmLogPath + '/' + mmdvmFileRoot + '-' + datenow + '.log'
currentLogYSF = ysfGatewayLogPath + '/' + \
ysfGatewayFileRoot + '-' + datenow + '.log'
loglist = []
# Open the MMDVMHost Log
if os.path.isfile(currentLog):
logfile = open(currentLog, 'r')
loglist = logfile.readlines()
logfile.close()
# Parse the log lines
for line in loglist:
# We only care about logs in the last 60 secs
if line[3:22] >= logstampnowminus60sec and line[3:22] <= logstampnow:
dmrlocalfound= re.search('received RF voice header from ' + keeperCall + ' to ' + local_prefix + '([0-9])([0-9][0-9][0-9][0-9])',line)
if dmrlocalfound:
localverb=dmrlocalfound.group(1)
localid=dmrlocalfound.group(2)
os.system(f'{local_script} dmr "{localverb}" "{localid}"')
# DMR Stop MMDVMHost
# M: 2017-07-03 09:39:38.208 DMR Slot 2, received RF voice header from M1ABC to 123456
if str('received RF voice header from ' + keeperCall + ' to ' + dmrstop) in line:
# Kill the Services
os.system(r'systemctl stop mmdvmhost.timer')
os.system(r'systemctl stop pistar-watchdog.timer')
os.system(r'systemctl stop mmdvmhost.service')
os.system(r'systemctl stop pistar-watchdog.service')
# DMR Restart MMDVMHost
# M: 2017-07-03 09:39:38.208 DMR Slot 2, received RF voice header from M1ABC to 123456
if str('received RF voice header from ' + keeperCall + ' to ' + dmrrestart) in line:
# Restart the Services
os.system(r'systemctl restart mmdvmhost.service')
os.system(r'systemctl restart dmrgateway.service')
time.sleep(30)
# DMR Restart the OS
# M: 2017-07-03 09:39:38.208 DMR Slot 2, received RF voice header from M1ABC to 123456
if str('received RF voice header from ' + keeperCall + ' to ' + dmrreboot) in line:
# Restart the OS
os.system(r'shutdown -r now')
# DMR Shutdown the OS
# M: 2017-07-03 09:39:38.208 DMR Slot 2, received RF voice header from M1ABC to 123456
if str('received RF voice header from ' + keeperCall + ' to ' + dmrshutdown) in line:
# Shutdown the OS
os.system(r'shutdown -h now')
# DMR Update Hostfiles
# M: 2017-07-03 09:39:38.208 DMR Slot 2, received RF voice header from M1ABC to 123456
if str('received RF voice header from ' + keeperCall + ' to ' + dmrhostfiles) in line:
# Update Host files
os.system(r'/usr/local/sbin/wpsd-hostfile-update')
# DMR Reconnect WIFI
# M: 2017-07-03 09:39:38.208 DMR Slot 2, received RF voice header from M1ABC to 123456
if str('received RF voice header from ' + keeperCall + ' to ' + dmrreconnect) in line:
# trigger reconnect
os.system(
r'logger -t "[$$]" "Pi-Star --> Wifi Reconnect initiated <--"')
os.system(r'wpa_cli reconfigure wlan0')
os.system(r'ifdown wlan0 && sleep 3')
os.system(r'ifup wlan0')
os.system(r'wpa_cli scan')
# P25 Stop MMDVMHost
# M: 2017-08-08 08:02:43.352 P25, received RF transmission from M1ABC to TG 123456
if str('P25, received RF transmission from ' + keeperCall + ' to TG ' + p25stop) in line:
# Kill the Services
os.system(r'systemctl stop mmdvmhost.timer')
os.system(r'systemctl stop pistar-watchdog.timer')
os.system(r'systemctl stop mmdvmhost.service')
os.system(r'systemctl stop pistar-watchdog.service')
# P25 Restart MMDVMHost
# M: 2017-08-08 08:02:43.352 P25, received RF transmission from M1ABC to TG 123456
if str('P25, received RF transmission from ' + keeperCall + ' to TG ' + p25restart) in line:
# Restart the Services
os.system(r'systemctl restart mmdvmhost.service')
os.system(r'systemctl restart dmrgateway.service')
time.sleep(30)
# P25 Restart the OS
# M: 2017-08-08 08:02:43.352 P25, received RF transmission from M1ABC to TG 123456
if str('P25, received RF transmission from ' + keeperCall + ' to TG ' + p25reboot) in line:
# Restart the OS
os.system(r'shutdown -r now')
# P25 Shutdown the OS
# M: 2017-08-08 08:02:43.352 P25, received RF transmission from M1ABC to TG 123456
if str('P25, received RF transmission from ' + keeperCall + ' to TG ' + p25shutdown) in line:
# Shutdown the OS
os.system(r'shutdown -h now')
# P25 Update Hostfiles
# M: 2017-08-08 08:02:43.352 P25, received RF transmission from M1ABC to TG 123456
if str('P25, received RF transmission from ' + keeperCall + ' to TG ' + p25hostfiles) in line:
# Update Host Files
os.system(r'/usr/local/sbin/wpsd-hostfile-update')
# D-Star Stop MMDVMHost
# M: 2017-07-03 11:38:57.349 D-Star, received RF header from M1ABC /1234 to COMMAND1
if str('D-Star, received RF header from ' + keeperCall + ' ') and str(dstarstop) in line:
# Kill the Services
os.system(
txtTransmitBin + ' -text "Shutting down at keeper request" "' + dstarmodule + '"')
time.sleep(5)
os.system(r'systemctl stop mmdvmhost.timer')
os.system(r'systemctl stop pistar-watchdog.timer')
os.system(r'systemctl stop mmdvmhost.service')
os.system(r'systemctl stop pistar-watchdog.service')
# D-Star Restart MMDVMHost
# M: 2017-07-03 11:38:57.349 D-Star, received RF header from M1ABC /1234 to COMMAND1
if str('D-Star, received RF header from ' + keeperCall + ' ') and str(dstarrestart) in line:
# Restart the Services
os.system(
txtTransmitBin + ' -text "Restarting services at keeper request" "' + dstarmodule + '"')
time.sleep(5)
os.system(r'systemctl restart ircddbgateway.service')
os.system(r'systemctl restart mmdvmhost.service')
time.sleep(30)
# D-Star Reboot the OS
# M: 2017-07-03 11:38:57.349 D-Star, received RF header from M1ABC /1234 to COMMAND1
if str('D-Star, received RF header from ' + keeperCall + ' ') and str(dstarreboot) in line:
# Kill the Services
os.system(
txtTransmitBin + ' -text "Rebooting OS at keeper request" "' + dstarmodule + '"')
time.sleep(5)
os.system(r'shutdown -r now')
# D-Star Shutdown the OS
# M: 2017-07-03 11:38:57.349 D-Star, received RF header from M1ABC /1234 to COMMAND1
if str('D-Star, received RF header from ' + keeperCall + ' ') and str(dstarshutdown) in line:
# Shutdown
os.system(
txtTransmitBin + ' -text "Shutting down OS at keeper request" "' + dstarmodule + '"')
time.sleep(5)
os.system(r'shutdown -h now')
# D-Star Hostfiles Update
# M: 2017-07-03 11:38:57.349 D-Star, received RF header from M1ABC /1234 to COMMAND1
if str('D-Star, received RF header from ' + keeperCall + ' ') and str(dstarhostfiles) in line:
# Update Host files
os.system(
txtTransmitBin + ' -text "Updating Hostfiles at keeper request" "' + dstarmodule + '"')
time.sleep(5)
os.system(r'/usr/local/sbin/wpsd-hostfile-update')
# D-Star Get the current IP
# M: 2017-07-03 11:38:57.349 D-Star, received RF header from M1ABC /1234 to COMMAND1
if str('D-Star, received RF header from ' + keeperCall + ' ') and str(dstargetip) in line:
# Get the IP
myIP = os.popen(
'/bin/hostname -I | awk \'{print $1}\'').read()
os.system(txtTransmitBin + ' -text "IP: ' +
myIP + '" "' + dstarmodule + '"')
# D-Star Get the wifi SSID
# M: 2017-07-03 11:38:57.349 D-Star, received RF header from M1ABC /1234 to COMMAND1
if str('D-Star, received RF header from ' + keeperCall + ' ') and str(dstargetwifissid) in line:
# Get the SSID
if os.path.islink('/sys/class/net/wlan0'):
ssid = os.popen(
'/sbin/iwconfig wlan0 | grep ESSID | awk -F ":" \'{print $2}\' | sed \'s/"//g\'').read()
os.system(txtTransmitBin + ' -text "SSID: ' +
ssid + '" "' + dstarmodule + '"')
else:
os.system(
txtTransmitBin + ' -text "WiFi: Not connected" "' + dstarmodule + '"')
# D-Star Get the wifi RSSI
# M: 2017-07-03 11:38:57.349 D-Star, received RF header from M1ABC /1234 to COMMAND1
if str('D-Star, received RF header from ' + keeperCall + ' ') and str(dstargetwifirssi) in line:
# Get the SSID
if os.path.islink('/sys/class/net/wlan0'):
rssi = os.popen(
'/sbin/iwconfig wlan0 | grep -i "signal level" | awk -F "=" \'{print $3}\'').read()
os.system(txtTransmitBin + ' -text "RSSI: ' +
rssi + '" "' + dstarmodule + '"')
else:
os.system(
txtTransmitBin + ' -text "WiFi: Not connected" "' + dstarmodule + '"')
# D-Star 8Ball
# M: 2017-07-03 11:38:57.349 D-Star, received RF header from M1ABC /1234 to COMMAND1
if str('D-Star, received RF header from ' + keeperCall + ' ') and str(dstar8ball) in line:
# Ask the 8Ball
magic8ballanswer = random.choice(magic8ball)
os.system(txtTransmitBin + ' -text "' +
magic8ballanswer + '" "' + dstarmodule + '"')
# YSF
# M: 2017-07-13 19:50:43.464 YSF, received RF header from M1ABC to ALL
if str('YSF, received RF header from ' + keeperCall + ' ') in line:
# YSF Log Checking here
logfileysf = open(currentLogYSF, 'r')
loglistysf = logfileysf.readlines()
logfileysf.close()
checkproc = subprocess.Popen(
'pgrep' + ' YSFGateway', shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
if checkproc.stdout.readlines():
# Parse the log lines
for lineysf in loglistysf:
# We only care about logs in the last 60 secs
if lineysf[3:22] >= logstampnowminus60sec and lineysf[3:22] <= logstampnow:
# YSF Stop MMDVMHost
# M: 2017-07-13 19:24:59.704 Trying to find non existent reflector with an id of 12345
if (str('Trying to find non existent') and str(ysfstop) in lineysf) or (str('Received Connect to') and str(ysfstop) in lineysf):
# Kill the Services
os.system(
r'systemctl stop mmdvmhost.timer')
os.system(
r'systemctl stop pistar-watchdog.timer')
os.system(
r'systemctl stop mmdvmhost.service')
os.system(
r'systemctl stop pistar-watchdog.service')
# YSF Restart MMDVMHost
# M: 2017-07-13 19:24:59.704 Trying to find non existent reflector with an id of 12345
if (str('Trying to find non existent') and str(ysfrestart) in lineysf) or (str('Received Connect to') and str(ysfrestart) in lineysf):
# Restart the Services
os.system(
r'systemctl restart ysfgateway.service')
os.system(
r'systemctl restart mmdvmhost.service')
time.sleep(30)
# YSF Reboot the OS
# M: 2017-07-13 19:24:59.704 Trying to find non existent reflector with an id of 12345
if (str('Trying to find non existent') and str(ysfreboot) in lineysf) or (str('Received Connect to') and str(ysfreboot) in lineysf):
# Reboot the OS
os.system(r'shutdown -r now')
# YSF Shutdown the OS
# M: 2017-07-13 19:24:59.704 Trying to find non existent reflector with an id of 12345
if (str('Trying to find non existent') and str(ysfshutdown) in lineysf) or (str('Received Connect to') and str(ysfshutdown) in lineysf):
# Shutdown the OS
os.system(r'shutdown -h now')
# YSF Update the Hostfiles
# M: 2017-07-13 19:24:59.704 Trying to find non existent reflector with an id of 12345
if (str('Trying to find non existent') and str(ysfhostfiles) in lineysf) or (str('Received Connect to') and str(ysfhostfiles) in lineysf):
# Update the host files
os.system(
r'/usr/local/sbin/wpsd-hostfile-update')
# M17 Stop MMDVMHost
# M: 2025-01-25 15:51:20.147 M17, received RF voice transmission from N1ADJ to SVCKILL
if str('M17, received RF voice transmission from ' + keeperCall + ' to ' + m17stop) in line:
# Kill the Services
os.system(r'systemctl stop mmdvmhost.timer')
os.system(r'systemctl stop pistar-watchdog.timer')
os.system(r'systemctl stop mmdvmhost.service')
os.system(r'systemctl stop pistar-watchdog.service')
# M17 Restart MMDVMHost
# M: 2025-01-25 15:51:20.147 M17, received RF voice transmission from N1ADJ to SVCKILL
if str('M17, received RF voice transmission from ' + keeperCall + ' to ' + m17restart) in line:
# Restart the Services
os.system(r'systemctl restart mmdvmhost.service')
os.system(r'systemctl restart m17gateway.service')
time.sleep(30)
# M17 Restart the OS
# M: 2025-01-25 15:51:20.147 M17, received RF voice transmission from N1ADJ to SVCKILL
if str('M17, received RF voice transmission from ' + keeperCall + ' to ' + m17reboot) in line:
# Restart the OS
os.system(r'shutdown -r now')
# M17 Shutdown the OS
# M: 2025-01-25 15:51:20.147 M17, received RF voice transmission from N1ADJ to SVCKILL
if str('M17, received RF voice transmission from ' + keeperCall + ' to ' + m17shutdown) in line:
# Shutdown the OS
os.system(r'shutdown -h now')
# M17 Update Hostfiles
# M: 2025-01-25 15:51:20.147 M17, received RF voice transmission from N1ADJ to SVCKILL
if str('M17, received RF voice transmission from ' + keeperCall + ' to ' + m17hostfiles) in line:
# Update Host Files
os.system(r'/usr/local/sbin/wpsd-hostfile-update')
# This is the 30 second sleep before the next pass.
time.sleep(scan_rate)
#!/bin/bash
# example remote user hook de WD5GNR
echo Loading user data
. /home/pi-star/user-hook-data
# Here is an example of the user-hook-data file
#
#
#DMRID=XXXXXXXX
#TG0001=3001
#TG0002=312681
#TG9999=93
# ignore everything but dmr
if [ "$1" != "dmr" ]
then
exit 0
fi
# we can pull your BMAPI key
bmkey=$(grep apikey= /etc/bmapi.key | cut -d = -f 2)
# read the verb (0-9) and the noun/argument (4 digits)
VERB="$2"
ARG="TG$3"
N=$(echo $3 | cut -c4 )
TG="${!ARG}"
echo $VERB " " $ARG
if [ "$VERB" == "0" ]
then
python /usr/local/bin/bm_tg.py rmtg "$TG" 1 "$DMRID" "$bmkey"
echo Removed $TG
exit 0
fi
if [ "$VERB" == "1" ]
then
python /usr/local/bin/bm_tg.py addtg "$TG" 1 "$DMRID" "$bmkey"
echo Added $TG
exit 0
fi
if [ "$VERB" == 2 ]
then
RemoteCommand 7643 disable net$N
exit 0
fi
if [ "$VERB" == 3 ]
then
RemoteCommand 7643 enable net$N
exit 0
fi
exit 1
#!/usr/bin/env python3
# de WD5GNR
# This script reads a verb (command), noun (tg #), your ESSID, and your BM API Key
# It will add a static TG on timeslot 1 (verb=1 or verb=addtg)
# It will remove a static TG on timeslot 1 (verb=0 or verb=rmtg)
# exit code 0=ok, 1=failed, 2=bad verb, 3=wrong number of arguments
import sys
import requests
def manage_talkgroup(verb, noun, slot, hotspot_id, bmkey):
base_url = f'https://api.brandmeister.network/v2/device/{hotspot_id}/talkgroup'
headers = {'Authorization': f'Bearer {bmkey}'}
# add talkgrou
if verb == '1' or verb == 'addtg':
# Add static talkgroup to timeslot 1
payload = {'group': int(noun), 'slot': int(slot)}
response = requests.post(base_url, json=payload, headers=headers)
if response.status_code == 200:
print(f'Successfully added talkgroup {noun} to timeslot {slot}.')
return 0
else:
print(f'Failed to add talkgroup {noun}. Status code: {response.status_code}')
return 1
elif verb == '0' or verb == 'rmtg':
# Remove static talkgroup
delete_url = f'{base_url}/{slot}/{noun}'
response = requests.delete(delete_url, headers=headers)
if response.status_code == 200:
print(f'Successfully removed talkgroup {noun}.')
return 0
else:
print(f'Failed to remove talkgroup {noun}. Status code: {response.status_code}')
return 1
else:
print('Invalid verb. Use 0 to add or 1 to remove a talkgroup.')
return 2
if __name__ == '__main__':
if len(sys.argv) != 6:
print('Usage: bm_tg <verb> <noun> <slot> <node> <key>')
print('verb: 0 to add a talkgroup, 1 to remove a talkgroup')
print('noun: ID of the talkgroup')
print('slot: The slot (if needed)')
print('node: ID of the repeater')
print('key: BM API Key')
exit(3)
else:
verb = sys.argv[1]
noun = sys.argv[2]
slot = sys.argv[3]
id = sys.argv[4]
apikey = sys.argv[5]
rc=manage_talkgroup(verb, noun, slot, id, apikey)
exit(rc)
# Sample user-hook-data file for remote-user-hook-example0
# We need your DMR ID
DMRID=123456701 # hard to pull from /etc/dmrgateway
# List of 4 digit TGs to map to real TGs
TG0001=3001
TG0002=312681
TG9999=93
# order doesn't matter, but you do need 4 digits (e.g., don't do TG2=...)
# If your user prefix is 77 then
# 7710002 will activate 312681 as a static group
# 7700002 will deactivate 312681
# 7730001 will activate DMR network #1 (as in WPSD admin dashboard)
# 7720001 will deactivate DMR network #1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment