Skip to content

Commit

Permalink
working on stop events function call
Browse files Browse the repository at this point in the history
  • Loading branch information
jlusiardi committed Apr 11, 2019
1 parent 2cca94d commit 53c1788
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 48 deletions.
43 changes: 40 additions & 3 deletions homekit/controller/ble_impl/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import sys
import uuid
import struct
import threading
from distutils.util import strtobool

from homekit.controller.tools import AbstractPairing
Expand Down Expand Up @@ -84,10 +85,44 @@ def list_pairings(self):
# TODO implementation still missing
pass

def get_events(self, characteristics, callback_fun, max_events=-1, max_seconds=-1):
def get_events(self, characteristics, callback_fun, max_events=-1, max_seconds=-1,
stop_event: threading.Event = None):
"""
This function is called to register for events on characteristics and receive them. Each time events are
received a call back function is invoked. By that the caller gets information about the events.
The characteristics are identified via their proper accessory id (aid) and instance id (iid).
The call back function takes a list of 3-tupels of aid, iid and the value, e.g.:
[(1, 9, 26.1), (1, 10, 30.5)]
If the input contains characteristics without the event permission or any other error, the function will return
a dict containing tupels of aid and iid for each requested characteristic with error. Those who would have
worked are not in the result.
:param characteristics: a list of 2-tuples of accessory id (aid) and instance id (iid)
:param callback_fun: a function that is called each time events were received
:param max_events: number of reported events, default value -1 means unlimited
:param max_seconds: number of seconds to wait for events, default value -1 means unlimited
:param stop_event: a threading.Event instance that when set commands this function to clean up and return
:return: a dict mapping 2-tuples of aid and iid to dicts with status and description, e.g.
{(1, 37): {'description': 'Notification is not supported for characteristic.', 'status': -70406}}
"""
# TODO implementation still missing
pass

def stop_events(self, characteristics):
"""
This functions sets the events to False so that the accessory no longer sends events to the controller
The characteristics are identified via their proper accessory id (aid) and instance id (iid).
:param characteristics: a list of 2-tuples of accessory id (aid) and instance id (iid)
:return: a dict from (aid, iid) onto {status, description}
"""
# TODO implementation still missing
return {}

def identify(self):
"""
This call can be used to trigger the identification of a paired accessory. A successful call should
Expand Down Expand Up @@ -296,14 +331,16 @@ def _find_characteristic_in_pairing_data(self, aid, cid):
return c
return None

def put_characteristics(self, characteristics, do_conversion=False):
def put_characteristics(self, characteristics, do_conversion=False, field='value', default_value=None):
"""
Update the values of writable characteristics. The characteristics have to be identified by accessory id (aid),
instance id (iid). If do_conversion is False (the default), the value must be of proper format for the
characteristic since no conversion is done. If do_conversion is True, the value is converted.
:param characteristics: a list of 3-tupels of accessory id, instance id and the value
:param characteristics: a list of tupels of accessory id, instance id and optional the value
:param do_conversion: select if conversion is done (False is default)
:param field: the name of the field to write the values to
:param default_value: this value is taken if no value was given in the characteristics list
:return: a dict from (aid, iid) onto {status, description}
:raises FormatError: if the input value could not be converted to the target type and conversion was
requested
Expand Down
82 changes: 37 additions & 45 deletions homekit/controller/ip_implementation.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#

