Skip to content

Commit f0b15f3

Browse files
committed
v2.0.1-preview: ServiceShield stavy v češtině, oprava dashboard panelu a battery prediction
- Přidány české překlady ServiceShield stavů (aktivní, nečinný, monitoruje, atd.) - Vylepšena kontrola existence frontend panelu před odstraněním - Oprava senzoru time_to_empty - zobrazuje 'Nabito' při 100% nabití místo 'Nabíjí se' - Přesunut battery_prediction_morning_soc do kategorie battery_prediction - Zlepšené error handling pro neexistující dashboard panely - Eliminace spam logů 'Removing unknown panel' změnou na debug level
1 parent 720bae6 commit f0b15f3

File tree

7 files changed

+678
-61
lines changed

7 files changed

+678
-61
lines changed

custom_components/oig_cloud/__init__.py

Lines changed: 67 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -179,28 +179,66 @@ async def _remove_frontend_panel(hass: HomeAssistant, entry: ConfigEntry) -> Non
179179

180180
from homeassistant.components import frontend
181181

182+
# OPRAVA: Důkladnější kontrola existence panelu
183+
panels_exist = False
184+
try:
185+
# Zkusíme získat přístup k registrovaným panelům
186+
if hasattr(hass, "components") and "frontend" in hass.components:
187+
frontend_component = hass.components.frontend
188+
if hasattr(frontend_component, "async_register_built_in_panel"):
189+
# Panel systém je dostupný
190+
panels_exist = True
191+
except Exception:
192+
pass
193+
194+
if not panels_exist:
195+
_LOGGER.debug(
196+
f"Frontend panel system not available, skipping removal of {panel_id}"
197+
)
198+
return
199+
200+
# OPRAVA: Kontrola existence panelu před odstraněním
201+
try:
202+
# Pokusíme se získat informace o panelu před jeho odstraněním
203+
# Tím ověříme, že skutečně existuje
204+
panel_exists = False
205+
206+
if hasattr(hass.data.get("frontend_panels", {}), panel_id):
207+
panel_exists = True
208+
elif hasattr(hass.data.get("frontend", {}), "panels"):
209+
existing_panels = hass.data["frontend"].panels
210+
panel_exists = panel_id in existing_panels
211+
212+
if not panel_exists:
213+
_LOGGER.debug(f"Panel {panel_id} doesn't exist, nothing to remove")
214+
return
215+
except Exception as check_error:
216+
_LOGGER.debug(
217+
f"Cannot check panel existence, proceeding with removal attempt: {check_error}"
218+
)
219+
220+
# Pokus o odebrání panelu
182221
if hasattr(frontend, "async_remove_panel") and callable(
183222
getattr(frontend, "async_remove_panel")
184223
):
185-
# OPRAVA: Nejdříve zkontrolujeme, jestli panel existuje
186-
if hasattr(hass.data.get("frontend", {}), "panels"):
187-
existing_panels = hass.data["frontend"].panels
188-
if panel_id in existing_panels:
189-
await frontend.async_remove_panel(hass, panel_id)
190-
_LOGGER.info(f"✅ Panel removed: {panel_id}")
191-
else:
192-
_LOGGER.debug(f"Panel {panel_id} not found, nothing to remove")
193-
else:
194-
# Pokud nemůžeme zkontrolovat existenci, zkusíme smazat přímo
224+
try:
195225
await frontend.async_remove_panel(hass, panel_id)
196226
_LOGGER.info(f"✅ Panel removed: {panel_id}")
227+
except ValueError as ve:
228+
if "unknown panel" in str(ve).lower():
229+
_LOGGER.debug(
230+
f"Panel {panel_id} was already removed or never existed"
231+
)
232+
else:
233+
_LOGGER.warning(f"Error removing panel {panel_id}: {ve}")
234+
except Exception as re:
235+
_LOGGER.debug(f"Panel removal handled (panel may not exist): {re}")
197236
else:
198237
_LOGGER.debug("async_remove_panel not available")
199238

200239
except Exception as e:
201-
_LOGGER.debug(
202-
f"Error removing frontend panel (this is expected if panel doesn't exist): {e}"
203-
)
240+
# OPRAVA: Všechny chyby logujeme jako debug, protože jsou očekávané
241+
_LOGGER.debug(f"Panel removal handled gracefully: {e}")
204242

205243

206244
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
@@ -389,7 +427,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
389427
_LOGGER.debug("Spot prices disabled - skipping OTE API initialization")
390428

