Refactor Neato botvac components as a vacuum (#9946)
* Refactor Neato botvac components as a vacuum A switch is still use to enable/disable the schedule Signed-off-by: Hugo D. (jabesq) <jabesq@gmail.com> * CI Hound fixes * Fix lint errors Signed-off-by: Hugo D. (jabesq) <jabesq@gmail.com> * [Neato vacumm] Add sensor attributes to vacuum Signed-off-by: Hugo D. (jabesq) <jabesq@gmail.com> * Remove line breaks and fix docstring * PR fixespull/10317/head
parent
4e8e04fe66
commit
1347c3191f
|
@ -90,7 +90,7 @@ def setup(hass, config):
|
|||
_LOGGER.debug("Failed to login to Neato API")
|
||||
return False
|
||||
hub.update_robots()
|
||||
for component in ('camera', 'sensor', 'switch'):
|
||||
for component in ('camera', 'vacuum', 'switch'):
|
||||
discovery.load_platform(hass, component, DOMAIN, {}, config)
|
||||
|
||||
return True
|
||||
|
|
|
@ -1,174 +0,0 @@
|
|||
"""
|
||||
Support for Neato Connected Vaccums sensors.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/sensor.neato/
|
||||
"""
|
||||
import logging
|
||||
import requests
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.components.neato import (
|
||||
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'
|
||||
|
||||
SENSOR_TYPES = {
|
||||
SENSOR_TYPE_STATUS: ['Status'],
|
||||
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):
|
||||
"""Set up the Neato sensor platform."""
|
||||
dev = []
|
||||
for robot in hass.data[NEATO_ROBOTS]:
|
||||
for type_name in SENSOR_TYPES:
|
||||
dev.append(NeatoConnectedSensor(hass, robot, type_name))
|
||||
_LOGGER.debug("Adding sensors %s", dev)
|
||||
add_devices(dev)
|
||||
|
||||
|
||||
class NeatoConnectedSensor(Entity):
|
||||
"""Neato Connected Sensor."""
|
||||
|
||||
def __init__(self, hass, robot, sensor_type):
|
||||
"""Initialize the Neato Connected sensor."""
|
||||
self.type = sensor_type
|
||||
self.robot = robot
|
||||
self.neato = hass.data[NEATO_LOGIN]
|
||||
self._robot_name = self.robot.name + ' ' + SENSOR_TYPES[self.type][0]
|
||||
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()
|
||||
self._mapdata = self.hass.data[NEATO_MAP_DATA]
|
||||
try:
|
||||
self._state = self.robot.state
|
||||
except (requests.exceptions.ConnectionError,
|
||||
requests.exceptions.HTTPError) as ex:
|
||||
self._state = None
|
||||
self._status_state = 'Offline'
|
||||
_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:
|
||||
if self._state['state'] == 1:
|
||||
if self._state['details']['isCharging']:
|
||||
self._status_state = 'Charging'
|
||||
elif (self._state['details']['isDocked'] and
|
||||
not self._state['details']['isCharging']):
|
||||
self._status_state = 'Docked'
|
||||
else:
|
||||
self._status_state = 'Stopped'
|
||||
elif self._state['state'] == 2:
|
||||
if ALERTS.get(self._state['error']) is None:
|
||||
self._status_state = (
|
||||
MODE.get(self._state['cleaning']['mode'])
|
||||
+ ' ' + ACTION.get(self._state['action']))
|
||||
else:
|
||||
self._status_state = ALERTS.get(self._state['error'])
|
||||
elif self._state['state'] == 3:
|
||||
self._status_state = 'Paused'
|
||||
elif self._state['state'] == 4:
|
||||
self._status_state = ERRORS.get(self._state['error'])
|
||||
if self.type == SENSOR_TYPE_BATTERY:
|
||||
self._battery_state = self._state['details']['charge']
|
||||
if not self._mapdata.get(self.robot.serial, {}).get('maps', []):
|
||||
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):
|
||||
"""Return unit for the sensor."""
|
||||
if self.type == SENSOR_TYPE_BATTERY:
|
||||
return '%'
|
||||
|
||||
@property
|
||||
def available(self):
|
||||
"""Return True if sensor data is available."""
|
||||
return self._state
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
"""Return the sensor state."""
|
||||
if self.type == SENSOR_TYPE_STATUS:
|
||||
return self._status_state
|
||||
if self.type == SENSOR_TYPE_BATTERY:
|
||||
return self._battery_state
|
||||
|
||||
@property
|
||||
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
|
|
@ -14,11 +14,9 @@ _LOGGER = logging.getLogger(__name__)
|
|||
|
||||
DEPENDENCIES = ['neato']
|
||||
|
||||
SWITCH_TYPE_CLEAN = 'clean'
|
||||
SWITCH_TYPE_SCHEDULE = 'scedule'
|
||||
SWITCH_TYPE_SCHEDULE = 'schedule'
|
||||
|
||||
SWITCH_TYPES = {
|
||||
SWITCH_TYPE_CLEAN: ['Clean'],
|
||||
SWITCH_TYPE_SCHEDULE: ['Schedule']
|
||||
}
|
||||
|
||||
|
@ -64,15 +62,6 @@ class NeatoConnectedSwitch(ToggleEntity):
|
|||
self._state = None
|
||||
return
|
||||
_LOGGER.debug('self._state=%s', self._state)
|
||||
if self.type == SWITCH_TYPE_CLEAN:
|
||||
if (self.robot.state['action'] == 1 or
|
||||
self.robot.state['action'] == 2 or
|
||||
self.robot.state['action'] == 3 and
|
||||
self.robot.state['state'] == 2):
|
||||
self._clean_state = STATE_ON
|
||||
else:
|
||||
self._clean_state = STATE_OFF
|
||||
_LOGGER.debug("Clean state: %s", self._clean_state)
|
||||
if self.type == SWITCH_TYPE_SCHEDULE:
|
||||
_LOGGER.debug("State: %s", self._state)
|
||||
if self.robot.schedule_enabled:
|
||||
|
@ -94,26 +83,17 @@ class NeatoConnectedSwitch(ToggleEntity):
|
|||
@property
|
||||
def is_on(self):
|
||||
"""Return true if switch is on."""
|
||||
if self.type == SWITCH_TYPE_CLEAN:
|
||||
if self._clean_state == STATE_ON:
|
||||
return True
|
||||
return False
|
||||
elif self.type == SWITCH_TYPE_SCHEDULE:
|
||||
if self.type == SWITCH_TYPE_SCHEDULE:
|
||||
if self._schedule_state == STATE_ON:
|
||||
return True
|
||||
return False
|
||||
|
||||
def turn_on(self, **kwargs):
|
||||
"""Turn the switch on."""
|
||||
if self.type == SWITCH_TYPE_CLEAN:
|
||||
self.robot.start_cleaning()
|
||||
elif self.type == SWITCH_TYPE_SCHEDULE:
|
||||
if self.type == SWITCH_TYPE_SCHEDULE:
|
||||
self.robot.enable_schedule()
|
||||
|
||||
def turn_off(self, **kwargs):
|
||||
"""Turn the switch off."""
|
||||
if self.type == SWITCH_TYPE_CLEAN:
|
||||
self.robot.pause_cleaning()
|
||||
self.robot.send_to_base()
|
||||
elif self.type == SWITCH_TYPE_SCHEDULE:
|
||||
if self.type == SWITCH_TYPE_SCHEDULE:
|
||||
self.robot.disable_schedule()
|
||||
|
|
|
@ -0,0 +1,214 @@
|
|||
"""
|
||||
Support for Neato Connected Vaccums.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/vacuum.neato/
|
||||
"""
|
||||
import logging
|
||||
|
||||
import requests
|
||||
|
||||
from homeassistant.const import STATE_OFF, STATE_ON
|
||||
from homeassistant.components.vacuum import (
|
||||
VacuumDevice, SUPPORT_BATTERY, SUPPORT_PAUSE, SUPPORT_RETURN_HOME,
|
||||
SUPPORT_STATUS, SUPPORT_STOP, SUPPORT_TURN_OFF, SUPPORT_TURN_ON,
|
||||
SUPPORT_MAP, ATTR_STATUS, ATTR_BATTERY_LEVEL, ATTR_BATTERY_ICON)
|
||||
from homeassistant.components.neato import (
|
||||
NEATO_ROBOTS, NEATO_LOGIN, NEATO_MAP_DATA, ACTION, ERRORS, MODE, ALERTS)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DEPENDENCIES = ['neato']
|
||||
|
||||
SUPPORT_NEATO = SUPPORT_BATTERY | SUPPORT_PAUSE | SUPPORT_RETURN_HOME | \
|
||||
SUPPORT_STOP | SUPPORT_TURN_OFF | SUPPORT_TURN_ON | \
|
||||
SUPPORT_STATUS | SUPPORT_MAP
|
||||
|
||||
ICON = "mdi:roomba"
|
||||
|
||||
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):
|
||||
"""Set up the Neato vacuum."""
|
||||
dev = []
|
||||
for robot in hass.data[NEATO_ROBOTS]:
|
||||
dev.append(NeatoConnectedVacuum(hass, robot))
|
||||
_LOGGER.debug("Adding vacuums %s", dev)
|
||||
add_devices(dev, True)
|
||||
|
||||
|
||||
class NeatoConnectedVacuum(VacuumDevice):
|
||||
"""Neato Connected Vacuums."""
|
||||
|
||||
def __init__(self, hass, robot):
|
||||
"""Initialize the Neato Connected Vacuums."""
|
||||
self.robot = robot
|
||||
self.neato = hass.data[NEATO_LOGIN]
|
||||
self._name = '{}'.format(self.robot.name)
|
||||
self._status_state = None
|
||||
self._clean_state = None
|
||||
self._state = None
|
||||
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
|
||||
|
||||
def update(self):
|
||||
"""Update the states of Neato Vacuums."""
|
||||
_LOGGER.debug("Running Vacuums update")
|
||||
self.neato.update_robots()
|
||||
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
|
||||
return
|
||||
_LOGGER.debug('self._state=%s', self._state)
|
||||
if self._state['state'] == 1:
|
||||
if self._state['details']['isCharging']:
|
||||
self._status_state = 'Charging'
|
||||
elif (self._state['details']['isDocked'] and
|
||||
not self._state['details']['isCharging']):
|
||||
self._status_state = 'Docked'
|
||||
else:
|
||||
self._status_state = 'Stopped'
|
||||
elif self._state['state'] == 2:
|
||||
if ALERTS.get(self._state['error']) is None:
|
||||
self._status_state = (
|
||||
MODE.get(self._state['cleaning']['mode'])
|
||||
+ ' ' + ACTION.get(self._state['action']))
|
||||
else:
|
||||
self._status_state = ALERTS.get(self._state['error'])
|
||||
elif self._state['state'] == 3:
|
||||
self._status_state = 'Paused'
|
||||
elif self._state['state'] == 4:
|
||||
self._status_state = ERRORS.get(self._state['error'])
|
||||
|
||||
if (self.robot.state['action'] == 1 or
|
||||
self.robot.state['action'] == 2 or
|
||||
self.robot.state['action'] == 3 and
|
||||
self.robot.state['state'] == 2):
|
||||
self._clean_state = STATE_ON
|
||||
else:
|
||||
self._clean_state = STATE_OFF
|
||||
|
||||
if not self._mapdata.get(self.robot.serial, {}).get('maps', []):
|
||||
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 name(self):
|
||||
"""Return the name of the device."""
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def icon(self):
|
||||
"""Return the icon to use for device."""
|
||||
return ICON
|
||||
|
||||
@property
|
||||
def supported_features(self):
|
||||
"""Flag vacuum cleaner robot features that are supported."""
|
||||
return SUPPORT_NEATO
|
||||
|
||||
@property
|
||||
def battery_level(self):
|
||||
"""Return the battery level of the vacuum cleaner."""
|
||||
return self._state['details']['charge']
|
||||
|
||||
@property
|
||||
def status(self):
|
||||
"""Return the status of the vacuum cleaner."""
|
||||
return self._status_state
|
||||
|
||||
@property
|
||||
def state_attributes(self):
|
||||
"""Return the state attributes of the vacuum cleaner."""
|
||||
data = {}
|
||||
|
||||
if self.status is not None:
|
||||
data[ATTR_STATUS] = self.status
|
||||
|
||||
if self.battery_level is not None:
|
||||
data[ATTR_BATTERY_LEVEL] = self.battery_level
|
||||
data[ATTR_BATTERY_ICON] = self.battery_icon
|
||||
|
||||
if self.clean_time_start is not None:
|
||||
data[ATTR_CLEAN_START] = self.clean_time_start
|
||||
if self.clean_time_stop is not None:
|
||||
data[ATTR_CLEAN_STOP] = self.clean_time_stop
|
||||
if self.clean_area is not None:
|
||||
data[ATTR_CLEAN_AREA] = self.clean_area
|
||||
if self.clean_suspension_charge_count is not None:
|
||||
data[ATTR_CLEAN_SUSP_COUNT] = (
|
||||
self.clean_suspension_charge_count)
|
||||
if self.clean_suspension_time is not None:
|
||||
data[ATTR_CLEAN_SUSP_TIME] = self.clean_suspension_time
|
||||
if self.clean_battery_start is not None:
|
||||
data[ATTR_CLEAN_BATTERY_START] = self.clean_battery_start
|
||||
if self.clean_battery_end is not None:
|
||||
data[ATTR_CLEAN_BATTERY_END] = self.clean_battery_end
|
||||
|
||||
return data
|
||||
|
||||
def turn_on(self, **kwargs):
|
||||
"""Turn the vacuum on and start cleaning."""
|
||||
self.robot.start_cleaning()
|
||||
|
||||
@property
|
||||
def is_on(self):
|
||||
"""Return true if switch is on."""
|
||||
return self._clean_state == STATE_ON
|
||||
|
||||
def turn_off(self, **kwargs):
|
||||
"""Turn the switch off."""
|
||||
self.robot.pause_cleaning()
|
||||
self.robot.send_to_base()
|
||||
|
||||
def return_to_base(self, **kwargs):
|
||||
"""Set the vacuum cleaner to return to the dock."""
|
||||
self.robot.send_to_base()
|
||||
|
||||
def stop(self, **kwargs):
|
||||
"""Stop the vacuum cleaner."""
|
||||
self.robot.stop_cleaning()
|
||||
|
||||
def start_pause(self, **kwargs):
|
||||
"""Start, pause or resume the cleaning task."""
|
||||
if self._state['state'] == 1:
|
||||
self.robot.start_cleaning()
|
||||
elif self._state['state'] == 2 and\
|
||||
ALERTS.get(self._state['error']) is None:
|
||||
self.robot.pause_cleaning()
|
||||
if self._state['state'] == 3:
|
||||
self.robot.resume_cleaning()
|
Loading…
Reference in New Issue