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.
This commit is contained in:
@ -141,6 +141,7 @@ The service supports environment variable configuration:
|
|||||||
| `ALERTING_ENABLED` | Enable MQTT alerts | true |
|
| `ALERTING_ENABLED` | Enable MQTT alerts | true |
|
||||||
| `SENSOR_TIMEOUT_THRESHOLD` | Failures before alert | 3 |
|
| `SENSOR_TIMEOUT_THRESHOLD` | Failures before alert | 3 |
|
||||||
| `RECOVERY_CONFIRMATION_COUNT` | Successes to confirm recovery | 2 |
|
| `RECOVERY_CONFIRMATION_COUNT` | Successes to confirm recovery | 2 |
|
||||||
|
| `TIMEZONE_OFFSET` | Timezone offset from UTC (hours) | 7 (GMT+7) |
|
||||||
|
|
||||||
## 🏥 Health Check & Monitoring
|
## 🏥 Health Check & Monitoring
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import time
|
import time
|
||||||
import os
|
import os
|
||||||
|
from datetime import timezone, timedelta
|
||||||
|
|
||||||
# Modbus configuration
|
# Modbus configuration
|
||||||
MODBUS_HOSTS = [
|
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"]
|
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
|
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
|
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
|
||||||
|
@ -7,7 +7,7 @@ import time
|
|||||||
import socket
|
import socket
|
||||||
import os
|
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):
|
class SimpleHealthHandler(BaseHTTPRequestHandler):
|
||||||
def do_GET(self):
|
def do_GET(self):
|
||||||
@ -34,7 +34,7 @@ class SimpleHealthHandler(BaseHTTPRequestHandler):
|
|||||||
try:
|
try:
|
||||||
health_data = {
|
health_data = {
|
||||||
"status": "healthy",
|
"status": "healthy",
|
||||||
"timestamp": datetime.now(timezone.utc).isoformat(),
|
"timestamp": datetime.now(LOCAL_TIMEZONE).isoformat(),
|
||||||
"service": "poe-sensor",
|
"service": "poe-sensor",
|
||||||
"version": "1.0.0"
|
"version": "1.0.0"
|
||||||
}
|
}
|
||||||
@ -82,7 +82,7 @@ class SimpleHealthHandler(BaseHTTPRequestHandler):
|
|||||||
error_data = {
|
error_data = {
|
||||||
"error": "Sensor data unavailable",
|
"error": "Sensor data unavailable",
|
||||||
"message": str(e),
|
"message": str(e),
|
||||||
"timestamp": datetime.now(timezone.utc).isoformat()
|
"timestamp": datetime.now(LOCAL_TIMEZONE).isoformat()
|
||||||
}
|
}
|
||||||
response = json.dumps(error_data, indent=2)
|
response = json.dumps(error_data, indent=2)
|
||||||
self.send_response(500)
|
self.send_response(500)
|
||||||
|
@ -10,7 +10,7 @@ import paho.mqtt.client as mqtt
|
|||||||
from config import (
|
from config import (
|
||||||
MODBUS_HOSTS, MODBUS_PORT, UNIT_ID,
|
MODBUS_HOSTS, MODBUS_PORT, UNIT_ID,
|
||||||
MQTT_BROKER, MQTT_PORT, MQTT_TOPIC, MQTT_CLIENT_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):
|
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
|
# Publish data
|
||||||
location = host_info["location"]
|
location = host_info["location"]
|
||||||
sensor_type = "temperature-humidity"
|
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}"
|
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
|
# Publish data
|
||||||
location = host_info["location"]
|
location = host_info["location"]
|
||||||
sensor_type = "CO2-gas"
|
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}"
|
base_topic = f"Location/{location}/{sensor_type}"
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ import logging
|
|||||||
|
|
||||||
from config import (
|
from config import (
|
||||||
MODBUS_HOSTS, SENSOR_TIMEOUT_THRESHOLD, RECOVERY_CONFIRMATION_COUNT,
|
MODBUS_HOSTS, SENSOR_TIMEOUT_THRESHOLD, RECOVERY_CONFIRMATION_COUNT,
|
||||||
ALERTING_ENABLED
|
ALERTING_ENABLED, LOCAL_TIMEZONE
|
||||||
)
|
)
|
||||||
|
|
||||||
class SensorTracker:
|
class SensorTracker:
|
||||||
@ -37,7 +37,7 @@ class SensorTracker:
|
|||||||
def record_success(self, host_info, mqtt_client=None):
|
def record_success(self, host_info, mqtt_client=None):
|
||||||
"""Record a successful sensor reading"""
|
"""Record a successful sensor reading"""
|
||||||
sensor_id = f"{host_info['ip']}_{host_info['location']}"
|
sensor_id = f"{host_info['ip']}_{host_info['location']}"
|
||||||
current_time = datetime.now(timezone.utc)
|
current_time = datetime.now(LOCAL_TIMEZONE)
|
||||||
|
|
||||||
with self.lock:
|
with self.lock:
|
||||||
sensor = self.sensor_status[sensor_id]
|
sensor = self.sensor_status[sensor_id]
|
||||||
@ -64,7 +64,7 @@ class SensorTracker:
|
|||||||
def record_failure(self, host_info, error_message, mqtt_client=None):
|
def record_failure(self, host_info, error_message, mqtt_client=None):
|
||||||
"""Record a failed sensor reading"""
|
"""Record a failed sensor reading"""
|
||||||
sensor_id = f"{host_info['ip']}_{host_info['location']}"
|
sensor_id = f"{host_info['ip']}_{host_info['location']}"
|
||||||
current_time = datetime.now(timezone.utc)
|
current_time = datetime.now(LOCAL_TIMEZONE)
|
||||||
|
|
||||||
with self.lock:
|
with self.lock:
|
||||||
sensor = self.sensor_status[sensor_id]
|
sensor = self.sensor_status[sensor_id]
|
||||||
@ -107,7 +107,7 @@ class SensorTracker:
|
|||||||
|
|
||||||
alert_message = {
|
alert_message = {
|
||||||
"alert_type": "sensor_failure",
|
"alert_type": "sensor_failure",
|
||||||
"timestamp": datetime.now(timezone.utc).isoformat(),
|
"timestamp": datetime.now(LOCAL_TIMEZONE).isoformat(),
|
||||||
"sensor_id": sensor_id,
|
"sensor_id": sensor_id,
|
||||||
"sensor_ip": sensor["ip"],
|
"sensor_ip": sensor["ip"],
|
||||||
"sensor_location": sensor["location"],
|
"sensor_location": sensor["location"],
|
||||||
@ -140,7 +140,7 @@ class SensorTracker:
|
|||||||
|
|
||||||
alert_message = {
|
alert_message = {
|
||||||
"alert_type": "sensor_recovery",
|
"alert_type": "sensor_recovery",
|
||||||
"timestamp": datetime.now(timezone.utc).isoformat(),
|
"timestamp": datetime.now(LOCAL_TIMEZONE).isoformat(),
|
||||||
"sensor_id": sensor_id,
|
"sensor_id": sensor_id,
|
||||||
"sensor_ip": sensor["ip"],
|
"sensor_ip": sensor["ip"],
|
||||||
"sensor_location": sensor["location"],
|
"sensor_location": sensor["location"],
|
||||||
@ -168,7 +168,7 @@ class SensorTracker:
|
|||||||
|
|
||||||
# Create base topic path
|
# Create base topic path
|
||||||
base_topic = f"Location/{location}/{sensor_type}"
|
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
|
# Publish offline status to individual topics
|
||||||
topics_data = [
|
topics_data = [
|
||||||
@ -198,7 +198,7 @@ class SensorTracker:
|
|||||||
"""Get status of all sensors"""
|
"""Get status of all sensors"""
|
||||||
with self.lock:
|
with self.lock:
|
||||||
return {
|
return {
|
||||||
"timestamp": datetime.now(timezone.utc).isoformat(),
|
"timestamp": datetime.now(LOCAL_TIMEZONE).isoformat(),
|
||||||
"total_sensors": len(self.sensor_status),
|
"total_sensors": len(self.sensor_status),
|
||||||
"online_sensors": len([s for s in self.sensor_status.values() if s["status"] == "online"]),
|
"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"]),
|
"offline_sensors": len([s for s in self.sensor_status.values() if s["status"] == "offline"]),
|
||||||
|
Reference in New Issue
Block a user