Fix for x86_64 build fail
[platform/upstream/connectedhomeip.git] / src / controller / python / chip / ChipDeviceCtrl.py
1 #
2 #    Copyright (c) 2020-2021 Project CHIP Authors
3 #    Copyright (c) 2019-2020 Google, LLC.
4 #    Copyright (c) 2013-2018 Nest Labs, Inc.
5 #    All rights reserved.
6 #
7 #    Licensed under the Apache License, Version 2.0 (the "License");
8 #    you may not use this file except in compliance with the License.
9 #    You may obtain a copy of the License at
10 #
11 #        http://www.apache.org/licenses/LICENSE-2.0
12 #
13 #    Unless required by applicable law or agreed to in writing, software
14 #    distributed under the License is distributed on an "AS IS" BASIS,
15 #    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 #    See the License for the specific language governing permissions and
17 #    limitations under the License.
18 #
19
20 #
21 #    @file
22 #      Python interface for Chip Device Manager
23 #
24
25 """Chip Device Controller interface
26 """
27
28 from __future__ import absolute_import
29 from __future__ import print_function
30 import time
31 from threading import Thread
32 from ctypes import *
33 from .ChipStack import *
34 from .clusters.CHIPClusters import *
35 import enum
36
37
38 __all__ = ["ChipDeviceController"]
39
40 _DevicePairingDelegate_OnPairingCompleteFunct = CFUNCTYPE(None, c_uint32)
41 _DeviceAddressUpdateDelegate_OnUpdateComplete = CFUNCTYPE(None, c_uint64, c_uint32)
42
43 # This is a fix for WEAV-429. Jay Logue recommends revisiting this at a later
44 # date to allow for truely multiple instances so this is temporary.
45 def _singleton(cls):
46     instance = [None]
47
48     def wrapper(*args, **kwargs):
49         if instance[0] is None:
50             instance[0] = cls(*args, **kwargs)
51         return instance[0]
52
53     return wrapper
54
55
56 class DCState(enum.IntEnum):
57     NOT_INITIALIZED = 0
58     IDLE = 1
59     BLE_READY = 2
60     RENDEZVOUS_ONGOING = 3
61     RENDEZVOUS_CONNECTED = 4
62
63 @_singleton
64 class ChipDeviceController(object):
65     def __init__(self, startNetworkThread=True, controllerNodeId=0, bluetoothAdapter=0):
66         self.state = DCState.NOT_INITIALIZED
67         self.devCtrl = None
68         self._ChipStack = ChipStack(bluetoothAdapter=bluetoothAdapter)
69         self._dmLib = None
70
71         self._InitLib()
72
73         devCtrl = c_void_p(None)
74         addressUpdater = c_void_p(None)
75         res = self._dmLib.pychip_DeviceController_NewDeviceController(pointer(devCtrl), controllerNodeId)
76         if res != 0:
77             raise self._ChipStack.ErrorToException(res)
78
79         res = self._dmLib.pychip_DeviceAddressUpdater_New(pointer(addressUpdater), devCtrl)
80         if res != 0:
81             raise self._ChipStack.ErrorToException(res)
82
83         self.devCtrl = devCtrl
84         self.addressUpdater = addressUpdater
85         self._ChipStack.devCtrl = devCtrl
86
87         self._Cluster = ChipClusters(self._ChipStack)
88         self._Cluster.InitLib(self._dmLib)
89
90         def HandleKeyExchangeComplete(err):
91             if err != 0:
92                 print("Failed to establish secure session to device: {}".format(err))
93                 self._ChipStack.callbackRes = False
94             else:
95                 print("Secure Session to Device Established")
96                 self._ChipStack.callbackRes = True
97             self.state = DCState.IDLE
98             self._ChipStack.completeEvent.set()
99
100         def HandleAddressUpdateComplete(nodeid, err):
101             if err != 0:
102                 print("Failed to update node address: {}".format(err))
103             else:
104                 print("Node address has been updated")
105             self.state = DCState.IDLE
106             self._ChipStack.callbackRes = err
107             self._ChipStack.completeEvent.set()
108
109         self.cbHandleKeyExchangeCompleteFunct = _DevicePairingDelegate_OnPairingCompleteFunct(HandleKeyExchangeComplete)
110         self._dmLib.pychip_ScriptDevicePairingDelegate_SetKeyExchangeCallback(self.devCtrl, self.cbHandleKeyExchangeCompleteFunct)
111
112         self.cbOnAddressUpdateComplete = _DeviceAddressUpdateDelegate_OnUpdateComplete(HandleAddressUpdateComplete)
113         self._dmLib.pychip_ScriptDeviceAddressUpdateDelegate_SetOnAddressUpdateComplete(self.cbOnAddressUpdateComplete)
114
115         self.state = DCState.IDLE
116
117     def __del__(self):
118         if self.devCtrl != None:
119             self._dmLib.pychip_DeviceAddressUpdater_Delete(self.addressUpdater)
120             self._dmLib.pychip_DeviceController_DeleteDeviceController(self.devCtrl)
121             self.devCtrl = None
122
123     def IsConnected(self):
124         return self._ChipStack.Call(
125             lambda: self._dmLib.pychip_DeviceController_IsConnected(self.devCtrl)
126         )
127
128     def ConnectBle(self, bleConnection):
129         self._ChipStack.CallAsync(
130             lambda: self._dmLib.pychip_DeviceController_ValidateBTP(
131                 self.devCtrl,
132                 bleConnection,
133                 self._ChipStack.cbHandleComplete,
134                 self._ChipStack.cbHandleError,
135             )
136         )
137
138     def ConnectBLE(self, discriminator, setupPinCode, nodeid):
139         self.state = DCState.RENDEZVOUS_ONGOING
140         return self._ChipStack.CallAsync(
141             lambda: self._dmLib.pychip_DeviceController_ConnectBLE(self.devCtrl, discriminator, setupPinCode, nodeid)
142         )
143
144     def ConnectIP(self, ipaddr, setupPinCode, nodeid):
145         self.state = DCState.RENDEZVOUS_ONGOING
146         return self._ChipStack.CallAsync(
147             lambda: self._dmLib.pychip_DeviceController_ConnectIP(self.devCtrl, ipaddr, setupPinCode, nodeid)
148         )
149
150     def ResolveNode(self, fabricid, nodeid):
151         return self._ChipStack.CallAsync(
152             lambda: self._dmLib.pychip_Resolver_ResolveNode(fabricid, nodeid)
153         )
154
155     def GetAddressAndPort(self, nodeid):
156         address = create_string_buffer(64)
157         port = c_uint16(0)
158
159         error = self._ChipStack.Call(
160             lambda: self._dmLib.pychip_DeviceController_GetAddressAndPort(self.devCtrl, nodeid, address, 64, pointer(port))
161         )
162
163         return (address.value.decode(), port.value) if error == 0 else None
164
165     def ZCLSend(self, cluster, command, nodeid, endpoint, groupid, args):
166         device = c_void_p(None)
167         self._ChipStack.Call(
168             lambda: self._dmLib.pychip_GetDeviceByNodeId(self.devCtrl, nodeid, pointer(device))
169         )
170
171         self._Cluster.SendCommand(device, cluster, command, endpoint, groupid, args)
172
173     def ZCLReadAttribute(self, cluster, attribute, nodeid, endpoint, groupid):
174         device = c_void_p(None)
175         self._ChipStack.Call(
176             lambda: self._dmLib.pychip_GetDeviceByNodeId(self.devCtrl, nodeid, pointer(device))
177         )
178
179         self._Cluster.ReadAttribute(device, cluster, attribute, endpoint, groupid)
180
181     def ZCLCommandList(self):
182         return self._Cluster.ListClusterCommands()
183
184     def ZCLAttributeList(self):
185         return self._Cluster.ListClusterAttributes()
186
187     def SetLogFilter(self, category):
188         if category < 0 or category > pow(2, 8):
189             raise ValueError("category must be an unsigned 8-bit integer")
190
191         self._ChipStack.Call(
192             lambda: self._dmLib.pychip_DeviceController_SetLogFilter(category)
193         )
194
195     def GetLogFilter(self):
196         self._ChipStack.Call(
197             lambda: self._dmLib.pychip_DeviceController_GetLogFilter()
198         )
199
200     def SetBlockingCB(self, blockingCB):
201         self._ChipStack.blockingCB = blockingCB
202
203     def SetWifiCredential(self, ssid, password):
204         ret = self._dmLib.pychip_ScriptDevicePairingDelegate_SetWifiCredential(self.devCtrl, ssid.encode("utf-8") + b'\0', password.encode("utf-8") + b'\0')
205         if ret != 0:
206             raise self._ChipStack.ErrorToException(res)
207
208     def SetThreadCredential(self, channel, panid, masterKey):
209         ret = self._dmLib.pychip_ScriptDevicePairingDelegate_SetThreadCredential(self.devCtrl, channel, panid, masterKey.encode("utf-8") + b'\0')
210         if ret != 0:
211             raise self._ChipStack.ErrorToException(ret)
212
213     # ----- Private Members -----
214     def _InitLib(self):
215         if self._dmLib is None:
216             self._dmLib = CDLL(self._ChipStack.LocateChipDLL())
217
218             self._dmLib.pychip_DeviceController_NewDeviceController.argtypes = [POINTER(c_void_p), c_uint64]
219             self._dmLib.pychip_DeviceController_NewDeviceController.restype = c_uint32
220
221             self._dmLib.pychip_DeviceController_DeleteDeviceController.argtypes = [c_void_p]
222             self._dmLib.pychip_DeviceController_DeleteDeviceController.restype = c_uint32
223
224             self._dmLib.pychip_DeviceController_ConnectBLE.argtypes = [c_void_p, c_uint16, c_uint32, c_uint64]
225             self._dmLib.pychip_DeviceController_ConnectBLE.restype = c_uint32
226
227             self._dmLib.pychip_DeviceController_ConnectIP.argtypes = [c_void_p, c_char_p, c_uint32, c_uint64]
228             self._dmLib.pychip_DeviceController_ConnectIP.restype = c_uint32
229
230             self._dmLib.pychip_DeviceController_GetAddressAndPort.argtypes = [c_void_p, c_uint64, c_char_p, c_uint64, POINTER(c_uint16)]
231             self._dmLib.pychip_DeviceController_GetAddressAndPort.restype = c_uint32
232
233             self._dmLib.pychip_ScriptDevicePairingDelegate_SetWifiCredential.argtypes = [c_void_p, c_char_p, c_char_p]
234             self._dmLib.pychip_ScriptDevicePairingDelegate_SetWifiCredential.restype = c_uint32
235
236             self._dmLib.pychip_ScriptDevicePairingDelegate_SetKeyExchangeCallback.argtypes = [c_void_p, _DevicePairingDelegate_OnPairingCompleteFunct]
237             self._dmLib.pychip_ScriptDevicePairingDelegate_SetKeyExchangeCallback.restype = c_uint32
238
239             self._dmLib.pychip_DeviceAddressUpdater_New.argtypes = [POINTER(c_void_p), c_void_p]
240             self._dmLib.pychip_DeviceAddressUpdater_New.restype = c_uint32
241
242             self._dmLib.pychip_DeviceAddressUpdater_Delete.argtypes = [c_void_p]
243             self._dmLib.pychip_DeviceAddressUpdater_Delete.restype = None
244
245             self._dmLib.pychip_ScriptDeviceAddressUpdateDelegate_SetOnAddressUpdateComplete.argtypes = [_DeviceAddressUpdateDelegate_OnUpdateComplete]
246             self._dmLib.pychip_ScriptDeviceAddressUpdateDelegate_SetOnAddressUpdateComplete.restype = None
247
248             self._dmLib.pychip_Resolver_ResolveNode.argtypes = [c_uint64, c_uint64]
249             self._dmLib.pychip_Resolver_ResolveNode.restype = c_uint32
250
251             self._dmLib.pychip_GetDeviceByNodeId.argtypes = [c_void_p, c_uint64, POINTER(c_void_p)]
252             self._dmLib.pychip_GetDeviceByNodeId.restype = c_uint32