Skip to content

Parse OCMF reading values serialized as JSON strings - #50

Merged
bforma merged 1 commit into
mainfrom
parse-ocmf-string-reading-values
Aug 13, 2026
Merged

Parse OCMF reading values serialized as JSON strings#50
bforma merged 1 commit into
mainfrom
parse-ocmf-string-reading-values

Conversation

@bforma

@bforma bforma commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Zaptec-meterstanden waarvan het OCMF RV-veld als JSON-string binnenkomt worden nu gewoon geparsed in plaats van de hele refresh van de charger te laten crashen.

Root cause

Zaptec::MeterReading.build_reading deelt het RV-veld direct door de kWh-magnitude (lib/zaptec/meter_reading.rb:47). De parser neemt stilzwijgend aan dat JSON.parse daar een Numeric oplevert; komt het veld als string binnen, dan raist String#/ een NoMethodError. Er zit geen coercion op de parse-boundary.

Waarom-keten

  1. Waarom crasht ChargePointRefreshJob? → build_reading roept / aan op een String (meter_reading.rb:47).
  2. Waarom is RV een String? → De charger serialiseert het reading-veld in zijn OCMF SignedMeterValue als JSON-string in plaats van number.
  3. Waarom doet de charger dat? → De OCMF-spec typeert RV als number, maar eist tegelijk dat de exacte numerieke representatie bewaard blijft voor handtekening-verificatie ("the representation must not be transformed by further handling methods (e.g. processing by JSON parser)"). Strings zijn daarvoor de robuuste uitweg die meter-firmware in de praktijk kiest. Dit is de grens van ons systeem: third-party data, wij bepalen de serialisatie niet.
  4. Waarom vangt onze parser dat niet op? → Hij is gebouwd op de payloads die we tot nu toe zagen (altijd numeriek) en coercet niet naar een domain-type op de boundary.

Waarom nu

De gem is sinds begin 2024 ongewijzigd op dit pad; de trigger is een runtime-verandering aan de charger-kant. Charge point 60487 (Zaptec Pro, in ons systeem sinds 2024-02-01) begon op 2026-08-11 om 10:35 UTC RV als string te sturen — vrijwel zeker een OTA firmware/meter-update, die Zaptec zelf uitrolt. Sindsdien vuurt elke 5-minuten-poll van ChargePointRefreshJob het event (579 events t/m 2026-08-13).

Blast radius

Alle 579 Sentry-events dragen charge_point_id: 60487 — op dit moment precies één Zaptec Pro. Voor die charger faalt de volledige status-refresh (niet alleen de meterstand): geen sessie-updates, geen meter readings, geen online-status zolang de charger een SignedMeterValue in deze vorm rapporteert. Naarmate Zaptec dezelfde firmware breder uitrolt, raakt dit elke API-connected Zaptec-charger; ook het archived-sessions-importpad (ArchivedSession#meter_readings) gebruikt dezelfde build_reading en zou op dezelfde payload breken.

Gekozen oplossing

Float(reading.fetch(VALUE)) in build_reading, zodat zowel numbers als string-representaties naar een Float coercen vóór de deling. Dit fixt why 4 — de diepste why die binnen ons systeem ligt (why 3 is vendor-gedrag dat de OCMF-spec zelf uitlokt). Float() blijft strikt: een niet-numerieke string raist alsnog, dus echte garbage blijft zichtbaar in plaats van stilletjes 0.0 te worden.

Alternatieven overwogen

  • to_f op het veld: maakt van garbage stilletjes 0.0 en van nil een valide meterstand — verbergt echte datacorruptie.
  • Afvangen/skippen van niet-numerieke readings in parse_all (zoals bij ST != G): een string-RV is geen ongeldige reading maar een andere serialisatie van een valide waarde; skippen zou goede meterstanden weggooien.

Reproductie

spec/zaptec/meter_reading_spec.rb — "supports reading values serialized as JSON strings" faalt vóór de fix met exact de productie-error (NoMethodError: undefined method '/' for an instance of String op meter_reading.rb:47).

Na merge is een bundle update stekker_zaptec in StekkerWeb nodig om de fix naar productie te brengen; die bump-PR sluit stekker/backlog#2121.

Sentry: https://sentry2.stekker.app/organizations/sentry/issues/2186/
Onderdeel van stekker/backlog#2121

The OCMF spec types RV as a JSON number but requires the exact
numeric representation to survive parsing for signature verification,
which is why some meter firmware emits it as a string. Coercing at
the parse boundary accepts both representations.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@bforma
bforma merged commit 1825545 into main Aug 13, 2026
1 of 2 checks passed
@bforma
bforma deleted the parse-ocmf-string-reading-values branch August 13, 2026 11:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants