From 0bbf01e0077160f4982c4b826cf0d73485cdbf92 Mon Sep 17 00:00:00 2001 From: Adam Date: Sun, 25 Jan 2026 09:44:34 +0100 Subject: [PATCH] feat: add the foundation files with a working script --- .dockerignore | 11 + .gitignore | 1 + Dockerfile | 17 ++ README.md | 43 +++ index.html | 576 ++++++++++++++++++++++++++++++++++++++++ main.py | 103 +++++++ requirements.txt | 2 + templates/index.html.j2 | 80 ++++++ 8 files changed, 833 insertions(+) create mode 100644 .dockerignore create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 README.md create mode 100644 index.html create mode 100644 main.py create mode 100644 requirements.txt create mode 100644 templates/index.html.j2 diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..e0d2d68 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,11 @@ +__pycache__/ +*.pyc +*.pyo +*.pyd +.Python +env/ +.env +.git +.gitignore +Dockerfile +README.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f0b5b1e --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +sensor_data.json diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..dc6e084 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,17 @@ +# Use an official Python runtime as a parent image +FROM python:3.9-slim + +# Set the working directory in the container +WORKDIR /app + +# Copy the dependencies file to the working directory +COPY requirements.txt . + +# Install any needed packages specified in requirements.txt +RUN pip install --no-cache-dir -r requirements.txt + +# Copy the rest of the application +COPY . . + +# Run main.py when the container launches +CMD ["python", "main.py"] diff --git a/README.md b/README.md new file mode 100644 index 0000000..8b48449 --- /dev/null +++ b/README.md @@ -0,0 +1,43 @@ +# Home Assistant Logger + +This script connects to a Home Assistant instance, fetches temperature and humidity data, stores it, and generates an HTML report. + +## Prerequisites + +- Docker + +## How to Use + +### 1. Build the Docker Image + +```bash +docker build -t ha-logger . +``` + +### 2. Run the Docker Container + +You need to provide your Home Assistant URL and a Long-Lived Access Token as environment variables. + +```bash +docker run --rm -v $(pwd):/app + -e HA_URL="http://your-home-assistant-ip:8123" + -e HA_TOKEN="your-long-lived-access-token" + ha-logger +``` + +- Replace `"http://your-home-assistant-ip:8123"` with the URL of your Home Assistant instance. +- Replace `"your-long-lived-access-token"` with your token. + +The `-v $(pwd):/app` command mounts the current directory into the container, so the output files (`index.html` and `sensor_data.json`) are created and updated in your project folder. + +### How it Works + +- The script connects to your Home Assistant instance using the provided URL and token. +- It fetches all devices and filters for sensors that have a unit of `°C` (Celsius) or `%` (for humidity). +- The new readings are appended to the `sensor_data.json` file. +- An `index.html` file is generated, displaying the latest readings and a searchable, sortable history of all readings. + +### Generated Files + +- **`sensor_data.json`**: Stores the historical data for all sensor readings. +- **`index.html`**: The generated HTML report. Open this file in your browser to see the sensor data. diff --git a/index.html b/index.html new file mode 100644 index 0000000..7096d64 --- /dev/null +++ b/index.html @@ -0,0 +1,576 @@ + + + + + + Home Assistant Sensor Readings + + + + +

Last updated: 2026-01-25 09:40:05

+ +

Recent Sensor Readings

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Entity IDFriendly NameStateUnitLast Updated
sensor.kuchnia_czujnik_klimatu_temperaturaKuchnia Czujnik Klimatu Temperatura21.8°C2026-01-25T08:35:10.975179+00:00
sensor.kuchnia_czujnik_klimatu_wilgotnoscKuchnia Czujnik Klimatu Wilgotność31.6%2026-01-25T08:38:41.244256+00:00
sensor.czujnik_klimatu_salon_temperaturaCzujnik Klimatu Salon Temperatura22.2°C2026-01-25T07:55:35.936876+00:00
sensor.czujnik_klimatu_salon_wilgotnoscCzujnik Klimatu Salon Wilgotność26.8%2026-01-25T08:23:14.904455+00:00
sensor.czujnik_klimatu_gora_front_temperaturaCzujnik Klimatu Góra Front Temperatura20.4°C2026-01-25T08:07:11.830919+00:00
sensor.czujnik_klimatu_gora_front_wilgotnoscCzujnik Klimatu Góra Front Wilgotność25.8%2026-01-25T08:28:17.305597+00:00
sensor.czujnik_klimatu_gora_tyl_temperaturaCzujnik Klimatu Góra Tył Temperatura19.4°C2026-01-25T08:06:49.620048+00:00
sensor.czujnik_klimatu_gora_tyl_wilgotnoscCzujnik Klimatu Góra Tył Wilgotność27.4%2026-01-25T08:02:24.184686+00:00
sensor.czujnik_klimatu_kotlownia_temperaturaCzujnik Klimatu Kotłownia Temperatura20.3°C2026-01-25T08:30:37.760046+00:00
sensor.czujnik_klimatu_kotlownia_wilgotnoscCzujnik Klimatu Kotłownia Wilgotność24.2%2026-01-25T08:18:35.511162+00:00
sensor.czujnik_klimatu_na_zewnatrz_temperaturaCzujnik Klimatu Na Zewnątrz Temperatura0.6°C2026-01-25T08:38:14.361984+00:00
sensor.czujnik_klimatu_na_zewnatrz_wilgotnoscCzujnik Klimatu Na Zewnątrz Wilgotność82.1%2026-01-25T08:27:38.801311+00:00
+ +

