diff --git a/apps/ui/templates/index.html b/apps/ui/templates/index.html
index 2288fc1..338f97b 100644
--- a/apps/ui/templates/index.html
+++ b/apps/ui/templates/index.html
@@ -196,12 +196,15 @@
Status:
off
+
+ Helligkeit:
+ 50%
@@ -282,8 +300,45 @@
}
}
+ // Update brightness value display
+ function updateBrightnessValue(deviceId, value) {
+ const valueSpan = document.getElementById(`brightness-value-${deviceId}`);
+ if (valueSpan) {
+ valueSpan.textContent = value;
+ }
+ }
+
+ // Set brightness
+ async function setBrightness(deviceId, brightness) {
+ try {
+ const response = await fetch(`${API_BASE}/devices/${deviceId}/set`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify({
+ type: 'light',
+ payload: {
+ brightness: parseInt(brightness)
+ }
+ })
+ });
+
+ if (response.ok) {
+ console.log(`Sent brightness ${brightness} to ${deviceId}`);
+ addEvent({
+ action: 'brightness_set',
+ device_id: deviceId,
+ brightness: parseInt(brightness)
+ });
+ }
+ } catch (error) {
+ console.error('Failed to set brightness:', error);
+ }
+ }
+
// Update device UI
- function updateDeviceUI(deviceId, power) {
+ function updateDeviceUI(deviceId, power, brightness) {
currentState[deviceId] = power;
const stateSpan = document.getElementById(`state-${deviceId}`);
@@ -303,6 +358,23 @@
toggleButton.className = 'toggle-button off';
}
}
+
+ // Update brightness display and slider
+ if (brightness !== undefined) {
+ const brightnessSpan = document.getElementById(`brightness-${deviceId}`);
+ const brightnessValue = document.getElementById(`brightness-value-${deviceId}`);
+ const brightnessSlider = document.getElementById(`brightness-slider-${deviceId}`);
+
+ if (brightnessSpan) {
+ brightnessSpan.textContent = brightness;
+ }
+ if (brightnessValue) {
+ brightnessValue.textContent = brightness;
+ }
+ if (brightnessSlider) {
+ brightnessSlider.value = brightness;
+ }
+ }
}
// Add event to list
@@ -349,8 +421,12 @@
// Update device state
if (data.type === 'state' && data.device_id) {
- if (data.payload && data.payload.power) {
- updateDeviceUI(data.device_id, data.payload.power);
+ if (data.payload) {
+ updateDeviceUI(
+ data.device_id,
+ data.payload.power,
+ data.payload.brightness
+ );
}
}
});
diff --git a/config/devices.yaml b/config/devices.yaml
index 7fd0270..723ee7f 100644
--- a/config/devices.yaml
+++ b/config/devices.yaml
@@ -19,6 +19,7 @@ devices:
technology: zigbee2mqtt
features:
power: true
+ brightness: true
topics:
set: "vendor/test_lampe_1/set"
state: "vendor/test_lampe_1/state"
diff --git a/packages/home_capabilities/light.py b/packages/home_capabilities/light.py
index bf25c8e..cc2352d 100644
--- a/packages/home_capabilities/light.py
+++ b/packages/home_capabilities/light.py
@@ -18,8 +18,8 @@ class LightState(BaseModel):
color: Optional hex color string in format "#RRGGBB"
"""
- power: Literal["on", "off"] = Field(
- ...,
+ power: Optional[Literal["on", "off"]] = Field(
+ None,
description="Power state of the light"
)
diff --git a/tools/sim_test_lampe.py b/tools/sim_test_lampe.py
index b889de3..db65543 100644
--- a/tools/sim_test_lampe.py
+++ b/tools/sim_test_lampe.py
@@ -1,10 +1,10 @@
#!/usr/bin/env python3
-"""MQTT Simulator for test_lampe device.
+"""MQTT Simulator for multiple test_lampe devices.
-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)
+This simulator acts as virtual light devices that:
+- Subscribe to vendor/test_lampe_*/set
+- Maintain local state for each device
+- Publish state changes to vendor/test_lampe_*/state (retained)
"""
import json
@@ -26,14 +26,20 @@ 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
+# Devices to simulate
+DEVICES = ["test_lampe_1", "test_lampe_2"]
+
+# Device states (one per device)
+device_states = {
+ "test_lampe_1": {
+ "power": "off",
+ "brightness": 50
+ },
+ "test_lampe_2": {
+ "power": "off",
+ "brightness": 50
+ }
}
# Global client for signal handler
@@ -53,13 +59,16 @@ def on_connect(client, userdata, flags, rc, properties=None):
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}")
+ # Subscribe to SET topics for all devices
+ for device_id in DEVICES:
+ set_topic = f"vendor/{device_id}/set"
+ 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}")
+ # Publish initial states (retained)
+ for device_id in DEVICES:
+ publish_state(client, device_id)
+ logger.info(f"Simulator started for {device_id}, initial state: {device_states[device_id]}")
else:
logger.error(f"Connection failed with code {rc}")
@@ -72,53 +81,67 @@ def on_message(client, userdata, msg):
userdata: User data
msg: MQTT message
"""
- global device_state
+ # Extract device_id from topic (vendor/test_lampe_X/set)
+ topic_parts = msg.topic.split('/')
+ if len(topic_parts) != 3 or topic_parts[0] != "vendor" or topic_parts[2] != "set":
+ logger.warning(f"Unexpected topic format: {msg.topic}")
+ return
+
+ device_id = topic_parts[1]
+
+ if device_id not in device_states:
+ logger.warning(f"Unknown device: {device_id}")
+ return
try:
payload = json.loads(msg.payload.decode())
- logger.info(f"Received SET command: {payload}")
+ logger.info(f"[{device_id}] Received SET command: {payload}")
# Update device state
updated = False
+ device_state = device_states[device_id]
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']}")
+ logger.info(f"[{device_id}] 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']}")
+ logger.info(f"[{device_id}] 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}")
+ publish_state(client, device_id)
+ logger.info(f"[{device_id}] Published new state: {device_state}")
except json.JSONDecodeError as e:
- logger.error(f"Invalid JSON in message: {e}")
+ logger.error(f"[{device_id}] Invalid JSON in message: {e}")
except Exception as e:
- logger.error(f"Error processing message: {e}")
+ logger.error(f"[{device_id}] Error processing message: {e}")
-def publish_state(client):
+def publish_state(client, device_id):
"""Publish current device state to STATE topic.
Args:
client: MQTT client instance
+ device_id: Device identifier
"""
+ device_state = device_states[device_id]
+ state_topic = f"vendor/{device_id}/state"
state_json = json.dumps(device_state)
- result = client.publish(STATE_TOPIC, state_json, qos=1, retain=True)
+ 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}")
+ logger.debug(f"[{device_id}] Published state to {state_topic}: {state_json}")
else:
- logger.error(f"Failed to publish state: {result.rc}")
+ logger.error(f"[{device_id}] Failed to publish state: {result.rc}")
def signal_handler(sig, frame):
@@ -131,11 +154,13 @@ def signal_handler(sig, 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")
+ # Publish offline state for all devices before disconnecting
+ for device_id in DEVICES:
+ offline_state = device_states[device_id].copy()
+ offline_state["power"] = "off"
+ state_topic = f"vendor/{device_id}/state"
+ client_global.publish(state_topic, json.dumps(offline_state), qos=1, retain=True)
+ logger.info(f"[{device_id}] Published offline state")
client_global.disconnect()
client_global.loop_stop()
@@ -153,7 +178,7 @@ def main():
# Create MQTT client
client = mqtt.Client(
- client_id=f"simulator-{DEVICE_ID}",
+ client_id="simulator-test-lampes",
protocol=mqtt.MQTTv5,
callback_api_version=mqtt.CallbackAPIVersion.VERSION2
)