Skip to content

Conversation

@Bre77
Copy link
Contributor

@Bre77 Bre77 commented May 29, 2025

Proposed change

Add calendar entities for energy site tariff schedules, providing:

  • Buy tariff calendar showing energy purchase pricing periods
  • Sell tariff calendar showing energy sell-back pricing periods

Features:

  • Displays current active pricing period
  • Shows seasonal pricing variations
  • Handles time-of-use (TOU) periods
  • Supports midnight crossing periods

This is a carve out from #142894

Type of change

  • Dependency upgrade
  • Bugfix (non-breaking change which fixes an issue)
  • New integration (thank you!)
  • New feature (which adds functionality to an existing integration)
  • Deprecation (breaking change to happen in the future)
  • Breaking change (fix/feature causing existing functionality to break)
  • Code quality improvements to existing code or addition of tests

Additional information

Checklist

  • The code change is tested and works locally.
  • Local tests pass. Your PR cannot be merged unless tests pass
  • There is no commented out code in this PR.
  • I have followed the development checklist
  • I have followed the perfect PR recommendations
  • The code has been formatted using Ruff (ruff format homeassistant tests)
  • Tests have been added to verify that the new code works.

If user exposed functionality or configuration variables are added/changed:

If the code communicates with devices, web services, or third-party tools:

  • The manifest file has all fields filled out correctly.
    Updated and included derived files by running: python3 -m script.hassfest.
  • New or updated dependencies have been added to requirements_all.txt.
    Updated by running python3 -m script.gen_requirements_all.
  • For the updated dependencies - a link to the changelog, or at minimum a diff between library versions is added to the PR description.

To help with the load of incoming pull requests:

@joostlek
Copy link
Member

I am wondering how well a calendar works for this. As in, people generally need this for raw data, while a calendar is used for things that happen during a day

@Bre77
Copy link
Contributor Author

Bre77 commented Jun 21, 2025

I am wondering how well a calendar works for this. As in, people generally need this for raw data, while a calendar is used for things that happen during a day

It works very well, it's time based and seasonal data. It lets users see the current value and future values.

Screenshot_20250621-185950.png

@joostlek
Copy link
Member

Right I agree that it shows, but I would argue that it's not a calendar thing. As in, if I would import this in my calendar app my app would be unusable.

@Bre77
Copy link
Contributor Author

Bre77 commented Jun 22, 2025

@joostlek are you saying this should just be a sensor entity with the current price?

@joostlek
Copy link
Member

And maybe a service with json return for forecasting data. But I'm not sure if my opinion is shared by others so I'm open to other arguments

@Bre77 Bre77 mentioned this pull request Jun 29, 2025
19 tasks
@dgomes
Copy link
Contributor

dgomes commented Jun 29, 2025

I'm struggling with a similar use case (sensor vs calendar)

The calendar has the benefit of conveying future information as the sensor will only show current values.

If I want to do an automation that needs to know the future tariff start time and duration the sensor is not enough.

@Bre77
Copy link
Contributor Author

Bre77 commented Jul 6, 2025

@joostlek the best case I have, is that a sensor entity will only ever tell you whats happening now, but the calendar entity allows more flexible automations with offsets from the start and end of the segment. https://www.home-assistant.io/integrations/calendar/#automation

The primary usecase I can think of here is that Tesla Powerwalls take a few minutes for certain actions to take effect, so a user may wish to have automations run a few minutes before a certain tarrif period starts or ends.

I fully accept people may never want to see this on a calandar in the UI, but its calendar like data that the Home Assistant calendar platform serves well.

There is a future possibility that I will allow editing through the create and delete event actions.

@Bre77
Copy link
Contributor Author

Bre77 commented Jul 22, 2025

@joostlek can I please bump this review.

@joostlek
Copy link
Member

I will throw this for in the architecture meeting on thursday

@Bre77
Copy link
Contributor Author

Bre77 commented Aug 10, 2025

@joostlek did you get an architecture answer on this?

@dgomes
Copy link
Contributor

dgomes commented Aug 19, 2025

looking into this issue before submitting yet another integration tracking tariff using a calendar entity.

@emontnemery
Copy link
Contributor

emontnemery commented Sep 16, 2025

This was discussed in the architecture meeting, but it was forgotten to update here.

The conclusion was:

We should implement both action responses and a calendar entity, and explore to add it to the energy dashboard

I think it's fine to split that in two PRs: One which adds the calendar (this PR) and one which adds a service returning data.

@dgomes dgomes mentioned this pull request Sep 16, 2025
18 tasks
@Bre77
Copy link
Contributor Author

Bre77 commented Sep 18, 2025

Thanks for letting me know @emontnemery, @joostlek does that mean we can push this one through?

