From 31b792c36a8f1c987bf5799efb79cb660d2a53f6 Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Fri, 20 Dec 2024 03:19:36 +0200 Subject: [PATCH] Implement some of the 0x23 register The last two bytes represent the status of interior and hazard lights (on MY19.) --- README.md | 2 ++ cmd/mqtt.go | 3 +++ protocol/README.md | 47 +++++++++++++++++++++++++-------------------- protocol/message.go | 47 ++++++++++++++++++++++++++++++++++++++------- 4 files changed, 71 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index b2bbd84..6574402 100644 --- a/README.md +++ b/README.md @@ -120,6 +120,8 @@ The following topics are published: | phev/door/boot | State of doors. *closed* or *open* | | phev/lights/parking | Parking lights. *on* or *off* | | phev/lights/head | Head lights. *on* or *off* | +| phev/lights/hazard | Hazard lights. *on* or *off* | +| phev/lights/interior | Interior lights. *on* or *off* | | phev/vin | Discovered VIN of the car | | phev/registrations | Number of wifi clients registered to the car | diff --git a/cmd/mqtt.go b/cmd/mqtt.go index 5820c46..9de63af 100644 --- a/cmd/mqtt.go +++ b/cmd/mqtt.go @@ -442,6 +442,9 @@ func (m *mqttClient) publishRegister(msg *protocol.PhevMessage) { case *protocol.RegisterBatteryLevel: m.publish("/battery/level", fmt.Sprintf("%d", reg.Level)) m.publish("/lights/parking", boolOnOff[reg.ParkingLights]) + case *protocol.RegisterLightStatus: + m.publish("/lights/interior", boolOnOff[reg.Interior]) + m.publish("/lights/hazard", boolOnOff[reg.Hazard]) case *protocol.RegisterChargePlug: if reg.Connected { m.publish("/charge/plug", "connected") diff --git a/protocol/README.md b/protocol/README.md index 66af0d1..7f5f595 100644 --- a/protocol/README.md +++ b/protocol/README.md @@ -271,7 +271,7 @@ There seem to be two types of register layout (A/B). |0x20 | ?? | 10 | 146 | [2,2,2,2,2] | | |0x21 | ?? | 1 | 156 | [1] | | |0x22 | ?? | 6 | 157 | [2,2,2] | | -|0x23 | ?? | 5 | 163 | [1,1,1,1,1] | Maybe AC related | +|0x23 | Interior/Hazard Lights + AC? | 5 | 163 | [1,1,1,1,1] | | |0x24 | Door Lock Status | 10 | 168 | [1,1,1,1,1,1,1,1,1,1] | | |0x25 | ?? | 3 | 178 | [1,1,1] | | |0x26 | ?? | 1 | 181 | [1] | | @@ -393,7 +393,12 @@ Single byte. |0 | Charge status [0=not charging 1=charging]| | 1-2 | Charge time remaining | -### 0x23 - Something AC? +### 0x23 - Interior/Hazard lights + Something AC? + +Interior lights ON: `0000000201` +Interior lights OFF: `0000000202` +Hazard lights ON: `0000000102` +Hazard lights OFF: `0000000202` AC timer sniff: @@ -522,35 +527,35 @@ AC data sniff. Windscreen for 10 mins: -INFO[0000] out [a1] REGISTER SET (reg 0x1b data 02030000) -INFO[0000] in [a1] REGISTER NTFY (reg 0x10 data 02b00b) -INFO[0000] in [55] REGISTER NTFY (reg 0x1a data 0001000000) -INFO[0000] in [70] REGISTER NTFY (reg 0x1c data 03) +INFO[0000] out [a1] REGISTER SET (reg 0x1b data 02030000) +INFO[0000] in [a1] REGISTER NTFY (reg 0x10 data 02b00b) +INFO[0000] in [55] REGISTER NTFY (reg 0x1a data 0001000000) +INFO[0000] in [70] REGISTER NTFY (reg 0x1c data 03) Heat in 5 mins for 10 mins: -INFO[0000] out [70] REGISTER SET (reg 0x1b data 02020001) -INFO[0000] in [01] REGISTER NTFY (reg 0x1a data 0000000101) -INFO[0000] in [86] REGISTER NTFY (reg 0x1c data 02) +INFO[0000] out [70] REGISTER SET (reg 0x1b data 02020001) +INFO[0000] in [01] REGISTER NTFY (reg 0x1a data 0000000101) +INFO[0000] in [86] REGISTER NTFY (reg 0x1c data 02) Heat in 0 mins for 10 mins: -INFO[0000] out [73] REGISTER SET (reg 0x1b data 02020000) -INFO[0000] in [73] REGISTER NTFY (reg 0x10 data 02b020) -INFO[0000] in [22] REGISTER NTFY (reg 0x1a data 0001000000) -INFO[0000] in [50] REGISTER NTFY (reg 0x1c data 02) +INFO[0000] out [73] REGISTER SET (reg 0x1b data 02020000) +INFO[0000] in [73] REGISTER NTFY (reg 0x10 data 02b020) +INFO[0000] in [22] REGISTER NTFY (reg 0x1a data 0001000000) +INFO[0000] in [50] REGISTER NTFY (reg 0x1c data 02) Heat in 0 mins for 20 mins -INFO[0000] out [4e] REGISTER SET (reg 0x1b data 02020100) -INFO[0000] in [1c] REGISTER NTFY (reg 0x10 data 02b034) -INFO[0000] in [e7] REGISTER NTFY (reg 0x1a data 0001010000) -INFO[0000] in [c6] REGISTER NTFY (reg 0x1c data 02) +INFO[0000] out [4e] REGISTER SET (reg 0x1b data 02020100) +INFO[0000] in [1c] REGISTER NTFY (reg 0x10 data 02b034) +INFO[0000] in [e7] REGISTER NTFY (reg 0x1a data 0001010000) +INFO[0000] in [c6] REGISTER NTFY (reg 0x1c data 02) Cool 0 mins for 10 mins -INFO[0170] out [5f] REGISTER SET (reg 0x1b data 02010000) -INFO[0170] in [5b] REGISTER NTFY (reg 0x10 data 02b139) -INFO[0171] in [18] REGISTER NTFY (reg 0x1a data 0001000000) -INFO[0171] in [95] REGISTER NTFY (reg 0x1c data 01) +INFO[0170] out [5f] REGISTER SET (reg 0x1b data 02010000) +INFO[0170] in [5b] REGISTER NTFY (reg 0x10 data 02b139) +INFO[0171] in [18] REGISTER NTFY (reg 0x1a data 0001000000) +INFO[0171] in [95] REGISTER NTFY (reg 0x1c data 01) Cool 10 mins for 10 mins SETREG 0x1b: -> 02010002 diff --git a/protocol/message.go b/protocol/message.go index 20b2368..f33b496 100644 --- a/protocol/message.go +++ b/protocol/message.go @@ -184,6 +184,8 @@ func (p *PhevMessage) DecodeFromBytes(data []byte, key *SecurityKey) error { p.Reg = new(RegisterACOperStatus) case ACModeRegister: p.Reg = new(RegisterACMode) + case LightStatusRegister: + p.Reg = new(RegisterLightStatus) default: p.Reg = new(RegisterGeneric) } @@ -234,18 +236,19 @@ func NewFromBytes(data []byte, key *SecurityKey) []*PhevMessage { } const ( - VINRegister = 0x15 - ECUVersionRegister = 0xc0 - BatteryLevelRegister = 0x1d BatteryWarningRegister = 0x02 - DoorStatusRegister = 0x24 - ChargeStatusRegister = 0x1f - ACOperStatusRegister = 0x1a - SetACEnabledRegisterMY14 = 0x04 SetACModeRegisterMY14 = 0x02 + SetACEnabledRegisterMY14 = 0x04 + VINRegister = 0x15 + ACOperStatusRegister = 0x1a SetACModeRegisterMY18 = 0x1b ACModeRegister = 0x1c + BatteryLevelRegister = 0x1d ChargePlugRegister = 0x1e + ChargeStatusRegister = 0x1f + LightStatusRegister = 0x23 + DoorStatusRegister = 0x24 + ECUVersionRegister = 0xc0 ) type Register interface { @@ -577,3 +580,33 @@ func (r *RegisterChargePlug) String() string { func (r *RegisterChargePlug) Register() byte { return ChargePlugRegister } + + +type RegisterLightStatus struct { + Interior bool + Hazard bool + raw []byte +} + + +func (r *RegisterLightStatus) Decode(m *PhevMessage) { + if len(m.Data) != 5 { + return + } + // Switches between 2 for Off and 1 for On. + r.Interior = m.Data[4] & 0b11 == 1; + r.Hazard = m.Data[3] & 0b11 == 1; + r.raw = m.Data +} + +func (r *RegisterLightStatus) Raw() string { + return hex.EncodeToString(r.raw) +} + +func (r *RegisterLightStatus) String() string { + return fmt.Sprintf("Hazard lights: %t; Interior lights: %t.", r.Hazard, r.Interior) +} + +func (r *RegisterLightStatus) Register() byte { + return LightStatusRegister +}