some code
This commit is contained in:
21
env
Normal file
21
env
Normal file
@ -0,0 +1,21 @@
|
||||
export MQTT_BROKER="172.23.1.102"
|
||||
export MQTT_PORT=1883
|
||||
export MQTT_BOXES='{
|
||||
"box1": {
|
||||
"label": "living_room"
|
||||
},
|
||||
"box2": {
|
||||
"label": "kitchen"
|
||||
}
|
||||
}'
|
||||
export MQTT_BOX_TOPIC_PREFIXES='{
|
||||
"window": "heating/sensor/window/",
|
||||
"high_temp": "heating/config/high_temp/",
|
||||
"cmd": "heating/command/"
|
||||
}'
|
||||
export MQTT_CENTRAL_TOPICS='{
|
||||
"general_off": "heating/system/general_off",
|
||||
"maintenance_mode": "heating/system/maintenance_mode"
|
||||
}'
|
||||
export MQTT_CLIENT_PREFIX="MyMQTTClient"
|
||||
export MQTT_PUBLISH_PREFIX="output/"
|
@ -1 +1,2 @@
|
||||
loguru==0.7.2
|
||||
paho-mqtt==2.1.0
|
||||
|
167
src/main.py
Normal file
167
src/main.py
Normal file
@ -0,0 +1,167 @@
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import uuid
|
||||
import signal
|
||||
from loguru import logger
|
||||
import paho.mqtt.client as mqtt
|
||||
from message_processor import process_message # Import the moved function
|
||||
|
||||
|
||||
# MQTT configuration parameters
|
||||
BROKER = os.getenv("MQTT_BROKER") # Read broker from environment variable
|
||||
PORT = int(os.getenv("MQTT_PORT", 1883)) # Default port if not set
|
||||
BOXES_CONFIG = os.getenv("MQTT_BOXES") # JSON configuration of boxes from environment variable
|
||||
BOX_TOPIC_PREFIXES_CONFIG = os.getenv("MQTT_BOX_TOPIC_PREFIXES") # JSON configuration of box-specific topic prefixes
|
||||
CENTRAL_TOPICS_CONFIG = os.getenv("MQTT_CENTRAL_TOPICS") # JSON configuration of central topics
|
||||
PUBLISH_PREFIX = os.getenv("MQTT_PUBLISH_PREFIX", "output/") # Globales Publish-Präfix
|
||||
|
||||
# Check if required environment variables are set
|
||||
missing_vars = []
|
||||
if not BROKER:
|
||||
missing_vars.append('MQTT_BROKER')
|
||||
if not BOXES_CONFIG:
|
||||
missing_vars.append('MQTT_BOXES')
|
||||
if not BOX_TOPIC_PREFIXES_CONFIG:
|
||||
missing_vars.append('MQTT_BOX_TOPIC_PREFIXES')
|
||||
if not CENTRAL_TOPICS_CONFIG:
|
||||
missing_vars.append('MQTT_CENTRAL_TOPICS')
|
||||
if not PUBLISH_PREFIX:
|
||||
missing_vars.append('MQTT_PUBLISH_PREFIX')
|
||||
|
||||
if missing_vars:
|
||||
logger.error(f"Error: The following environment variables are not set: {', '.join(missing_vars)}")
|
||||
sys.exit(1)
|
||||
|
||||
# context for box operations
|
||||
context = {}
|
||||
|
||||
# publish topic prefix for boxes
|
||||
context['publish'] = PUBLISH_PREFIX
|
||||
|
||||
# Load box configurations from JSON
|
||||
try:
|
||||
boxes = json.loads(BOXES_CONFIG)
|
||||
context['boxes'] = boxes
|
||||
except json.JSONDecodeError as e:
|
||||
logger.error(f"Error parsing JSON configuration for boxes: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
# Load box-specific topic prefixes from JSON
|
||||
try:
|
||||
box_topic_prefixes = json.loads(BOX_TOPIC_PREFIXES_CONFIG)
|
||||
except json.JSONDecodeError as e:
|
||||
logger.error(f"Error parsing JSON configuration for box-specific topic prefixes: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
# Load central topics from JSON
|
||||
try:
|
||||
central_topics = json.loads(CENTRAL_TOPICS_CONFIG)
|
||||
except json.JSONDecodeError as e:
|
||||
logger.error(f"Error parsing JSON configuration for central topics: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
# Generate CLIENT_ID from UUID and optional prefix
|
||||
CLIENT_PREFIX = os.getenv("MQTT_CLIENT_PREFIX", "MQTTClient")
|
||||
CLIENT_ID = f"{CLIENT_PREFIX}_{uuid.uuid4()}"
|
||||
|
||||
# Mapping of topics to boxes and topic keys for efficient lookup
|
||||
topic_mapping = {}
|
||||
|
||||
# Callback function for successful connection to the broker
|
||||
def on_connect(client, userdata, flags, rc):
|
||||
if rc == 0:
|
||||
logger.info("Connected to the broker")
|
||||
|
||||
# Subscribe to dynamically generated topics for each box and create mappings
|
||||
for box_name, config in boxes.items():
|
||||
label = config.get("label")
|
||||
if not label:
|
||||
logger.error(f"[{box_name}] No 'label' defined.")
|
||||
continue
|
||||
|
||||
# Generate topics based on configured box-specific prefixes and box label
|
||||
topics = {}
|
||||
for topic_key, prefix in box_topic_prefixes.items():
|
||||
topics[topic_key] = f"{prefix}{label}"
|
||||
config["subscribe"] = topics # Store the generated topics
|
||||
|
||||
# Subscribe to the topics for the box and create mapping
|
||||
for topic_key, topic in topics.items():
|
||||
client.subscribe(topic)
|
||||
topic_mapping[topic] = (box_name, topic_key)
|
||||
logger.info(f"[{box_name}] Subscribed to '{topic}' (Key: '{topic_key}')")
|
||||
|
||||
# Subscribe to central topics and create mappings
|
||||
for central_key, central_topic in central_topics.items():
|
||||
client.subscribe(central_topic)
|
||||
# Mark central topics with a special key to identify them
|
||||
topic_mapping[central_topic] = ("__central__", central_key)
|
||||
logger.info(f"Subscribed to central topic '{central_topic}' (Key: '{central_key}')")
|
||||
else:
|
||||
logger.error(f"Connection error with code {rc}")
|
||||
|
||||
# Callback function for received messages
|
||||
def on_message(client, userdata, msg):
|
||||
try:
|
||||
topic = msg.topic
|
||||
payload = msg.payload.decode()
|
||||
|
||||
if topic in topic_mapping:
|
||||
box_name, topic_key = topic_mapping[topic]
|
||||
if box_name == "__central__":
|
||||
# Central message, process for all boxes
|
||||
logger.info(f"[Central] Processing central message for '{topic_key}': {payload}")
|
||||
for current_box_name in boxes.keys():
|
||||
process_message(current_box_name, topic_key, payload, context)
|
||||
else:
|
||||
# Box-specific message
|
||||
process_message(box_name, topic_key, payload, context)
|
||||
else:
|
||||
logger.warning(f"Received unknown topic: '{topic}'")
|
||||
except Exception as e:
|
||||
logger.error(f"Error processing message from '{msg.topic}': {e}")
|
||||
|
||||
# Callback function for disconnection
|
||||
def on_disconnect(client, userdata, rc):
|
||||
if rc != 0:
|
||||
logger.warning("Unexpected disconnection, attempting to reconnect...")
|
||||
else:
|
||||
logger.info("Disconnected from the broker.")
|
||||
|
||||
# Signal handler for graceful shutdown
|
||||
def handle_exit_signal(signum, frame):
|
||||
logger.info("Shutting down the program due to external signal.")
|
||||
client.disconnect() # Disconnects from the broker and stops loop_forever()
|
||||
|
||||
# Initialize the MQTT client and configure callbacks
|
||||
client = mqtt.Client(client_id=CLIENT_ID)
|
||||
|
||||
context['client'] = client
|
||||
|
||||
client.on_connect = on_connect
|
||||
client.on_message = on_message
|
||||
client.on_disconnect = on_disconnect
|
||||
|
||||
# Optional: Set auto-reconnect parameters
|
||||
client.reconnect_delay_set(min_delay=1, max_delay=120)
|
||||
|
||||
# Register signal handlers for graceful shutdown
|
||||
signal.signal(signal.SIGTERM, handle_exit_signal)
|
||||
signal.signal(signal.SIGINT, handle_exit_signal)
|
||||
|
||||
# Connect to the broker
|
||||
try:
|
||||
client.connect(BROKER, PORT, keepalive=60)
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to connect to the broker: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
# Start the MQTT client loop in a blocking manner
|
||||
try:
|
||||
client.loop_forever()
|
||||
except KeyboardInterrupt:
|
||||
logger.info("Shutting down the program due to KeyboardInterrupt.")
|
||||
finally:
|
||||
client.disconnect() # Ensure the connection is closed
|
||||
logger.info("Program terminated.")
|
15
src/message_processor.py
Normal file
15
src/message_processor.py
Normal file
@ -0,0 +1,15 @@
|
||||
from loguru import logger
|
||||
|
||||
def process_message(box_name, topic_key, payload, context):
|
||||
try:
|
||||
logger.info(f"[{box_name}] Processing message for '{topic_key}': {payload}")
|
||||
|
||||
# Example processing: Implement your specific logic here
|
||||
# For example, perform calculations or store data.
|
||||
result = f"Processed by {box_name} for '{topic_key}': {payload}"
|
||||
|
||||
publish_topic = f"{context['publish']}/{box_name}"
|
||||
context['client'].publish(publish_topic, result)
|
||||
logger.info(f"[{box_name}] Result published on '{publish_topic}': {result}")
|
||||
except Exception as e:
|
||||
logger.error(f"[{box_name}] Error processing '{topic_key}': {e}")
|
Reference in New Issue
Block a user