Compare commits
5 Commits
groups_and
...
ui_fix_the
| Author | SHA1 | Date | |
|---|---|---|---|
|
2e24c259cb
|
|||
|
bbf280bdf4
|
|||
|
a7d778b211
|
|||
|
a7d8afc98b
|
|||
|
a4ae8a2f6c
|
@@ -246,6 +246,23 @@ class RedisState:
|
|||||||
|
|
||||||
await self._execute_with_retry(_expire, key, ttl_secs)
|
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:
|
async def close(self) -> None:
|
||||||
"""
|
"""
|
||||||
Close Redis connection and cleanup resources.
|
Close Redis connection and cleanup resources.
|
||||||
|
|||||||
@@ -365,32 +365,57 @@
|
|||||||
color: #999;
|
color: #999;
|
||||||
}
|
}
|
||||||
|
|
||||||
.temp-controls {
|
/* Thermostat Slider Styles */
|
||||||
display: flex;
|
.thermostat-slider-control {
|
||||||
gap: 0.5rem;
|
margin: 1rem 0;
|
||||||
margin-bottom: 1rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.temp-button {
|
.thermostat-slider-label {
|
||||||
flex: 1;
|
font-size: 0.875rem;
|
||||||
padding: 0.75rem;
|
color: #666;
|
||||||
border: none;
|
display: block;
|
||||||
border-radius: 8px;
|
margin-bottom: 0.5rem;
|
||||||
font-size: 1.125rem;
|
}
|
||||||
font-weight: 700;
|
|
||||||
|
.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;
|
cursor: pointer;
|
||||||
transition: all 0.2s;
|
|
||||||
background: #667eea;
|
|
||||||
color: white;
|
|
||||||
min-height: 44px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.temp-button:hover {
|
.thermostat-slider::-webkit-slider-thumb {
|
||||||
background: #5568d3;
|
-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 {
|
.thermostat-slider::-moz-range-thumb {
|
||||||
transform: scale(0.95);
|
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 */
|
/* Contact Sensor Styles */
|
||||||
@@ -398,6 +423,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.75rem;
|
gap: 0.75rem;
|
||||||
|
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
background: #f8f9fa;
|
background: #f8f9fa;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
@@ -965,13 +991,23 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="temp-controls">
|
<div class="thermostat-slider-control">
|
||||||
<button class="temp-button" onclick="adjustTarget('{{ device.device_id }}', -1.0)">
|
<label for="slider-{{ device.device_id }}" class="thermostat-slider-label">
|
||||||
-1.0
|
🎯 Zieltemperatur: <span id="thermostat-slider-value-{{ device.device_id }}">21.0</span>°C
|
||||||
</button>
|
</label>
|
||||||
<button class="temp-button" onclick="adjustTarget('{{ device.device_id }}', 1.0)">
|
<input type="range"
|
||||||
+1.0
|
min="5"
|
||||||
</button>
|
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>
|
</div>
|
||||||
|
|
||||||
{% elif device.type == "contact" or device.type == "contact_sensor" %}
|
{% elif device.type == "contact" or device.type == "contact_sensor" %}
|
||||||
@@ -1327,6 +1363,8 @@
|
|||||||
function updateThermostatUI(deviceId, current, target, mode) {
|
function updateThermostatUI(deviceId, current, target, mode) {
|
||||||
const currentSpan = document.getElementById(`state-${deviceId}-current`);
|
const currentSpan = document.getElementById(`state-${deviceId}-current`);
|
||||||
const targetSpan = document.getElementById(`state-${deviceId}-target`);
|
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) {
|
if (current !== undefined && currentSpan) {
|
||||||
currentSpan.textContent = current.toFixed(1);
|
currentSpan.textContent = current.toFixed(1);
|
||||||
@@ -1336,6 +1374,14 @@
|
|||||||
if (targetSpan) {
|
if (targetSpan) {
|
||||||
targetSpan.textContent = target.toFixed(1);
|
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;
|
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
|
// Execute group action
|
||||||
async function setGroup(groupId, power, buttonElement) {
|
async function setGroup(groupId, power, buttonElement) {
|
||||||
const allButtons = buttonElement.parentElement.querySelectorAll('button');
|
const allButtons = buttonElement.parentElement.querySelectorAll('button');
|
||||||
|
|||||||
Reference in New Issue
Block a user