zwei Lampen und Test-Werkzeug
This commit is contained in:
186
tools/sim_test_lampe.py
Normal file
186
tools/sim_test_lampe.py
Normal file
@@ -0,0 +1,186 @@
|
||||
#!/usr/bin/env python3
|
||||
"""MQTT Simulator for test_lampe device.
|
||||
|
||||
This simulator acts as a virtual light device that:
|
||||
- Subscribes to vendor/test_lampe/set
|
||||
- Maintains local state
|
||||
- Publishes state changes to vendor/test_lampe/state (retained)
|
||||
"""
|
||||
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import signal
|
||||
import sys
|
||||
import time
|
||||
|
||||
import paho.mqtt.client as mqtt
|
||||
|
||||
# Configure logging
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
|
||||
)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Configuration
|
||||
BROKER_HOST = os.environ.get("MQTT_HOST", "172.16.2.16")
|
||||
BROKER_PORT = int(os.environ.get("MQTT_PORT", "1883"))
|
||||
DEVICE_ID = "test_lampe_1"
|
||||
SET_TOPIC = f"vendor/{DEVICE_ID}/set"
|
||||
STATE_TOPIC = f"vendor/{DEVICE_ID}/state"
|
||||
|
||||
# Device state
|
||||
device_state = {
|
||||
"power": "off",
|
||||
"brightness": 50
|
||||
}
|
||||
|
||||
# Global client for signal handler
|
||||
client_global = None
|
||||
|
||||
|
||||
def on_connect(client, userdata, flags, rc, properties=None):
|
||||
"""Callback when connected to MQTT broker.
|
||||
|
||||
Args:
|
||||
client: MQTT client instance
|
||||
userdata: User data
|
||||
flags: Connection flags
|
||||
rc: Connection result code
|
||||
properties: Connection properties (MQTT v5)
|
||||
"""
|
||||
if rc == 0:
|
||||
logger.info(f"Connected to MQTT broker {BROKER_HOST}:{BROKER_PORT}")
|
||||
|
||||
# Subscribe to SET topic
|
||||
client.subscribe(SET_TOPIC, qos=1)
|
||||
logger.info(f"Subscribed to {SET_TOPIC}")
|
||||
|
||||
# Publish initial state (retained)
|
||||
publish_state(client)
|
||||
logger.info(f"Simulator started, initial state published: {device_state}")
|
||||
else:
|
||||
logger.error(f"Connection failed with code {rc}")
|
||||
|
||||
|
||||
def on_message(client, userdata, msg):
|
||||
"""Callback when message received on subscribed topic.
|
||||
|
||||
Args:
|
||||
client: MQTT client instance
|
||||
userdata: User data
|
||||
msg: MQTT message
|
||||
"""
|
||||
global device_state
|
||||
|
||||
try:
|
||||
payload = json.loads(msg.payload.decode())
|
||||
logger.info(f"Received SET command: {payload}")
|
||||
|
||||
# Update device state
|
||||
updated = False
|
||||
|
||||
if "power" in payload:
|
||||
old_power = device_state["power"]
|
||||
device_state["power"] = payload["power"]
|
||||
if old_power != device_state["power"]:
|
||||
updated = True
|
||||
logger.info(f"Power changed: {old_power} -> {device_state['power']}")
|
||||
|
||||
if "brightness" in payload:
|
||||
old_brightness = device_state["brightness"]
|
||||
device_state["brightness"] = int(payload["brightness"])
|
||||
if old_brightness != device_state["brightness"]:
|
||||
updated = True
|
||||
logger.info(f"Brightness changed: {old_brightness} -> {device_state['brightness']}")
|
||||
|
||||
# Publish updated state if changed
|
||||
if updated:
|
||||
publish_state(client)
|
||||
logger.info(f"Published new state: {device_state}")
|
||||
|
||||
except json.JSONDecodeError as e:
|
||||
logger.error(f"Invalid JSON in message: {e}")
|
||||
except Exception as e:
|
||||
logger.error(f"Error processing message: {e}")
|
||||
|
||||
|
||||
def publish_state(client):
|
||||
"""Publish current device state to STATE topic.
|
||||
|
||||
Args:
|
||||
client: MQTT client instance
|
||||
"""
|
||||
state_json = json.dumps(device_state)
|
||||
result = client.publish(STATE_TOPIC, state_json, qos=1, retain=True)
|
||||
|
||||
if result.rc == mqtt.MQTT_ERR_SUCCESS:
|
||||
logger.debug(f"Published state to {STATE_TOPIC}: {state_json}")
|
||||
else:
|
||||
logger.error(f"Failed to publish state: {result.rc}")
|
||||
|
||||
|
||||
def signal_handler(sig, frame):
|
||||
"""Handle shutdown signals gracefully.
|
||||
|
||||
Args:
|
||||
sig: Signal number
|
||||
frame: Current stack frame
|
||||
"""
|
||||
logger.info(f"Received signal {sig}, shutting down...")
|
||||
|
||||
if client_global:
|
||||
# Publish offline state before disconnecting
|
||||
offline_state = device_state.copy()
|
||||
offline_state["power"] = "off"
|
||||
client_global.publish(STATE_TOPIC, json.dumps(offline_state), qos=1, retain=True)
|
||||
logger.info("Published offline state")
|
||||
|
||||
client_global.disconnect()
|
||||
client_global.loop_stop()
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
def main():
|
||||
"""Main entry point for the simulator."""
|
||||
global client_global
|
||||
|
||||
# Setup signal handlers
|
||||
signal.signal(signal.SIGINT, signal_handler)
|
||||
signal.signal(signal.SIGTERM, signal_handler)
|
||||
|
||||
# Create MQTT client
|
||||
client = mqtt.Client(
|
||||
client_id=f"simulator-{DEVICE_ID}",
|
||||
protocol=mqtt.MQTTv5,
|
||||
callback_api_version=mqtt.CallbackAPIVersion.VERSION2
|
||||
)
|
||||
client_global = client
|
||||
|
||||
# Set callbacks
|
||||
client.on_connect = on_connect
|
||||
client.on_message = on_message
|
||||
|
||||
# Connect to broker
|
||||
logger.info(f"Connecting to MQTT broker {BROKER_HOST}:{BROKER_PORT}...")
|
||||
|
||||
try:
|
||||
client.connect(BROKER_HOST, BROKER_PORT, keepalive=60)
|
||||
|
||||
# Start network loop
|
||||
client.loop_forever()
|
||||
|
||||
except KeyboardInterrupt:
|
||||
logger.info("Interrupted by user")
|
||||
except Exception as e:
|
||||
logger.error(f"Error: {e}")
|
||||
finally:
|
||||
if client.is_connected():
|
||||
client.disconnect()
|
||||
client.loop_stop()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user