Skip to content

Commit

Permalink
Add entity pictures, e.g. for use in map card
Browse files Browse the repository at this point in the history
  • Loading branch information
vingerha committed Mar 17, 2024
1 parent a696662 commit 22be8cc
Show file tree
Hide file tree
Showing 14 changed files with 131 additions and 34 deletions.
11 changes: 11 additions & 0 deletions custom_components/adsb_lol/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,19 @@
from homeassistant.const import CONF_HOST
import voluptuous as vol

from .const import DOMAIN, ICONS_URL, ICONS_PATH

_LOGGER = logging.getLogger(__name__)

async def async_setup(hass, config):

hass.http.register_static_path(
ICONS_URL,
hass.config.path(ICONS_PATH),
True
)

return True

async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up ADSB from a config entry."""
Expand Down
22 changes: 21 additions & 1 deletion custom_components/adsb_lol/adsb_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

import requests
import logging
import os

from .const import CONF_EXTRACT_TYPE, CONF_ENTITY_PICTURE, CONF_ENTITY_PICTURE_ASC, CONF_ENTITY_PICTURE_DESC

_LOGGER = logging.getLogger(__name__)

Expand Down Expand Up @@ -32,11 +34,29 @@ def get_point_of_interest(self):
aircraft["mach"] = ac.get("mach", None)
aircraft["altitude_baro_ft"] = ac.get("alt_baro", None)
aircraft["altitude_geom_ft"] = ac.get("alt_geom", None)
aircraft["altitude_baro_rate"] = ac.get("baro_rate", 0)
aircraft["altitude_geom_rate"] = ac.get("geom_rate", 0)
aircraft["latitude"] = ac.get("lat", None)
aircraft["longitude"] = ac.get("lon", None)
aircraft[CONF_EXTRACT_TYPE] = self._CONF_EXTRACT_TYPE
aircraft[CONF_ENTITY_PICTURE] = self._CONF_ENTITY_PICTURE
aircraft[CONF_ENTITY_PICTURE_ASC] = self._CONF_ENTITY_PICTURE_ASC
aircraft[CONF_ENTITY_PICTURE_DESC] = self._CONF_ENTITY_PICTURE_DESC
aircraft_h[str(self._reg)] = aircraft.copy()


_response_h = aircraft_h

_LOGGER.debug ("Get flight poi: %s", response.json())
return _response_h
return _response_h


def get_entity_pictures(hass, path) -> dict[str]:
_LOGGER.debug(f"Getting icons for path: {path}")
icon_dir = hass.config.path(path)
files = os.listdir(icon_dir)
entity_pictures = []
for file in files:
entity_pictures.append(file)
_LOGGER.debug(f"Icons in folder: {entity_pictures}")
return entity_pictures
43 changes: 34 additions & 9 deletions custom_components/adsb_lol/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,20 @@
ATTR_DEFAULT_RADIUS,
CONF_DEVICE_TRACKER_ID,
CONF_ALTITUDE_LIMIT,
ATTR_DEFAULT_ALTITUDE_LIMIT
ATTR_DEFAULT_ALTITUDE_LIMIT,
CONF_ENTITY_PICTURE,
ATTR_DEFAULT_ENTITY_PICTURE,
CONF_ENTITY_PICTURE_ASC,
ATTR_DEFAULT_ENTITY_PICTURE_ASC,
CONF_ENTITY_PICTURE_DESC,
ATTR_DEFAULT_ENTITY_PICTURE_DESC,
ICONS_URL,
ICONS_PATH
)

from .adsb_helper import (
get_flight,
get_entity_pictures
)

_LOGGER = logging.getLogger(__name__)
Expand Down Expand Up @@ -74,6 +83,7 @@ async def async_step_flight_details(self, user_input: dict | None = None) -> Flo
},
),
)
user_input[CONF_EXTRACT_PARAM_INPUT] = None
self._user_inputs.update(user_input)
_LOGGER.debug(f"UserInputs Start End: {self._user_inputs}")
return self.async_create_entry(
Expand Down Expand Up @@ -150,14 +160,29 @@ async def async_step_init(
) -> FlowResult:
"""Manage the options."""
if user_input is None:
return self.async_show_form(
step_id="init",
data_schema=vol.Schema(
{
vol.Optional(CONF_REFRESH_INTERVAL, default=self.config_entry.options.get(CONF_REFRESH_INTERVAL, ATTR_DEFAULT_REFRESH_INTERVAL)): vol.All(vol.Coerce(int), vol.Range(min=1)),
},
),
)
entity_pictures = get_entity_pictures(self.hass, ICONS_PATH)
if self.config_entry.data.get(CONF_EXTRACT_TYPE, None) != "point":
return self.async_show_form(
step_id="init",
data_schema=vol.Schema(
{
vol.Optional(CONF_REFRESH_INTERVAL, default=self.config_entry.options.get(CONF_REFRESH_INTERVAL, ATTR_DEFAULT_REFRESH_INTERVAL)): vol.All(vol.Coerce(int), vol.Range(min=1)),
vol.Optional(CONF_ENTITY_PICTURE, default=self.config_entry.options.get(CONF_ENTITY_PICTURE, ATTR_DEFAULT_ENTITY_PICTURE)): vol.In(entity_pictures),
},
),
)
else:
return self.async_show_form(
step_id="init",
data_schema=vol.Schema(
{
vol.Optional(CONF_REFRESH_INTERVAL, default=self.config_entry.options.get(CONF_REFRESH_INTERVAL, ATTR_DEFAULT_REFRESH_INTERVAL)): vol.All(vol.Coerce(int), vol.Range(min=1)),
vol.Optional(CONF_ENTITY_PICTURE_ASC, default=self.config_entry.options.get(CONF_ENTITY_PICTURE_ASC, ATTR_DEFAULT_ENTITY_PICTURE_ASC)): vol.In(entity_pictures),
vol.Optional(CONF_ENTITY_PICTURE_DESC, default=self.config_entry.options.get(CONF_ENTITY_PICTURE_DESC, ATTR_DEFAULT_ENTITY_PICTURE_DESC)): vol.In(entity_pictures),
},
),
)

self._user_inputs.update(user_input)
_LOGGER.debug(f"UserInputs Options Init: {self._user_inputs}")
return self.async_create_entry(title="", data=self._user_inputs)
10 changes: 9 additions & 1 deletion custom_components/adsb_lol/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
DOMAIN = "adsb_lol"
PLATFORMS = [Platform.SENSOR]

ICONS_PATH = f'custom_components/{DOMAIN}/icons'
ICONS_URL = f'/{DOMAIN}/icons'

# default values for options
CONF_REFRESH_INTERVAL = "refresh_interval"
ATTR_DEFAULT_REFRESH_INTERVAL = 5
Expand All @@ -26,7 +29,12 @@
CONF_EXTRACT_PARAM = "extract_param"
CONF_EXTRACT_PARAM_INPUT = "input_entity"


CONF_ENTITY_PICTURE = "entity_picture"
CONF_ENTITY_PICTURE_ASC = "entity_picture_asc"
CONF_ENTITY_PICTURE_DESC = "entity_picture_desc"
ATTR_DEFAULT_ENTITY_PICTURE = "airplane_1.png"
ATTR_DEFAULT_ENTITY_PICTURE_ASC = "airplane_asc.png"
ATTR_DEFAULT_ENTITY_PICTURE_DESC = "airplane_desc.png"

# constants used in helpers
ATTR_DELAY = "Delay"
Expand Down
42 changes: 29 additions & 13 deletions custom_components/adsb_lol/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,13 @@
ATTR_DEFAULT_REFRESH_INTERVAL,
ATTR_LATITUDE,
ATTR_LONGITUDE,
CONF_EXTRACT_TYPE
CONF_EXTRACT_TYPE,
CONF_ENTITY_PICTURE,
ATTR_DEFAULT_ENTITY_PICTURE,
CONF_ENTITY_PICTURE_ASC,
ATTR_DEFAULT_ENTITY_PICTURE_ASC,
CONF_ENTITY_PICTURE_DESC,
ATTR_DEFAULT_ENTITY_PICTURE_DESC,
)

_LOGGER = logging.getLogger(__name__)
Expand Down Expand Up @@ -62,16 +68,20 @@ async def _async_update_data(self) -> dict[str, str]:

if self._flight.get("ac", None):
self._data = {
"registration": self._flight["ac"][0]["r"],
"callsign": self._flight["ac"][0]["flight"],
"type": self._flight["ac"][0]["t"],
"icao24": self._flight["ac"][0]["hex"],
"altitude_baro": self._flight["ac"][0]["alt_baro"],
"altitude_geom": self._flight["ac"][0]["alt_geom"],
"ground_speed": self._flight["ac"][0]["gs"],
"mach": self._flight["ac"][0]["mach"],
"latitude": self._flight["ac"][0]["lat"],
"longitude": self._flight["ac"][0]["lon"],
"registration": self._flight["ac"][0].get("r",None),
"callsign": self._flight["ac"][0].get("flight",None),
"type": self._flight["ac"][0].get("t",None),
"icao24": self._flight["ac"][0].get("hex",None),
"altitude_baro": self._flight["ac"][0].get("alt_baro", None),
"altitude_geom": self._flight["ac"][0].get("alt_geom", None),
"ground_speed": self._flight["ac"][0].get("gs",None),
"mach": self._flight["ac"][0].get("mach",None),
"altitude_baro_rate": self._flight["ac"][0].get("baro_rate", None),
"altitude_geom_rate": self._flight["ac"][0].get("geom_rate", None),
"latitude": self._flight["ac"][0].get("lat",None),
"longitude": self._flight["ac"][0].get("lon",None),
CONF_EXTRACT_TYPE: data[CONF_EXTRACT_TYPE],
CONF_ENTITY_PICTURE: options.get(CONF_ENTITY_PICTURE, ATTR_DEFAULT_ENTITY_PICTURE)
}
else:
_LOGGER.warning("No flights found for: %s", extract_param)
Expand All @@ -80,6 +90,8 @@ async def _async_update_data(self) -> dict[str, str]:
"callsign": "Not found",
"type": "Not found",
"icao24": "Not found",
CONF_EXTRACT_TYPE: data[CONF_EXTRACT_TYPE],
CONF_ENTITY_PICTURE: options.get(CONF_ENTITY_PICTURE, ATTR_DEFAULT_ENTITY_PICTURE)
}

_LOGGER.debug("Coordinator data: %s", self._data)
Expand Down Expand Up @@ -117,10 +129,14 @@ async def _async_update_data(self) -> dict[str, str]:
_LOGGER.debug("Point search on lat: %s, lon: %s", latitude, longitude)
self._url = str(data["url"]) + "/" + str(data["extract_type"]) + "/" + str(latitude) + "/" + str(longitude) + "/" + str(int(data["radius"] * 1000 / 1852) )
_LOGGER.debug("Point search on URL: %s", self._url)

self._CONF_EXTRACT_TYPE = data[CONF_EXTRACT_TYPE]
self._CONF_ENTITY_PICTURE = options.get(CONF_ENTITY_PICTURE, ATTR_DEFAULT_ENTITY_PICTURE)
self._CONF_ENTITY_PICTURE_ASC = options.get(CONF_ENTITY_PICTURE_ASC, ATTR_DEFAULT_ENTITY_PICTURE_ASC)
self._CONF_ENTITY_PICTURE_DESC = options.get(CONF_ENTITY_PICTURE_DESC, ATTR_DEFAULT_ENTITY_PICTURE_DESC)
self._data = await self.hass.async_add_executor_job(
get_point_of_interest, self
)
)


_LOGGER.debug("Coordinator data: %s", self._data)

Expand Down
Binary file added custom_components/adsb_lol/icons/airplane_1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added custom_components/adsb_lol/icons/airplane_2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added custom_components/adsb_lol/icons/airplane_3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 14 additions & 1 deletion custom_components/adsb_lol/sensor.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Support for ADSB.lol"""
from datetime import datetime
import logging
from PIL import Image
from typing import Any

