Nissanleaf (#21145)
* Remove unneeded returns from handle_update() * Start __init__() params with hass. * Remove excess logging and downgrade remaining logging. * Remove period from end of comment * Decorate callback with @callback * Use more descriptive variables than key and value. * Inherit from BinarySensorDevice and overwrite is_on rather than state. * Removed uncheckedreturn values. * Use super() rather than explicit object. * Use add_entities instead of add_devices. * Don't use listener when calling immediately. * Remove some excess logging. * Switch to sync since pycarwings2 is sync. * Remove RuntimeError exception matching. * Add temporary reviewer comments. * Add UI help descriptions for update service. * Fix hound errors. * Replaced time.sleep() with await asyncio.sleep() * Removed location_updateon_on attribute since on device_tracker. * Use async_added_to_hass() and async_dispatcher_connect(). * Use dict[key] because schema key is required. * Clarify variable names. * Remove icon for charging switch. * Convert LeafChargeSwitch into service and sensor. * Use async_dispatcher_send(). * Add guard checks for discovery_info. Consistent logs. * Use async_schedul_update_ha_state(). * Device tracker should return true. * Remove icon for climate control. * Really remove icon for climate control. * Use register() instead of async_register(). * Add guard on device tracker if discovery_info is None.pull/21329/head
parent
4102e24481
commit
caa3b123ae
|
@ -1,13 +1,13 @@
|
|||
"""Support for the Nissan Leaf Carwings/Nissan Connect API."""
|
||||
import asyncio
|
||||
from datetime import datetime, timedelta
|
||||
import asyncio
|
||||
import logging
|
||||
import sys
|
||||
import urllib
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
|
||||
from homeassistant.core import callback
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.discovery import load_platform
|
||||
from homeassistant.helpers.dispatcher import (
|
||||
|
@ -78,32 +78,60 @@ LEAF_COMPONENTS = [
|
|||
SIGNAL_UPDATE_LEAF = 'nissan_leaf_update'
|
||||
|
||||
SERVICE_UPDATE_LEAF = 'update'
|
||||
SERVICE_START_CHARGE_LEAF = 'start_charge'
|
||||
ATTR_VIN = 'vin'
|
||||
|
||||
UPDATE_LEAF_SCHEMA = vol.Schema({
|
||||
vol.Required(ATTR_VIN): cv.string,
|
||||
})
|
||||
START_CHARGE_LEAF_SCHEMA = vol.Schema({
|
||||
vol.Required(ATTR_VIN): cv.string,
|
||||
})
|
||||
|
||||
|
||||
async def async_setup(hass, config):
|
||||
def setup(hass, config):
|
||||
"""Set up the Nissan Leaf component."""
|
||||
import pycarwings2
|
||||
|
||||
async def handle_update(service):
|
||||
async def async_handle_update(service):
|
||||
"""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.get(ATTR_VIN, '')
|
||||
vin = service.data[ATTR_VIN]
|
||||
|
||||
if vin in hass.data[DATA_LEAF]:
|
||||
data_store = hass.data[DATA_LEAF][vin]
|
||||
async_track_point_in_utc_time(
|
||||
hass, data_store.async_update_data, utcnow())
|
||||
return True
|
||||
await data_store.async_update_data(utcnow())
|
||||
else:
|
||||
_LOGGER.debug("Vin %s not recognised for update", vin)
|
||||
|
||||
_LOGGER.debug("Vin %s not recognised for update", vin)
|
||||
return False
|
||||
async def async_handle_start_charge(service):
|
||||
"""Handle service to start charging."""
|
||||
# It would be better if this was changed to use nickname, or
|
||||
# an entity name rather than a vin.
|
||||
vin = service.data[ATTR_VIN]
|
||||
|
||||
async def async_setup_leaf(car_config):
|
||||
if vin in hass.data[DATA_LEAF]:
|
||||
data_store = hass.data[DATA_LEAF][vin]
|
||||
|
||||
# Send the command to request charging is started to Nissan
|
||||
# servers. If that completes OK then trigger a fresh update to
|
||||
# pull the charging status from the car after waiting a minute
|
||||
# for the charging request to reach the car.
|
||||
result = await hass.async_add_executor_job(
|
||||
data_store.leaf.start_charging)
|
||||
if result:
|
||||
_LOGGER.debug("Start charging sent, "
|
||||
"request updated data in 1 minute")
|
||||
check_charge_at = utcnow() + timedelta(minutes=1)
|
||||
data_store.next_update = check_charge_at
|
||||
async_track_point_in_utc_time(
|
||||
hass, data_store.async_update_data, check_charge_at)
|
||||
|
||||
else:
|
||||
_LOGGER.debug("Vin %s not recognised for update", vin)
|
||||
|
||||
def setup_leaf(car_config):
|
||||
"""Set up a car."""
|
||||
_LOGGER.debug("Logging into You+Nissan...")
|
||||
|
||||
|
@ -112,20 +140,11 @@ async def async_setup(hass, config):
|
|||
region = car_config[CONF_REGION]
|
||||
leaf = None
|
||||
|
||||
async def leaf_login():
|
||||
nonlocal leaf
|
||||
sess = pycarwings2.Session(username, password, region)
|
||||
leaf = sess.get_leaf()
|
||||
|
||||
try:
|
||||
# This might need to be made async (somehow) causes
|
||||
# homeassistant to be slow to start
|
||||
await hass.async_add_job(leaf_login)
|
||||
except(RuntimeError, urllib.error.HTTPError):
|
||||
_LOGGER.error(
|
||||
"Unable to connect to Nissan Connect with "
|
||||
"username and password")
|
||||
return False
|
||||
sess = pycarwings2.Session(username, password, region)
|
||||
leaf = sess.get_leaf()
|
||||
except KeyError:
|
||||
_LOGGER.error(
|
||||
"Unable to fetch car details..."
|
||||
|
@ -143,7 +162,7 @@ async def async_setup(hass, config):
|
|||
" as the drive train battery won't connect."
|
||||
" Don't set the intervals too low.")
|
||||
|
||||
data_store = LeafDataStore(leaf, hass, car_config)
|
||||
data_store = LeafDataStore(hass, leaf, car_config)
|
||||
hass.data[DATA_LEAF][leaf.vin] = data_store
|
||||
|
||||
for component in LEAF_COMPONENTS:
|
||||
|
@ -154,12 +173,15 @@ async def async_setup(hass, config):
|
|||
utcnow() + INITIAL_UPDATE)
|
||||
|
||||
hass.data[DATA_LEAF] = {}
|
||||
tasks = [async_setup_leaf(car) for car in config[DOMAIN]]
|
||||
if tasks:
|
||||
await asyncio.wait(tasks, loop=hass.loop)
|
||||
for car in config[DOMAIN]:
|
||||
setup_leaf(car)
|
||||
|
||||
hass.services.async_register(DOMAIN, SERVICE_UPDATE_LEAF, handle_update,
|
||||
schema=UPDATE_LEAF_SCHEMA)
|
||||
hass.services.register(
|
||||
DOMAIN, SERVICE_UPDATE_LEAF,
|
||||
async_handle_update, schema=UPDATE_LEAF_SCHEMA)
|
||||
hass.services.register(
|
||||
DOMAIN, SERVICE_START_CHARGE_LEAF,
|
||||
async_handle_start_charge, schema=START_CHARGE_LEAF_SCHEMA)
|
||||
|
||||
return True
|
||||
|
||||
|
@ -167,13 +189,13 @@ async def async_setup(hass, config):
|
|||
class LeafDataStore:
|
||||
"""Nissan Leaf Data Store."""
|
||||
|
||||
def __init__(self, leaf, hass, car_config):
|
||||
def __init__(self, hass, leaf, car_config):
|
||||
"""Initialise the data store."""
|
||||
self.hass = hass
|
||||
self.leaf = leaf
|
||||
self.car_config = car_config
|
||||
self.nissan_connect = car_config[CONF_NCONNECT]
|
||||
self.force_miles = car_config[CONF_FORCE_MILES]
|
||||
self.hass = hass
|
||||
self.data = {}
|
||||
self.data[DATA_CLIMATE] = False
|
||||
self.data[DATA_BATTERY] = 0
|
||||
|
@ -223,25 +245,19 @@ class LeafDataStore:
|
|||
if (self.last_battery_response is not None and
|
||||
self.data[DATA_CHARGING] is False and
|
||||
self.data[DATA_BATTERY] <= RESTRICTED_BATTERY):
|
||||
_LOGGER.info("Low battery so restricting refresh frequency (%s)",
|
||||
self.leaf.nickname)
|
||||
_LOGGER.debug("Low battery so restricting refresh frequency (%s)",
|
||||
self.leaf.nickname)
|
||||
interval = RESTRICTED_INTERVAL
|
||||
else:
|
||||
intervals = [base_interval]
|
||||
_LOGGER.debug("Could use base interval=%s", base_interval)
|
||||
|
||||
if self.data[DATA_CHARGING]:
|
||||
intervals.append(charging_interval)
|
||||
_LOGGER.debug("Could use charging interval=%s",
|
||||
charging_interval)
|
||||
|
||||
if self.data[DATA_CLIMATE]:
|
||||
intervals.append(climate_interval)
|
||||
_LOGGER.debug(
|
||||
"Could use climate interval=%s", climate_interval)
|
||||
|
||||
interval = min(intervals)
|
||||
_LOGGER.debug("Resulting interval=%s", interval)
|
||||
|
||||
return utcnow() + interval
|
||||
|
||||
|
@ -310,12 +326,10 @@ class LeafDataStore:
|
|||
_LOGGER.debug("Empty Location Response Received")
|
||||
self.data[DATA_LOCATION] = None
|
||||
else:
|
||||
_LOGGER.debug("Got location data for Leaf")
|
||||
self.data[DATA_LOCATION] = location_response
|
||||
self.last_location_response = utcnow()
|
||||
|
||||
_LOGGER.debug("Location Response: %s",
|
||||
location_response.__dict__)
|
||||
self.data[DATA_LOCATION] = location_response
|
||||
self.last_location_response = utcnow()
|
||||
except CarwingsError:
|
||||
_LOGGER.error("Error fetching location info")
|
||||
|
||||
|
@ -336,9 +350,8 @@ class LeafDataStore:
|
|||
from pycarwings2 import CarwingsError
|
||||
try:
|
||||
# First, check nissan servers for the latest data
|
||||
start_server_info = await self.hass.async_add_job(
|
||||
self.leaf.get_latest_battery_status
|
||||
)
|
||||
start_server_info = await self.hass.async_add_executor_job(
|
||||
self.leaf.get_latest_battery_status)
|
||||
|
||||
# Store the date from the nissan servers
|
||||
start_date = self._extract_start_date(start_server_info)
|
||||
|
@ -346,33 +359,35 @@ class LeafDataStore:
|
|||
_LOGGER.info("No start date from servers. Aborting")
|
||||
return None
|
||||
|
||||
_LOGGER.info("Start server date=%s", start_date)
|
||||
_LOGGER.debug("Start server date=%s", start_date)
|
||||
|
||||
# Request battery update from the car
|
||||
_LOGGER.info("Requesting battery update, %s", self.leaf.vin)
|
||||
request = await self.hass.async_add_job(self.leaf.request_update)
|
||||
_LOGGER.debug("Requesting battery update, %s", self.leaf.vin)
|
||||
request = await self.hass.async_add_executor_job(
|
||||
self.leaf.request_update)
|
||||
if not request:
|
||||
_LOGGER.error("Battery update request failed")
|
||||
return None
|
||||
|
||||
for attempt in range(MAX_RESPONSE_ATTEMPTS):
|
||||
_LOGGER.info("Waiting %s seconds for battery update (%s) (%s)",
|
||||
PYCARWINGS2_SLEEP, self.leaf.vin, attempt)
|
||||
_LOGGER.debug(
|
||||
"Waiting %s seconds for battery update (%s) (%s)",
|
||||
PYCARWINGS2_SLEEP, self.leaf.vin, attempt)
|
||||
await asyncio.sleep(PYCARWINGS2_SLEEP)
|
||||
|
||||
# Note leaf.get_status_from_update is always returning 0, so
|
||||
# don't try to use it anymore.
|
||||
server_info = await self.hass.async_add_job(
|
||||
self.leaf.get_latest_battery_status
|
||||
)
|
||||
server_info = await self.hass.async_add_executor_job(
|
||||
self.leaf.get_latest_battery_status)
|
||||
|
||||
latest_date = self._extract_start_date(server_info)
|
||||
_LOGGER.info("Latest server date=%s", latest_date)
|
||||
_LOGGER.debug("Latest server date=%s", latest_date)
|
||||
if latest_date is not None and latest_date != start_date:
|
||||
return server_info
|
||||
|
||||
_LOGGER.info("%s attempts exceeded return latest data from server",
|
||||
MAX_RESPONSE_ATTEMPTS)
|
||||
_LOGGER.debug(
|
||||
"%s attempts exceeded return latest data from server",
|
||||
MAX_RESPONSE_ATTEMPTS)
|
||||
return server_info
|
||||
except CarwingsError:
|
||||
_LOGGER.error("An error occurred getting battery status.")
|
||||
|
@ -382,10 +397,8 @@ class LeafDataStore:
|
|||
"""Request climate data from Nissan servers."""
|
||||
from pycarwings2 import CarwingsError
|
||||
try:
|
||||
request = await self.hass.async_add_job(
|
||||
self.leaf.get_latest_hvac_status
|
||||
)
|
||||
return request
|
||||
return await self.hass.async_add_executor_job(
|
||||
self.leaf.get_latest_hvac_status)
|
||||
except CarwingsError:
|
||||
_LOGGER.error(
|
||||
"An error occurred communicating with the car %s",
|
||||
|
@ -396,43 +409,27 @@ class LeafDataStore:
|
|||
"""Set climate control mode via Nissan servers."""
|
||||
climate_result = None
|
||||
if toggle:
|
||||
_LOGGER.info("Requesting climate turn on for %s", self.leaf.vin)
|
||||
request = await self.hass.async_add_job(
|
||||
self.leaf.start_climate_control
|
||||
)
|
||||
for attempt in range(MAX_RESPONSE_ATTEMPTS):
|
||||
if attempt > 0:
|
||||
_LOGGER.info("Climate data not in yet (%s) (%s). "
|
||||
"Waiting (%s) seconds.", self.leaf.vin,
|
||||
attempt, PYCARWINGS2_SLEEP)
|
||||
await asyncio.sleep(PYCARWINGS2_SLEEP)
|
||||
|
||||
climate_result = await self.hass.async_add_job(
|
||||
self.leaf.get_start_climate_control_result, request
|
||||
)
|
||||
|
||||
if climate_result is not None:
|
||||
break
|
||||
|
||||
_LOGGER.debug("Requesting climate turn on for %s", self.leaf.vin)
|
||||
set_function = self.leaf.start_climate_control
|
||||
result_function = self.leaf.get_start_climate_control_result
|
||||
else:
|
||||
_LOGGER.info("Requesting climate turn off for %s", self.leaf.vin)
|
||||
request = await self.hass.async_add_job(
|
||||
self.leaf.stop_climate_control
|
||||
)
|
||||
_LOGGER.debug("Requesting climate turn off for %s", self.leaf.vin)
|
||||
set_function = self.leaf.stop_climate_control
|
||||
result_function = self.leaf.get_stop_climate_control_result
|
||||
|
||||
for attempt in range(MAX_RESPONSE_ATTEMPTS):
|
||||
if attempt > 0:
|
||||
_LOGGER.debug("Climate data not in yet. (%s) (%s). "
|
||||
"Waiting %s seconds", self.leaf.vin,
|
||||
attempt, PYCARWINGS2_SLEEP)
|
||||
await asyncio.sleep(PYCARWINGS2_SLEEP)
|
||||
request = await self.hass.async_add_executor_job(set_function)
|
||||
for attempt in range(MAX_RESPONSE_ATTEMPTS):
|
||||
if attempt > 0:
|
||||
_LOGGER.debug("Climate data not in yet (%s) (%s). "
|
||||
"Waiting (%s) seconds", self.leaf.vin,
|
||||
attempt, PYCARWINGS2_SLEEP)
|
||||
await asyncio.sleep(PYCARWINGS2_SLEEP)
|
||||
|
||||
climate_result = await self.hass.async_add_job(
|
||||
self.leaf.get_stop_climate_control_result, request
|
||||
)
|
||||
climate_result = await self.hass.async_add_executor_job(
|
||||
result_function, request)
|
||||
|
||||
if climate_result is not None:
|
||||
break
|
||||
if climate_result is not None:
|
||||
break
|
||||
|
||||
if climate_result is not None:
|
||||
_LOGGER.debug("Climate result: %s", climate_result.__dict__)
|
||||
|
@ -444,7 +441,8 @@ class LeafDataStore:
|
|||
|
||||
async def async_get_location(self):
|
||||
"""Get location from Nissan servers."""
|
||||
request = await self.hass.async_add_job(self.leaf.request_location)
|
||||
request = await self.hass.async_add_executor_job(
|
||||
self.leaf.request_location)
|
||||
for attempt in range(MAX_RESPONSE_ATTEMPTS):
|
||||
if attempt > 0:
|
||||
_LOGGER.debug("Location data not in yet. (%s) (%s). "
|
||||
|
@ -452,9 +450,8 @@ class LeafDataStore:
|
|||
attempt, PYCARWINGS2_SLEEP)
|
||||
await asyncio.sleep(PYCARWINGS2_SLEEP)
|
||||
|
||||
location_status = await self.hass.async_add_job(
|
||||
self.leaf.get_status_from_location, request
|
||||
)
|
||||
location_status = await self.hass.async_add_executor_job(
|
||||
self.leaf.get_status_from_location, request)
|
||||
|
||||
if location_status is not None:
|
||||
_LOGGER.debug("Location_status=%s", location_status.__dict__)
|
||||
|
@ -462,21 +459,6 @@ class LeafDataStore:
|
|||
|
||||
return location_status
|
||||
|
||||
async def async_start_charging(self):
|
||||
"""Request start charging via Nissan servers."""
|
||||
# Send the command to request charging is started to Nissan servers.
|
||||
# If that completes OK then trigger a fresh update to pull the
|
||||
# charging status from the car after waiting a minute for the
|
||||
# charging request to reach the car.
|
||||
result = await self.hass.async_add_job(self.leaf.start_charging)
|
||||
if result:
|
||||
_LOGGER.debug("Start charging sent, "
|
||||
"request updated data in 1 minute")
|
||||
check_charge_at = utcnow() + timedelta(minutes=1)
|
||||
self.next_update = check_charge_at
|
||||
async_track_point_in_utc_time(
|
||||
self.hass, self.async_update_data, check_charge_at)
|
||||
|
||||
|
||||
class LeafEntity(Entity):
|
||||
"""Base class for Nissan Leaf entity."""
|
||||
|
@ -499,7 +481,6 @@ class LeafEntity(Entity):
|
|||
'last_attempt': self.car.last_check,
|
||||
'updated_on': self.car.last_battery_response,
|
||||
'update_in_progress': self.car.request_in_progress,
|
||||
'location_updated_on': self.car.last_location_response,
|
||||
'vin': self.car.leaf.vin,
|
||||
}
|
||||
|
||||
|
@ -509,6 +490,7 @@ class LeafEntity(Entity):
|
|||
async_dispatcher_connect(
|
||||
self.car.hass, SIGNAL_UPDATE_LEAF, self._update_callback)
|
||||
|
||||
@callback
|
||||
def _update_callback(self):
|
||||
"""Update the state."""
|
||||
self.schedule_update_ha_state(True)
|
||||
self.async_schedule_update_ha_state(True)
|
||||
|
|
|
@ -2,28 +2,29 @@
|
|||
import logging
|
||||
|
||||
from homeassistant.components.nissan_leaf import (
|
||||
DATA_LEAF, DATA_PLUGGED_IN, LeafEntity)
|
||||
DATA_CHARGING, DATA_LEAF, DATA_PLUGGED_IN, LeafEntity)
|
||||
from homeassistant.components.binary_sensor import BinarySensorDevice
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DEPENDENCIES = ['nissan_leaf']
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
def setup_platform(hass, config, add_entities, discovery_info=None):
|
||||
"""Set up of a Nissan Leaf binary sensor."""
|
||||
_LOGGER.debug(
|
||||
"binary_sensor setup_platform, discovery_info=%s", discovery_info)
|
||||
if discovery_info is None:
|
||||
return
|
||||
|
||||
devices = []
|
||||
for key, value in hass.data[DATA_LEAF].items():
|
||||
_LOGGER.debug(
|
||||
"binary_sensor setup_platform, key=%s, value=%s", key, value)
|
||||
devices.append(LeafPluggedInSensor(value))
|
||||
for vin, datastore in hass.data[DATA_LEAF].items():
|
||||
_LOGGER.debug("Adding binary_sensors for vin=%s", vin)
|
||||
devices.append(LeafPluggedInSensor(datastore))
|
||||
devices.append(LeafChargingSensor(datastore))
|
||||
|
||||
add_devices(devices, True)
|
||||
add_entities(devices, True)
|
||||
|
||||
|
||||
class LeafPluggedInSensor(LeafEntity):
|
||||
class LeafPluggedInSensor(LeafEntity, BinarySensorDevice):
|
||||
"""Plugged In Sensor class."""
|
||||
|
||||
@property
|
||||
|
@ -32,7 +33,7 @@ class LeafPluggedInSensor(LeafEntity):
|
|||
return "{} {}".format(self.car.leaf.nickname, "Plug Status")
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
def is_on(self):
|
||||
"""Return true if plugged in."""
|
||||
return self.car.data[DATA_PLUGGED_IN]
|
||||
|
||||
|
@ -42,3 +43,24 @@ class LeafPluggedInSensor(LeafEntity):
|
|||
if self.car.data[DATA_PLUGGED_IN]:
|
||||
return 'mdi:power-plug'
|
||||
return 'mdi:power-plug-off'
|
||||
|
||||
|
||||
class LeafChargingSensor(LeafEntity, BinarySensorDevice):
|
||||
"""Charging Sensor class."""
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Sensor name."""
|
||||
return "{} {}".format(self.car.leaf.nickname, "Charging Status")
|
||||
|
||||
@property
|
||||
def is_on(self):
|
||||
"""Return true if charging."""
|
||||
return self.car.data[DATA_CHARGING]
|
||||
|
||||
@property
|
||||
def icon(self):
|
||||
"""Icon handling."""
|
||||
if self.car.data[DATA_CHARGING]:
|
||||
return 'mdi:flash'
|
||||
return 'mdi:flash-off'
|
||||
|
|
|
@ -15,28 +15,28 @@ ICON_CAR = "mdi:car"
|
|||
|
||||
def setup_scanner(hass, config, see, discovery_info=None):
|
||||
"""Set up the Nissan Leaf tracker."""
|
||||
_LOGGER.debug("Setting up Scanner (device_tracker) for Nissan Leaf, "
|
||||
"discovery_info=%s", discovery_info)
|
||||
if discovery_info is None:
|
||||
return False
|
||||
|
||||
def see_vehicle():
|
||||
"""Handle the reporting of the vehicle position."""
|
||||
for key, value in hass.data[DATA_LEAF].items():
|
||||
host_name = value.leaf.nickname
|
||||
for vin, datastore in hass.data[DATA_LEAF].items():
|
||||
host_name = datastore.leaf.nickname
|
||||
dev_id = 'nissan_leaf_{}'.format(slugify(host_name))
|
||||
if not value.data[DATA_LOCATION]:
|
||||
_LOGGER.debug("No position found for vehicle %s", key)
|
||||
return False
|
||||
if not datastore.data[DATA_LOCATION]:
|
||||
_LOGGER.debug("No position found for vehicle %s", vin)
|
||||
return
|
||||
_LOGGER.debug("Updating device_tracker for %s with position %s",
|
||||
value.leaf.nickname,
|
||||
value.data[DATA_LOCATION].__dict__)
|
||||
datastore.leaf.nickname,
|
||||
datastore.data[DATA_LOCATION].__dict__)
|
||||
attrs = {
|
||||
'updated_on': value.last_location_response,
|
||||
'updated_on': datastore.last_location_response,
|
||||
}
|
||||
see(dev_id=dev_id,
|
||||
host_name=host_name,
|
||||
gps=(
|
||||
value.data[DATA_LOCATION].latitude,
|
||||
value.data[DATA_LOCATION].longitude
|
||||
datastore.data[DATA_LOCATION].latitude,
|
||||
datastore.data[DATA_LOCATION].longitude
|
||||
),
|
||||
attributes=attrs,
|
||||
icon=ICON_CAR)
|
||||
|
|
|
@ -18,15 +18,15 @@ ICON_RANGE = 'mdi:speedometer'
|
|||
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
"""Sensors setup."""
|
||||
_LOGGER.debug("setup_platform nissan_leaf sensors, discovery_info=%s",
|
||||
discovery_info)
|
||||
if discovery_info is None:
|
||||
return
|
||||
|
||||
devices = []
|
||||
for key, value in hass.data[DATA_LEAF].items():
|
||||
_LOGGER.debug("adding sensor for item key=%s, value=%s", key, value)
|
||||
devices.append(LeafBatterySensor(value))
|
||||
devices.append(LeafRangeSensor(value, True))
|
||||
devices.append(LeafRangeSensor(value, False))
|
||||
for vin, datastore in hass.data[DATA_LEAF].items():
|
||||
_LOGGER.debug("Adding sensors for vin=%s", vin)
|
||||
devices.append(LeafBatterySensor(datastore))
|
||||
devices.append(LeafRangeSensor(datastore, True))
|
||||
devices.append(LeafRangeSensor(datastore, False))
|
||||
|
||||
add_devices(devices, True)
|
||||
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
# Describes the format for available services for nissan_leaf
|
||||
|
||||
start_charge:
|
||||
description: >
|
||||
Start the vehicle charging. It must be plugged in first!
|
||||
fields:
|
||||
vin:
|
||||
description: >
|
||||
The vehicle identification number (VIN) of the vehicle, 17 characters
|
||||
example: WBANXXXXXX1234567
|
||||
|
||||
update:
|
||||
description: >
|
||||
Fetch the last state of the vehicle of all your accounts, requesting
|
||||
an update from of the state from the car if possible.
|
||||
fields:
|
||||
vin:
|
||||
description: >
|
||||
The vehicle identification number (VIN) of the vehicle, 17 characters
|
||||
example: WBANXXXXXX1234567
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
import logging
|
||||
|
||||
from homeassistant.components.nissan_leaf import (
|
||||
DATA_CHARGING, DATA_CLIMATE, DATA_LEAF, LeafEntity)
|
||||
DATA_CLIMATE, DATA_LEAF, LeafEntity)
|
||||
from homeassistant.helpers.entity import ToggleEntity
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
@ -12,13 +12,13 @@ DEPENDENCIES = ['nissan_leaf']
|
|||
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
"""Nissan Leaf switch platform setup."""
|
||||
_LOGGER.debug(
|
||||
"In switch setup platform, discovery_info=%s", discovery_info)
|
||||
if discovery_info is None:
|
||||
return
|
||||
|
||||
devices = []
|
||||
for value in hass.data[DATA_LEAF].values():
|
||||
devices.append(LeafChargeSwitch(value))
|
||||
devices.append(LeafClimateSwitch(value))
|
||||
for vin, datastore in hass.data[DATA_LEAF].items():
|
||||
_LOGGER.debug("Adding switch for vin=%s", vin)
|
||||
devices.append(LeafClimateSwitch(datastore))
|
||||
|
||||
add_devices(devices, True)
|
||||
|
||||
|
@ -40,7 +40,7 @@ class LeafClimateSwitch(LeafEntity, ToggleEntity):
|
|||
@property
|
||||
def device_state_attributes(self):
|
||||
"""Return climate control attributes."""
|
||||
attrs = super(LeafClimateSwitch, self).device_state_attributes
|
||||
attrs = super().device_state_attributes
|
||||
attrs["updated_on"] = self.car.last_climate_response
|
||||
return attrs
|
||||
|
||||
|
@ -58,42 +58,3 @@ class LeafClimateSwitch(LeafEntity, ToggleEntity):
|
|||
"""Turn off climate control."""
|
||||
if await self.car.async_set_climate(False):
|
||||
self.car.data[DATA_CLIMATE] = False
|
||||
|
||||
@property
|
||||
def icon(self):
|
||||
"""Climate control icon."""
|
||||
if self.car.data[DATA_CLIMATE]:
|
||||
return 'mdi:fan'
|
||||
return 'mdi:fan-off'
|
||||
|
||||
|
||||
class LeafChargeSwitch(LeafEntity, ToggleEntity):
|
||||
"""Nissan Leaf Charging On switch."""
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Switch name."""
|
||||
return "{} {}".format(self.car.leaf.nickname, "Charging Status")
|
||||
|
||||
@property
|
||||
def icon(self):
|
||||
"""Charging switch icon."""
|
||||
if self.car.data[DATA_CHARGING]:
|
||||
return 'mdi:flash'
|
||||
return 'mdi:flash-off'
|
||||
|
||||
@property
|
||||
def is_on(self):
|
||||
"""Return true if charging."""
|
||||
return self.car.data[DATA_CHARGING]
|
||||
|
||||
async def async_turn_on(self, **kwargs):
|
||||
"""Start car charging."""
|
||||
if await self.car.async_start_charging():
|
||||
self.car.data[DATA_CHARGING] = True
|
||||
|
||||
def turn_off(self, **kwargs):
|
||||
"""Nissan API doesn't allow stopping of charge remotely."""
|
||||
_LOGGER.info(
|
||||
"Cannot turn off Leaf charging."
|
||||
" Nissan API does not support stopping charge remotely")
|
||||
|
|
Loading…
Reference in New Issue