brightness
This commit is contained in:
@@ -196,12 +196,15 @@
|
||||
<div class="device-card">
|
||||
<div class="device-header">
|
||||
<div class="device-name">💡 Test Lampe 1</div>
|
||||
<div class="device-type">Light</div>
|
||||
<div class="device-type">Light • Dimmbar</div>
|
||||
</div>
|
||||
|
||||
<div class="device-state">
|
||||
<span class="state-label">Status:</span>
|
||||
<span class="state-value off" id="state-test_lampe_1">off</span>
|
||||
<br>
|
||||
<span class="state-label">Helligkeit:</span>
|
||||
<span class="state-value" id="brightness-test_lampe_1">50</span>%
|
||||
</div>
|
||||
|
||||
<div class="controls">
|
||||
@@ -211,6 +214,21 @@
|
||||
onclick="toggleDevice('test_lampe_1')">
|
||||
Einschalten
|
||||
</button>
|
||||
|
||||
<div style="margin-top: 1rem;">
|
||||
<label for="brightness-slider-test_lampe_1" style="font-size: 0.875rem; color: #666;">
|
||||
Helligkeit: <span id="brightness-value-test_lampe_1">50</span>%
|
||||
</label>
|
||||
<input
|
||||
type="range"
|
||||
id="brightness-slider-test_lampe_1"
|
||||
min="0"
|
||||
max="100"
|
||||
value="50"
|
||||
style="width: 100%; margin-top: 0.5rem;"
|
||||
oninput="updateBrightnessValue('test_lampe_1', this.value)"
|
||||
onchange="setBrightness('test_lampe_1', this.value)">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="device-card">
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
|
||||
@@ -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 = {
|
||||
# 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()
|
||||
# Publish offline state for all devices before disconnecting
|
||||
for device_id in DEVICES:
|
||||
offline_state = device_states[device_id].copy()
|
||||
offline_state["power"] = "off"
|
||||
client_global.publish(STATE_TOPIC, json.dumps(offline_state), qos=1, retain=True)
|
||||
logger.info("Published offline state")
|
||||
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
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user