From 545d9241113452780417c9ed3e9030cce0bbce27 Mon Sep 17 00:00:00 2001 From: Jens Ahrensfeld Date: Sat, 27 Jun 2026 08:40:53 +0200 Subject: [PATCH] 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 Claude-Session: https://claude.ai/code/session_01JLR2133M2PyjiYq584i2G7 --- tasks/stirrer.py | 2 +- web/app.js | 11 ++++++++--- web/style.css | 5 ++++- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/tasks/stirrer.py b/tasks/stirrer.py index 08bf3a2..08758c0 100644 --- a/tasks/stirrer.py +++ b/tasks/stirrer.py @@ -33,7 +33,7 @@ class StirrerTask(ATask): await self.msg_handler.send(data) 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_duty_cycle(1.0) diff --git a/web/app.js b/web/app.js index 4a2cd9b..f291098 100644 --- a/web/app.js +++ b/web/app.js @@ -38,6 +38,7 @@ let initialStirrerActivateSync = true; let ambientTemp = null; let warpFactor = 1.0; let heaterPowerIst = 0; +let heaterPowerSoll = 0; let heaterMaxPower = 100; // Sud state - mirrors the sud_* fields on client/brewpi_gui.py's Window. @@ -278,13 +279,16 @@ function updateStepPlates() { function updateStirrerVisualization() { const g = document.getElementById('stirrer-blades'); + const particles = document.getElementById('grain-particles'); if (!g) return; if (stirrerSpeedIst > 0) { const duration = Math.min(10, 50 / stirrerSpeedIst).toFixed(2); g.style.setProperty('--stir-duration', duration + 's'); g.classList.add('spinning'); + if (particles) particles.classList.add('stirring'); } else { 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 fireTip = 177; // y of tallest flame tip const fireH = fireBase - fireTip; // 71px total flame height - if (heaterPowerIst <= 0) { + if (heaterPowerSoll <= 0) { g.style.opacity = '0'; clipRect.setAttribute('height', '0'); } 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 const visibleH = (0.35 + 0.65 * intensity) * fireH; const topBuffer = 20; // headroom for flicker scaleY overshoot (~12% of height) @@ -513,7 +517,9 @@ function onHeaterChanged(msg) { initialHeaterActivateSync = false; } } else if (key === 'PowerSet') { + heaterPowerSoll = msg.PowerSet; document.getElementById('lcd-power-soll').textContent = msg.PowerSet; + updateFireVisualization(); } else if (key === 'Power') { heaterPowerIst = 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; initialPowerSync = false; } - updateFireVisualization(); } else if (key === 'Capabilities') { const power = msg.Capabilities.Power; heaterMaxPower = power.Max || 100; diff --git a/web/style.css b/web/style.css index 0ea5c32..72c7c3c 100644 --- a/web/style.css +++ b/web/style.css @@ -163,7 +163,9 @@ header#connection-bar { .grain-p { fill: #c8a060; 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; } /* Stirrer animation */ @@ -173,7 +175,8 @@ header#connection-bar { #stirrer-blades { transform-box: fill-box; 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 { animation-play-state: running;