import json
import threading
from json.decoder import JSONDecodeError
import time
import logging
Expand Down Expand Up @@ -215,14 +216,16 @@ def get_characteristics(self, characteristics, include_meta=False, include_perms
tmp[key] = c
return tmp

def put_characteristics(self, characteristics, do_conversion=False):
def put_characteristics(self, characteristics, do_conversion=False, field='value', default_value=None):
"""
Update the values of writable characteristics. The characteristics have to be identified by accessory id (aid),
instance id (iid). If do_conversion is False (the default), the value must be of proper format for the
characteristic since no conversion is done. If do_conversion is True, the value is converted.
:param characteristics: a list of 3-tupels of accessory id, instance id and the value
:param characteristics: a list of tupels of accessory id, instance id and optional the value
:param do_conversion: select if conversion is done (False is default)
:param field: the name of the field to write the values to
:param default_value: this value is taken if no value was given in the characteristics list
:return: a dict from (aid, iid) onto {status, description}
:raises FormatError: if the input value could not be converted to the target type and conversion was
requested
Expand All @@ -236,7 +239,13 @@ def put_characteristics(self, characteristics, do_conversion=False):
for characteristic in characteristics:
aid = characteristic[0]
iid = characteristic[1]
value = characteristic[2]
try:
value = characteristic[2]
except IndexError:
if default_value is not None:
value = default_value
else:
raise
if do_conversion:
# evaluate proper format
c_format = None
Expand All @@ -249,7 +258,7 @@ def put_characteristics(self, characteristics, do_conversion=False):

value = check_convert_value(value, c_format)
characteristics_set.add('{a}.{i}'.format(a=aid, i=iid))
data.append({'aid': aid, 'iid': iid, 'value': value})
data.append({'aid': aid, 'iid': iid, field: value})
data = json.dumps({'characteristics': data})

try:
Expand All @@ -273,7 +282,8 @@ def put_characteristics(self, characteristics, do_conversion=False):
return data
return {}

def get_events(self, characteristics, callback_fun, max_events=-1, max_seconds=-1):
def get_events(self, characteristics, callback_fun, max_events=-1, max_seconds=-1,
stop_event: threading.Event = None):
"""
This function is called to register for events on characteristics and receive them. Each time events are
received a call back function is invoked. By that the caller gets information about the events.
Expand All @@ -287,54 +297,25 @@ def get_events(self, characteristics, callback_fun, max_events=-1, max_seconds=-
a dict containing tupels of aid and iid for each requested characteristic with error. Those who would have
worked are not in the result.
:param characteristics: a list of 2-tupels of accessory id (aid) and instance id (iid)
:param callback_fun: a function that is called each time events were recieved
:param characteristics: a list of 2-tuples of accessory id (aid) and instance id (iid)
:param callback_fun: a function that is called each time events were received
:param max_events: number of reported events, default value -1 means unlimited
:param max_seconds: number of seconds to wait for events, default value -1 means unlimited
:return: a dict mapping 2-tupels of aid and iid to dicts with status and description, e.g.
:param stop_event: a threading.Event instance that when set commands this function to clean up and return
:return: a dict mapping 2-tuples of aid and iid to dicts with status and description, e.g.
{(1, 37): {'description': 'Notification is not supported for characteristic.', 'status': -70406}}
"""
if not self.session:
self.session = IpSession(self.pairing_data)
data = []
characteristics_set = set()
for characteristic in characteristics:
aid = characteristic[0]
iid = characteristic[1]
characteristics_set.add('{a}.{i}'.format(a=aid, i=iid))
data.append({'aid': aid, 'iid': iid, 'ev': True})
data = json.dumps({'characteristics': data})

try:
response = self.session.put('/characteristics', data)
except (AccessoryDisconnectedError, EncryptionError):
self.session.close()
self.session = None
raise

# handle error responses
if response.code != 204:
tmp = {}
try:
data = json.loads(response.read().decode())
except JSONDecodeError:
self.session.close()
self.session = None
raise AccessoryDisconnectedError("Session closed after receiving malformed response from device")

for characteristic in data['characteristics']:
status = characteristic['status']
if status == 0:
continue
aid = characteristic['aid']
iid = characteristic['iid']
tmp[(aid, iid)] = {'status': status, 'description': HapStatusCodes[status]}
# register for events
tmp = self.put_characteristics(characteristics, field='ev', default_value=True)
if tmp != {}:
return tmp

# wait for incoming events
event_count = 0
s = time.time()
while (max_events == -1 or event_count < max_events) and (max_seconds == -1 or s + max_seconds >= time.time()):
while ((max_events == -1 or event_count < max_events) and (
max_seconds == -1 or s + max_seconds >= time.time()) and (
stop_event is None or not stop_event.isSet())):
try:
r = self.session.sec_http.handle_event_response()
body = r.read().decode()
Expand All @@ -355,7 +336,18 @@ def get_events(self, characteristics, callback_fun, max_events=-1, max_seconds=-
tmp.append((c['aid'], c['iid'], c['value']))
callback_fun(tmp)
event_count += 1
return {}
return self.stop_events(characteristics)

def stop_events(self, characteristics):
"""
This functions sets the events to False so that the accessory no longer sends events to the controller
The characteristics are identified via their proper accessory id (aid) and instance id (iid).
:param characteristics: a list of 2-tuples of accessory id (aid) and instance id (iid)
:return: a dict from (aid, iid) onto {status, description}
"""
return self.put_characteristics(characteristics, field='ev', default_value=False)

def identify(self):
"""
Expand Down

0 comments on commit 53c1788

Please sign in to comment.