Add support fo map data from Neato (#6939)
* Responsiveness * Delay was not needed as commands does not return until done. * Add support for cleaning maps and cleaning data * Hound * Docstring * Update requirements * Review changes * External lib now returns the raw data. * debug * Sensor did not refresh * Error handling * Issue warning on connection error * Update requirements * Review changes
This commit is contained in:
parent
01c7616147
commit
38ad5714cd
5 changed files with 164 additions and 22 deletions
65
homeassistant/components/camera/neato.py
Normal file
65
homeassistant/components/camera/neato.py
Normal file
|
@ -0,0 +1,65 @@
|
|||
"""
|
||||
Camera that loads a picture from a local file.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/camera.neato/
|
||||
"""
|
||||
import logging
|
||||
|
||||
from datetime import timedelta
|
||||
from homeassistant.components.camera import Camera
|
||||
from homeassistant.components.neato import (
|
||||
NEATO_MAP_DATA, NEATO_ROBOTS, NEATO_LOGIN)
|
||||
from homeassistant.util import Throttle
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DEPENDENCIES = ['neato']
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
"""Setup the Camera."""
|
||||
dev = []
|
||||
for robot in hass.data[NEATO_ROBOTS]:
|
||||
if 'maps' in robot.traits:
|
||||
dev.append(NeatoCleaningMap(hass, robot))
|
||||
_LOGGER.debug('Adding robots for cleaning maps %s', dev)
|
||||
add_devices(dev, True)
|
||||
|
||||
|
||||
class NeatoCleaningMap(Camera):
|
||||
"""Neato cleaning map for last clean."""
|
||||
|
||||
def __init__(self, hass, robot):
|
||||
"""Initialize Neato cleaning map."""
|
||||
super().__init__()
|
||||
self.robot = robot
|
||||
self._robot_name = self.robot.name + ' Cleaning Map'
|
||||
self._robot_serial = self.robot.serial
|
||||
self.neato = hass.data[NEATO_LOGIN]
|
||||
self._image_url = None
|
||||
self._image = None
|
||||
|
||||
def camera_image(self):
|
||||
"""Return image response."""
|
||||
self.update()
|
||||
return self._image
|
||||
|
||||
@Throttle(timedelta(seconds=10))
|
||||
def update(self):
|
||||
"""Check the contents of the map list."""
|
||||
self.neato.update_robots()
|
||||
image_url = None
|
||||
map_data = self.hass.data[NEATO_MAP_DATA]
|
||||
image_url = map_data[self._robot_serial]['maps'][0]['url']
|
||||
if image_url == self._image_url:
|
||||
_LOGGER.debug('The map image_url is the same as old')
|
||||
return
|
||||
image = self.neato.download_map(image_url)
|
||||
self._image = image.read()
|
||||
self._image_url = image_url
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of this camera."""
|
||||
return self._robot_name
|
|
@ -17,12 +17,13 @@ import homeassistant.helpers.config_validation as cv
|
|||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
REQUIREMENTS = ['https://github.com/jabesq/pybotvac/archive/v0.0.1.zip'
|
||||
'#pybotvac==0.0.1']
|
||||
REQUIREMENTS = ['https://github.com/jabesq/pybotvac/archive/v0.0.3.zip'
|
||||
'#pybotvac==0.0.3']
|
||||
|
||||
DOMAIN = 'neato'
|
||||
NEATO_ROBOTS = 'neato_robots'
|
||||
NEATO_LOGIN = 'neato_login'
|
||||
NEATO_MAP_DATA = 'neato_map_data'
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema({
|
||||
DOMAIN: vol.Schema({
|
||||
|
@ -89,7 +90,7 @@ def setup(hass, config):
|
|||
_LOGGER.debug('Failed to login to Neato API')
|
||||
return False
|
||||
hub.update_robots()
|
||||
for component in ('sensor', 'switch'):
|
||||
for component in ('camera', 'sensor', 'switch'):
|
||||
discovery.load_platform(hass, component, DOMAIN, {}, config)
|
||||
|
||||
return True
|
||||
|
@ -108,6 +109,7 @@ class NeatoHub(object):
|
|||
domain_config[CONF_USERNAME],
|
||||
domain_config[CONF_PASSWORD])
|
||||
self._hass.data[NEATO_ROBOTS] = self.my_neato.robots
|
||||
self._hass.data[NEATO_MAP_DATA] = self.my_neato.maps
|
||||
|
||||
def login(self):
|
||||
"""Login to My Neato."""
|
||||
|
@ -126,3 +128,9 @@ class NeatoHub(object):
|
|||
_LOGGER.debug('Running HUB.update_robots %s',
|
||||
self._hass.data[NEATO_ROBOTS])
|
||||
self._hass.data[NEATO_ROBOTS] = self.my_neato.robots
|
||||
self._hass.data[NEATO_MAP_DATA] = self.my_neato.maps
|
||||
|
||||
def download_map(self, url):
|
||||
"""Download a new map image."""
|
||||
map_image_data = self.my_neato.get_map_image(url)
|
||||
return map_image_data
|
||||
|
|
|
@ -8,9 +8,12 @@ import logging
|
|||
import requests
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.components.neato import (
|
||||
NEATO_ROBOTS, NEATO_LOGIN, ACTION, ERRORS, MODE, ALERTS)
|
||||
NEATO_ROBOTS, NEATO_LOGIN, NEATO_MAP_DATA, ACTION, ERRORS, MODE, ALERTS)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DEPENDENCIES = ['neato']
|
||||
|
||||
SENSOR_TYPE_STATUS = 'status'
|
||||
SENSOR_TYPE_BATTERY = 'battery'
|
||||
|
||||
|
@ -19,12 +22,17 @@ SENSOR_TYPES = {
|
|||
SENSOR_TYPE_BATTERY: ['Battery']
|
||||
}
|
||||
|
||||
ATTR_CLEAN_START = 'clean_start'
|
||||
ATTR_CLEAN_STOP = 'clean_stop'
|
||||
ATTR_CLEAN_AREA = 'clean_area'
|
||||
ATTR_CLEAN_BATTERY_START = 'battery_level_at_clean_start'
|
||||
ATTR_CLEAN_BATTERY_END = 'battery_level_at_clean_end'
|
||||
ATTR_CLEAN_SUSP_COUNT = 'clean_suspension_count'
|
||||
ATTR_CLEAN_SUSP_TIME = 'clean_suspension_time'
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
"""Setup the Neato sensor platform."""
|
||||
if not hass.data['neato_robots']:
|
||||
return False
|
||||
|
||||
dev = []
|
||||
for robot in hass.data[NEATO_ROBOTS]:
|
||||
for type_name in SENSOR_TYPES:
|
||||
|
@ -42,22 +50,37 @@ class NeatoConnectedSensor(Entity):
|
|||
self.robot = robot
|
||||
self.neato = hass.data[NEATO_LOGIN]
|
||||
self._robot_name = self.robot.name + ' ' + SENSOR_TYPES[self.type][0]
|
||||
self._state = self.robot.state
|
||||
self._battery_state = None
|
||||
self._status_state = None
|
||||
try:
|
||||
self._state = self.robot.state
|
||||
except (requests.exceptions.ConnectionError,
|
||||
requests.exceptions.HTTPError) as ex:
|
||||
self._state = None
|
||||
_LOGGER.warning('Neato connection error: %s', ex)
|
||||
self._mapdata = hass.data[NEATO_MAP_DATA]
|
||||
self.clean_time_start = None
|
||||
self.clean_time_stop = None
|
||||
self.clean_area = None
|
||||
self.clean_battery_start = None
|
||||
self.clean_battery_end = None
|
||||
self.clean_suspension_charge_count = None
|
||||
self.clean_suspension_time = None
|
||||
self._battery_state = None
|
||||
|
||||
def update(self):
|
||||
"""Update the properties of sensor."""
|
||||
_LOGGER.debug('Update of sensor')
|
||||
self.neato.update_robots()
|
||||
if not self._state:
|
||||
return
|
||||
self._mapdata = self.hass.data[NEATO_MAP_DATA]
|
||||
try:
|
||||
self._state = self.robot.state
|
||||
except requests.exceptions.HTTPError as ex:
|
||||
except (requests.exceptions.ConnectionError,
|
||||
requests.exceptions.HTTPError) as ex:
|
||||
self._state = None
|
||||
self._status_state = 'Offline'
|
||||
_LOGGER.debug('Neato connection issue: %s', ex)
|
||||
_LOGGER.warning('Neato connection error: %s', ex)
|
||||
return
|
||||
if not self._state:
|
||||
return
|
||||
_LOGGER.debug('self._state=%s', self._state)
|
||||
if self.type == SENSOR_TYPE_STATUS:
|
||||
|
@ -82,6 +105,27 @@ class NeatoConnectedSensor(Entity):
|
|||
self._status_state = ERRORS.get(self._state['error'])
|
||||
if self.type == SENSOR_TYPE_BATTERY:
|
||||
self._battery_state = self._state['details']['charge']
|
||||
if self._mapdata is None:
|
||||
return
|
||||
self.clean_time_start = (
|
||||
(self._mapdata[self.robot.serial]['maps'][0]['start_at']
|
||||
.strip('Z'))
|
||||
.replace('T', ' '))
|
||||
self.clean_time_stop = (
|
||||
(self._mapdata[self.robot.serial]['maps'][0]['end_at'].strip('Z'))
|
||||
.replace('T', ' '))
|
||||
self.clean_area = (
|
||||
self._mapdata[self.robot.serial]['maps'][0]['cleaned_area'])
|
||||
self.clean_suspension_charge_count = (
|
||||
self._mapdata[self.robot.serial]['maps'][0]
|
||||
['suspended_cleaning_charging_count'])
|
||||
self.clean_suspension_time = (
|
||||
self._mapdata[self.robot.serial]['maps'][0]
|
||||
['time_in_suspended_cleaning'])
|
||||
self.clean_battery_start = (
|
||||
self._mapdata[self.robot.serial]['maps'][0]['run_charge_at_start'])
|
||||
self.clean_battery_end = (
|
||||
self._mapdata[self.robot.serial]['maps'][0]['run_charge_at_end'])
|
||||
|
||||
@property
|
||||
def unit_of_measurement(self):
|
||||
|
@ -109,3 +153,25 @@ class NeatoConnectedSensor(Entity):
|
|||
def name(self):
|
||||
"""Return the name of the sensor."""
|
||||
return self._robot_name
|
||||
|
||||
@property
|
||||
def device_state_attributes(self):
|
||||
"""Return the device specific attributes."""
|
||||
data = {}
|
||||
if self.type is SENSOR_TYPE_STATUS:
|
||||
if self.clean_time_start:
|
||||
data[ATTR_CLEAN_START] = self.clean_time_start
|
||||
if self.clean_time_stop:
|
||||
data[ATTR_CLEAN_STOP] = self.clean_time_stop
|
||||
if self.clean_area:
|
||||
data[ATTR_CLEAN_AREA] = self.clean_area
|
||||
if self.clean_suspension_charge_count:
|
||||
data[ATTR_CLEAN_SUSP_COUNT] = (
|
||||
self.clean_suspension_charge_count)
|
||||
if self.clean_suspension_time:
|
||||
data[ATTR_CLEAN_SUSP_TIME] = self.clean_suspension_time
|
||||
if self.clean_battery_start:
|
||||
data[ATTR_CLEAN_BATTERY_START] = self.clean_battery_start
|
||||
if self.clean_battery_end:
|
||||
data[ATTR_CLEAN_BATTERY_END] = self.clean_battery_end
|
||||
return data
|
||||
|
|
|
@ -12,6 +12,8 @@ from homeassistant.components.neato import NEATO_ROBOTS, NEATO_LOGIN
|
|||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DEPENDENCIES = ['neato']
|
||||
|
||||
SWITCH_TYPE_CLEAN = 'clean'
|
||||
SWITCH_TYPE_SCHEDULE = 'scedule'
|
||||
|
||||
|
@ -23,9 +25,6 @@ SWITCH_TYPES = {
|
|||
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
"""Setup the Neato switches."""
|
||||
if not hass.data[NEATO_ROBOTS]:
|
||||
return False
|
||||
|
||||
dev = []
|
||||
for robot in hass.data[NEATO_ROBOTS]:
|
||||
for type_name in SWITCH_TYPES:
|
||||
|
@ -43,7 +42,12 @@ class NeatoConnectedSwitch(ToggleEntity):
|
|||
self.robot = robot
|
||||
self.neato = hass.data[NEATO_LOGIN]
|
||||
self._robot_name = self.robot.name + ' ' + SWITCH_TYPES[self.type][0]
|
||||
self._state = self.robot.state
|
||||
try:
|
||||
self._state = self.robot.state
|
||||
except (requests.exceptions.ConnectionError,
|
||||
requests.exceptions.HTTPError) as ex:
|
||||
_LOGGER.warning('Neato connection error: %s', ex)
|
||||
self._state = None
|
||||
self._schedule_state = None
|
||||
self._clean_state = None
|
||||
|
||||
|
@ -51,14 +55,13 @@ class NeatoConnectedSwitch(ToggleEntity):
|
|||
"""Update the states of Neato switches."""
|
||||
_LOGGER.debug('Running switch update')
|
||||
self.neato.update_robots()
|
||||
if not self._state:
|
||||
return
|
||||
try:
|
||||
self._state = self.robot.state
|
||||
except requests.exceptions.HTTPError:
|
||||
except (requests.exceptions.ConnectionError,
|
||||
requests.exceptions.HTTPError) as ex:
|
||||
_LOGGER.warning('Neato connection error: %s', ex)
|
||||
self._state = None
|
||||
return
|
||||
self._state = self.robot.state
|
||||
_LOGGER.debug('self._state=%s', self._state)
|
||||
if self.type == SWITCH_TYPE_CLEAN:
|
||||
if (self.robot.state['action'] == 1 or
|
||||
|
|
|
@ -269,7 +269,7 @@ https://github.com/gurumitts/pylutron-caseta/archive/v0.2.5.zip#pylutron-caseta=
|
|||
https://github.com/jabesq/netatmo-api-python/archive/v0.9.1.zip#lnetatmo==0.9.1
|
||||
|
||||
# homeassistant.components.neato
|
||||
https://github.com/jabesq/pybotvac/archive/v0.0.1.zip#pybotvac==0.0.1
|
||||
https://github.com/jabesq/pybotvac/archive/v0.0.3.zip#pybotvac==0.0.3
|
||||
|
||||
# homeassistant.components.sensor.sabnzbd
|
||||
https://github.com/jamespcole/home-assistant-nzb-clients/archive/616cad59154092599278661af17e2a9f2cf5e2a9.zip#python-sabnzbd==0.1
|
||||
|
|
Loading…
Add table
Reference in a new issue