391429
# NOVÉ: Podmíněné nastavení dashboard podle konfigurace
392-
dashboard_enabled = entry.options.get("enable_dashboard", False)
430+
dashboard_enabled = entry.options.get(
431+
"enable_dashboard", False
432+
) # OPRAVA: default False místo True
393433
# OPRAVA: Dashboard registrujeme AŽ PO vytvoření senzorů
394434

395435
# Uložení dat do hass.data
@@ -437,13 +477,15 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
437477
# Vždy registrovat sensor platform
438478
await hass.config_entries.async_forward_entry_setups(entry, ["sensor"])
439479

440-
# OPRAVA: Dashboard registrujeme až TERAZ - po vytvoření všech senzorů
480+
# OPRAVA: Dashboard registrujeme až TERAZ - po vytvoření všech senzorů A POUZE pokud je enabled
441481
if dashboard_enabled:
442482
await _setup_frontend_panel(hass, entry)
443483
_LOGGER.info("OIG Cloud Dashboard panel enabled and registered")
444484
else:
445485
await _remove_frontend_panel(hass, entry)
446-
_LOGGER.debug("OIG Cloud Dashboard panel disabled")
486+
_LOGGER.info(
487+
"OIG Cloud Dashboard panel disabled - panel not registered"
488+
) # OPRAVA: lepší log message
447489

448490
# Přidáme listener pro změny konfigurace - OPRAVEN callback na async funkci
449491
entry.async_on_unload(entry.add_update_listener(async_update_options))
@@ -594,6 +636,10 @@ async def async_update_options(
594636
old_dashboard_enabled = old_options.get("enable_dashboard", False)
595637
new_dashboard_enabled = new_options.get("enable_dashboard", False)
596638

639+
_LOGGER.debug(
640+
f"Dashboard options update: old={old_dashboard_enabled}, new={new_dashboard_enabled}"
641+
)
642+
597643
if old_dashboard_enabled != new_dashboard_enabled:
598644
_LOGGER.info(
599645
f"Dashboard setting changed: {old_dashboard_enabled} -> {new_dashboard_enabled}"
@@ -616,6 +662,11 @@ async def async_update_options(
616662
hass.data[DOMAIN][config_entry.entry_id]["config"][
617663
"enable_dashboard"
618664
] = new_dashboard_enabled
665+
else:
666+
# PŘIDÁNO: I když se hodnota nezměnila, ujistíme se že panel není registrován pokud je disabled
667+
if not new_dashboard_enabled:
668+
await _remove_frontend_panel(hass, config_entry)
669+
_LOGGER.debug("Ensuring dashboard panel is not registered (disabled)")
619670

620671
# Pokud byla označena potřeba reload, proveď ho
621672
if new_options.get("_needs_reload"):

custom_components/oig_cloud/config_flow.py

Lines changed: 12 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ async def validate_input(hass, data: Dict[str, Any]) -> Dict[str, Any]:
150150
): bool,
151151
vol.Optional(
152152
"enable_dashboard",
153-
default=True,
153+
default=False, # OPRAVA: změna z True na False
154154
description="Povolit webový dashboard s grafy",
155155
): bool, # NOVÉ: dashboard option
156156
}
@@ -222,6 +222,10 @@ async def async_step_user(
222222
"enable_extended_fve_sensors": True,
223223
"enable_extended_grid_sensors": True,
224224
"disable_extended_stats_api": False,
225+
# OPRAVA: Explicitně zakázat battery prediction
226+
"enable_battery_prediction": False,
227+
# OPRAVA: Explicitně zakázat dashboard
228+
"enable_dashboard": user_input.get("enable_dashboard", False),
225229
},
226230
)
227231

