Files
brewpi/components/sensor/tempSensorSim.py
T
jensandClaude Sonnet 5 698c019581 refactor: make ATemperatureSensor's temp observable directly on the sensor
TempSensorSim/TempSensor_max31865's temperature() now stores its
reading on self.temp, so ATemperatureSensor's inherited AttributeChange
(previously never triggered by anything) actually fires. TempSensorTask
no longer keeps its own shadow copy of the reading - it registers its
websocket-push callback on self.sensor directly and just drives the
read each tick; server/brewpi.py's TC-feeding registration moved from
sensor_task to sensor for the same reason.

Priming read happens before registering the callback (not after) since
all tasks are built synchronously at module level, before the asyncio
event loop starts - registering first would fire on_temp_changed's
asyncio.create_task() with no running loop.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GpePKZiEZWbGo9HrfuML6U
2026-07-02 22:03:50 +02:00

47 lines
1.6 KiB
Python

from components import ATemperatureSensor
import numpy as np
class TempSensorSim(ATemperatureSensor):
"""Simulated temperature sensor with two independent noise components.
sigma -- white Gaussian noise std [°C]; calibrated against the
20260628T184903 Sud-0010 log: detrended hold-phase
tick-to-tick std ≈ 0.053 °C.
stirrer_sigma -- steady-state std [°C] of a slow AR(1) process that
models stirrer-induced low-frequency temperature
fluctuations at the sensor. Zero by default (off).
stirrer_tau -- correlation time [ticks] of the AR(1) process;
stirrer_sigma * sqrt(2/stirrer_tau) is the per-tick
innovation std so that the steady-state variance equals
stirrer_sigma².
"""
def name(self):
return "FakeTemp"
def __init__(self, sigma=0.05, stirrer_sigma=0.0, stirrer_tau=20.0,
temp_offset=0.0, **kwargs):
ATemperatureSensor.__init__(self)
self.temp_set = 19.99
self.offset = temp_offset
self.sigma = sigma
self.stirrer_sigma = stirrer_sigma
self.stirrer_tau = stirrer_tau
self._stirrer_state = 0.0
def set_fake_temp(self, temp):
self.temp_set = temp
def temperature(self):
white = self.sigma * np.random.normal() if self.sigma > 0 else 0.0
if self.stirrer_sigma > 0:
alpha = 1.0 / self.stirrer_tau
innovation = self.stirrer_sigma * np.sqrt(2.0 * alpha) * np.random.normal()
self._stirrer_state = (1.0 - alpha) * self._stirrer_state + innovation
self.temp = self.temp_set + self.offset + white + self._stirrer_state
return self.temp