Commit Graph
463 Commits
Author SHA1 Message Date
jensandClaude Sonnet 4.6 95bc7dbaf0 Move New/Load/Save below host input; double transport button size
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SH2D4LRuCnhLSzoYerAfJX
2026-06-28 12:56:22 +02:00
jensandClaude Sonnet 4.6 6640d99e4f README: refresh docs for heater shutdown, resume fix, browser countdown
- HeaterTask.shutdown() on DONE/IDLE: document the _on_end callback,
  power-zero, TC disable, and ClosedLoop:false broadcast to clients.
- Resume with inherited temperature: document _paused_temp_soll fallback
  for steps that carry no temperature of their own.
- Browser client additional features: update Header countdown (now three
  rows: total/ramp/hold, always h:mm:ss) and describe the step-plate
  Ramp/Hold green countdown and configured hold-duration field.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SH2D4LRuCnhLSzoYerAfJX
2026-06-28 12:36:24 +02:00
jensandClaude Sonnet 4.6 e43d47252e Progress panes: replace live actual temp with configured hold duration
Each step plate now shows the scheduled hold time ("Hold h:mm:ss") as a
static field instead of the live actual temperature. The value is set once
at plate-creation time from the step's hold.duration; steps without a hold
phase show "Hold —".

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SH2D4LRuCnhLSzoYerAfJX
2026-06-28 12:34:30 +02:00
jensandClaude Sonnet 4.6 fd4cede19b Progress pane: ramp/hold countdown with green digits; header ramp+hold rows
Step plates now show "Ramp h:mm:ss" (ramp time only) during ramp phase
and "Hold h:mm:ss" (hold remaining) during hold phase, both in the same
digit area in green (#4ade80). Inactive steps show planned ramp duration.

Header countdown gains a "hold" row alongside the existing "ramp" (renamed
from "step") and "total" rows. All three use the same h:mm:ss format.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SH2D4LRuCnhLSzoYerAfJX
2026-06-28 12:30:23 +02:00
jensandClaude Sonnet 4.6 6d481d7764 Shut heater off on brew end (DONE) and stop (IDLE)
Adds HeaterTask.shutdown() which switches to open-loop at power 0,
disables TC, and broadcasts ClosedLoop=false to clients so the browser
checkbox unchecks immediately. SudTask fires it via a new set_on_end()
callback whenever state reaches DONE or IDLE, giving both a completed
schedule and a manual Stop the same safe behaviour: stirrer and heater
both off, no heater left running unattended.

Re-enabling after shutdown: check the closed-loop checkbox (sends
ClosedLoop=true → TC re-enabled) or adjust the power slider directly;
the next brew's Play press already sends ClosedLoop=true automatically.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SH2D4LRuCnhLSzoYerAfJX
2026-06-28 12:07:21 +02:00
jensandClaude Sonnet 4.6 b54cda2f97 Browser GUI: swap total/step countdown order; always use h:mm:ss format
Total remaining now appears above step remaining in the header. Both
countdowns and all other duration displays always use h:mm:ss format
regardless of whether hours is zero, so the layout never shifts width.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SH2D4LRuCnhLSzoYerAfJX
2026-06-28 12:00:11 +02:00
jensandClaude Sonnet 4.6 8714a140bb Fix resume not resetting temp soll for steps that inherit temperature
Steps with no 'temperature' field inherit their target from a prior step
(temperature=None in the resolved schedule). The resume-from-pause code
only called set_theta_soll when the current step had an explicit temperature,
so any manual setpoint change made during pause was never reverted for
inherited steps (e.g. sud_0010 steps 2-3 both hold at step 1's 57 °C).

Fix: save theta_soll_set at pause time; use it as fallback on resume when
the current step's temperature is None. Also set initialTempSollSync=true
in the browser on play so the slider accepts the next server Soll push
regardless of message arrival order relative to the State update.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SH2D4LRuCnhLSzoYerAfJX
2026-06-28 11:49:00 +02:00
jensandClaude Sonnet 4.6 6d65d11856 README: refresh docs; highlight server-owns-state design principle
- Rename "State replay on connect" to "State is server-only; GUIs only
  reflect it" and expand it with the core design principle: neither GUI
  client ever maintains its own model of server state — controls update
  only on server push, never speculatively on user input. Multi-client
  sync, stale-overwrite safety, and reconnect correctness all follow from
  this single rule.

- Update TC-enable description: HeaterTask now owns the TC's enabled
  switch via the closed_loop flag; SudTask no longer touches it.

- Add "Heater control modes" subsection describing Closed-loop/Open-loop,
  the enable/disable rules per mode and sud state, the auto-reset on play,
  and the schedule parameter restore on resume.

- Refresh the Browser client section: document the Heater control mode
  checkbox, header countdown timers, and the always-reflect-server-values
  slider policy; call out the server-only-state principle explicitly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SH2D4LRuCnhLSzoYerAfJX
2026-06-28 10:41:31 +02:00
jensandClaude Sonnet 4.6 05358e7c4c Reset TC setpoints and stirrer to schedule on resume from pause
On resume from PAUSED, on_step_changed does not re-fire (same step
continues), so any manual TC/stirrer adjustments made while paused
would persist into the run. Explicitly re-apply the current step's
temperature, heatrate, and stirrer config on the resume path in
SudTask.recv().

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SH2D4LRuCnhLSzoYerAfJX
2026-06-28 10:36:26 +02:00
jensandClaude Sonnet 4.6 35e50f5644 Disable TC sliders during active run in closed-loop mode
Temp-soll and heatrate-soll are only editable when closed-loop AND
sud is paused or stopped; during a running sud the schedule owns those
setpoints.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SH2D4LRuCnhLSzoYerAfJX
2026-06-28 10:31:20 +02:00
jensandClaude Sonnet 4.6 f55d085ae4 Add Closed-loop/Open-loop heater control mode
Closed-loop (default, checkbox checked): TC has exclusive control over
the heater; temp-soll and heatrate-soll sliders are active; direct
heater power entry is disabled. TC now stays enabled at server startup
and whenever closed-loop mode is active — SudTask no longer disables it
on IDLE/DONE, so adjusting temp-soll manually with no sud running
immediately drives the heater via the TC.

Open-loop (checkbox unchecked, only selectable when paused/stopped):
TC is disconnected from the heater; only the heater-power slider is
active. Switching back to closed-loop resets power_soll to 0 and
re-enables the TC.

On play, the mode is automatically reset to closed-loop.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SH2D4LRuCnhLSzoYerAfJX
2026-06-28 10:29:30 +02:00
jensandClaude Sonnet 4.6 956f32e6ea Browser GUI: heater manual slider tracks PowerSet (soll), not Power (ist)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SH2D4LRuCnhLSzoYerAfJX
2026-06-28 09:37:12 +02:00
jensandClaude Sonnet 4.6 a500bc559e Browser GUI: add step and total countdown to header
Shows remaining time for the current step and the entire sud in large
accent-coloured monospace text to the right of the transport buttons,
matching the per-step plate colour. Hidden until a step is active.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SH2D4LRuCnhLSzoYerAfJX
2026-06-28 09:34:21 +02:00
jensandClaude Sonnet 4.6 93fcf373ab Browser GUI: always reflect server ist values for heater power and stirrer speed
Remove one-time sync guards (initialPowerSync, initialSpeedSync) so heater
power and stirrer speed sliders track the server's ist readbacks on every
push, not just on first connect. Also remove the zero-out on sud
stop/pause: with continuous ist tracking the animation and slider always
agree, and the zero-out caused them to diverge when the server kept sending
non-zero values during a pause.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SH2D4LRuCnhLSzoYerAfJX
2026-06-28 09:27:30 +02:00
jensandClaude Sonnet 4.6 2b73ff7d5c Zero heater and stirrer sliders when sud stops in both clients
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DF16nV2ispNw1C9SbvqbRQ
2026-06-27 22:00:18 +02:00
jensandClaude Sonnet 4.6 897a69fd17 Scale LCD panel and pot SVG to 200%, shrink progress column
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DF16nV2ispNw1C9SbvqbRQ
2026-06-27 21:41:01 +02:00
jensandClaude Sonnet 4.6 5c2a301f1f Redesign browser GUI: drop tabs, 3-column layout, labels above sliders
- Remove tab system (nav#tabs, tab-btn, tab-content) from HTML/CSS/JS
- Permanent 3-column grid: manual controls (left, 16em) | LCD+pot (center) | progress (right, fills remaining)
- LCD panel stacks above pot SVG in center column, both hidden until connected
- Step plates no longer max-width capped, fill the right column
- Slider labels moved above the slider+readout row via flex-direction: column

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DF16nV2ispNw1C9SbvqbRQ
2026-06-27 21:35:25 +02:00
jensandClaude Sonnet 4.6 b4e7007c4a Pre-init pot temperature input with ambient temp on first server push
Mirrors PyQt client which seeds doubleSpinBox_pot_temp from the
remembered ambient on startup. In the browser there is no persistence,
so the first AmbientTemp message from the server is used instead.
Flag resets on reconnect so it re-syncs if the server restarts.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DF16nV2ispNw1C9SbvqbRQ
2026-06-27 21:16:49 +02:00
jensandClaude Sonnet 4.6 0316cfd33b Remove server-side Activate/Enable residues from heater and tempctrl tasks
HeaterTask.on_changed_active() was never registered as a callback so
never fired; TcTask.recv() no longer needs to handle {Enable: ...}
since no client sends it after the override checkbox removal.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DF16nV2ispNw1C9SbvqbRQ
2026-06-27 21:13:49 +02:00
jensandClaude Sonnet 4.6 ce6f14eccf Disable manual sliders during sud, track live values continuously
During a running sud, all four manual sliders (temp soll, heat rate,
heater power, stirrer speed) are disabled for user input but update
their positions continuously from server pushes. They re-enable when
the sud stops or is paused, or on disconnect. Browser client also
resets initial-sync flags on reconnect so sliders re-sync correctly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DF16nV2ispNw1C9SbvqbRQ
2026-06-27 21:10:40 +02:00
jensandClaude Sonnet 4.6 734da781f8 Fix browser client WS connection: missing brace, error handling, localhost fallback
Removing the Enabled else-if branch in a previous commit left the Soll
if-block unclosed, crashing app.js at parse time so no JS ran at all.
Also adds: onerror handler surfaces connection failures in the status
label; try/catch for invalid URL; 'Connecting…' feedback; localhost
fallback when location.hostname is empty (file:// open); wider host
input field.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DF16nV2ispNw1C9SbvqbRQ
2026-06-27 20:56:34 +02:00
jensandClaude Sonnet 4.6 dd6193fa46 Remove manual tab override/enable checkboxes from browser and PyQt clients
Drops the tc-enable, heater-activate and stirrer-activate checkboxes
and all associated state, signal connections, initial-sync flags, and
channel-handler logic. Regenerates main_window.py from the updated .ui.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DF16nV2ispNw1C9SbvqbRQ
2026-06-27 20:37:14 +02:00
jensandClaude Sonnet 4.6 82acc2d149 Scale fire flames with SVG transform anchored at base, floor at 1200W
Replace clip-rect height manipulation with translate-scale-translate SVG
transform on fire-group, pivoting at the fixed base coordinate y=248.
Eliminates base-hopping caused by fill-box bounding-box drift from
individual flame flicker animations. Clip rect is now static (guards
against flicker overshoot above the pot bottom). Minimum visible flame
size floored at the scale corresponding to 1200W.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JLR2133M2PyjiYq584i2G7
2026-06-27 10:32:47 +02:00
jens eef391e048 Revert "Revert LCD/pot to always-visible with placeholder values when disconnected"
This reverts commit 975c9c97ef.
2026-06-27 10:07:23 +02:00
jensandClaude Sonnet 4.6 975c9c97ef Revert LCD/pot to always-visible with placeholder values when disconnected
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JLR2133M2PyjiYq584i2G7
2026-06-27 10:06:22 +02:00
jensandClaude Sonnet 4.6 fd3cd64a60 Hide status lines when disconnected, show on connect
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JLR2133M2PyjiYq584i2G7
2026-06-27 09:42:49 +02:00
jensandClaude Sonnet 4.6 dee0d4e1cc Hide LCD/pot panel when disconnected, show on connect
lcd-pot-row is hidden by default in CSS and only made visible
when the WebSocket connection is established.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JLR2133M2PyjiYq584i2G7
2026-06-27 09:35:10 +02:00
jensandClaude Sonnet 4.6 4375538c25 Fix grain/level vanishing after reconnect at an active step
sudStepIndex was reset to null in updateSudForecast whenever
isNewSchedule=true (i.e. on every fresh page load). If the Step
message arrived before Json in the global-state replay, or in a
separate message, the schedule would be rebuilt but the index
would be null, leaving grain mass as 0 with no subsequent Step
to restore it.

Step messages are the authoritative source for the current index,
so the reset is removed. The Step handler always overwrites it.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JLR2133M2PyjiYq584i2G7
2026-06-27 09:27:28 +02:00
jensandClaude Sonnet 4.6 fe44c870a7 Add utilized volume level indicator and fix water fill level
Water fill height now reflects water_mass + grain_mass*0.7 (grain
displacement). A small blue tick and label on the left pot wall shows
the utilized volume in litres, hidden when the pot is unconfigured.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JLR2133M2PyjiYq584i2G7
2026-06-27 09:04:04 +02:00
jensandClaude Sonnet 4.6 0c30ea5dd4 sud_0010: reset grain_mass to 0 at mash-in step
The "Einmaischen einfüllen" step waits for the user to add grain, so
grain_mass starts at 0 here and is added by the following steps.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JLR2133M2PyjiYq584i2G7
2026-06-27 08:53:59 +02:00
jensandClaude Sonnet 4.6 545d924111 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
2026-06-27 08:40:53 +02:00
jensandClaude Sonnet 4.6 c92c41230d Fix fire gap: restore flame positions, increase clip topBuffer to 20px
Flames back at y=177 (tips touching pot bottom at y=174), viewBox
back to 255. topBuffer raised from 10 to 20px to cover flicker
overshoot without clipping the tips.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DGKe8JzTM2Gr1fdx9wYXTi
2026-06-26 23:02:11 +02:00
jensandClaude Sonnet 4.6 7809cb1938 Fix fire scaling: clip upper flames, keep base constant
Replace scaleY group transform with a dynamic clipPath that reveals
flame height proportional to heater power. Base at y=256 stays fixed;
only the tips grow with intensity. Includes flicker overshoot buffer.
Fire paths shifted +8px down, viewBox extended to 265.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DGKe8JzTM2Gr1fdx9wYXTi
2026-06-26 22:55:27 +02:00
jensandClaude Sonnet 4.6 6bcf1fda0a Add soll temperature marker to thermometer
Orange tick and label on the left side of the tube, dynamically
positioned via plotTempSoll updated from TempCtrl Soll messages.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DGKe8JzTM2Gr1fdx9wYXTi
2026-06-26 22:31:20 +02:00
jensandClaude Sonnet 4.6 bc4a11dcb7 Show floating grain particles in water when grain_mass > 0
35 particles distributed across full pot height, clipped to dynamic
water area via water-clip-rect updated alongside water fill level.
Particle opacity transitions on grain_mass changes per step.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DGKe8JzTM2Gr1fdx9wYXTi
2026-06-26 22:27:05 +02:00
jensandClaude Sonnet 4.6 7dfe88b89e Shorten stirrer shaft 15% and raise blades to match
Shaft height 158→134px, blades y=157→133 (both −24px).
Also clip stirrer blades to pot interior.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DGKe8JzTM2Gr1fdx9wYXTi
2026-06-26 22:18:27 +02:00
jensandClaude Sonnet 4.6 5e423badd8 Scale LCD panel and pot visualization 2x
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DGKe8JzTM2Gr1fdx9wYXTi
2026-06-26 22:13:38 +02:00
jensandClaude Sonnet 4.6 47bbc53d3c Animate stirrer blades proportional to speed
CSS rotation animation on #stirrer-blades group, speed mapped via
--stir-duration custom property (speed=100 → 0.5s, speed=50 → 1s).
Driven by stirrerSpeedIst on each Speed message.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DGKe8JzTM2Gr1fdx9wYXTi
2026-06-26 22:09:22 +02:00
jensandClaude Sonnet 4.6 083bc97651 Add animated fire visualization and fix step pot param reading
- SVG fire group with 3 layered flame paths (red/orange/yellow gradients)
- CSS keyframe flicker animations with different timings per flame layer
- JS controls fire opacity and scaleY proportional to heater power
- Fix buildStep() reading grain_mass/water_mass from nested step.pot
  instead of flat step keys, matching the JSON structure

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DGKe8JzTM2Gr1fdx9wYXTi
2026-06-26 22:04:32 +02:00
jensandClaude Sonnet 4.6 59b18b95b6 Move pot/thermometer next to LCD panel, scale to 144px
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DGKe8JzTM2Gr1fdx9wYXTi
2026-06-26 21:49:36 +02:00
jensandClaude Sonnet 4.6 b721db2396 Add pot/thermometer visualization to browser GUI
- Add volumen field to all sud JSON pot entries (30 L)
- Fix parseSudDoc to read from nested doc.pot (was using stale flat keys)
- SVG widget shows transparent rectangular pot with stirrer, thermometer
  with live temperature, and water fill level (water_mass/volumen) when
  a sud is loaded; no water shown when no sud is loaded

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DGKe8JzTM2Gr1fdx9wYXTi
2026-06-26 21:46:58 +02:00
jensandClaude Sonnet 4.6 99201833ff Fix GUI client references to nested pot params
Update set_step(), status bar mass display, and on_action_sud_new() to
read grain_mass/water_mass from step['pot'] and use the new pot key
structure when sending an empty Load document.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T52JH848ojhXXHn1bAzdC3
2026-06-26 08:04:01 +02:00
jensandClaude Sonnet 4.6 8c845ed5c5 Fix crash: rename shadowing 'pot' variable in forecast closure
In estimate()'s on_step_changed closure, the new 'pot = step.get("pot", {})'
local variable shadowed the outer 'pot' (Pot plant instance), causing
pot.set_plant_params() to be called on a plain dict and crash on load.
Renamed to 'step_pot' to avoid the collision.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T52JH848ojhXXHn1bAzdC3
2026-06-26 08:02:53 +02:00
jensandClaude Sonnet 4.6 e2e3baab08 Refactor sud JSON pot params under a nested 'pot' key
Top-level pot_mass/pot_material/L/Td/grain_mass/water_mass and their
per-step overrides are now grouped under a 'pot' sub-object, matching
the structure already applied to sud_0010.json's parent level. Updated
all sude/*.json files and the parsing/consuming code in components/sud.py,
tasks/sud.py, components/sud_forecast.py, and scripts/demos/sud/demo_sud.py.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T52JH848ojhXXHn1bAzdC3
2026-06-26 07:31:27 +02:00
jensandClaude Sonnet 4.6 7dd0400278 Format temperature and rate LCD displays to always show 1 decimal place
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tqxrk8uj4M3w3d3eXm3xK8
2026-06-25 22:26:06 +02:00
jensandClaude Sonnet 4.6 0215bac13e Distributed confirmation dialog: all GUIs open on WAIT_USER, server closes all
- Browser: OK button sends Confirm without closing dialog; Escape blocked;
  dialog closes when state leaves WAIT_USER (server-driven).
- PyQt: non-blocking QDialog replaces blocking QMessageBox; OK sends Confirm
  without closing; dialog closed by server state change or on disconnect.
- ws_client.py: remove deprecated loop= params from websockets.connect() and
  asyncio.ensure_future() (same fix already applied to ws_server.py) so the
  PyQt client can actually connect with websockets >= 10.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tqxrk8uj4M3w3d3eXm3xK8
2026-06-25 22:21:41 +02:00
jensandClaude Sonnet 4.6 dabec1fd31 Wire PyQt power ist/soll LCDs to match browser GUI
lcdNumber_power_pot (col 0, ist) now shows heater effective Power;
lcdNumber_power_heater (col 1, soll) now shows heater PowerSet.
Removes debug prints from on_heater_changed and on_pot_changed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tqxrk8uj4M3w3d3eXm3xK8
2026-06-25 21:39:10 +02:00
jensandClaude Sonnet 4.6 44541220f9 Fix FSM regression: swap TempControllerBase base class order
With TempControllerBase(APid, TempControllerFsm), APid.is_holding()
(abstract stub returning None) shadowed TempControllerFsm.is_holding()
in the MRO, so tc.is_holding() always returned None. The forecast
simulation never called sud.temp_reached() and always hit MAX_TICKS.

Swapping to TempControllerBase(TempControllerFsm, APid) puts the
concrete FSM methods before APid's abstract stubs in the MRO.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tqxrk8uj4M3w3d3eXm3xK8
2026-06-25 21:22:52 +02:00
jensandClaude Sonnet 4.6 95a05eb0c2 Retheme web UI to dark Mainsail-inspired design with transport buttons
- Full dark theme: #121212 background, #1e1e1e surface cards, orange
  accent (#ff7043), muted secondary text, subtle borders/shadows
- Green phosphor LCD displays with glow effect
- Circular colour-coded transport buttons: green play, amber pause,
  red stop (Unicode icons, hover brightness effect)
- Panel headings in orange uppercase with letter-spacing
- Orange accent-color on range/checkbox inputs
- Fixed .hidden specificity vs #pot-reset-row with !important

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tqxrk8uj4M3w3d3eXm3xK8
2026-06-25 21:00:53 +02:00
jensandClaude Sonnet 4.6 42d7776a85 Extract FSM logic from TempControllerBase into TempControllerFsm
States, DEFAULT_THRESHOLDS, the three PIDs, FSM state vars, process_fsm(),
on_state_entered(), set_enabled(), and is_holding() move to a new
TempControllerFsm class in temp_controller_fsm.py. TempControllerBase
inherits from both APid and TempControllerFsm; States is re-exported so
existing imports in temp_controller_smith.py are unaffected.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tqxrk8uj4M3w3d3eXm3xK8
2026-06-25 20:50:20 +02:00