Fix: authorize by RFID or WebUI

This commit is contained in:
2026-06-11 14:09:57 +02:00
parent ca51807d92
commit 48c7587550
+53 -6
View File
@@ -46,12 +46,29 @@ type ChargerController struct {
} }
func NewChargerController(host string, pool *pgxpool.Pool) *ChargerController { func NewChargerController(host string, pool *pgxpool.Pool) *ChargerController {
return &ChargerController{ c := &ChargerController{
host: host, host: host,
pool: pool, pool: pool,
params: ChargingParams{Mode: ModeOff}, params: ChargingParams{Mode: ModeOff},
status: "idle", status: "initializing",
} }
// Always restore RFID-gated idle state on startup. nmo/acs settings persist
// on the charger hardware across app restarts, so we must reset them explicitly.
go c.initSafeState()
return c
}
func (c *ChargerController) initSafeState() {
if err := setChargerFrc(c.host, 1); err != nil {
log.Printf("charger ctrl: init: %v", err)
c.setStatus("charger unreachable")
return
}
setChargerNmo(c.host, false)
setChargerAcs(c.host, 1)
setChargerFrc(c.host, 0)
c.setStatus("idle")
log.Printf("charger ctrl: initialized — RFID-gated (nmo=false, acs=1)")
} }
func (c *ChargerController) State() ControllerState { func (c *ChargerController) State() ControllerState {
@@ -131,12 +148,15 @@ func (c *ChargerController) enableCharging(amps int) error {
if err != nil { if err != nil {
return fmt.Errorf("charger unreachable: %w", err) return fmt.Errorf("charger unreachable: %w", err)
} }
if st.Car == 4 { // car=4: previous session must be cleared before a new one can start.
c.setStatus("resetting — previous session complete") // car=3: car entered IEC state B while acs=1 was active; the CP-line
// "wait for auth" signal means the car won't accept frc=2 alone —
// a full reset forces a fresh IEC 61851 handshake.
if st.Car == 4 || st.Car == 3 {
c.setStatus("resetting — fresh IEC negotiation needed")
if err := resetCharger(c.host); err != nil { if err := resetCharger(c.host); err != nil {
return fmt.Errorf("reset: %w", err) return fmt.Errorf("reset: %w", err)
} }
// Reset clears currentPhases so phase-switch logic starts fresh
c.mu.Lock() c.mu.Lock()
c.currentPhases = 0 c.currentPhases = 0
c.mu.Unlock() c.mu.Unlock()
@@ -276,6 +296,24 @@ func (c *ChargerController) adjust(ctx context.Context) {
return return
} }
} }
// frc=2 alone does not override acs=1 on this firmware: if access control
// is still blocking (alw=false) and we haven't intentionally force-paused
// (frc=1), the session hasn't been authorised yet — call enableCharging to
// set acs=0 and start the session properly.
// Guard on battPaused: if the battery SOC is already below the cutoff
// (set at the top of this function), do not enable charging even if the
// charger looks idle — the pause takes priority.
c.mu.RLock()
paused := c.battPaused
c.mu.RUnlock()
if !st.Alw && st.Frc != 1 && !paused {
if err := c.enableCharging(params.MaxAmp); err != nil {
log.Printf("charger ctrl: session start: %v", err)
c.setStatus("session start failed: " + err.Error())
}
return
}
} }
// Grid mode: current is fixed and already applied by enableCharging(); the // Grid mode: current is fixed and already applied by enableCharging(); the
@@ -412,7 +450,16 @@ func (c *ChargerController) stopOnDisconnect() {
func (c *ChargerController) stopForTarget(reason string) { func (c *ChargerController) stopForTarget(reason string) {
if err := setChargerFrc(c.host, 1); err != nil { if err := setChargerFrc(c.host, 1); err != nil {
log.Printf("charger ctrl: stop: %v", err) log.Printf("charger ctrl: stop frc1: %v", err)
}
if err := setChargerNmo(c.host, false); err != nil {
log.Printf("charger ctrl: stop nmo: %v", err)
}
if err := setChargerAcs(c.host, 1); err != nil {
log.Printf("charger ctrl: stop acs: %v", err)
}
if err := setChargerFrc(c.host, 0); err != nil {
log.Printf("charger ctrl: stop frc0: %v", err)
} }
c.mu.Lock() c.mu.Lock()
c.params.Mode = ModeOff c.params.Mode = ModeOff