2 # Copyright (c) 2020 Project CHIP Authors
3 # Copyright (c) 2019-2020 Google LLC.
4 # Copyright (c) 2015-2018 Nest Labs, Inc.
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
11 # http://www.apache.org/licenses/LICENSE-2.0
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.
22 # BLE Central support for Chip Device Manager via BlueZ APIs.
25 from __future__ import absolute_import
26 from __future__ import print_function
29 import dbus.mainloop.glib
43 from gi.repository import GObject
44 except Exception as ex:
45 logging.exception("Unable to find GObject from gi.repository")
46 from pgi.repository import GObject
48 from .ChipUtility import ChipUtility
50 from .ChipBleUtility import (
51 BLE_SUBSCRIBE_OPERATION_SUBSCRIBE,
52 BLE_SUBSCRIBE_OPERATION_UNSUBSCRIBE,
53 BLE_ERROR_REMOTE_DEVICE_DISCONNECTED,
60 BleDisconnectEventStruct,
62 BleSubscribeEventStruct,
63 BleDeviceIdentificationInfo,
67 from .ChipBleBase import ChipBleBase
69 chip_service = uuid.UUID("0000FEAF-0000-1000-8000-00805F9B34FB")
70 chip_tx = uuid.UUID("18EE2EF5-263D-4559-959F-4F9C429F9D11")
71 chip_rx = uuid.UUID("18EE2EF5-263D-4559-959F-4F9C429F9D12")
72 chip_service_short = uuid.UUID("0000FEAF-0000-0000-0000-000000000000")
73 chromecast_setup_service = uuid.UUID("0000FEA0-0000-1000-8000-00805F9B34FB")
74 chromecast_setup_service_short = uuid.UUID("0000FEA0-0000-0000-0000-000000000000")
76 BLUEZ_NAME = "org.bluez"
77 ADAPTER_INTERFACE = BLUEZ_NAME + ".Adapter1"
78 DEVICE_INTERFACE = BLUEZ_NAME + ".Device1"
79 SERVICE_INTERFACE = BLUEZ_NAME + ".GattService1"
80 CHARACTERISTIC_INTERFACE = BLUEZ_NAME + ".GattCharacteristic1"
81 DBUS_PROPERTIES = "org.freedesktop.DBus.Properties"
83 BLE_SCAN_CONNECT_GUARD_SEC = 2.0
84 BLE_STATUS_TRANSITION_TIMEOUT_SEC = 5.0
85 BLE_CONNECT_TIMEOUT_SEC = 15.0
86 BLE_SERVICE_DISCOVERY_TIMEOUT_SEC = 5.0
87 BLE_CHAR_DISCOVERY_TIMEOUT_SEC = 5.0
88 BLE_SUBSCRIBE_TIMEOUT_SEC = 5.0
89 BLE_WRITE_CHARACTERISTIC_TIMEOUT_SEC = 10.0
93 def get_bluez_objects(bluez, bus, interface, prefix_path):
95 if bluez is None or bus is None or interface is None or prefix_path is None:
97 for item in bluez.GetManagedObjects().items():
98 delegates = item[1].get(interface)
102 if item[0].startswith(prefix_path):
103 slice["object"] = bus.get_object(BLUEZ_NAME, item[0])
104 slice["path"] = item[0]
105 results.append(slice)
109 class BluezDbusAdapter:
110 def __init__(self, bluez_obj, bluez, bus, logger=None):
111 self.logger = logger if logger else logging.getLogger("ChipBLEMgr")
112 self.object = bluez_obj
113 self.adapter = dbus.Interface(bluez_obj, ADAPTER_INTERFACE)
114 self.adapter_properties = dbus.Interface(bluez_obj, DBUS_PROPERTIES)
115 self.adapter_event = threading.Event()
118 self.path = self.adapter.object_path
119 self.signalReceiver = None
125 self.logger.debug("destroy adapter")
126 self.adapter_unregister_signal()
128 self.adapter_properties = None
129 self.adapter_event.clear()
134 self.signalReceiver = None
136 def adapter_register_signal(self):
137 if self.signalReceiver is None:
138 self.logger.debug("add adapter signal")
139 self.signalReceiver = self.bus.add_signal_receiver(
140 self.adapter_on_prop_changed_cb,
142 dbus_interface=DBUS_PROPERTIES,
143 signal_name="PropertiesChanged",
147 def adapter_unregister_signal(self):
148 if self.signalReceiver is not None:
149 self.logger.debug(" remove adapter signal")
150 self.bus.remove_signal_receiver(
152 signal_name="PropertiesChanged",
153 dbus_interface="org.freedesktop.DBus.Properties",
156 def adapter_on_prop_changed_cb(
157 self, interface, changed_properties, invalidated_properties
159 if len(changed_properties) == 0:
160 self.logger.debug("changed_properties is empty")
163 if len(invalidated_properties) > 0:
165 "invalidated_properties is not empty %s" % str(invalidated_properties)
169 if interface == ADAPTER_INTERFACE:
170 if "Discovering" in changed_properties:
171 self.adapter_event.set()
173 def adapter_bg_scan(self, enable):
174 self.adapter_event.clear()
178 if not self.Discovering:
180 self.logger.info("scanning started")
181 self.adapter.StartDiscovery()
183 self.logger.info("it has started scanning")
187 self.adapter.StopDiscovery()
188 self.logger.info("scanning stopped")
190 print("it has stopped scanning")
192 if not self.adapter_event.wait(BLE_STATUS_TRANSITION_TIMEOUT_SEC):
194 self.logger.debug("scan start error")
196 self.logger.debug("scan stop error")
197 self.adapter_event.clear()
198 except dbus.exceptions.DBusException as ex:
199 self.adapter_event.clear()
200 self.logger.debug(str(ex))
201 except Exception as ex:
202 self.logger.debug(traceback.format_exc())
207 result = self.adapter_properties.Get(ADAPTER_INTERFACE, "Address")
209 except dbus.exceptions.DBusException as ex:
210 self.logger.debug(str(ex))
212 except Exception as ex:
213 self.logger.debug(traceback.format_exc())
219 return self.adapter_properties.Get(ADAPTER_INTERFACE, "UUIDs")
220 except dbus.exceptions.DBusException as ex:
221 self.logger.debug(str(ex))
223 except Exception as ex:
224 self.logger.debug(traceback.format_exc())
227 def SetDiscoveryFilter(self, dict):
229 self.adapter.SetDiscoveryFilter(dict)
230 except dbus.exceptions.DBusException as ex:
231 self.logger.debug(str(ex))
232 except Exception as ex:
233 self.logger.debug(traceback.format_exc())
236 def Discovering(self):
238 result = self.adapter_properties.Get(ADAPTER_INTERFACE, "Discovering")
240 except dbus.exceptions.DBusException as ex:
241 self.logger.debug(str(ex))
243 except Exception as ex:
244 self.logger.debug(traceback.format_exc())
247 def DiscoverableTimeout(self, timeoutSec):
249 result = self.adapter_properties.Set(
250 ADAPTER_INTERFACE, "DiscoverableTimeout", timeoutSec
253 except dbus.exceptions.DBusException as ex:
254 self.logger.debug(str(ex))
256 except Exception as ex:
257 self.logger.debug(traceback.format_exc())
260 def Powered(self, enable):
262 result = self.adapter_properties.Set(ADAPTER_INTERFACE, "Powered", enable)
264 except dbus.exceptions.DBusException as ex:
265 self.logger.debug(str(ex))
267 except Exception as ex:
268 self.logger.debug(traceback.format_exc())
271 def find_devices(self, uuids):
273 BluezDbusDevice(p["object"], self.bluez, self.bus, self.logger)
274 for p in get_bluez_objects(
275 self.bluez, self.bus, DEVICE_INTERFACE, self.path
279 for device in devices:
280 for i in device.uuids:
284 # Some devices do not advertise their uuid lists, thus we should also check service data.
285 if device.ServiceData:
286 for i in device.ServiceData:
287 if uuid.UUID(str(i)) in uuids:
292 def clear_adapter(self):
294 BluezDbusDevice(p["object"], self.bluez, self.bus, self.logger)
295 for p in get_bluez_objects(
296 self.bluez, self.bus, DEVICE_INTERFACE, self.path
299 for device in devices:
302 device.device_bg_connect(False)
303 self.adapter.RemoveDevice(device.device.object_path)
304 except Exception as ex:
308 class BluezDbusDevice:
309 def __init__(self, bluez_obj, bluez, bus, logger=None):
310 self.logger = logger if logger else logging.getLogger("ChipBLEMgr")
311 self.object = bluez_obj
312 self.device = dbus.Interface(bluez_obj, DEVICE_INTERFACE)
313 self.device_properties = dbus.Interface(bluez_obj, DBUS_PROPERTIES)
314 self.path = self.device.object_path
315 self.device_event = threading.Event()
318 self.device_id = uuid.uuid3(uuid.NAMESPACE_DNS, self.Name)
319 except UnicodeDecodeError:
320 self.device_id = uuid.uuid3(
321 uuid.NAMESPACE_DNS, self.Name.encode("utf-8")
324 self.device_id = uuid.uuid4()
327 self.signalReceiver = None
328 self.path = self.device.object_path
334 self.logger.debug("destroy device")
335 self.device_unregister_signal()
337 self.device_properties = None
338 self.device_event = None
339 self.device_id = None
343 self.signalReceiver = None
345 def device_register_signal(self):
346 if self.signalReceiver is None:
347 self.logger.debug("add device signal")
348 self.signalReceiver = self.bus.add_signal_receiver(
349 self.device_on_prop_changed_cb,
351 dbus_interface=DBUS_PROPERTIES,
352 signal_name="PropertiesChanged",
356 def device_unregister_signal(self):
357 if self.signalReceiver is not None:
358 self.logger.debug("remove device signal")
359 self.bus.remove_signal_receiver(
361 signal_name="PropertiesChanged",
362 dbus_interface=DBUS_PROPERTIES,
365 def device_on_prop_changed_cb(
366 self, interface, changed_properties, invalidated_properties
368 if len(changed_properties) == 0:
369 self.logger.debug("changed_properties is empty")
372 if len(invalidated_properties) > 0:
374 "invalidated_properties is not empty %s" % str(invalidated_properties)
378 if interface == DEVICE_INTERFACE:
379 if "Connected" in changed_properties:
380 self.device_event.set()
382 def device_bg_connect(self, enable):
383 time.sleep(BLE_SCAN_CONNECT_GUARD_SEC)
385 self.device_event.clear()
388 if not self.Connected:
390 self.device.Connect()
391 self.logger.info("BLE connecting")
393 self.logger.info("BLE has connected")
397 self.device.Disconnect()
398 self.logger.info("BLE disconnected")
400 self.logger.info("BLE has disconnected")
402 if not self.device_event.wait(BLE_STATUS_TRANSITION_TIMEOUT_SEC):
404 self.logger.info("BLE connect error")
406 self.logger.info("BLE disconnect error")
407 self.device_event.clear()
408 except dbus.exceptions.DBusException as ex:
409 self.device_event.clear()
410 self.logger.info(str(ex))
411 except Exception as ex:
412 self.logger.debug(traceback.format_exc())
414 def service_discover(self, gatt_dic):
415 self.logger.info("Discovering services")
417 expired = time.time() + BLE_SERVICE_DISCOVERY_TIMEOUT_SEC
418 while time.time() < expired:
419 if self.ServicesResolved:
421 BluezDbusGattService(
422 p["object"], self.bluez, self.bus, self.logger
424 for p in get_bluez_objects(
425 self.bluez, self.bus, SERVICE_INTERFACE, self.path
428 for service in services:
429 if service.uuid in gatt_dic["services"]:
430 self.logger.info("Service discovering success")
432 time.sleep(BLE_IDLE_DELTA)
433 self.logger.error("Service discovering fail")
435 except dbus.exceptions.DBusException as ex:
436 self.logger.debug(str(ex))
438 except Exception as ex:
439 self.logger.debug(traceback.format_exc())
445 uuids = self.device_properties.Get(DEVICE_INTERFACE, "UUIDs")
449 uuid_normal = "0000%s-0000-0000-0000-000000000000" % i
452 uuid_result.append(uuid.UUID(str(uuid_normal)))
454 except dbus.exceptions.DBusException as ex:
455 self.logger.debug(str(ex))
457 except Exception as ex:
458 self.logger.debug(traceback.format_exc())
464 return self.device_properties.Get(DEVICE_INTERFACE, "Address")
465 except dbus.exceptions.DBusException as ex:
466 self.logger.debug(str(ex))
468 except Exception as ex:
469 self.logger.debug(traceback.format_exc())
475 name = self.device_properties.Get(DEVICE_INTERFACE, "Name")
477 except dbus.exceptions.DBusException as ex:
478 self.logger.debug(str(ex))
480 except Exception as ex:
481 self.logger.debug(traceback.format_exc())
487 result = self.device_properties.Get(DEVICE_INTERFACE, "Connected")
489 except dbus.exceptions.DBusException as ex:
490 self.logger.debug(str(ex))
492 except Exception as ex:
493 self.logger.debug(traceback.format_exc())
499 return self.device_properties.Get(DEVICE_INTERFACE, "TxPower")
500 except dbus.exceptions.DBusException as ex:
501 self.logger.debug(str(ex))
503 except Exception as ex:
504 self.logger.debug(traceback.format_exc())
510 result = self.device_properties.Get(DEVICE_INTERFACE, "RSSI")
512 except dbus.exceptions.DBusException as ex:
513 self.logger.debug(str(ex))
515 except Exception as ex:
516 self.logger.debug(traceback.format_exc())
522 return self.device_properties.Get(DEVICE_INTERFACE, "Adapter")
523 except dbus.exceptions.DBusException as ex:
524 self.logger.debug(str(ex))
526 except Exception as ex:
527 self.logger.debug(traceback.format_exc())
531 def ServiceData(self):
533 return self.device_properties.Get(DEVICE_INTERFACE, "ServiceData")
534 except dbus.exceptions.DBusException as ex:
535 self.logger.debug(str(ex))
537 except Exception as ex:
538 self.logger.debug(traceback.format_exc())
542 def ServicesResolved(self):
544 result = self.device_properties.Get(DEVICE_INTERFACE, "ServicesResolved")
546 except dbus.exceptions.DBusException as ex:
547 self.logger.debug(str(ex))
549 except Exception as ex:
550 self.logger.debug(traceback.format_exc())
554 class BluezDbusGattService:
555 def __init__(self, bluez_obj, bluez, bus, logger=None):
556 self.logger = logger if logger else logging.getLogger("ChipBLEMgr")
557 self.object = bluez_obj
558 self.service = dbus.Interface(bluez_obj, SERVICE_INTERFACE)
559 self.service_properties = dbus.Interface(bluez_obj, DBUS_PROPERTIES)
562 self.path = self.service.object_path
568 self.logger.debug("destroy GattService")
570 self.service_properties = None
580 str(self.service_properties.Get(SERVICE_INTERFACE, "UUID"))
583 except dbus.exceptions.DBusException as ex:
584 self.logger.debug(str(ex))
586 except Exception as ex:
587 self.logger.debug(traceback.format_exc())
593 result = bool(self.service_properties.Get(SERVICE_INTERFACE, "Primary"))
595 except dbus.exceptions.DBusException as ex:
596 self.logger.debug(str(ex))
598 except Exception as ex:
599 self.logger.debug(traceback.format_exc())
605 result = self.service_properties.Get(SERVICE_INTERFACE, "Device")
607 except dbus.exceptions.DBusException as ex:
608 self.logger.debug(str(ex))
610 except Exception as ex:
611 self.logger.debug(traceback.format_exc())
614 def find_characteristic(self, uuid):
616 expired = time.time() + BLE_CHAR_DISCOVERY_TIMEOUT_SEC
617 while time.time() < expired:
619 BluezDbusGattCharacteristic(
620 p["object"], self.bluez, self.bus, self.logger
622 for p in get_bluez_objects(
623 self.bluez, self.bus, CHARACTERISTIC_INTERFACE, self.path
626 for characteristic in characteristics:
627 if characteristic.uuid == uuid:
628 return characteristic
629 time.sleep(BLE_IDLE_DELTA)
630 self.logger.error("Char discovering fail")
632 except dbus.exceptions.DBusException as ex:
633 self.logger.debug(str(ex))
635 except Exception as ex:
636 self.logger.debug(traceback.format_exc())
640 class BluezDbusGattCharacteristic:
641 def __init__(self, bluez_obj, bluez, bus, logger=None):
642 self.logger = logger if logger else logging.getLogger("ChipBLEMgr")
643 self.object = bluez_obj
644 self.characteristic = dbus.Interface(bluez_obj, CHARACTERISTIC_INTERFACE)
645 self.characteristic_properties = dbus.Interface(bluez_obj, DBUS_PROPERTIES)
647 self.path = self.characteristic.object_path
650 self.signalReceiver = None
656 self.logger.debug("destroy GattCharacteristic")
657 self.gattCharacteristic_unregister_signal()
658 self.characteristic = None
660 self.characteristic_properties = None
665 self.signalReceiver = None
667 def gattCharacteristic_register_signal(self):
668 if not self.signalReceiver:
669 self.logger.debug("add GattCharacteristic signal")
670 self.signalReceiver = self.bus.add_signal_receiver(
671 self.gatt_on_characteristic_changed_cb,
673 dbus_interface=DBUS_PROPERTIES,
674 signal_name="PropertiesChanged",
678 def gattCharacteristic_unregister_signal(self):
679 if self.signalReceiver:
680 self.logger.debug("remove GattCharacteristic signal")
682 self.bus.remove_signal_receiver(
685 signal_name="PropertiesChanged",
686 dbus_interface=DBUS_PROPERTIES,
689 self.signalReceiver = None
691 def gatt_on_characteristic_changed_cb(
692 self, interface, changed_properties, invalidated_properties
695 "property change in" + str(self.characteristic) + str(changed_properties)
698 if len(changed_properties) == 0:
701 if len(invalidated_properties) > 0:
704 if interface == CHARACTERISTIC_INTERFACE:
705 if "Value" in changed_properties:
707 self.received(changed_properties["Value"])
709 def WriteValue(self, value, options, reply_handler, error_handler, timeout):
711 self.characteristic.WriteValue(
714 reply_handler=reply_handler,
715 error_handler=error_handler,
718 except dbus.exceptions.DBusException as ex:
719 self.logger.debug(str(ex))
720 except Exception as ex:
721 self.logger.debug(traceback.format_exc())
728 self.characteristic_properties.Get(CHARACTERISTIC_INTERFACE, "UUID")
732 except dbus.exceptions.DBusException as ex:
733 self.logger.debug(str(ex))
735 except Exception as ex:
736 self.logger.debug(traceback.format_exc())
739 def StartNotify(self, cbfunct, reply_handler, error_handler, timeout):
742 self.logger.info("please provide the notify callback function")
743 self.received = cbfunct
744 self.gattCharacteristic_register_signal()
745 self.characteristic.StartNotify(
746 reply_handler=reply_handler,
747 error_handler=error_handler,
750 except dbus.exceptions.DBusException as ex:
751 self.logger.debug(str(ex))
752 except Exception as ex:
753 self.logger.debug(traceback.format_exc())
755 def StopNotify(self, reply_handler, error_handler, timeout):
757 self.logger.debug("stopping notifying")
758 self.characteristic.StopNotify(
759 reply_handler=reply_handler,
760 error_handler=error_handler,
763 self.gattCharacteristic_unregister_signal()
765 except dbus.exceptions.DBusException as ex:
766 self.logger.debug(str(ex))
767 except Exception as ex:
768 self.logger.debug(traceback.format_exc())
773 result = self.characteristic_properties.Get(
774 CHARACTERISTIC_INTERFACE, "Notifying"
777 except dbus.exceptions.DBusException as ex:
778 self.logger.debug(str(ex))
780 except Exception as ex:
781 self.logger.debug(traceback.format_exc())
785 class BluezManager(ChipBleBase):
786 def __init__(self, devMgr, logger=None):
790 self.logger = logging.getLogger("ChipBLEMgr")
793 format="%(asctime)s %(name)-12s %(levelname)-8s %(message)s",
795 self.scan_quiet = False
796 self.peripheral_list = []
797 self.device_uuid_list = []
798 self.chip_queue = queue.Queue()
799 self.Gmainloop = None
800 self.daemon_thread = None
802 dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
803 GObject.threads_init()
804 dbus.mainloop.glib.threads_init()
805 self.bus = dbus.SystemBus()
806 self.bluez = dbus.Interface(
807 self.bus.get_object(BLUEZ_NAME, "/"), "org.freedesktop.DBus.ObjectManager"
811 self.orig_input_hook = None
812 self.hookFuncPtr = None
813 self.connect_state = False
816 self.setInputHook(self.readlineCB)
818 self.devMgr.SetBlockingCB(self.devMgrCB)
822 self.setInputHook(self.orig_input_hook)
824 def ble_adapter_select(self, identifier=None):
826 self.adapter.destroy()
828 self.adapter = self.get_adapter_by_addr(identifier)
829 self.adapter.adapter_register_signal()
830 self.adapter.Powered(False)
831 self.adapter.Powered(True)
833 def get_adapters(self):
835 BluezDbusAdapter(p["object"], self.bluez, self.bus, self.logger)
836 for p in get_bluez_objects(
837 self.bluez, self.bus, ADAPTER_INTERFACE, "/org/bluez"
842 def ble_adapter_print(self):
845 BluezDbusAdapter(p["object"], self.bluez, self.bus, self.logger)
846 for p in get_bluez_objects(
847 self.bluez, self.bus, ADAPTER_INTERFACE, "/org/bluez"
850 for i in range(len(adapters)):
851 self.logger.info("AdapterName: %s AdapterAddress: %s" % (adapters[i].path.replace("/org/bluez/", ""), adapters[i].Address))
852 except dbus.exceptions.DBusException as ex:
853 self.logger.debug(str(ex))
855 def get_adapter_by_addr(self, identifier):
858 BluezDbusAdapter(p["object"], self.bluez, self.bus, self.logger)
859 for p in get_bluez_objects(
860 self.bluez, self.bus, ADAPTER_INTERFACE, "/org/bluez"
863 if identifier is None:
865 if len(adapters) > 0:
866 for adapter in adapters:
867 if str(adapter.Address).upper() == str(identifier).upper() or "/org/bluez/{}".format(identifier) == str(adapter.path):
870 "adapter %s cannot be found, expect the ble mac address" % (identifier)
874 except dbus.exceptions.DBusException as ex:
875 self.logger.debug(str(ex))
877 def runLoopUntil(self, target=None, **kwargs):
879 self.daemon_thread = threading.Thread(
880 target=self.running_thread, args=(target, kwargs)
882 self.daemon_thread.daemon = True
883 self.daemon_thread.start()
886 self.Gmainloop = GObject.MainLoop()
888 except KeyboardInterrupt:
889 self.Gmainloop.quit()
892 def running_thread(self, target, kwargs):
894 while not self.Gmainloop or not self.Gmainloop.is_running():
897 except Exception as err:
898 traceback.print_exc()
900 self.Gmainloop.quit()
902 def setInputHook(self, hookFunc):
903 """Set the PyOS_InputHook to call the specific function."""
904 hookFunctionType = CFUNCTYPE(None)
905 self.hookFuncPtr = hookFunctionType(hookFunc)
906 pyos_inputhook_ptr = c_void_p.in_dll(pythonapi, "PyOS_InputHook")
907 # save the original so that on del we can revert it back to the way it was.
908 self.orig_input_hook = cast(pyos_inputhook_ptr.value, PYFUNCTYPE(c_int))
909 # set the new hook. readLine will call this periodically as it polls for input.
910 pyos_inputhook_ptr.value = cast(self.hookFuncPtr, c_void_p).value
912 def runIdleLoop(self, **kwargs):
916 self.runLoopUntil(self.runIdleLoop)
918 def readlineCB(self):
919 self.runLoopUntil(self.runIdleLoop)
921 if self.orig_input_hook:
922 self.orig_input_hook()
924 def dump_scan_result(self, device):
925 self.logger.info("{0:<16}= {1}".format("Name", device.Name))
926 self.logger.info("{0:<16}= {1}".format("ID", device.device_id))
927 self.logger.info("{0:<16}= {1}".format("RSSI", device.RSSI))
928 self.logger.info("{0:<16}= {1}".format("Address", device.Address))
930 devIdInfo = self.get_peripheral_devIdInfo(device)
931 if devIdInfo != None:
932 self.logger.info("{0:<16}= {1}".format("Pairing State", devIdInfo.pairingState))
933 self.logger.info("{0:<16}= {1}".format("Discriminator", devIdInfo.discriminator))
934 self.logger.info("{0:<16}= {1}".format("Vendor Id", devIdInfo.vendorId))
935 self.logger.info("{0:<16}= {1}".format("Product Id", devIdInfo.productId))
937 if device.ServiceData:
938 for advuuid in device.ServiceData:
939 self.logger.info("{0:<16}= {1}".format("Adv UUID", str(advuuid)))
940 self.logger.info("{0:<16}= {1}".format("Adv Data", bytes(device.ServiceData[advuuid]).hex()))
945 def scan_bg_implementation(self, **kwargs):
946 self.adapter.clear_adapter()
947 with self.chip_queue.mutex:
948 self.chip_queue.queue.clear()
949 self.adapter.adapter_bg_scan(True)
951 identifier = kwargs["identifier"]
952 timeout = kwargs["timeout"] + time.time()
953 self.device_uuid_list = []
954 self.peripheral_list = []
956 while time.time() < timeout:
957 scanned_peripheral_list = self.adapter.find_devices(
961 chromecast_setup_service,
962 chromecast_setup_service_short,
965 for device in scanned_peripheral_list:
967 if not self.scan_quiet and device.Address not in self.device_uuid_list:
968 # display all scanned results
969 self.device_uuid_list.append(device.Address)
970 self.peripheral_list.append(device)
971 self.dump_scan_result(device)
972 devIdInfo = self.get_peripheral_devIdInfo(device)
976 if identifier and (device.Name == identifier or str(device.Address).upper() == str(
978 ) or str(devIdInfo.discriminator) == identifier):
980 # only display the scanned target's info when quiet
981 self.dump_scan_result(device)
986 traceback.print_exc()
990 time.sleep(BLE_IDLE_DELTA)
991 self.adapter.adapter_bg_scan(False)
993 def scan(self, line):
994 args = self.ParseInputLine(line, "scan")
999 self.logger.info("use default adapter")
1000 self.ble_adapter_select()
1001 del self.peripheral_list[:]
1002 self.scan_quiet = args[1]
1004 self.scan_bg_implementation, timeout=args[0], identifier=args[2]
1008 def get_peripheral_devIdInfo(self, peripheral):
1009 if not peripheral.ServiceData:
1011 for advuuid in peripheral.ServiceData:
1012 if str(advuuid).lower() == str(chip_service).lower():
1013 return ParseServiceData(bytes(peripheral.ServiceData[advuuid]))
1016 def ble_debug_log(self, line):
1017 args = self.ParseInputLine(line)
1018 if int(args[0]) == 1:
1019 self.logger.setLevel(logging.DEBUG)
1020 self.logger.debug("current logging level is debug")
1022 self.logger.setLevel(logging.INFO)
1023 self.logger.info("current logging level is info")
1026 def CloseBle(self, connObj):
1027 """ Called by Chip to close the BLE connection."""
1028 # Workaround: comment out disconnect because of hang when close, plz call disconnect explicitly after close
1032 dcEvent = BleDisconnectEvent(BLE_ERROR_REMOTE_DEVICE_DISCONNECTED)
1033 self.chip_queue.put(dcEvent)
1034 self.devMgr.DriveBleIO()