Historical Sensor Readings

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Entity IDFriendly NameStateUnitTimestamp
sensor.kuchnia_czujnik_klimatu_bateriaKuchnia Czujnik Klimatu Bateria100.0%2026-01-25T09:33:39.202329
sensor.kuchnia_czujnik_klimatu_temperaturaKuchnia Czujnik Klimatu Temperatura21.7°C2026-01-25T09:33:39.202329
sensor.kuchnia_czujnik_klimatu_wilgotnoscKuchnia Czujnik Klimatu Wilgotność29.4%2026-01-25T09:33:39.202329
sensor.czujnik_klimatu_salon_bateriaCzujnik Klimatu Salon Bateria100.0%2026-01-25T09:33:39.202329
sensor.czujnik_klimatu_salon_temperaturaCzujnik Klimatu Salon Temperatura22.2°C2026-01-25T09:33:39.202329
sensor.czujnik_klimatu_salon_wilgotnoscCzujnik Klimatu Salon Wilgotność26.8%2026-01-25T09:33:39.202329
sensor.czujnik_klimatu_gora_front_bateriaCzujnik Klimatu Góra Front Bateria100.0%2026-01-25T09:33:39.202329
sensor.czujnik_klimatu_gora_front_temperaturaCzujnik Klimatu Góra Front Temperatura20.4°C2026-01-25T09:33:39.202329
sensor.czujnik_klimatu_gora_front_wilgotnoscCzujnik Klimatu Góra Front Wilgotność25.8%2026-01-25T09:33:39.202329
sensor.czujnik_klimatu_gora_tyl_bateriaCzujnik Klimatu Góra Tył Bateria100.0%2026-01-25T09:33:39.202329
sensor.czujnik_klimatu_gora_tyl_temperaturaCzujnik Klimatu Góra Tył Temperatura19.4°C2026-01-25T09:33:39.202329
sensor.czujnik_klimatu_gora_tyl_wilgotnoscCzujnik Klimatu Góra Tył Wilgotność27.4%2026-01-25T09:33:39.202329
sensor.czujnik_klimatu_kotlownia_bateriaCzujnik Klimatu Kotłownia Bateria100.0%2026-01-25T09:33:39.202329
sensor.czujnik_klimatu_kotlownia_temperaturaCzujnik Klimatu Kotłownia Temperatura20.3°C2026-01-25T09:33:39.202329
sensor.czujnik_klimatu_kotlownia_wilgotnoscCzujnik Klimatu Kotłownia Wilgotność24.2%2026-01-25T09:33:39.202329
sensor.czujnik_klimatu_na_zewnatrz_bateriaCzujnik Klimatu Na Zewnątrz Bateria100.0%2026-01-25T09:33:39.202329
sensor.czujnik_klimatu_na_zewnatrz_temperaturaCzujnik Klimatu Na Zewnątrz Temperatura0.3°C2026-01-25T09:33:39.202329
sensor.czujnik_klimatu_na_zewnatrz_wilgotnoscCzujnik Klimatu Na Zewnątrz Wilgotność82.1%2026-01-25T09:33:39.202329
sensor.czujnik_ruchu_taras_bateriaCzujnik Ruchu Taras Bateria100.0%2026-01-25T09:33:39.202329
sensor.ewelink_snzb_03p_bateriaCzujnik Ruchu Garaże Bateria100.0%2026-01-25T09:33:39.202329
sensor.kuchnia_czujnik_klimatu_bateriaKuchnia Czujnik Klimatu Bateria100.0%2026-01-25T09:37:31.559000
sensor.kuchnia_czujnik_klimatu_temperaturaKuchnia Czujnik Klimatu Temperatura21.8°C2026-01-25T09:37:31.559000
sensor.kuchnia_czujnik_klimatu_wilgotnoscKuchnia Czujnik Klimatu Wilgotność32.6%2026-01-25T09:37:31.559000
sensor.czujnik_klimatu_salon_bateriaCzujnik Klimatu Salon Bateria100.0%2026-01-25T09:37:31.559000
sensor.czujnik_klimatu_salon_temperaturaCzujnik Klimatu Salon Temperatura22.2°C2026-01-25T09:37:31.559000
sensor.czujnik_klimatu_salon_wilgotnoscCzujnik Klimatu Salon Wilgotność26.8%2026-01-25T09:37:31.559000
sensor.czujnik_klimatu_gora_front_bateriaCzujnik Klimatu Góra Front Bateria100.0%2026-01-25T09:37:31.559000
sensor.czujnik_klimatu_gora_front_temperaturaCzujnik Klimatu Góra Front Temperatura20.4°C2026-01-25T09:37:31.559000
sensor.czujnik_klimatu_gora_front_wilgotnoscCzujnik Klimatu Góra Front Wilgotność25.8%2026-01-25T09:37:31.559000
sensor.czujnik_klimatu_gora_tyl_bateriaCzujnik Klimatu Góra Tył Bateria100.0%2026-01-25T09:37:31.559000
sensor.czujnik_klimatu_gora_tyl_temperaturaCzujnik Klimatu Góra Tył Temperatura19.4°C2026-01-25T09:37:31.559000
sensor.czujnik_klimatu_gora_tyl_wilgotnoscCzujnik Klimatu Góra Tył Wilgotność27.4%2026-01-25T09:37:31.559000
sensor.czujnik_klimatu_kotlownia_bateriaCzujnik Klimatu Kotłownia Bateria100.0%2026-01-25T09:37:31.559000
sensor.czujnik_klimatu_kotlownia_temperaturaCzujnik Klimatu Kotłownia Temperatura20.3°C2026-01-25T09:37:31.559000
sensor.czujnik_klimatu_kotlownia_wilgotnoscCzujnik Klimatu Kotłownia Wilgotność24.2%2026-01-25T09:37:31.559000
sensor.czujnik_klimatu_na_zewnatrz_bateriaCzujnik Klimatu Na Zewnątrz Bateria100.0%2026-01-25T09:37:31.559000
sensor.czujnik_klimatu_na_zewnatrz_temperaturaCzujnik Klimatu Na Zewnątrz Temperatura0.3°C2026-01-25T09:37:31.559000
sensor.czujnik_klimatu_na_zewnatrz_wilgotnoscCzujnik Klimatu Na Zewnątrz Wilgotność82.1%2026-01-25T09:37:31.559000
sensor.czujnik_ruchu_taras_bateriaCzujnik Ruchu Taras Bateria100.0%2026-01-25T09:37:31.559000
sensor.ewelink_snzb_03p_bateriaCzujnik Ruchu Garaże Bateria100.0%2026-01-25T09:37:31.559000
sensor.kuchnia_czujnik_klimatu_temperaturaKuchnia Czujnik Klimatu Temperatura21.8°C2026-01-25T09:40:05.881503
sensor.kuchnia_czujnik_klimatu_wilgotnoscKuchnia Czujnik Klimatu Wilgotność31.6%2026-01-25T09:40:05.881503
sensor.czujnik_klimatu_salon_temperaturaCzujnik Klimatu Salon Temperatura22.2°C2026-01-25T09:40:05.881503
sensor.czujnik_klimatu_salon_wilgotnoscCzujnik Klimatu Salon Wilgotność26.8%2026-01-25T09:40:05.881503
sensor.czujnik_klimatu_gora_front_temperaturaCzujnik Klimatu Góra Front Temperatura20.4°C2026-01-25T09:40:05.881503
sensor.czujnik_klimatu_gora_front_wilgotnoscCzujnik Klimatu Góra Front Wilgotność25.8%2026-01-25T09:40:05.881503
sensor.czujnik_klimatu_gora_tyl_temperaturaCzujnik Klimatu Góra Tył Temperatura19.4°C2026-01-25T09:40:05.881503
sensor.czujnik_klimatu_gora_tyl_wilgotnoscCzujnik Klimatu Góra Tył Wilgotność27.4%2026-01-25T09:40:05.881503
sensor.czujnik_klimatu_kotlownia_temperaturaCzujnik Klimatu Kotłownia Temperatura20.3°C2026-01-25T09:40:05.881503
sensor.czujnik_klimatu_kotlownia_wilgotnoscCzujnik Klimatu Kotłownia Wilgotność24.2%2026-01-25T09:40:05.881503
sensor.czujnik_klimatu_na_zewnatrz_temperaturaCzujnik Klimatu Na Zewnątrz Temperatura0.6°C2026-01-25T09:40:05.881503
sensor.czujnik_klimatu_na_zewnatrz_wilgotnoscCzujnik Klimatu Na Zewnątrz Wilgotność82.1%2026-01-25T09:40:05.881503
+
+ + + + \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..f5a6a62 --- /dev/null +++ b/main.py @@ -0,0 +1,103 @@ + +import os +import json +import requests +from jinja2 import Environment, FileSystemLoader +from datetime import datetime + +# --- Configuration --- +HA_URL = os.environ.get("HA_URL") +HA_TOKEN = os.environ.get("HA_TOKEN") +DATA_FILE = "sensor_data.json" +HTML_FILE = "index.html" +TEMPLATE_DIR = "templates" + +# --- Error Handling --- +if not HA_URL or not HA_TOKEN: + raise ValueError("HA_URL and HA_TOKEN environment variables must be set.") + +# --- Functions --- + +def get_ha_data(): + """Fetches all states from Home Assistant and filters for temp/humidity sensors.""" + headers = { + "Authorization": f"Bearer {HA_TOKEN}", + "content-type": "application/json", + } + url = f"{HA_URL}/api/states" + try: + response = requests.get(url, headers=headers) + response.raise_for_status() # Raise an exception for bad status codes + states = response.json() + + sensors = [ + s for s in states + if "unit_of_measurement" in s["attributes"] and ( + s["attributes"]["unit_of_measurement"] == "°C" or + (s["attributes"]["unit_of_measurement"] == "%" and + s["attributes"].get("device_class") != "battery") + ) + ] + return sensors + + except requests.exceptions.RequestException as e: + print(f"Error connecting to Home Assistant: {e}") + return None + +def read_historical_data(): + """Reads historical sensor data from the JSON file.""" + if not os.path.exists(DATA_FILE): + return [] + with open(DATA_FILE, "r") as f: + return json.load(f) + +def write_historical_data(new_data): + """Appends new sensor data to the historical record.""" + historical_data = read_historical_data() + timestamp = datetime.now().isoformat() + + for sensor in new_data: + historical_data.append({ + "entity_id": sensor["entity_id"], + "friendly_name": sensor["attributes"].get("friendly_name", sensor["entity_id"]), + "state": sensor["state"], + "unit": sensor["attributes"].get("unit_of_measurement"), + "timestamp": timestamp, + }) + + with open(DATA_FILE, "w") as f: + json.dump(historical_data, f, indent=4) + +def generate_html_report(recent_data, historical_data, last_updated_time): + """Generates an HTML report from the sensor data.""" + env = Environment(loader=FileSystemLoader(TEMPLATE_DIR)) + template = env.get_template("index.html.j2") + + html_content = template.render( + recent_data=recent_data, + historical_data=historical_data, + last_updated_time=last_updated_time + ) + + with open(HTML_FILE, "w") as f: + f.write(html_content) + print(f"Successfully generated HTML report: {HTML_FILE}") + +# --- Main Execution --- + +def main(): + """Main function to run the script.""" + print("Fetching data from Home Assistant...") + recent_sensor_data = get_ha_data() + + if recent_sensor_data: + print(f"Found {len(recent_sensor_data)} temperature/humidity sensors.") + write_historical_data(recent_sensor_data) + historical_data = read_historical_data() + last_updated_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + generate_html_report(recent_sensor_data, historical_data, last_updated_time) + else: + print("Could not fetch new data. Report generation skipped.") + +if __name__ == "__main__": + main() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..3e9d06e --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +requests +Jinja2 diff --git a/templates/index.html.j2 b/templates/index.html.j2 new file mode 100644 index 0000000..27cc90a --- /dev/null +++ b/templates/index.html.j2 @@ -0,0 +1,80 @@ + + + + + + Home Assistant Sensor Readings + + + + +

Last updated: {{ last_updated_time }}

+ +

Recent Sensor Readings

+ + + + + + + + + + + + {% for sensor in recent_data %} + + + + + + + + {% endfor %} + +
Entity IDFriendly NameStateUnitLast Updated
{{ sensor.entity_id }}{{ sensor.attributes.friendly_name }}{{ sensor.state }}{{ sensor.attributes.unit_of_measurement }}{{ sensor.last_updated }}
+ +

Historical Sensor Readings

+
+ + + + + + + + + + + + + {% for reading in historical_data %} + + + + + + + + {% endfor %} + +
Entity IDFriendly NameStateUnitTimestamp
{{ reading.entity_id }}{{ reading.friendly_name }}{{ reading.state }}{{ reading.unit }}{{ reading.timestamp }}
+
+ + + +