Skip to content

Commit

Permalink
Allow picture for device tracker (#63)
Browse files Browse the repository at this point in the history
  • Loading branch information
thomasddn committed Jan 31, 2025
1 parent 9960f78 commit e03626a
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 30 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,8 @@ Once a car has been added, you can configure additional options for it.

| Option | Description | Availability |
| ----------------------------- | ------------------------------------------------------------------------- | ----------------------------------------------------- |
| Volvo API key | The generated API key in the developer account. |
| Volvo API key | The generated API key in the developer account. | |
| Device tracker picture | The picture that will be shown on the map. | |
| Fuel consumption unit | You can choose between `l/100km`, `mpg (UK)` and `mpg (US)`. | Cars with a combustion engine. |
| Images transparent background | Whether or not you want transparent a background for the exterior images. | Depending on the image URL provided by the Volvo API. |
| Images background color | Choose the background color for the exterior images. | Depending on the image URL provided by the Volvo API. |
Expand Down
72 changes: 46 additions & 26 deletions custom_components/volvo_cars/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,19 @@

from homeassistant import config_entries
from homeassistant.config_entries import SOURCE_REAUTH, ConfigEntry, ConfigFlowResult
from homeassistant.const import CONF_FRIENDLY_NAME, CONF_PASSWORD, CONF_USERNAME
from homeassistant.const import (
CONF_FRIENDLY_NAME,
CONF_PASSWORD,
CONF_USERNAME,
Platform,
)
from homeassistant.core import callback
from homeassistant.data_entry_flow import section
from homeassistant.exceptions import ConfigEntryError
from homeassistant.helpers.selector import (
ColorRGBSelector,
EntitySelector,
EntitySelectorConfig,
SelectSelector,
SelectSelectorConfig,
)
Expand All @@ -27,6 +34,7 @@
CONF_VIN,
DOMAIN,
MANUFACTURER,
OPT_DEVICE_TRACKER_PICTURE,
OPT_FUEL_CONSUMPTION_UNIT,
OPT_IMG_BG_COLOR,
OPT_IMG_TRANSPARENT,
Expand All @@ -47,7 +55,16 @@ def get_setting(entry: VolvoCarsConfigEntry, key: str) -> Any:
if key in entry.options:
return entry.options[key]

return entry.data[key]
return entry.data.get(key, None)


def _create_section(name: str, schema: dict[vol.Marker, Any]) -> dict[vol.Marker, Any]:
return {
vol.Required(name): section(
vol.Schema(schema),
{"collapsed": True},
)
}


class VolvoCarsFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
Expand Down Expand Up @@ -248,7 +265,7 @@ async def async_step_init(
) -> ConfigFlowResult:
"""Manage the options."""
if user_input is not None:
# Remove sections
# Remove sections from input
flat_input = {}
for key, value in user_input.items():
if isinstance(value, dict):
Expand All @@ -263,20 +280,33 @@ async def async_step_init(

coordinator = self.config_entry.runtime_data.coordinator

schema: dict[vol.Marker, Any] = self._create_section(
"api",
{
vol.Required(
CONF_VCC_API_KEY,
default=get_setting(self.config_entry, CONF_VCC_API_KEY),
): str
},
)
schema: dict[vol.Marker, Any] = {
**_create_section(
"api",
{
vol.Required(
CONF_VCC_API_KEY,
default=get_setting(self.config_entry, CONF_VCC_API_KEY),
): str
},
),
**_create_section(
"device_tracker",
{
vol.Optional(
OPT_DEVICE_TRACKER_PICTURE,
default=get_setting(
self.config_entry, OPT_DEVICE_TRACKER_PICTURE
),
): EntitySelector(EntitySelectorConfig(domain=Platform.IMAGE))
},
),
}

# Check engine
# Units
if coordinator.vehicle.has_combustion_engine():
schema.update(
self._create_section(
_create_section(
"units",
{
vol.Required(OPT_FUEL_CONSUMPTION_UNIT): SelectSelector(
Expand All @@ -294,13 +324,13 @@ async def async_step_init(
)
)

# Check image URL
# Images
url = coordinator.vehicle.images.exterior_image_url
url_parts = parse.urlparse(url)

if url_parts.netloc.startswith("cas"):
schema.update(
self._create_section(
_create_section(
"images",
{
vol.Optional(OPT_IMG_TRANSPARENT, default=True): bool,
Expand All @@ -321,13 +351,3 @@ async def async_step_init(
vol.Schema(schema), self.config_entry.options
),
)

def _create_section(
self, name: str, schema: dict[vol.Marker, Any]
) -> dict[vol.Marker, Any]:
return {
vol.Required(name): section(
vol.Schema(schema),
{"collapsed": True},
)
}
1 change: 1 addition & 0 deletions custom_components/volvo_cars/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@

MANUFACTURER = "Volvo"

OPT_DEVICE_TRACKER_PICTURE = "device_tracker_picture"
OPT_FUEL_CONSUMPTION_UNIT = "fuel_consumption_unit"
OPT_IMG_BG_COLOR = "image_bg_color"
OPT_IMG_TRANSPARENT = "image_transparent"
Expand Down
32 changes: 29 additions & 3 deletions custom_components/volvo_cars/device_tracker.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@
TrackerEntity,
TrackerEntityDescription,
)
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from homeassistant.const import ATTR_ENTITY_PICTURE, Platform
from homeassistant.core import Event, EventStateChangedData, HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.event import async_track_state_change_event

from .const import ATTR_API_TIMESTAMP, ATTR_DIRECTION
from .config_flow import get_setting
from .const import ATTR_API_TIMESTAMP, ATTR_DIRECTION, OPT_DEVICE_TRACKER_PICTURE
from .coordinator import VolvoCarsConfigEntry, VolvoCarsDataCoordinator
from .entity import VolvoCarsEntity
from .entity_description import VolvoCarsDescription
Expand Down Expand Up @@ -64,6 +66,24 @@ def __init__(
"""Initialize."""
super().__init__(coordinator, description, Platform.DEVICE_TRACKER)

picture_entity_id: str | None = get_setting(
self.coordinator.config_entry, OPT_DEVICE_TRACKER_PICTURE
)

if picture_entity_id:
self.coordinator.config_entry.async_on_unload(
async_track_state_change_event(
self.coordinator.hass, picture_entity_id, self._set_picture
)
)
elif (
self.coordinator.vehicle.images
and self.coordinator.vehicle.images.exterior_image_url
):
self._attr_extra_state_attributes[ATTR_ENTITY_PICTURE] = (
self.coordinator.vehicle.images.exterior_image_url
)

def _update_state(self, api_field: VolvoCarsApiBaseModel | None) -> None:
if not isinstance(api_field, VolvoCarsLocation):
return
Expand All @@ -79,3 +99,9 @@ def _update_state(self, api_field: VolvoCarsApiBaseModel | None) -> None:
self._attr_extra_state_attributes[ATTR_API_TIMESTAMP] = (
api_field.properties.timestamp
)

def _set_picture(self, event: Event[EventStateChangedData]) -> None:
url = event.data["new_state"].attributes.get(ATTR_ENTITY_PICTURE, None)

Check failure on line 104 in custom_components/volvo_cars/device_tracker.py

View workflow job for this annotation

GitHub Actions / Check mypy

Item "None" of "State | None" has no attribute "attributes" [union-attr]
if url:
self._attr_extra_state_attributes[ATTR_ENTITY_PICTURE] = url
self.schedule_update_ha_state()
6 changes: 6 additions & 0 deletions custom_components/volvo_cars/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -1178,6 +1178,12 @@
},
"name": "API"
},
"device_tracker": {
"data": {
"device_tracker_picture": "Picture"
},
"name": "Device tracker"
},
"images": {
"data": {
"image_bg_color": "Images background color",
Expand Down

0 comments on commit e03626a

Please sign in to comment.