Skip to content

Support single-zone static price in tariff_zones provider#324

Merged
MaStr merged 3 commits into
mainfrom
claude/static-single-price-mode-uFXee
Apr 14, 2026
Merged

Support single-zone static price in tariff_zones provider#324
MaStr merged 3 commits into
mainfrom
claude/static-single-price-mode-uFXee

Conversation

@MaStr
Copy link
Copy Markdown
Owner

@MaStr MaStr commented Apr 14, 2026

Summary

Converts the tariff_zones price mode into a flexible 1-, 2-, or 3-zone provider so it also covers the "static single price" use case from #318. When only tariff_zone_1 is configured, zone_1_hours defaults to all 24 hours and every hour of the day gets the same flat price — which is exactly what a non-dynamic-price user needs for peak-shaving scenarios.

Existing 2-zone and 3-zone configurations are fully backwards-compatible; only the previously-required zone 2 became optional.

Changes

  • tariffzones.py: make tariff_zone_2 / zone_2_hours optional (must still be paired together); default zone_1_hours to 0-23 when zone 1 is the only configured zone; updated docstrings.
  • dynamictariff.py factory: require only tariff_zone_1; validate zone 2 / zone 3 price+hours pairing with consistent error wording.
  • config/batcontrol_config_dummy.yaml: document the single-zone static-price mode.
  • tests/.../test_tariffzones.py: add tests for single-zone default hours, explicit full-day hours, partial-hour rejection, factory single-zone config, missing tariff_zone_1, and orphan zone-2 price/hours; adjust pre-existing assertions to the new (clearer) error messages.

Example new static-price config:

utility:
  type: tariff_zones
  tariff_zone_1: 0.30   # flat price for the whole day, EUR/kWh incl. vat/fees

Refs #318.

Test plan

  • python -m pytest tests/batcontrol/dynamictariff/ — 77 passed
  • Smoke-check a real single-zone YAML config in a running instance
  • Verify existing 2-zone/3-zone configs still load unchanged

claude added 2 commits April 14, 2026 18:18
Convert the tariff_zones provider into a flexible 1-, 2-, or 3-zone mode,
so it also covers the "static price mode" use case (issue #318). When only
tariff_zone_1 is configured, zone_1_hours defaults to all 24 hours and the
provider returns a flat price for every hour.

- Make tariff_zone_2 / zone_2_hours optional (must still be paired)
- Require tariff_zone_1 only in the factory; validate zone_2 pairing
- Default zone_1_hours to 0-23 in single-zone mode
- Update config example and docstrings
- Add tests covering single-zone static price mode and zone-2 pairing
Align factory and class-level error messages for zone 2/3 paired-option
checks, and clarify in the docstring that an explicitly-set zone_1_hours
is not auto-extended in single-zone mode.
Copilot AI review requested due to automatic review settings April 14, 2026 18:20
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds support for using the tariff_zones dynamic tariff provider as a single-zone “static price” mode (flat price all day) while keeping existing 2-/3-zone configurations working.

Changes:

  • Allow TariffZones to run with only tariff_zone_1 by defaulting zone_1_hours to all 24 hours in single-zone mode.
  • Update DynamicTariff.create_tarif_provider() to only require tariff_zone_1 and to validate zone-2/zone-3 price+hours pairing.
  • Extend docs (dummy config) and tests to cover single-zone behavior and updated error messages.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
tests/batcontrol/dynamictariff/test_tariffzones.py Adds/updates tests for single-zone static pricing and revised validation/error cases.
src/batcontrol/dynamictariff/tariffzones.py Implements single-zone default hours and makes zone 2 optional with paired validation.
src/batcontrol/dynamictariff/dynamictariff.py Loosens required fields for tariff_zones and adds zone-2/zone-3 pairing checks in the factory.
config/batcontrol_config_dummy.yaml Documents the single-zone static-price configuration mode.

Comment on lines 133 to 166
elif provider.lower() == 'tariff_zones':
required_fields = ['tariff_zone_1', 'zone_1_hours', 'tariff_zone_2', 'zone_2_hours']
for field in required_fields:
if field not in config:
# Only tariff_zone_1 is strictly required. A single-zone
# configuration acts as a static flat price for all 24 hours.
if 'tariff_zone_1' not in config:
raise RuntimeError(
'[DynTariff] Please include tariff_zone_1 in your configuration file'
)
# zone_2 and zone_3 are optional, but price and hours must be
# provided together for each additional zone.
for zone in (2, 3):
price_key = f'tariff_zone_{zone}'
hours_key = f'zone_{zone}_hours'
if (price_key in config) != (hours_key in config):
raise RuntimeError(
f'[DynTariff] Please include {field} in your configuration file'
f'[DynTariff] {hours_key} and {price_key} must both be '
'set or both omitted'
)
zone_3_hours = config.get('zone_3_hours')
zone_1_hours = config.get('zone_1_hours')
tariff_zone_2 = config.get('tariff_zone_2')
zone_2_hours = config.get('zone_2_hours')
tariff_zone_3 = config.get('tariff_zone_3')
zone_3_hours = config.get('zone_3_hours')
selected_tariff = TariffZones(
timezone,
min_time_between_api_calls,
delay_evaluation_by_seconds,
target_resolution=target_resolution,
tariff_zone_1=float(config['tariff_zone_1']),
zone_1_hours=config['zone_1_hours'],
tariff_zone_2=float(config['tariff_zone_2']),
zone_2_hours=config['zone_2_hours'],
zone_1_hours=zone_1_hours,
tariff_zone_2=float(tariff_zone_2) if tariff_zone_2 is not None else None,
zone_2_hours=zone_2_hours,
tariff_zone_3=float(tariff_zone_3) if tariff_zone_3 is not None else None,
zone_3_hours=zone_3_hours,
)
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

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

In the tariff_zones factory branch, zone_1_hours is no longer validated when additional zones are configured. This means a misconfigured 2-/3-zone setup (e.g., tariff_zone_2+zone_2_hours present but no zone_1_hours) will successfully create the provider and only fail later when prices are requested, which is a behavior regression vs. the previous required-fields check. Consider adding an explicit factory-time validation: if any additional zone (2 or 3) is configured, require zone_1_hours to be present/non-null and raise a clear [DynTariff] error immediately (and add/adjust a regression test accordingly).

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Good catch — fixed in 86c0dd0. The factory now raises immediately when tariff_zone_2 or tariff_zone_3 is present but zone_1_hours is missing, and there's a regression test (test_factory_requires_zone_1_hours_when_extra_zones_configured) covering it.


Generated by Claude Code

Addresses Copilot review feedback on #324: with zone 2 or zone 3
configured, a missing zone_1_hours used to fail at first price fetch.
Restore the fail-fast check at factory construction and add a regression
test.
@MaStr MaStr merged commit 34a37f8 into main Apr 14, 2026
13 checks passed
@MaStr MaStr deleted the claude/static-single-price-mode-uFXee branch April 14, 2026 18:34
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.

3 participants