pull/2/head
Michaela 2021-02-02 10:33:53 +10:00
commit 6a7c5e4130
7 zmienionych plików z 331 dodań i 0 usunięć

138
.gitignore vendored 100644
Wyświetl plik

@ -0,0 +1,138 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/

57
README.md 100644
Wyświetl plik

@ -0,0 +1,57 @@
Simple realtime streaming SDK for sondehub.org V2 API.
```
sondehub.Stream(sondes=["serial number"], on_message=callback)
```
If no `sondes` list is provided then all radiosondes will be streamed.
On message callback will contain a python dictonary using the [Universal Sonde Telemetry Format](https://github.com/projecthorus/radiosonde_auto_rx/wiki/Suggested-Universal-Sonde-Telemetry-Format)
Example Usage
--
```python
import sondehub
def on_message(message):
print(message)
test = sondehub.Stream(sondes=["R3320848"], on_message=on_message)
#test = sondehub.Stream(on_message=on_message)
while 1:
pass
```
CLI Usage
```sh
# all radiosondes
sondehub
# single radiosonde
sondehub --serial "IMET-73217972"
# multiple radiosondes
sondehub --serial "IMET-73217972" --serial "IMET-73217973"
#pipe in jq
sondehub | jq .
{
"subtype": "SondehubV1",
"temp": "-4.0",
"manufacturer": "SondehubV1",
"serial": "IMET54-55067143",
"lat": "-25.95437",
"frame": "85436",
"datetime": "2021-02-01T23:43:57.043655Z",
"software_name": "SondehubV1",
"humidity": "97.8",
"alt": "5839",
"vel_h": "-9999.0",
"uploader_callsign": "ZS6TVB",
"lon": "28.19082",
"software_version": "SondehubV1",
"type": "SondehubV1",
"time_received": "2021-02-01T23:43:57.043655Z",
"position": "-25.95437,28.19082"
}
....
```

9
example.py 100644
Wyświetl plik

@ -0,0 +1,9 @@
import sondehub
def on_message(message):
print(message)
test = sondehub.Stream(sondes=["R3320848"], on_message=on_message)
#test = sondehub.Stream(on_message=on_message)
while 1:
pass

20
poetry.lock wygenerowano 100644
Wyświetl plik

@ -0,0 +1,20 @@
[[package]]
name = "paho-mqtt"
version = "1.5.1"
description = "MQTT version 5.0/3.1.1 client class"
category = "main"
optional = false
python-versions = "*"
[package.extras]
proxy = ["pysocks"]
[metadata]
lock-version = "1.1"
python-versions = "^3.9"
content-hash = "76d5dfeb617b3f477c15007458ec4f5fad94e4d9ab19f2c8080bb7581685bc1f"
[metadata.files]
paho-mqtt = [
{file = "paho-mqtt-1.5.1.tar.gz", hash = "sha256:9feb068e822be7b3a116324e01fb6028eb1d66412bf98595ae72698965cb1cae"},
]

18
pyproject.toml 100644
Wyświetl plik

@ -0,0 +1,18 @@
[tool.poetry]
name = "sondehub"
version = "0.1.0"
description = ""
authors = ["Michaela <git@michaela.lgbt>"]
[tool.poetry.dependencies]
python = "^3.9"
paho-mqtt = "^1.5.1"
[tool.poetry.dev-dependencies]
[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
[tool.poetry.scripts]
sondehub = 'sondehub:__main__.main'

Wyświetl plik

@ -0,0 +1,70 @@
import paho.mqtt.client as mqtt
from urllib.parse import urlparse
import http.client
import json
class Stream():
def __init__(self, sondes: list = ["#"], on_connect=None, on_message=None):
self.mqttc = mqtt.Client(transport="websockets")
self._sondes = sondes
self.ws_connect()
self.on_connect = on_connect
self.on_message = on_message
def add_sonde(self, sonde):
if sonde not in self._sondes:
self._sondes.append(sonde)
(result, mid) = self.mqttc.subscribe(f"sondes/{sonde}", 0)
if result != mqtt.MQTT_ERR_SUCCESS:
self.ws_connect()
def remove_sonde(self, sonde):
self._sondes.remove(sonde)
(result, mid) = self.mqttc.unsubscribe(f"sondes/{sonde}", 0)
if result != mqtt.MQTT_ERR_SUCCESS:
self.ws_connect()
def ws_connect(self):
url = self.get_url()
urlparts = urlparse(url)
headers = {
"Host": "{0:s}".format(urlparts.netloc),
}
self.mqttc.on_message = self._on_message # self.on_message
self.mqttc.on_connect = self._on_connect
self.mqttc.on_disconnect = self._on_disconnect
self.mqttc.ws_set_options(path="{}?{}".format(urlparts.path, urlparts.query), headers=headers)
try:
self.mqttc.tls_set()
except ValueError:
pass
self.mqttc.connect(urlparts.netloc, 443, 60)
self.mqttc.loop_start()
for sonde in self._sondes:
self.add_sonde(sonde)
def get_url(self):
conn = http.client.HTTPSConnection("api.v2.sondehub.org")
conn.request("GET", "/sondes/websocket")
res = conn.getresponse()
data = res.read()
return data.decode("utf-8")
def _on_message(self, mqttc, obj, msg):
if self.on_message:
self.on_message(json.loads(msg.payload))
def _on_connect(self, mqttc, obj, flags, rc):
if mqtt.MQTT_ERR_SUCCESS != rc:
self.ws_connect()
if self.on_connect:
self.on_connect()
def _on_disconnect(self, client, userdata, rc):
self.ws_connect()
def __exit__(self, type, value, traceback):
self.mqttc.disconnect()
def disconnect(self):
self.mqttc.disconnect()

Wyświetl plik

@ -0,0 +1,19 @@
import json
import sondehub
import argparse
def on_message(message):
print(json.dumps(message))
def main():
parser = argparse.ArgumentParser(description='Sondehub CLI')
parser.add_argument('--serial', dest="sondes", default=["#"],nargs="*", help="Filter to sonde serial", type=str, action="extend")
args = parser.parse_args()
if len(args.sondes) > 1: # we need to drop the default value if the user specifies sepcific sondes
args.sondes = args.sondes[1:]
test = sondehub.Stream(on_message=on_message, sondes=args.sondes)
while 1:
pass
if __name__ == "__main__":
main()