mirror of
https://github.com/jpawlowski/hass.tibber_prices.git
synced 2026-03-30 05:13:40 +00:00
feat(services): add rolling window options with auto-zoom for ApexCharts
Added two new rolling window options for get_apexcharts_yaml service to provide flexible dynamic chart visualization: - rolling_window: Fixed 48h window that automatically shifts between yesterday+today and today+tomorrow based on data availability - rolling_window_autozoom: Same as rolling_window but with progressive zoom-in (2h lookback + remaining time until midnight, updates every 15min) Implementation changes: - Updated service schema validation to accept new day options - Added entity mapping patterns for both rolling modes - Implemented minute-based graph_span calculation with quarter-hour alignment - Added config-template-card integration for dynamic span updates - Used current_interval_price sensor as 15-minute update trigger - Unified data loading: both rolling modes omit day parameter for dynamic selection - Applied ternary operator pattern for cleaner day_param logic - Made grid lines more subtle (borderColor #f5f5f5, strokeDashArray 0) Translation updates: - Added selector options in all 5 languages (de, en, nb, nl, sv) - Updated field descriptions to include default behavior and new options - Documented that rolling window is default when day parameter omitted Documentation updates: - Updated user docs (actions.md, automation-examples.md) with new options - Added detailed explanation of day parameter options - Included examples for both rolling_window and rolling_window_autozoom modes Impact: Users can now create auto-adapting ApexCharts that show 48h rolling windows with optional progressive zoom throughout the day. Requires config-template-card for dynamic behavior.
This commit is contained in:
parent
1386407df8
commit
c9a7dcdae7
9 changed files with 197 additions and 147 deletions
|
|
@ -1,54 +1,30 @@
|
||||||
get_price:
|
get_price:
|
||||||
name: Get Price Data
|
|
||||||
description: >-
|
|
||||||
Fetch price data for a specific time range with automatic routing. Development and testing service for the price_info_for_range API function. Automatically uses PRICE_INFO, PRICE_INFO_RANGE, or both based on the time range boundary.
|
|
||||||
fields:
|
fields:
|
||||||
entry_id:
|
entry_id:
|
||||||
name: Entry ID
|
|
||||||
description: The config entry ID for the Tibber integration.
|
|
||||||
required: true
|
required: true
|
||||||
example: "1234567890abcdef"
|
example: "1234567890abcdef"
|
||||||
selector:
|
selector:
|
||||||
config_entry:
|
config_entry:
|
||||||
integration: tibber_prices
|
integration: tibber_prices
|
||||||
start_time:
|
start_time:
|
||||||
name: Start Time
|
|
||||||
description: Start of the time range (inclusive, timezone-aware).
|
|
||||||
required: true
|
required: true
|
||||||
example: "2025-11-01T00:00:00+01:00"
|
example: "2025-11-01T00:00:00+01:00"
|
||||||
selector:
|
selector:
|
||||||
datetime:
|
datetime:
|
||||||
end_time:
|
end_time:
|
||||||
name: End Time
|
|
||||||
description: End of the time range (exclusive, timezone-aware).
|
|
||||||
required: true
|
required: true
|
||||||
example: "2025-11-02T00:00:00+01:00"
|
example: "2025-11-02T00:00:00+01:00"
|
||||||
selector:
|
selector:
|
||||||
datetime:
|
datetime:
|
||||||
get_apexcharts_yaml:
|
get_apexcharts_yaml:
|
||||||
name: Get ApexCharts Card YAML
|
|
||||||
description: >-
|
|
||||||
⚠️ IMPORTANT: This service generates a BASIC EXAMPLE configuration for ApexCharts Card as a starting point. It is NOT a complete solution for all ApexCharts features.
|
|
||||||
This integration is primarily a DATA PROVIDER. The generated YAML demonstrates how to use the `get_chartdata` service to fetch price data. Due to the segmented nature of our data (different time periods per series) and the use of Home Assistant's service API instead of entity attributes, many advanced ApexCharts features (like in_header, certain transformations) are not compatible or require manual customization.
|
|
||||||
|
|
||||||
|
|
||||||
You are welcome to customize the generated YAML for your specific needs, but please understand that comprehensive ApexCharts configuration support is beyond the scope of this integration. Community contributions with improved configurations are always appreciated - if you find a better setup that works, please share it so everyone can benefit!
|
|
||||||
|
|
||||||
|
|
||||||
For direct data access to build your own charts, use the `get_chartdata` service instead.
|
|
||||||
fields:
|
fields:
|
||||||
entry_id:
|
entry_id:
|
||||||
name: Entry ID
|
|
||||||
description: The config entry ID for the Tibber integration.
|
|
||||||
required: true
|
required: true
|
||||||
example: "1234567890abcdef"
|
example: "1234567890abcdef"
|
||||||
selector:
|
selector:
|
||||||
config_entry:
|
config_entry:
|
||||||
integration: tibber_prices
|
integration: tibber_prices
|
||||||
day:
|
day:
|
||||||
name: Day
|
|
||||||
description: >-
|
|
||||||
Which day to visualize (yesterday, today, or tomorrow). If not specified, returns a rolling 2-day window: today+tomorrow (when tomorrow data is available) or yesterday+today (when tomorrow data is not yet available).
|
|
||||||
required: false
|
required: false
|
||||||
example: today
|
example: today
|
||||||
selector:
|
selector:
|
||||||
|
|
@ -57,11 +33,10 @@ get_apexcharts_yaml:
|
||||||
- yesterday
|
- yesterday
|
||||||
- today
|
- today
|
||||||
- tomorrow
|
- tomorrow
|
||||||
|
- rolling_window
|
||||||
|
- rolling_window_autozoom
|
||||||
translation_key: day
|
translation_key: day
|
||||||
level_type:
|
level_type:
|
||||||
name: Level Type
|
|
||||||
description: >-
|
|
||||||
Select which price level classification to visualize: 'rating_level' (LOW/NORMAL/HIGH based on your configured thresholds) or 'level' (Tibber API levels: VERY_CHEAP/CHEAP/NORMAL/EXPENSIVE/VERY_EXPENSIVE).
|
|
||||||
required: false
|
required: false
|
||||||
default: rating_level
|
default: rating_level
|
||||||
example: rating_level
|
example: rating_level
|
||||||
|
|
@ -72,24 +47,16 @@ get_apexcharts_yaml:
|
||||||
- level
|
- level
|
||||||
translation_key: level_type
|
translation_key: level_type
|
||||||
highlight_best_price:
|
highlight_best_price:
|
||||||
name: Highlight Best Price Periods
|
|
||||||
description: >-
|
|
||||||
Add a semi-transparent green overlay to highlight the best price periods on the chart. This makes it easy to visually identify the optimal times for energy consumption.
|
|
||||||
required: false
|
required: false
|
||||||
default: true
|
default: true
|
||||||
example: true
|
example: true
|
||||||
selector:
|
selector:
|
||||||
boolean:
|
boolean:
|
||||||
get_chartdata:
|
get_chartdata:
|
||||||
name: Get Chart Data
|
|
||||||
description: >-
|
|
||||||
Returns price data in a chart-friendly format compatible with Tibber Core output. Works with ha-price-timeline-card, ApexCharts, Plotly, Mini Graph Card, and History Graph. Field names and structure are configurable.
|
|
||||||
fields:
|
fields:
|
||||||
general:
|
general:
|
||||||
fields:
|
fields:
|
||||||
entry_id:
|
entry_id:
|
||||||
name: Entry ID
|
|
||||||
description: The config entry ID for the Tibber integration.
|
|
||||||
required: true
|
required: true
|
||||||
example: "1234567890abcdef"
|
example: "1234567890abcdef"
|
||||||
selector:
|
selector:
|
||||||
|
|
@ -99,9 +66,6 @@ get_chartdata:
|
||||||
collapsed: true
|
collapsed: true
|
||||||
fields:
|
fields:
|
||||||
day:
|
day:
|
||||||
name: Day
|
|
||||||
description: >-
|
|
||||||
Which day(s) to fetch prices for. You can select multiple days. If not specified, returns a rolling 2-day window: today+tomorrow (when tomorrow data is available) or yesterday+today (when tomorrow data is not yet available). This provides continuous chart display without gaps.
|
|
||||||
required: false
|
required: false
|
||||||
selector:
|
selector:
|
||||||
select:
|
select:
|
||||||
|
|
@ -112,9 +76,6 @@ get_chartdata:
|
||||||
multiple: true
|
multiple: true
|
||||||
translation_key: day
|
translation_key: day
|
||||||
resolution:
|
resolution:
|
||||||
name: Resolution
|
|
||||||
description: >-
|
|
||||||
Time resolution for the returned data. Options: 'interval' (default, 15-minute intervals, 96 points per day), 'hourly' (hourly averages, 24 points per day).
|
|
||||||
required: false
|
required: false
|
||||||
default: interval
|
default: interval
|
||||||
example: hourly
|
example: hourly
|
||||||
|
|
@ -128,9 +89,6 @@ get_chartdata:
|
||||||
collapsed: true
|
collapsed: true
|
||||||
fields:
|
fields:
|
||||||
level_filter:
|
level_filter:
|
||||||
name: Level Filter
|
|
||||||
description: >-
|
|
||||||
Filter intervals to include only specific Tibber price levels (very_cheap, cheap, normal, expensive, very_expensive). If not specified, all levels are included.
|
|
||||||
required: false
|
required: false
|
||||||
selector:
|
selector:
|
||||||
select:
|
select:
|
||||||
|
|
@ -143,9 +101,6 @@ get_chartdata:
|
||||||
multiple: true
|
multiple: true
|
||||||
translation_key: level_filter
|
translation_key: level_filter
|
||||||
rating_level_filter:
|
rating_level_filter:
|
||||||
name: Rating Level Filter
|
|
||||||
description: >-
|
|
||||||
Filter intervals to include only specific rating levels (low, normal, high). If not specified, all rating levels are included.
|
|
||||||
required: false
|
required: false
|
||||||
selector:
|
selector:
|
||||||
select:
|
select:
|
||||||
|
|
@ -156,9 +111,6 @@ get_chartdata:
|
||||||
multiple: true
|
multiple: true
|
||||||
translation_key: rating_level_filter
|
translation_key: rating_level_filter
|
||||||
period_filter:
|
period_filter:
|
||||||
name: Period Filter
|
|
||||||
description: >-
|
|
||||||
Filter intervals to include only those within Best Price or Peak Price periods. Options: 'best_price' (only intervals in Best Price periods), 'peak_price' (only intervals in Peak Price periods). If not specified, all intervals are included. This uses the precomputed period data from binary sensors (binary_sensor.best_price_period / binary_sensor.peak_price_period).
|
|
||||||
required: false
|
required: false
|
||||||
selector:
|
selector:
|
||||||
select:
|
select:
|
||||||
|
|
@ -170,18 +122,12 @@ get_chartdata:
|
||||||
collapsed: true
|
collapsed: true
|
||||||
fields:
|
fields:
|
||||||
minor_currency:
|
minor_currency:
|
||||||
name: Minor Currency
|
|
||||||
description: >-
|
|
||||||
Return prices in minor currency units (cents for EUR, øre for NOK/SEK) instead of major currency units. Disabled by default.
|
|
||||||
required: false
|
required: false
|
||||||
default: false
|
default: false
|
||||||
example: true
|
example: true
|
||||||
selector:
|
selector:
|
||||||
boolean:
|
boolean:
|
||||||
round_decimals:
|
round_decimals:
|
||||||
name: Round Decimals
|
|
||||||
description: >-
|
|
||||||
Number of decimal places to round prices to (0-10). If not specified, uses default precision (4 decimals for major currency, 2 for minor currency).
|
|
||||||
required: false
|
required: false
|
||||||
example: 2
|
example: 2
|
||||||
selector:
|
selector:
|
||||||
|
|
@ -190,12 +136,6 @@ get_chartdata:
|
||||||
max: 10
|
max: 10
|
||||||
mode: box
|
mode: box
|
||||||
insert_nulls:
|
insert_nulls:
|
||||||
name: Insert NULL Values
|
|
||||||
description: >-
|
|
||||||
NULL insertion mode for filtered data.
|
|
||||||
• none (default): only matching intervals
|
|
||||||
• segments: add NULLs at segment boundaries (clean gaps)
|
|
||||||
• all: NULL for every non-matching timestamp
|
|
||||||
required: false
|
required: false
|
||||||
default: none
|
default: none
|
||||||
selector:
|
selector:
|
||||||
|
|
@ -206,19 +146,11 @@ get_chartdata:
|
||||||
- all
|
- all
|
||||||
translation_key: insert_nulls
|
translation_key: insert_nulls
|
||||||
connect_segments:
|
connect_segments:
|
||||||
name: Connect Segments
|
|
||||||
description: >-
|
|
||||||
Only with insert_nulls='segments'. Adds boundary points to visually connect segments (stepline).
|
|
||||||
• Downward boundary: lower price at end of current segment
|
|
||||||
• Upward boundary: hold previous price before the gap
|
|
||||||
required: false
|
required: false
|
||||||
default: false
|
default: false
|
||||||
selector:
|
selector:
|
||||||
boolean:
|
boolean:
|
||||||
add_trailing_null:
|
add_trailing_null:
|
||||||
name: Add Trailing Null Point
|
|
||||||
description: >-
|
|
||||||
Add a final data point with null values (except timestamp) at the end. Some chart libraries need this to prevent extrapolation/interpolation to the viewport edge when using stepline rendering. Leave disabled unless your chart requires it.
|
|
||||||
required: false
|
required: false
|
||||||
default: false
|
default: false
|
||||||
selector:
|
selector:
|
||||||
|
|
@ -227,11 +159,6 @@ get_chartdata:
|
||||||
collapsed: true
|
collapsed: true
|
||||||
fields:
|
fields:
|
||||||
output_format:
|
output_format:
|
||||||
name: Output Format
|
|
||||||
description: >-
|
|
||||||
Output format.
|
|
||||||
• array_of_objects (default): objects with configurable field names
|
|
||||||
• array_of_arrays: [timestamp, price] tuples
|
|
||||||
required: false
|
required: false
|
||||||
default: array_of_objects
|
default: array_of_objects
|
||||||
example: array_of_objects
|
example: array_of_objects
|
||||||
|
|
@ -242,9 +169,6 @@ get_chartdata:
|
||||||
- array_of_arrays
|
- array_of_arrays
|
||||||
translation_key: output_format
|
translation_key: output_format
|
||||||
data_key:
|
data_key:
|
||||||
name: Data Key
|
|
||||||
description: >-
|
|
||||||
Custom name for the top-level data key in the response. Defaults to "data" if not specified. For ApexCharts compatibility with array_of_arrays, use "points".
|
|
||||||
required: false
|
required: false
|
||||||
example: prices
|
example: prices
|
||||||
selector:
|
selector:
|
||||||
|
|
@ -253,73 +177,46 @@ get_chartdata:
|
||||||
collapsed: true
|
collapsed: true
|
||||||
fields:
|
fields:
|
||||||
include_level:
|
include_level:
|
||||||
name: Include Level
|
|
||||||
description: >-
|
|
||||||
Include Tibber price level (VERY_CHEAP … VERY_EXPENSIVE).
|
|
||||||
required: false
|
required: false
|
||||||
default: false
|
default: false
|
||||||
example: true
|
example: true
|
||||||
selector:
|
selector:
|
||||||
boolean:
|
boolean:
|
||||||
include_rating_level:
|
include_rating_level:
|
||||||
name: Include Rating Level
|
|
||||||
description: >-
|
|
||||||
Include rating level (LOW/NORMAL/HIGH) based on configured thresholds.
|
|
||||||
required: false
|
required: false
|
||||||
default: false
|
default: false
|
||||||
example: true
|
example: true
|
||||||
selector:
|
selector:
|
||||||
boolean:
|
boolean:
|
||||||
include_average:
|
include_average:
|
||||||
name: Include Average
|
|
||||||
description: >-
|
|
||||||
Include daily average price.
|
|
||||||
required: false
|
required: false
|
||||||
default: false
|
default: false
|
||||||
selector:
|
selector:
|
||||||
boolean:
|
boolean:
|
||||||
start_time_field:
|
start_time_field:
|
||||||
name: Start Time Field Name
|
|
||||||
description: >-
|
|
||||||
Custom name for the start time field in the output. Defaults to "start_time" if not specified.
|
|
||||||
required: false
|
required: false
|
||||||
example: time
|
example: time
|
||||||
selector:
|
selector:
|
||||||
text:
|
text:
|
||||||
end_time_field:
|
end_time_field:
|
||||||
name: End Time Field Name
|
|
||||||
description: >-
|
|
||||||
Custom name for the end time field in the output. Defaults to "end_time" if not specified. Only used with period_filter.
|
|
||||||
required: false
|
required: false
|
||||||
example: end
|
example: end
|
||||||
selector:
|
selector:
|
||||||
text:
|
text:
|
||||||
price_field:
|
price_field:
|
||||||
name: Price Field Name
|
|
||||||
description: >-
|
|
||||||
Custom name for the price field in the output. Defaults to "price_per_kwh" if not specified.
|
|
||||||
required: false
|
required: false
|
||||||
example: price
|
example: price
|
||||||
selector:
|
selector:
|
||||||
text:
|
text:
|
||||||
level_field:
|
level_field:
|
||||||
name: Level Field Name
|
|
||||||
description: >-
|
|
||||||
Custom name for the level field in the output. Defaults to "level" if not specified. Only used when include_level is enabled.
|
|
||||||
required: false
|
required: false
|
||||||
selector:
|
selector:
|
||||||
text:
|
text:
|
||||||
rating_level_field:
|
rating_level_field:
|
||||||
name: Rating Level Field Name
|
|
||||||
description: >-
|
|
||||||
Custom name for the rating_level field in the output. Defaults to "rating_level" if not specified. Only used when include_rating_level is enabled.
|
|
||||||
required: false
|
required: false
|
||||||
selector:
|
selector:
|
||||||
text:
|
text:
|
||||||
average_field:
|
average_field:
|
||||||
name: Average Field Name
|
|
||||||
description: >-
|
|
||||||
Custom name for the average field in the output. Defaults to "average" if not specified. Only used when include_average is enabled.
|
|
||||||
required: false
|
required: false
|
||||||
selector:
|
selector:
|
||||||
text:
|
text:
|
||||||
|
|
@ -327,22 +224,12 @@ get_chartdata:
|
||||||
collapsed: true
|
collapsed: true
|
||||||
fields:
|
fields:
|
||||||
array_fields:
|
array_fields:
|
||||||
name: Array Fields
|
|
||||||
description: >-
|
|
||||||
Choose extra fields to include using {field} syntax, comma-separated.
|
|
||||||
Available: start_time, price_per_kwh, level, rating_level, average.
|
|
||||||
Empty = default (timestamp + price).
|
|
||||||
required: false
|
required: false
|
||||||
selector:
|
selector:
|
||||||
text:
|
text:
|
||||||
refresh_user_data:
|
refresh_user_data:
|
||||||
name: Refresh User Data
|
|
||||||
description: >-
|
|
||||||
Forces a refresh of the user data (homes, profile information) from the Tibber API. This can be useful after making changes to your Tibber account or when troubleshooting connectivity issues.
|
|
||||||
fields:
|
fields:
|
||||||
entry_id:
|
entry_id:
|
||||||
name: Entry ID
|
|
||||||
description: The config entry ID for the Tibber integration.
|
|
||||||
required: true
|
required: true
|
||||||
example: "1234567890abcdef"
|
example: "1234567890abcdef"
|
||||||
selector:
|
selector:
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ ATTR_ENTRY_ID: Final = "entry_id"
|
||||||
APEXCHARTS_SERVICE_SCHEMA = vol.Schema(
|
APEXCHARTS_SERVICE_SCHEMA = vol.Schema(
|
||||||
{
|
{
|
||||||
vol.Required(ATTR_ENTRY_ID): cv.string,
|
vol.Required(ATTR_ENTRY_ID): cv.string,
|
||||||
vol.Optional("day"): vol.In(["yesterday", "today", "tomorrow"]),
|
vol.Optional("day"): vol.In(["yesterday", "today", "tomorrow", "rolling_window", "rolling_window_autozoom"]),
|
||||||
vol.Optional("level_type", default="rating_level"): vol.In(["rating_level", "level"]),
|
vol.Optional("level_type", default="rating_level"): vol.In(["rating_level", "level"]),
|
||||||
vol.Optional("highlight_best_price", default=True): cv.boolean,
|
vol.Optional("highlight_best_price", default=True): cv.boolean,
|
||||||
}
|
}
|
||||||
|
|
@ -92,6 +92,7 @@ def _build_entity_map(
|
||||||
# Define mapping patterns for each combination of level_type and day
|
# Define mapping patterns for each combination of level_type and day
|
||||||
# Note: Match by entity key (in unique_id), not entity_id (user can rename)
|
# Note: Match by entity key (in unique_id), not entity_id (user can rename)
|
||||||
# Note: For "yesterday", we use "today" sensors as they show current state
|
# Note: For "yesterday", we use "today" sensors as they show current state
|
||||||
|
# Note: For "yesterday_today_tomorrow" and "today_tomorrow", we use "today" sensors (dynamic windows)
|
||||||
pattern_map = {
|
pattern_map = {
|
||||||
("rating_level", "today"): [
|
("rating_level", "today"): [
|
||||||
("lowest_price_today", [PRICE_RATING_LOW]),
|
("lowest_price_today", [PRICE_RATING_LOW]),
|
||||||
|
|
@ -108,6 +109,16 @@ def _build_entity_map(
|
||||||
("average_price_tomorrow", [PRICE_RATING_NORMAL]),
|
("average_price_tomorrow", [PRICE_RATING_NORMAL]),
|
||||||
("highest_price_tomorrow", [PRICE_RATING_HIGH]),
|
("highest_price_tomorrow", [PRICE_RATING_HIGH]),
|
||||||
],
|
],
|
||||||
|
("rating_level", "rolling_window"): [
|
||||||
|
("lowest_price_today", [PRICE_RATING_LOW]),
|
||||||
|
("average_price_today", [PRICE_RATING_NORMAL]),
|
||||||
|
("highest_price_today", [PRICE_RATING_HIGH]),
|
||||||
|
],
|
||||||
|
("rating_level", "rolling_window_autozoom"): [
|
||||||
|
("lowest_price_today", [PRICE_RATING_LOW]),
|
||||||
|
("average_price_today", [PRICE_RATING_NORMAL]),
|
||||||
|
("highest_price_today", [PRICE_RATING_HIGH]),
|
||||||
|
],
|
||||||
("level", "today"): [
|
("level", "today"): [
|
||||||
("lowest_price_today", [PRICE_LEVEL_VERY_CHEAP, PRICE_LEVEL_CHEAP]),
|
("lowest_price_today", [PRICE_LEVEL_VERY_CHEAP, PRICE_LEVEL_CHEAP]),
|
||||||
("average_price_today", [PRICE_LEVEL_NORMAL]),
|
("average_price_today", [PRICE_LEVEL_NORMAL]),
|
||||||
|
|
@ -123,6 +134,16 @@ def _build_entity_map(
|
||||||
("average_price_tomorrow", [PRICE_LEVEL_NORMAL]),
|
("average_price_tomorrow", [PRICE_LEVEL_NORMAL]),
|
||||||
("highest_price_tomorrow", [PRICE_LEVEL_EXPENSIVE, PRICE_LEVEL_VERY_EXPENSIVE]),
|
("highest_price_tomorrow", [PRICE_LEVEL_EXPENSIVE, PRICE_LEVEL_VERY_EXPENSIVE]),
|
||||||
],
|
],
|
||||||
|
("level", "rolling_window"): [
|
||||||
|
("lowest_price_today", [PRICE_LEVEL_VERY_CHEAP, PRICE_LEVEL_CHEAP]),
|
||||||
|
("average_price_today", [PRICE_LEVEL_NORMAL]),
|
||||||
|
("highest_price_today", [PRICE_LEVEL_EXPENSIVE, PRICE_LEVEL_VERY_EXPENSIVE]),
|
||||||
|
],
|
||||||
|
("level", "rolling_window_autozoom"): [
|
||||||
|
("lowest_price_today", [PRICE_LEVEL_VERY_CHEAP, PRICE_LEVEL_CHEAP]),
|
||||||
|
("average_price_today", [PRICE_LEVEL_NORMAL]),
|
||||||
|
("highest_price_today", [PRICE_LEVEL_EXPENSIVE, PRICE_LEVEL_VERY_EXPENSIVE]),
|
||||||
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
patterns = pattern_map.get((level_type, day), [])
|
patterns = pattern_map.get((level_type, day), [])
|
||||||
|
|
@ -237,7 +258,8 @@ async def handle_apexcharts_yaml(call: ServiceCall) -> dict[str, Any]: # noqa:
|
||||||
filter_param = f"level_filter: ['{level_key}']"
|
filter_param = f"level_filter: ['{level_key}']"
|
||||||
|
|
||||||
# Conditionally include day parameter (omit for rolling window mode)
|
# Conditionally include day parameter (omit for rolling window mode)
|
||||||
day_param = f"day: ['{day}'], " if day else ""
|
# For rolling_window and rolling_window_autozoom, omit day parameter (dynamic selection)
|
||||||
|
day_param = "" if day in ("rolling_window", "rolling_window_autozoom", None) else f"day: ['{day}'], "
|
||||||
|
|
||||||
data_generator = (
|
data_generator = (
|
||||||
f"const response = await hass.callWS({{ "
|
f"const response = await hass.callWS({{ "
|
||||||
|
|
@ -282,7 +304,8 @@ async def handle_apexcharts_yaml(call: ServiceCall) -> dict[str, Any]: # noqa:
|
||||||
# Create vertical highlight bands using separate Y-axis (0-1 range)
|
# Create vertical highlight bands using separate Y-axis (0-1 range)
|
||||||
# This creates a semi-transparent overlay from bottom to top without affecting price scale
|
# This creates a semi-transparent overlay from bottom to top without affecting price scale
|
||||||
# Conditionally include day parameter (omit for rolling window mode)
|
# Conditionally include day parameter (omit for rolling window mode)
|
||||||
day_param = f"day: ['{day}'], " if day else ""
|
# For rolling_window and rolling_window_autozoom, omit day parameter (dynamic selection)
|
||||||
|
day_param = "" if day in ("rolling_window", "rolling_window_autozoom", None) else f"day: ['{day}'], "
|
||||||
|
|
||||||
# Store original prices for tooltip, but map to 1 for full-height overlay
|
# Store original prices for tooltip, but map to 1 for full-height overlay
|
||||||
# We use a custom tooltip formatter to show the real price
|
# We use a custom tooltip formatter to show the real price
|
||||||
|
|
@ -326,13 +349,13 @@ async def handle_apexcharts_yaml(call: ServiceCall) -> dict[str, Any]: # noqa:
|
||||||
"Price Phases Daily Progress" if level_type == "rating_level" else "Price Level"
|
"Price Phases Daily Progress" if level_type == "rating_level" else "Price Level"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Add translated day to title (only if day parameter was provided)
|
# Add translated day to title (only for fixed day views, not for dynamic modes)
|
||||||
if day:
|
if day and day not in ("rolling_window", "rolling_window_autozoom"):
|
||||||
day_translated = get_translation(["selector", "day", "options", day], user_language) or day.capitalize()
|
day_translated = get_translation(["selector", "day", "options", day], user_language) or day.capitalize()
|
||||||
title = f"{title} - {day_translated}"
|
title = f"{title} - {day_translated}"
|
||||||
|
|
||||||
# Configure span based on selected day
|
# Configure span based on selected day
|
||||||
# For rolling window mode, use config-template-card for dynamic offset
|
# For rolling window modes, use config-template-card for dynamic config
|
||||||
if day == "yesterday":
|
if day == "yesterday":
|
||||||
span_config = {"start": "day", "offset": "-1d"}
|
span_config = {"start": "day", "offset": "-1d"}
|
||||||
graph_span_value = None
|
graph_span_value = None
|
||||||
|
|
@ -341,11 +364,22 @@ async def handle_apexcharts_yaml(call: ServiceCall) -> dict[str, Any]: # noqa:
|
||||||
span_config = {"start": "day", "offset": "+1d"}
|
span_config = {"start": "day", "offset": "+1d"}
|
||||||
graph_span_value = None
|
graph_span_value = None
|
||||||
use_template = False
|
use_template = False
|
||||||
|
elif day == "rolling_window":
|
||||||
|
# Rolling 48h window: yesterday+today OR today+tomorrow (shifts at 13:00)
|
||||||
|
span_config = None # Will be set in template
|
||||||
|
graph_span_value = "48h"
|
||||||
|
use_template = True
|
||||||
|
elif day == "rolling_window_autozoom":
|
||||||
|
# Rolling 48h window with auto-zoom: yesterday+today OR today+tomorrow (shifts at 13:00)
|
||||||
|
# Auto-zooms based on current time (2h lookback + remaining time)
|
||||||
|
span_config = None # Will be set in template
|
||||||
|
graph_span_value = None # Will be set in template
|
||||||
|
use_template = True
|
||||||
elif day: # today (explicit)
|
elif day: # today (explicit)
|
||||||
span_config = {"start": "day"}
|
span_config = {"start": "day"}
|
||||||
graph_span_value = None
|
graph_span_value = None
|
||||||
use_template = False
|
use_template = False
|
||||||
else: # Rolling window mode (None)
|
else: # Rolling window mode (None - same as rolling_window)
|
||||||
# Use config-template-card to dynamically set offset based on data availability
|
# Use config-template-card to dynamically set offset based on data availability
|
||||||
span_config = None # Will be set in template
|
span_config = None # Will be set in template
|
||||||
graph_span_value = "48h"
|
graph_span_value = "48h"
|
||||||
|
|
@ -390,8 +424,8 @@ async def handle_apexcharts_yaml(call: ServiceCall) -> dict[str, Any]: # noqa:
|
||||||
},
|
},
|
||||||
"grid": {
|
"grid": {
|
||||||
"show": True,
|
"show": True,
|
||||||
"borderColor": "#40475D",
|
"borderColor": "#f5f5f5",
|
||||||
"strokeDashArray": 4,
|
"strokeDashArray": 0,
|
||||||
"xaxis": {"lines": {"show": True}},
|
"xaxis": {"lines": {"show": True}},
|
||||||
"yaxis": {"lines": {"show": True}},
|
"yaxis": {"lines": {"show": True}},
|
||||||
},
|
},
|
||||||
|
|
@ -420,11 +454,8 @@ async def handle_apexcharts_yaml(call: ServiceCall) -> dict[str, Any]: # noqa:
|
||||||
"series": series,
|
"series": series,
|
||||||
}
|
}
|
||||||
|
|
||||||
# For rolling window mode, wrap in config-template-card for dynamic offset
|
# For rolling window mode and today_tomorrow, wrap in config-template-card for dynamic config
|
||||||
if use_template:
|
if use_template:
|
||||||
# Add graph_span to base config (48h window)
|
|
||||||
result["graph_span"] = graph_span_value
|
|
||||||
|
|
||||||
# Find tomorrow_data_available binary sensor
|
# Find tomorrow_data_available binary sensor
|
||||||
tomorrow_data_sensor = next(
|
tomorrow_data_sensor = next(
|
||||||
(
|
(
|
||||||
|
|
@ -438,6 +469,81 @@ async def handle_apexcharts_yaml(call: ServiceCall) -> dict[str, Any]: # noqa:
|
||||||
)
|
)
|
||||||
|
|
||||||
if tomorrow_data_sensor:
|
if tomorrow_data_sensor:
|
||||||
|
if day == "rolling_window_autozoom":
|
||||||
|
# rolling_window_autozoom mode: Dynamic graph_span with auto-zoom
|
||||||
|
# Shows last 120 min (8 intervals) + remaining minutes until end of time window
|
||||||
|
# Auto-zooms every 15 minutes when current interval completes
|
||||||
|
# When tomorrow data arrives after 13:00, extends to show tomorrow too
|
||||||
|
#
|
||||||
|
# Key principle: graph_span must always be divisible by 15 (full intervals)
|
||||||
|
# The current (running) interval stays included until it completes
|
||||||
|
#
|
||||||
|
# Calculation:
|
||||||
|
# 1. Round current time UP to next quarter-hour (include running interval)
|
||||||
|
# 2. Calculate minutes from end of running interval to midnight
|
||||||
|
# 3. Round to ensure full 15-minute intervals
|
||||||
|
# 4. Add 120min lookback (always 8 intervals)
|
||||||
|
# 5. If tomorrow data available: add 1440min (96 intervals)
|
||||||
|
#
|
||||||
|
# Example timeline (without tomorrow data):
|
||||||
|
# 08:00 → next quarter: 08:15 → to midnight: 945min → span: 120+945 = 1065min (71 intervals)
|
||||||
|
# 08:07 → next quarter: 08:15 → to midnight: 945min → span: 120+945 = 1065min (stays same)
|
||||||
|
# 08:15 → next quarter: 08:30 → to midnight: 930min → span: 120+930 = 1050min (70 intervals)
|
||||||
|
# 14:23 → next quarter: 14:30 → to midnight: 570min → span: 120+570 = 690min (46 intervals)
|
||||||
|
#
|
||||||
|
# After 13:00 with tomorrow data:
|
||||||
|
# 14:00 → next quarter: 14:15 → to midnight: 585min → span: 120+585+1440 = 2145min (143 intervals)
|
||||||
|
# 14:15 → next quarter: 14:30 → to midnight: 570min → span: 120+570+1440 = 2130min (142 intervals)
|
||||||
|
template_graph_span = (
|
||||||
|
f"const now = new Date(); "
|
||||||
|
f"const currentMinute = now.getMinutes(); "
|
||||||
|
f"const nextQuarterMinute = Math.ceil(currentMinute / 15) * 15; "
|
||||||
|
f"const currentIntervalEnd = new Date(now); "
|
||||||
|
f"if (nextQuarterMinute === 60) {{ "
|
||||||
|
f" currentIntervalEnd.setHours(now.getHours() + 1, 0, 0, 0); "
|
||||||
|
f"}} else {{ "
|
||||||
|
f" currentIntervalEnd.setMinutes(nextQuarterMinute, 0, 0); "
|
||||||
|
f"}} "
|
||||||
|
f"const midnight = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1, 0, 0, 0); "
|
||||||
|
f"const minutesFromIntervalEndToMidnight = Math.ceil((midnight - currentIntervalEnd) / 60000); "
|
||||||
|
f"const minutesRounded = Math.ceil(minutesFromIntervalEndToMidnight / 15) * 15; "
|
||||||
|
f"const lookback = 120; "
|
||||||
|
f"const hasTomorrowData = states['{tomorrow_data_sensor}'].state === 'on'; "
|
||||||
|
f"const totalMinutes = lookback + minutesRounded + (hasTomorrowData ? 1440 : 0); "
|
||||||
|
f"totalMinutes + 'min';"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Find current_interval_price sensor for 15-minute update trigger
|
||||||
|
current_price_sensor = next(
|
||||||
|
(
|
||||||
|
entity.entity_id
|
||||||
|
for entity in entity_registry.entities.values()
|
||||||
|
if entity.config_entry_id == entry_id
|
||||||
|
and entity.unique_id
|
||||||
|
and entity.unique_id.endswith("_current_interval_price")
|
||||||
|
),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
|
||||||
|
trigger_entities = [tomorrow_data_sensor]
|
||||||
|
if current_price_sensor:
|
||||||
|
trigger_entities.append(current_price_sensor)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"type": "custom:config-template-card",
|
||||||
|
"variables": {
|
||||||
|
"v_graph_span": template_graph_span,
|
||||||
|
},
|
||||||
|
"entities": trigger_entities,
|
||||||
|
"card": {
|
||||||
|
**result,
|
||||||
|
"span": {"start": "minute", "offset": "-120min"},
|
||||||
|
"graph_span": "${v_graph_span}",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
# Rolling window modes (day is None or rolling_window): Dynamic offset
|
||||||
|
# Add graph_span to base config (48h window)
|
||||||
|
result["graph_span"] = graph_span_value
|
||||||
# Wrap in config-template-card with dynamic offset calculation
|
# Wrap in config-template-card with dynamic offset calculation
|
||||||
# Template checks if tomorrow data is available (binary sensor state)
|
# Template checks if tomorrow data is available (binary sensor state)
|
||||||
# If 'on' (tomorrow data available) → offset +1d (show today+tomorrow)
|
# If 'on' (tomorrow data available) → offset +1d (show today+tomorrow)
|
||||||
|
|
@ -458,8 +564,15 @@ async def handle_apexcharts_yaml(call: ServiceCall) -> dict[str, Any]: # noqa:
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
# Fallback if sensor not found: just use +1d offset
|
# Fallback if sensor not found
|
||||||
result["span"] = {"end": "day", "offset": "+1d"}
|
if day == "rolling_window_autozoom":
|
||||||
|
# Fallback: show today with 24h span
|
||||||
|
result["span"] = {"start": "day"}
|
||||||
|
result["graph_span"] = "24h"
|
||||||
|
else:
|
||||||
|
# Rolling window fallback (rolling_window or None): just use +1d offset
|
||||||
|
result["span"] = {"end": "day", "offset": "+1d"}
|
||||||
|
result["graph_span"] = "48h"
|
||||||
return result
|
return result
|
||||||
|
|
||||||
# Add span for fixed-day views
|
# Add span for fixed-day views
|
||||||
|
|
|
||||||
|
|
@ -861,11 +861,15 @@
|
||||||
},
|
},
|
||||||
"day": {
|
"day": {
|
||||||
"name": "Tag",
|
"name": "Tag",
|
||||||
"description": "Welcher Tag visualisiert werden soll (gestern, heute oder morgen). Falls nicht angegeben, wird ein rollierendes 2-Tage-Fenster zurückgegeben: heute+morgen (wenn Daten für morgen verfügbar sind) oder gestern+heute (wenn Daten für morgen noch nicht verfügbar sind)."
|
"description": "Welcher Tag visualisiert werden soll (Standard: Rollierendes Fenster). Feste Tag-Optionen (Gestern/Heute/Morgen) zeigen 24h-Fenster ohne zusätzliche Abhängigkeiten. Dynamische Optionen benötigen config-template-card: Rollierendes Fenster zeigt ein festes 48h-Fenster, das automatisch zwischen gestern+heute und heute+morgen wechselt basierend auf Datenverfügbarkeit. Rollierendes Fenster (Auto-Zoom) verhält sich gleich, zoomt aber zusätzlich automatisch rein (2h Rückblick + verbleibende Zeit bis Mitternacht, graph_span verringert sich alle 15 Minuten)."
|
||||||
},
|
},
|
||||||
"level_type": {
|
"level_type": {
|
||||||
"name": "Stufen-Typ",
|
"name": "Stufen-Typ",
|
||||||
"description": "Wähle, welche Preisstufen-Klassifizierung visualisiert werden soll: 'rating_level' (niedrig/normal/hoch basierend auf deinen konfigurierten Schwellenwerten) oder 'level' (Tibber-API-Stufen: sehr günstig/günstig/normal/teuer/sehr teuer)."
|
"description": "Wähle, welche Preisstufen-Klassifizierung visualisiert werden soll: 'rating_level' (niedrig/normal/hoch basierend auf deinen konfigurierten Schwellenwerten) oder 'level' (Tibber-API-Stufen: sehr günstig/günstig/normal/teuer/sehr teuer)."
|
||||||
|
},
|
||||||
|
"highlight_best_price": {
|
||||||
|
"name": "Bestpreis-Zeiträume hervorheben",
|
||||||
|
"description": "Füge eine halbtransparente grüne Überlagerung hinzu, um die Bestpreis-Zeiträume im Diagramm hervorzuheben. Dies erleichtert die visuelle Identifizierung der optimalen Zeiten für den Energieverbrauch."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -1018,7 +1022,9 @@
|
||||||
"options": {
|
"options": {
|
||||||
"yesterday": "Gestern",
|
"yesterday": "Gestern",
|
||||||
"today": "Heute",
|
"today": "Heute",
|
||||||
"tomorrow": "Morgen"
|
"tomorrow": "Morgen",
|
||||||
|
"rolling_window": "Rollierendes Fenster",
|
||||||
|
"rolling_window_autozoom": "Rollierendes Fenster (Auto-Zoom)"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"resolution": {
|
"resolution": {
|
||||||
|
|
|
||||||
|
|
@ -857,11 +857,15 @@
|
||||||
},
|
},
|
||||||
"day": {
|
"day": {
|
||||||
"name": "Day",
|
"name": "Day",
|
||||||
"description": "Which day to visualize (yesterday, today, or tomorrow). If not specified, returns a rolling 2-day window: today+tomorrow (when tomorrow data is available) or yesterday+today (when tomorrow data is not yet available)."
|
"description": "Which day to visualize (default: Rolling Window). Fixed day options (Yesterday/Today/Tomorrow) show 24h spans without additional dependencies. Dynamic options require config-template-card: Rolling Window displays a fixed 48h window that automatically shifts between yesterday+today and today+tomorrow based on data availability. Rolling Window (Auto-Zoom) behaves the same but additionally auto-zooms in (2h lookback + remaining time until midnight, graph_span decreases every 15 minutes)."
|
||||||
},
|
},
|
||||||
"level_type": {
|
"level_type": {
|
||||||
"name": "Level Type",
|
"name": "Level Type",
|
||||||
"description": "Select which price level classification to visualize: 'rating_level' (low/normal/high based on your configured thresholds) or 'level' (Tibber API levels: very cheap/cheap/normal/expensive/very expensive)."
|
"description": "Select which price level classification to visualize: 'rating_level' (low/normal/high based on your configured thresholds) or 'level' (Tibber API levels: very cheap/cheap/normal/expensive/very expensive)."
|
||||||
|
},
|
||||||
|
"highlight_best_price": {
|
||||||
|
"name": "Highlight Best Price Periods",
|
||||||
|
"description": "Add a semi-transparent green overlay to highlight the best price periods on the chart. This makes it easy to visually identify the optimal times for energy consumption."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -1014,7 +1018,9 @@
|
||||||
"options": {
|
"options": {
|
||||||
"yesterday": "Yesterday",
|
"yesterday": "Yesterday",
|
||||||
"today": "Today",
|
"today": "Today",
|
||||||
"tomorrow": "Tomorrow"
|
"tomorrow": "Tomorrow",
|
||||||
|
"rolling_window": "Rolling Window",
|
||||||
|
"rolling_window_autozoom": "Rolling Window (Auto-Zoom)"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"resolution": {
|
"resolution": {
|
||||||
|
|
|
||||||
|
|
@ -857,11 +857,15 @@
|
||||||
},
|
},
|
||||||
"day": {
|
"day": {
|
||||||
"name": "Dag",
|
"name": "Dag",
|
||||||
"description": "Hvilken dag som skal visualiseres (i går, i dag eller i morgen). Hvis ikke angitt, returneres et rullende 2-dagers vindu: i dag+i morgen (når data for i morgen er tilgjengelig) eller i går+i dag (når data for i morgen ikke er tilgjengelig ennå)."
|
"description": "Hvilken dag som skal visualiseres (standard: Rullerende vindu). Faste dagalternativer (I går/I dag/I morgen) viser 24t-spenn uten ekstra avhengigheter. Dynamiske alternativer krever config-template-card: Rullerende vindu lager et fast 48t-vindu som automatisk skifter mellom i går+i dag og i dag+i morgen basert på datatilgjengelighet. Rullerende vindu (Auto-Zoom) oppfører seg likt, men zoomer i tillegg automatisk inn (2t tilbakeblikk + gjenværende tid til midnatt, graph_span reduseres hvert 15. minutt)."
|
||||||
},
|
},
|
||||||
"level_type": {
|
"level_type": {
|
||||||
"name": "Nivåtype",
|
"name": "Nivåtype",
|
||||||
"description": "Velg hvilken prisnivåklassifisering som skal visualiseres: 'rating_level' (lav/normal/høy basert på dine konfigurerte terskelverdier) eller 'level' (Tibber API-nivåer: veldig billig/billig/normal/dyr/veldig dyr)."
|
"description": "Velg hvilken prisnivåklassifisering som skal visualiseres: 'rating_level' (lav/normal/høy basert på dine konfigurerte terskelverdier) eller 'level' (Tibber API-nivåer: veldig billig/billig/normal/dyr/veldig dyr)."
|
||||||
|
},
|
||||||
|
"highlight_best_price": {
|
||||||
|
"name": "Fremhev beste prisperioder",
|
||||||
|
"description": "Legg til et halvgjennomsiktig grønt overlegg for å fremheve de beste prisperiodene i diagrammet. Dette gjør det enkelt å visuelt identifisere de optimale tidene for energiforbruk."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -1014,7 +1018,9 @@
|
||||||
"options": {
|
"options": {
|
||||||
"yesterday": "I går",
|
"yesterday": "I går",
|
||||||
"today": "I dag",
|
"today": "I dag",
|
||||||
"tomorrow": "I morgen"
|
"tomorrow": "I morgen",
|
||||||
|
"rolling_window": "Rullerende vindu",
|
||||||
|
"rolling_window_autozoom": "Rullerende vindu (Auto-Zoom)"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"resolution": {
|
"resolution": {
|
||||||
|
|
|
||||||
|
|
@ -857,11 +857,15 @@
|
||||||
},
|
},
|
||||||
"day": {
|
"day": {
|
||||||
"name": "Dag",
|
"name": "Dag",
|
||||||
"description": "Welke dag gevisualiseerd moet worden (gisteren, vandaag of morgen). Indien niet opgegeven, wordt een rollend 2-dagen venster geretourneerd: vandaag+morgen (wanneer gegevens voor morgen beschikbaar zijn) of gisteren+vandaag (wanneer gegevens voor morgen nog niet beschikbaar zijn)."
|
"description": "Welke dag gevisualiseerd moet worden (standaard: Rollend venster). Vaste dagopties (Gisteren/Vandaag/Morgen) tonen 24u-vensters zonder extra afhankelijkheden. Dynamische opties vereisen config-template-card: Rollend venster creëert een vast 48u-venster dat automatisch wisselt tussen gisteren+vandaag en vandaag+morgen op basis van databeschikbaarheid. Rollend venster (Auto-Zoom) gedraagt zich hetzelfde maar zoomt bovendien automatisch in (2u terugkijken + resterende tijd tot middernacht, graph_span neemt elke 15 minuten af)."
|
||||||
},
|
},
|
||||||
"level_type": {
|
"level_type": {
|
||||||
"name": "Niveautype",
|
"name": "Niveautype",
|
||||||
"description": "Selecteer welke prijsniveauclassificatie gevisualiseerd moet worden: 'rating_level' (laag/normaal/hoog op basis van jouw geconfigureerde drempelwaarden) of 'level' (Tibber API-niveaus: zeer goedkoop/goedkoop/normaal/duur/zeer duur)."
|
"description": "Selecteer welke prijsniveauclassificatie gevisualiseerd moet worden: 'rating_level' (laag/normaal/hoog op basis van jouw geconfigureerde drempelwaarden) of 'level' (Tibber API-niveaus: zeer goedkoop/goedkoop/normaal/duur/zeer duur)."
|
||||||
|
},
|
||||||
|
"highlight_best_price": {
|
||||||
|
"name": "Beste prijsperiodes markeren",
|
||||||
|
"description": "Voeg een halfdo0rzichtige groene overlay toe om de beste prijsperiodes in de grafiek te markeren. Dit maakt het gemakkelijk om visueel de optimale tijden voor energieverbruik te identificeren."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -1014,7 +1018,9 @@
|
||||||
"options": {
|
"options": {
|
||||||
"yesterday": "Gisteren",
|
"yesterday": "Gisteren",
|
||||||
"today": "Vandaag",
|
"today": "Vandaag",
|
||||||
"tomorrow": "Morgen"
|
"tomorrow": "Morgen",
|
||||||
|
"rolling_window": "Rollend venster",
|
||||||
|
"rolling_window_autozoom": "Rollend venster (Auto-Zoom)"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"resolution": {
|
"resolution": {
|
||||||
|
|
|
||||||
|
|
@ -857,11 +857,15 @@
|
||||||
},
|
},
|
||||||
"day": {
|
"day": {
|
||||||
"name": "Dag",
|
"name": "Dag",
|
||||||
"description": "Vilken dag som ska visualiseras (igår, idag eller imorgon). Om inte angivet returneras ett rullande 2-dagarsfönster: idag+imorgon (när data för imorgon är tillgänglig) eller igår+idag (när data för imorgon inte är tillgänglig ännu)."
|
"description": "Vilken dag som ska visualiseras (standard: Rullande fönster). Fasta dagalternativ (Igår/Idag/Imorgon) visar 24t-spann utan extra beroenden. Dynamiska alternativ kräver config-template-card: Rullande fönster skapar ett fast 48t-fönster som automatiskt växlar mellan igår+idag och idag+imorgon baserat på datatillgänglighet. Rullande fönster (Auto-Zoom) beter sig likadant men zoomar dessutom automatiskt in (2t tillbakablick + återstående tid till midnatt, graph_span minskar varje kvart)."
|
||||||
},
|
},
|
||||||
"level_type": {
|
"level_type": {
|
||||||
"name": "Nivåtyp",
|
"name": "Nivåtyp",
|
||||||
"description": "Välj vilken prisnivåklassificering som ska visualiseras: 'rating_level' (låg/normal/hög baserat på dina konfigurerade tröskelvärden) eller 'level' (Tibber API-nivåer: mycket billig/billig/normal/dyr/mycket dyr)."
|
"description": "Välj vilken prisnivåklassificering som ska visualiseras: 'rating_level' (låg/normal/hög baserat på dina konfigurerade tröskelvärden) eller 'level' (Tibber API-nivåer: mycket billig/billig/normal/dyr/mycket dyr)."
|
||||||
|
},
|
||||||
|
"highlight_best_price": {
|
||||||
|
"name": "Markera bästa prisperioder",
|
||||||
|
"description": "Lägg till ett halvgenomskinligt grönt överlägg för att markera de bästa prisperioderna i diagrammet. Detta gör det enkelt att visuellt identifiera de optimala tiderna för energiförbrukning."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -1014,7 +1018,9 @@
|
||||||
"options": {
|
"options": {
|
||||||
"yesterday": "Igår",
|
"yesterday": "Igår",
|
||||||
"today": "Idag",
|
"today": "Idag",
|
||||||
"tomorrow": "Imorgon"
|
"tomorrow": "Imorgon",
|
||||||
|
"rolling_window": "Rullande fönster",
|
||||||
|
"rolling_window_autozoom": "Rullande fönster (Auto-Zoom)"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"resolution": {
|
"resolution": {
|
||||||
|
|
|
||||||
|
|
@ -132,15 +132,17 @@ For detailed parameter descriptions, open **Developer Tools → Actions** (the U
|
||||||
service: tibber_prices.get_apexcharts_yaml
|
service: tibber_prices.get_apexcharts_yaml
|
||||||
data:
|
data:
|
||||||
entry_id: YOUR_ENTRY_ID
|
entry_id: YOUR_ENTRY_ID
|
||||||
day: today # Optional: omit for rolling 48h window (requires config-template-card)
|
day: today # Optional: yesterday, today, tomorrow, rolling_window, rolling_window_autozoom
|
||||||
response_variable: apexcharts_config
|
response_variable: apexcharts_config
|
||||||
```
|
```
|
||||||
|
|
||||||
**Rolling Window Mode:** When omitting the `day` parameter, the action generates a dynamic 48-hour rolling window that automatically shows:
|
**Day Parameter Options:**
|
||||||
- Today + Tomorrow (when tomorrow data is available)
|
|
||||||
- Yesterday + Today (when tomorrow data is not yet available)
|
|
||||||
|
|
||||||
This mode requires the Config Template Card to dynamically adjust the time window based on data availability.
|
- **Fixed days** (`yesterday`, `today`, `tomorrow`): Static 24-hour views, no additional dependencies
|
||||||
|
- **Rolling Window** (default when omitted or `rolling_window`): Dynamic 48-hour window that automatically shifts between yesterday+today and today+tomorrow based on data availability
|
||||||
|
- **Rolling Window (Auto-Zoom)** (`rolling_window_autozoom`): Same as rolling window, but additionally zooms in progressively (2h lookback + remaining time until midnight, graph span decreases every 15 minutes)
|
||||||
|
|
||||||
|
**Note:** Rolling window modes require [Config Template Card](https://github.com/iantrich/config-template-card) for dynamic behavior.
|
||||||
|
|
||||||
Use the response in Lovelace dashboards by copying the generated YAML.
|
Use the response in Lovelace dashboards by copying the generated YAML.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -285,7 +285,7 @@ For a dynamic chart that automatically adapts to data availability:
|
||||||
service: tibber_prices.get_apexcharts_yaml
|
service: tibber_prices.get_apexcharts_yaml
|
||||||
data:
|
data:
|
||||||
entry_id: YOUR_ENTRY_ID
|
entry_id: YOUR_ENTRY_ID
|
||||||
# Omit 'day' parameter for rolling window
|
day: rolling_window # Or omit for same behavior (default)
|
||||||
level_type: rating_level
|
level_type: rating_level
|
||||||
response_variable: apexcharts_config
|
response_variable: apexcharts_config
|
||||||
```
|
```
|
||||||
|
|
@ -293,8 +293,26 @@ response_variable: apexcharts_config
|
||||||
**Behavior:**
|
**Behavior:**
|
||||||
- **When tomorrow data available** (typically after ~13:00): Shows today + tomorrow
|
- **When tomorrow data available** (typically after ~13:00): Shows today + tomorrow
|
||||||
- **When tomorrow data not available**: Shows yesterday + today
|
- **When tomorrow data not available**: Shows yesterday + today
|
||||||
|
- **Fixed 48h span:** Always shows full 48 hours
|
||||||
|
|
||||||
**Note:** Rolling window mode requires Config Template Card to dynamically adjust the time range.
|
**Auto-Zoom Variant:**
|
||||||
|
|
||||||
|
For progressive zoom-in throughout the day:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
service: tibber_prices.get_apexcharts_yaml
|
||||||
|
data:
|
||||||
|
entry_id: YOUR_ENTRY_ID
|
||||||
|
day: rolling_window_autozoom
|
||||||
|
level_type: rating_level
|
||||||
|
response_variable: apexcharts_config
|
||||||
|
```
|
||||||
|
|
||||||
|
- Same data loading as rolling window
|
||||||
|
- **Progressive zoom:** Graph span starts at ~26h in the morning and decreases to ~14h by midnight
|
||||||
|
- **Updates every 15 minutes:** Always shows 2h lookback + remaining time until midnight
|
||||||
|
|
||||||
|
**Note:** Rolling window modes require Config Template Card to dynamically adjust the time range.
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue