diff --git a/.env.dist b/.env.dist index bc3c6c7..d4bb3af 100644 --- a/.env.dist +++ b/.env.dist @@ -2,4 +2,14 @@ OPENWEATHER_API_KEY=CHANGEME LOCATION_ID=CHANGEME FLIPDOT_API=http://127.0.0.1/ FLIPDOT_SLOTS=15 -LUFTDATEN_CLOSEST_SENSOR=CHANGEME \ No newline at end of file +LUFTDATEN_CLOSEST_SENSOR=CHANGEME +MQTT_BROKER=127.0.0.1 +MQTT_PASSWORD=CHANGEME +MQTT_PORT=1883 +MQTT_TOPIC_BASE=flipdot/app/weather +MQTT_USERNAME=changeme +FLIPDOT_MQTT=True +FLIPDOT_POST=False +EVEN_DISTRIBUTION=False +TEXT_FONT=twin6 +TEXT_ALIGN=right \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index c26921f..b96c413 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,19 +1,18 @@ FROM python:3.9-slim -WORKDIR /app -COPY requirements.txt /app -RUN pip install --no-cache-dir -r requirements.txt - - RUN apt-get update && apt-get install --assume-yes \ cron \ && rm -rf /var/lib/apt/lists/* - COPY . /app +WORKDIR /app +COPY requirements.txt /app +RUN pip install --no-cache-dir -r requirements.txt + +COPY . /app RUN (echo "SHELL=/bin/bash" > /etc/cron.d/fetch_crl_cron) RUN (echo "BASH_ENV=/app/.env" >> /etc/cron.d/fetch_crl_cron) -RUN (echo "* * * * * root /usr/local/bin/python3 /app/app.py >> /var/log/cron.log 2>&1" >> /etc/cron.d/fetch_crl_cron) +RUN (echo "*/30 * * * * root /usr/local/bin/python3 /app/app.py >> /var/log/cron.log 2>&1" >> /etc/cron.d/fetch_crl_cron) RUN touch /var/log/cron.log diff --git a/app.py b/app.py index 8ca0be9..4f8478f 100644 --- a/app.py +++ b/app.py @@ -2,8 +2,10 @@ import requests from datetime import datetime import os import logging +import paho.mqtt.client as mqtt +import json -logging.basicConfig(format='%(asctime)s %(levelname)s:%(message)s',level=logging.DEBUG) +logging.basicConfig(format='%(asctime)s %(levelname)s:%(message)s',level=logging.INFO) logger = logging.getLogger(__name__) # OpenWeather API settings @@ -14,6 +16,32 @@ FLIPDOT_API = os.getenv('FLIPDOT_API', 'http://127.0.0.1/') FLIPDOT_SLOTS = int(os.getenv('FLIPDOT_SLOTS', 15)) LUFTDATEN_CLOSEST_SENSOR = os.getenv('LUFTDATEN_CLOSEST_SENSOR', '84121') DAYS = os.getenv('DAYS', 3) +is_mqtt = os.getenv('FLIPDOT_MQTT', 'False').lower() in ('true', '1', 't') +is_post = os.getenv('FLIPDOT_POST', 'False').lower() in ('true', '1', 't') +mqtt_broker = os.getenv('MQTT_BROKER', 'localhost') +mqtt_port = int(os.getenv('MQTT_PORT', 1883)) +mqtt_topic_base = os.getenv('MQTT_TOPIC_BASE', 'flipdot/trash/changeme') +mqtt_username = os.getenv('MQTT_USERNAME', None) +mqtt_password = os.getenv('MQTT_PASSWORD', None) +even_distribution = os.getenv('EVEN_DISTRIBUTION', 'False').lower() in ('true', '1', 't') +text_font = os.getenv('TEXT_FONT', 'twin6') +text_align = os.getenv('TEXT_ALIGN', 'right') + +def on_connect(client, userdata, flags, rc): + if rc == 0: + logger.info("Connected to MQTT Broker!") + else: + logger.error("Failed to connect, return code %d\n", rc) + +if is_mqtt: + client = mqtt.Client() + if mqtt_username and mqtt_password: + client.username_pw_set(mqtt_username, mqtt_password) + logger.debug("Setting username and password") + client.on_connect = on_connect + logger.debug("Connecting to MQTT Broker") + client.connect(mqtt_broker, mqtt_port, 60) + client.loop_start() # Icon mapping from API's "icon" field ICON_MAPPING = { @@ -67,16 +95,19 @@ def send_post(endpoint,payload = {}, data = {}): 'accept': 'application/json', } uri = (FLIPDOT_API+endpoint).replace('\r', '').replace('\n', '') - print(uri) requests.post( uri, headers=headers, json = payload, params=data ) # Helper function to map API's icon to your icon set def get_icon(api_icon): return ICON_MAPPING.get(api_icon, ICON_MAPPING["unknown"]) -def fetch_luftdaten_data(): - url = "https://data.sensor.community/airrohr/v1/sensor/{}/".format(LUFTDATEN_CLOSEST_SENSOR).replace('\r', '').replace('\n', '') - response = requests.get(url) - return response.json() +# def fetch_luftdaten_data(): +# url = "https://data.sensor.community/airrohr/v1/sensor/{}/".format(LUFTDATEN_CLOSEST_SENSOR).replace('\r', '').replace('\n', '') +# try: +# response = requests.get(url, timeout=5) +# except requests.exceptions.Timeout: +# logger.error("Request to Luftdaten API timed out") +# response = {"sensordatavalues": [{"value": "N/A"}, {"value": "N/A"}]} +# return response.json() # Fetch current weather def fetch_current_weather(): @@ -128,8 +159,8 @@ def generate_payload(current_weather, forecast): weather_main = current_weather["weather"][0]["main"] wind_speed = round(current_weather["wind"]["speed"] * 3.6) # Convert m/s to km/h wind_dir = current_weather["wind"]["deg"] - pm25 = fetch_luftdaten_data()[0]['sensordatavalues'][0]['value'] - pm10 = fetch_luftdaten_data()[0]['sensordatavalues'][1]['value'] + # pm25 = fetch_luftdaten_data()[0]['sensordatavalues'][0]['value'] + # pm10 = fetch_luftdaten_data()[0]['sensordatavalues'][1]['value'] if "rain" in current_weather.keys(): rain = current_weather["rain"]["1h"] @@ -142,14 +173,14 @@ def generate_payload(current_weather, forecast): snow = 0 payloads.append(create_payload(get_icon(weather_icon), f"NOW: {temp} st.C - {weather_main}", f"WIND: {wind_speed}KM/H {wind_dir}°")) - payloads.append(create_payload("mist", f"PM2.5: {pm25} ug/m3", f"PM10: {pm10} ug/m3")) + # payloads.append(create_payload("mist", f"PM2.5: {pm25} ug/m3", f"PM10: {pm10} ug/m3")) if rain > 0: payloads.append(create_payload("rain2", f"NOW: RAIN: {rain} mm", "IN 3H")) if snow > 0: payloads.append(create_payload("snow", f"NOW: SNOW: {snow} mm", "IN 3H")) - payloads.append(create_payload('mist', f"PM2.5: {pm25} ug/m3", f"PM10: {pm10} ug/m3")) + # payloads.append(create_payload('mist', f"PM2.5: {pm25} ug/m3", f"PM10: {pm10} ug/m3")) # Hourly forecast payloads for entry in forecast["list"][:DAYS]: @@ -170,18 +201,31 @@ def generate_payload(current_weather, forecast): if snow > 0: payloads.append(create_payload("snow", f"{dt}: SNOW: {snow} mm", "IN 3H")) - return evenly_distribute(payloads, FLIPDOT_SLOTS) + + return payloads if not even_distribution else evenly_distribute(payloads,FLIPDOT_SLOTS) + +def send_mqtt(page, payload): + topic = f"{mqtt_topic_base}/{page}" + client.publish(topic, json.dumps(payload)) # Main function def main(): current_weather = fetch_current_weather() forecast = fetch_hourly_forecast() - payloads = generate_payload(current_weather, forecast) - clean() + payloads = generate_payload(current_weather, forecast, font=text_font, align=text_align) + if is_post: + clean() for i, payload in enumerate(payloads): logger.debug(payload) logger.info(f"Sending frame {i+1} of {len(payloads)} - text: {payload['lines'][0]['text']}") - send_post('display/complex', payload=payload,data={'page':i}) + if is_post: + send_post(payload['endpoint'], data={'page':i},payload=payload) + logger.info("Sending payload via POST request") + if is_mqtt: + send_mqtt(i, payload) + logger.info("Sending payload via MQTT") + logger.info("Text: {}".format(payload['lines'][0]['text'])) + logger.info("Text: {}".format(payload['lines'][1]['text'])) if __name__ == "__main__": main() diff --git a/apply_env.sh b/apply_env.sh new file mode 100644 index 0000000..6eb5822 --- /dev/null +++ b/apply_env.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +# Check if the .env file exists +if [ ! -f .env ]; then + echo ".env file not found!" + exit 1 +fi + +# Read the .env file line by line +while IFS='=' read -r key value; do + # Skip empty lines or comments + if [[ -z "$key" || "$key" =~ ^# ]]; then + continue + fi + + # Export the variable + echo "Setting $key as $value" + export "$key=$value" +done < .env diff --git a/requirements.txt b/requirements.txt index e4954fc..a24b368 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ requests urllib3 -python-dotenv \ No newline at end of file +python-dotenv +paho-mqtt \ No newline at end of file