@@ -436,7 +440,7 @@ async def async_step_extended_sensors(
436440
step_id="extended_sensors",
437441
data_schema=vol.Schema(schema_fields),
438442
description_placeholders={
439-
"current_state": "Povoleno" if extended_enabled else "Zakázáno",
443+
"current_state": "Povolen" if extended_enabled else "Zakázáno",
440444
"info": (
441445
"⚠️ Rozšířené senzory jsou vypnuté - sub-moduly se aktivují po zapnutí"
442446
if not extended_enabled
@@ -500,8 +504,8 @@ async def async_step_battery_prediction(
500504
vol.Required(
501505
"info_only",
502506
default="back_to_menu",
503-
description="⚠️ MODUL VE VÝVOJI - Změny nejsou možné",
504-
): vol.In({"back_to_menu": "⬅️ Zpět do hlavního menu"})
507+
description="Modul ve vývoji - změny nejsou možné",
508+
): vol.In({"back_to_menu": "Zpět do hlavního menu"})
505509
}
506510
)
507511

@@ -515,25 +519,7 @@ async def async_step_battery_prediction(
515519
"percentile": current_options.get("percentile_conf", 80.0),
516520
"max_price": current_options.get("max_price_conf", 4.0),
517521
"total_hours": current_options.get("total_hours", 24),
518-
"dev_status": "🚧 MODUL VE VÝVOJI",
519-
"info": (
520-
"⚠️ POUZE PRO ČTENÍ - MODUL VE VÝVOJI\n\n"
521-
"Predikce baterie je momentálně ve vývoji a není dostupná pro konfiguraci. "
522-
f"Aktuální stav: {('POVOLEN' if battery_enabled else 'ZAKÁZÁN')}"
523-
+ (
524-
f"\n\nAktuální parametry:\n"
525-
f"• Min. kapacita: {current_options.get('min_capacity_percent', 20.0)}%\n"
526-
f"• Nabíjecí výkon: {current_options.get('home_charge_rate', 2800)}W\n"
527-
f"• Percentil: {current_options.get('percentile_conf', 80.0)}%\n"
528-
f"• Max. cena: {current_options.get('max_price_conf', 4.0)} CZK/kWh\n"
529-
f"• Horizont: {current_options.get('total_hours', 24)}h"
530-
if battery_enabled
531-
else ""
532-
)
533-
),
534-
"requirements": "POŽADAVKY: Statistiky (📊) + Spotové ceny (💰) musí být zapnuté",
535-
"features": "PLÁNOVANÉ FUNKCE: • Inteligentní plánování nabíjení • Optimalizace podle spotových cen • Predikce kapacity baterie • Automatické doporučení kdy nabíjet",
536-
"timeline": "ČASOVÝ PLÁN: Modul bude dokončen v příští verzi integrace",
522+
"info": "Predikce baterie je momentálně ve vývoji a není dostupná pro konfiguraci",
537523
},
538524
)
539525

@@ -1444,8 +1430,8 @@ async def async_step_dashboard_config(
14441430
vol.Required(
14451431
"info_only",
14461432
default="back_to_menu",
1447-
description="⚠️ MODUL VE VÝVOJI - Změny nejsou možné",
1448-
): vol.In({"back_to_menu": "⬅️ Zpět do hlavního menu"})
1433+
description="Modul ve vývoji - změny nejsou možné",
1434+
): vol.In({"back_to_menu": "Zpět do hlavního menu"})
14491435
}
14501436
)
14511437

@@ -1454,15 +1440,6 @@ async def async_step_dashboard_config(
14541440
data_schema=schema,
14551441
description_placeholders={
14561442
"current_state": ("Povolen" if dashboard_enabled else "Zakázán"),
1457-
"dev_status": "🚧 MODUL VE VÝVOJI",
1458-
"info": (
1459-
"⚠️ POUZE PRO ČTENÍ - MODUL VE VÝVOJI\n\n"
1460-
"Webový dashboard je momentálně ve vývoji a není dostupný pro konfiguraci. "
1461-
f"Aktuální stav: {('POVOLEN' if dashboard_enabled else 'ZAKÁZÁN')}\n\n"
1462-
"Dashboard bude automaticky dostupný v levém menu Home Assistant po dokončení vývoje."
1463-
),
1464-
"features": "PLÁNOVANÉ FUNKCE:\n• Predikce kapacity baterie\n• Solární předpověď\n• Spotové ceny elektřiny\n• Interaktivní grafy s Apex Charts\n• Real-time monitoring\n• Exporty dat",
1465-
"timeline": "ČASOVÝ PLÁN: Dashboard bude dokončen v příští verzi integrace",
1466-
"access": "PŘÍSTUP: Po dokončení bude dostupný přes levé menu → 'OIG Dashboard'",
1443+
"info": "Webový dashboard je momentálně ve vývoji a není dostupný pro konfiguraci",
14671444
},
14681445
)

custom_components/oig_cloud/oig_cloud_computed_sensor.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,11 @@ def state(self) -> Optional[Union[float, str]]:
195195
usable = bat_p * 0.8
196196
missing = bat_p * (1 - bat_c / 100)
197197
remaining = usable - missing
198-
if bat_power < 0:
198+
199+
# OPRAVA: Kontrola na plně nabitou baterii (100%)
200+
if bat_c >= 100:
201+
return "Nabito"
202+
elif bat_power < 0:
199203
return self._format_time(remaining / abs(bat_power))
200204
elif remaining == 0:
201205
return "Vybito"

