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

Support binary_sensor and device_tracker for HomeKit #13701

Closed
wants to merge 31 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
05041c9
Merge remote-tracking branch 'home-assistant/dev' into dev
Apr 5, 2018
aaa5e5f
Support binary_sensor and device_tracker for HomeKit
Apr 5, 2018
1e4cd78
Line wrap to 79 chars
Apr 5, 2018
af89ce6
Check 'home' for device_tracker for HomeKit
Apr 5, 2018
cacf0e5
Refine params/const based on advice:
Apr 6, 2018
c72faea
Trim trailing whitespace and align hanging indent
Apr 6, 2018
469b7e9
Trim trailing whitespace
Apr 6, 2018
a80f739
Revise param/const/device_class_key based on advice
Apr 6, 2018
4eb721e
Wrap long line and add a blank line
Apr 6, 2018
d10c6cf
Merge remote-tracking branch 'home-assistant/dev' into Yonsm_dev
Apr 6, 2018
ef448c7
Merge remote-tracking branch 'home-assistant/dev' into Yonsm_dev
Apr 6, 2018
3fe9284
Revise const and coding style
Apr 6, 2018
8548761
Update Homekit to 1.1.9 (#13716)
cdce8p Apr 6, 2018
3394916
Update docstrings (#13720)
fabaff Apr 6, 2018
48fe2d1
Add option to ignore availability in google calendar events (#13714)
cgtobi Apr 6, 2018
c77d013
Allow use of date_string in service call (#13256)
bjw-s Apr 6, 2018
262ea14
Add timeout / debounce (for brightness and others) (#13534)
cdce8p Apr 6, 2018
fdf93d1
added support for smappee water sensors (#12831)
hmn Apr 6, 2018
286476f
Initialise filter_sensor with historical values (#13075)
dgomes Apr 7, 2018
58f3690
Fix Gogogate2 'available' attribute (#13728)
dlbroadfoot Apr 7, 2018
669dbe6
Support binary_sensor and device_tracker for HomeKit
Apr 5, 2018
7fa34fe
Line wrap to 79 chars
Apr 5, 2018
050d0d9
Check 'home' for device_tracker for HomeKit
Apr 5, 2018
44436ce
Refine params/const based on advice:
Apr 6, 2018
8af2099
Trim trailing whitespace and align hanging indent
Apr 6, 2018
43c5ea7
Trim trailing whitespace
Apr 6, 2018
82606ae
Revise param/const/device_class_key based on advice
Apr 6, 2018
260a47d
Wrap long line and add a blank line
Apr 6, 2018
73cac24
Revise const and coding style
Apr 6, 2018
51d8e22
Remove should_callback=False for HAP-python 1.1.9
Apr 7, 2018
4e08e1e
Merge remote-tracking branch 'origin/Yonsm_dev' into Yonsm_dev
Apr 7, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
25 changes: 21 additions & 4 deletions homeassistant/components/calendar/google.py
Expand Up @@ -11,14 +11,15 @@
from homeassistant.components.calendar import CalendarEventDevice
from homeassistant.components.google import (
CONF_CAL_ID, CONF_ENTITIES, CONF_TRACK, TOKEN_FILE,
CONF_IGNORE_AVAILABILITY, CONF_SEARCH,
GoogleCalendarService)
from homeassistant.util import Throttle, dt

_LOGGER = logging.getLogger(__name__)

DEFAULT_GOOGLE_SEARCH_PARAMS = {
'orderBy': 'startTime',
'maxResults': 1,
'maxResults': 5,
'singleEvents': True,
}

Expand All @@ -45,18 +46,22 @@ class GoogleCalendarEventDevice(CalendarEventDevice):
def __init__(self, hass, calendar_service, calendar, data):
"""Create the Calendar event device."""
self.data = GoogleCalendarData(calendar_service, calendar,
data.get('search', None))
data.get(CONF_SEARCH),
data.get(CONF_IGNORE_AVAILABILITY))

super().__init__(hass, data)


class GoogleCalendarData(object):
"""Class to utilize calendar service object to get next event."""

def __init__(self, calendar_service, calendar_id, search=None):
def __init__(self, calendar_service, calendar_id, search,
ignore_availability):
"""Set up how we are going to search the google calendar."""
self.calendar_service = calendar_service
self.calendar_id = calendar_id
self.search = search
self.ignore_availability = ignore_availability
self.event = None

@Throttle(MIN_TIME_BETWEEN_UPDATES)
Expand All @@ -80,5 +85,17 @@ def update(self):
result = events.list(**params).execute()

items = result.get('items', [])
self.event = items[0] if len(items) == 1 else None

new_event = None
for item in items:
if (not self.ignore_availability
and 'transparency' in item.keys()):
if item['transparency'] == 'opaque':
new_event = item
break
else:
new_event = item
break

self.event = new_event
return True
43 changes: 24 additions & 19 deletions homeassistant/components/calendar/services.yaml
@@ -1,21 +1,26 @@
# Describes the format for available calendar services

todoist:
new_task:
description: Create a new task and add it to a project.
fields:
content:
description: The name of the task (Required).
example: Pick up the mail
project:
description: The name of the project this task should belong to. Defaults to Inbox (Optional).
example: Errands
labels:
description: Any labels that you want to apply to this task, separated by a comma (Optional).
example: Chores,Deliveries
priority:
description: The priority of this task, from 1 (normal) to 4 (urgent) (Optional).
example: 2
due_date:
description: The day this task is due, in format YYYY-MM-DD (Optional).
example: "2018-04-01"
todoist_new_task:
description: Create a new task and add it to a project.
fields:
content:
description: The name of the task.
example: Pick up the mail
project:
description: The name of the project this task should belong to. Defaults to Inbox.
example: Errands
labels:
description: Any labels that you want to apply to this task, separated by a comma.
example: Chores,Deliveries
priority:
description: The priority of this task, from 1 (normal) to 4 (urgent).
example: 2
due_date_string:
description: The day this task is due, in natural language.
example: "tomorrow"
due_date_lang:
description: The language of due_date_string.
example: "en"
due_date:
description: The day this task is due, in format YYYY-MM-DD.
example: "2018-04-01"
20 changes: 19 additions & 1 deletion homeassistant/components/calendar/todoist.py
Expand Up @@ -41,6 +41,14 @@
DESCRIPTION = 'description'
# Calendar Platform: Used in the '_get_date()' method
DATETIME = 'dateTime'
# Service Call: When is this task due (in natural language)?
DUE_DATE_STRING = 'due_date_string'
# Service Call: The language of DUE_DATE_STRING
DUE_DATE_LANG = 'due_date_lang'
# Service Call: The available options of DUE_DATE_LANG
DUE_DATE_VALID_LANGS = ['en', 'da', 'pl', 'zh', 'ko', 'de',
'pt', 'ja', 'it', 'fr', 'sv', 'ru',
'es', 'nl']
# Attribute: When is this task due?
# Service Call: When is this task due?
DUE_DATE = 'due_date'
Expand Down Expand Up @@ -83,7 +91,11 @@
vol.Optional(PROJECT_NAME, default='inbox'): vol.All(cv.string, vol.Lower),
vol.Optional(LABELS): cv.ensure_list_csv,
vol.Optional(PRIORITY): vol.All(vol.Coerce(int), vol.Range(min=1, max=4)),
vol.Optional(DUE_DATE): cv.string,

vol.Exclusive(DUE_DATE_STRING, 'due_date'): cv.string,
vol.Optional(DUE_DATE_LANG):
vol.All(cv.string, vol.In(DUE_DATE_VALID_LANGS)),
vol.Exclusive(DUE_DATE, 'due_date'): cv.string,
})

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
Expand Down Expand Up @@ -186,6 +198,12 @@ def handle_new_task(call):
if PRIORITY in call.data:
item.update(priority=call.data[PRIORITY])

if DUE_DATE_STRING in call.data:
item.update(date_string=call.data[DUE_DATE_STRING])

if DUE_DATE_LANG in call.data:
item.update(date_lang=call.data[DUE_DATE_LANG])

if DUE_DATE in call.data:
due_date = dt.parse_datetime(call.data[DUE_DATE])
if due_date is None:
Expand Down
16 changes: 6 additions & 10 deletions homeassistant/components/cover/gogogate2.py
Expand Up @@ -11,7 +11,7 @@
from homeassistant.components.cover import (
CoverDevice, SUPPORT_OPEN, SUPPORT_CLOSE)
from homeassistant.const import (
CONF_USERNAME, CONF_PASSWORD, STATE_CLOSED, STATE_UNKNOWN,
CONF_USERNAME, CONF_PASSWORD, STATE_CLOSED,
CONF_IP_ADDRESS, CONF_NAME)
import homeassistant.helpers.config_validation as cv

Expand Down Expand Up @@ -50,7 +50,6 @@ def setup_platform(hass, config, add_devices, discovery_info=None):

add_devices(MyGogogate2Device(
mygogogate2, door, name) for door in devices)
return

except (TypeError, KeyError, NameError, ValueError) as ex:
_LOGGER.error("%s", ex)
Expand All @@ -60,7 +59,6 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
''.format(ex),
title=NOTIFICATION_TITLE,
notification_id=NOTIFICATION_ID)
return


class MyGogogate2Device(CoverDevice):
Expand All @@ -72,7 +70,7 @@ def __init__(self, mygogogate2, device, name):
self.device_id = device['door']
self._name = name or device['name']
self._status = device['status']
self.available = None
self._available = None

@property
def name(self):
Expand All @@ -97,24 +95,22 @@ def supported_features(self):
@property
def available(self):
"""Could the device be accessed during the last update call."""
return self.available
return self._available

def close_cover(self, **kwargs):
"""Issue close command to cover."""
self.mygogogate2.close_device(self.device_id)
self.schedule_update_ha_state(True)

def open_cover(self, **kwargs):
"""Issue open command to cover."""
self.mygogogate2.open_device(self.device_id)
self.schedule_update_ha_state(True)

def update(self):
"""Update status of cover."""
try:
self._status = self.mygogogate2.get_status(self.device_id)
self.available = True
self._available = True
except (TypeError, KeyError, NameError, ValueError) as ex:
_LOGGER.error("%s", ex)
self._status = STATE_UNKNOWN
self.available = False
self._status = None
self._available = False
47 changes: 24 additions & 23 deletions homeassistant/components/cover/opengarage.py
Expand Up @@ -18,30 +18,31 @@

_LOGGER = logging.getLogger(__name__)

ATTR_DISTANCE_SENSOR = "distance_sensor"
ATTR_DOOR_STATE = "door_state"
ATTR_SIGNAL_STRENGTH = "wifi_signal"
ATTR_DISTANCE_SENSOR = 'distance_sensor'
ATTR_DOOR_STATE = 'door_state'
ATTR_SIGNAL_STRENGTH = 'wifi_signal'

CONF_DEVICEKEY = "device_key"
CONF_DEVICE_ID = 'device_id'
CONF_DEVICE_KEY = 'device_key'

DEFAULT_NAME = 'OpenGarage'
DEFAULT_PORT = 80

STATE_CLOSING = "closing"
STATE_OFFLINE = "offline"
STATE_OPENING = "opening"
STATE_STOPPED = "stopped"
STATE_CLOSING = 'closing'
STATE_OFFLINE = 'offline'
STATE_OPENING = 'opening'
STATE_STOPPED = 'stopped'

STATES_MAP = {
0: STATE_CLOSED,
1: STATE_OPEN
1: STATE_OPEN,
}

COVER_SCHEMA = vol.Schema({
vol.Required(CONF_DEVICEKEY): cv.string,
vol.Required(CONF_DEVICE_KEY): cv.string,
vol.Required(CONF_HOST): cv.string,
vol.Optional(CONF_NAME): cv.string,
vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
vol.Optional(CONF_NAME): cv.string
})

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
Expand All @@ -50,7 +51,7 @@


def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up OpenGarage covers."""
"""Set up the OpenGarage covers."""
covers = []
devices = config.get(CONF_COVERS)

Expand All @@ -59,8 +60,8 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
CONF_NAME: device_config.get(CONF_NAME),
CONF_HOST: device_config.get(CONF_HOST),
CONF_PORT: device_config.get(CONF_PORT),
"device_id": device_config.get(CONF_DEVICE, device_id),
CONF_DEVICEKEY: device_config.get(CONF_DEVICEKEY)
CONF_DEVICE_ID: device_config.get(CONF_DEVICE, device_id),
CONF_DEVICE_KEY: device_config.get(CONF_DEVICE_KEY)
}

covers.append(OpenGarageCover(hass, args))
Expand All @@ -79,8 +80,8 @@ def __init__(self, hass, args):
self.hass = hass
self._name = args[CONF_NAME]
self.device_id = args['device_id']
self._devicekey = args[CONF_DEVICEKEY]
self._state = STATE_UNKNOWN
self._device_key = args[CONF_DEVICE_KEY]
self._state = None
self._state_before_move = None
self.dist = None
self.signal = None
Expand Down Expand Up @@ -138,8 +139,8 @@ def update(self):
try:
status = self._get_status()
if self._name is None:
if status["name"] is not None:
self._name = status["name"]
if status['name'] is not None:
self._name = status['name']
state = STATES_MAP.get(status.get('door'), STATE_UNKNOWN)
if self._state_before_move is not None:
if self._state_before_move != state:
Expand All @@ -152,7 +153,7 @@ def update(self):
self.signal = status.get('rssi')
self.dist = status.get('dist')
self._available = True
except (requests.exceptions.RequestException) as ex:
except requests.exceptions.RequestException as ex:
_LOGGER.error("Unable to connect to OpenGarage device: %(reason)s",
dict(reason=ex))
self._state = STATE_OFFLINE
Expand All @@ -166,15 +167,15 @@ def _get_status(self):
def _push_button(self):
"""Send commands to API."""
url = '{}/cc?dkey={}&click=1'.format(
self.opengarage_url, self._devicekey)
self.opengarage_url, self._device_key)
try:
response = requests.get(url, timeout=10).json()
if response["result"] == 2:
_LOGGER.error("Unable to control %s: device_key is incorrect.",
if response['result'] == 2:
_LOGGER.error("Unable to control %s: Device key is incorrect",
self._name)
self._state = self._state_before_move
self._state_before_move = None
except (requests.exceptions.RequestException) as ex:
except requests.exceptions.RequestException as ex:
_LOGGER.error("Unable to connect to OpenGarage device: %(reason)s",
dict(reason=ex))
self._state = self._state_before_move
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/cover/tahoma.py
Expand Up @@ -16,7 +16,7 @@


def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up Tahoma covers."""
"""Set up the Tahoma covers."""
controller = hass.data[TAHOMA_DOMAIN]['controller']
devices = []
for device in hass.data[TAHOMA_DOMAIN]['devices']['cover']:
Expand Down
4 changes: 3 additions & 1 deletion homeassistant/components/google.py
Expand Up @@ -44,6 +44,7 @@
CONF_TRACK = 'track'
CONF_SEARCH = 'search'
CONF_OFFSET = 'offset'
CONF_IGNORE_AVAILABILITY = 'ignore_availability'

DEFAULT_CONF_TRACK_NEW = True
DEFAULT_CONF_OFFSET = '!!'
Expand Down Expand Up @@ -74,8 +75,9 @@
vol.Required(CONF_NAME): cv.string,
vol.Required(CONF_DEVICE_ID): cv.string,
vol.Optional(CONF_TRACK): cv.boolean,
vol.Optional(CONF_SEARCH): vol.Any(cv.string, None),
vol.Optional(CONF_SEARCH): cv.string,
vol.Optional(CONF_OFFSET): cv.string,
vol.Optional(CONF_IGNORE_AVAILABILITY, default=True): cv.boolean,
})

DEVICE_SCHEMA = vol.Schema({
Expand Down
24 changes: 24 additions & 0 deletions homeassistant/components/history.py
Expand Up @@ -118,6 +118,30 @@ def state_changes_during_period(hass, start_time, end_time=None,
return states_to_json(hass, states, start_time, entity_ids)


def get_last_state_changes(hass, number_of_states, entity_id):
"""Return the last number_of_states."""
from homeassistant.components.recorder.models import States

start_time = dt_util.utcnow()

with session_scope(hass=hass) as session:
query = session.query(States).filter(
(States.last_changed == States.last_updated))

if entity_id is not None:
query = query.filter_by(entity_id=entity_id.lower())

entity_ids = [entity_id] if entity_id is not None else None

states = execute(
query.order_by(States.last_updated.desc()).limit(number_of_states))

return states_to_json(hass, reversed(states),
start_time,
entity_ids,
include_start_time_state=False)


def get_states(hass, utc_point_in_time, entity_ids=None, run=None,
filters=None):
"""Return the states at a specific point in time."""
Expand Down