Treat missing SignedMeterValue observation as no reading#40
Merged
Conversation
There was a problem hiding this comment.
Pull request overview
This PR aligns Zaptec::State#meter_reading with Zaptec’s sparse /api/chargers/{id}/state observation set by treating a missing SignedMeterValue observation as “no reading” (returning nil) instead of raising a KeyError.
Changes:
- Update
Zaptec::State#meter_readingto safely handle missing/blankSignedMeterValueby short-circuiting tonil. - Add specs covering both the happy path (parsing when present) and the missing-observation case.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
lib/zaptec/state.rb |
Makes meter_reading resilient to missing/blank SignedMeterValue by returning nil rather than raising. |
spec/zaptec/state_spec.rb |
Adds regression coverage for missing SignedMeterValue and verifies parsing still works when present. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Voorkomt
KeyError: key not found: :SignedMeterValuewanneer Zaptec's/api/chargers/{id}/stateendpoint geenSignedMeterValueobservatie teruggeeft.Zaptec::State#meter_readingretourneert nunilin plaats van te crashen, wat aansluit op de callers in StekkerWeb die al eenreturn if meter_reading.nil?guard hebben.Root cause
Zaptec::State#meter_reading(lib/zaptec/state.rb:39) gebruikt@data.fetch(:SignedMeterValue). Het Zaptec state endpoint retourneert observaties spaars: alleen de StateIds die een charger ooit heeft geëmit. Een charger die nog nooit een OCMF signed meter value heeft geproduceerd (sessie net begonnen, niet-MID-certified hardware) heeft simpelweg geen row 554 in de response. De callers in StekkerWeb (ChargePointRefreshJob#create_meter_readingregel 107-108 en#create_start_meter_readingregel 124-125) doen vervolgensreturn if meter_reading.nil?— maar zien dienilnooit omdatfetcheerder al KeyError raised.Waarom-keten
ChargePointRefreshJob#create_start_meter_reading? →charge_point.meter_readingraisedKeyError: key not found: :SignedMeterValue.Zaptec::State#meter_readingdoet@data.fetch(:SignedMeterValue)en de hash bevat die key niet.:SignedMeterValueniet in de hash? → Zaptec's state API retourneert observaties spaars (alleen wat de charger ooit heeft geëmit). Observatie 554 verschijnt alleen op MID-certified chargers en pas vanaf de eerste signed meter event in een sessie. Op andere chargers / vroeg in een sessie is hij afwezig.session_identifier(regel 31-34) gebruikt@data[:SessionIdentifier]; value.presence, enfinal_stop_active?(regel 27, na Treat missing FinalStopActive observation as inactive #38) gebruikt@data[:FinalStopActive].to_i.meter_readingis per ongeluk strict gebleven.MeterReading.parseop semantische gronden ook al nullable behandeld (return if meter_reading.blank?op regel 30). Geen SignedMeterValue observatie betekent: er is nog niets om te parsen.nilis precies de waarde die callers verwachten —ChargePointRefreshJobschrijft die conditie al op twee plekken.Waarom nu
Dit bug-patroon bestaat sinds 6440245 (2024-03-29, "Use Zaptec signed meter reading"). Eerste Sentry-event vandaag (2026-06-18) suggereert dat een net gekoppelde charger of een charger in een vroege sessie-fase voor het eerst gepolled werd zonder dat hij nog een signed meter value had geëmit. Vergelijkbare sparse-observation bug (#38, gemerged 2026-06-03) dook ook pas op nadat een externe caller een tot dan toe onbenutte code-path raakte.
Blast radius
Iedere Zaptec charger waarvan de state-response (nog) geen
SignedMeterValueobservatie bevat. In de praktijk: chargers in de eerste seconden/minuten van een sessie waar nog geen signed meter event is geweest, en non-MID-certified chargers die het feature niet ondersteunen. Tot dusver één event in productie, maar zonder de fix valt elkeChargePointRefreshJobvoor zo'n charger om bijcreate_meter_reading/create_start_meter_reading.Gekozen oplossing
Zaptec::State#meter_readinggebruikt nu@data[:SignedMeterValue]met een nil short-circuit, in plaats vanfetch. Een ontbrekende key retourneertnil— wat StekkerWeb's callers al verwachten. Dit volgt het bestaande nullable-patroon in dezelfde file (session_identifier,final_stop_active?na #38) en aligneert de gem met het werkelijke API-contract (sparse observation set). Adresseert why 3 (sparse API) door het contract goed te zetten, niet why 1 (de KeyError op zich).Alternatieven overwogen
ChargePointRefreshJob: symptom-fix op de verkeerde laag. Demeter_readingAPI in de gem hoort nullable te zijn omdat het onderliggende data-veld dat is. Een rescue op twee call-sites in StekkerWeb verbergt het probleem; een gem-fix lost het structureel op (zoals Treat missing FinalStopActive observation as inactive #38 voor FinalStopActive).@data[:SignedMeterValue] || ""en laten falen inMeterReading.parse: legt een lege-string-conventie op die de gem nergens anders gebruikt.nilis de natuurlijke "geen waarde"-representatie en sluit aan opMeterReading.parse's eigen nullable contract (regel 30).Reproductie
spec/zaptec/state_spec.rb: nieuwe test"is nil when the SignedMeterValue observation is missing from the API response"faalt vóór de fix met exact dezelfdeKeyError: key not found: :SignedMeterValueals in Sentry 2072, en slaagt erna. Een aanvullende happy-path test borgt dat de OCMF-parsing intact blijft wanneer de observatie wél aanwezig is.Follow-up
Na merge:
bundle update stekker_zaptecinstekker/stekkerom de gem-bump uit te rollen — anders blijft Sentry 2072 doorvuren tot productie de nieuwe sha pakt.Sentry: https://sentry2.stekker.app/organizations/sentry/issues/2072/