Fix AttributeError in OptionsFlow and add loading icon overlay

- Fix BomLocalOptionsFlow AttributeError by using super().__init__() instead of direct assignment
- Add single_config_entry to manifest to enforce single integration instance
- Add YAML config check to prevent UI config when YAML config exists
- Add icon static path registration for /local/community/bom_local/icon.png
- Add loading overlay with animated icon to card
- Bump version to 0.3.0

Fixes #1
This commit is contained in:
2025-12-24 02:04:58 +10:00
parent 9d4d176013
commit 5e456e7dd4
9 changed files with 55 additions and 5 deletions
+5
View File
@@ -66,6 +66,11 @@ async def _async_setup_integration(hass: HomeAssistant, service_url: str):
"/local/community/bom_local/bom-local-card.js", "/local/community/bom_local/bom-local-card.js",
hass.config.path("custom_components/bom_local/www/bom-local-card.js"), hass.config.path("custom_components/bom_local/www/bom-local-card.js"),
True True
),
StaticPathConfig(
"/local/community/bom_local/icon.png",
hass.config.path("custom_components/bom_local/icon.png"),
True
) )
]) ])
+5 -1
View File
@@ -17,6 +17,10 @@ class BomLocalConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
async def async_step_user(self, user_input=None): async def async_step_user(self, user_input=None):
"""Handle the initial step.""" """Handle the initial step."""
# Check if YAML configuration already exists by checking if component is loaded
if DOMAIN in self.hass.config.components:
return self.async_abort(reason="already_configured")
errors = {} errors = {}
if user_input is not None: if user_input is not None:
# We could validate the URL here by trying a fetch # We could validate the URL here by trying a fetch
@@ -39,7 +43,7 @@ class BomLocalOptionsFlow(config_entries.OptionsFlow):
"""Handle options flow.""" """Handle options flow."""
def __init__(self, config_entry): def __init__(self, config_entry):
self.config_entry = config_entry super().__init__(config_entry)
async def async_step_init(self, user_input=None): async def async_step_init(self, user_input=None):
"""Manage the options.""" """Manage the options."""
Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

+2 -1
View File
@@ -3,8 +3,9 @@
"name": "BOM Local Radar", "name": "BOM Local Radar",
"codeowners": ["@alexhopeoconnor"], "codeowners": ["@alexhopeoconnor"],
"config_flow": true, "config_flow": true,
"single_config_entry": true,
"iot_class": "local_polling", "iot_class": "local_polling",
"version": "0.2.0", "version": "0.3.0",
"dependencies": ["http", "websocket_api"], "dependencies": ["http", "websocket_api"],
"documentation": "https://github.com/alexhopeoconnor/bom-local-card", "documentation": "https://github.com/alexhopeoconnor/bom-local-card",
"issue_tracker": "https://github.com/alexhopeoconnor/bom-local-card/issues" "issue_tracker": "https://github.com/alexhopeoconnor/bom-local-card/issues"
@@ -1073,7 +1073,7 @@ BomLocalRadarCardEditor = __decorate([
t$1('bom-local-card-editor') t$1('bom-local-card-editor')
], BomLocalRadarCardEditor); ], BomLocalRadarCardEditor);
const CARD_VERSION = '0.2.0'; const CARD_VERSION = '0.3.0';
const DEFAULT_FRAME_INTERVAL = 2.0; // seconds const DEFAULT_FRAME_INTERVAL = 2.0; // seconds
const DEFAULT_RESTART_DELAY = 2000; // ms (pause before looping) const DEFAULT_RESTART_DELAY = 2000; // ms (pause before looping)
const DEFAULT_REFRESH_INTERVAL = 30; // seconds const DEFAULT_REFRESH_INTERVAL = 30; // seconds
@@ -1419,6 +1419,25 @@ const imageStyles = i$3 `
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
gap: 12px; gap: 12px;
z-index: 10;
}
.loading-icon {
width: 80px;
height: 80px;
animation: pulse 2s ease-in-out infinite;
opacity: 0.8;
}
@keyframes pulse {
0%, 100% {
opacity: 0.8;
transform: scale(1);
}
50% {
opacity: 1;
transform: scale(1.05);
}
} }
.loading-message { .loading-message {
@@ -3267,6 +3286,7 @@ let BomLocalRadarCard = class BomLocalRadarCard extends i {
${this.isLoading ${this.isLoading
? x ` ? x `
<div class="loading"> <div class="loading">
<img class="loading-icon" src="/local/community/bom_local/icon.png" alt="BOM Local Radar" />
<ha-circular-progress indeterminate></ha-circular-progress> <ha-circular-progress indeterminate></ha-circular-progress>
<div class="loading-message">Loading radar data...</div> <div class="loading-message">Loading radar data...</div>
</div> </div>
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "bom-local-card", "name": "bom-local-card",
"version": "0.2.0", "version": "0.3.0",
"description": "Home Assistant custom card for displaying Australian Bureau of Meteorology (BOM) rain radar data with animated slideshow and historical data support", "description": "Home Assistant custom card for displaying Australian Bureau of Meteorology (BOM) rain radar data with animated slideshow and historical data support",
"module": "bom-local-card.js", "module": "bom-local-card.js",
"keywords": [ "keywords": [
+1
View File
@@ -591,6 +591,7 @@ export class BomLocalRadarCard extends LitElement implements LovelaceCard {
${this.isLoading ${this.isLoading
? html` ? html`
<div class="loading"> <div class="loading">
<img class="loading-icon" src="/local/community/bom_local/icon.png" alt="BOM Local Radar" />
<ha-circular-progress indeterminate></ha-circular-progress> <ha-circular-progress indeterminate></ha-circular-progress>
<div class="loading-message">Loading radar data...</div> <div class="loading-message">Loading radar data...</div>
</div> </div>
+1 -1
View File
@@ -1,4 +1,4 @@
export const CARD_VERSION = '0.2.0'; export const CARD_VERSION = '0.3.0';
export const DEFAULT_FRAME_INTERVAL = 2.0; // seconds export const DEFAULT_FRAME_INTERVAL = 2.0; // seconds
export const DEFAULT_RESTART_DELAY = 2000; // ms (pause before looping) export const DEFAULT_RESTART_DELAY = 2000; // ms (pause before looping)
export const DEFAULT_REFRESH_INTERVAL = 30; // seconds export const DEFAULT_REFRESH_INTERVAL = 30; // seconds
+19
View File
@@ -72,6 +72,25 @@ export const imageStyles: CSSResultGroup = css`
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
gap: 12px; gap: 12px;
z-index: 10;
}
.loading-icon {
width: 80px;
height: 80px;
animation: pulse 2s ease-in-out infinite;
opacity: 0.8;
}
@keyframes pulse {
0%, 100% {
opacity: 0.8;
transform: scale(1);
}
50% {
opacity: 1;
transform: scale(1.05);
}
} }
.loading-message { .loading-message {