Skip to content

Commit

Permalink
Added tracking scheduled flights
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexandrErohin committed Jun 27, 2024
1 parent 29fdda5 commit fd5691c
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 35 deletions.
23 changes: 20 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ Flightradar24 integration allows one to track overhead flights in a given region

It allows you:
1. Know how many flights in your area right now, or just have entered or exited it. And get list of flights with [full information](#flight) by every relevant flight for the sensor
2. Track a particular plane or planes no matter where it currently is
2. Track a particular plane or planes no matter where it currently is, even if it is a scheduled flight
3. Get [top 10 most tracked flights on FlightRadar24](#most-tracked)
4. Create notifications (example - [Get a notification when a flight enters or exits your area](#notification))
4. Create notifications (example - [Get a notification when a flight enters or exits your area](#notification-enters), [Get a notification when a tracked scheduled flight takes off](#notification-scheduled))
5. Create automations (example - [Automatically track a flight by your needs](#automation))
6. Add flights table to your [Home Assistant dashboard](https://www.home-assistant.io/dashboards/) by [Lovelace Card](#lovelace))

Expand Down Expand Up @@ -90,7 +90,7 @@ To do that:
4. Edit the options you need and click `SUBMIT`

## Uses
### <a id="notification">Notification</a>
### <a id="notification-enters">Notification - When a flight enters or exits your area</a>
To receive notifications of the entering flights add following lines to your `configuration.yaml` file:
```yaml
automation:
Expand Down Expand Up @@ -119,6 +119,22 @@ To change name in tracked_by_device
4. Click on `Rename` in the opened sub-menu
5. Enter new name and click `OK`

### <a id="notification-scheduled">Notification - When a tracked scheduled flight takes off</a>
To receive notification of taking off tracked scheduled flight add following lines to your `configuration.yaml` file:
```yaml
automation:
- alias: "Scheduled flight takes off"
trigger:
platform: event
event_type: flightradar24_tracked_took_off
action:
service: notify.mobile_app_<device_name>
data:
content: >-
Flight takes off {{ trigger.event.data.callsign }} to {{ trigger.event.data.airport_destination_city }}
[Open FlightRadar](https://www.flightradar24.com/{{ trigger.event.data.callsign }})
```

### <a id="automation">Automation</a>
To automatically add a flight to additional tracking add following lines to your `configuration.yaml` file:
```yaml
Expand Down Expand Up @@ -235,6 +251,7 @@ recorder:
| Field | Description |
| --- |---|
| tracked_by_device | If you have defined more than one device of FlightRadar24 for more places to observe - you may be interested to know what device has fired the event. To rename the device check [this](#tracked_by_device) |
| tracked_type | Only for tracked flights. It shows is flight live or scheduled |
| flight_number | Flight Number |
| latitude | Current latitude of the aircraft |
| longitude | Current longitude of the aircraft |
Expand Down
103 changes: 72 additions & 31 deletions custom_components/flightradar24/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,30 +68,51 @@ def __init__(
)

async def add_track(self, number: str) -> None:
current: dict[str, dict[str, Any]] = {}
await self._find_flight(current, number)
if not current:
self.logger.error('FlightRadar24: No flight found by - {}'.format(number))
return
self.tracked = self.tracked | current if self.tracked else current

async def _find_flight(self, current: dict[str, dict[str, Any]], number: str) -> None:
def process_search_flight(objects: dict, search: str) -> dict | None:
live = objects.get('live')
if live:
for element in live:
detail = element.get('detail')
if detail and search in (detail.get('reg'), detail.get('callsign'), detail.get('flight')):
return element
schedule = objects.get('schedule')
if schedule:
for element in schedule:
detail = element.get('detail')
if detail and search in (detail.get('callsign'), detail.get('flight')):
return element
return None

try:
flights = await self.hass.async_add_executor_job(self._client.search, number)
flights = flights.get('live')
found = None
if flights:
for element in flights:
detail = element.get('detail')
if detail and number in (detail.get('reg'), detail.get('callsign'), detail.get('flight')):
found = element
break
found = process_search_flight(flights, number)
if not found:
self.logger.error('FlightRadar24: No live flight found by - {}'.format(number))
return
current: dict[int, dict[str, Any]] = {}
data = [None] * 20
data[1] = self._get_value(found, ['detail', 'lat'])
data[2] = self._get_value(found, ['detail', 'lon'])
data[13] = []
flight = Flight(found.get('id'), data)
flight.registration = self._get_value(found, ['detail', 'reg'])
flight.callsign = self._get_value(found, ['detail', 'callsign'])

await self._update_flights_data(flight, current, self.tracked)
self.tracked = self.tracked | current if self.tracked else current
if found.get('type') == 'live':
data = [None] * 20
data[1] = self._get_value(found, ['detail', 'lat'])
data[2] = self._get_value(found, ['detail', 'lon'])
data[13] = []
flight = Flight(found.get('id'), data)
flight.registration = found['detail']['reg']
flight.callsign = found['detail']['callsign']

await self._update_flights_data(flight, current, self.tracked)
else:
current[found.get('id')] = {
'callsign': found['detail']['callsign'],
'flight_number': found['detail']['flight'],
'aircraft_registration': None,
}
current[found.get('id')]['tracked_type'] = found.get('type')
except Exception as e:
self.logger.error(e)

Expand All @@ -118,7 +139,7 @@ async def _update_flights_in_area(self) -> None:
flights = await self.hass.async_add_executor_job(
self._client.get_flights, None, self._bounds
)
current: dict[int, dict[str, Any]] = {}
current: dict[str, dict[str, Any]] = {}
for obj in flights:
if not self.min_altitude <= obj.altitude <= self.max_altitude:
continue
Expand All @@ -137,13 +158,23 @@ async def _update_flights_tracked(self) -> None:
if not self.tracked:
return

flights = await self.hass.async_add_executor_job(
self._client.get_flights, None, None,
','.join([self.tracked[x]['aircraft_registration'] for x in self.tracked])
)
current: dict[int, dict[str, Any]] = {}
for obj in flights:
await self._update_flights_data(obj, current, self.tracked, SensorType.TRACKED)
reg_numbers = []
current: dict[str, dict[str, Any]] = {}
for flight in self.tracked:
if self.tracked[flight].get('aircraft_registration'):
reg_numbers.append(self.tracked[flight].get('aircraft_registration'))

if reg_numbers:
flights = await self.hass.async_add_executor_job(self._client.get_flights, None, None,
','.join(reg_numbers))
for obj in flights:
await self._update_flights_data(obj, current, self.tracked, SensorType.TRACKED)
current[obj.id]['tracked_type'] = 'live'
remains = self.tracked.keys() - current.keys()
if remains:
for flight_id in remains:
await self._find_flight(current, self.tracked[flight_id]['flight_number'])

self.tracked = current

async def _update_most_tracked(self) -> None:
Expand Down Expand Up @@ -172,7 +203,7 @@ async def _update_most_tracked(self) -> None:

async def _update_flights_data(self,
obj: Flight,
current: dict[int, dict[str, Any]],
current: dict[str, dict[str, Any]],
tracked: dict[str, dict[str, Any]],
sensor_type: SensorType | None = None,
) -> None:
Expand Down Expand Up @@ -209,7 +240,17 @@ def _takeoff_and_landing(self,
flight: dict[str, Any],
altitude_old, altitude_new,
sensor_type: SensorType | None) -> None:
if sensor_type is None or altitude_old is None:
def to_int(element: any) -> None | int:
if element is None:
return None
try:
return int(element)
except ValueError:
return None

altitude_old = to_int(altitude_old)
altitude_new = to_int(altitude_new)
if sensor_type is None or altitude_old is None or altitude_new is None:
return
if altitude_old < 10 and altitude_new >= 10:
self._fire_event(EVENT_AREA_TOOK_OFF if SensorType.IN_AREA == sensor_type else EVENT_TRACKED_TOOK_OFF,
Expand All @@ -219,7 +260,7 @@ def _takeoff_and_landing(self,

@staticmethod
def _is_valid(flight: dict) -> bool:
return flight.get('flight_number') is not None
return flight.get('flight_number') is not None and flight.get('time_scheduled_departure') is not None

@staticmethod
def _get_value(dictionary: dict, keys: list) -> Any | None:
Expand Down
2 changes: 1 addition & 1 deletion custom_components/flightradar24/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@
"iot_class": "cloud_polling",
"issue_tracker": "https://github.com/AlexandrErohin/home-assistant-flightradar24/issues",
"requirements": ["FlightRadarAPI==1.3.25", "pycountry==23.12.11"],
"version": "1.14.2"
"version": "1.15.0"
}

0 comments on commit fd5691c

Please sign in to comment.