5 Commits

Author SHA1 Message Date
2e24c259cb fix 4 2025-11-17 08:42:26 +01:00
bbf280bdf4 fix 3 2025-11-17 08:39:50 +01:00
a7d778b211 fix 2 2025-11-17 08:37:02 +01:00
a7d8afc98b fix 2025-11-17 08:10:11 +01:00
a4ae8a2f6c slider for thermostats 2025-11-17 08:05:58 +01:00
2 changed files with 132 additions and 26 deletions

View File

@@ -246,6 +246,23 @@ class RedisState:
await self._execute_with_retry(_expire, key, ttl_secs)
async def delete(self, key: str) -> None:
"""
Delete a key from Redis.
Args:
key: Redis key to delete
Example:
>>> state = RedisState("redis://localhost:6379/0")
>>> await state.set("rules:r1:temp", "22.5")
>>> await state.delete("rules:r1:temp")
"""
async def _delete(client, k):
await client.delete(k)
await self._execute_with_retry(_delete, key)
async def close(self) -> None:
"""
Close Redis connection and cleanup resources.

View File

@@ -365,32 +365,57 @@
color: #999;
}
.temp-controls {
display: flex;
gap: 0.5rem;
margin-bottom: 1rem;
/* Thermostat Slider Styles */
.thermostat-slider-control {
margin: 1rem 0;
}
.temp-button {
flex: 1;
padding: 0.75rem;
border: none;
border-radius: 8px;
font-size: 1.125rem;
font-weight: 700;
.thermostat-slider-label {
font-size: 0.875rem;
color: #666;
display: block;
margin-bottom: 0.5rem;
}
.thermostat-slider {
width: 100%;
height: 8px;
border-radius: 4px;
background: linear-gradient(to right, #667eea 0%, #764ba2 100%);
outline: none;
-webkit-appearance: none;
appearance: none;
cursor: pointer;
transition: all 0.2s;
background: #667eea;
color: white;
min-height: 44px;
}
.temp-button:hover {
background: #5568d3;
.thermostat-slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 24px;
height: 24px;
border-radius: 50%;
background: white;
border: 3px solid #667eea;
cursor: pointer;
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
}
.temp-button:active {
transform: scale(0.95);
.thermostat-slider::-moz-range-thumb {
width: 24px;
height: 24px;
border-radius: 50%;
background: white;
border: 3px solid #667eea;
cursor: pointer;
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
}
.thermostat-slider-range {
display: flex;
justify-content: space-between;
margin-top: 0.25rem;
font-size: 0.75rem;
color: #999;
}
/* Contact Sensor Styles */
@@ -398,6 +423,7 @@
display: flex;
align-items: center;
gap: 0.75rem;
padding: 1rem;
background: #f8f9fa;
border-radius: 8px;
@@ -965,13 +991,23 @@
</div>
</div>
<div class="temp-controls">
<button class="temp-button" onclick="adjustTarget('{{ device.device_id }}', -1.0)">
-1.0
</button>
<button class="temp-button" onclick="adjustTarget('{{ device.device_id }}', 1.0)">
+1.0
</button>
<div class="thermostat-slider-control">
<label for="slider-{{ device.device_id }}" class="thermostat-slider-label">
🎯 Zieltemperatur: <span id="thermostat-slider-value-{{ device.device_id }}">21.0</span>°C
</label>
<input type="range"
min="5"
max="30"
step="0.5"
value="21.0"
class="thermostat-slider"
id="slider-{{ device.device_id }}"
oninput="updateThermostatSliderValue('{{ device.device_id }}', this.value)"
onchange="setThermostatTarget('{{ device.device_id }}', this.value)">
<div class="thermostat-slider-range">
<span>5°C</span>
<span>30°C</span>
</div>
</div>
{% elif device.type == "contact" or device.type == "contact_sensor" %}
@@ -1327,6 +1363,8 @@
function updateThermostatUI(deviceId, current, target, mode) {
const currentSpan = document.getElementById(`state-${deviceId}-current`);
const targetSpan = document.getElementById(`state-${deviceId}-target`);
const slider = document.getElementById(`slider-${deviceId}`);
const sliderValueSpan = document.getElementById(`thermostat-slider-value-${deviceId}`);
if (current !== undefined && currentSpan) {
currentSpan.textContent = current.toFixed(1);
@@ -1336,6 +1374,14 @@
if (targetSpan) {
targetSpan.textContent = target.toFixed(1);
}
// Sync slider with actual state
if (slider) {
slider.value = target;
}
// Sync slider value display
if (sliderValueSpan) {
sliderValueSpan.textContent = target.toFixed(1);
}
thermostatTargets[deviceId] = target;
}
}
@@ -1733,6 +1779,49 @@
}
}
// Update thermostat slider value display while dragging
function updateThermostatSliderValue(deviceId, value) {
const valueSpan = document.getElementById(`thermostat-slider-value-${deviceId}`);
if (valueSpan) {
valueSpan.textContent = parseFloat(value).toFixed(1);
}
}
// Set thermostat target temperature when slider is released
async function setThermostatTarget(deviceId, value) {
try {
const targetTemp = parseFloat(value);
const response = await fetch(api(`/devices/${deviceId}/set`), {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
type: 'thermostat',
payload: {
target: targetTemp
}
})
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.detail || 'Request failed');
}
console.log(`Thermostat ${deviceId} target set to ${targetTemp}°C`);
addEvent({
action: 'thermostat_set',
device_id: deviceId,
target_temperature: targetTemp
});
} catch (error) {
console.error('Failed to set thermostat target:', error);
showToast(`Fehler beim Setzen der Zieltemperatur: ${error.message}`, 'error');
}
}
// Execute group action
async function setGroup(groupId, power, buttonElement) {
const allButtons = buttonElement.parentElement.querySelectorAll('button');