Relays that run at 33 degrees, not 331

The two relay temperatures came back as 331 and 319 from a charger sitting idle
with nothing plugged in. The spec's gain column says 1 for both, so we reported
them as 331 °C and 319 °C — a reading that would have meant a fire rather than a
wallbox at room temperature.

The gain is 10. The same table hands the maximum current setting a unit of watts
and the timeout a unit of amps, so its unit and gain columns are not load-bearing
here; what settles the alignment is the LED brightness two registers earlier,
which reads exactly 100 at gain 1, and the fact that the neighbouring registers
all decode as tabulated. Read back from the charger afterwards: 33.1 °C and
31.9 °C.

The field becomes a float, as the voltages and currents beside it already are.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
tajniak81
2026-09-01 19:01:29 +02:00
co-authored by Claude Opus 5
parent cf4fd14b56
commit aaa89dfe10
2 changed files with 20 additions and 13 deletions
@@ -65,8 +65,8 @@ const (
regSolarBalancing = 20090 regSolarBalancing = 20090
regCPVoltage = 20091 regCPVoltage = 20091
regCPSignal = 20092 regCPSignal = 20092
regRelay1Temp = 20093 // INT16, degC regRelay1Temp = 20093 // INT16, decidegC — see decodeLive on the gain
regRelay2Temp = 20094 // INT16, degC regRelay2Temp = 20094 // INT16, decidegC
regBoostMode = 20095 regBoostMode = 20095
regLEDBrightness = 20096 // % regLEDBrightness = 20096 // %
regChargingStatus = 20097 // same 0-8 enum as the cloud's operating_state regChargingStatus = 20097 // same 0-8 enum as the cloud's operating_state
@@ -171,8 +171,8 @@ type ModbusSnapshot struct {
CPSignal *int `json:"cpSignal,omitempty"` CPSignal *int `json:"cpSignal,omitempty"`
CPSignalDesc string `json:"cpSignalDesc,omitempty"` CPSignalDesc string `json:"cpSignalDesc,omitempty"`
Relay1TempC *int `json:"relay1TempC,omitempty"` Relay1TempC *float64 `json:"relay1TempC,omitempty"`
Relay2TempC *int `json:"relay2TempC,omitempty"` Relay2TempC *float64 `json:"relay2TempC,omitempty"`
OcppStatus *int `json:"ocppStatus,omitempty"` OcppStatus *int `json:"ocppStatus,omitempty"`
OcppStatusDesc string `json:"ocppStatusDesc,omitempty"` OcppStatusDesc string `json:"ocppStatusDesc,omitempty"`
@@ -271,13 +271,19 @@ func decodeLive(snap *ModbusSnapshot, regs []uint16) {
b := v != 0 b := v != 0
return &b return &b
} }
degrees := func(addr int) *int { // The spec's gain column says 1 for the two relay temperatures, but a charger
// idling with nothing plugged in reads 331 and 319 there, which is a gain of
// 10 and not a pair of relays at 331 °C. The same table gives the maximum
// current setting in watts and the timeout in amps, so its unit and gain
// columns are not load-bearing; the LED brightness two registers earlier
// reads exactly 100 at gain 1, which is what fixes the alignment.
degrees := func(addr int) *float64 {
v, ok := at(addr) v, ok := at(addr)
if !ok { if !ok {
return nil return nil
} }
n := int(int16(v)) // signed: the relays can read below zero c := float64(int16(v)) / 10 // signed: the relays can read below zero
return &n return &c
} }
snap.VoltageL1 = scaled(regVoltageL1N, 10) snap.VoltageL1 = scaled(regVoltageL1N, 10)
@@ -106,18 +106,19 @@ func TestDecodeLiveNamesStates(t *testing.T) {
func TestDecodeLiveReadsNegativeTemperatures(t *testing.T) { func TestDecodeLiveReadsNegativeTemperatures(t *testing.T) {
b := newLiveBlock() b := newLiveBlock()
below := int16(-7) below := int16(-72)
b.set(regRelay1Temp, uint16(below)) b.set(regRelay1Temp, uint16(below))
b.set(regRelay2Temp, 41) // 331 is what an idle charger actually reports: 33.1 °C, not 331 °C.
b.set(regRelay2Temp, 331)
var snap ModbusSnapshot var snap ModbusSnapshot
decodeLive(&snap, b) decodeLive(&snap, b)
if snap.Relay1TempC == nil || *snap.Relay1TempC != -7 { if snap.Relay1TempC == nil || *snap.Relay1TempC != -7.2 {
t.Errorf("Relay1TempC = %v, want -7", snap.Relay1TempC) t.Errorf("Relay1TempC = %v, want -7.2", snap.Relay1TempC)
} }
if snap.Relay2TempC == nil || *snap.Relay2TempC != 41 { if snap.Relay2TempC == nil || *snap.Relay2TempC != 33.1 {
t.Errorf("Relay2TempC = %v, want 41", snap.Relay2TempC) t.Errorf("Relay2TempC = %v, want 33.1", snap.Relay2TempC)
} }
} }