Octopus: Support dual night rate tariffs with hard-wired times#3900
Merged
springfall2008 merged 4 commits intomainfrom May 10, 2026
Merged
Octopus: Support dual night rate tariffs with hard-wired times#3900springfall2008 merged 4 commits intomainfrom
springfall2008 merged 4 commits intomainfrom
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
This PR updates the Octopus tariff-fetching logic to support dual-register (day/night) electricity tariffs by detecting endpoint types via product metadata and generating hard-wired night-rate windows (IOG TOU / GO / Economy 7), alongside adding a dedicated unit test suite for the day/night window selection.
Changes:
- Add product-info lookup + in-memory caching to detect whether a tariff exposes
standard_unit_ratesvsday_unit_rates/night_unit_rates, and fetch accordingly. - Replace single hard-wired night window constants with a set of tariff-family window definitions (IOG/GO/Eco7) and update day/night schedule generation.
- Add/adjust unit tests and mocks to accommodate the new
json_onlyplumbing and validate window selection.
Reviewed changes
Copilot reviewed 6 out of 7 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| apps/predbat/octopus.py | Adds product-info link inspection, new night-window definitions, json_only fetch mode, and day/night rate schedule generation updates. |
| apps/predbat/tests/test_octopus_day_night_rates.py | New tests validating correct night window selection for IOG TOU, INTELLI, GO-style, and Economy 7 tariffs. |
| apps/predbat/tests/test_octopus_url.py | Updates download-url tests to reflect removal of 400-based day/night detection behavior. |
| apps/predbat/tests/test_octopus_misc.py | Updates fetch_tariffs mocks to accept new kwargs (json_only) and simulate product-info lookups. |
| apps/predbat/tests/test_fetch_url_cached.py | Updates mocked downloader signature to accept kwargs passed through caching. |
| apps/predbat/unit_test.py | Registers the new day/night rate test wrapper in the unit test runner. |
| .gitignore | Ignores temp_octopus directory used by local/manual Octopus testing. |
Comments suppressed due to low confidence (1)
apps/predbat/octopus.py:1291
- async_download_octopus_url() no longer accepts HTTP 400 responses. If the product-info lookup fails (or is unavailable) and a dual-register tariff still returns 400 for standard-unit-rates, fetch_tariffs() will now fail to retrieve any rates. Consider restoring 400 handling (e.g., parse the JSON "detail" and fall back to async_get_day_night_rates for day/night tariffs, or at least treat 400 as a handled client error rather than an immediate hard failure).
if response.status not in [200, 201]:
self.failures_total += 1
self.log("Warn: OctopusAPI: Error downloading Octopus data from URL {}, code {}".format(url, response.status))
record_api_call("octopus_url", False, "server_error")
return {}
Comment on lines
+1426
to
+1428
| # Standard rates, or product info unavailable — fall back to existing path | ||
| # (which handles the 400 "This tariff has day and night rates" error for Economy 7) | ||
| tariffs[tariff]["data"] = await self.fetch_url_cached(standard_url) |
| elif tariff_code and tariff_code.startswith("E-2R-"): | ||
| window = OCTOPUS_NIGHT_RATE_WINDOWS["eco7"] | ||
| else: | ||
| self.log("Warn: OctopusAPI: Unknown tariff code {}, defaulting to GO night rate window".format(tariff_code)) |
| Get day and night rates from Octopus. | ||
|
|
||
| Selects the correct night/day window based on the tariff type: | ||
| - IOG TOU (product_code contains INTELLI+IOG+TOU): 23:30 to 05:30 (crosses midnight) |
Comment on lines
+1344
to
1348
| async def fetch_url_cached(self, url, json_only=False): | ||
| """ | ||
| Fetch a URL from the cache or reload it | ||
| Uses individual file per URL in shared cache directory | ||
| Implements stale-while-revalidate to prevent thundering herd |
Comment on lines
+1418
to
+1424
| # Always check product links first to determine the correct rate endpoint type, | ||
| # avoiding the 400 round-trip for tariffs that only expose day/night rate endpoints. | ||
| link_types = await self._async_get_product_rate_link_types(product_code, tariff_code) | ||
| if link_types is not None and "standard_unit_rates" not in link_types and "day_unit_rates" in link_types: | ||
| # Day/night tariff (e.g. IOG-TOU, GO): fetch directly without hitting standard-unit-rates | ||
| self.log("Info: OctopusAPI: Product {} tariff {} has day/night rate endpoints (no standard-unit-rates), fetching directly".format(product_code, tariff_code)) | ||
| tariffs[tariff]["data"] = await self.async_get_day_night_rates(standard_url, product_code=product_code, tariff_code=tariff_code) |
|
|
||
|
|
||
| def _extract_schedule(mdata): | ||
| """Return a sorted list of (start_hour, start_min, end_hour, end_min, rate) tuples from mdata.""" |
…pred into fix/octopus_dual_rate
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.
No description provided.