| # |
| # Copyright (c) 2021 Project CHIP Authors |
| # All rights reserved. |
| # |
| # 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 asyncio.futures import Future |
| import ctypes |
| from dataclasses import dataclass |
| from typing import Tuple, Type, Union, List, Any, Callable |
| from ctypes import CFUNCTYPE, c_char_p, c_size_t, c_void_p, c_uint64, c_uint32, c_uint16, c_uint8, py_object, c_uint64 |
| |
| from .ClusterObjects import Cluster, ClusterAttributeDescriptor, ClusterEventDescriptor |
| import chip.exceptions |
| import chip.interaction_model |
| import chip.tlv |
| from enum import Enum, unique |
| import inspect |
| import sys |
| import logging |
| import threading |
| import builtins |
| |
| |
| @unique |
| class EventTimestampType(Enum): |
| SYSTEM = 1 |
| EPOCH = 2 |
| |
| |
| @unique |
| class EventPriority(Enum): |
| DEBUG = 1 |
| INFO = 2 |
| CRITICAL = 3 |
| |
| |
| @dataclass |
| class AttributePath: |
| EndpointId: int = None |
| ClusterId: int = None |
| AttributeId: int = None |
| |
| def __init__(self, EndpointId: int = None, Cluster=None, Attribute=None, ClusterId=None, AttributeId=None): |
| self.EndpointId = EndpointId |
| if Cluster is not None: |
| # Wildcard read for a specific cluster |
| if (Attribute is not None) or (ClusterId is not None) or (AttributeId is not None): |
| raise Warning( |
| "Attribute, ClusterId and AttributeId is ignored when Cluster is specified") |
| self.ClusterId = Cluster.id |
| return |
| if Attribute is not None: |
| if (ClusterId is not None) or (AttributeId is not None): |
| raise Warning( |
| "ClusterId and AttributeId is ignored when Attribute is specified") |
| self.ClusterId = Attribute.cluster_id |
| self.AttributeId = Attribute.attribute_id |
| return |
| self.ClusterId = ClusterId |
| self.AttributeId = AttributeId |
| |
| def __str__(self) -> str: |
| return f"{self.EndpointId}/{self.ClusterId}/{self.AttributeId}" |
| |
| def __hash__(self): |
| return str(self).__hash__() |
| |
| |
| @dataclass |
| class EventPath: |
| EndpointId: int = None |
| ClusterId: int = None |
| EventId: int = None |
| |
| def __init__(self, EndpointId: int = None, Cluster=None, Event=None, ClusterId=None, EventId=None): |
| self.EndpointId = EndpointId |
| if Cluster is not None: |
| # Wildcard read for a specific cluster |
| if (Event is not None) or (ClusterId is not None) or (EventId is not None): |
| raise Warning( |
| "Event, ClusterId and AttributeId is ignored when Cluster is specified") |
| self.ClusterId = Cluster.id |
| return |
| if Event is not None: |
| if (ClusterId is not None) or (EventId is not None): |
| raise Warning( |
| "ClusterId and EventId is ignored when Event is specified") |
| self.ClusterId = Event.cluster_id |
| self.EventId = Event.event_id |
| return |
| self.ClusterId = ClusterId |
| self.EventId = EventId |
| |
| def __str__(self) -> str: |
| return f"{self.EndpointId}/{self.EventId}/{self.EventId}" |
| |
| def __hash__(self): |
| return str(self).__hash__() |
| |
| |
| @dataclass |
| class AttributePathWithListIndex(AttributePath): |
| ListIndex: int = None |
| |
| |
| @dataclass |
| class EventHeader: |
| EndpointId: int = None |
| Event: ClusterEventDescriptor = None |
| EventNumber: int = None |
| Priority: EventPriority = None |
| Timestamp: int = None |
| TimestampType: EventTimestampType = None |
| |
| def __init__(self, EndpointId: int = None, Event=None, EventNumber=None, Priority=None, Timestamp=None, TimestampType=None): |
| self.EndpointId = EndpointId |
| self.Event = Event |
| self.EventNumber = EventNumber |
| self.Priority = Priority |
| self.Timestamp = Timestamp |
| self.Timestamp = TimestampType |
| |
| def __str__(self) -> str: |
| return f"{self.EndpointId}/{self.Event.cluster_id}/{self.Event.event_id}/{self.EventNumber}/{self.Priority}/{self.Timestamp}/{self.TimestampType}" |
| |
| |
| @dataclass |
| class AttributeStatus: |
| Path: AttributePath |
| Status: Union[chip.interaction_model.Status, int] |
| |
| |
| @dataclass |
| class EventStatus: |
| Header: EventHeader |
| Status: chip.interaction_model.Status |
| |
| |
| AttributeWriteResult = AttributeStatus |
| |
| |
| @dataclass |
| class AttributeDescriptorWithEndpoint: |
| EndpointId: int |
| Attribute: ClusterAttributeDescriptor |
| |
| |
| @dataclass |
| class EventDescriptorWithEndpoint: |
| EndpointId: int |
| Event: ClusterEventDescriptor |
| |
| |
| @dataclass |
| class AttributeWriteRequest(AttributeDescriptorWithEndpoint): |
| Data: Any |
| |
| |
| AttributeReadRequest = AttributeDescriptorWithEndpoint |
| EventReadRequest = EventDescriptorWithEndpoint |
| |
| |
| @dataclass |
| class AttributeReadResult(AttributeStatus): |
| Data: Any = None |
| |
| |
| @dataclass |
| class ValueDecodeFailure: |
| ''' Encapsulates a failure to decode a TLV value into a cluster object. |
| Some exceptions have custom fields, so run str(ReasonException) to get more info. |
| ''' |
| |
| TLVValue: Any = None |
| Reason: Exception = None |
| |
| |
| class EventReadResult(EventStatus): |
| Data: Any = None |
| |
| |
| _AttributeIndex = {} |
| _EventIndex = {} |
| |
| |
| def _BuildAttributeIndex(): |
| ''' Build internal attribute index for locating the corresponding cluster object by path in the future. |
| We do this because this operation will take a long time when there are lots of attributes, it takes about 300ms for a single query. |
| This is acceptable during init, but unacceptable when the server returns lots of attributes at the same time. |
| ''' |
| for clusterName, obj in inspect.getmembers(sys.modules['chip.clusters.Objects']): |
| if ('chip.clusters.Objects' in str(obj)) and inspect.isclass(obj): |
| for objName, subclass in inspect.getmembers(obj): |
| if inspect.isclass(subclass) and (('Attributes') in str(subclass)): |
| for attributeName, attribute in inspect.getmembers(subclass): |
| if inspect.isclass(attribute): |
| base_classes = inspect.getmro(attribute) |
| |
| # Only match on classes that extend the ClusterAttributeDescriptor class |
| matched = [ |
| value for value in base_classes if 'ClusterAttributeDescriptor' in str(value)] |
| if (matched == []): |
| continue |
| |
| _AttributeIndex[str(AttributePath(ClusterId=attribute.cluster_id, AttributeId=attribute.attribute_id))] = eval( |
| 'chip.clusters.Objects.' + clusterName + '.Attributes.' + attributeName) |
| |
| |
| def _on_update_noop(path: AttributePath, value: Any): |
| ''' |
| Default OnUpdate callback, simplily does nothing. |
| ''' |
| pass |
| |
| |
| @dataclass |
| class SubscriptionParameters: |
| MinReportIntervalFloorSeconds: int |
| MaxReportIntervalCeilingSeconds: int |
| |
| |
| class SubscriptionTransaction: |
| def __init__(self, transaction: 'AsyncReadTransaction', subscriptionId, devCtrl): |
| self._on_update = _on_update_noop |
| self._read_transaction = transaction |
| self._subscriptionId = subscriptionId |
| self._devCtrl = devCtrl |
| |
| def GetAttributeValue(self, path: Tuple[int, Type[ClusterAttributeDescriptor]]): |
| ''' |
| Gets the attribute from cache, returns the value and the timestamp when it was updated last time. |
| ''' |
| return self._read_transaction.GetAttributeValue(AttributePath(path[0], Attribute=path[1])) |
| |
| def GetAllAttributeValues(self): |
| return self._read_transaction.GetAllAttributeValues() |
| |
| def GetAllEventValues(self): |
| return self._read_transaction.GetAllEventValues() |
| |
| def SetAttributeUpdateCallback(self, callback: Callable[[AttributePath, Any], None]): |
| ''' |
| Sets the callback function for the attribute value change event, accepts a Callable accpets an attribute path and its updated value. |
| ''' |
| if callback is None: |
| self._on_update = _on_update_noop |
| else: |
| self._on_update = callback |
| |
| @property |
| def OnUpdate(self) -> Callable[[AttributePath, Any], None]: |
| return self._on_update |
| |
| def Shutdown(self): |
| self._devCtrl.ZCLShutdownSubscription(self._subscriptionId) |
| |
| def __repr__(self): |
| return f'<Subscription (Id={self._subscriptionId})>' |
| |
| |
| def _BuildEventIndex(): |
| ''' Build internal event index for locating the corresponding cluster object by path in the future. |
| We do this because this operation will take a long time when there are lots of events, it takes about 300ms for a single query. |
| This is acceptable during init, but unacceptable when the server returns lots of events at the same time. |
| ''' |
| for clusterName, obj in inspect.getmembers(sys.modules['chip.clusters.Objects']): |
| if ('chip.clusters.Objects' in str(obj)) and inspect.isclass(obj): |
| for objName, subclass in inspect.getmembers(obj): |
| if inspect.isclass(subclass) and (('Events') in str(subclass)): |
| for eventName, event in inspect.getmembers(subclass): |
| if inspect.isclass(event): |
| base_classes = inspect.getmro(event) |
| |
| # Only match on classes that extend the ClusterEventescriptor class |
| matched = [ |
| value for value in base_classes if 'ClusterEventDescriptor' in str(value)] |
| if (matched == []): |
| continue |
| |
| _EventIndex[str(EventPath(ClusterId=event.cluster_id, EventId=event.event_id))] = eval( |
| 'chip.clusters.Objects.' + clusterName + '.Events.' + eventName) |
| |
| |
| class AsyncReadTransaction: |
| def __init__(self, future: Future, eventLoop, devCtrl): |
| self._event_loop = eventLoop |
| self._future = future |
| self._subscription_handler = None |
| self._res = {'Attributes': {}, 'Events': []} |
| self._devCtrl = devCtrl |
| # For subscriptions, the data comes from CHIP Thread, whild the value will be accessed from Python's thread, so a lock is required here. |
| self._resLock = threading.Lock() |
| |
| def GetAttributeValue(self, path: AttributePath): |
| with self._resLock: |
| return self._res['Attributes'].get(path) |
| |
| def GetAllAttributeValues(self): |
| return self._res['Attributes'] |
| |
| def GetAllEventValues(self): |
| return self._res['Events'] |
| |
| def _handleAttributeData(self, path: AttributePathWithListIndex, status: int, data: bytes): |
| try: |
| imStatus = status |
| try: |
| imStatus = chip.interaction_model.Status(status) |
| except: |
| pass |
| |
| if (imStatus != chip.interaction_model.Status.Success): |
| logging.error( |
| f"For path: Endpoint = {path.EndpointId}/Cluster = {path.ClusterId}/Attribute = {path.AttributeId}, got IM Error: {str(imStatus)}") |
| attributeValue = None |
| else: |
| attributeType = _AttributeIndex.get(str(AttributePath( |
| ClusterId=path.ClusterId, AttributeId=path.AttributeId)), None) |
| attributeValue = None |
| tlvData = chip.tlv.TLVReader(data).get().get("Any", {}) |
| if attributeType is None: |
| attributeValue = ValueDecodeFailure( |
| tlvData, LookupError("attribute schema not found")) |
| else: |
| try: |
| attributeValue = attributeType( |
| attributeType.FromTLV(data)) |
| except Exception as ex: |
| logging.error( |
| f"Error convering TLV to Cluster Object for path: Endpoint = {path.EndpointId}/Cluster = {path.ClusterId}/Attribute = {path.AttributeId}") |
| logging.error( |
| f"Failed Cluster Object: {str(attributeType)}") |
| logging.error(ex) |
| attributeValue = ValueDecodeFailure( |
| tlvData, ex) |
| |
| # If we're in debug mode, raise the exception so that we can better debug what's happening. |
| if (builtins.enableDebugMode): |
| raise |
| |
| with self._resLock: |
| self._res['Attributes'][path] = AttributeReadResult( |
| Path=path, Status=imStatus, Data=attributeValue) |
| if self._subscription_handler is not None: |
| self._subscription_handler.OnUpdate( |
| path, attributeType(attributeValue)) |
| except Exception as ex: |
| logging.exception(ex) |
| |
| def handleAttributeData(self, path: AttributePath, status: int, data: bytes): |
| if self._subscription_handler is not None: |
| self._handleAttributeData(path, status, data) |
| else: |
| self._event_loop.call_soon_threadsafe( |
| self._handleAttributeData, path, status, data) |
| |
| def _handleEventData(self, header: EventHeader, path: EventPath, data: bytes): |
| try: |
| eventType = _EventIndex.get(str(path), None) |
| eventValue = None |
| tlvData = chip.tlv.TLVReader(data).get().get("Any", {}) |
| if eventType is None: |
| eventValue = ValueDecodeFailure( |
| tlvData, LookupError("event schema not found")) |
| else: |
| try: |
| eventValue = eventType(eventType.FromTLV(data)) |
| except Exception as ex: |
| logging.error( |
| f"Error convering TLV to Cluster Object for path: Endpoint = {path.EndpointId}/Cluster = {path.ClusterId}/Event = {path.EventId}") |
| logging.error( |
| f"Failed Cluster Object: {str(eventType)}") |
| logging.error(ex) |
| eventValue = ValueDecodeFailure( |
| tlvData, ex) |
| |
| # If we're in debug mode, raise the exception so that we can better debug what's happening. |
| if (builtins.enableDebugMode): |
| raise |
| |
| with self._resLock: |
| self._res['Events'].append[EventReadResult( |
| Header=header, Data=eventValue)] |
| except Exception as ex: |
| logging.exception(ex) |
| |
| def handleEventData(self, header: EventHeader, path: EventPath, data: bytes): |
| if self._subscription_handler is not None: |
| self._handleEventData(header, path, data) |
| else: |
| self._event_loop.call_soon_threadsafe( |
| self._handleEventData, header, path, data) |
| |
| def _handleError(self, chipError: int): |
| self._future.set_exception( |
| chip.exceptions.ChipStackError(chipError)) |
| |
| def handleError(self, chipError: int): |
| self._event_loop.call_soon_threadsafe( |
| self._handleError, chipError |
| ) |
| |
| def _handleSubscriptionEstablished(self, subscriptionId): |
| if not self._future.done(): |
| self._subscription_handler = SubscriptionTransaction( |
| self, subscriptionId, self._devCtrl) |
| self._future.set_result(self._subscription_handler) |
| |
| def handleSubscriptionEstablished(self, subscriptionId): |
| self._event_loop.call_soon_threadsafe( |
| self._handleSubscriptionEstablished, subscriptionId) |
| |
| def _handleDone(self): |
| if not self._future.done(): |
| self._future.set_result(self._res) |
| |
| def handleDone(self): |
| self._event_loop.call_soon_threadsafe(self._handleDone) |
| |
| |
| class AsyncWriteTransaction: |
| def __init__(self, future: Future, eventLoop): |
| self._event_loop = eventLoop |
| self._future = future |
| self._res = [] |
| |
| def _handleResponse(self, path: AttributePath, status: int): |
| try: |
| imStatus = chip.interaction_model.Status(status) |
| self._res.append(AttributeWriteResult(Path=path, Status=imStatus)) |
| except: |
| self._res.append(AttributeWriteResult(Path=path, Status=status)) |
| |
| def handleResponse(self, path: AttributePath, status: int): |
| self._event_loop.call_soon_threadsafe( |
| self._handleResponse, path, status) |
| |
| def _handleError(self, chipError: int): |
| self._future.set_exception( |
| chip.exceptions.ChipStackError(chipError)) |
| |
| def handleError(self, chipError: int): |
| self._event_loop.call_soon_threadsafe( |
| self._handleError, chipError |
| ) |
| |
| def _handleDone(self): |
| if not self._future.done(): |
| self._future.set_result(self._res) |
| |
| def handleDone(self): |
| self._event_loop.call_soon_threadsafe(self._handleDone) |
| |
| |
| _OnReadAttributeDataCallbackFunct = CFUNCTYPE( |
| None, py_object, c_uint16, c_uint32, c_uint32, c_uint32, c_void_p, c_size_t) |
| _OnSubscriptionEstablishedCallbackFunct = CFUNCTYPE(None, py_object, c_uint64) |
| _OnReadEventDataCallbackFunct = CFUNCTYPE( |
| None, py_object, c_uint16, c_uint32, c_uint32, c_uint32, c_uint8, c_uint64, c_uint8, c_void_p, c_size_t) |
| _OnReadErrorCallbackFunct = CFUNCTYPE( |
| None, py_object, c_uint32) |
| _OnReadDoneCallbackFunct = CFUNCTYPE( |
| None, py_object) |
| |
| |
| @_OnReadAttributeDataCallbackFunct |
| def _OnReadAttributeDataCallback(closure, endpoint: int, cluster: int, attribute: int, status, data, len): |
| dataBytes = ctypes.string_at(data, len) |
| closure.handleAttributeData(AttributePath( |
| EndpointId=endpoint, ClusterId=cluster, AttributeId=attribute), status, dataBytes[:]) |
| |
| |
| @_OnReadEventDataCallbackFunct |
| def _OnReadEventDataCallback(closure, endpoint: int, cluster: int, event: int, number: int, priority: int, timestamp: int, timestampType: int, data, len): |
| dataBytes = ctypes.string_at(data, len) |
| path = EventPath(ClusterId=cluster, EventId=event) |
| closure.handleEventData(EventHeader( |
| EndpointId=endpoint, EventNumber=number, Priority=EventPriority(priority), Timestamp=timestamp, TimestampType=EventTimestampType(timestampType)), path, dataBytes[:]) |
| |
| |
| @_OnSubscriptionEstablishedCallbackFunct |
| def _OnSubscriptionEstablishedCallback(closure, subscriptionId): |
| closure.handleSubscriptionEstablished(subscriptionId) |
| |
| |
| @_OnReadErrorCallbackFunct |
| def _OnReadErrorCallback(closure, chiperror: int): |
| closure.handleError(chiperror) |
| |
| |
| @_OnReadDoneCallbackFunct |
| def _OnReadDoneCallback(closure): |
| closure.handleDone() |
| ctypes.pythonapi.Py_DecRef(ctypes.py_object(closure)) |
| |
| |
| _OnWriteResponseCallbackFunct = CFUNCTYPE( |
| None, py_object, c_uint16, c_uint32, c_uint32, c_uint16) |
| _OnWriteErrorCallbackFunct = CFUNCTYPE( |
| None, py_object, c_uint32) |
| _OnWriteDoneCallbackFunct = CFUNCTYPE( |
| None, py_object) |
| |
| |
| @_OnWriteResponseCallbackFunct |
| def _OnWriteResponseCallback(closure, endpoint: int, cluster: int, attribute: int, status): |
| closure.handleResponse(AttributePath( |
| EndpointId=endpoint, ClusterId=cluster, AttributeId=attribute), status) |
| |
| |
| @_OnWriteErrorCallbackFunct |
| def _OnWriteErrorCallback(closure, chiperror: int): |
| closure.handleError(chiperror) |
| |
| |
| @_OnWriteDoneCallbackFunct |
| def _OnWriteDoneCallback(closure): |
| closure.handleDone() |
| ctypes.pythonapi.Py_DecRef(ctypes.py_object(closure)) |
| |
| |
| def WriteAttributes(future: Future, eventLoop, device, attributes: List[AttributeWriteRequest]) -> int: |
| handle = chip.native.GetLibraryHandle() |
| transaction = AsyncWriteTransaction(future, eventLoop) |
| |
| writeargs = [] |
| for attr in attributes: |
| path = chip.interaction_model.AttributePathIBstruct.parse( |
| b'\x00' * chip.interaction_model.AttributePathIBstruct.sizeof()) |
| path.EndpointId = attr.EndpointId |
| path.ClusterId = attr.Attribute.cluster_id |
| path.AttributeId = attr.Attribute.attribute_id |
| path = chip.interaction_model.AttributePathIBstruct.build(path) |
| tlv = attr.Attribute.ToTLV(None, attr.Data) |
| writeargs.append(ctypes.c_char_p(path)) |
| writeargs.append(ctypes.c_char_p(bytes(tlv))) |
| writeargs.append(ctypes.c_int(len(tlv))) |
| |
| ctypes.pythonapi.Py_IncRef(ctypes.py_object(transaction)) |
| res = handle.pychip_WriteClient_WriteAttributes( |
| ctypes.py_object(transaction), device, ctypes.c_size_t(len(attributes)), *writeargs) |
| if res != 0: |
| ctypes.pythonapi.Py_DecRef(ctypes.py_object(transaction)) |
| return res |
| |
| |
| def ReadAttributes(future: Future, eventLoop, device, devCtrl, attributes: List[AttributePath], subscriptionParameters: SubscriptionParameters = None) -> int: |
| handle = chip.native.GetLibraryHandle() |
| transaction = AsyncReadTransaction(future, eventLoop, devCtrl) |
| |
| readargs = [] |
| for attr in attributes: |
| path = chip.interaction_model.AttributePathIBstruct.parse( |
| b'\xff' * chip.interaction_model.AttributePathIBstruct.sizeof()) |
| if attr.EndpointId is not None: |
| path.EndpointId = attr.EndpointId |
| if attr.ClusterId is not None: |
| path.ClusterId = attr.ClusterId |
| if attr.AttributeId is not None: |
| path.AttributeId = attr.AttributeId |
| path = chip.interaction_model.AttributePathIBstruct.build(path) |
| readargs.append(ctypes.c_char_p(path)) |
| |
| ctypes.pythonapi.Py_IncRef(ctypes.py_object(transaction)) |
| minInterval = 0 |
| maxInterval = 0 |
| if subscriptionParameters is not None: |
| minInterval = subscriptionParameters.MinReportIntervalFloorSeconds |
| maxInterval = subscriptionParameters.MaxReportIntervalCeilingSeconds |
| res = handle.pychip_ReadClient_ReadAttributes( |
| ctypes.py_object(transaction), device, |
| ctypes.c_bool(subscriptionParameters is not None), |
| ctypes.c_uint32(minInterval), ctypes.c_uint32(maxInterval), |
| ctypes.c_size_t(len(attributes)), *readargs) |
| if res != 0: |
| ctypes.pythonapi.Py_DecRef(ctypes.py_object(transaction)) |
| return res |
| |
| |
| def ReadEvents(future: Future, eventLoop, device, devCtrl, events: List[EventPath], subscriptionParameters: SubscriptionParameters = None) -> int: |
| handle = chip.native.GetLibraryHandle() |
| transaction = AsyncReadTransaction(future, eventLoop, devCtrl) |
| |
| readargs = [] |
| for attr in events: |
| path = chip.interaction_model.EventPathIBstruct.parse( |
| b'\xff' * chip.interaction_model.EventPathIBstruct.sizeof()) |
| if attr.EndpointId is not None: |
| path.EndpointId = attr.EndpointId |
| if attr.ClusterId is not None: |
| path.ClusterId = attr.ClusterId |
| if attr.EventId is not None: |
| path.EventId = attr.EventId |
| path = chip.interaction_model.EventPathIBstruct.build(path) |
| readargs.append(ctypes.c_char_p(path)) |
| |
| ctypes.pythonapi.Py_IncRef(ctypes.py_object(transaction)) |
| minInterval = 0 |
| maxInterval = 0 |
| if subscriptionParameters is not None: |
| minInterval = subscriptionParameters.MinReportIntervalFloorSeconds |
| maxInterval = subscriptionParameters.MaxReportIntervalCeilingSeconds |
| res = handle.pychip_ReadClient_ReadEvents( |
| ctypes.py_object(transaction), device, |
| ctypes.c_bool(subscriptionParameters is not None), |
| ctypes.c_uint32(minInterval), ctypes.c_uint32(maxInterval), |
| ctypes.c_size_t(len(events)), *readargs) |
| if res != 0: |
| ctypes.pythonapi.Py_DecRef(ctypes.py_object(transaction)) |
| return res |
| |
| |
| def Init(): |
| handle = chip.native.GetLibraryHandle() |
| |
| # Uses one of the type decorators as an indicator for everything being |
| # initialized. |
| if not handle.pychip_WriteClient_InitCallbacks.argtypes: |
| setter = chip.native.NativeLibraryHandleMethodArguments(handle) |
| |
| handle.pychip_WriteClient_WriteAttributes.restype = c_uint32 |
| setter.Set('pychip_WriteClient_InitCallbacks', None, [ |
| _OnWriteResponseCallbackFunct, _OnWriteErrorCallbackFunct, _OnWriteDoneCallbackFunct]) |
| handle.pychip_ReadClient_ReadAttributes.restype = c_uint32 |
| setter.Set('pychip_ReadClient_InitCallbacks', None, [ |
| _OnReadAttributeDataCallbackFunct, _OnReadEventDataCallbackFunct, _OnSubscriptionEstablishedCallbackFunct, _OnReadErrorCallbackFunct, _OnReadDoneCallbackFunct]) |
| |
| handle.pychip_WriteClient_InitCallbacks( |
| _OnWriteResponseCallback, _OnWriteErrorCallback, _OnWriteDoneCallback) |
| handle.pychip_ReadClient_InitCallbacks( |
| _OnReadAttributeDataCallback, _OnReadEventDataCallback, _OnSubscriptionEstablishedCallback, _OnReadErrorCallback, _OnReadDoneCallback) |
| |
| _BuildAttributeIndex() |
| _BuildEventIndex() |