Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 commits
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
1 change: 1 addition & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,7 @@ omit =
homeassistant/components/light/yeelightsunflower.py
homeassistant/components/light/zengge.py
homeassistant/components/lirc.py
homeassistant/components/lock/kiwi.py
homeassistant/components/lock/lockitron.py
homeassistant/components/lock/nello.py
homeassistant/components/lock/nuki.py
Expand Down
103 changes: 103 additions & 0 deletions homeassistant/components/lock/kiwi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
"""
Support for the KIWI.KI lock platform.

For more details about this platform, please refer to the documentation
https://home-assistant.io/components/lock.kiwi/
"""
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_PASSWORD, CONF_USERNAME, ATTR_ID, ATTR_LONGITUDE, ATTR_LATITUDE,
STATE_LOCKED, STATE_UNLOCKED)
from homeassistant.helpers.event import async_call_later
from homeassistant.core import callback

REQUIREMENTS = ['kiwiki-client==0.1']

_LOGGER = logging.getLogger(__name__)

ATTR_TYPE = 'hardware_type'
ATTR_PERMISSION = 'permission'
ATTR_CAN_INVITE = 'can_invite_others'

UNLOCK_MAINTAIN_TIME = 5

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_USERNAME): cv.string,
vol.Required(CONF_PASSWORD): cv.string
})


def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the KIWI lock platform."""
from kiwiki import KiwiClient, KiwiException
try:
kiwi = KiwiClient(config[CONF_USERNAME], config[CONF_PASSWORD])
except KiwiException as e:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Linter is failing here. Please use eg exc instead of e.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

that's sad.... 😢

_LOGGER.error(e.msg)
return False

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Don't return False. Just return.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

That is horrible to read. I prefer return False - there is no room for misinterpretation.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nothing is checking the return value.

@c7h c7h Jun 2, 2018

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Check the platform example -
False is returned here on login failed. It makes sense even if its not checked because it improves the readability. https://developers.home-assistant.io/docs/en/creating_platform_example_light.html

@MartinHjelmare MartinHjelmare Jun 2, 2018

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

That example needs updating.

It doesn't improve readability. It gives the wrong impression what happens when the function returns.

It also makes the return inconsistent.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Ok, I created an Issue (home-assistant/developers.home-assistant#21) and will change it to return

add_devices([KiwiLock(lock, kiwi) for lock in kiwi.get_locks()], True)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

A log entry that there are no locks found would help the users. Also, the setup can be terminated if there are no locks.



class KiwiLock(LockDevice):
"""Representation of a Kiwi lock."""

def __init__(self, kiwi_lock, client):
"""Initialize the lock."""
self._sensor = kiwi_lock
self._client = client
self.lock_id = kiwi_lock['sensor_id']
self._state = STATE_LOCKED

address = kiwi_lock.get('address')
address.update({
ATTR_LATITUDE: address.pop('lat', None),
ATTR_LONGITUDE: address.pop('lng', None)
})

self._device_attrs = {
ATTR_ID: self.lock_id,
ATTR_TYPE: kiwi_lock.get('hardware_type'),
ATTR_PERMISSION: kiwi_lock.get('highest_permission'),
ATTR_CAN_INVITE: kiwi_lock.get('can_invite'),
**address
}

@property
def name(self):
"""Return the name of the lock."""
name = self._sensor.get('name')
specifier = self._sensor['address'].get('specifier')
return name or specifier

@property
def is_locked(self):
"""Return true if lock is locked."""
return self._state == STATE_LOCKED

@property
def device_state_attributes(self):
"""Return the device specific state attributes."""
return self._device_attrs

@callback
def clear_unlock_state(self, _):
"""Clear unlock state automatically."""
self._state = STATE_LOCKED
self.async_schedule_update_ha_state()

def unlock(self, **kwargs):
"""Unlock the device."""
from kiwiki import KiwiException
try:
self._client.open_door(self.lock_id)
except KiwiException:
_LOGGER.error("failed to open door")
else:
self._state = STATE_UNLOCKED
async_call_later(self.hass, UNLOCK_MAINTAIN_TIME,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is a callback so have to be run from within the event loop. Use hass.add_job to schedule async_call_later on the event loop.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

doesn't async_call_later already do that for you?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

async_call_later schedules the passed in callback yes, but async_call_later itself needs to be called from within the event loop. The unlock method here will not be run within the event loop but in a worker thread. So to make sure that async_call_later is called from the event loop, we need to use hass.add_job.

self.hass.add_job(
    async_call_later, self.hass, UNLOCK_MAINTAIN_TIME, self.clear_unlock_state)

self.clear_unlock_state)
3 changes: 3 additions & 0 deletions requirements_all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,9 @@ keyring==12.2.0
# homeassistant.scripts.keyring
keyrings.alt==3.1

# homeassistant.components.lock.kiwi
kiwiki-client==0.1

# homeassistant.components.konnected
konnected==0.1.2

Expand Down