Skip to content

feat(teslemetry): enable teslemetry_tbc_control by default - #5188

Merged
springfall2008 merged 3 commits into
mainfrom
feat/teslemetry-tbc-control-default-on-5186
Sep 24, 2026
Merged

springfall2008 merged 3 commits into
mainfrom
feat/teslemetry-tbc-control-default-on-5186

Conversation

@springfall2008

Copy link
Copy Markdown
Owner

This is an automated draft PR generated from issue #5186 — a maintainer should review it before merging.

Fixes #5186

Summary

Makes teslemetry_tbc_control on by default. Before this it was an opt-in trial setting, added in PR #4976 for GH#4892. If apps.yaml doesn't set the key, the Teslemetry component now pushes the signal tariff (0p over the charge window, 50p import elsewhere, 100p over the export window) and puts the Powerwall in autonomous mode, so Tesla's Opticaster charges at full rate. Setting teslemetry_tbc_control: False explicitly still gives the real-rate tariff and reserve-driven charging.

  • apps/predbat/components.py: registry default for tbc_control changed from False to True. This is the functional change: Components.start() resolves an unset key to this value (components.py:806).
  • apps/predbat/teslemetry.py: initialize()'s own tbc_control default also changed to True, and the docstring updated. Production always passes the registry value, so this doesn't change production behaviour. It keeps a direct construction on the same path as production; the __main__ CLI test run builds the class without passing tbc_control.
  • templates/teslemetry.yaml: the commented #teslemetry_tbc_control: True hint is replaced with a commented opt-out, #teslemetry_tbc_control: False.
  • docs/components.md and docs/inverter-setup.md: the defaults table, the "Important notes" tariff bullets and the Teslemetry setup section now describe the setting as on by default, with an opt-out. The setup section's example YAML previously had an uncommented teslemetry_tbc_control: False, so anyone copying it would have stayed opted out. That line is now a commented opt-out.

Testing

  • ./run_pre_commit (from coverage/): every hook passed, and run_all --quick passed (4 slow tests skipped). I ran it before and after the self-review edits.
  • tools/triage_test.sh teslemetry: fails without the fix and passes with it. With only components.py and teslemetry.py stashed, the new test_teslemetry_tbc_control_defaults_on_via_the_registry fails at assert api.tbc_control is True. With the fix restored, the module passes.
  • tools/triage_test.sh components: passes (9/9).

Test changes in apps/predbat/tests/test_teslemetry.py:

  • New: test_teslemetry_tbc_control_defaults_on_via_the_registry. Takes the registry default, passes it through initialize(), and checks the TBC path is selected (autonomous mode inside a charge window). It also checks that a bare initialize() agrees with the registry default.
  • Renamed: test_teslemetry_tbc_control_defaults_off_and_uses_the_real_rate_tariff is now test_teslemetry_tbc_control_off_uses_the_real_rate_tariff, and it sets tbc_control = False explicitly. It now tests the opt-out rather than a default.
  • Extended: test_teslemetry_initialize_sets_tbc_control_from_component_arg now also asserts that initialize(tbc_control=False) is honoured.
  • Updated: test_teslemetry_component_registry_config now pins the registry default as True.

Notes

  • Who is affected. The emulator loop in run() (teslemetry.py:508) is gated on read-only mode but not on teslemetry_automatic. So after upgrading, every non-read-only Teslemetry user who hasn't set the key moves to the signal tariff and autonomous mode on their first cycle. That includes users who don't have teslemetry_automatic: True. The triage comment said only automatic users would be affected; that isn't quite right. Two visible changes: the Tesla app will show the 0/50/100p signal tariff instead of the user's real rates, and Predbat's charge and export targets become advisory. Probably worth a line in the release notes.
  • 81-99% reserve limitation, now on by default. The limitation documented in docs/inverter-setup.md now applies to anyone on the default: with set_reserve_min anywhere in 81-99, grid charging stays disabled in every state, and the only explanation is a one-off log line. This PR doesn't change that behaviour. It's a judgement for the maintainer whether that needs a louder diagnostic before the setting defaults on.
  • Two defaults I left alone.
    • The getattr(self, "tbc_control", False) fallbacks in evaluate_schedule and sync_tariff (teslemetry.py:592/:1414) still default to False. They can't be reached in production, because initialize() always sets the attribute, so I kept this change scoped.
    • MockTeslemetryAPI still sets tbc_control = False, so the roughly 100 existing tests written against the real-rate path keep testing it. A comment on the mock says why, and the TBC tests opt in explicitly.
  • Related: Teslemetry: flat-priced tariff across the whole export window lets Tesla's own TBC defer most of the discharge to the last ~30 minutes #4887. That issue (a flat ON_PEAK band on the real-rate tariff path) now only affects users who opt out.
  • Blast radius. GitNexus impact on TeslemetryAPI.initialize is LOW with no static callers; the registry constructs the class dynamically. COMPONENT_LIST is a data constant that GitNexus can't resolve as a target, so I found its readers by search: the registry resolver (components.py:806), the web component-config view (web.py:5483, which only displays the default), and the tests. detect_changes: LOW risk, 0 affected execution flows.
  • The Teslemetry entry in tools/debug-journal.md informed this change: it describes the TBC path and the 81-99% rounding.

