feat: Add month and end_time to time window configuration

Extend TimeWindow with two new optional fields:

- month (1–12): restricts the window to a start date in the given
  calendar month. Useful for seasonal schedules.

- end_time: explicit exclusive end-of-window clock time. The effective
  end is the later of end_time and start_time + duration. Primary
  motivation is DST handling — a wall-clock end_time stays correct
  across DST transitions, whereas a fixed duration does not.

Rework window semantics so date, weekday, and month restrictions apply
to the window's start date, not to every timestamp covered by the
window. This makes overnight windows (e.g. Friday 22:00 → Saturday
06:00) behave intuitively under calendar constraints; contains() now
checks both today's and yesterday's candidate start.

duration becomes optional (default 0). All start/end/duration
computations route through _window_start_end and a new
_matches_start_date helper, so contains, earliest_start_time,
latest_start_time, and available_duration stay consistent.

Dashboard: add end_time and month inputs to the time-window editor,
render the collapsed window summary as icons + pills.

Docs, OpenAPI spec, and generated config docs are regenerated for the
new fields. Tests cover month filtering, end_time boundary cases (incl.
midnight and overnight), and start-date semantics for overnight windows.

Bump version to support automatic updates in Home Assistant.

Signed-off-by: Bobby Noelte <b0661n0e17e@gmail.com>
This commit is contained in:
Bobby Noelte
2026-10-28 18:59:43 +01:00
parent 8a8cd43a19
commit c02d250639
12 changed files with 1196 additions and 472 deletions
+19 -3
View File
@@ -124,10 +124,12 @@ This model extends `TimeWindow` by associating a value with the defined time int
| Name | Type | Read-Only | Default | Description |
| ---- | ---- | --------- | ------- | ----------- |
| date | `Optional[pydantic_extra_types.pendulum_dt.Date]` | `rw` | `None` | Optional specific calendar date for the time window. Naive — matched against the local date of the datetime passed to contains(). Overrides `day_of_week` if set. |
| day_of_week | `Union[int, str, NoneType]` | `rw` | `None` | Optional day of the week restriction. Can be specified as integer (0=Monday to 6=Sunday) or localized weekday name. If None, applies every day unless `date` is set. |
| duration | `Duration` | `rw` | `required` | Duration of the time window starting from `start_time`. |
| date | `Optional[pydantic_extra_types.pendulum_dt.Date]` | `rw` | `None` | Optional specific calendar date on which the time window must start. For overnight windows, this restriction applies to the start date, not the date of each datetime tested by contains(). |
| day_of_week | `Union[int, str, NoneType]` | `rw` | `None` | Optional day of the week on which the time window must start. Can be specified as integer (0=Monday to 6=Sunday) or localized weekday name. For overnight windows, the restriction applies to the start date. |
| duration | `Duration` | `rw` | `0 microseconds` | Duration of the time window starting from `start_time`. |
| end_time | `Optional[akkudoktoreos.utils.datetimeutil.Time]` | `rw` | `None` | Optional exclusive end time of the window. The effective end is the later of end_time and start_time + duration. End time is dalight saving time change save, whereas duration is always fixed. If None (the default), the window ends at start_time + duration. |
| locale | `Optional[str]` | `rw` | `None` | Locale used to parse weekday names in `day_of_week` when given as string. If not set, Pendulum's default locale is used. Examples: 'en', 'de', 'fr', etc. |
| month | `Optional[int]` | `rw` | `None` | Optional calendar month restriction (1=January to 12=December). The window's start date must fall in this month, including for overnight windows that extend into the following month. |
| start_time | `Time` | `rw` | `required` | Naive start time of the time window (time of day, no timezone). Interpreted in the timezone of the datetime passed to contains() or earliest_start_time(). |
| value | `Optional[float]` | `rw` | `None` | Value applicable during this time window. |
:::
@@ -147,8 +149,10 @@ This model extends `TimeWindow` by associating a value with the defined time int
{
"start_time": "00:00:00.000000",
"duration": "2 hours",
"end_time": null,
"day_of_week": null,
"date": null,
"month": null,
"locale": null,
"value": 0.288
}
@@ -230,16 +234,20 @@ fee applicable during that interval.
{
"start_time": "00:00:00.000000",
"duration": "8 hours",
"end_time": null,
"day_of_week": null,
"date": null,
"month": null,
"locale": null,
"value": 0.00288
},
{
"start_time": "08:00:00.000000",
"duration": "16 hours",
"end_time": null,
"day_of_week": null,
"date": null,
"month": null,
"locale": null,
"value": 0.0034
}
@@ -250,8 +258,10 @@ fee applicable during that interval.
{
"start_time": "00:00:00.000000",
"duration": "1 day",
"end_time": null,
"day_of_week": null,
"date": null,
"month": null,
"locale": null,
"value": 19.0
}
@@ -262,16 +272,20 @@ fee applicable during that interval.
{
"start_time": "00:00:00.000000",
"duration": "8 hours",
"end_time": null,
"day_of_week": null,
"date": null,
"month": null,
"locale": null,
"value": 0.00288
},
{
"start_time": "08:00:00.000000",
"duration": "16 hours",
"end_time": null,
"day_of_week": null,
"date": null,
"month": null,
"locale": null,
"value": 0.0034
}
@@ -282,8 +296,10 @@ fee applicable during that interval.
{
"start_time": "00:00:00.000000",
"duration": "1 day",
"end_time": null,
"day_of_week": null,
"date": null,
"month": null,
"locale": null,
"value": 19.0
}
+4
View File
@@ -249,16 +249,20 @@ price applicable during that interval.
{
"start_time": "00:00:00.000000",
"duration": "8 hours",
"end_time": null,
"day_of_week": null,
"date": null,
"month": null,
"locale": null,
"value": 0.288
},
{
"start_time": "08:00:00.000000",
"duration": "16 hours",
"end_time": null,
"day_of_week": null,
"date": null,
"month": null,
"locale": null,
"value": 0.34
}
+4
View File
@@ -175,16 +175,20 @@
{
"start_time": "00:00:00.000000",
"duration": "8 hours",
"end_time": null,
"day_of_week": null,
"date": null,
"month": null,
"locale": null,
"value": 0.028
},
{
"start_time": "08:00:00.000000",
"duration": "16 hours",
"end_time": null,
"day_of_week": null,
"date": null,
"month": null,
"locale": null,
"value": 0.034
}
+1 -1
View File
@@ -1,6 +1,6 @@
# Akkudoktor-EOS
**Version**: `v0.3.0.dev2610260646141478`
**Version**: `v0.3.0.dev2610261491542381`
<!-- pyml disable line-length -->
**Description**: This project provides a comprehensive solution for simulating and optimizing an energy system based on renewable energy sources. With a focus on photovoltaic (PV) systems, battery storage (batteries), load management (consumer requirements), heat pumps, electric vehicles, and consideration of electricity price data, this system enables forecasting and optimization of energy flow and costs over a specified period.
File diff suppressed because it is too large Load Diff