Fix animations: stirrer/grains static at rest, fire uses power soll

- Stirrer starts at speed 0 (was 50) so blades don't spin on connect
- Grain particles only levitate when stirrer is running
- Fire size driven by PowerSet (soll) instead of Power (ist)
- Split animation-play-state out of stirrer shorthand for reliable pausing

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JLR2133M2PyjiYq584i2G7
This commit is contained in:
2026-06-27 08:40:53 +02:00
co-authored by Claude Sonnet 4.6
parent c92c41230d
commit 545d924111
3 changed files with 13 additions and 5 deletions
+1 -1
View File
@@ -33,7 +33,7 @@ class StirrerTask(ATask):
await self.msg_handler.send(data) await self.msg_handler.send(data)
async def on_process(self): async def on_process(self):
self.device.set_speed(50.0) self.device.set_speed(0.0)
self.device.set_cycle_time(10.0) self.device.set_cycle_time(10.0)
self.device.set_duty_cycle(1.0) self.device.set_duty_cycle(1.0)
+8 -3
View File
@@ -38,6 +38,7 @@ let initialStirrerActivateSync = true;
let ambientTemp = null; let ambientTemp = null;
let warpFactor = 1.0; let warpFactor = 1.0;
let heaterPowerIst = 0; let heaterPowerIst = 0;
let heaterPowerSoll = 0;
let heaterMaxPower = 100; let heaterMaxPower = 100;
// Sud state - mirrors the sud_* fields on client/brewpi_gui.py's Window. // Sud state - mirrors the sud_* fields on client/brewpi_gui.py's Window.
@@ -278,13 +279,16 @@ function updateStepPlates() {
function updateStirrerVisualization() { function updateStirrerVisualization() {
const g = document.getElementById('stirrer-blades'); const g = document.getElementById('stirrer-blades');
const particles = document.getElementById('grain-particles');
if (!g) return; if (!g) return;
if (stirrerSpeedIst > 0) { if (stirrerSpeedIst > 0) {
const duration = Math.min(10, 50 / stirrerSpeedIst).toFixed(2); const duration = Math.min(10, 50 / stirrerSpeedIst).toFixed(2);
g.style.setProperty('--stir-duration', duration + 's'); g.style.setProperty('--stir-duration', duration + 's');
g.classList.add('spinning'); g.classList.add('spinning');
if (particles) particles.classList.add('stirring');
} else { } else {
g.classList.remove('spinning'); g.classList.remove('spinning');
if (particles) particles.classList.remove('stirring');
} }
} }
@@ -297,11 +301,11 @@ function updateFireVisualization() {
const fireBase = 248; // y of flame base (all paths end here) const fireBase = 248; // y of flame base (all paths end here)
const fireTip = 177; // y of tallest flame tip const fireTip = 177; // y of tallest flame tip
const fireH = fireBase - fireTip; // 71px total flame height const fireH = fireBase - fireTip; // 71px total flame height
if (heaterPowerIst <= 0) { if (heaterPowerSoll <= 0) {
g.style.opacity = '0'; g.style.opacity = '0';
clipRect.setAttribute('height', '0'); clipRect.setAttribute('height', '0');
} else { } else {
const intensity = Math.min(1, heaterPowerIst / heaterMaxPower); const intensity = Math.min(1, heaterPowerSoll / heaterMaxPower);
// Always show the lower 35% (base), scale upper 65% with intensity // Always show the lower 35% (base), scale upper 65% with intensity
const visibleH = (0.35 + 0.65 * intensity) * fireH; const visibleH = (0.35 + 0.65 * intensity) * fireH;
const topBuffer = 20; // headroom for flicker scaleY overshoot (~12% of height) const topBuffer = 20; // headroom for flicker scaleY overshoot (~12% of height)
@@ -513,7 +517,9 @@ function onHeaterChanged(msg) {
initialHeaterActivateSync = false; initialHeaterActivateSync = false;
} }
} else if (key === 'PowerSet') { } else if (key === 'PowerSet') {
heaterPowerSoll = msg.PowerSet;
document.getElementById('lcd-power-soll').textContent = msg.PowerSet; document.getElementById('lcd-power-soll').textContent = msg.PowerSet;
updateFireVisualization();
} else if (key === 'Power') { } else if (key === 'Power') {
heaterPowerIst = msg.Power; heaterPowerIst = msg.Power;
document.getElementById('lcd-power-ist').textContent = msg.Power; document.getElementById('lcd-power-ist').textContent = msg.Power;
@@ -522,7 +528,6 @@ function onHeaterChanged(msg) {
document.getElementById('heater-power-readout').textContent = msg.Power; document.getElementById('heater-power-readout').textContent = msg.Power;
initialPowerSync = false; initialPowerSync = false;
} }
updateFireVisualization();
} else if (key === 'Capabilities') { } else if (key === 'Capabilities') {
const power = msg.Capabilities.Power; const power = msg.Capabilities.Power;
heaterMaxPower = power.Max || 100; heaterMaxPower = power.Max || 100;
+4 -1
View File
@@ -163,7 +163,9 @@ header#connection-bar {
.grain-p { .grain-p {
fill: #c8a060; fill: #c8a060;
animation: float-p var(--fp-dur, 3s) ease-in-out infinite var(--fp-del, 0s); animation: float-p var(--fp-dur, 3s) ease-in-out infinite var(--fp-del, 0s);
animation-play-state: paused;
} }
#grain-particles.stirring .grain-p { animation-play-state: running; }
#grain-particles { transition: opacity 0.5s; } #grain-particles { transition: opacity 0.5s; }
/* Stirrer animation */ /* Stirrer animation */
@@ -173,7 +175,8 @@ header#connection-bar {
#stirrer-blades { #stirrer-blades {
transform-box: fill-box; transform-box: fill-box;
transform-origin: 50% 50%; transform-origin: 50% 50%;
animation: stir-rotate var(--stir-duration, 1s) linear infinite paused; animation: stir-rotate var(--stir-duration, 1s) linear infinite;
animation-play-state: paused;
} }
#stirrer-blades.spinning { #stirrer-blades.spinning {
animation-play-state: running; animation-play-state: running;