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
47 lines
1.6 KiB
Python
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
|