Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
24 changes: 19 additions & 5 deletions homekit/controller/ble_impl/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -373,15 +373,29 @@ def put_characteristics(self, characteristics, do_conversion=False):
}
continue

value = tlv8.encode([
value = [
tlv8.Entry(AdditionalParameterTypes.Value, self._convert_from_python(aid, cid, value))
])
body = len(value).to_bytes(length=2, byteorder='little') + value
]

pdu_ty = HapBleOpCodes.CHAR_WRITE
fc, fc_info = self.session.find_characteristic_by_iid(cid)
if fc_info and ('tw' in fc_info['perms']):
# add the TTL-field, see 7.3.4.8
pdu_ty = HapBleOpCodes.CHAR_TIMED_WRITE
value.insert(0, tlv8.Entry(AdditionalParameterTypes.TTL, 11)) # TTL for 1.1 s

tlv_value = tlv8.encode(value)
body = len(tlv_value).to_bytes(length=2, byteorder='little') + tlv_value

try:
fc, fc_info = self.session.find_characteristic_by_iid(cid)
response = self.session.request(fc, cid, HapBleOpCodes.CHAR_WRITE, body)
response = self.session.request(fc, cid, pdu_ty, body)
logger.debug('response %s', tlv8.format_string(response))

if HapBleOpCodes.CHAR_TIMED_WRITE == pdu_ty:
# execute necessary, see 7.3.5.4
response = self.session.request(fc, cid, HapBleOpCodes.CHAR_EXEC_WRITE)
logger.debug('response %s', tlv8.format_string(response))

# TODO does the response contain useful information here?
except RequestRejected as e:
results[(aid, cid)] = {
Expand Down
18 changes: 17 additions & 1 deletion homekit/controller/ip_implementation.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import time
import logging
import tlv8
import random

from homekit.controller.tools import AbstractPairing, check_convert_value
from homekit.protocol.statuscodes import HapStatusCodes
Expand Down Expand Up @@ -273,6 +274,7 @@ def put_characteristics(self, characteristics, do_conversion=False):
self.list_accessories_and_characteristics()
data = []
characteristics_set = set()
request_timed_write = False
for characteristic in characteristics:
aid = characteristic[0]
iid = characteristic[1]
Expand All @@ -286,13 +288,27 @@ def put_characteristics(self, characteristics, do_conversion=False):
for c in s['characteristics']:
if 'iid' in c and c['iid'] == iid:
c_format = c['format']
request_timed_write |= 'perms' in c and 'tw' in c['perms']

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 = _dump_json({'characteristics': data})

data_map = {'characteristics': data}
if request_timed_write:
# add random pid as specified in 6.7.2.4
data_map['pid'] = random.randrange(0, 999999999)

data = _dump_json(data_map)

try:
if request_timed_write:
# perform write request as specified in 6.7.2.4
data_tw = _dump_json({'ttl': 2500, 'pid': data_map['pid']})
response = self.session.put('/prepare', data_tw)
if response.code != 200:
return {}

response = self.session.put('/characteristics', data)
except (AccessoryDisconnectedError, EncryptionError):
self.session.close()
Expand Down
8 changes: 7 additions & 1 deletion homekit/model/characteristics/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@
'SaturationCharacteristicMixin', 'SerialNumberCharacteristic', 'TargetHeatingCoolingStateCharacteristic',
'TargetHeatingCoolingStateCharacteristicMixin', 'TargetTemperatureCharacteristic',
'TargetTemperatureCharacteristicMixin', 'TemperatureDisplayUnitCharacteristic', 'TemperatureDisplayUnitsMixin',
'VolumeCharacteristic', 'VolumeCharacteristicMixin', 'CharacteristicsDecoderLoader'
'VolumeCharacteristic', 'VolumeCharacteristicMixin', 'CharacteristicsDecoderLoader',
'LockMechanismCurrentStateCharacteristic', 'LockMechanismCurrentStateCharacteristicMixin',
'LockMechanismTargetStateCharacteristic', 'LockMechanismTargetStateCharacteristicMixin'
]

import importlib
Expand All @@ -49,6 +51,10 @@
from homekit.model.characteristics.hardware_revision import HardwareRevisionCharacteristic
from homekit.model.characteristics.hue import HueCharacteristicMixin, HueCharacteristic
from homekit.model.characteristics.identify import IdentifyCharacteristic
from homekit.model.characteristics.lockmechanism_current_state import LockMechanismCurrentStateCharacteristic, \
LockMechanismCurrentStateCharacteristicMixin
from homekit.model.characteristics.lockmechanism_target_state import LockMechanismTargetStateCharacteristic, \
LockMechanismTargetStateCharacteristicMixin
from homekit.model.characteristics.manufacturer import ManufacturerCharacteristic
from homekit.model.characteristics.model import ModelCharacteristic
from homekit.model.characteristics.name import NameCharacteristic
Expand Down
45 changes: 45 additions & 0 deletions homekit/model/characteristics/lockmechanism_current_state.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#
# Copyright 2022 Robert Schulze
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

