mirror of
https://github.com/Schrolli91/BOSWatch.git
synced 2026-01-20 15:20:16 +01:00
Merge branch 'develop' into patch-1
This commit is contained in:
commit
0ab896b17a
|
|
@ -49,7 +49,7 @@ doubleFilter_ignore_time = 5
|
|||
doubleFilter_check_msg = 0
|
||||
|
||||
# writes the multimon-ng raw data stream into a text file named mm_raw.txt
|
||||
writeMultimonRaw = 1
|
||||
writeMultimonRaw = 0
|
||||
|
||||
[NMAHandler]
|
||||
# you can use a logging handler for sending logging records to NotifyMyAndroid
|
||||
|
|
@ -169,6 +169,8 @@ tableSIG = bos_signal
|
|||
[httpRequest]
|
||||
# example URL http://example.com/remote.php?DESCR=%DESCR%
|
||||
|
||||
# multiple URLs can be separated by comma
|
||||
|
||||
# you can use the following wildcards in your URL as GET params:
|
||||
# http://en.wikipedia.org/wiki/Query_string
|
||||
|
||||
|
|
@ -413,13 +415,46 @@ RICforLocationAPIKey =
|
|||
# Required if you want to create a map based on location information received with the above RIC.
|
||||
GoogleAPIKey =
|
||||
|
||||
# %FMS% = FMS Code
|
||||
# %STATUS% = FMS Status
|
||||
# %DIR% = Direction of the telegram (0/1)
|
||||
# %DIRT% = Direction of the telegram (Text-String)
|
||||
# %TSI% = Tactical Short Information (I-IV)
|
||||
# %DESCR% = Description, if description-module is used
|
||||
# %DATE% = Date (by script)
|
||||
# %TIME% = Time (by script)
|
||||
# %LPAR% = (
|
||||
# %RPAR% = )
|
||||
FMS_message = %DATE% %TIME%: %FMS%
|
||||
|
||||
# %ZVEI% = ZVEI 5-tone Code
|
||||
# %DESCR% = Description, if description-module is used
|
||||
# %DATE% = Date (by script)
|
||||
# %TIME% = Time (by script)
|
||||
# %LPAR% = (
|
||||
# %RPAR% = )
|
||||
ZVEI_message = %DATE% %TIME%: %ZVEI%
|
||||
|
||||
# %RIC% = POCSAG RIC
|
||||
# %FUNC% = POCSAG function/Subric (1-4)
|
||||
# %FUNCCHAR% = POCSAG function/Subric als character (a-d)
|
||||
# %MSG% = Message of the POCSAG telegram
|
||||
# %BITRATE% = Bitrate of the POCSAG telegram
|
||||
# %DESCR% = Description, if description-module is used
|
||||
# %DATE% = Date (by script)
|
||||
# %TIME% = Time (by script)
|
||||
# %LPAR% = (
|
||||
# %RPAR% = )
|
||||
POC_message = %MSG%
|
||||
|
||||
|
||||
[yowsup]
|
||||
# number or chat-number who whants to become the news
|
||||
empfaenger =
|
||||
empfaenger =
|
||||
# WhatsApp-number of that the news comes
|
||||
sender =
|
||||
sender =
|
||||
# password from this number
|
||||
password=
|
||||
password=
|
||||
|
||||
# %FMS% = FMS Code
|
||||
# %STATUS% = FMS Status
|
||||
|
|
|
|||
|
Can't render this file because it has a wrong number of fields in line 2.
|
|
Can't render this file because it has a wrong number of fields in line 2.
|
|
Can't render this file because it has a wrong number of fields in line 2.
|
|
Can't render this file because it has a wrong number of fields in line 2.
|
|
|
@ -126,7 +126,7 @@ def decode(freq, decoded):
|
|||
# If enabled, look up description
|
||||
if globalVars.config.getint("POC", "idDescribed"):
|
||||
from includes import descriptionList
|
||||
data["description"] = descriptionList.getDescription("POC", poc_id)
|
||||
data["description"] = descriptionList.getDescription("POC", poc_id+data["functionChar"])
|
||||
# processing the alarm
|
||||
try:
|
||||
from includes import alarmHandler
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ Function to expand the dataset with a description.
|
|||
|
||||
import logging # Global logger
|
||||
import csv # for loading the description files
|
||||
import re # for matching IDs with a regular expression
|
||||
|
||||
from includes import globalVars # Global variables
|
||||
from includes.helper import stringConverter
|
||||
|
|
@ -41,10 +42,10 @@ def loadCSV(typ, idField):
|
|||
reader = csv.DictReader(csvfile)
|
||||
for row in reader:
|
||||
logging.debug(row)
|
||||
# only import rows with an integer as id
|
||||
if row[idField].isdigit() == True:
|
||||
# only import rows with an integer as id, allow subrics though
|
||||
if re.match("^[0-9]+[A-D]?$", row[idField], re.IGNORECASE):
|
||||
try:
|
||||
resultList[row[idField]] = stringConverter.convertToUTF8(row['description'])
|
||||
resultList[row[idField].lower()] = stringConverter.convertToUTF8(row['description'])
|
||||
except:
|
||||
# skip entry in case of an exception
|
||||
pass
|
||||
|
|
@ -112,7 +113,8 @@ def getDescription(typ, data):
|
|||
resultStr = zveiDescribtionList[data]
|
||||
elif typ == "POC":
|
||||
global ricDescribtionList
|
||||
resultStr = ricDescribtionList[data]
|
||||
resultStr = ricDescribtionList[data[:-1]] # MainRIC
|
||||
resultStr += " " + ricDescribtionList[data] # SubRIC
|
||||
else:
|
||||
logging.warning("Invalid Typ: %s", typ)
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ Global variables
|
|||
"""
|
||||
|
||||
# version info
|
||||
versionNr = "2.2"
|
||||
versionNr = "2.2-dev"
|
||||
buildDate = "2017/03/30"
|
||||
|
||||
# Global variables
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ from includes import globalVars
|
|||
from includes.helper import timeHandler
|
||||
|
||||
|
||||
def replaceWildcards(text, data, lineBrakeAllowed=False):
|
||||
def replaceWildcards(text, data):
|
||||
"""
|
||||
Replace all official Wildcards with the Information from the data[] var
|
||||
|
||||
|
|
@ -25,8 +25,6 @@ def replaceWildcards(text, data, lineBrakeAllowed=False):
|
|||
@param text: Input text with wildcards
|
||||
@type data: map
|
||||
@param data: map of data (structure see readme.md in plugin folder)
|
||||
@type lineBrakeAllowed: Boolean
|
||||
@param lineBrakeAllowed: switch to allow lineBreak (%BR%) as wildcard
|
||||
|
||||
@return: text with replaced wildcards
|
||||
@exception: Exception if Error at replace
|
||||
|
|
@ -36,8 +34,7 @@ def replaceWildcards(text, data, lineBrakeAllowed=False):
|
|||
text = text.replace("%TIME%", timeHandler.getTime(data["timestamp"])).replace("%DATE%", timeHandler.getDate(data["timestamp"]))
|
||||
|
||||
# replace some special chars
|
||||
if lineBrakeAllowed == True:
|
||||
text = text.replace("%BR%", "\r\n")
|
||||
text = text.replace("%BR%", "\r\n")
|
||||
text = text.replace("%LPAR%", "(")
|
||||
text = text.replace("%RPAR%", ")")
|
||||
|
||||
|
|
|
|||
|
|
@ -111,9 +111,9 @@ def run(typ,freq,data):
|
|||
|
||||
elif typ == "POC":
|
||||
if isSignal(data["ric"]):
|
||||
cursor.execute("UPDATE "+globalVars.config.get("MySQL","tableSIG")+" SET time = NOW() WHERE ric = "+data["ric"])
|
||||
cursor.execute("UPDATE "+globalVars.config.get("MySQL","tableSIG")+" SET time = NOW() WHERE ric = '"+data["ric"]+"';")
|
||||
if cursor.rowcount == 0:
|
||||
cursor.execute("INSERT INTO "+globalVars.config.get("MySQL","tableSIG")+" (time,ric) VALUES (NOW(),"+data["ric"]+")")
|
||||
cursor.execute("INSERT INTO "+globalVars.config.get("MySQL","tableSIG")+" (time,ric) VALUES (NOW(), '"+data["ric"]+"');")
|
||||
else:
|
||||
cursor.execute("INSERT INTO "+globalVars.config.get("MySQL","tablePOC")+" (time, ric, function, functionChar, msg, bitrate, description) VALUES (FROM_UNIXTIME(%s),%s,%s,%s,%s,%s,%s)", (data["timestamp"], data["ric"], data["function"], data["functionChar"], data["msg"], data["bitrate"], data["description"]))
|
||||
|
||||
|
|
|
|||
|
|
@ -104,44 +104,6 @@ CREATE TABLE IF NOT EXISTS `bos_signal` (
|
|||
PRIMARY KEY (`ID`)
|
||||
) ENGINE=MYISAM DEFAULT CHARSET=UTF8 AUTO_INCREMENT=1;
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- Schedule für Tabelle `bos_pocsag`
|
||||
--
|
||||
CREATE EVENT IF NOT EXISTS `Delete POCSAG Entries > 3 Months`
|
||||
ON SCHEDULE EVERY 1 DAY
|
||||
STARTS '2016-01-01 00:00:00'
|
||||
ON COMPLETION PRESERVE ENABLE
|
||||
DO
|
||||
DELETE FROM bos_pocsag WHERE time < DATE_SUB(NOW(),INTERVAL 3 MONTH);
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- Schedule für Tabelle `bos_fms`
|
||||
--
|
||||
|
||||
CREATE EVENT IF NOT EXISTS `Delete FMS Entries > 3 Months`
|
||||
ON SCHEDULE EVERY 1 DAY
|
||||
STARTS '2016-01-01 00:00:00'
|
||||
ON COMPLETION PRESERVE ENABLE
|
||||
DO
|
||||
DELETE FROM bos_fms WHERE time < DATE_SUB(NOW(),INTERVAL 3 MONTH);
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- Schedule für Tabelle `bos_zvei`
|
||||
--
|
||||
|
||||
CREATE EVENT IF NOT EXISTS `Delete ZVEI Entries > 3 Months`
|
||||
ON SCHEDULE EVERY 1 DAY
|
||||
STARTS '2016-01-01 00:00:00'
|
||||
ON COMPLETION PRESERVE ENABLE
|
||||
DO
|
||||
DELETE FROM bos_zvei WHERE time < DATE_SUB(NOW(),INTERVAL 3 MONTH);
|
||||
|
||||
|
||||
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
|
||||
|
|
|
|||
40
plugins/MySQL/boswatch_cleanup.sql
Normal file
40
plugins/MySQL/boswatch_cleanup.sql
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
-- Cleanup-routines for boswatch-tables
|
||||
|
||||
use boswatch;
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- Schedule für Tabelle `bos_pocsag`
|
||||
--
|
||||
CREATE EVENT IF NOT EXISTS `Delete POCSAG Entries > 3 Months`
|
||||
ON SCHEDULE EVERY 1 DAY
|
||||
STARTS '2016-01-01 00:00:00'
|
||||
ON COMPLETION PRESERVE ENABLE
|
||||
DO
|
||||
DELETE FROM bos_pocsag WHERE time < DATE_SUB(NOW(),INTERVAL 3 MONTH);
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- Schedule für Tabelle `bos_fms`
|
||||
--
|
||||
|
||||
CREATE EVENT IF NOT EXISTS `Delete FMS Entries > 3 Months`
|
||||
ON SCHEDULE EVERY 1 DAY
|
||||
STARTS '2016-01-01 00:00:00'
|
||||
ON COMPLETION PRESERVE ENABLE
|
||||
DO
|
||||
DELETE FROM bos_fms WHERE time < DATE_SUB(NOW(),INTERVAL 3 MONTH);
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- Schedule für Tabelle `bos_zvei`
|
||||
--
|
||||
|
||||
CREATE EVENT IF NOT EXISTS `Delete ZVEI Entries > 3 Months`
|
||||
ON SCHEDULE EVERY 1 DAY
|
||||
STARTS '2016-01-01 00:00:00'
|
||||
ON COMPLETION PRESERVE ENABLE
|
||||
DO
|
||||
DELETE FROM bos_zvei WHERE time < DATE_SUB(NOW(),INTERVAL 3 MONTH);
|
||||
|
|
@ -58,6 +58,20 @@ def run(typ,freq,data):
|
|||
"""
|
||||
try:
|
||||
if configHandler.checkConfig("Sms77"): #read and debug the config
|
||||
|
||||
# create an empty message an fill it with the required information
|
||||
message = "Alarm"
|
||||
if typ == "FMS":
|
||||
logging.debug("FMS detected, building message")
|
||||
message = data["description"]+"<br>"+data["status"]
|
||||
elif typ == "ZVEI":
|
||||
logging.debug("ZVEI detected, building message")
|
||||
message = data["zvei"]+" - "+data["description"]
|
||||
elif typ == "POC":
|
||||
logging.debug("POC detected, building message")
|
||||
message = data["description"]+"<br>"+data["msg"].replace(";", "<br>")
|
||||
else:
|
||||
logging.warning("Invalid typ - use empty message")
|
||||
|
||||
try:
|
||||
|
||||
|
|
@ -74,7 +88,7 @@ def run(typ,freq,data):
|
|||
"to": globalVars.config.get("Sms77", "to"),
|
||||
"from": globalVars.config.get("Sms77", "from"),
|
||||
"type": globalVars.config.get("Sms77", "type"),
|
||||
"text": data["description"]+"<br>"+data["msg"].replace(";", "<br>")
|
||||
"text": message
|
||||
}),{"Content-type": "application/x-www-form-urlencoded", "Accept": "text/plain"})
|
||||
|
||||
except:
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@ from telegram.error import (TelegramError, Unauthorized, BadRequest, NetworkErro
|
|||
from includes import globalVars # Global variables
|
||||
|
||||
# Helper function, uncomment to use
|
||||
from includes.helper import wildcardHandler
|
||||
from includes.helper import configHandler
|
||||
from includes.helper import timeHandler
|
||||
|
||||
# local variables
|
||||
BOTTokenAPIKey = None
|
||||
|
|
@ -78,20 +78,21 @@ def run(typ,freq,data):
|
|||
try:
|
||||
########## User Plugin CODE ##########
|
||||
try:
|
||||
if typ == "POC":
|
||||
logging.debug("Compose output from POCSAG-message")
|
||||
if typ in ("POC", "FMS", "ZVEI"):
|
||||
logging.debug("Read format and compose output for %s-message" % typ)
|
||||
# compose message content
|
||||
output = timeHandler.curtime()+"\n"+data["ric"]+"("+data["functionChar"]+")\n"+data["description"]+"\n"+data["msg"]
|
||||
text = globalVars.config.get("Telegram", "%s_message" % typ)
|
||||
text = wildcardHandler.replaceWildcards(text, data)
|
||||
|
||||
# Initiate Telegram Bot
|
||||
logging.debug("Initiate Telegram BOT")
|
||||
bot = telegram.Bot(token='%s' % BOTTokenAPIKey)
|
||||
# Send message to chat via Telegram BOT API
|
||||
logging.debug("Send message to chat via Telegram BOT API")
|
||||
bot.sendMessage('%s' % BOTChatIDAPIKey, output)
|
||||
bot.sendMessage('%s' % BOTChatIDAPIKey, text)
|
||||
|
||||
# Generate location information only for specific RIC
|
||||
if data["ric"] == RICforLocationAPIKey:
|
||||
if typ == "POC" and data["ric"] == RICforLocationAPIKey:
|
||||
# Generate map
|
||||
logging.debug("Extract address from POCSAG message")
|
||||
address = "+".join(data["msg"].split(')')[0].split('/',1)[1].replace('(',' ').split())
|
||||
|
|
@ -113,28 +114,6 @@ def run(typ,freq,data):
|
|||
gcode_result = gcode.geocode(address)
|
||||
logging.debug("Send location via Telegram BOT API")
|
||||
bot.sendLocation('%s' % BOTChatIDAPIKey, gcode_result[0]['geometry']['location']['lat'], gcode_result[0]['geometry']['location']['lng'], disable_notification='true')
|
||||
elif typ == "FMS":
|
||||
logging.debug("Compose output from FMS-message")
|
||||
# compose message content
|
||||
output = timeHandler.curtime()+"\n"+data["fms"]+"\n"+data["description"]+"\n"+data["status"]
|
||||
|
||||
# Initiate Telegram Bot
|
||||
logging.debug("Initiate Telegram BOT")
|
||||
bot = telegram.Bot(token='%s' % BOTTokenAPIKey)
|
||||
# Send message to chat via Telegram BOT API
|
||||
logging.debug("Send message to chat via Telegram BOT API")
|
||||
bot.sendMessage('%s' % BOTChatIDAPIKey, output)
|
||||
elif typ == "ZVEI":
|
||||
logging.debug("Compose output from ZVEI-message")
|
||||
# compose message content
|
||||
output = timeHandler.curtime()+"\n"+data["zvei"]+"\n"+data["description"]
|
||||
|
||||
# Initiate Telegram Bot
|
||||
logging.debug("Initiate Telegram BOT")
|
||||
bot = telegram.Bot(token='%s' % BOTTokenAPIKey)
|
||||
# Send message to chat via Telegram BOT API
|
||||
logging.debug("Send message to chat via Telegram BOT API")
|
||||
bot.sendMessage('%s' % BOTChatIDAPIKey, output)
|
||||
else:
|
||||
logging.warning("Invalid Typ: %s", typ)
|
||||
except Unauthorized:
|
||||
|
|
|
|||
|
|
@ -137,7 +137,7 @@ def run(typ,freq,data):
|
|||
# read mailtext-structure from config.ini
|
||||
mailtext = globalVars.config.get("eMail", "fms_message")
|
||||
# replace wildcards with helper function
|
||||
mailtext = wildcardHandler.replaceWildcards(mailtext, data, lineBrakeAllowed=True)
|
||||
mailtext = wildcardHandler.replaceWildcards(mailtext, data)
|
||||
|
||||
# send eMail
|
||||
doSendmail(server, subject, mailtext)
|
||||
|
|
@ -157,7 +157,7 @@ def run(typ,freq,data):
|
|||
# read mailtext-structure from config.ini
|
||||
mailtext = globalVars.config.get("eMail", "zvei_message")
|
||||
# replace wildcards with helper function
|
||||
mailtext = wildcardHandler.replaceWildcards(mailtext, data, lineBrakeAllowed=True)
|
||||
mailtext = wildcardHandler.replaceWildcards(mailtext, data)
|
||||
|
||||
# send eMail
|
||||
doSendmail(server, subject, mailtext)
|
||||
|
|
@ -177,7 +177,7 @@ def run(typ,freq,data):
|
|||
# read mailtext-structure from config.ini
|
||||
mailtext = globalVars.config.get("eMail", "poc_message")
|
||||
# replace wildcards with helper function
|
||||
mailtext = wildcardHandler.replaceWildcards(mailtext, data, lineBrakeAllowed=True)
|
||||
mailtext = wildcardHandler.replaceWildcards(mailtext, data)
|
||||
|
||||
# send eMail
|
||||
doSendmail(server, subject, mailtext)
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ httpRequest-Plugin to dispatch FMS-, ZVEI- and POCSAG - messages to an URL
|
|||
#
|
||||
# Imports
|
||||
#
|
||||
import urllib
|
||||
import urllib2
|
||||
import logging # Global logger
|
||||
from includes import globalVars # Global variables
|
||||
|
|
@ -67,36 +68,41 @@ def run(typ,freq,data):
|
|||
|
||||
try:
|
||||
#
|
||||
# Create URL
|
||||
# Replace special characters in data Strings for URL
|
||||
#
|
||||
for key in data:
|
||||
if isinstance(data[key], basestring):
|
||||
data[key] = urllib.quote(data[key])
|
||||
#
|
||||
# Get URLs
|
||||
#
|
||||
if typ == "FMS":
|
||||
url = globalVars.config.get("httpRequest", "fms_url") #Get URL
|
||||
url = wildcardHandler.replaceWildcards(url, data) # replace wildcards with helper function
|
||||
url = url.replace(" ","%20") # replace space with %20 to be a vaild http request
|
||||
urls = globalVars.config.get("httpRequest", "fms_url").split(",")
|
||||
elif typ == "ZVEI":
|
||||
url = globalVars.config.get("httpRequest", "zvei_url") #Get URL
|
||||
url = wildcardHandler.replaceWildcards(url, data) # replace wildcards with helper function
|
||||
url = url.replace(" ","%20") # replace space with %20 to be a vaild http request
|
||||
urls = globalVars.config.get("httpRequest", "zvei_url").split(",")
|
||||
elif typ == "POC":
|
||||
url = globalVars.config.get("httpRequest", "poc_url") #Get URL
|
||||
url = wildcardHandler.replaceWildcards(url, data) # replace wildcards with helper function
|
||||
url = url.replace(" ","%20") # replace space with %20 to be a vaild http request
|
||||
urls = globalVars.config.get("httpRequest", "poc_url").split(",")
|
||||
else:
|
||||
logging.warning("Invalid Typ: %s", typ)
|
||||
return
|
||||
|
||||
#
|
||||
# replace wildcards
|
||||
#
|
||||
for (i, url) in enumerate(urls):
|
||||
urls[i] = wildcardHandler.replaceWildcards(urls[i].strip(), data)
|
||||
#
|
||||
# HTTP-Request
|
||||
#
|
||||
logging.debug("send %s HTTP request", typ)
|
||||
logging.debug("send %s HTTP requests", typ)
|
||||
|
||||
try:
|
||||
#resp = urllib2.urlopen(url)
|
||||
urllib2.urlopen(url)
|
||||
except urllib2.HTTPError as e:
|
||||
logging.warning("HTTP response: %s", e.code)
|
||||
except urllib2.URLError as e:
|
||||
logging.warning("HTTP-specific error: %s", e.args)
|
||||
for url in urls:
|
||||
try:
|
||||
urllib2.urlopen(url)
|
||||
except urllib2.HTTPError as e:
|
||||
logging.warning("HTTP response: %s", e.code)
|
||||
except urllib2.URLError as e:
|
||||
logging.warning("HTTP-specific error: %s", e.args)
|
||||
|
||||
except:
|
||||
logging.error("cannot send HTTP request")
|
||||
|
|
|
|||
|
|
@ -1,3 +1,20 @@
|
|||
### Use BOSWatch as service ###
|
||||
|
||||
Old description below
|
||||
|
||||
We assume that BOSWatch is installed to /opt/boswatch! Otherwise you need to adapt all the pathes in this description and in the service-file itself.
|
||||
|
||||
#### Adapt the script
|
||||
Enter the frequency and the decoder(s) you want to use in line 7; you can add more specific switches if you need to
|
||||
|
||||
### Install the service
|
||||
1. Copy the file to /lib/systemd/system: sudo cp /opt/boswatch/service/boswatch.service /lib/systemd/system/
|
||||
2. Change the rights: sudo chmod 644 /lib/systemd/system/boswatch.service
|
||||
3. Enable the service: sudo systemcl enable boswatch.service
|
||||
4. Start the service: sudo systemcl start boswatch.service
|
||||
|
||||
---
|
||||
|
||||
### Start BOSWatch as a daemon
|
||||
|
||||
##### Changing the init script
|
||||
|
|
|
|||
11
service/boswatch.service
Normal file
11
service/boswatch.service
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
[Unit]
|
||||
Description=BOSWatch
|
||||
After=multi-user.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/bin/python /opt/boswatch/boswatch.py -f 123.45M -a POC512
|
||||
Restart=on-abort
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
Loading…
Reference in a new issue