From a90fcab083e06edbec69eca230cb8be856680f50 Mon Sep 17 00:00:00 2001 From: David Hoover Date: Sat, 5 Feb 2022 01:15:50 -0700 Subject: [PATCH] Initial commit --- .gitignore | 2 ++ .python-version | 1 + .vscode/settings.json | 3 ++ Dockerfile | 11 +++++++ influxdb.sh | 13 ++++++++ requests.txt | 2 ++ src/__init__.py | 0 src/main.py | 74 +++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 106 insertions(+) create mode 100644 .gitignore create mode 100644 .python-version create mode 100644 .vscode/settings.json create mode 100644 Dockerfile create mode 100755 influxdb.sh create mode 100644 requests.txt create mode 100644 src/__init__.py create mode 100644 src/main.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e647774 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.venv +environment.sh \ No newline at end of file diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..f870be2 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.10.1 diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..de288e1 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "python.formatting.provider": "black" +} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..98cba19 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,11 @@ +FROM python:3.10-alpine + +WORKDIR /usr/src/app + +COPY requests.txt . + +RUN pip install -r requests.txt + +COPY src . + +CMD ["python", "main.py"] diff --git a/influxdb.sh b/influxdb.sh new file mode 100755 index 0000000..733b10f --- /dev/null +++ b/influxdb.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +scriptPath="$( cd "$(dirname "$0")" && pwd -P )" + +source "$scriptPath/../environment.sh" + +docker run -p 8086:8086 \ + -v influxdb2:/var/lib/influxdb2 \ + -e DOCKER_INFLUXDB_INIT_USERNAME="$INFLUXDB_INIT_USERNAME" \ + -e DOCKER_INFLUXDB_INIT_PASSWORD="$INFLUXDB_INIT_USERNAME" \ + -e DOCKER_INFLUXDB_INIT_ORG="$INFLUXDB_INIT_ORG" \ + -e DOCKER_INFLUXDB_INIT_BUCKET="$INFLUXDB_INIT_BUCKET" \ + influxdb:2.0 diff --git a/requests.txt b/requests.txt new file mode 100644 index 0000000..f919c8d --- /dev/null +++ b/requests.txt @@ -0,0 +1,2 @@ +influxdb-client==1.25.0 +websockets==10.1 diff --git a/src/__init__.py b/src/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/main.py b/src/main.py new file mode 100644 index 0000000..266b93f --- /dev/null +++ b/src/main.py @@ -0,0 +1,74 @@ +import asyncio +from functools import partial +import json +import logging +import os +from typing import Optional +import websockets + +from dataclasses import dataclass + +from influxdb_client import InfluxDBClient, Point +from influxdb_client.client.write_api import SYNCHRONOUS + +logging.basicConfig() +logger = logging.getLogger(__name__) +logger.setLevel(logging.INFO) + + +@dataclass +class HubitatElevationEvent: + source: str + name: str + displayName: str + value: float + type: Optional[str] + unit: str + deviceId: int + hubId: int + installedAppId: int + descriptionText: str + + def __post_init__(self): + self.value = float(self.value) + + +hs_ws_url = os.environ["HUBITAT_ELEVATION_WEBSOCKET_URL"] +influxdb_url = os.environ["INFLUXDB_URL"] +token = os.environ["INFLUXDB_TOKEN"] +org = os.environ["INFLUXDB_ORG"] +bucket = os.environ["INFLUXDB_BUCKET"] + +client = InfluxDBClient(url=influxdb_url, token=token, org=org) +write_api = client.write_api(write_options=SYNCHRONOUS) + + +async def write_event(record): + return asyncio.get_running_loop().run_in_executor( + None, partial(write_api.write, bucket, record=record) + ) + + +async def he_event(): + async with websockets.connect(hs_ws_url) as websocket: + loop = asyncio.get_running_loop() + + async for message in websocket: + try: + event = HubitatElevationEvent(**json.loads(message)) + logger.info( + "%s (%s): %s" + % (event.displayName, event.deviceId, event.descriptionText) + ) + except Exception as ex: + logger.error("Failed parsing message from HE websocket: %s" % message) + + p = ( + Point("home") + .tag("device", event.displayName) + .field(event.name, event.value) + ) + await write_event(p) + + +asyncio.run(he_event())