add geocoding module, resolve threads

This commit is contained in:
Jan Speller 2020-02-22 19:08:53 +01:00
parent 575e44ae54
commit 4aea7b5d78
6 changed files with 291 additions and 100 deletions

View file

@ -0,0 +1,53 @@
# <center>Geocoding</center>
---
## Beschreibung
Mit diesem Modul können einem Paket die Koordinaten eines Ortes oder einer Adresse angefügt werden.
## Unterstützte Alarmtypen
- Pocsag
## Resource
`geocoding`
## Konfiguration
|Feld|Beschreibung|Default|
|----|------------|-------|
apiProvider|Der Provider für das Geocoding|
apiToken|Der Api-Token fuer die Geocoding-Api|
geoRegex|Regex Capture-Group zum Herausfiltern der Adresse|
#### Verfügbare Geocoding Provider
|Name|Einstellungswert|
|----|------------|
|Mapbox|mapbox|
|Google Maps|google|
**Beispiel:**
```yaml
- type: module
name: Geocoding Module
res: geocoding
config:
apiProvider: "{{ Provider für Geocoding }}"
apiToken: "{{ API-Key für Provider }}"
regex: "((?:[^ ]*,)*?)"
```
---
## Abhängigkeiten
- geocoder
- re
---
## Paket Modifikationen
- Im Paket werden die Felder `lat` und `lng` hinterlegt
---
## Zusätzliche Wildcards
- keine

View file

@ -2,38 +2,20 @@
--- ---
## Beschreibung ## Beschreibung
Mit diesem Plugin ist es moeglich, Telegram-Nachrichten für POCSAG-Alarmierungen zu senden. Mit diesem Plugin ist es moeglich, Telegram-Nachrichten für POCSAG-Alarmierungen zu senden. Außerdem werden Locations versenden, wenn die Felder `lat` und `lng` im Paket definiert sind.
Außerdem unterstützt das Plugin das Versenden von Location über folgende geocoding-Api's:
- Mapbox ## Unterstütze Alarmtypen
- Google Maps - Pocsag
## Resource ## Resource
`telegram` `telegram`
## Konfiguration ## Konfiguration
|Feld|Beschreibung|Default|
|----|------------|-------|
|name|Beliebiger Name des Plugins||
#### `config:`
|Feld|Beschreibung|Default| |Feld|Beschreibung|Default|
|----|------------|-------| |----|------------|-------|
|botToken|Der Api-Key des Telegram-Bots|| |botToken|Der Api-Key des Telegram-Bots||
|chatId|Die Chat-Id des Empfängers / der Emfänger-Gruppe|| |chatIds|Liste mit Chat-Ids der Empfängers / der Emfänger-Gruppen||
|geocoding|Aktivieren des Geocodings|false|
|geoRegex|Regex Capture-Group zum Herausfiltern der Adresse||
|geoApiProvider|Der Provider für das Geocoding||
|geoApiToken|Der Api-Token fuer die Geocoding-Api||
#### Verfügbare Geocoding Provider
|Name|Einstellungswert|
|----|------------|
|Mapbox|mapbox|
|Google Maps|google|
**Beispiel:** **Beispiel:**
```yaml ```yaml
@ -41,19 +23,15 @@ Außerdem unterstützt das Plugin das Versenden von Location über folgende geoc
name: Telegram Plugin name: Telegram Plugin
res: telegram res: telegram
config: config:
botToken: {{ Telegram Bot Token }} botToken: "{{ Telegram Bot Token }}"
chatId: {{ Telegram Chat Id }} chatIds:
geocoding: true - "{{ Telegram Chat Id }}"
geoRegex: ((?:[^ ]*,)*?)
geoApiProvider: mapbox
geoApiToken: {{ Mapbox Api Key }}
``` ```
--- ---
## Abhängigkeiten ## Abhängigkeiten
- python-telegram-bot - python-telegram-bot
- geocoder
--- ---
## Paket Modifikationen ## Paket Modifikationen

View file

@ -21,6 +21,7 @@ nav:
- Mode Filter: modul/mode_filter.md - Mode Filter: modul/mode_filter.md
- Regex Filter: modul/regex_filter.md - Regex Filter: modul/regex_filter.md
- Descriptor: modul/descriptor.md - Descriptor: modul/descriptor.md
- Geocoding: modul/geocoding.md
- Plugins: - Plugins:
- Telegram: plugin/telegram.md - Telegram: plugin/telegram.md
- Entwickler: - Entwickler:

72
module/geocoding.py Normal file
View file