from homeassistant.helpers import entity_registry as er
Expand All @@ -17,6 +18,10 @@
from .const import (
ATTR_LATITUDE,
DOMAIN,
ICONS_URL,
CONF_ENTITY_PICTURE,
CONF_ENTITY_PICTURE_ASC,
CONF_ENTITY_PICTURE_DESC
)

from .coordinator import ADSBUpdateCoordinator, ADSBPointUpdateCoordinator
Expand Down Expand Up @@ -136,6 +141,9 @@ def _update_attrs(self): # noqa: C901 PLR0911
self._state = self.coordinator.data["callsign"]
self._attr_native_value = self._state
self._attr_extra_state_attributes = self.coordinator.data

self._attr_entity_picture = f'{ICONS_URL}/{self.coordinator.data[CONF_ENTITY_PICTURE]}'

return self._attr_extra_state_attributes

class ADSBPointSensor(CoordinatorEntity, SensorEntity):
Expand Down Expand Up @@ -181,6 +189,7 @@ def _update_attrs(self): # noqa: C901 PLR0911
self._attributes["aircraft"] = self.coordinator.data
self._attr_extra_state_attributes = self._attributes


return self._attr_extra_state_attributes

class ADSBPointACSensor(CoordinatorEntity, SensorEntity):
Expand Down Expand Up @@ -232,7 +241,11 @@ def _update_attrs(self): # noqa: C901 PLR0911
self._attr_native_value = self._aircraft["callsign"]
self._attributes = self._aircraft
self._attr_extra_state_attributes = self._attributes