🤖 Generated with Claude Code

Graduate the Time-Based Control trial setting (added in PR #4976 for
GH#4892) to default-on: an apps.yaml that never sets
teslemetry_tbc_control now gets the signal tariff and autonomous mode,
so Tesla's Opticaster runs the charge at full rate. An explicit
teslemetry_tbc_control: False keeps the real-rate tariff and
reserve-driven charging.

The registry default and initialize()'s own default are flipped
together so a direct construction (the CLI test run) takes the same
path as production. Template and docs now describe the setting as
on by default with an opt-out.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@springfall2008 springfall2008 self-assigned this Sep 21, 2026
@springfall2008 springfall2008 added the BOT_REVIEW Trigger an autotriage label Sep 21, 2026
@springfall2008
springfall2008 marked this pull request as ready for review September 21, 2026 19:35
Copilot AI lite review requested due to automatic review settings September 21, 2026 19:35

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

🔵 Needs a closer look

The registry-to-constructor default wiring is not directly covered by the test, and the related lifecycle docstring needs correction.

Review effort: Lite
Findings: None

What changed in this PR

Enables Teslemetry Time-Based Control by default while retaining an explicit opt-out.

Changes:

  • Updates registry and initialization defaults to True.
  • Documents the new default and opt-out configuration.
  • Updates tests for default-on and explicit-off behavior.
File Summary
templates/​teslemetry.yaml Adds a commented opt-out configuration.
docs/​inverter-setup.md Documents default-on behavior, opt-out guidance, and limitations.
docs/​components.md Updates component defaults and tariff behavior.
apps/​predbat/​tests/​test_teslemetry.py Updates default and opt-out tests. A moderate issue remains: the test does not exercise Components.initialize() default resolution; a docstring lifecycle reference also needs correction.
apps/​predbat/​teslemetry.py Aligns direct initialization with the new default.
apps/​predbat/​components.py Enables TBC by default in the component registry.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread apps/predbat/components.py
Comment thread apps/predbat/components.py
Comment thread apps/predbat/teslemetry.py Outdated
Comment thread apps/predbat/teslemetry.py Outdated
Comment thread apps/predbat/teslemetry.py Outdated
Comment thread apps/predbat/tests/test_teslemetry.py
Comment thread docs/inverter-setup.md Outdated
@springfall2008 springfall2008 added BOT_CLEANUP Trigger: bot should address PR review feedback and CI failures, then commit and push and removed BOT_REVIEW Trigger an autotriage labels Sep 21, 2026
CI and others added 2 commits September 24, 2026 08:13
…gnostics

Addresses review feedback on #5188:

- components.py: a registry default is now applied when apps.yaml has the key
  present but empty. A YAML null reached get_arg as None, and its bool branch
  only coerces strings - unlike the int/float branches it has no None fallback -
  so "on by default" silently did not apply, with nothing logged.
- teslemetry.py: DEFAULT_TBC_CONTROL is now a single class constant, read by
  initialize() and by both getattr(self, "tbc_control", ...) fallbacks, which
  still encoded the old False and so would have selected the non-default path.
- teslemetry.py: the diagnostic CLI takes --tbc-control/--no-tbc-control and
  names the control path in its mode banner, so a --control run cannot silently
  flip the Powerwall to the signal tariff and autonomous mode.
- teslemetry.py: the 81-99 reserve-band diagnostic is now Warn rather than Info,
  since that band is reachable by a plausible set_reserve_min on the default path.
- docs/inverter-setup.md: "on by default" is scoped to within the beta component,
  which resolves the contradiction with the beta warning above it.

Tests: a Components-level test pins the resolution end-to-end (absent / null /
explicit False), and a teslemetry test pins the fallback and the registry-to-
constant agreement. Both fail without their fix.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@springfall2008 springfall2008 removed the BOT_CLEANUP Trigger: bot should address PR review feedback and CI failures, then commit and push label Sep 24, 2026
@springfall2008
springfall2008 merged commit 7a0a44d into main Sep 24, 2026
2 checks passed
@springfall2008
springfall2008 deleted the feat/teslemetry-tbc-control-default-on-5186 branch September 24, 2026 07:56
springfall2008 pushed a commit that referenced this pull request Sep 26, 2026
Resolve debug-journal conflicts with #5223's fold of the same merge batch:
- Solis: keep this PR's #5189 detail and write-skip/test notes, then main's GH#5152 window-scoped hold lever
- GE Cloud: main's #5109 wording plus the once-per-episode ems_slot_warned warning and its log text
- Teslemetry: this PR's #5188 default-flip consequences plus main's GH#5157 forced re-assert
- Manual rates: fold both rows into one (#5173 fix, surviving :59:59 boundary-minute and offset traps, test lesson)
- Kraken, Sunsynk, Octopus, Car charging from main; Axle, GivTCP, Compare and the three new rows from this PR

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
springfall2008 added a commit that referenced this pull request Sep 26, 2026
…9-26

docs(debug-journal): fold in the 2026-09-26 queue slice (15 candidates); correct entries overtaken by the #5109/#5173/#5185/#5188/#5189 merge batch
CossieRob pushed a commit to CossieRob/batpred that referenced this pull request Sep 26, 2026
…pringfall2008#5103/springfall2008#5186/springfall2008#5053-adjacent entries

Folded (13 entries from the 15-candidate slice, all re-verified against origin/main 130596b):
- Load ML row (GH#5075, PR springfall2008#5112 open draft): is_valid() can report active for a never-trained
  model; training_timestamp is None is not a sound never-trained test (legacy saved models);
  train() stamps at the end of every curriculum pass, so an abort leaves fresh stamps that
  suppress the ml_max_model_age_hours retrain. Normalisation-statistics skew stays suspected.
- Octopus row: GH#5144 overlapping DIRECT/NON_DIRECT_DEBIT rows, minute_data last-row-wins and
  the period-to-period flip - fixed in PR springfall2008#5145 (filter_payment_method, keeps nulls).
- Car charging row: GH#5146/GH#5151 the two different Hold-for-car gates (execute.py live block
  vs prediction.py plan gate), deliberate export-window exemption test-codified
  (discharge_car_full_bat2), read-only users never see the status sensor's annotation.
- Sunsynk/DEYE row: PR springfall2008#5150 control_active persistence + upgrade-cache inference; the in-code
  press every cycle comment is wrong (both press sites change-gated, post-restart re-commit is
  is_hm_format-only); GH#5156 TOU padding truncation fixed in tou_schedule.py _padding_segments.
- Solis row: GH#5152 the hold lever is the window-scoped timed current (standing
  battery_discharge_current never driven); PR springfall2008#5189 adds the rated-current cap (GH#5187).
- Predheat row: GH#5153 no Fahrenheit conversion in get_weather_data + the optional
  heating_energy age bug pinning minute_data_age to 0.
- Teslemetry row: GH#5157 forced re-assert implemented (FORCED_ASSERT_SECONDS = 2h, dedupe
  bypassed, tariff excluded); tbc_control default on since PR springfall2008#5188.
