commit 0bbf01e0077160f4982c4b826cf0d73485cdbf92 Author: Adam Date: Sun Jan 25 09:44:34 2026 +0100 feat: add the foundation files with a working script 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 }}
+
+ + + +