diff --git a/homeassistant/components/nissan_leaf/__init__.py b/homeassistant/components/nissan_leaf/__init__.py index 9c9f2f484ef..75757ae4a25 100644 --- a/homeassistant/components/nissan_leaf/__init__.py +++ b/homeassistant/components/nissan_leaf/__init__.py @@ -87,7 +87,7 @@ CONFIG_SCHEMA = vol.Schema( extra=vol.ALLOW_EXTRA, ) -PLATFORMS = [Platform.SENSOR, Platform.SWITCH, Platform.BINARY_SENSOR] +PLATFORMS = [Platform.SENSOR, Platform.SWITCH, Platform.BINARY_SENSOR, Platform.BUTTON] SIGNAL_UPDATE_LEAF = "nissan_leaf_update" @@ -104,8 +104,6 @@ def setup(hass: HomeAssistant, config: ConfigType) -> bool: async def async_handle_update(service: ServiceCall) -> None: """Handle service to update leaf data from Nissan servers.""" - # It would be better if this was changed to use nickname, or - # an entity name rather than a vin. vin = service.data[ATTR_VIN] if vin in hass.data[DATA_LEAF]: @@ -116,8 +114,11 @@ def setup(hass: HomeAssistant, config: ConfigType) -> bool: async def async_handle_start_charge(service: ServiceCall) -> None: """Handle service to start charging.""" - # It would be better if this was changed to use nickname, or - # an entity name rather than a vin. + _LOGGER.warning( + "The 'nissan_leaf.start_charge' service is deprecated and has been" + "replaced by a dedicated button entity: Please use that to start" + "the charge instead" + ) vin = service.data[ATTR_VIN] if vin in hass.data[DATA_LEAF]: @@ -476,6 +477,25 @@ class LeafDataStore: _LOGGER.debug("Climate result not returned by Nissan servers") return False + async def async_start_charging(self) -> None: + """Request to start charging the car. Used by the button platform.""" + await self.hass.async_add_executor_job(self.leaf.start_charging) + self.schedule_update() + + def schedule_update(self) -> None: + """Set the next update to be triggered in a minute.""" + + # Remove any future updates that may be scheduled + if self._remove_listener: + self._remove_listener() + # Schedule update for one minute in the future - so that previously sent + # requests can be processed by Nissan servers or the car. + update_at = utcnow() + timedelta(minutes=1) + self.next_update = update_at + self._remove_listener = async_track_point_in_utc_time( + self.hass, self.async_update_data, update_at + ) + class LeafEntity(Entity): """Base class for Nissan Leaf entity.""" diff --git a/homeassistant/components/nissan_leaf/button.py b/homeassistant/components/nissan_leaf/button.py new file mode 100644 index 00000000000..9366ea3a6ac --- /dev/null +++ b/homeassistant/components/nissan_leaf/button.py @@ -0,0 +1,51 @@ +"""Button to start charging the Nissan Leaf.""" +from __future__ import annotations + +import logging + +from homeassistant.components.button import ButtonEntity +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType + +from . import DATA_CHARGING, DATA_LEAF, LeafEntity + +_LOGGER = logging.getLogger(__name__) + + +def setup_platform( + hass: HomeAssistant, + config: ConfigType, + add_entities: AddEntitiesCallback, + discovery_info: DiscoveryInfoType | None = None, +) -> None: + """Set up of a Nissan Leaf button.""" + if discovery_info is None: + return + + devices: list[LeafEntity] = [] + for vin, datastore in hass.data[DATA_LEAF].items(): + _LOGGER.debug("Adding button for vin=%s", vin) + devices.append(LeafChargingButton(datastore)) + + add_entities(devices, True) + + +class LeafChargingButton(LeafEntity, ButtonEntity): + """Charging Button class.""" + + _attr_icon = "mdi:power" + + @property + def name(self) -> str: + """Sensor name.""" + return f"Start {self.car.leaf.nickname} Charging" + + @property + def available(self) -> bool: + """Button availability.""" + return self.car.data[DATA_CHARGING] is not None + + async def async_press(self) -> None: + """Start charging.""" + await self.car.async_start_charging()