Merge pull request #1302 from persandstrom/vsure_maintenace

updated verisure reconnect
This commit is contained in:
Paulus Schoutsen 2016-02-27 14:50:24 -08:00
commit afb51d14b8
7 changed files with 213 additions and 219 deletions

View file

@ -21,7 +21,7 @@ ENTITY_ID_FORMAT = DOMAIN + '.{}'
# Maps discovered services to their platforms
DISCOVERY_PLATFORMS = {
verisure.DISCOVER_SENSORS: 'verisure'
verisure.DISCOVER_ALARMS: 'verisure'
}
SERVICE_TO_METHOD = {

View file

@ -9,7 +9,8 @@ https://home-assistant.io/components/verisure/
import logging
import homeassistant.components.alarm_control_panel as alarm
import homeassistant.components.verisure as verisure
from homeassistant.components.verisure import HUB as hub
from homeassistant.const import (
STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_DISARMED,
STATE_UNKNOWN)
@ -20,18 +21,13 @@ _LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_devices, discovery_info=None):
""" Sets up the Verisure platform. """
if not verisure.MY_PAGES:
_LOGGER.error('A connection has not been made to Verisure mypages.')
return False
alarms = []
alarms.extend([
VerisureAlarm(value)
for value in verisure.ALARM_STATUS.values()
if verisure.SHOW_ALARM
])
if int(hub.config.get('alarm', '1')):
hub.update_alarms()
alarms.extend([
VerisureAlarm(value.id)
for value in hub.alarm_status.values()
])
add_devices(alarms)
@ -39,9 +35,10 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
class VerisureAlarm(alarm.AlarmControlPanel):
""" Represents a Verisure alarm status. """
def __init__(self, alarm_status):
self._id = alarm_status.id
def __init__(self, device_id):
self._id = device_id
self._state = STATE_UNKNOWN
self._digits = int(hub.config.get('code_digits', '4'))
@property
def name(self):
@ -55,41 +52,41 @@ class VerisureAlarm(alarm.AlarmControlPanel):
@property
def code_format(self):
""" Four digit code required. """
return '^\\d{%s}$' % verisure.CODE_DIGITS
""" code format as regex """
return '^\\d{%s}$' % self._digits
def update(self):
""" Update alarm status """
verisure.update_alarm()
hub.update_alarms()
if verisure.ALARM_STATUS[self._id].status == 'unarmed':
if hub.alarm_status[self._id].status == 'unarmed':
self._state = STATE_ALARM_DISARMED
elif verisure.ALARM_STATUS[self._id].status == 'armedhome':
elif hub.alarm_status[self._id].status == 'armedhome':
self._state = STATE_ALARM_ARMED_HOME
elif verisure.ALARM_STATUS[self._id].status == 'armed':
elif hub.alarm_status[self._id].status == 'armed':
self._state = STATE_ALARM_ARMED_AWAY
elif verisure.ALARM_STATUS[self._id].status != 'pending':
elif hub.alarm_status[self._id].status != 'pending':
_LOGGER.error(
'Unknown alarm state %s',
verisure.ALARM_STATUS[self._id].status)
hub.alarm_status[self._id].status)
def alarm_disarm(self, code=None):
""" Send disarm command. """
verisure.MY_PAGES.alarm.set(code, 'DISARMED')
hub.my_pages.alarm.set(code, 'DISARMED')
_LOGGER.info('verisure alarm disarming')
verisure.MY_PAGES.alarm.wait_while_pending()
verisure.update_alarm()
hub.my_pages.alarm.wait_while_pending()
self.update()
def alarm_arm_home(self, code=None):
""" Send arm home command. """
verisure.MY_PAGES.alarm.set(code, 'ARMED_HOME')
hub.my_pages.alarm.set(code, 'ARMED_HOME')
_LOGGER.info('verisure alarm arming home')
verisure.MY_PAGES.alarm.wait_while_pending()
verisure.update_alarm()
hub.my_pages.alarm.wait_while_pending()
self.update()
def alarm_arm_away(self, code=None):
""" Send arm away command. """
verisure.MY_PAGES.alarm.set(code, 'ARMED_AWAY')
hub.my_pages.alarm.set(code, 'ARMED_AWAY')
_LOGGER.info('verisure alarm arming away')
verisure.MY_PAGES.alarm.wait_while_pending()
verisure.update_alarm()
hub.my_pages.alarm.wait_while_pending()
self.update()

