feat(funnel): add Teslemetry stream publisher - #136
Merged
Conversation
Streaming is meant to be the primary source of truth for the funnel's three fields, with Bluetooth opportunistic - but until now only the Bluetooth publisher existed. Add TeslemetryStreamPublisher alongside it, translating a caller-supplied stream signal update into the same FieldPath set BleBroadcastPublisher already feeds. Follows VehicleDataResultPublisher's precedent: the update is an argument (one stream push's data mapping, keyed by signal name) rather than a held client, so this library stays independent of the separate teslemetry-stream package. No source ranking is introduced - the funnel has none by design, and a real-world race between the two sources is what will decide it.
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
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.
Intent
Add a Teslemetry stream publisher to the observation funnel in python-tesla-fleet-api. Streaming is meant to be the primary source of truth for the funnel's three fields (Locked, ChargePortDoorOpen, DoorState.TrunkFront), with Bluetooth as the opportunistic secondary — but until now only the Bluetooth publisher (BleBroadcastPublisher) existed, which inverted that. Added TeslemetryStreamPublisher alongside it, feeding the same FieldPath set.
Deliberate dependency-direction decision: the stream client lives in the separate teslemetry-stream PyPI package. Rather than hard-depending on it, TeslemetryStreamPublisher follows the existing VehicleDataResultPublisher precedent — it takes a caller-supplied payload as an argument (publish_update(data)) and holds no client, session, or callable able to obtain one. I fetched the real teslemetry-stream source from GitHub to confirm the actual wire shape a stream push takes: a data mapping keyed by signal name (Locked, ChargePortDoorOpen, DoorState with a nested TrunkFront), and that some vehicles stream booleans as literal 'true'/'false' strings rather than JSON booleans even when the stream prefers typed values (confirmed from that package's own make_bool coercion) — the new publisher's translation handles that quirk.
Explicitly out of scope, per direction: no source ranking, precedence, or conflict-resolution logic between Bluetooth and streaming was added — the funnel has none by design today, and that is intentional; a real-world race between the two sources is what will decide ranking, not a guess baked in now. Also out of scope: no changes to vehicle_data or VehicleDataResultPublisher beyond using it as a structural precedent, and no changes to the Home Assistant integration (attaching this publisher there is separate downstream work).
Backwards compatible: purely additive (one new class, two new init.py exports). Existing Bluetooth funnel behavior is verified unchanged — all pre-existing funnel tests pass unmodified, and a new regression test proves both publishers reach the same funnel listeners with neither ranked over the other. New tests are colocated in tests/test_funnel_stream.py, matching this repo's existing test_funnel_vehicle_data.py/test_funnel_bluetooth.py style and coverage shape (signal translation edge cases, funnelling/dedup behavior, and proof the publisher cannot originate a request). Full suite (725 tests), ruff, and pyright strict all pass.
What Changed
TeslemetryStreamPublishertotesla_fleet_api/funnel.py, a passive publisher that translates a caller-supplied Teslemetry stream update (publish_update(data)) intoLocked/ChargePortDoorOpen/DoorState.TrunkFrontobservations, matching theVehicleDataResultPublisherprecedent of holding no client/session/callable of its own."true"/"false"string-encoded booleans alongside real JSON booleans, and accepts signal keys matching theteslemetry-streampackage's ownSignalenum values without depending on that package.TeslemetryStreamPublisherfromtesla_fleet_api/__init__.pyand documented it inAGENTS.mdalongside the existingObservationFunnel/BleBroadcastPublisherdescription, clarifying streaming is the intended primary source with Bluetooth as opportunistic secondary (no ranking/precedence logic added between them).tests/test_funnel_stream.pycovering signal translation edge cases, funnel dedup behavior, and that the publisher cannot originate a request.Risk Assessment
✅ Low: Purely additive, well-isolated change (one new class + two exports) that closely mirrors the existing VehicleDataResultPublisher pattern; traced the absent/null/string-boolean translation logic and found it correct in every case, and the new tests exercise real behavior through the public API without touching existing funnel code paths.
Testing
Ran the targeted funnel test modules (new tests/test_funnel_stream.py plus the three pre-existing funnel test files) end-to-end via pytest — all 79 tests passed, including the new signal-translation edge cases (string 'true'/'false' coercion, absent-vs-null leaf handling, malformed DoorState), the funnel-dedup/no-re-dispatch behavior, the cannot-originate-a-request proof, and a regression test proving both BleBroadcastPublisher and TeslemetryStreamPublisher reach the same funnel listeners with neither ranked over the other. The pre-existing AST-based synchronous-module lock test also still passes with the new class added. Working tree is clean with no stray artifacts from the test run.
Pipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
✅ **Rebase** - passed
✅ No issues found.
✅ **Review** - passed
✅ No issues found.
✅ **Test** - passed
✅ No issues found.
uv run pytest tests/test_funnel_stream.py tests/test_funnel.py tests/test_funnel_bluetooth.py tests/test_funnel_vehicle_data.py -q— 79 passeduv run pytest tests/test_funnel.py -k Cannot -q— the AST-based synchronous-module lock (TestFunnelCannotOriginateWork) still passes with the new publisher addedManual code read of the diff (funnel.py_translate/publish_update,__init__.pyexports) confirming_leaf's absent-vs-None sentinel semantics are preserved for the new publisher and thatTestRegressionWalkthrough.test_streaming_and_bluetooth_both_reach_the_same_listenersin tests/test_funnel_stream.py exercises both BleBroadcastPublisher and TeslemetryStreamPublisher against one funnel with neither ranked over the other✅ **Document** - passed
✅ No issues found.
✅ **Lint** - passed
✅ No issues found.
✅ **Push** - passed
✅ No issues found.