mirror of
https://github.com/jpawlowski/hass.tibber_prices.git
synced 2026-03-30 05:13:40 +00:00
feat(services): add rolling 48h window support to chart services
Add dynamic rolling window mode to get_chartdata and get_apexcharts_yaml services that automatically adapts to data availability. When 'day' parameter is omitted, services return 48-hour window: - With tomorrow data (after ~13:00): today + tomorrow - Without tomorrow data: yesterday + today Changes: - Implement rolling window logic in get_chartdata using has_tomorrow_data() - Generate config-template-card wrapper in get_apexcharts_yaml for dynamic ApexCharts span.offset based on tomorrow_data_available binary sensor - Update service descriptions in services.yaml - Add rolling window descriptions to all translations (de, en, nb, nl, sv) - Document rolling window mode in docs/user/services.md - Add ApexCharts examples with prerequisites in docs/user/automation-examples.md BREAKING CHANGE: get_apexcharts_yaml rolling window mode requires config-template-card in addition to apexcharts-card for dynamic offset calculation. Impact: Users can create auto-adapting 48h price charts without manual day selection. Fixed day views (day: today/yesterday/tomorrow) still work with apexcharts-card only.
This commit is contained in:
parent
cf8d9ba8e8
commit
e156dfb061
11 changed files with 222 additions and 34 deletions
|
|
@ -40,9 +40,9 @@ get_apexcharts_yaml:
|
|||
integration: tibber_prices
|
||||
day:
|
||||
name: Day
|
||||
description: Which day to visualize (yesterday, today, or tomorrow).
|
||||
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
|
||||
default: today
|
||||
example: today
|
||||
selector:
|
||||
select:
|
||||
|
|
@ -90,7 +90,8 @@ get_chartdata:
|
|||
# === DATA SELECTION ===
|
||||
day:
|
||||
name: Day
|
||||
description: Which day(s) to fetch prices for. You can select multiple days. If not specified, returns all available data (today + tomorrow if available).
|
||||
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
|
||||
selector:
|
||||
select:
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ from custom_components.tibber_prices.const import (
|
|||
get_translation,
|
||||
)
|
||||
from homeassistant.exceptions import ServiceValidationError
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.helpers.entity_registry import (
|
||||
EntityRegistry,
|
||||
)
|
||||
|
|
@ -55,12 +56,12 @@ ATTR_DAY: Final = "day"
|
|||
ATTR_ENTRY_ID: Final = "entry_id"
|
||||
|
||||
# Service schema
|
||||
APEXCHARTS_SERVICE_SCHEMA: Final = vol.Schema(
|
||||
APEXCHARTS_SERVICE_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Required(ATTR_ENTRY_ID): str,
|
||||
vol.Optional("day", default="today"): vol.In(["yesterday", "today", "tomorrow"]),
|
||||
vol.Required(ATTR_ENTRY_ID): cv.string,
|
||||
vol.Optional("day"): vol.In(["yesterday", "today", "tomorrow"]),
|
||||
vol.Optional("level_type", default="rating_level"): vol.In(["rating_level", "level"]),
|
||||
vol.Optional("highlight_best_price", default=True): bool,
|
||||
vol.Optional("highlight_best_price", default=True): cv.boolean,
|
||||
}
|
||||
)
|
||||
|
||||
|
|
@ -158,7 +159,7 @@ def _get_current_price_entity(entity_registry: EntityRegistry, entry_id: str) ->
|
|||
)
|
||||
|
||||
|
||||
async def handle_apexcharts_yaml(call: ServiceCall) -> dict[str, Any]:
|
||||
async def handle_apexcharts_yaml(call: ServiceCall) -> dict[str, Any]: # noqa: PLR0912, PLR0915
|
||||
"""
|
||||
Return YAML snippet for ApexCharts card.
|
||||
|
||||
|
|
@ -186,7 +187,7 @@ async def handle_apexcharts_yaml(call: ServiceCall) -> dict[str, Any]:
|
|||
raise ServiceValidationError(translation_domain=DOMAIN, translation_key="missing_entry_id")
|
||||
entry_id: str = str(entry_id_raw)
|
||||
|
||||
day = call.data.get("day", "today")
|
||||
day = call.data.get("day") # Can be None (rolling window mode)
|
||||
level_type = call.data.get("level_type", "rating_level")
|
||||
highlight_best_price = call.data.get("highlight_best_price", True)
|
||||
|
||||
|
|
@ -203,7 +204,8 @@ async def handle_apexcharts_yaml(call: ServiceCall) -> dict[str, Any]:
|
|||
entity_registry = async_get_entity_registry(hass)
|
||||
|
||||
# Build entity mapping based on level_type and day for clickable states
|
||||
entity_map = _build_entity_map(entity_registry, entry_id, level_type, day)
|
||||
# When day is None, use "today" as fallback for entity mapping
|
||||
entity_map = _build_entity_map(entity_registry, entry_id, level_type, day or "today")
|
||||
|
||||
if level_type == "rating_level":
|
||||
series_levels = [
|
||||
|
|
@ -234,13 +236,16 @@ async def handle_apexcharts_yaml(call: ServiceCall) -> dict[str, Any]:
|
|||
else:
|
||||
filter_param = f"level_filter: ['{level_key}']"
|
||||
|
||||
# Conditionally include day parameter (omit for rolling window mode)
|
||||
day_param = f"day: ['{day}'], " if day else ""
|
||||
|
||||
data_generator = (
|
||||
f"const response = await hass.callWS({{ "
|
||||
f"type: 'call_service', "
|
||||
f"domain: 'tibber_prices', "
|
||||
f"service: 'get_chartdata', "
|
||||
f"return_response: true, "
|
||||
f"service_data: {{ entry_id: '{entry_id}', day: ['{day}'], {filter_param}, "
|
||||
f"service_data: {{ entry_id: '{entry_id}', {day_param}{filter_param}, "
|
||||
f"output_format: 'array_of_arrays', insert_nulls: 'segments', minor_currency: true, "
|
||||
f"connect_segments: true }} }}); "
|
||||
f"return response.response.data;"
|
||||
|
|
@ -276,13 +281,16 @@ async def handle_apexcharts_yaml(call: ServiceCall) -> dict[str, Any]:
|
|||
if highlight_best_price and entity_map:
|
||||
# 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
|
||||
# Conditionally include day parameter (omit for rolling window mode)
|
||||
day_param = f"day: ['{day}'], " if day else ""
|
||||
|
||||
best_price_generator = (
|
||||
f"const response = await hass.callWS({{ "
|
||||
f"type: 'call_service', "
|
||||
f"domain: 'tibber_prices', "
|
||||
f"service: 'get_chartdata', "
|
||||
f"return_response: true, "
|
||||
f"service_data: {{ entry_id: '{entry_id}', day: ['{day}'], "
|
||||
f"service_data: {{ entry_id: '{entry_id}', {day_param}"
|
||||
f"period_filter: 'best_price', "
|
||||
f"output_format: 'array_of_arrays', minor_currency: true }} }}); "
|
||||
f"return response.response.data.map(point => [point[0], 1]);"
|
||||
|
|
@ -311,22 +319,34 @@ async def handle_apexcharts_yaml(call: ServiceCall) -> dict[str, Any]:
|
|||
"Price Phases Daily Progress" if level_type == "rating_level" else "Price Level"
|
||||
)
|
||||
|
||||
# Add translated day to title
|
||||
# Add translated day to title (only if day parameter was provided)
|
||||
if day:
|
||||
day_translated = get_translation(["selector", "day", "options", day], user_language) or day.capitalize()
|
||||
title = f"{title} - {day_translated}"
|
||||
|
||||
# Configure span based on selected day
|
||||
# For rolling window mode, use config-template-card for dynamic offset
|
||||
if day == "yesterday":
|
||||
span_config = {"start": "day", "offset": "-1d"}
|
||||
graph_span_value = None
|
||||
use_template = False
|
||||
elif day == "tomorrow":
|
||||
span_config = {"start": "day", "offset": "+1d"}
|
||||
else: # today
|
||||
graph_span_value = None
|
||||
use_template = False
|
||||
elif day: # today (explicit)
|
||||
span_config = {"start": "day"}
|
||||
graph_span_value = None
|
||||
use_template = False
|
||||
else: # Rolling window mode (None)
|
||||
# Use config-template-card to dynamically set offset based on data availability
|
||||
span_config = None # Will be set in template
|
||||
graph_span_value = "48h"
|
||||
use_template = True
|
||||
|
||||
return {
|
||||
result = {
|
||||
"type": "custom:apexcharts-card",
|
||||
"update_interval": "5m",
|
||||
"span": span_config,
|
||||
"header": {
|
||||
"show": True,
|
||||
"title": title,
|
||||
|
|
@ -392,3 +412,55 @@ async def handle_apexcharts_yaml(call: ServiceCall) -> dict[str, Any]:
|
|||
},
|
||||
"series": series,
|
||||
}
|
||||
|
||||
# For rolling window mode, wrap in config-template-card for dynamic offset
|
||||
if use_template:
|
||||
# Add graph_span to base config (48h window)
|
||||
result["graph_span"] = graph_span_value
|
||||
|
||||
# Find tomorrow_data_available binary sensor
|
||||
tomorrow_data_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("_tomorrow_data_available")
|
||||
),
|
||||
None,
|
||||
)
|
||||
|
||||
if tomorrow_data_sensor:
|
||||
# Wrap in config-template-card with dynamic offset calculation
|
||||
# Template checks if tomorrow data is available (binary sensor state)
|
||||
# If 'on' (tomorrow data available) → offset +1d (show today+tomorrow)
|
||||
# If 'off' (no tomorrow data) → offset +0d (show yesterday+today)
|
||||
template_value = f"states['{tomorrow_data_sensor}'].state === 'on' ? '+1d' : '+0d'"
|
||||
return {
|
||||
"type": "custom:config-template-card",
|
||||
"variables": {
|
||||
"v_offset": template_value,
|
||||
},
|
||||
"entities": [tomorrow_data_sensor],
|
||||
"card": {
|
||||
**result,
|
||||
"span": {
|
||||
"end": "day",
|
||||
"offset": "${v_offset}",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
# Fallback if sensor not found: just use +1d offset
|
||||
result["span"] = {"end": "day", "offset": "+1d"}
|
||||
return result
|
||||
|
||||
# Add span for fixed-day views
|
||||
if span_config:
|
||||
result["span"] = span_config
|
||||
|
||||
# Add graph_span if needed
|
||||
if graph_span_value:
|
||||
result["graph_span"] = graph_span_value
|
||||
|
||||
return result
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ from custom_components.tibber_prices.coordinator.helpers import (
|
|||
from homeassistant.exceptions import ServiceValidationError
|
||||
|
||||
from .formatters import aggregate_hourly_exact, get_period_data, normalize_level_filter, normalize_rating_level_filter
|
||||
from .helpers import get_entry_and_data
|
||||
from .helpers import get_entry_and_data, has_tomorrow_data
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from homeassistant.core import ServiceCall
|
||||
|
|
@ -114,6 +114,11 @@ async def handle_chartdata(call: ServiceCall) -> dict[str, Any]: # noqa: PLR091
|
|||
Supports both 15-minute intervals and hourly aggregation, with optional filtering by
|
||||
price level, rating level, or period (best_price/peak_price).
|
||||
|
||||
Default behavior (no day parameter):
|
||||
- Returns rolling 2-day window for continuous chart display
|
||||
- If tomorrow data available: today + tomorrow
|
||||
- If tomorrow data NOT available: yesterday + today
|
||||
|
||||
See services.yaml for detailed parameter documentation.
|
||||
|
||||
Args:
|
||||
|
|
@ -132,10 +137,15 @@ async def handle_chartdata(call: ServiceCall) -> dict[str, Any]: # noqa: PLR091
|
|||
raise ServiceValidationError(translation_domain=DOMAIN, translation_key="missing_entry_id")
|
||||
entry_id: str = str(entry_id_raw)
|
||||
|
||||
# Get coordinator to check data availability
|
||||
_, coordinator, _ = get_entry_and_data(hass, entry_id)
|
||||
|
||||
days_raw = call.data.get(ATTR_DAY)
|
||||
# If no day specified, return all available data (today + tomorrow)
|
||||
# If no day specified, use rolling 2-day window:
|
||||
# - If tomorrow data available: today + tomorrow
|
||||
# - If tomorrow data NOT available: yesterday + today
|
||||
if days_raw is None:
|
||||
days = ["today", "tomorrow"]
|
||||
days = ["today", "tomorrow"] if has_tomorrow_data(coordinator) else ["yesterday", "today"]
|
||||
# Convert single string to list for uniform processing
|
||||
elif isinstance(days_raw, str):
|
||||
days = [days_raw]
|
||||
|
|
@ -174,8 +184,6 @@ async def handle_chartdata(call: ServiceCall) -> dict[str, Any]: # noqa: PLR091
|
|||
if average_field in array_fields_template:
|
||||
include_average = True
|
||||
|
||||
_, coordinator, _ = get_entry_and_data(hass, entry_id)
|
||||
|
||||
# Get thresholds from config for rating aggregation
|
||||
threshold_low = coordinator.config_entry.options.get(
|
||||
CONF_PRICE_RATING_THRESHOLD_LOW, DEFAULT_PRICE_RATING_THRESHOLD_LOW
|
||||
|
|
|
|||
|
|
@ -19,9 +19,11 @@ from __future__ import annotations
|
|||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from custom_components.tibber_prices.const import DOMAIN
|
||||
from custom_components.tibber_prices.coordinator.helpers import get_intervals_for_day_offsets
|
||||
from homeassistant.exceptions import ServiceValidationError
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from custom_components.tibber_prices.coordinator import TibberPricesDataUpdateCoordinator
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
|
||||
|
|
@ -51,3 +53,22 @@ def get_entry_and_data(hass: HomeAssistant, entry_id: str) -> tuple[Any, Any, di
|
|||
coordinator = entry.runtime_data.coordinator
|
||||
data = coordinator.data or {}
|
||||
return entry, coordinator, data
|
||||
|
||||
|
||||
def has_tomorrow_data(coordinator: TibberPricesDataUpdateCoordinator) -> bool:
|
||||
"""
|
||||
Check if tomorrow's price data is available in coordinator.
|
||||
|
||||
Uses get_intervals_for_day_offsets() to automatically determine tomorrow
|
||||
based on current date.
|
||||
|
||||
Args:
|
||||
coordinator: TibberPricesDataUpdateCoordinator instance
|
||||
|
||||
Returns:
|
||||
True if tomorrow's data exists (at least one interval), False otherwise
|
||||
|
||||
"""
|
||||
coordinator_data = coordinator.data or {}
|
||||
tomorrow_intervals = get_intervals_for_day_offsets(coordinator_data, [1])
|
||||
return len(tomorrow_intervals) > 0
|
||||
|
|
|
|||
|
|
@ -799,7 +799,7 @@
|
|||
},
|
||||
"day": {
|
||||
"name": "Tag",
|
||||
"description": "Welcher Tag visualisiert werden soll (gestern, heute oder morgen)."
|
||||
"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)."
|
||||
},
|
||||
"level_type": {
|
||||
"name": "Stufen-Typ",
|
||||
|
|
@ -817,7 +817,7 @@
|
|||
},
|
||||
"day": {
|
||||
"name": "Tag",
|
||||
"description": "Für welche(n) Tag(e) sollen Preise abgerufen werden. Du kannst mehrere Tage auswählen. Falls nicht angegeben, werden alle verfügbaren Daten zurückgegeben (heute + morgen falls verfügbar)."
|
||||
"description": "Für welche(n) Tag(e) sollen Preise abgerufen werden. Du kannst mehrere Tage auswählen. Falls nicht angegeben, wird ein rollierendes 2-Tages-Fenster zurückgegeben: heute+morgen (wenn Morgendaten verfügbar) oder gestern+heute (wenn Morgendaten noch nicht verfügbar). Dies ermöglicht eine kontinuierliche Diagrammanzeige ohne Lücken."
|
||||
},
|
||||
"resolution": {
|
||||
"name": "Auflösung",
|
||||
|
|
|
|||
|
|
@ -795,7 +795,7 @@
|
|||
},
|
||||
"day": {
|
||||
"name": "Day",
|
||||
"description": "Which day to visualize (yesterday, today, or tomorrow)."
|
||||
"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)."
|
||||
},
|
||||
"level_type": {
|
||||
"name": "Level Type",
|
||||
|
|
@ -813,7 +813,7 @@
|
|||
},
|
||||
"day": {
|
||||
"name": "Day",
|
||||
"description": "Which day(s) to fetch prices for. You can select multiple days. If not specified, returns all available data (today + tomorrow if available)."
|
||||
"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."
|
||||
},
|
||||
"resolution": {
|
||||
"name": "Resolution",
|
||||
|
|
|
|||
|
|
@ -795,7 +795,7 @@
|
|||
},
|
||||
"day": {
|
||||
"name": "Dag",
|
||||
"description": "Hvilken dag som skal visualiseres (i går, i dag eller i morgen)."
|
||||
"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å)."
|
||||
},
|
||||
"level_type": {
|
||||
"name": "Nivåtype",
|
||||
|
|
@ -813,7 +813,7 @@
|
|||
},
|
||||
"day": {
|
||||
"name": "Dag",
|
||||
"description": "Hvilken dag(er) skal det hentes priser for. Du kan velge flere dager. Hvis ikke angitt, returneres alle tilgjengelige data (i dag + i morgen hvis tilgjengelig)."
|
||||
"description": "Hvilken dag(er) skal det hentes priser for. Du kan velge flere dager. Hvis ikke angitt, returneres et rullerende 2-dagers vindu: i dag+i morgen (når morgendagens data er tilgjengelig) eller i går+i dag (når morgendagens data ikke er tilgjengelig ennå). Dette gir kontinuerlig diagramvisning uten hull."
|
||||
},
|
||||
"resolution": {
|
||||
"name": "Oppløsning",
|
||||
|
|
|
|||
|
|
@ -795,7 +795,7 @@
|
|||
},
|
||||
"day": {
|
||||
"name": "Dag",
|
||||
"description": "Welke dag gevisualiseerd moet worden (gisteren, vandaag of morgen)."
|
||||
"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)."
|
||||
},
|
||||
"level_type": {
|
||||
"name": "Niveautype",
|
||||
|
|
@ -813,7 +813,7 @@
|
|||
},
|
||||
"day": {
|
||||
"name": "Dag",
|
||||
"description": "Voor welke dag(en) moeten prijzen worden opgehaald. Je kunt meerdere dagen selecteren. Als niet opgegeven, worden alle beschikbare gegevens geretourneerd (vandaag + morgen indien beschikbaar)."
|
||||
"description": "Voor welke dag(en) moeten prijzen worden opgehaald. Je kunt meerdere dagen selecteren. Als niet opgegeven, wordt een rollend 2-daags venster geretourneerd: vandaag+morgen (wanneer morgengegevens beschikbaar zijn) of gisteren+vandaag (wanneer morgengegevens nog niet beschikbaar zijn). Dit zorgt voor een continue grafiekweergave zonder hiaten."
|
||||
},
|
||||
"resolution": {
|
||||
"name": "Resolutie",
|
||||
|
|
|
|||
|
|
@ -795,7 +795,7 @@
|
|||
},
|
||||
"day": {
|
||||
"name": "Dag",
|
||||
"description": "Vilken dag som ska visualiseras (igår, idag eller imorgon)."
|
||||
"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)."
|
||||
},
|
||||
"level_type": {
|
||||
"name": "Nivåtyp",
|
||||
|
|
@ -813,7 +813,7 @@
|
|||
},
|
||||
"day": {
|
||||
"name": "Dag",
|
||||
"description": "Vilken dag(ar) ska priser hämtas för. Du kan välja flera dagar. Om inte angivet, returneras alla tillgängliga data (idag + imorgon om tillgängligt)."
|
||||
"description": "Vilken dag(ar) ska priser hämtas för. Du kan välja flera dagar. Om inte angivet, returneras ett rullande 2-dagars fönster: idag+imorgon (när morgondagens data är tillgänglig) eller igår+idag (när morgondagens data inte är tillgänglig ännu). Detta ger kontinuerlig diagramvisning utan luckor."
|
||||
},
|
||||
"resolution": {
|
||||
"name": "Upplösning",
|
||||
|
|
|
|||
|
|
@ -241,4 +241,60 @@ Coming soon...
|
|||
|
||||
## ApexCharts Cards
|
||||
|
||||
Coming soon...
|
||||
The `tibber_prices.get_apexcharts_yaml` service generates complete ApexCharts card configurations for visualizing electricity prices.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
**Required:**
|
||||
- [ApexCharts Card](https://github.com/RomRider/apexcharts-card) - Install via HACS
|
||||
|
||||
**Optional (for rolling window mode):**
|
||||
- [Config Template Card](https://github.com/iantrich/config-template-card) - Install via HACS
|
||||
|
||||
### Installation
|
||||
|
||||
1. Open HACS → Frontend
|
||||
2. Search for "ApexCharts Card" and install
|
||||
3. (Optional) Search for "Config Template Card" and install if you want rolling window mode
|
||||
|
||||
### Example: Fixed Day View
|
||||
|
||||
```yaml
|
||||
# Generate configuration via automation/script
|
||||
service: tibber_prices.get_apexcharts_yaml
|
||||
data:
|
||||
entry_id: YOUR_ENTRY_ID
|
||||
day: today # or "yesterday", "tomorrow"
|
||||
level_type: rating_level # or "level" for 5-level view
|
||||
response_variable: apexcharts_config
|
||||
```
|
||||
|
||||
Then copy the generated YAML into your Lovelace dashboard.
|
||||
|
||||
### Example: Rolling 48h Window
|
||||
|
||||
For a dynamic chart that automatically adapts to data availability:
|
||||
|
||||
```yaml
|
||||
service: tibber_prices.get_apexcharts_yaml
|
||||
data:
|
||||
entry_id: YOUR_ENTRY_ID
|
||||
# Omit 'day' parameter for rolling window
|
||||
level_type: rating_level
|
||||
response_variable: apexcharts_config
|
||||
```
|
||||
|
||||
**Behavior:**
|
||||
- **When tomorrow data available** (typically after ~13:00): Shows today + tomorrow
|
||||
- **When tomorrow data not available**: Shows yesterday + today
|
||||
|
||||
**Note:** Rolling window mode requires Config Template Card to dynamically adjust the time range.
|
||||
|
||||
### Features
|
||||
|
||||
- Color-coded price levels/ratings (green = cheap, yellow = normal, red = expensive)
|
||||
- Best price period highlighting (semi-transparent green overlay)
|
||||
- Automatic NULL insertion for clean gaps
|
||||
- Translated labels based on your Home Assistant language
|
||||
- Interactive zoom and pan
|
||||
- Live marker showing current time
|
||||
|
|
|
|||
|
|
@ -57,6 +57,25 @@ response_variable: chart_data
|
|||
| `minor_currency` | Return prices in ct/øre instead of EUR/NOK | `false` |
|
||||
| `round_decimals` | Decimal places (0-10) | 4 (major) or 2 (minor) |
|
||||
|
||||
**Rolling Window Mode:**
|
||||
|
||||
Omit the `day` parameter to get a dynamic 48-hour rolling window that automatically adapts to data availability:
|
||||
|
||||
```yaml
|
||||
service: tibber_prices.get_chartdata
|
||||
data:
|
||||
entry_id: YOUR_ENTRY_ID
|
||||
# Omit 'day' for rolling window
|
||||
output_format: array_of_objects
|
||||
response_variable: chart_data
|
||||
```
|
||||
|
||||
**Behavior:**
|
||||
- **When tomorrow data available** (typically after ~13:00): Returns today + tomorrow
|
||||
- **When tomorrow data not available**: Returns yesterday + today
|
||||
|
||||
This is useful for charts that should always show a 48-hour window without manual day selection.
|
||||
|
||||
**Period Filter Example:**
|
||||
|
||||
Get best price periods as summaries instead of intervals:
|
||||
|
|
@ -93,15 +112,26 @@ For detailed parameter descriptions, see the service definition in **Developer T
|
|||
|
||||
**Purpose:** Generates complete ApexCharts card YAML configuration for visualizing electricity prices.
|
||||
|
||||
**Prerequisites:**
|
||||
- [ApexCharts Card](https://github.com/RomRider/apexcharts-card) (required for all configurations)
|
||||
- [Config Template Card](https://github.com/iantrich/config-template-card) (required only for rolling window mode without `day` parameter)
|
||||
|
||||
**Quick Example:**
|
||||
|
||||
```yaml
|
||||
service: tibber_prices.get_apexcharts_yaml
|
||||
data:
|
||||
entry_id: YOUR_ENTRY_ID
|
||||
day: today # Optional: omit for rolling 48h window (requires config-template-card)
|
||||
response_variable: apexcharts_config
|
||||
```
|
||||
|
||||
**Rolling Window Mode:** When omitting the `day` parameter, the service generates a dynamic 48-hour rolling window that automatically shows:
|
||||
- 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.
|
||||
|
||||
Use the response in Lovelace dashboards by copying the generated YAML.
|
||||
|
||||
**Documentation:** See Developer Tools → Services for parameter details.
|
||||
|
|
|
|||
Loading…
Reference in a new issue