WIP: Verisure app api (#7394)

update to verisure app api
This commit is contained in:
Per Sandström 2017-06-26 22:30:25 +02:00 committed by GitHub
parent d73b695e73
commit 80c187f8ea
8 changed files with 299 additions and 245 deletions

View File

@ -5,6 +5,7 @@ For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/alarm_control_panel.verisure/ https://home-assistant.io/components/alarm_control_panel.verisure/
""" """
import logging import logging
from time import sleep
import homeassistant.components.alarm_control_panel as alarm import homeassistant.components.alarm_control_panel as alarm
from homeassistant.components.verisure import HUB as hub from homeassistant.components.verisure import HUB as hub
@ -20,20 +21,29 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the Verisure platform.""" """Set up the Verisure platform."""
alarms = [] alarms = []
if int(hub.config.get(CONF_ALARM, 1)): if int(hub.config.get(CONF_ALARM, 1)):
hub.update_alarms() hub.update_overview()
alarms.extend([ alarms.append(VerisureAlarm())
VerisureAlarm(value.id)
for value in hub.alarm_status.values()
])
add_devices(alarms) add_devices(alarms)
def set_arm_state(state, code=None):
"""Send set arm state command."""
transaction_id = hub.session.set_arm_state(code, state)[
'armStateChangeTransactionId']
_LOGGER.info('verisure set arm state %s', state)
transaction = {}
while 'result' not in transaction:
sleep(0.5)
transaction = hub.session.get_arm_state_transaction(transaction_id)
# pylint: disable=unexpected-keyword-arg
hub.update_overview(no_throttle=True)
class VerisureAlarm(alarm.AlarmControlPanel): class VerisureAlarm(alarm.AlarmControlPanel):
"""Representation of a Verisure alarm status.""" """Representation of a Verisure alarm status."""
def __init__(self, device_id): def __init__(self):
"""Initialize the Verisure alarm panel.""" """Initalize the Verisure alarm panel."""
self._id = device_id
self._state = STATE_UNKNOWN self._state = STATE_UNKNOWN
self._digits = hub.config.get(CONF_CODE_DIGITS) self._digits = hub.config.get(CONF_CODE_DIGITS)
self._changed_by = None self._changed_by = None
@ -41,18 +51,13 @@ class VerisureAlarm(alarm.AlarmControlPanel):
@property @property
def name(self): def name(self):
"""Return the name of the device.""" """Return the name of the device."""
return 'Alarm {}'.format(self._id) return '{} alarm'.format(hub.session.installations[0]['alias'])
@property @property
def state(self): def state(self):
"""Return the state of the device.""" """Return the state of the device."""
return self._state return self._state
@property
def available(self):
"""Return True if entity is available."""
return hub.available
@property @property
def code_format(self): def code_format(self):
"""Return the code format as regex.""" """Return the code format as regex."""
@ -65,33 +70,26 @@ class VerisureAlarm(alarm.AlarmControlPanel):
def update(self): def update(self):
"""Update alarm status.""" """Update alarm status."""
hub.update_alarms() hub.update_overview()
status = hub.get_first("$.armState.statusType")
if hub.alarm_status[self._id].status == 'unarmed': if status == 'DISARMED':
self._state = STATE_ALARM_DISARMED self._state = STATE_ALARM_DISARMED
elif hub.alarm_status[self._id].status == 'armedhome': elif status == 'ARMED_HOME':
self._state = STATE_ALARM_ARMED_HOME self._state = STATE_ALARM_ARMED_HOME
elif hub.alarm_status[self._id].status == 'armed': elif status == 'ARMED_AWAY':
self._state = STATE_ALARM_ARMED_AWAY self._state = STATE_ALARM_ARMED_AWAY
elif hub.alarm_status[self._id].status != 'pending': elif status != 'PENDING':
_LOGGER.error( _LOGGER.error('Unknown alarm state %s', status)
"Unknown alarm state %s", hub.alarm_status[self._id].status) self._changed_by = hub.get_first("$.armState.name")
self._changed_by = hub.alarm_status[self._id].name
def alarm_disarm(self, code=None): def alarm_disarm(self, code=None):
"""Send disarm command.""" """Send disarm command."""
hub.my_pages.alarm.set(code, 'DISARMED') set_arm_state('DISARMED', code)
_LOGGER.info("Verisure alarm disarming")
hub.my_pages.alarm.wait_while_pending()
def alarm_arm_home(self, code=None): def alarm_arm_home(self, code=None):
"""Send arm home command.""" """Send arm home command."""
hub.my_pages.alarm.set(code, 'ARMED_HOME') set_arm_state('ARMED_HOME', code)
_LOGGER.info("Verisure alarm arming home")
hub.my_pages.alarm.wait_while_pending()
def alarm_arm_away(self, code=None): def alarm_arm_away(self, code=None):
"""Send arm away command.""" """Send arm away command."""
hub.my_pages.alarm.set(code, 'ARMED_AWAY') set_arm_state('ARMED_AWAY', code)
_LOGGER.info("Verisure alarm arming away")
hub.my_pages.alarm.wait_while_pending()

View File

@ -0,0 +1,59 @@
"""
Interfaces with Verisure sensors.
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/binary_sensor.verisure/
"""
import logging
from homeassistant.components.verisure import HUB as hub
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.components.verisure import CONF_DOOR_WINDOW
_LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Setup Verisure binary sensors."""
sensors = []
hub.update_overview()
if int(hub.config.get(CONF_DOOR_WINDOW, 1)):
sensors.extend([
VerisureDoorWindowSensor(device_label)
for device_label in hub.get(
"$.doorWindow.doorWindowDevice[*].deviceLabel")])
add_devices(sensors)
class VerisureDoorWindowSensor(BinarySensorDevice):
"""Verisure door window sensor."""
def __init__(self, device_label):
"""Initialize the modbus coil sensor."""
self._device_label = device_label
@property
def name(self):
"""Return the name of the binary sensor."""
return hub.get_first(
"$.doorWindow.doorWindowDevice[?(@.deviceLabel=='%s')].area",
self._device_label) + " door window"
@property
def is_on(self):
"""Return the state of the sensor."""
return hub.get_first(
"$.doorWindow.doorWindowDevice[?(@.deviceLabel=='%s')].state",
self._device_label) == "OPEN"
@property
def available(self):
"""Return True if entity is available."""
return hub.get_first(
"$.doorWindow.doorWindowDevice[?(@.deviceLabel=='%s')]",
self._device_label) is not None
def update(self):
"""Update the state of the sensor."""
hub.update_overview()

View File

@ -24,22 +24,23 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
if not os.access(directory_path, os.R_OK): if not os.access(directory_path, os.R_OK):
_LOGGER.error("file path %s is not readable", directory_path) _LOGGER.error("file path %s is not readable", directory_path)
return False return False
hub.update_smartcam() hub.update_overview()
smartcams = [] smartcams = []
smartcams.extend([ smartcams.extend([
VerisureSmartcam(hass, value.deviceLabel, directory_path) VerisureSmartcam(hass, device_label, directory_path)
for value in hub.smartcam_status.values()]) for device_label in hub.get(
"$.customerImageCameras[*].deviceLabel")])
add_devices(smartcams) add_devices(smartcams)
class VerisureSmartcam(Camera): class VerisureSmartcam(Camera):
"""Representation of a Verisure camera.""" """Representation of a Verisure camera."""
def __init__(self, hass, device_id, directory_path): def __init__(self, hass, device_label, directory_path):
"""Initialize Verisure File Camera component.""" """Initialize Verisure File Camera component."""
super().__init__() super().__init__()
self._device_id = device_id self._device_label = device_label
self._directory_path = directory_path self._directory_path = directory_path
self._image = None self._image = None
self._image_id = None self._image_id = None
@ -58,28 +59,27 @@ class VerisureSmartcam(Camera):
def check_imagelist(self): def check_imagelist(self):
"""Check the contents of the image list.""" """Check the contents of the image list."""
hub.update_smartcam_imagelist() hub.update_smartcam_imageseries()
if (self._device_id not in hub.smartcam_dict or image_ids = hub.get_image_info(
not hub.smartcam_dict[self._device_id]): "$.imageSeries[?(@.deviceLabel=='%s')].image[0].imageId",
self._device_label)
if not image_ids:
return return
images = hub.smartcam_dict[self._device_id] new_image_id = image_ids[0]
new_image_id = images[0]
_LOGGER.debug("self._device_id=%s, self._images=%s, "
"self._new_image_id=%s", self._device_id,
images, new_image_id)
if (new_image_id == '-1' or if (new_image_id == '-1' or
self._image_id == new_image_id): self._image_id == new_image_id):
_LOGGER.debug("The image is the same, or loading image_id") _LOGGER.debug("The image is the same, or loading image_id")
return return
_LOGGER.debug("Download new image %s", new_image_id) _LOGGER.debug("Download new image %s", new_image_id)
hub.my_pages.smartcam.download_image( new_image_path = os.path.join(
self._device_id, new_image_id, self._directory_path) self._directory_path, '{}{}'.format(new_image_id, '.jpg'))
hub.session.download_image(
self._device_label, new_image_id, new_image_path)
_LOGGER.debug("Old image_id=%s", self._image_id) _LOGGER.debug("Old image_id=%s", self._image_id)
self.delete_image(self) self.delete_image(self)
self._image_id = new_image_id self._image_id = new_image_id
self._image = os.path.join( self._image = new_image_path
self._directory_path, '{}{}'.format(self._image_id, '.jpg'))
def delete_image(self, event): def delete_image(self, event):
"""Delete an old image.""" """Delete an old image."""
@ -95,4 +95,6 @@ class VerisureSmartcam(Camera):
@property @property
def name(self): def name(self):
"""Return the name of this camera.""" """Return the name of this camera."""
return hub.smartcam_status[self._device_id].location return hub.get_first(
"$.customerImageCameras[?(@.deviceLabel=='%s')].area",
self._device_label) + " camera"

View File

@ -5,7 +5,8 @@ For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/verisure/ https://home-assistant.io/components/verisure/
""" """
import logging import logging
from time import sleep
from time import time
from homeassistant.components.verisure import HUB as hub from homeassistant.components.verisure import HUB as hub
from homeassistant.components.verisure import (CONF_LOCKS, CONF_CODE_DIGITS) from homeassistant.components.verisure import (CONF_LOCKS, CONF_CODE_DIGITS)
from homeassistant.components.lock import LockDevice from homeassistant.components.lock import LockDevice
@ -19,28 +20,32 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the Verisure platform.""" """Set up the Verisure platform."""
locks = [] locks = []
if int(hub.config.get(CONF_LOCKS, 1)): if int(hub.config.get(CONF_LOCKS, 1)):
hub.update_locks() hub.update_overview()
locks.extend([ locks.extend([
VerisureDoorlock(device_id) VerisureDoorlock(device_label)
for device_id in hub.lock_status for device_label in hub.get(
]) "$.doorLockStatusList[*].deviceLabel")])
add_devices(locks) add_devices(locks)
class VerisureDoorlock(LockDevice): class VerisureDoorlock(LockDevice):
"""Representation of a Verisure doorlock.""" """Representation of a Verisure doorlock."""
def __init__(self, device_id): def __init__(self, device_label):
"""Initialize the Verisure lock.""" """Initialize the Verisure lock."""
self._id = device_id self._device_label = device_label
self._state = STATE_UNKNOWN self._state = STATE_UNKNOWN
self._digits = hub.config.get(CONF_CODE_DIGITS) self._digits = hub.config.get(CONF_CODE_DIGITS)
self._changed_by = None self._changed_by = None
self._change_timestamp = 0
@property @property
def name(self): def name(self):
"""Return the name of the lock.""" """Return the name of the lock."""
return '{}'.format(hub.lock_status[self._id].location) return hub.get_first(
"$.doorLockStatusList[?(@.deviceLabel=='%s')].area",
self._device_label) + " lock"
@property @property
def state(self): def state(self):
@ -50,7 +55,9 @@ class VerisureDoorlock(LockDevice):
@property @property
def available(self): def available(self):
"""Return True if entity is available.""" """Return True if entity is available."""
return hub.available return hub.get_first(
"$.doorLockStatusList[?(@.deviceLabel=='%s')]",
self._device_label) is not None
@property @property
def changed_by(self): def changed_by(self):
@ -64,32 +71,52 @@ class VerisureDoorlock(LockDevice):
def update(self): def update(self):
"""Update lock status.""" """Update lock status."""
hub.update_locks() if time() - self._change_timestamp < 10:
return
if hub.lock_status[self._id].status == 'unlocked': hub.update_overview()
status = hub.get_first(
"$.doorLockStatusList[?(@.deviceLabel=='%s')].lockedState",
self._device_label)
if status == 'UNLOCKED':
self._state = STATE_UNLOCKED self._state = STATE_UNLOCKED
elif hub.lock_status[self._id].status == 'locked': elif status == 'LOCKED':
self._state = STATE_LOCKED self._state = STATE_LOCKED
elif hub.lock_status[self._id].status != 'pending': elif status != 'PENDING':
_LOGGER.error( _LOGGER.error('Unknown lock state %s', status)
"Unknown lock state %s", hub.lock_status[self._id].status) self._changed_by = hub.get_first(
self._changed_by = hub.lock_status[self._id].name "$.doorLockStatusList[?(@.deviceLabel=='%s')].userString",
self._device_label)
@property @property
def is_locked(self): def is_locked(self):
"""Return true if lock is locked.""" """Return true if lock is locked."""
return hub.lock_status[self._id].status return self._state == STATE_LOCKED
def unlock(self, **kwargs): def unlock(self, **kwargs):
"""Send unlock command.""" """Send unlock command."""
hub.my_pages.lock.set(kwargs[ATTR_CODE], self._id, 'UNLOCKED') if self._state == STATE_UNLOCKED:
_LOGGER.debug("Verisure doorlock unlocking") return
hub.my_pages.lock.wait_while_pending() self.set_lock_state(kwargs[ATTR_CODE], STATE_UNLOCKED)
self.update()
def lock(self, **kwargs): def lock(self, **kwargs):
"""Send lock command.""" """Send lock command."""
hub.my_pages.lock.set(kwargs[ATTR_CODE], self._id, 'LOCKED') if self._state == STATE_LOCKED:
_LOGGER.debug("Verisure doorlock locking") return
hub.my_pages.lock.wait_while_pending() self.set_lock_state(kwargs[ATTR_CODE], STATE_LOCKED)
self.update()
def set_lock_state(self, code, state):
"""Send set lock state command."""
lock_state = 'lock' if state == STATE_LOCKED else 'unlock'
transaction_id = hub.session.set_lock_state(
code,
self._device_label,
lock_state)['doorLockStateChangeTransactionId']
_LOGGER.debug("Verisure doorlock %s", state)
transaction = {}
while 'result' not in transaction:
sleep(0.5)
transaction = hub.session.get_lock_state_transaction(
transaction_id)
if transaction['result'] == 'OK':
self._state = state
self._change_timestamp = time()

View File

@ -18,31 +18,25 @@ _LOGGER = logging.getLogger(__name__)
def setup_platform(hass, config, add_devices, discovery_info=None): def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the Verisure platform.""" """Set up the Verisure platform."""
sensors = [] sensors = []
hub.update_overview()
if int(hub.config.get(CONF_THERMOMETERS, 1)): if int(hub.config.get(CONF_THERMOMETERS, 1)):
hub.update_climate()
sensors.extend([ sensors.extend([
VerisureThermometer(value.id) VerisureThermometer(device_label)
for value in hub.climate_status.values() for device_label in hub.get(
if hasattr(value, 'temperature') and value.temperature '$.climateValues[?(@.temperature)].deviceLabel')])
])
if int(hub.config.get(CONF_HYDROMETERS, 1)): if int(hub.config.get(CONF_HYDROMETERS, 1)):
hub.update_climate()
sensors.extend([ sensors.extend([
VerisureHygrometer(value.id) VerisureHygrometer(device_label)
for value in hub.climate_status.values() for device_label in hub.get(
if hasattr(value, 'humidity') and value.humidity '$.climateValues[?(@.humidity)].deviceLabel')])
])
if int(hub.config.get(CONF_MOUSE, 1)): if int(hub.config.get(CONF_MOUSE, 1)):
hub.update_mousedetection()
sensors.extend([ sensors.extend([
VerisureMouseDetection(value.deviceLabel) VerisureMouseDetection(device_label)
for value in hub.mouse_status.values() for device_label in hub.get(
# is this if needed? "$.eventCounts[?(@.deviceType=='MOUSE1')].deviceLabel")])
if hasattr(value, 'amountText') and value.amountText
])
add_devices(sensors) add_devices(sensors)
@ -50,26 +44,30 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
class VerisureThermometer(Entity): class VerisureThermometer(Entity):
"""Representation of a Verisure thermometer.""" """Representation of a Verisure thermometer."""
def __init__(self, device_id): def __init__(self, device_label):
"""Initialize the sensor.""" """Initialize the sensor."""
self._id = device_id self._device_label = device_label
@property @property
def name(self): def name(self):
"""Return the name of the device.""" """Return the name of the device."""
return '{} {}'.format( return hub.get_first(
hub.climate_status[self._id].location, 'Temperature') "$.climateValues[?(@.deviceLabel=='%s')].deviceArea",
self._device_label) + " temperature"
@property @property
def state(self): def state(self):
"""Return the state of the device.""" """Return the state of the device."""
# Remove ° character return hub.get_first(
return hub.climate_status[self._id].temperature[:-1] "$.climateValues[?(@.deviceLabel=='%s')].temperature",
self._device_label)
@property @property
def available(self): def available(self):
"""Return True if entity is available.""" """Return True if entity is available."""
return hub.available return hub.get_first(
"$.climateValues[?(@.deviceLabel=='%s')].temperature",
self._device_label) is not None
@property @property
def unit_of_measurement(self): def unit_of_measurement(self):
@ -78,71 +76,80 @@ class VerisureThermometer(Entity):
def update(self): def update(self):
"""Update the sensor.""" """Update the sensor."""
hub.update_climate() hub.update_overview()
class VerisureHygrometer(Entity): class VerisureHygrometer(Entity):
"""Representation of a Verisure hygrometer.""" """Representation of a Verisure hygrometer."""
def __init__(self, device_id): def __init__(self, device_label):
"""Initialize the sensor.""" """Initialize the sensor."""
self._id = device_id self._device_label = device_label
@property @property
def name(self): def name(self):
"""Return the name of the sensor.""" """Return the name of the device."""
return '{} {}'.format( return hub.get_first(
hub.climate_status[self._id].location, 'Humidity') "$.climateValues[?(@.deviceLabel=='%s')].deviceArea",
self._device_label) + " humidity"
@property @property
def state(self): def state(self):
"""Return the state of the sensor.""" """Return the state of the device."""
# remove % character return hub.get_first(
return hub.climate_status[self._id].humidity[:-1] "$.climateValues[?(@.deviceLabel=='%s')].humidity",
self._device_label)
@property @property
def available(self): def available(self):
"""Return True if entity is available.""" """Return True if entity is available."""
return hub.available return hub.get_first(
"$.climateValues[?(@.deviceLabel=='%s')].humidity",
self._device_label) is not None
@property @property
def unit_of_measurement(self): def unit_of_measurement(self):
"""Return the unit of measurement of this sensor.""" """Return the unit of measurement of this entity."""
return "%" return '%'
def update(self): def update(self):
"""Update the sensor.""" """Update the sensor."""
hub.update_climate() hub.update_overview()
class VerisureMouseDetection(Entity): class VerisureMouseDetection(Entity):
"""Representation of a Verisure mouse detector.""" """Representation of a Verisure mouse detector."""
def __init__(self, device_id): def __init__(self, device_label):
"""Initialize the sensor.""" """Initialize the sensor."""
self._id = device_id self._device_label = device_label
@property @property
def name(self): def name(self):
"""Return the name of the sensor.""" """Return the name of the device."""
return '{} {}'.format( return hub.get_first(
hub.mouse_status[self._id].location, 'Mouse') "$.eventCounts[?(@.deviceLabel=='%s')].area",
self._device_label) + " mouse"
@property @property
def state(self): def state(self):
"""Return the state of the sensor.""" """Return the state of the device."""
return hub.mouse_status[self._id].count return hub.get_first(
"$.eventCounts[?(@.deviceLabel=='%s')].detections",
self._device_label)
@property @property
def available(self): def available(self):
"""Return True if entity is available.""" """Return True if entity is available."""
return hub.available return hub.get_first(
"$.eventCounts[?(@.deviceLabel=='%s')]",
self._device_label) is not None
@property @property
def unit_of_measurement(self): def unit_of_measurement(self):
"""Return the unit of measurement of this sensor.""" """Return the unit of measurement of this entity."""
return "Mice" return 'Mice'
def update(self): def update(self):
"""Update the sensor.""" """Update the sensor."""
hub.update_mousedetection() hub.update_overview()

View File

@ -5,6 +5,7 @@ For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/switch.verisure/ https://home-assistant.io/components/switch.verisure/
""" """
import logging import logging
from time import time
from homeassistant.components.verisure import HUB as hub from homeassistant.components.verisure import HUB as hub
from homeassistant.components.verisure import CONF_SMARTPLUGS from homeassistant.components.verisure import CONF_SMARTPLUGS
@ -18,11 +19,11 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
if not int(hub.config.get(CONF_SMARTPLUGS, 1)): if not int(hub.config.get(CONF_SMARTPLUGS, 1)):
return False return False
hub.update_smartplugs() hub.update_overview()
switches = [] switches = []
switches.extend([ switches.extend([
VerisureSmartplug(value.deviceLabel) VerisureSmartplug(device_label)
for value in hub.smartplug_status.values()]) for device_label in hub.get('$.smartPlugs[*].deviceLabel')])
add_devices(switches) add_devices(switches)
@ -31,35 +32,46 @@ class VerisureSmartplug(SwitchDevice):
def __init__(self, device_id): def __init__(self, device_id):
"""Initialize the Verisure device.""" """Initialize the Verisure device."""
self._id = device_id self._device_label = device_id
self._change_timestamp = 0
self._state = False
@property @property
def name(self): def name(self):
"""Return the name or location of the smartplug.""" """Return the name or location of the smartplug."""
return hub.smartplug_status[self._id].location return hub.get_first(
"$.smartPlugs[?(@.deviceLabel == '%s')].area",
self._device_label) + " switch"
@property @property
def is_on(self): def is_on(self):
"""Return true if on.""" """Return true if on."""
return hub.smartplug_status[self._id].status == 'on' if time() - self._change_timestamp < 10:
return self._state
self._state = hub.get_first(
"$.smartPlugs[?(@.deviceLabel == '%s')].currentState",
self._device_label) == "ON"
return self._state
@property @property
def available(self): def available(self):
"""Return True if entity is available.""" """Return True if entity is available."""
return hub.available return hub.get_first(
"$.smartPlugs[?(@.deviceLabel == '%s')]",
self._device_label) is not None
def turn_on(self): def turn_on(self):
"""Set smartplug status on.""" """Set smartplug status on."""
hub.my_pages.smartplug.set(self._id, 'on') hub.session.set_smartplug_state(self._device_label, True)
hub.my_pages.smartplug.wait_while_updating(self._id, 'on') self._state = True
self.update() self._change_timestamp = time()
def turn_off(self): def turn_off(self):
"""Set smartplug status off.""" """Set smartplug status off."""
hub.my_pages.smartplug.set(self._id, 'off') hub.session.set_smartplug_state(self._device_label, False)
hub.my_pages.smartplug.wait_while_updating(self._id, 'off') self._state = False
self.update() self._change_timestamp = time()
def update(self): def update(self):
"""Get the latest date of the smartplug.""" """Get the latest date of the smartplug."""
hub.update_smartplugs() hub.update_overview()

View File

@ -6,19 +6,19 @@ https://home-assistant.io/components/verisure/
""" """
import logging import logging
import threading import threading
import time
import os.path import os.path
from datetime import timedelta from datetime import timedelta
import voluptuous as vol import voluptuous as vol
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME from homeassistant.const import (CONF_PASSWORD, CONF_USERNAME,
EVENT_HOMEASSISTANT_STOP)
from homeassistant.helpers import discovery from homeassistant.helpers import discovery
from homeassistant.util import Throttle from homeassistant.util import Throttle
import homeassistant.config as conf_util import homeassistant.config as conf_util
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
REQUIREMENTS = ['vsure==0.11.1'] REQUIREMENTS = ['vsure==1.3.6', 'jsonpath==0.75']
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -26,6 +26,7 @@ ATTR_DEVICE_SERIAL = 'device_serial'
CONF_ALARM = 'alarm' CONF_ALARM = 'alarm'
CONF_CODE_DIGITS = 'code_digits' CONF_CODE_DIGITS = 'code_digits'
CONF_DOOR_WINDOW = 'door_window'
CONF_HYDROMETERS = 'hygrometers' CONF_HYDROMETERS = 'hygrometers'
CONF_LOCKS = 'locks' CONF_LOCKS = 'locks'
CONF_MOUSE = 'mouse' CONF_MOUSE = 'mouse'
@ -45,6 +46,7 @@ CONFIG_SCHEMA = vol.Schema({
vol.Required(CONF_USERNAME): cv.string, vol.Required(CONF_USERNAME): cv.string,
vol.Optional(CONF_ALARM, default=True): cv.boolean, vol.Optional(CONF_ALARM, default=True): cv.boolean,
vol.Optional(CONF_CODE_DIGITS, default=4): cv.positive_int, vol.Optional(CONF_CODE_DIGITS, default=4): cv.positive_int,
vol.Optional(CONF_DOOR_WINDOW, default=True): cv.boolean,
vol.Optional(CONF_HYDROMETERS, default=True): cv.boolean, vol.Optional(CONF_HYDROMETERS, default=True): cv.boolean,
vol.Optional(CONF_LOCKS, default=True): cv.boolean, vol.Optional(CONF_LOCKS, default=True): cv.boolean,
vol.Optional(CONF_MOUSE, default=True): cv.boolean, vol.Optional(CONF_MOUSE, default=True): cv.boolean,
@ -66,9 +68,12 @@ def setup(hass, config):
HUB = VerisureHub(config[DOMAIN], verisure) HUB = VerisureHub(config[DOMAIN], verisure)
if not HUB.login(): if not HUB.login():
return False return False
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP,
lambda event: HUB.logout())
HUB.update_overview()
for component in ('sensor', 'switch', 'alarm_control_panel', 'lock', for component in ('sensor', 'switch', 'alarm_control_panel', 'lock',
'camera'): 'camera', 'binary_sensor'):
discovery.load_platform(hass, component, DOMAIN, {}, config) discovery.load_platform(hass, component, DOMAIN, {}, config)
descriptions = conf_util.load_yaml_config_file( descriptions = conf_util.load_yaml_config_file(
@ -93,132 +98,73 @@ class VerisureHub(object):
def __init__(self, domain_config, verisure): def __init__(self, domain_config, verisure):
"""Initialize the Verisure hub.""" """Initialize the Verisure hub."""
self.alarm_status = {} self.overview = {}
self.lock_status = {} self.imageseries = {}
self.climate_status = {}
self.mouse_status = {}
self.smartplug_status = {}
self.smartcam_status = {}
self.smartcam_dict = {}
self.config = domain_config self.config = domain_config
self._verisure = verisure self._verisure = verisure
self._lock = threading.Lock() self._lock = threading.Lock()
# When MyPages is brought up from maintenance it sometimes give us a self.session = verisure.Session(
# "wrong password" message. We will continue to retry after maintenance
# regardless of that error.
self._disable_wrong_password_error = False
self._password_retries = 1
self._reconnect_timeout = time.time()
self.my_pages = verisure.MyPages(
domain_config[CONF_USERNAME], domain_config[CONF_USERNAME],
domain_config[CONF_PASSWORD]) domain_config[CONF_PASSWORD])
import jsonpath
self.jsonpath = jsonpath.jsonpath
def login(self): def login(self):
"""Login to Verisure MyPages.""" """Login to Verisure."""
try: try:
self.my_pages.login() self.session.login()
except self._verisure.Error as ex: except self._verisure.Error as ex:
_LOGGER.error("Could not log in to verisure mypages, %s", ex) _LOGGER.error('Could not log in to verisure, %s', ex)
return False return False
return True return True
@Throttle(timedelta(seconds=1)) def logout(self):
def update_alarms(self): """Logout from Verisure."""
"""Update the status of the alarm.""" try:
self.update_component( self.session.logout()
self.my_pages.alarm.get, except self._verisure.Error as ex:
self.alarm_status) _LOGGER.error('Could not log out from verisure, %s', ex)
return False
@Throttle(timedelta(seconds=1)) return True
def update_locks(self):
"""Update the status of the locks."""
self.update_component(
self.my_pages.lock.get,
self.lock_status)
@Throttle(timedelta(seconds=60)) @Throttle(timedelta(seconds=60))
def update_climate(self): def update_overview(self):
"""Update the status of the climate units.""" """Update the overview."""
self.update_component( try:
self.my_pages.climate.get, self.overview = self.session.get_overview()
self.climate_status) except self._verisure.ResponseError as ex:
_LOGGER.error('Could not read overview, %s', ex)
if ex.status_code == 503: # Service unavailable
_LOGGER.info('Trying to log in again')
self.login()
else:
raise
@Throttle(timedelta(seconds=60)) @Throttle(timedelta(seconds=60))
def update_mousedetection(self): def update_smartcam_imageseries(self):
"""Update the status of the mouse detectors.""" """Update the image series."""
self.update_component( self.imageseries = self.session.get_camera_imageseries()
self.my_pages.mousedetection.get,
self.mouse_status)
@Throttle(timedelta(seconds=1))
def update_smartplugs(self):
"""Update the status of the smartplugs."""
self.update_component(
self.my_pages.smartplug.get,
self.smartplug_status)
@Throttle(timedelta(seconds=30))
def update_smartcam(self):
"""Update the status of the smartcam."""
self.update_component(
self.my_pages.smartcam.get,
self.smartcam_status)
@Throttle(timedelta(seconds=30))
def update_smartcam_imagelist(self):
"""Update the imagelist for the camera."""
_LOGGER.debug("Running update imagelist")
self.smartcam_dict = self.my_pages.smartcam.get_imagelist()
_LOGGER.debug("New dict: %s", self.smartcam_dict)
@Throttle(timedelta(seconds=30)) @Throttle(timedelta(seconds=30))
def smartcam_capture(self, device_id): def smartcam_capture(self, device_id):
"""Capture a new image from a smartcam.""" """Capture a new image from a smartcam."""
self.my_pages.smartcam.capture(device_id) self.session.capture_image(device_id)
@property def get(self, jpath, *args):
def available(self): """Get values from the overview that matches the jsonpath."""
"""Return True if hub is available.""" res = self.jsonpath(self.overview, jpath % args)
return self._password_retries >= 0 return res if res else []
def update_component(self, get_function, status): def get_first(self, jpath, *args):
"""Update the status of Verisure components.""" """Get first value from the overview that matches the jsonpath."""
try: res = self.get(jpath, *args)
for overview in get_function(): return res[0] if res else None
try:
status[overview.id] = overview
except AttributeError:
status[overview.deviceLabel] = overview
except self._verisure.Error as ex:
_LOGGER.info("Caught connection error %s, tries to reconnect", ex)
self.reconnect()
def reconnect(self): def get_image_info(self, jpath, *args):
"""Reconnect to Verisure MyPages.""" """Get values from the imageseries that matches the jsonpath."""
if (self._reconnect_timeout > time.time() or res = self.jsonpath(self.imageseries, jpath % args)
not self._lock.acquire(blocking=False) or return res if res else []
self._password_retries < 0):
return
try:
self.my_pages.login()
self._disable_wrong_password_error = False
self._password_retries = 1
except self._verisure.LoginError as ex:
_LOGGER.error("Wrong user name or password for Verisure MyPages")
if self._disable_wrong_password_error:
self._reconnect_timeout = time.time() + 60*60
else:
self._password_retries = self._password_retries - 1
except self._verisure.MaintenanceError:
self._disable_wrong_password_error = True
self._reconnect_timeout = time.time() + 60*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() + 60
finally:
self._lock.release()

View File

@ -327,6 +327,9 @@ insteonlocal==0.52
# homeassistant.components.insteon_plm # homeassistant.components.insteon_plm
insteonplm==0.7.4 insteonplm==0.7.4
# homeassistant.components.verisure
jsonpath==0.75
# homeassistant.components.media_player.kodi # homeassistant.components.media_player.kodi
# homeassistant.components.notify.kodi # homeassistant.components.notify.kodi
jsonrpc-async==0.6 jsonrpc-async==0.6
@ -893,7 +896,7 @@ uvcclient==0.10.0
volvooncall==0.3.3 volvooncall==0.3.3
# homeassistant.components.verisure # homeassistant.components.verisure
vsure==0.11.1 vsure==1.3.6
# homeassistant.components.sensor.vasttrafik # homeassistant.components.sensor.vasttrafik
vtjp==0.1.14 vtjp==0.1.14