@Bre77 Bre77 force-pushed the t-calendar-energy-price branch from 56c4bdb to 45aa5fc Compare September 18, 2025 03:53

def flatten(data: dict[str, Any], parent: str | None = None) -> dict[str, Any]:
def flatten(
data: dict[str, Any], parent: str | None = None, exceptions: list[str] | None = None
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
data: dict[str, Any], parent: str | None = None, exceptions: list[str] | None = None
data: dict[str, Any], parent: str | None = None, *, exceptions: list[str] | None = None

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What does this achieve? Is it simply forcing exceptions (now skip_keys) to be named?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, exactly. It improves readability because it's clearer what the optional passed in list is for

@emontnemery emontnemery marked this pull request as draft September 24, 2025 12:14
@emontnemery
Copy link
Contributor

@Bre77 please address the review comments and mark the PR ready for review when done

Add calendar entities for energy site tariff schedules, providing:
- Buy tariff calendar showing energy purchase pricing periods
- Sell tariff calendar showing energy sell-back pricing periods

Features:
- Displays current active pricing period
- Shows seasonal pricing variations
- Handles time-of-use (TOU) periods
- Supports midnight crossing periods
- Update TeslemetryEnergyInfoCoordinator to preserve tariff structure during flattening
- Fix test file to only test energy tariff calendars
- Update snapshots for energy price calendar entities
@Bre77 Bre77 force-pushed the t-calendar-energy-price branch from 45aa5fc to 09c50ce Compare September 26, 2025 23:28
@Bre77 Bre77 marked this pull request as ready for review September 27, 2025 07:35
Comment on lines +89 to +90
from_day = period_def.get("fromDayOfWeek", 0)
to_day = period_def.get("toDayOfWeek", 6)
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it correct that the API doesn't always set fromDayOfWeek + toDayOfWeek, and are the defaults correct?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, the data outside of the API is a protobuf, which treats missing values as their defaults, so we have to fill this back in, as the default is the full week.

Copy link
Contributor

Choose a reason for hiding this comment

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

OK. Can you add a comment in the code then with a reference to where the defaults are defined?

Comment on lines +95 to +98
from_hour = period_def.get("fromHour", 0) % 24
from_minute = period_def.get("fromMinute", 0) % 60
to_hour = period_def.get("toHour", 0) % 24
to_minute = period_def.get("toMinute", 0) % 60
Copy link
Contributor

Choose a reason for hiding this comment

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

The defaults here differ from the defaults for weekdays, is that correct?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I dont know what your referring to, this code has no concept of weekdays and weekends, its just start days of week and end days of week.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So for example, the default here is all values as zero, which equals a full day due to the Handle periods that cross midnight logic.

Copy link
Contributor

Choose a reason for hiding this comment

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

I mean that the default for fromDayOfWeek is 0, which means Monday, and the default for toDayOfWeek is 6 which means Sunday, whereas the defaults for from_hour, from_minute, to_hour, to_minute are all 0 which means midnight.
Hence if nothing is set, the period starts at 00:00 Monday, and ends at 00:00 Sunday, that is, the default period spans 6 full days, not a full week. Is that correct?

Comment on lines +107 to +117
# Handle periods that cross midnight
if end_time <= start_time:
potential_end_time = end_time + timedelta(days=1)
if start_time <= now < potential_end_time:
end_time = potential_end_time
elif (start_time - timedelta(days=1)) <= now < end_time:
start_time -= timedelta(days=1)
else:
continue
elif not (start_time <= now < end_time):
continue
Copy link
Contributor

Choose a reason for hiding this comment

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

Please add test cases which assert the behavior here. It's very easy to mess up, and even if your code is correct now it might break tomorrow if the code is changed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I am not sure which behavior specifically you are referring to, as this would already be tested with the fixture and snapshot.

Copy link
Contributor

Choose a reason for hiding this comment

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

There's a single, non-parametrized, test case each to test the state of the calendar entity as well as testing the get_events service.

What I mean is that you should add parametrization or separate test cases testing the different branches to account for crossing midnight, crossing weeks and so on, since that logic is non trivial and very easy to mess up.


return None # No active period found for the current time and season

async def async_get_events(
Copy link
Contributor

Choose a reason for hiding this comment

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

Same comments as for the event property

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have tried to address feedback in multiple places where I noticed.

@home-assistant home-assistant bot marked this pull request as draft November 3, 2025 14:04
@home-assistant
Copy link

home-assistant bot commented Nov 3, 2025

Please take a look at the requested changes, and use the Ready for review button when you are done, thanks 👍

Learn more about our pull request process.

@Bre77 Bre77 requested a review from emontnemery November 3, 2025 23:27
@Bre77 Bre77 marked this pull request as ready for review November 3, 2025 23:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants