"""Support for BMW notifications.""" from __future__ import annotations import logging from typing import Any, cast from bimmer_connected.models import MyBMWAPIError, PointOfInterest from bimmer_connected.vehicle import MyBMWVehicle import voluptuous as vol from homeassistant.components.notify import ( ATTR_DATA, ATTR_TARGET, BaseNotificationService, ) from homeassistant.const import ATTR_LATITUDE, ATTR_LONGITUDE, CONF_ENTITY_ID from homeassistant.core import HomeAssistant from homeassistant.exceptions import HomeAssistantError, ServiceValidationError from homeassistant.helpers import config_validation as cv from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from . import DOMAIN, BMWConfigEntry ATTR_LOCATION_ATTRIBUTES = ["street", "city", "postal_code", "country"] POI_SCHEMA = vol.Schema( { vol.Required(ATTR_LATITUDE): cv.latitude, vol.Required(ATTR_LONGITUDE): cv.longitude, vol.Optional("street"): cv.string, vol.Optional("city"): cv.string, vol.Optional("postal_code"): cv.string, vol.Optional("country"): cv.string, } ) _LOGGER = logging.getLogger(__name__) def get_service( hass: HomeAssistant, config: ConfigType, discovery_info: DiscoveryInfoType | None = None, ) -> BMWNotificationService: """Get the BMW notification service.""" config_entry: BMWConfigEntry | None = hass.config_entries.async_get_entry( (discovery_info or {})[CONF_ENTITY_ID] ) targets = {} if ( config_entry and (coordinator := config_entry.runtime_data.coordinator) and not coordinator.read_only ): targets.update({v.name: v for v in coordinator.account.vehicles}) return BMWNotificationService(targets) class BMWNotificationService(BaseNotificationService): """Send Notifications to BMW.""" vehicle_targets: dict[str, MyBMWVehicle] def __init__(self, targets: dict[str, MyBMWVehicle]) -> None: """Set up the notification service.""" self.vehicle_targets = targets @property def targets(self) -> dict[str, Any] | None: """Return a dictionary of registered targets.""" return self.vehicle_targets async def async_send_message(self, message: str = "", **kwargs: Any) -> None: """Send a message or POI to the car.""" try: # Verify data schema poi_data = kwargs.get(ATTR_DATA) or {} POI_SCHEMA(poi_data) # Create the POI object poi = PointOfInterest( lat=poi_data.pop(ATTR_LATITUDE), lon=poi_data.pop(ATTR_LONGITUDE), name=(message or None), **poi_data, ) except (vol.Invalid, TypeError, ValueError) as ex: raise ServiceValidationError( translation_domain=DOMAIN, translation_key="invalid_poi", translation_placeholders={ "poi_exception": str(ex), }, ) from ex for vehicle in kwargs[ATTR_TARGET]: vehicle = cast(MyBMWVehicle, vehicle) _LOGGER.debug("Sending message to %s", vehicle.name) try: await vehicle.remote_services.trigger_send_poi(poi) except MyBMWAPIError as ex: raise HomeAssistantError(ex) from ex