- Keep section: GH#5162 manual_soc floor penalty is import-rate-weighted, so inert inside 0p
  windows; the manual_soc_max ceiling is export-rate-weighted; manual_charge lookalike trap.
- New Kraken row: GH#5166/PR springfall2008#5167 EDF 400-on-day/night vs Octopus 200-empty;
  KRAKEN_REST_RATES_UNAVAILABLE_STATUSES; the (None, None) tuple contract and the
  failures_total double-count, both fixed in the merged PR and kept as mechanisms.
- New Manual rates row: GH#5168 day_of_week stamped across the whole rate horizon, fixed in
  PR springfall2008#5173; end ':59:59' final-minute trap and the metric_future_rate_offset test pin kept.

Dropped: 5169-5053-cache-now-on-main (already covered by the redaction entry and the GH#5063
premise bullet; its lock-lazy-init nugget is recorded as a fact of the merged PR springfall2008#5171 mixin).

Corrected by the merge scan: GH#5103 fixed in PR springfall2008#5109 (hourly settings refresh + the new
check_ems_inverter_slots warning); teslemetry_tbc_control default on (PR springfall2008#5188); the
log-redaction cache moved into the LogRedaction mixin (log_secrets.py, PR springfall2008#5171).

Co-Authored-By: Claude Code <noreply@anthropic.com>
CossieRob pushed a commit to CossieRob/batpred that referenced this pull request Sep 26, 2026
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.

Feat(Teslemetry) - enable tbc control by default

2 participants