View file

@ -8,7 +8,7 @@ https://home-assistant.io/components/verisure/
"""
import logging
import homeassistant.components.verisure as verisure
from homeassistant.components.verisure import HUB as hub
from homeassistant.components.lock import LockDevice
from homeassistant.const import STATE_LOCKED, STATE_UNKNOWN, STATE_UNLOCKED
@ -19,16 +19,13 @@ ATTR_CODE = 'code'
def setup_platform(hass, config, add_devices, discovery_info=None):
""" Sets up the Verisure platform. """
if not verisure.MY_PAGES:
_LOGGER.error('A connection has not been made to Verisure mypages.')
return False
locks = []
locks.extend([VerisureDoorlock(value)
for value in verisure.LOCK_STATUS.values()
if verisure.SHOW_LOCKS])
if int(hub.config.get('locks', '1')):
hub.update_locks()
locks.extend([
VerisureDoorlock(device_id)
for device_id in hub.lock_status.keys()
])
add_devices(locks)
@ -36,10 +33,10 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
class VerisureDoorlock(LockDevice):
""" Represents a Verisure doorlock status. """
def __init__(self, lock_status, code=None):
self._id = lock_status.id
def __init__(self, device_id):
self._id = device_id
self._state = STATE_UNKNOWN
self._code = code
self._digits = int(hub.config.get('code_digits', '4'))
@property
def name(self):
@ -54,36 +51,36 @@ class VerisureDoorlock(LockDevice):
@property
def code_format(self):
""" Six digit code required. """
return '^\\d{%s}$' % verisure.CODE_DIGITS
return '^\\d{%s}$' % self._digits
def update(self):
""" Update lock status """
verisure.update_lock()
hub.update_locks()
if verisure.LOCK_STATUS[self._id].status == 'unlocked':
if hub.lock_status[self._id].status == 'unlocked':
self._state = STATE_UNLOCKED
elif verisure.LOCK_STATUS[self._id].status == 'locked':
elif hub.lock_status[self._id].status == 'locked':
self._state = STATE_LOCKED
elif verisure.LOCK_STATUS[self._id].status != 'pending':
elif hub.lock_status[self._id].status != 'pending':
_LOGGER.error(
'Unknown lock state %s',
verisure.LOCK_STATUS[self._id].status)
hub.lock_status[self._id].status)
@property
def is_locked(self):
""" True if device is locked. """
return verisure.LOCK_STATUS[self._id].status
return hub.lock_status[self._id].status
def unlock(self, **kwargs):
""" Send unlock command. """
verisure.MY_PAGES.lock.set(kwargs[ATTR_CODE], self._id, 'UNLOCKED')
hub.my_pages.lock.set(kwargs[ATTR_CODE], self._id, 'UNLOCKED')
_LOGGER.info('verisure doorlock unlocking')
verisure.MY_PAGES.lock.wait_while_pending()
verisure.update_lock()
hub.my_pages.lock.wait_while_pending()
self.update()
def lock(self, **kwargs):
""" Send lock command. """
verisure.MY_PAGES.lock.set(kwargs[ATTR_CODE], self._id, 'LOCKED')
hub.my_pages.lock.set(kwargs[ATTR_CODE], self._id, 'LOCKED')
_LOGGER.info('verisure doorlock locking')
verisure.MY_PAGES.lock.wait_while_pending()
verisure.update_lock()
hub.my_pages.lock.wait_while_pending()
self.update()

View file

@ -6,7 +6,7 @@ documentation at https://home-assistant.io/components/verisure/
"""
import logging
import homeassistant.components.verisure as verisure
from homeassistant.components.verisure import HUB as hub
from homeassistant.const import TEMP_CELCIUS
from homeassistant.helpers.entity import Entity
@ -15,32 +15,33 @@ _LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Sets up the Verisure platform."""
if not verisure.MY_PAGES:
_LOGGER.error('A connection has not been made to Verisure mypages.')
return False
sensors = []
sensors.extend([
VerisureThermometer(value)
for value in verisure.CLIMATE_STATUS.values()
if verisure.SHOW_THERMOMETERS and
hasattr(value, 'temperature') and value.temperature
])
if int(hub.config.get('temperature', '1')):
hub.update_climate()
sensors.extend([
VerisureThermometer(value.id)
for value in hub.climate_status.values()
if hasattr(value, 'temperature') and value.temperature
])
sensors.extend([
VerisureHygrometer(value)
for value in verisure.CLIMATE_STATUS.values()
if verisure.SHOW_HYGROMETERS and
hasattr(value, 'humidity') and value.humidity
])
if int(hub.config.get('hygrometers', '1')):
hub.update_climate()
sensors.extend([
VerisureHygrometer(value.id)
for value in hub.climate_status.values()
if hasattr(value, 'humidity') and value.humidity
])
sensors.extend([
VerisureMouseDetection(value)
for value in verisure.MOUSEDETECTION_STATUS.values()
if verisure.SHOW_MOUSEDETECTION and
hasattr(value, 'amountText') and value.amountText
])
if int(hub.config.get('mouse', '1')):
hub.update_mousedetection()
sensors.extend([
VerisureMouseDetection(value.deviceLabel)
for value in hub.mouse_status.values()
# is this if needed?
if hasattr(value, 'amountText') and value.amountText
])
add_devices(sensors)
@ -48,21 +49,21 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
class VerisureThermometer(Entity):
"""Represents a Verisure thermometer."""
def __init__(self, climate_status):
self._id = climate_status.id
def __init__(self, device_id):
self._id = device_id
@property
def name(self):
"""Returns the name of the device."""
return '{} {}'.format(
verisure.CLIMATE_STATUS[self._id].location,
hub.climate_status[self._id].location,
"Temperature")
@property
def state(self):
"""Returns the state of the device."""
# remove ° character
return verisure.CLIMATE_STATUS[self._id].temperature[:-1]
return hub.climate_status[self._id].temperature[:-1]
@property
def unit_of_measurement(self):
@ -71,27 +72,27 @@ class VerisureThermometer(Entity):
def update(self):
"""Update the sensor."""
verisure.update_climate()
hub.update_climate()
class VerisureHygrometer(Entity):
"""Represents a Verisure hygrometer."""
def __init__(self, climate_status):
self._id = climate_status.id
def __init__(self, device_id):
self._id = device_id
@property
def name(self):
"""Returns the name of the sensor."""
return '{} {}'.format(
verisure.CLIMATE_STATUS[self._id].location,
hub.climate_status[self._id].location,
"Humidity")
@property
def state(self):
"""Returns the state of the sensor."""
# remove % character
return verisure.CLIMATE_STATUS[self._id].humidity[:-1]
return hub.climate_status[self._id].humidity[:-1]
@property
def unit_of_measurement(self):
@ -99,27 +100,27 @@ class VerisureHygrometer(Entity):
return "%"
def update(self):
"""Update sensor the sensor."""
verisure.update_climate()
"""Update the sensor."""
hub.update_climate()
class VerisureMouseDetection(Entity):
""" Represents a Verisure mouse detector."""
def __init__(self, mousedetection_status):
self._id = mousedetection_status.deviceLabel
def __init__(self, device_id):
self._id = device_id
@property
def name(self):
"""Returns the name of the sensor."""
return '{} {}'.format(
verisure.MOUSEDETECTION_STATUS[self._id].location,
hub.mouse_status[self._id].location,
"Mouse")
@property
def state(self):
"""Returns the state of the sensor."""
return verisure.MOUSEDETECTION_STATUS[self._id].count
return hub.mouse_status[self._id].count
@property
def unit_of_measurement(self):
@ -128,4 +129,4 @@ class VerisureMouseDetection(Entity):
def update(self):
"""Update the sensor."""
verisure.update_mousedetection()
hub.update_mousedetection()

View file

@ -8,7 +8,7 @@ documentation at https://home-assistant.io/components/verisure/
"""
import logging
import homeassistant.components.verisure as verisure
from homeassistant.components.verisure import HUB as hub
from homeassistant.components.switch import SwitchDevice
_LOGGER = logging.getLogger(__name__)
@ -16,49 +16,43 @@ _LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_devices, discovery_info=None):
""" Sets up the Verisure platform. """
if not verisure.MY_PAGES:
_LOGGER.error('A connection has not been made to Verisure mypages.')
if not int(hub.config.get('smartplugs', '1')):
return False
hub.update_smartplugs()
switches = []
switches.extend([
VerisureSmartplug(value)
for value in verisure.SMARTPLUG_STATUS.values()
if verisure.SHOW_SMARTPLUGS
])
VerisureSmartplug(value.id)
for value in hub.smartplug_status.values()])
add_devices(switches)
class VerisureSmartplug(SwitchDevice):
""" Represents a Verisure smartplug. """
def __init__(self, smartplug_status):
self._id = smartplug_status.id
def __init__(self, device_id):
self._id = device_id
@property
def name(self):
""" Get the name (location) of the smartplug. """
return verisure.SMARTPLUG_STATUS[self._id].location
return hub.smartplug_status[self._id].location
@property
def is_on(self):
""" Returns True if on """
plug_status = verisure.SMARTPLUG_STATUS[self._id].status
return plug_status == 'on'
return hub.smartplug_status[self._id].status == 'on'
def turn_on(self):
""" Set smartplug status on. """
verisure.MY_PAGES.smartplug.set(self._id, 'on')
verisure.MY_PAGES.smartplug.wait_while_updating(self._id, 'on')
verisure.update_smartplug()
hub.my_pages.smartplug.set(self._id, 'on')
hub.my_pages.smartplug.wait_while_updating(self._id, 'on')
self.update()
def turn_off(self):
""" Set smartplug status off. """
verisure.MY_PAGES.smartplug.set(self._id, 'off')
verisure.MY_PAGES.smartplug.wait_while_updating(self._id, 'off')
verisure.update_smartplug()
hub.my_pages.smartplug.set(self._id, 'off')
hub.my_pages.smartplug.wait_while_updating(self._id, 'off')
self.update()
def update(self):
verisure.update_smartplug()
hub.update_smartplugs()