if self._aircraft.get("altitude_baro_rate", 0) < 0 or self._aircraft.get("altitude_geom_rate",0) < 0 :
self._attr_entity_picture = f'{ICONS_URL}/{self._aircraft[CONF_ENTITY_PICTURE_DESC]}'
else:
self._attr_entity_picture = f'{ICONS_URL}/{self._aircraft[CONF_ENTITY_PICTURE_ASC]}'

return self._attr_extra_state_attributes

async def remove_entity(self):
Expand Down
22 changes: 13 additions & 9 deletions custom_components/adsb_lol/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,30 @@
"step": {
"user": {
"menu_options": {
"flight_details": "Search for flight details",
"flight_details_input": "Search for flight details using an input select/text",
"flight_details": "Search for aircraft flight details",
"flight_details_input": "Search for aircraft flight details using an input select/text",
"point_of_interest": "Search aircraft around a point of interest"
},
"description": "Select your choice "
},
"flight_details": {
"data": {
"extract_type": "Find flight using:",
"extract_type": "Identify aircraft by using:",
"url": "Select url",
"name": "Name",
"extract_param": "flight parameter (registration or callsign)"
"extract_param": "Aircraft identifying parameter (icao, registration or callsign)"
},
"description": "Select"
"description": "Select parameters to identify your aircraft"
},
"flight_details_input": {
"data": {
"extract_type": "Find flight using:",
"extract_type": "Identify aicraft by using",
"url": "Select url",
"name": "Name",
"extract_param": "Entity providing parameter (registration or callsign)"
"input_entity": "Input/Select entity"

},
"description": "Select"
"description": "Identify your aircraft via input/select entity"
},
"point_of_interest": {
"data": {
Expand All @@ -44,7 +45,10 @@
"init": {
"description": "Customize the way the integration works",
"data": {
"refresh_interval": "Data refresh interval (in minutes)"
"refresh_interval": "Data refresh interval (in minutes)",
"entity_picture": "Select aircraft picture e.g in map card",
"entity_picture_asc": "Select picture for ascending aircraft e.g in map card",
"entity_picture_desc": "Select picture for descending aircraft e.g in map card"
}
}
}
Expand Down

0 comments on commit 22be8cc

Please sign in to comment.