from homekit.model.characteristics import AbstractCharacteristic, CharacteristicFormats, CharacteristicPermissions, \
CharacteristicsTypes


class LockMechanismCurrentStateCharacteristic(AbstractCharacteristic):
"""
Described in section 9.52
"""

def __init__(self, iid):
AbstractCharacteristic.__init__(self,
iid,
CharacteristicsTypes.LOCK_MECHANISM_CURRENT_STATE,
CharacteristicFormats.uint8)
self.perms = [CharacteristicPermissions.paired_read, CharacteristicPermissions.events]
self.description = 'Current state of the lock mechanism'
self.minValue = 0
self.maxValue = 3
self.minStep = 1
self.value = 0


class LockMechanismCurrentStateCharacteristicMixin(object):
def __init__(self, iid):
self._lockMechanismCurrentStateCharacteristic = LockMechanismCurrentStateCharacteristic(iid)
self.characteristics.append(self._lockMechanismCurrentStateCharacteristic)

def set_lockmechanism_current_state_get_callback(self, callback):
self._lockMechanismCurrentStateCharacteristic.set_get_value_callback(callback)
49 changes: 49 additions & 0 deletions homekit/model/characteristics/lockmechanism_target_state.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#
# Copyright 2022 Robert Schulze
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

from homekit.model.characteristics import AbstractCharacteristic, CharacteristicFormats, CharacteristicPermissions, \
CharacteristicsTypes


class LockMechanismTargetStateCharacteristic(AbstractCharacteristic):
"""
Described in section 9.56
"""

def __init__(self, iid):
AbstractCharacteristic.__init__(self,
iid,
CharacteristicsTypes.LOCK_MECHANISM_TARGET_STATE,
CharacteristicFormats.uint8)
self.perms = [CharacteristicPermissions.paired_read, CharacteristicPermissions.paired_write,
CharacteristicPermissions.events, CharacteristicPermissions.timed_write]
self.description = 'Target state of the lock mechanism'
self.minValue = 0
self.maxValue = 1
self.minStep = 1
self.value = 0


class LockMechanismTargetStateCharacteristicMixin(object):
def __init__(self, iid):
self._lockMechanismTargetStateCharacteristic = LockMechanismTargetStateCharacteristic(iid)
self.characteristics.append(self._lockMechanismTargetStateCharacteristic)

def set_lockmechanism_current_state_get_callback(self, callback):
self._lockMechanismTargetStateCharacteristic.set_get_value_callback(callback)

def set_lockmechanism_current_state_set_callback(self, callback):
self._lockMechanismTargetStateCharacteristic.set_set_value_callback(callback)
3 changes: 2 additions & 1 deletion homekit/model/services/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

__all__ = [
'ThermostatService', 'LightBulbService', 'FanService', 'BHSLightBulbService', 'AccessoryInformationService',
'OutletService', 'AbstractService', 'ServicesTypes'
'OutletService', 'AbstractService', 'ServicesTypes', 'LockMechanismService'
]

from homekit.model.services.service_types import ServicesTypes
Expand All @@ -26,5 +26,6 @@
from homekit.model.services.bhslightbulb_service import BHSLightBulbService
from homekit.model.services.fan_service import FanService
from homekit.model.services.lightbulb_service import LightBulbService
from homekit.model.services.lockmechanism_service import LockMechanismService
from homekit.model.services.outlet_service import OutletService
from homekit.model.services.thermostat_service import ThermostatService
32 changes: 32 additions & 0 deletions homekit/model/services/lockmechanism_service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#
# Copyright 2018 Joachim Lusiardi
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

from homekit.model import get_id
from homekit.model.characteristics import LockMechanismCurrentStateCharacteristicMixin, \
LockMechanismTargetStateCharacteristicMixin
from homekit.model.services import ServicesTypes, AbstractService


class LockMechanismService(AbstractService, LockMechanismCurrentStateCharacteristicMixin,
LockMechanismTargetStateCharacteristicMixin):
"""
Descripted in section 8.26
"""

def __init__(self):
AbstractService.__init__(self, ServicesTypes.get_uuid('public.hap.service.lock-mechanism'), get_id())
LockMechanismCurrentStateCharacteristicMixin.__init__(self, get_id())
LockMechanismTargetStateCharacteristicMixin.__init__(self, get_id())
1 change: 1 addition & 0 deletions homekit/model/services/service_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class _ServicesTypes(object):
PAIRING_SERVICE = '55' # new for ble, homekit spec page 57
ACCESSORY_INFORMATION_SERVICE = '3E'
BATTERY_SERVICE = '96'
LOCK_MECHANISM = '45'

def __init__(self):
self.baseUUID = '-0000-1000-8000-0026BB765291'
Expand Down
Loading