Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add nuki lock'n'go and unlatch services and add attributes #8687

Merged
merged 4 commits into from
Aug 7, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Add lock'n'go service
  • Loading branch information
pschmitt committed Jul 28, 2017
commit 07df9134c52a96197b898785aa1114a082382b88
73 changes: 69 additions & 4 deletions homeassistant/components/lock/nuki.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,42 @@
https://home-assistant.io/components/lock.nuki/
"""
from datetime import timedelta
from os import path
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move this line below import logging.

import asyncio
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move this line above from datatime...

import logging

import voluptuous as vol

import homeassistant.helpers.config_validation as cv
from homeassistant.components.lock import (LockDevice, PLATFORM_SCHEMA)
from homeassistant.const import (CONF_HOST, CONF_PORT, CONF_TOKEN)
from homeassistant.config import load_yaml_config_file
from homeassistant.const import (
ATTR_ENTITY_ID, CONF_HOST, CONF_PORT, CONF_TOKEN)
from homeassistant.helpers.service import extract_entity_ids
from homeassistant.util import Throttle

REQUIREMENTS = ['pynuki==1.2.2']
REQUIREMENTS = ['pynuki==1.3.0']

_LOGGER = logging.getLogger(__name__)

DEFAULT_PORT = 8080

ATTR_BATTERY_CRITICAL = 'battery_critical'
ATTR_NUKI_ID = 'nuki_id'
ATTR_UNLATCH = 'unlatch'
DOMAIN = 'nuki'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Platforms always inherit the domain from their base component, in this case that's lock. You should call this constant something else, like NUKI_DATA. You can import DOMAIN too from homeassistant.components.lock and use that below in the service handler.

SERVICE_LOCK_N_GO = 'nuki_lock_n_go'

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_HOST): cv.string,
vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
vol.Required(CONF_TOKEN): cv.string
})

LOCK_N_GO_SERVICE_SCHEMA = vol.Schema({
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
vol.Optional(ATTR_UNLATCH, default=False): cv.boolean
})

MIN_TIME_BETWEEN_SCANS = timedelta(seconds=30)
MIN_TIME_BETWEEN_FORCED_SCANS = timedelta(seconds=5)
Expand All @@ -36,17 +51,50 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the Nuki lock platform."""
from pynuki import NukiBridge
bridge = NukiBridge(config.get(CONF_HOST), config.get(CONF_TOKEN))
add_devices([NukiLock(lock) for lock in bridge.locks])
add_devices([NukiLock(lock, hass) for lock in bridge.locks])

def lock_n_go(service):
"""Service handler for nuki.lock_n_go."""
unlatch = service.data.get(ATTR_UNLATCH, False)
entity_ids = extract_entity_ids(hass, service)
all_locks = hass.data[DOMAIN]['lock']
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update to use the new name for 'nuki' eg:

all_locks = hass.data[NUKI_DATA][DOMAIN]

target_locks = []
if entity_ids is None:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extract_entity_ids will return an empty list if missing entity_id in service data. It won't return None. Just change to:

if not entity_ids:

target_locks = all_locks
else:
for lock in all_locks:
if lock.entity_id in entity_ids:
target_locks.append(lock)
for lock in target_locks:
lock.lock_n_go(unlatch=unlatch)

descriptions = load_yaml_config_file(
path.join(path.dirname(__file__), 'services.yaml'))

hass.services.register(
DOMAIN, SERVICE_LOCK_N_GO, lock_n_go,
descriptions.get(SERVICE_LOCK_N_GO), schema=LOCK_N_GO_SERVICE_SCHEMA)


class NukiLock(LockDevice):
"""Representation of a Nuki lock."""

def __init__(self, nuki_lock):
def __init__(self, nuki_lock, hass):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't pass in hass. It will be set on the entity when it's added to home assistant.

"""Initialize the lock."""
self.hass = hass
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove.

self._nuki_lock = nuki_lock
self._locked = nuki_lock.is_locked
self._name = nuki_lock.name
self._battery_critical = nuki_lock.battery_critical

@asyncio.coroutine
def async_added_to_hass(self):
"""Callback when entity is added to hass."""
if not DOMAIN in self.hass.data:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

test for membership should be 'not in'

self.hass.data[DOMAIN] = {}
if not 'lock' in self.hass.data[DOMAIN]:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

test for membership should be 'not in'

self.hass.data[DOMAIN]['lock'] = []
self.hass.data[DOMAIN]['lock'].append(self)

@property
def name(self):
Expand All @@ -58,12 +106,21 @@ def is_locked(self):
"""Return true if lock is locked."""
return self._locked

@property
def device_state_attributes(self):
"""Return the device specific state attributes."""
data = {
ATTR_BATTERY_CRITICAL: self._battery_critical,
ATTR_NUKI_ID: self._nuki_lock.nuki_id}
return data

@Throttle(MIN_TIME_BETWEEN_SCANS, MIN_TIME_BETWEEN_FORCED_SCANS)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is old code, but Throttle should only be used in a common data instance method to protect from multiple updates from different entity against a common API. Applying the throttle here will just limit updates for the same NukiLock entity. It will not limit updates from other NukiLock entities during the same time. So for this use case, there's already built in functionality in the EntityComponent class that the lock base component uses. Just define SCAN_INTERVAL in this module if you want to override the default SCAN_INTERVAL = timedelta(seconds=30).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll just remove that. There is no reason to alter the scan interval here.

def update(self):
"""Update the nuki lock properties."""
self._nuki_lock.update(aggressive=False)
self._name = self._nuki_lock.name
self._locked = self._nuki_lock.is_locked
self._battery_critical = self._nuki_lock.battery_critical

def lock(self, **kwargs):
"""Lock the device."""
Expand All @@ -72,3 +129,11 @@ def lock(self, **kwargs):
def unlock(self, **kwargs):
"""Unlock the device."""
self._nuki_lock.unlock()

def lock_n_go(self, **kwargs):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

def lock_n_go(self, unlatch=False, **kwargs):

"""Lock and go.

This will first unlock the door, then wait for 20 seconds (or another
amount of time depending on the lock settings) and relock.
"""
self._nuki_lock.lock_n_go(kwargs)
11 changes: 11 additions & 0 deletions homeassistant/components/lock/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,17 @@ get_usercode:
description: Code slot to retrive a code from
example: 1

nuki_lock_n_go:
description: "Lock 'n' Go"

fields:
entity_id:
description: Entity id of the Nuki lock
example: 'lock.front_door'
unlatch:
description: Whether to unlatch the lock
example: false

lock:
description: Lock all or specified locks

Expand Down
2 changes: 1 addition & 1 deletion requirements_all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -651,7 +651,7 @@ pynetgear==0.3.3
pynetio==0.1.6

# homeassistant.components.lock.nuki
pynuki==1.2.2
pynuki==1.3.0

# homeassistant.components.sensor.nut
pynut2==2.1.2
Expand Down