feat(options): show persistent repair issue after currency mode change

Add DATA_STATISTICS_REVIEW_REQUIRED flag to config_entry.data. Set on
currency mode change, cleared on same-mode save. On every async_setup_entry
with flag set, delete and recreate the repair issue so it reappears after
HA restart even if previously dismissed.

Repair issue text explains that HA Recorder shows its own unit-change
dialog (delayed) and recommends deleting old statistic data rather than
re-labeling, which would leave wrong values with the new unit.

Impact: Users are notified to review statistics and automations after
switching between base/subunit currency mode. Notification persists across
HA restarts until acknowledged by saving display settings again.
This commit is contained in:
Julian Pawlowski 2026-04-14 19:28:47 +00:00
parent a4ad506e01
commit 061b42b8f3
7 changed files with 86 additions and 2 deletions

View file

@ -14,6 +14,7 @@ import voluptuous as vol
from homeassistant.config_entries import ConfigEntry, ConfigEntryState from homeassistant.config_entries import ConfigEntry, ConfigEntryState
from homeassistant.const import CONF_ACCESS_TOKEN, Platform from homeassistant.const import CONF_ACCESS_TOKEN, Platform
from homeassistant.exceptions import ConfigEntryAuthFailed from homeassistant.exceptions import ConfigEntryAuthFailed
from homeassistant.helpers import issue_registry as ir
from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.storage import Store from homeassistant.helpers.storage import Store
from homeassistant.loader import async_get_loaded_integration from homeassistant.loader import async_get_loaded_integration
@ -25,6 +26,7 @@ from .const import (
CONF_PRICE_TREND_MIN_PRICE_CHANGE_STRONGLY, CONF_PRICE_TREND_MIN_PRICE_CHANGE_STRONGLY,
DATA_CHART_CONFIG, DATA_CHART_CONFIG,
DATA_CHART_METADATA_CONFIG, DATA_CHART_METADATA_CONFIG,
DATA_STATISTICS_REVIEW_REQUIRED,
DISPLAY_MODE_SUBUNIT, DISPLAY_MODE_SUBUNIT,
DOMAIN, DOMAIN,
LOGGER, LOGGER,
@ -212,6 +214,30 @@ def _get_access_token(hass: HomeAssistant, entry: ConfigEntry) -> str:
raise ConfigEntryAuthFailed(msg) raise ConfigEntryAuthFailed(msg)
def _check_statistics_review_repair(hass: HomeAssistant, entry: TibberPricesConfigEntry) -> None:
"""Re-create the statistics-review repair issue fresh on every setup when the flag is set.
Using delete + create (instead of get_or_create) resets dismissed_version, so the issue
reappears in the Repairs panel even if the user had dismissed it before a restart.
The flag is cleared from config_entry.data only when the user acknowledges the change
by re-saving the currency display settings in the options flow.
"""
if not entry.data.get(DATA_STATISTICS_REVIEW_REQUIRED):
return
issue_id = f"currency_display_mode_changed_{entry.entry_id}"
ir.async_delete_issue(hass, DOMAIN, issue_id)
ir.async_create_issue(
hass,
DOMAIN,
issue_id,
is_fixable=False,
is_persistent=True,
severity=ir.IssueSeverity.WARNING,
translation_key="currency_display_mode_changed",
translation_placeholders={"home_name": entry.title},
)
# https://developers.home-assistant.io/docs/config_entries_index/#setting-up-an-entry # https://developers.home-assistant.io/docs/config_entries_index/#setting-up-an-entry
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
@ -226,6 +252,9 @@ async def async_setup_entry(
# Check for entity migrations (renames, breaking changes) and create repairs # Check for entity migrations (renames, breaking changes) and create repairs
check_entity_migrations(hass, entry) check_entity_migrations(hass, entry)
# Re-create statistics review repair issue fresh (resets any previous dismiss)
_check_statistics_review_repair(hass, entry)
# Preload translations to populate the cache # Preload translations to populate the cache
await async_load_translations(hass, "en") await async_load_translations(hass, "en")
await async_load_standard_translations(hass, "en") await async_load_standard_translations(hass, "en")

View file

@ -2,8 +2,8 @@
from __future__ import annotations from __future__ import annotations
import logging
from copy import deepcopy from copy import deepcopy
import logging
from typing import TYPE_CHECKING, Any from typing import TYPE_CHECKING, Any
if TYPE_CHECKING: if TYPE_CHECKING:
@ -52,6 +52,7 @@ from custom_components.tibber_prices.const import (
CONF_BEST_PRICE_MAX_LEVEL_GAP_COUNT, CONF_BEST_PRICE_MAX_LEVEL_GAP_COUNT,
CONF_BEST_PRICE_MIN_DISTANCE_FROM_AVG, CONF_BEST_PRICE_MIN_DISTANCE_FROM_AVG,
CONF_BEST_PRICE_MIN_PERIOD_LENGTH, CONF_BEST_PRICE_MIN_PERIOD_LENGTH,
CONF_CURRENCY_DISPLAY_MODE,
CONF_MIN_PERIODS_BEST, CONF_MIN_PERIODS_BEST,
CONF_MIN_PERIODS_PEAK, CONF_MIN_PERIODS_PEAK,
CONF_PEAK_PRICE_FLEX, CONF_PEAK_PRICE_FLEX,
@ -71,6 +72,7 @@ from custom_components.tibber_prices.const import (
CONF_VOLATILITY_THRESHOLD_HIGH, CONF_VOLATILITY_THRESHOLD_HIGH,
CONF_VOLATILITY_THRESHOLD_MODERATE, CONF_VOLATILITY_THRESHOLD_MODERATE,
CONF_VOLATILITY_THRESHOLD_VERY_HIGH, CONF_VOLATILITY_THRESHOLD_VERY_HIGH,
DATA_STATISTICS_REVIEW_REQUIRED,
DEFAULT_VOLATILITY_THRESHOLD_HIGH, DEFAULT_VOLATILITY_THRESHOLD_HIGH,
DEFAULT_VOLATILITY_THRESHOLD_MODERATE, DEFAULT_VOLATILITY_THRESHOLD_MODERATE,
DEFAULT_VOLATILITY_THRESHOLD_VERY_HIGH, DEFAULT_VOLATILITY_THRESHOLD_VERY_HIGH,
@ -82,7 +84,7 @@ from custom_components.tibber_prices.const import (
get_display_unit_factor, get_display_unit_factor,
) )
from homeassistant.config_entries import ConfigFlowResult, OptionsFlow from homeassistant.config_entries import ConfigFlowResult, OptionsFlow
from homeassistant.helpers import entity_registry as er from homeassistant.helpers import entity_registry as er, issue_registry as ir
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -501,10 +503,43 @@ class TibberPricesOptionsFlowHandler(OptionsFlow):
currency_code = tibber_data.coordinator.data.get("currency") currency_code = tibber_data.coordinator.data.get("currency")
if user_input is not None: if user_input is not None:
# Detect currency display mode change before saving
old_mode = self.config_entry.options.get(CONF_CURRENCY_DISPLAY_MODE)
new_mode = user_input.get(CONF_CURRENCY_DISPLAY_MODE)
# Update options with new values # Update options with new values
self._options.update(user_input) self._options.update(user_input)
# async_create_entry automatically handles change detection and listener triggering # async_create_entry automatically handles change detection and listener triggering
self._save_options_if_changed() self._save_options_if_changed()
# Handle currency display mode change repair + persistent flag
issue_id = f"currency_display_mode_changed_{self.config_entry.entry_id}"
mode_changed = old_mode is not None and new_mode is not None and old_mode != new_mode
if mode_changed:
# Set persistent flag so repair issue reappears after dismiss + HA restart
self.hass.config_entries.async_update_entry(
self.config_entry,
data={**self.config_entry.data, DATA_STATISTICS_REVIEW_REQUIRED: True},
)
ir.async_create_issue(
self.hass,
DOMAIN,
issue_id,
is_fixable=False,
is_persistent=True,
severity=ir.IssueSeverity.WARNING,
translation_key="currency_display_mode_changed",
translation_placeholders={
"home_name": self.config_entry.title,
},
)
elif self.config_entry.data.get(DATA_STATISTICS_REVIEW_REQUIRED):
# User re-saved display settings with same mode = acknowledgement → clear flag
new_data = {k: v for k, v in self.config_entry.data.items() if k != DATA_STATISTICS_REVIEW_REQUIRED}
self.hass.config_entries.async_update_entry(self.config_entry, data=new_data)
ir.async_delete_issue(self.hass, DOMAIN, issue_id)
# Return to menu for more changes # Return to menu for more changes
return await self.async_step_init() return await self.async_step_init()

View file

@ -1153,6 +1153,10 @@
"entity_migration": { "entity_migration": {
"title": "Tibber Prices: Aktion nach Update erforderlich ({home_name})", "title": "Tibber Prices: Aktion nach Update erforderlich ({home_name})",
"description": "Dieses Update enthält Breaking Changes, die automatisch angewendet wurden.\n\n**Umbenannte Entitäten ({count})**\n\nDie folgenden Entity-Keys wurden umbenannt. Deine bestehenden Entity-IDs und Automationen bleiben erhalten:\n\n{entity_list}\n\n**Geänderte Dauer-Sensorwerte**\n\nAlle Dauer-Sensoren (verbleibende Zeit, startet in, Periodendauer, Trendänderungs-Countdown) geben ihren Zustandswert jetzt in **Minuten** statt Stunden an. Die Anzeigeeinheit in Dashboards bleibt standardmäßig Stunden.\n\nWenn du Automationen mit numerischen Vergleichen auf diesen Sensoren hast, aktualisiere deine Schwellwerte:\n- Alt: `state < 0.25` (15 Minuten als Stunden)\n- Neu: `state < 15` (15 Minuten)\n\nSchließe diesen Hinweis, nachdem du deine Automationen überprüft hast." "description": "Dieses Update enthält Breaking Changes, die automatisch angewendet wurden.\n\n**Umbenannte Entitäten ({count})**\n\nDie folgenden Entity-Keys wurden umbenannt. Deine bestehenden Entity-IDs und Automationen bleiben erhalten:\n\n{entity_list}\n\n**Geänderte Dauer-Sensorwerte**\n\nAlle Dauer-Sensoren (verbleibende Zeit, startet in, Periodendauer, Trendänderungs-Countdown) geben ihren Zustandswert jetzt in **Minuten** statt Stunden an. Die Anzeigeeinheit in Dashboards bleibt standardmäßig Stunden.\n\nWenn du Automationen mit numerischen Vergleichen auf diesen Sensoren hast, aktualisiere deine Schwellwerte:\n- Alt: `state < 0.25` (15 Minuten als Stunden)\n- Neu: `state < 15` (15 Minuten)\n\nSchließe diesen Hinweis, nachdem du deine Automationen überprüft hast."
},
"currency_display_mode_changed": {
"title": "Währungsanzeigeeinheit für {home_name} geändert",
"description": "Du hast den Währungsanzeigemodus für **{home_name}** geändert. Alle Preissensor-Werte und -Attribute verwenden jetzt die neue Einheit (z.B. 25,34 ct → 0,2534 € oder umgekehrt).\n\nDer Recorder von Home Assistant zeigt separat einen Dialog **„Die Einheit hat sich geändert“** für betroffene Sensoren — das kann einige Minuten dauern oder bis zum nächsten Statistik-Durchlauf (Warnungen im Log erscheinen früher). Wähle dann **Alle alten Statistikdaten löschen** für einen sauberen Neustart. Wähle nicht „Einheit aktualisieren ohne Konvertierung“: das benennt die alten Zahlen nur um, ohne die Werte anzupassen, und macht die historischen Daten inhaltlich falsch.\n\n**Manuell prüfen:**\n\n1. **Automationen & Templates:** Aktualisiere alle Automationen und Template-Sensoren mit numerischen Preis-Schwellwerten.\n2. **Dashboard-Karten:** Aktualisiere alle Karten mit fest codierten Schwellwerten oder Einheitenbezeichnungen.\n\nSchließe diesen Hinweis, nachdem du deine Automationen und Dashboards überprüft hast."
} }
}, },
"exceptions": { "exceptions": {

View file

@ -1153,6 +1153,10 @@
"entity_migration": { "entity_migration": {
"title": "Tibber Prices: Action required after update ({home_name})", "title": "Tibber Prices: Action required after update ({home_name})",
"description": "This update includes breaking changes that were applied automatically.\n\n**Renamed Entities ({count})**\n\nThe following entity keys were renamed. Your existing entity IDs and automations remain intact:\n\n{entity_list}\n\n**Duration Sensor Value Change**\n\nAll duration sensors (remaining time, starts in, period duration, trend change countdown) now report their state value in **minutes** instead of hours. The display unit in dashboards remains hours by default.\n\nIf you have automations using numeric comparisons on these sensors, update your thresholds:\n- Old: `state < 0.25` (15 minutes as hours)\n- New: `state < 15` (15 minutes)\n\nDismiss this notice after reviewing your automations." "description": "This update includes breaking changes that were applied automatically.\n\n**Renamed Entities ({count})**\n\nThe following entity keys were renamed. Your existing entity IDs and automations remain intact:\n\n{entity_list}\n\n**Duration Sensor Value Change**\n\nAll duration sensors (remaining time, starts in, period duration, trend change countdown) now report their state value in **minutes** instead of hours. The display unit in dashboards remains hours by default.\n\nIf you have automations using numeric comparisons on these sensors, update your thresholds:\n- Old: `state < 0.25` (15 minutes as hours)\n- New: `state < 15` (15 minutes)\n\nDismiss this notice after reviewing your automations."
},
"currency_display_mode_changed": {
"title": "Currency display unit changed for {home_name}",
"description": "You changed the currency display mode for **{home_name}**. All price sensor values and attributes now use the new unit (e.g. 25.34 ct → 0.2534 € or vice versa).\n\nHome Assistants Recorder will separately show a **“The unit has changed”** dialog for affected sensors — this may take a few minutes or until the next statistics run (log warnings appear earlier). When it appears, choose **Delete all old statistic data** to start fresh. Do not choose “Update the unit without converting”: that re-labels the old numbers without adjusting their values, making the historic data factually incorrect.\n\n**Review manually:**\n\n1. **Automations & Templates:** Update all automations and template sensors that use numeric price thresholds.\n2. **Dashboard Cards:** Update any cards with hardcoded thresholds or unit labels.\n\nDismiss this notice after reviewing your automations and dashboards."
} }
}, },
"exceptions": { "exceptions": {

View file

@ -1153,6 +1153,10 @@
"entity_migration": { "entity_migration": {
"title": "Tibber Prices: Handling kreves etter oppdatering ({home_name})", "title": "Tibber Prices: Handling kreves etter oppdatering ({home_name})",
"description": "Denne oppdateringen inkluderer endringer som ble brukt automatisk.\n\n**Omdøpte entiteter ({count})**\n\nFølgende entity-nøkler ble omdøpt. Dine eksisterende entity-ID-er og automatiseringer forblir intakte:\n\n{entity_list}\n\n**Endrede varighetssensorverdier**\n\nAlle varighetssensorer (gjenværende tid, starter om, periodevarighet, trendendrings-nedtelling) rapporterer nå tilstandsverdien i **minutter** i stedet for timer. Visningsenheten i dashboards forblir timer som standard.\n\nHvis du har automatiseringer med numeriske sammenligninger på disse sensorene, oppdater tersklene:\n- Gammelt: `state < 0.25` (15 minutter som timer)\n- Nytt: `state < 15` (15 minutter)\n\nAvvis dette varselet etter å ha gjennomgått automatiseringene dine." "description": "Denne oppdateringen inkluderer endringer som ble brukt automatisk.\n\n**Omdøpte entiteter ({count})**\n\nFølgende entity-nøkler ble omdøpt. Dine eksisterende entity-ID-er og automatiseringer forblir intakte:\n\n{entity_list}\n\n**Endrede varighetssensorverdier**\n\nAlle varighetssensorer (gjenværende tid, starter om, periodevarighet, trendendrings-nedtelling) rapporterer nå tilstandsverdien i **minutter** i stedet for timer. Visningsenheten i dashboards forblir timer som standard.\n\nHvis du har automatiseringer med numeriske sammenligninger på disse sensorene, oppdater tersklene:\n- Gammelt: `state < 0.25` (15 minutter som timer)\n- Nytt: `state < 15` (15 minutter)\n\nAvvis dette varselet etter å ha gjennomgått automatiseringene dine."
},
"currency_display_mode_changed": {
"title": "Valutavisningsenhet endret for {home_name}",
"description": "Du endret valutavisningsmodusen for **{home_name}**. Alle prissensorverdier og -attributter bruker nå den nye enheten (f.eks. 25,34 øre → 0,2534 kr eller omvendt).\n\nHome Assistants Recorder viser separat en **„Enheten har endret seg“**-dialog for berørte sensorer — dette kan ta noen minutter eller til neste statistikkjøring (advarsler i loggen dukker opp tidligere). Når den vises, velg **Slett alle gamle statistikkdata** for en ren start. Ikke velg „Oppdater enheten uten konvertering“: det beholder de gamle tallene med ny enhet uten å justere verdiene, og gjør de historiske dataene faktisk feil.\n\n**Gjennomgå manuelt:**\n\n1. **Automatiseringer & maler:** Oppdater alle automatiseringer og malsensorer som bruker numeriske pristerskler.\n2. **Dashboard-kort:** Oppdater kort med hardkodede terskelverdier eller enhetsetiketter.\n\nAvvis dette varselet etter å ha gjennomgått automatiseringene og dashboardene dine."
} }
}, },
"exceptions": { "exceptions": {

View file

@ -1153,6 +1153,10 @@
"entity_migration": { "entity_migration": {
"title": "Tibber Prices: Actie vereist na update ({home_name})", "title": "Tibber Prices: Actie vereist na update ({home_name})",
"description": "Deze update bevat wijzigingen die automatisch zijn toegepast.\n\n**Hernoemde entiteiten ({count})**\n\nDe volgende entity-sleutels zijn hernoemd. Je bestaande entity-ID's en automatiseringen blijven intact:\n\n{entity_list}\n\n**Gewijzigde duur-sensorwaarden**\n\nAlle duur-sensoren (resterende tijd, start over, periodeduur, trendwijzigings-aftelling) rapporteren hun statuswaarde nu in **minuten** in plaats van uren. De weergave-eenheid in dashboards blijft standaard uren.\n\nAls je automatiseringen hebt met numerieke vergelijkingen op deze sensoren, werk dan je drempelwaarden bij:\n- Oud: `state < 0.25` (15 minuten als uren)\n- Nieuw: `state < 15` (15 minuten)\n\nSluit deze melding nadat je je automatiseringen hebt gecontroleerd." "description": "Deze update bevat wijzigingen die automatisch zijn toegepast.\n\n**Hernoemde entiteiten ({count})**\n\nDe volgende entity-sleutels zijn hernoemd. Je bestaande entity-ID's en automatiseringen blijven intact:\n\n{entity_list}\n\n**Gewijzigde duur-sensorwaarden**\n\nAlle duur-sensoren (resterende tijd, start over, periodeduur, trendwijzigings-aftelling) rapporteren hun statuswaarde nu in **minuten** in plaats van uren. De weergave-eenheid in dashboards blijft standaard uren.\n\nAls je automatiseringen hebt met numerieke vergelijkingen op deze sensoren, werk dan je drempelwaarden bij:\n- Oud: `state < 0.25` (15 minuten als uren)\n- Nieuw: `state < 15` (15 minuten)\n\nSluit deze melding nadat je je automatiseringen hebt gecontroleerd."
},
"currency_display_mode_changed": {
"title": "Valutaweergave-eenheid gewijzigd voor {home_name}",
"description": "Je hebt de valutaweergavemodus voor **{home_name}** gewijzigd. Alle prijssensorwaarden en -attributen gebruiken nu de nieuwe eenheid (bijv. 25,34 ct → 0,2534 € of andersom).\n\nHome Assistants Recorder toont afzonderlijk een **„De eenheid is gewijzigd“**-dialoogvenster voor getroffen sensoren — dit kan enkele minuten duren of tot de volgende statistiekenrun (logwaarschuwingen verschijnen eerder). Kies dan **Alle oude statistiekgegevens verwijderen** voor een schone start. Kies niet „Eenheid bijwerken zonder conversie“: dat hernoemt de oude getallen zonder de waarden aan te passen, waardoor de historische gegevens inhoudelijk onjuist worden.\n\n**Handmatig controleren:**\n\n1. **Automatiseringen & templates:** Werk alle automatiseringen en template-sensoren bij die numerieke prijsdrempels gebruiken.\n2. **Dashboard-kaarten:** Werk kaarten bij met hardgecodeerde drempelwaarden of eenheidslabels.\n\nSluit deze melding nadat je je automatiseringen en dashboards hebt gecontroleerd."
} }
}, },
"exceptions": { "exceptions": {

View file

@ -1153,6 +1153,10 @@
"entity_migration": { "entity_migration": {
"title": "Tibber Prices: Åtgärd krävs efter uppdatering ({home_name})", "title": "Tibber Prices: Åtgärd krävs efter uppdatering ({home_name})",
"description": "Denna uppdatering innehåller ändringar som tillämpades automatiskt.\n\n**Omdöpta entiteter ({count})**\n\nFöljande entity-nycklar döptes om automatiskt. Dina befintliga entity-ID:n och automatiseringar förblir intakta:\n\n{entity_list}\n\n**Ändrade varaktighetssensorvärden**\n\nAlla varaktighetssensorer (återstående tid, startar om, periodvaraktighet, trendändrings-nedräkning) rapporterar nu sitt tillståndsvärde i **minuter** istället för timmar. Visningsenheten i dashboards förblir timmar som standard.\n\nOm du har automatiseringar med numeriska jämförelser på dessa sensorer, uppdatera dina tröskelvärden:\n- Gammalt: `state < 0.25` (15 minuter som timmar)\n- Nytt: `state < 15` (15 minuter)\n\nStäng detta meddelande efter att du har granskat dina automatiseringar." "description": "Denna uppdatering innehåller ändringar som tillämpades automatiskt.\n\n**Omdöpta entiteter ({count})**\n\nFöljande entity-nycklar döptes om automatiskt. Dina befintliga entity-ID:n och automatiseringar förblir intakta:\n\n{entity_list}\n\n**Ändrade varaktighetssensorvärden**\n\nAlla varaktighetssensorer (återstående tid, startar om, periodvaraktighet, trendändrings-nedräkning) rapporterar nu sitt tillståndsvärde i **minuter** istället för timmar. Visningsenheten i dashboards förblir timmar som standard.\n\nOm du har automatiseringar med numeriska jämförelser på dessa sensorer, uppdatera dina tröskelvärden:\n- Gammalt: `state < 0.25` (15 minuter som timmar)\n- Nytt: `state < 15` (15 minuter)\n\nStäng detta meddelande efter att du har granskat dina automatiseringar."
},
"currency_display_mode_changed": {
"title": "Valutavisningsenhet ändrad för {home_name}",
"description": "Du ändrade valutavisningsläget för **{home_name}**. Alla prissensorvärden och -attribut använder nu den nya enheten (t.ex. 25,34 öre → 0,2534 kr eller tvärtom).\n\nHome Assistants Recorder visar separat en **„Enheten har ändrats“**-dialog för berörda sensorer — det kan ta några minuter eller till nästa statistikkörning (loggvarningar dyker upp tidigare). När den visas, välj **Ta bort alla gamla statistikdata** för en ren start. Välj inte „Uppdatera enheten utan konvertering“: det behåller de gamla talen med ny enhet utan att justera värdena, vilket gör historiska data faktiskt felaktiga.\n\n**Granska manuellt:**\n\n1. **Automatiseringar & mallar:** Uppdatera alla automatiseringar och mallsensorer som använder numeriska priströsklar.\n2. **Dashboard-kort:** Uppdatera kort med hårdkodade tröskelvärden eller enhetsetiketter.\n\nStäng detta meddelande efter att du har granskat dina automatiseringar och dashboards."
} }
}, },
"exceptions": { "exceptions": {