View file

@ -7,6 +7,7 @@ For more details about this component, please refer to the documentation at
https://home-assistant.io/components/verisure/
"""
import logging
import threading
import time
from datetime import timedelta
@ -24,33 +25,11 @@ DISCOVER_SWITCHES = 'verisure.switches'
DISCOVER_ALARMS = 'verisure.alarm_control_panel'
DISCOVER_LOCKS = 'verisure.lock'
DEPENDENCIES = ['alarm_control_panel']
REQUIREMENTS = ['vsure==0.5.1']
REQUIREMENTS = ['vsure==0.6.1']
_LOGGER = logging.getLogger(__name__)
MY_PAGES = None
ALARM_STATUS = {}
SMARTPLUG_STATUS = {}
CLIMATE_STATUS = {}
LOCK_STATUS = {}
MOUSEDETECTION_STATUS = {}
VERISURE_LOGIN_ERROR = None
VERISURE_ERROR = None
SHOW_THERMOMETERS = True
SHOW_HYGROMETERS = True
SHOW_ALARM = True
SHOW_SMARTPLUGS = True
SHOW_LOCKS = True
SHOW_MOUSEDETECTION = True
CODE_DIGITS = 4
# if wrong password was given don't try again
WRONG_PASSWORD_GIVEN = False
MIN_TIME_BETWEEN_REQUESTS = timedelta(seconds=1)
HUB = None
def setup(hass, config):
@ -61,48 +40,19 @@ def setup(hass, config):
_LOGGER):
return False
from verisure import MyPages, LoginError, Error
global SHOW_THERMOMETERS, SHOW_HYGROMETERS,\
SHOW_ALARM, SHOW_SMARTPLUGS, SHOW_LOCKS, SHOW_MOUSEDETECTION,\
CODE_DIGITS
SHOW_THERMOMETERS = int(config[DOMAIN].get('thermometers', '1'))
SHOW_HYGROMETERS = int(config[DOMAIN].get('hygrometers', '1'))
SHOW_ALARM = int(config[DOMAIN].get('alarm', '1'))
SHOW_SMARTPLUGS = int(config[DOMAIN].get('smartplugs', '1'))
SHOW_LOCKS = int(config[DOMAIN].get('locks', '1'))
SHOW_MOUSEDETECTION = int(config[DOMAIN].get('mouse', '1'))
CODE_DIGITS = int(config[DOMAIN].get('code_digits', '4'))
global MY_PAGES
MY_PAGES = MyPages(
config[DOMAIN][CONF_USERNAME],
config[DOMAIN][CONF_PASSWORD])
global VERISURE_LOGIN_ERROR, VERISURE_ERROR
VERISURE_LOGIN_ERROR = LoginError
VERISURE_ERROR = Error
try:
MY_PAGES.login()
except (ConnectionError, Error) as ex:
_LOGGER.error('Could not log in to verisure mypages, %s', ex)
import verisure
global HUB
HUB = VerisureHub(config[DOMAIN], verisure)
if not HUB.login():
return False
update_alarm()
update_climate()
update_smartplug()
update_lock()
update_mousedetection()
# Load components for the devices in the ISY controller that we support
for comp_name, discovery in ((('sensor', DISCOVER_SENSORS),
('switch', DISCOVER_SWITCHES),
('alarm_control_panel', DISCOVER_ALARMS),
('lock', DISCOVER_LOCKS))):
component = get_component(comp_name)
_LOGGER.info(config[DOMAIN])
bootstrap.setup_component(hass, component.DOMAIN, config)
hass.bus.fire(EVENT_PLATFORM_DISCOVERED,
{ATTR_SERVICE: discovery,
ATTR_DISCOVERED: {}})
@ -110,58 +60,113 @@ def setup(hass, config):
return True
def reconnect():
""" Reconnect to verisure mypages. """
try:
time.sleep(1)
MY_PAGES.login()
except VERISURE_LOGIN_ERROR as ex:
_LOGGER.error("Could not login to Verisure mypages, %s", ex)
global WRONG_PASSWORD_GIVEN
WRONG_PASSWORD_GIVEN = True
except (ConnectionError, VERISURE_ERROR) as ex:
_LOGGER.error("Could not login to Verisure mypages, %s", ex)
# pylint: disable=too-many-instance-attributes
class VerisureHub(object):
""" Verisure wrapper class """
MAX_PASSWORD_RETRIES = 2
MIN_TIME_BETWEEN_REQUESTS = 1
@Throttle(MIN_TIME_BETWEEN_REQUESTS)
def update_alarm():
""" Updates the status of alarms. """
update_component(MY_PAGES.alarm.get, ALARM_STATUS)
def __init__(self, domain_config, verisure):
self.alarm_status = {}
self.lock_status = {}
self.climate_status = {}
self.mouse_status = {}
self.smartplug_status = {}
self.config = domain_config
self._verisure = verisure
@Throttle(MIN_TIME_BETWEEN_REQUESTS)
def update_climate():
""" Updates the status of climate sensors. """
update_component(MY_PAGES.climate.get, CLIMATE_STATUS)
self._lock = threading.Lock()
self._password_retries = VerisureHub.MAX_PASSWORD_RETRIES
self._wrong_password_given = False
self._reconnect_timeout = time.time()
@Throttle(MIN_TIME_BETWEEN_REQUESTS)
def update_smartplug():
""" Updates the status of smartplugs. """
update_component(MY_PAGES.smartplug.get, SMARTPLUG_STATUS)
self.my_pages = verisure.MyPages(
domain_config[CONF_USERNAME],
domain_config[CONF_PASSWORD])
def login(self):
""" Login to MyPages """
try:
self.my_pages.login()
except self._verisure.Error as ex:
_LOGGER.error('Could not log in to verisure mypages, %s', ex)
return False
return True
def update_lock():
""" Updates the status of alarms. """
update_component(MY_PAGES.lock.get, LOCK_STATUS)
@Throttle(timedelta(seconds=1))
def update_alarms(self):
""" Updates the status of the alarm. """
self.update_component(
self.my_pages.alarm.get,
self.alarm_status)
@Throttle(timedelta(seconds=1))
def update_locks(self):
""" Updates the status of the alarm. """
self.update_component(
self.my_pages.lock.get,
self.lock_status)
def update_mousedetection():
""" Updates the status of mouse detectors. """
update_component(MY_PAGES.mousedetection.get, MOUSEDETECTION_STATUS)
@Throttle(timedelta(seconds=60))
def update_climate(self):
""" Updates the status of the smartplugs. """
self.update_component(
self.my_pages.climate.get,
self.climate_status)
@Throttle(timedelta(seconds=60))
def update_mousedetection(self):
""" Updates the status of the smartplugs. """
self.update_component(
self.my_pages.mousedetection.get,
self.mouse_status)
def update_component(get_function, status):
""" Updates the status of verisure components. """
if WRONG_PASSWORD_GIVEN:
_LOGGER.error('Wrong password')
return
try:
for overview in get_function():
try:
status[overview.id] = overview
except AttributeError:
status[overview.deviceLabel] = overview
except (ConnectionError, VERISURE_ERROR) as ex:
_LOGGER.error('Caught connection error %s, tries to reconnect', ex)
reconnect()
@Throttle(timedelta(seconds=1))
def update_smartplugs(self):
""" Updates the status of the smartplugs. """
self.update_component(
self.my_pages.smartplug.get,
self.smartplug_status)
def update_component(self, get_function, status):
""" Updates the status of verisure components. """
if self._wrong_password_given:
_LOGGER.error('Wrong password for Verisure, update config')
return
try:
for overview in get_function():
try:
status[overview.id] = overview
except AttributeError:
status[overview.deviceLabel] = overview
except self._verisure.Error as ex:
_LOGGER.error('Caught connection error %s, tries to reconnect', ex)
self.reconnect()
def reconnect(self):
""" Reconnect to verisure mypages. """
if self._reconnect_timeout > time.time():
return
if not self._lock.acquire(blocking=False):
return
try:
self.my_pages.login()
self._password_retries = VerisureHub.MAX_PASSWORD_RETRIES
except self._verisure.LoginError as ex:
_LOGGER.error("Wrong user name or password for Verisure MyPages")
if self._password_retries > 0:
self._password_retries -= 1
self._reconnect_timeout = time.time() + 15 * 60
else:
self._wrong_password_given = True
except self._verisure.MaintenanceError:
self._reconnect_timeout = time.time() + 60
_LOGGER.error("Verisure MyPages down for maintenance")
except self._verisure.Error as ex:
_LOGGER.error("Could not login to Verisure MyPages, %s", ex)
self._reconnect_timeout = time.time() + 5
finally:
self._lock.release()

View file

@ -275,7 +275,7 @@ urllib3
uvcclient==0.8
# homeassistant.components.verisure
vsure==0.5.1
vsure==0.6.1
# homeassistant.components.zigbee
xbee-helper==0.0.6