From 84e7babf7ed568501507bb8aabe1a5882521e23e Mon Sep 17 00:00:00 2001 From: Naab2k3 Date: Tue, 24 Jun 2025 09:26:08 +0700 Subject: [PATCH] Add timezone configuration and update health check timestamps in POE project. Introduced LOCAL_TIMEZONE in config.py to support timezone offset configuration. Updated health_check.py, sensor_bridge.py, and sensor_tracker.py to utilize LOCAL_TIMEZONE for accurate timestamping in health responses and sensor data publishing. Enhanced README.md to document the new TIMEZONE_OFFSET environment variable, ensuring clarity for configuration options. --- README.md | 1 + config.py | 4 ++++ health_check.py | 6 +++--- sensor_bridge.py | 6 +++--- sensor_tracker.py | 14 +++++++------- 5 files changed, 18 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 21f9de2..d153f3e 100644 --- a/README.md +++ b/README.md @@ -141,6 +141,7 @@ The service supports environment variable configuration: | `ALERTING_ENABLED` | Enable MQTT alerts | true | | `SENSOR_TIMEOUT_THRESHOLD` | Failures before alert | 3 | | `RECOVERY_CONFIRMATION_COUNT` | Successes to confirm recovery | 2 | +| `TIMEZONE_OFFSET` | Timezone offset from UTC (hours) | 7 (GMT+7) | ## 🏥 Health Check & Monitoring diff --git a/config.py b/config.py index 70098dd..5ee7596 100644 --- a/config.py +++ b/config.py @@ -1,5 +1,6 @@ import time import os +from datetime import timezone, timedelta # Modbus configuration MODBUS_HOSTS = [ @@ -34,3 +35,6 @@ HEALTH_CHECK_ENABLED = os.getenv("HEALTH_CHECK_ENABLED", "true").lower() in ["tr ALERTING_ENABLED = os.getenv("ALERTING_ENABLED", "true").lower() in ["true", "1", "yes"] SENSOR_TIMEOUT_THRESHOLD = int(os.getenv("SENSOR_TIMEOUT_THRESHOLD", 3)) # Number of consecutive failures before alert RECOVERY_CONFIRMATION_COUNT = int(os.getenv("RECOVERY_CONFIRMATION_COUNT", 2)) # Number of consecutive successes to confirm recovery + +# Timezone configuration - Vietnam timezone (GMT+7) +LOCAL_TIMEZONE = timezone(timedelta(hours=int(os.getenv("TIMEZONE_OFFSET", 7)))) # Default GMT+7 for Vietnam diff --git a/health_check.py b/health_check.py index e5683d3..01c7c32 100644 --- a/health_check.py +++ b/health_check.py @@ -7,7 +7,7 @@ import time import socket import os -from config import HEALTH_CHECK_PORT, HEALTH_CHECK_ENABLED +from config import HEALTH_CHECK_PORT, HEALTH_CHECK_ENABLED, LOCAL_TIMEZONE class SimpleHealthHandler(BaseHTTPRequestHandler): def do_GET(self): @@ -34,7 +34,7 @@ class SimpleHealthHandler(BaseHTTPRequestHandler): try: health_data = { "status": "healthy", - "timestamp": datetime.now(timezone.utc).isoformat(), + "timestamp": datetime.now(LOCAL_TIMEZONE).isoformat(), "service": "poe-sensor", "version": "1.0.0" } @@ -82,7 +82,7 @@ class SimpleHealthHandler(BaseHTTPRequestHandler): error_data = { "error": "Sensor data unavailable", "message": str(e), - "timestamp": datetime.now(timezone.utc).isoformat() + "timestamp": datetime.now(LOCAL_TIMEZONE).isoformat() } response = json.dumps(error_data, indent=2) self.send_response(500) diff --git a/sensor_bridge.py b/sensor_bridge.py index 2fb6a8b..955d767 100644 --- a/sensor_bridge.py +++ b/sensor_bridge.py @@ -10,7 +10,7 @@ import paho.mqtt.client as mqtt from config import ( MODBUS_HOSTS, MODBUS_PORT, UNIT_ID, MQTT_BROKER, MQTT_PORT, MQTT_TOPIC, MQTT_CLIENT_ID, - MQTT_USERNAME, MQTT_PASSWORD, PUBLISH_INTERVAL + MQTT_USERNAME, MQTT_PASSWORD, PUBLISH_INTERVAL, LOCAL_TIMEZONE ) def on_connect(client, userdata, flags, rc): @@ -80,7 +80,7 @@ def read_and_publish_temperature_humidity(mqtt_client, modbus_client, host_info) # Publish data location = host_info["location"] sensor_type = "temperature-humidity" - current_time = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S") + current_time = datetime.now(LOCAL_TIMEZONE).strftime("%Y-%m-%d %H:%M:%S") base_topic = f"Location/{location}/{sensor_type}" @@ -142,7 +142,7 @@ def read_and_publish_cwt_co2(mqtt_client, modbus_client, host_info): # Publish data location = host_info["location"] sensor_type = "CO2-gas" - current_time = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S") + current_time = datetime.now(LOCAL_TIMEZONE).strftime("%Y-%m-%d %H:%M:%S") base_topic = f"Location/{location}/{sensor_type}" diff --git a/sensor_tracker.py b/sensor_tracker.py index 81e2414..0a33425 100644 --- a/sensor_tracker.py +++ b/sensor_tracker.py @@ -6,7 +6,7 @@ import logging from config import ( MODBUS_HOSTS, SENSOR_TIMEOUT_THRESHOLD, RECOVERY_CONFIRMATION_COUNT, - ALERTING_ENABLED + ALERTING_ENABLED, LOCAL_TIMEZONE ) class SensorTracker: @@ -37,7 +37,7 @@ class SensorTracker: def record_success(self, host_info, mqtt_client=None): """Record a successful sensor reading""" sensor_id = f"{host_info['ip']}_{host_info['location']}" - current_time = datetime.now(timezone.utc) + current_time = datetime.now(LOCAL_TIMEZONE) with self.lock: sensor = self.sensor_status[sensor_id] @@ -64,7 +64,7 @@ class SensorTracker: def record_failure(self, host_info, error_message, mqtt_client=None): """Record a failed sensor reading""" sensor_id = f"{host_info['ip']}_{host_info['location']}" - current_time = datetime.now(timezone.utc) + current_time = datetime.now(LOCAL_TIMEZONE) with self.lock: sensor = self.sensor_status[sensor_id] @@ -107,7 +107,7 @@ class SensorTracker: alert_message = { "alert_type": "sensor_failure", - "timestamp": datetime.now(timezone.utc).isoformat(), + "timestamp": datetime.now(LOCAL_TIMEZONE).isoformat(), "sensor_id": sensor_id, "sensor_ip": sensor["ip"], "sensor_location": sensor["location"], @@ -140,7 +140,7 @@ class SensorTracker: alert_message = { "alert_type": "sensor_recovery", - "timestamp": datetime.now(timezone.utc).isoformat(), + "timestamp": datetime.now(LOCAL_TIMEZONE).isoformat(), "sensor_id": sensor_id, "sensor_ip": sensor["ip"], "sensor_location": sensor["location"], @@ -168,7 +168,7 @@ class SensorTracker: # Create base topic path base_topic = f"Location/{location}/{sensor_type}" - current_time = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S") + current_time = datetime.now(LOCAL_TIMEZONE).strftime("%Y-%m-%d %H:%M:%S") # Publish offline status to individual topics topics_data = [ @@ -198,7 +198,7 @@ class SensorTracker: """Get status of all sensors""" with self.lock: return { - "timestamp": datetime.now(timezone.utc).isoformat(), + "timestamp": datetime.now(LOCAL_TIMEZONE).isoformat(), "total_sensors": len(self.sensor_status), "online_sensors": len([s for s in self.sensor_status.values() if s["status"] == "online"]), "offline_sensors": len([s for s in self.sensor_status.values() if s["status"] == "offline"]),