@ -0,0 +1,72 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
"""!
____ ____ ______ __ __ __ _____
/ __ )/ __ \/ ___/ | / /___ _/ /______/ /_ |__ /
/ __ / / / /\__ \| | /| / / __ `/ __/ ___/ __ \ /_ <
/ /_/ / /_/ /___/ /| |/ |/ / /_/ / /_/ /__/ / / / ___/ /
/_____/\____//____/ |__/|__/\__,_/\__/\___/_/ /_/ /____/
German BOS Information Script
by Bastian Schroll
@file: geocoding.py
@date: 01.03.2019
@author: Jan Speller
@description: Geocoding Module
"""
import logging
from module.moduleBase import ModuleBase
# ###################### #
# Custom plugin includes #
import geocoder
import re
# ###################### #
logging.debug("- %s loaded", __name__)
class BoswatchModule(ModuleBase):
"""!Description of the Module"""
def __init__(self, config):
"""!Do not change anything here!"""
super().__init__(__name__, config) # you can access the config class on 'self.config'
def doWork(self, bwPacket):
"""!start an run of the module.
@param bwPacket: A BOSWatch packet instance"""
if bwPacket.get("mode") == "pocsag":
self.geocode(bwPacket)
pass
return bwPacket
def geocode(self, bwPacket):
"""!find address in message and get latitude and longitude
@param bwPacket: A BOSWatch packet instance"""
try:
address = re.search(self.config.get("regex"), bwPacket.get("message"))[1]
provider = self.config.get("apiProvider")
if "mapbox" == provider:
g = geocoder.mapbox(address, key=self.config.get("apiToken"))
print(address)
elif "google" == provider:
g = geocoder.google(address, key=self.config.get("apiToken"))
else:
return bwPacket
(lat, lng) = g.latlng
bwPacket.set("lat", lat)
bwPacket.set("lng", lng)
return bwPacket
except (IndexError, TypeError, ValueError):
logging.warning("Address was not found in current Message, skipping geocoding")
except Exception as e:
logging.error("Unknown Error while executing geocoding module: " + str(type(e).__name__) + ": " + str(e))
return bwPacket
def onUnload(self):
"""!Called by destruction of the plugin"""
pass

View file

@ -9,22 +9,19 @@
German BOS Information Script German BOS Information Script
by Bastian Schroll by Bastian Schroll
@file: template_module.py @file: telegram.py
@date: 14.01.2018 @date: 20.02.2020
@author: Bastian Schroll @author: Jan Speller
@description: Template Plugin File @description: Telegram Plugin
""" """
import logging import logging
from telegram.error import (TelegramError, Unauthorized, BadRequest, TimedOut, ChatMigrated, NetworkError)
from plugin.pluginBase import PluginBase from plugin.pluginBase import PluginBase
import telegram
import re
import geocoder
# ###################### # # ###################### #
# Custom plugin includes # # Custom plugin includes #
from telegram.error import (TelegramError, Unauthorized, BadRequest, TimedOut, ChatMigrated, NetworkError)
import telegram
# ###################### # # ###################### #
logging.debug("- %s loaded", __name__) logging.debug("- %s loaded", __name__)
@ -39,75 +36,31 @@ class BoswatchPlugin(PluginBase):
def onLoad(self): def onLoad(self):
"""!Called by import of the plugin""" """!Called by import of the plugin"""
pass
def setup(self):
"""!Called before alarm"""
self.bot = telegram.Bot(token=self.config.get("botToken", default="")) self.bot = telegram.Bot(token=self.config.get("botToken", default=""))
pass pass
def fms(self, bwPacket):
"""!Called on FMS alarm
@param bwPacket: bwPacket instance"""
logging.warning("Telegram Plugin does not work for FMS")
pass
def pocsag(self, bwPacket): def pocsag(self, bwPacket):
"""!Called on POCSAG alarm """!Called on POCSAG alarm
@param bwPacket: bwPacket instance""" @param bwPacket: bwPacket instance"""
msg = bwPacket.get("ric") + " (" + bwPacket.get("subric") + ")\n" + bwPacket.get("message")
try: if bwPacket.get("lat") is not None and bwPacket.get("lng") is not None:
# Send Message via Telegram (lat, lng) = (bwPacket.get("lat"), bwPacket.get("lng"))
msg = bwPacket.get("ric") + " (" + bwPacket.get("subric") + ")\n" + bwPacket.get("message") for chatId in self.config.get("chatIds", default=[]):
self.bot.send_message(chat_id=self.config.get("chatId", default=""), text=msg) try:
# Send Message via Telegram
# Send Location via Telegram if Geocoding is enabled and Provider and Key are set self.bot.send_message(chat_id=chatId, default=""), text=msg)
if self.config.get("geocoding", default=False):
address = re.search(self.config.get("geoRegex"), bwPacket.get("message"))[1]
provider = self.config.get("geoApiProvider")
if "mapbox" == provider:
g = geocoder.mapbox(address, key=self.config.get("geoApiToken"))
elif "google" == provider:
g = geocoder.google(address, key=self.config.get("geoApiToken"))
else:
return
(lat, lng) = g.latlng
self.bot.sendLocation(chat_id=self.config.get("chatId", default=""), latitude=lat, longitude=lng)
except (IndexError, TypeError, ValueError):
logging.warning("Address was not found in current Message, skipping Location")
except Unauthorized:
logging.error("Error while sending Telegram Message, please Check your api-key")
except (TimedOut, NetworkError):
logging.error("Error while sending Telegram Message, please Check your connectivity")
except (BadRequest, TelegramError):
logging.error("Error while sending Telegram Message")
except Exception as e:
logging.error("Unknown Error while sending Telegram Message: " + str(type(e).__name__) + ": " + str(e))
pass # Send Location via Telegram if lat and lng exist in Package
if lat is not None and lng is not None:
def zvei(self, bwPacket): self.bot.sendLocation(chat_id=chatId, latitude=lat, longitude=lng)
"""!Called on ZVEI alarm except Unauthorized:
logging.error("Error while sending Telegram Message, please Check your api-key")
@param bwPacket: bwPacket instance""" except (TimedOut, NetworkError):
logging.warning("Telegram Plugin does not work for ZVEI") logging.error("Error while sending Telegram Message, please Check your connectivity")
pass except (BadRequest, TelegramError):
logging.error("Error while sending Telegram Message")
def msg(self, bwPacket): except Exception as e:
"""!Called on MSG packet logging.error("Unknown Error while sending Telegram Message: " + str(type(e).__name__) + ": " + str(e))
@param bwPacket: bwPacket instance"""
logging.warning("Telegram Plugin does not work for MSG")
pass
def teardown(self):
"""!Called after alarm"""
pass
def onUnload(self):
"""!Called by destruction of the plugin"""
pass pass

