From 4da30399d92c8bcc24df09b814ce8036bee1b417 Mon Sep 17 00:00:00 2001 From: Kenneth Selmer Lokshall Date: Thu, 2 Jul 2026 18:21:44 +0200 Subject: [PATCH 1/3] Update flight direction labels in config flow and sensor naming --- custom_components/avinor_flight_data/config_flow.py | 4 +++- custom_components/avinor_flight_data/sensor.py | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/custom_components/avinor_flight_data/config_flow.py b/custom_components/avinor_flight_data/config_flow.py index 429ba64..0b10084 100644 --- a/custom_components/avinor_flight_data/config_flow.py +++ b/custom_components/avinor_flight_data/config_flow.py @@ -65,8 +65,10 @@ async def async_step_user(self, user_input: Dict[str, Any] | None = None) -> Flo title_suffix = "All" if flight_type == "ALL" else flight_type if schedule_source == "airlabs": title_suffix = f"{title_suffix} Airlabs" + direction_labels = {"A": "Arrivals", "D": "Departures"} + direction_label = direction_labels.get(user_input[CONF_DIRECTION], user_input[CONF_DIRECTION]) return self.async_create_entry( - title=f"{user_input[CONF_AIRPORT]} {user_input[CONF_DIRECTION]} {title_suffix}", + title=f"{user_input[CONF_AIRPORT]} {direction_label} {title_suffix}", data=user_input, ) diff --git a/custom_components/avinor_flight_data/sensor.py b/custom_components/avinor_flight_data/sensor.py index 278da13..56d32e9 100644 --- a/custom_components/avinor_flight_data/sensor.py +++ b/custom_components/avinor_flight_data/sensor.py @@ -83,7 +83,9 @@ def __init__(self, entry: ConfigEntry, coordinator) -> None: name_suffix = "All" if flight_type == "ALL" else flight_type if schedule_source == "airlabs": name_suffix = f"{name_suffix} Airlabs" - self._attr_name = f"Avinor {airport} {direction} {name_suffix}" + direction_labels = {"A": "Arrivals", "D": "Departures"} + direction_label = direction_labels.get(direction, direction) + self._attr_name = f"Avinor {airport} {direction_label} {name_suffix}" @property def device_info(self): From 50c604ba2dea1f2c87a3e9bed9b6f83a74b3737a Mon Sep 17 00:00:00 2001 From: Kenneth Selmer Lokshall Date: Thu, 2 Jul 2026 19:02:54 +0200 Subject: [PATCH 2/3] Add support for Airlabs free tier with hourly updates and update translations --- custom_components/avinor_flight_data/__init__.py | 10 ++++++++-- custom_components/avinor_flight_data/config_flow.py | 4 ++++ custom_components/avinor_flight_data/const.py | 3 +++ .../avinor_flight_data/translations/en.json | 12 ++++++++---- .../avinor_flight_data/translations/nb.json | 12 ++++++++---- 5 files changed, 31 insertions(+), 10 deletions(-) diff --git a/custom_components/avinor_flight_data/__init__.py b/custom_components/avinor_flight_data/__init__.py index c712c1a..c90eeea 100644 --- a/custom_components/avinor_flight_data/__init__.py +++ b/custom_components/avinor_flight_data/__init__.py @@ -16,7 +16,7 @@ from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.exceptions import HomeAssistantError -from .const import DOMAIN, PLATFORMS, UPDATE_INTERVAL_SECONDS, CONF_AIRLABS_API_KEY, SERVICE_GET_FLIGHT_DETAILS +from .const import DOMAIN, PLATFORMS, UPDATE_INTERVAL_SECONDS, CONF_AIRLABS_API_KEY, SERVICE_GET_FLIGHT_DETAILS, CONF_SCHEDULE_SOURCE, CONF_AIRLABS_FREE_TIER, AIRLABS_FREE_TIER_UPDATE_INTERVAL_SECONDS from .coordinator import AvinorCoordinator from .api import AvinorApiClient, AirlabsApiClient @@ -131,12 +131,18 @@ async def async_setup_entry(hass: HomeAssistant, entry: AvinorConfigEntry) -> bo # Merge options over data so updated options take effect on reloads conf = {**entry.data, **entry.options} + # Use hourly update interval for Airlabs free-tier (1 000 requests/month) + if conf.get(CONF_SCHEDULE_SOURCE) == "airlabs" and conf.get(CONF_AIRLABS_FREE_TIER): + interval = timedelta(seconds=AIRLABS_FREE_TIER_UPDATE_INTERVAL_SECONDS) + else: + interval = timedelta(seconds=UPDATE_INTERVAL_SECONDS) + coordinator = AvinorCoordinator( hass, api, airlabs_api, conf, - update_interval=timedelta(seconds=UPDATE_INTERVAL_SECONDS), + update_interval=interval, ) await coordinator.async_config_entry_first_refresh() diff --git a/custom_components/avinor_flight_data/config_flow.py b/custom_components/avinor_flight_data/config_flow.py index 0b10084..af0f7fc 100644 --- a/custom_components/avinor_flight_data/config_flow.py +++ b/custom_components/avinor_flight_data/config_flow.py @@ -20,6 +20,7 @@ CONF_FLIGHT_TYPE, CONF_AIRLABS_API_KEY, CONF_SCHEDULE_SOURCE, + CONF_AIRLABS_FREE_TIER, DEFAULT_TIME_FROM, DEFAULT_TIME_TO, DEFAULT_FLIGHT_TYPE, @@ -107,6 +108,7 @@ async def async_step_user(self, user_input: Dict[str, Any] | None = None) -> Flo "airlabs": "Airlabs schedules", }), vol.Optional(CONF_AIRLABS_API_KEY): vol.All(str, vol.Length(min=1)), + vol.Optional(CONF_AIRLABS_FREE_TIER, default=False): bool, } ) @@ -145,6 +147,7 @@ async def async_step_init(self, user_input: Dict[str, Any] | None = None) -> Flo flight_type_default = current.get(CONF_FLIGHT_TYPE, DEFAULT_FLIGHT_TYPE) schedule_source_default = current.get(CONF_SCHEDULE_SOURCE, DEFAULT_SCHEDULE_SOURCE) airlabs_key_default = current.get(CONF_AIRLABS_API_KEY) + airlabs_free_tier_default = current.get(CONF_AIRLABS_FREE_TIER, False) # Build airport field - use simple vol.In for reliability if airports: @@ -177,6 +180,7 @@ async def async_step_init(self, user_input: Dict[str, Any] | None = None) -> Flo "airlabs": "Airlabs schedules", }), vol.Optional(CONF_AIRLABS_API_KEY, default=airlabs_key_default): vol.Any(None, vol.All(str, vol.Length(min=1))), + vol.Optional(CONF_AIRLABS_FREE_TIER, default=airlabs_free_tier_default): bool, } ) return self.async_show_form(step_id="init", data_schema=data_schema) diff --git a/custom_components/avinor_flight_data/const.py b/custom_components/avinor_flight_data/const.py index 9696d0e..fe60aec 100644 --- a/custom_components/avinor_flight_data/const.py +++ b/custom_components/avinor_flight_data/const.py @@ -8,6 +8,7 @@ # Optional Airlabs integration (flight details) CONF_AIRLABS_API_KEY = "airlabs_api_key" CONF_SCHEDULE_SOURCE = "schedule_source" +CONF_AIRLABS_FREE_TIER = "airlabs_free_tier" # Client-side filtering options CONF_FLIGHT_TYPE = "flight_type" # Avinor dom_int field @@ -34,3 +35,5 @@ # Update every 3 minutes as suggested by Avinor docs UPDATE_INTERVAL_SECONDS = 180 +# Hourly update interval for Airlabs free-tier API (1 000 requests/month) +AIRLABS_FREE_TIER_UPDATE_INTERVAL_SECONDS = 3600 diff --git a/custom_components/avinor_flight_data/translations/en.json b/custom_components/avinor_flight_data/translations/en.json index 1bc8c6c..1acbea1 100644 --- a/custom_components/avinor_flight_data/translations/en.json +++ b/custom_components/avinor_flight_data/translations/en.json @@ -11,7 +11,8 @@ "time_to": "Hours Forward", "flight_type": "Flight type", "schedule_source": "Schedule source", - "airlabs_api_key": "Airlabs API key" + "airlabs_api_key": "Airlabs API key", + "airlabs_free_tier": "Airlabs free tier (hourly updates)" }, "data_description": { "airport": "Select the airport to monitor (IATA code). Search by typing the airport name or code.", @@ -20,7 +21,8 @@ "time_to": "Include flights up to this many hours ahead (0-72 hours).", "flight_type": "Filter by flight type. All = no filtering.", "schedule_source": "Choose Avinor for the default feed, or Airlabs schedules when you need broader airport coverage. Airlabs requires an API key. The same key is also used when you want to click a flight for details.", - "airlabs_api_key": "Required for Airlabs schedules and for opening flight details when clicking a flight in supported cards." + "airlabs_api_key": "Required for Airlabs schedules and for opening flight details when clicking a flight in supported cards.", + "airlabs_free_tier": "Enable to limit Airlabs schedule updates to once per hour, preserving the free-tier quota of 1\u202f000 requests/month. Flight detail lookups are always on-demand and unaffected." } } } @@ -36,7 +38,8 @@ "time_to": "Hours Forward", "flight_type": "Flight type", "schedule_source": "Schedule source", - "airlabs_api_key": "Airlabs API key" + "airlabs_api_key": "Airlabs API key", + "airlabs_free_tier": "Airlabs free tier (hourly updates)" }, "data_description": { "airport": "Change the airport to monitor a different location.", @@ -45,7 +48,8 @@ "time_to": "Adjust the time window for future flights (0-72 hours).", "flight_type": "Filter by flight type. All = no filtering.", "schedule_source": "Choose Avinor for the default feed, or Airlabs schedules when you need broader airport coverage. Airlabs requires an API key. The same key is also used when you want to click a flight for details.", - "airlabs_api_key": "Required for Airlabs schedules and for opening flight details when clicking a flight in supported cards." + "airlabs_api_key": "Required for Airlabs schedules and for opening flight details when clicking a flight in supported cards.", + "airlabs_free_tier": "Enable to limit Airlabs schedule updates to once per hour, preserving the free-tier quota of 1\u202f000 requests/month. Flight detail lookups are always on-demand and unaffected." } } } diff --git a/custom_components/avinor_flight_data/translations/nb.json b/custom_components/avinor_flight_data/translations/nb.json index 73d28ac..c9050ef 100644 --- a/custom_components/avinor_flight_data/translations/nb.json +++ b/custom_components/avinor_flight_data/translations/nb.json @@ -11,7 +11,8 @@ "time_to": "Timer frem", "flight_type": "Flytype", "schedule_source": "Datakilde", - "airlabs_api_key": "Airlabs API-nøkkel" + "airlabs_api_key": "Airlabs API-nøkkel", + "airlabs_free_tier": "Airlabs gratisplan (oppdatering per time)" }, "data_description": { "airport": "Velg flyplassen du vil overvåke (IATA-kode). Søk ved å skrive flyplassnavn eller kode.", @@ -20,7 +21,8 @@ "time_to": "Inkluder fly opptil dette antall timer frem (0-72 timer).", "flight_type": "Filtrer på flytype. Alle = ingen filtrering.", "schedule_source": "Velg Avinor for standardstrømmen, eller Airlabs schedules når du trenger bredere flyplassdekning. Airlabs krever API-nøkkel. Den samme nøkkelen brukes også hvis du vil kunne klikke på et fly for detaljer.", - "airlabs_api_key": "Påkrevd for Airlabs schedules og for å åpne flydetaljer når du klikker på et fly i kort som støtter dette." + "airlabs_api_key": "Påkrevd for Airlabs schedules og for å åpne flydetaljer når du klikker på et fly i kort som støtter dette.", + "airlabs_free_tier": "Aktiver for å begrense Airlabs-oppdateringer til én gang per time og bevare gratisplanens kvote på 1\u202f000 forespørsler/måned. Oppslag av flydetaljer skjer alltid på forespørsel og påvirkes ikke." } } } @@ -36,7 +38,8 @@ "time_to": "Timer frem", "flight_type": "Flytype", "schedule_source": "Datakilde", - "airlabs_api_key": "Airlabs API-nøkkel" + "airlabs_api_key": "Airlabs API-nøkkel", + "airlabs_free_tier": "Airlabs gratisplan (oppdatering per time)" }, "data_description": { "airport": "Bytt flyplass for å overvåke en annen lokasjon.", @@ -45,7 +48,8 @@ "time_to": "Juster tidsvinduet for fremtidige fly (0-72 timer).", "flight_type": "Filtrer på flytype. Alle = ingen filtrering.", "schedule_source": "Velg Avinor for standardstrømmen, eller Airlabs schedules når du trenger bredere flyplassdekning. Airlabs krever API-nøkkel. Den samme nøkkelen brukes også hvis du vil kunne klikke på et fly for detaljer.", - "airlabs_api_key": "Påkrevd for Airlabs schedules og for å åpne flydetaljer når du klikker på et fly i kort som støtter dette." + "airlabs_api_key": "Påkrevd for Airlabs schedules og for å åpne flydetaljer når du klikker på et fly i kort som støtter dette.", + "airlabs_free_tier": "Aktiver for å begrense Airlabs-oppdateringer til én gang per time og bevare gratisplanens kvote på 1\u202f000 forespørsler/måned. Oppslag av flydetaljer skjer alltid på forespørsel og påvirkes ikke." } } } From 2b3c4bb86f65cf481018f26b05e1ba4159c6306a Mon Sep 17 00:00:00 2001 From: Kenneth Selmer Lokshall Date: Thu, 2 Jul 2026 19:29:21 +0200 Subject: [PATCH 3/3] Bump version to 1.1.3 in manifest.json --- custom_components/avinor_flight_data/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/avinor_flight_data/manifest.json b/custom_components/avinor_flight_data/manifest.json index 1ec449a..4f190a7 100644 --- a/custom_components/avinor_flight_data/manifest.json +++ b/custom_components/avinor_flight_data/manifest.json @@ -8,5 +8,5 @@ "issue_tracker": "https://github.com/WickedGhost/avinor_flight_data/issues", "loggers": ["custom_components.avinor_flight_data"], "requirements": ["xmltodict==0.13.0"], - "version": "1.1.2" + "version": "1.1.3" }