custom_components/oig_cloud/oig_cloud_shield_sensor.py

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,25 @@
99

1010
_LOGGER = logging.getLogger(__name__)
1111

12+
# OPRAVA: České překlady pro ServiceShield stavy
13+
SERVICESHIELD_STATE_TRANSLATIONS: Dict[str, str] = {
14+
"active": "aktivní",
15+
"idle": "nečinný",
16+
"monitoring": "monitoruje",
17+
"protecting": "chrání",
18+
"disabled": "zakázán",
19+
"error": "chyba",
20+
"starting": "spouští se",
21+
"stopping": "zastavuje se",
22+
"unknown": "neznámý",
23+
"unavailable": "nedostupný",
24+
}
25+
26+
27+
def translate_shield_state(state: str) -> str:
28+
"""Přeloží ServiceShield stav do češtiny."""
29+
return SERVICESHIELD_STATE_TRANSLATIONS.get(state.lower(), state)
30+
1231

1332
class OigCloudShieldSensor(OigCloudSensor):
1433
"""Senzor pro ServiceShield monitoring."""
@@ -91,10 +110,10 @@ def state(self) -> Optional[Union[str, int, datetime]]:
91110
try:
92111
shield = self.hass.data[DOMAIN].get("shield")
93112
if not shield:
94-
return "unavailable"
113+
return translate_shield_state("unavailable")
95114

96115
if self._sensor_type == "service_shield_status":
97-
return "active"
116+
return translate_shield_state("active")
98117
elif self._sensor_type == "service_shield_queue":
99118
# Celkový počet: čekající ve frontě + všechny pending služby
100119
queue = getattr(shield, "queue", [])
@@ -105,13 +124,13 @@ def state(self) -> Optional[Union[str, int, datetime]]:
105124
if running:
106125
return running.replace("oig_cloud.", "")
107126
else:
108-
return "idle"
127+
return translate_shield_state("idle")
109128

110129
except Exception as e:
111130
_LOGGER.error(f"Error getting shield sensor state: {e}")
112-
return None
131+
return translate_shield_state("error")
113132

114-
return None
133+
return translate_shield_state("unknown")
115134

116135
@property
117136
def extra_state_attributes(self) -> Dict[str, Any]:

custom_components/oig_cloud/sensor.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -417,7 +417,9 @@ async def async_setup_entry(
417417
_LOGGER.error(f"Error initializing notification sensors: {e}")
418418

419419
# 8. Battery Prediction sensors - pouze pokud je povolen
420-
battery_prediction_enabled = entry.options.get("enable_battery_prediction", True)
420+
battery_prediction_enabled = entry.options.get(
421+
"enable_battery_prediction", False
422+
) # OPRAVA: změna z True na False
421423
_LOGGER.info(f"Battery prediction enabled: {battery_prediction_enabled}")
422424

423425
if battery_prediction_enabled:

custom_components/oig_cloud/sensors/SENSOR_TYPES_STATISTICS.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@
169169
"unit": UnitOfTime.HOURS,
170170
"icon": "mdi:battery-clock",
171171
"device_class": SensorDeviceClass.DURATION,
172-
"sensor_type_category": "statistics",
172+
"sensor_type_category": "battery_prediction",
173173
},
174174
"battery_prediction_needed_capacity": {
175175
"name": "Battery Needed Capacity Prediction",
@@ -178,7 +178,7 @@
178178
"icon": "mdi:battery-plus",
179179
"device_class": SensorDeviceClass.ENERGY,
180180
"state_class": SensorStateClass.TOTAL,
181-
"sensor_type_category": "statistics",
181+
"sensor_type_category": "battery_prediction",
182182
},
183183
"battery_prediction_morning_soc": {
184184
"name": "Battery Morning SOC Prediction",
@@ -187,7 +187,7 @@
187187
"icon": "mdi:battery-clock-outline",
188188
"device_class": SensorDeviceClass.BATTERY,
189189
"state_class": SensorStateClass.MEASUREMENT,
190-
"sensor_type_category": "statistics",
190+
"sensor_type_category": "battery_prediction",
191191
},
192192
# Hodinové reálné senzory - používají existující computed energy senzory
193193
"hourly_real_battery_charge_kwh": {

0 commit comments

Comments
 (0)