134
testPackage.py Normal file
View file

@ -0,0 +1,134 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
import logging
import socket
import select
logging.debug("- %s loaded", __name__)
HEADERSIZE = 10
class TCPClient:
"""!TCP client class"""
def __init__(self, timeout=3):
"""!Create a new instance
@param timeout: timeout for the client in sec. (3)"""
socket.setdefaulttimeout(timeout)
self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
def connect(self, host="localhost", port=8080):
"""!Connect to the server
@param host: Server IP address ("localhost")
@param port: Server Port (8080)
@return True or False"""
try:
if not self.isConnected:
self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self._sock.connect((host, port))
logging.debug("connected to %s:%s", host, port)
return True
logging.warning("client always connected")
return True
except socket.error as e:
logging.error(e)
return False
def disconnect(self):
"""!Disconnect from the server
@return True or False"""
try:
if self.isConnected:
self._sock.shutdown(socket.SHUT_RDWR)
self._sock.close()
logging.debug("disconnected")
return True
logging.warning("client always disconnected")
return True
except socket.error as e:
logging.error(e)
return False
def transmit(self, data):
"""!Send a data packet to the server
@param data: data to send to the server
@return True or False"""
try:
logging.debug("transmitting:\n%s", data)
data = data.encode("utf-8")
header = str(len(data)).ljust(HEADERSIZE).encode("utf-8")
self._sock.sendall(header + data)
logging.debug("transmitted...")
return True
except socket.error as e:
logging.error(e)
return False
def receive(self, timeout=1):
"""!Receive data from the server
@param timeout: to wait for incoming data in seconds
@return received data"""
try:
read, _, _ = select.select([self._sock], [], [], timeout)
if not read: # check if there is something to read
return False
header = self._sock.recv(HEADERSIZE).decode("utf-8")
if not len(header): # check if there data
return False
length = int(header.strip())
received = self._sock.recv(length).decode("utf-8")
logging.debug("recv header: '%s'", header)
logging.debug("received %d bytes: %s", len(received), received)
return received
except socket.error as e:
logging.error(e)
return False
@property
def isConnected(self):
"""!Property of client connected state"""
try:
if self._sock:
_, write, _ = select.select([], [self._sock], [], 0.1)
if write:
data = "<keep-alive>".encode("utf-8")
header = str(len(data)).ljust(HEADERSIZE).encode("utf-8")
self._sock.sendall(header + data)
return True
return False
except socket.error as e:
if e.errno != 32:
logging.exception(e)
return False
except ValueError:
return False
client = TCPClient()
client.connect()
client.transmit(str({
'serverName': 'TestServer',
'serverVersion': '3.0',
'serverBuildDate': '01.01.2020',
'serverBranch': 'develop',
'clientName': 'TestClient',
'clientIP': '127.0.0.1',
'clientVersion': '3.0',
'clientBuildDate': '01.01.2020',
'clientBranch': 'develop',
'inputSource': 'sdr',
'timestamp': '15:30',
'frequency': '173.240',
'mode': 'pocsag',
'bitrate': '512',
'ric': '1326089',
'subric': '1',
'subricText': 'a',
'message': '17:22 FW 18865 Gebäudebrand_kle Sandkampstr.,15,-,Hopsten,NRW rauchentwicklung'
}))