Пример #1
0
def scan_chip_ble_devices(devCtrl):
    """
    BLE scan CHIP device
    BLE scanning for 10 seconds and collect the results 
    :param devCtrl: device controller instance
    :return: List of visible BLE devices
    """
    devices = []
    bleMgr = BleManager(devCtrl)
    bleMgr.scan("-t 10")

    for device in bleMgr.peripheral_list:
        devIdInfo = bleMgr.get_peripheral_devIdInfo(device)
        if devIdInfo:
            devInfo = devIdInfo.__dict__
            devInfo["name"] = device.Name
            devices.append(devInfo)

    return devices
Пример #2
0
    def __init__(self,
                 rendezvousAddr=None,
                 controllerNodeId=0,
                 bluetoothAdapter=None):
        self.lastNetworkId = None

        Cmd.__init__(self)

        Cmd.identchars = string.ascii_letters + string.digits + "-"

        if sys.stdin.isatty():
            self.prompt = "chip-device-ctrl > "
        else:
            self.use_rawinput = 0
            self.prompt = ""

        DeviceMgrCmd.command_names.sort()

        self.bleMgr = None

        self.devCtrl = ChipDeviceCtrl.ChipDeviceController(
            controllerNodeId=controllerNodeId,
            bluetoothAdapter=bluetoothAdapter)

        self.commissionableNodeCtrl = ChipCommissionableNodeCtrl.ChipCommissionableNodeController(
        )

        # If we are on Linux and user selects non-default bluetooth adapter.
        if sys.platform.startswith("linux") and (bluetoothAdapter is not None):
            try:
                self.bleMgr = BleManager(self.devCtrl)
                self.bleMgr.ble_adapter_select(
                    "hci{}".format(bluetoothAdapter))
            except Exception as ex:
                traceback.print_exc()
                print(
                    "Failed to initialize BLE, if you don't have BLE, run chip-device-ctrl with --no-ble"
                )
                raise ex

        self.historyFileName = os.path.expanduser(
            "~/.chip-device-ctrl-history")

        try:
            import readline

            if "libedit" in readline.__doc__:
                readline.parse_and_bind("bind ^I rl_complete")
            readline.set_completer_delims(" ")
            try:
                readline.read_history_file(self.historyFileName)
            except IOError:
                pass
        except ImportError:
            pass
    def do_bledebuglog(self, line):
        """
        ble-debug-log 0:1
          0: disable BLE debug log
          1: enable BLE debug log
        """
        if not self.bleMgr:
            self.bleMgr = BleManager(self.devCtrl)

        self.bleMgr.ble_debug_log(line)

        return
Пример #4
0
    def do_bleconnect(self, line):
        """
        ble-connect

        Connect to a BLE peripheral identified by line.
        """

        if not self.bleMgr:
            self.bleMgr = BleManager(self.devCtrl)
        self.bleMgr.connect(line)

        return
Пример #5
0
    def do_bledisconnect(self, line):
        """
        ble-disconnect

        Disconnect from a BLE peripheral.
        """

        if not self.bleMgr:
            self.bleMgr = BleManager(self.devCtrl)

        self.bleMgr.disconnect()

        return
Пример #6
0
    def do_blescanconnect(self, line):
        """
        ble-scan-connect

        Scan and connect to a BLE peripheral identified by line.
        """

        if not self.bleMgr:
            self.bleMgr = BleManager(self.devCtrl)

        self.bleMgr.scan_connect(line)

        return
    def do_blescan(self, line):
        """
        ble-scan

        Start BLE scanning operations.
        """

        if not self.bleMgr:
            self.bleMgr = BleManager(self.devCtrl)

        self.bleMgr.scan(line)

        return
Пример #8
0
    def do_bleconnect(self, line):
        """
        ble-connect <device-name>
        ble-connect <mac-address (linux only)>
        ble-connect <device-uuid>
        ble-connect <discriminator>

        Connect to a BLE peripheral identified by line.
        """

        if not self.bleMgr:
            self.bleMgr = BleManager(self.devCtrl)
        self.bleMgr.connect(line)

        return
    def do_bleadapterprint(self, line):
        """
        ble-adapter-print

        Print attached BLE adapter.
        """
        if sys.platform.startswith("linux"):
            if not self.bleMgr:
                self.bleMgr = BleManager(self.devCtrl)

            self.bleMgr.ble_adapter_print()
        else:
            print("ble-adapter-print only works in Linux")

        return
Пример #10
0
    def do_bleadapterselect(self, line):
        """
        ble-adapter-select

        Start BLE adapter select.
        """
        if sys.platform.startswith("linux"):
            if not self.bleMgr:
                self.bleMgr = BleManager(self.devCtrl)

            self.bleMgr.ble_adapter_select(line)
        else:
            print(
                "ble-adapter-select only works in Linux, ble-adapter-select mac_address"
            )

        return
Пример #11
0
    def do_bleadapterselect(self, line):
        """
        ble-adapter-select

        Start BLE adapter select, deprecated, you can select adapter by command line arguments.
        """
        if sys.platform.startswith("linux"):
            if not self.bleMgr:
                self.bleMgr = BleManager(self.devCtrl)

            self.bleMgr.ble_adapter_select(line)
            print(
                "This change only applies to ble-scan\n"
                "Please run device controller with --bluetooth-adapter=<adapter-name> to select adapter\n"
                + "e.g. chip-device-ctrl --bluetooth-adapter hci0")
        else:
            print(
                "ble-adapter-select only works in Linux, ble-adapter-select mac_address"
            )

        return
Пример #12
0
class DeviceMgrCmd(Cmd):
    def __init__(self,
                 rendezvousAddr=None,
                 controllerNodeId=0,
                 bluetoothAdapter=None):
        self.lastNetworkId = None

        Cmd.__init__(self)

        Cmd.identchars = string.ascii_letters + string.digits + "-"

        if sys.stdin.isatty():
            self.prompt = "chip-device-ctrl > "
        else:
            self.use_rawinput = 0
            self.prompt = ""

        DeviceMgrCmd.command_names.sort()

        self.bleMgr = None

        self.devCtrl = ChipDeviceCtrl.ChipDeviceController(
            controllerNodeId=controllerNodeId,
            bluetoothAdapter=bluetoothAdapter)

        # If we are on Linux and user selects non-default bluetooth adapter.
        if sys.platform.startswith("linux") and (bluetoothAdapter is not None):
            self.bleMgr = BleManager(self.devCtrl)
            self.bleMgr.ble_adapter_select("hci{}".format(bluetoothAdapter))

        self.historyFileName = os.path.expanduser(
            "~/.chip-device-ctrl-history")

        try:
            import readline

            if "libedit" in readline.__doc__:
                readline.parse_and_bind("bind ^I rl_complete")
            readline.set_completer_delims(" ")
            try:
                readline.read_history_file(self.historyFileName)
            except IOError:
                pass
        except ImportError:
            pass

    command_names = [
        "setup-payload",
        "ble-scan",
        "ble-adapter-select",
        "ble-adapter-print",
        "ble-debug-log",
        "connect",
        "resolve",
        "zcl",
        "zclread",
        "zclconfigure",
        "set-pairing-wifi-credential",
        "set-pairing-thread-credential",
    ]

    def parseline(self, line):
        cmd, arg, line = Cmd.parseline(self, line)
        if cmd:
            cmd = self.shortCommandName(cmd)
            line = cmd + " " + arg
        return cmd, arg, line

    def completenames(self, text, *ignored):
        return [
            name + " " for name in DeviceMgrCmd.command_names
            if name.startswith(text)
            or self.shortCommandName(name).startswith(text)
        ]

    def shortCommandName(self, cmd):
        return cmd.replace("-", "")

    def precmd(self, line):
        if not self.use_rawinput and line != "EOF" and line != "":
            print(">>> " + line)
        return line

    def postcmd(self, stop, line):
        if not stop and self.use_rawinput:
            self.prompt = "chip-device-ctrl > "
        return stop

    def postloop(self):
        try:
            import readline

            try:
                readline.write_history_file(self.historyFileName)
            except IOError:
                pass
        except ImportError:
            pass

    def do_help(self, line):
        if line:
            cmd, arg, unused = self.parseline(line)
            try:
                doc = getattr(self, "do_" + cmd).__doc__
            except AttributeError:
                doc = None
            if doc:
                self.stdout.write("%s\n" % textwrap.dedent(doc))
            else:
                self.stdout.write("No help on %s\n" % (line))
        else:
            self.print_topics(
                "\nAvailable commands (type help <name> for more information):",
                DeviceMgrCmd.command_names,
                15,
                80,
            )

    def do_close(self, line):
        """
        close

        Close the connection to the device.
        """

        args = shlex.split(line)

        if len(args) != 0:
            print("Usage:")
            self.do_help("close")
            return

        try:
            self.devCtrl.Close()
        except exceptions.ChipStackException as ex:
            print(str(ex))

    def do_setlogoutput(self, line):
        """
        set-log-output [ none | error | progress | detail ]

        Set the level of Chip logging output.
        """

        args = shlex.split(line)

        if len(args) == 0:
            print("Usage:")
            self.do_help("set-log-output")
            return
        if len(args) > 1:
            print("Unexpected argument: " + args[1])
            return

        category = args[0].lower()
        if category == "none":
            category = 0
        elif category == "error":
            category = 1
        elif category == "progress":
            category = 2
        elif category == "detail":
            category = 3
        else:
            print("Invalid argument: " + args[0])
            return

        try:
            self.devCtrl.SetLogFilter(category)
        except exceptions.ChipStackException as ex:
            print(str(ex))
            return

        print("Done.")

    def do_setuppayload(self, line):
        """
        setup-payload parse-manual <manual-pairing-code>
        setup-payload parse-qr <qr-code-payload>
        """
        try:
            args = shlex.split(line)
            if (len(args) != 2) or (args[0]
                                    not in ("parse-manual", "parse-qr")):
                self.do_help("setup-payload")
                return

            if args[0] == "parse-manual":
                SetupPayload().ParseManualPairingCode(args[1]).Print()

            if args[0] == "parse-qr":
                SetupPayload().ParseQrCode(args[1]).Print()

        except exceptions.ChipStackException as ex:
            print(str(ex))
            return

    def do_bleadapterselect(self, line):
        """
        ble-adapter-select

        Start BLE adapter select, deprecated, you can select adapter by command line arguments.
        """
        if sys.platform.startswith("linux"):
            if not self.bleMgr:
                self.bleMgr = BleManager(self.devCtrl)

            self.bleMgr.ble_adapter_select(line)
            print(
                "This change only applies to ble-scan\n"
                "Please run device controller with --bluetooth-adapter=<adapter-name> to select adapter\n"
                + "e.g. chip-device-ctrl --bluetooth-adapter hci0")
        else:
            print(
                "ble-adapter-select only works in Linux, ble-adapter-select mac_address"
            )

        return

    def do_bleadapterprint(self, line):
        """
        ble-adapter-print

        Print attached BLE adapter.
        """
        if sys.platform.startswith("linux"):
            if not self.bleMgr:
                self.bleMgr = BleManager(self.devCtrl)

            self.bleMgr.ble_adapter_print()
        else:
            print("ble-adapter-print only works in Linux")

        return

    def do_bledebuglog(self, line):
        """
        ble-debug-log 0:1
          0: disable BLE debug log
          1: enable BLE debug log
        """
        if not self.bleMgr:
            self.bleMgr = BleManager(self.devCtrl)

        self.bleMgr.ble_debug_log(line)

        return

    def do_blescan(self, line):
        """
        ble-scan

        Start BLE scanning operations.
        """

        if not self.bleMgr:
            self.bleMgr = BleManager(self.devCtrl)

        self.bleMgr.scan(line)

        return

    def do_connect(self, line):
        """
        connect -ip <ip address> <setup pin code> [<nodeid>]
        connect -ble <discriminator> <setup pin code> [<nodeid>]

        connect command is used for establishing a rendezvous session to the device.
        currently, only connect using setupPinCode is supported.

        TODO: Add more methods to connect to device (like cert for auth, and IP
              for connection)
        """

        try:
            args = shlex.split(line)
            if len(args) <= 1:
                print("Usage:")
                self.do_help("connect SetupPinCode")
                return

            nodeid = random.randint(1, 1000000)  # Just a random number
            if len(args) == 4:
                nodeid = int(args[3])
            print("Device is assigned with nodeid = {}".format(nodeid))

            if args[0] == "-ip" and len(args) >= 3:
                self.devCtrl.ConnectIP(args[1].encode("utf-8"), int(args[2]),
                                       nodeid)
            elif args[0] == "-ble" and len(args) >= 3:
                self.devCtrl.ConnectBLE(int(args[1]), int(args[2]), nodeid)
            else:
                print("Usage:")
                self.do_help("connect SetupPinCode")
                return
            print(
                "Device temporary node id (**this does not match spec**): {}".
                format(nodeid))
        except exceptions.ChipStackException as ex:
            print(str(ex))
            return

    def do_resolve(self, line):
        """
        resolve <fabricid> <nodeid>

        Resolve DNS-SD name corresponding with the given fabric and node IDs and
        update address of the node in the device controller.
        """
        try:
            args = shlex.split(line)
            if len(args) == 2:
                err = self.devCtrl.ResolveNode(int(args[0]), int(args[1]))
                if err == 0:
                    address = self.devCtrl.GetAddressAndPort(int(args[1]))
                    address = "{}:{}".format(
                        *address) if address else "unknown"
                    print("Current address: " + address)
            else:
                self.do_help("resolve")
        except exceptions.ChipStackException as ex:
            print(str(ex))
            return

    def do_zcl(self, line):
        """
        To send ZCL message to device:
        zcl <cluster> <command> <nodeid> <endpoint> <groupid> [key=value]...
        To get a list of clusters:
        zcl ?
        To get a list of commands in cluster:
        zcl ? <cluster>

        Send ZCL command to device nodeid
        """
        try:
            args = shlex.split(line)
            all_commands = self.devCtrl.ZCLCommandList()
            if len(args) == 1 and args[0] == '?':
                print('\n'.join(all_commands.keys()))
            elif len(args) == 2 and args[0] == '?':
                if args[1] not in all_commands:
                    raise exceptions.UnknownCluster(args[1])
                for commands in all_commands.get(args[1]).items():
                    args = ", ".join([
                        "{}: {}".format(argName, argType)
                        for argName, argType in commands[1].items()
                    ])
                    print(commands[0])
                    if commands[1]:
                        print("  ", args)
                    else:
                        print("  <no arguments>")
            elif len(args) > 4:
                if args[0] not in all_commands:
                    raise exceptions.UnknownCluster(args[0])
                command = all_commands.get(args[0]).get(args[1], None)
                # When command takes no arguments, (not command) is True
                if command == None:
                    raise exceptions.UnknownCommand(args[0], args[1])
                err, res = self.devCtrl.ZCLSend(args[0],
                                                args[1],
                                                int(args[2]),
                                                int(args[3]),
                                                int(args[4]),
                                                FormatZCLArguments(
                                                    args[5:], command),
                                                blocking=True)
                if err != 0:
                    print("Failed to receive command response: {}".format(res))
                elif res != None:
                    print("Received command status response:")
                    print(res)
                else:
                    print("Success, no status code is attached with response.")
            else:
                self.do_help("zcl")
        except exceptions.ChipStackException as ex:
            print("An exception occurred during process ZCL command:")
            print(str(ex))
        except Exception as ex:
            import traceback
            print("An exception occurred during processing input:")
            traceback.print_exc()
            print(str(ex))

    def do_zclread(self, line):
        """
        To read ZCL attribute:
        zclread <cluster> <attribute> <nodeid> <endpoint> <groupid>
        """
        try:
            args = shlex.split(line)
            all_attrs = self.devCtrl.ZCLAttributeList()
            if len(args) == 1 and args[0] == '?':
                print('\n'.join(all_attrs.keys()))
            elif len(args) == 2 and args[0] == '?':
                if args[1] not in all_attrs:
                    raise exceptions.UnknownCluster(args[1])
                print('\n'.join(all_attrs.get(args[1])))
            elif len(args) == 5:
                if args[0] not in all_attrs:
                    raise exceptions.UnknownCluster(args[0])
                self.devCtrl.ZCLReadAttribute(args[0], args[1], int(args[2]),
                                              int(args[3]), int(args[4]))
            else:
                self.do_help("zclread")
        except exceptions.ChipStackException as ex:
            print("An exception occurred during reading ZCL attribute:")
            print(str(ex))
        except Exception as ex:
            print("An exception occurred during processing input:")
            print(str(ex))

    def do_zclconfigure(self, line):
        """
        To configure ZCL attribute reporting:
        zclconfigure <cluster> <attribute> <nodeid> <endpoint> <minInterval> <maxInterval> <change>
        """
        try:
            args = shlex.split(line)
            all_attrs = self.devCtrl.ZCLAttributeList()
            if len(args) == 1 and args[0] == '?':
                print('\n'.join(all_attrs.keys()))
            elif len(args) == 2 and args[0] == '?':
                if args[1] not in all_attrs:
                    raise exceptions.UnknownCluster(args[1])
                print('\n'.join(all_attrs.get(args[1])))
            elif len(args) == 7:
                if args[0] not in all_attrs:
                    raise exceptions.UnknownCluster(args[0])
                self.devCtrl.ZCLConfigureAttribute(args[0], args[1],
                                                   int(args[2]), int(args[3]),
                                                   int(args[4]), int(args[5]),
                                                   int(args[6]))
            else:
                self.do_help("zclconfigure")
        except exceptions.ChipStackException as ex:
            print(
                "An exception occurred during configuring reporting of ZCL attribute:"
            )
            print(str(ex))
        except Exception as ex:
            print("An exception occurred during processing input:")
            print(str(ex))

    def do_setpairingwificredential(self, line):
        """
        set-pairing-wifi-credential <ssid> <password>

        Set WiFi credential to be used while pairing a Wi-Fi device
        """
        try:
            args = shlex.split(line)
            if len(args) == 2:
                self.devCtrl.SetWifiCredential(args[0], args[1])
                print("WiFi credential set")
            else:
                self.do_help("set-pairing-wifi-credential")
        except exceptions.ChipStackException as ex:
            print(str(ex))
            return

    def do_setpairingthreadcredential(self, line):
        """
        set-pairing-thread-credential <channel> <panid> <masterkey>

        Set Thread credential to be used while pairing a Thread device
        """
        try:
            args = shlex.split(line)
            if len(args) == 3:
                self.devCtrl.SetThreadCredential(int(args[0]),
                                                 int(args[1], 16), args[2])
                print("Thread credential set")
            else:
                self.do_help("set-pairing-thread-credential")
        except exceptions.ChipStackException as ex:
            print(str(ex))
            return

    def do_history(self, line):
        """
        history

        Show previously executed commands.
        """

        try:
            import readline

            h = readline.get_current_history_length()
            for n in range(1, h + 1):
                print(readline.get_history_item(n))
        except ImportError:
            pass

    def do_h(self, line):
        self.do_history(line)

    def do_exit(self, line):
        return True

    def do_quit(self, line):
        return True

    def do_q(self, line):
        return True

    def do_EOF(self, line):
        print()
        return True

    def emptyline(self):
        pass
Пример #13
0
class DeviceMgrCmd(Cmd):
    def __init__(self, rendezvousAddr=None):
        self.lastNetworkId = None

        Cmd.__init__(self)

        Cmd.identchars = string.ascii_letters + string.digits + "-"

        if sys.stdin.isatty():
            self.prompt = "chip-device-ctrl > "
        else:
            self.use_rawinput = 0
            self.prompt = ""

        DeviceMgrCmd.command_names.sort()

        self.bleMgr = None

        self.devCtrl = ChipDeviceCtrl.ChipDeviceController()

        self.historyFileName = os.path.expanduser(
            "~/.chip-device-ctrl-history")

        try:
            import readline

            if "libedit" in readline.__doc__:
                readline.parse_and_bind("bind ^I rl_complete")
            readline.set_completer_delims(" ")
            try:
                readline.read_history_file(self.historyFileName)
            except IOError:
                pass
        except ImportError:
            pass

    command_names = [
        "close",
        "btp-connect",
        "ble-scan",
        "ble-connect",
        "ble-disconnect",
        "ble-scan-connect",
        "ble-adapter-select",
        "ble-adapter-print",
        "ble-debug-log",
        "connect",
        "set-pairing-wifi-credential",
    ]

    def parseline(self, line):
        cmd, arg, line = Cmd.parseline(self, line)
        if cmd:
            cmd = self.shortCommandName(cmd)
            line = cmd + " " + arg
        return cmd, arg, line

    def completenames(self, text, *ignored):
        return [
            name + " " for name in DeviceMgrCmd.command_names
            if name.startswith(text)
            or self.shortCommandName(name).startswith(text)
        ]

    def shortCommandName(self, cmd):
        return cmd.replace("-", "")

    def precmd(self, line):
        if not self.use_rawinput and line != "EOF" and line != "":
            print(">>> " + line)
        return line

    def postcmd(self, stop, line):
        if not stop and self.use_rawinput:
            self.prompt = "chip-device-ctrl > "
        return stop

    def postloop(self):
        try:
            import readline

            try:
                readline.write_history_file(self.historyFileName)
            except IOError:
                pass
        except ImportError:
            pass

    def do_help(self, line):
        if line:
            cmd, arg, unused = self.parseline(line)
            try:
                doc = getattr(self, "do_" + cmd).__doc__
            except AttributeError:
                doc = None
            if doc:
                self.stdout.write("%s\n" % textwrap.dedent(doc))
            else:
                self.stdout.write("No help on %s\n" % (line))
        else:
            self.print_topics(
                "\nAvailable commands (type help <name> for more information):",
                DeviceMgrCmd.command_names,
                15,
                80,
            )

    def do_close(self, line):
        """
        close

        Close the connection to the device.
        """

        args = shlex.split(line)

        if len(args) != 0:
            print("Usage:")
            self.do_help("close")
            return

        try:
            self.devCtrl.Close()
        except ChipStack.ChipStackException as ex:
            print(str(ex))

    def do_setlogoutput(self, line):
        """
        set-log-output [ none | error | progress | detail ]

        Set the level of Chip logging output.
        """

        args = shlex.split(line)

        if len(args) == 0:
            print("Usage:")
            self.do_help("set-log-output")
            return
        if len(args) > 1:
            print("Unexpected argument: " + args[1])
            return

        category = args[0].lower()
        if category == "none":
            category = 0
        elif category == "error":
            category = 1
        elif category == "progress":
            category = 2
        elif category == "detail":
            category = 3
        else:
            print("Invalid argument: " + args[0])
            return

        try:
            self.devCtrl.SetLogFilter(category)
        except ChipStack.ChipStackException as ex:
            print(str(ex))
            return

        print("Done.")

    def do_bleadapterselect(self, line):
        """
        ble-adapter-select

        Start BLE adapter select.
        """
        if sys.platform.startswith("linux"):
            if not self.bleMgr:
                self.bleMgr = BleManager(self.devCtrl)

            self.bleMgr.ble_adapter_select(line)
        else:
            print(
                "ble-adapter-select only works in Linux, ble-adapter-select mac_address"
            )

        return

    def do_bleadapterprint(self, line):
        """
        ble-adapter-print

        Print attached BLE adapter.
        """
        if sys.platform.startswith("linux"):
            if not self.bleMgr:
                self.bleMgr = BleManager(self.devCtrl)

            self.bleMgr.ble_adapter_print()
        else:
            print("ble-adapter-print only works in Linux")

        return

    def do_bledebuglog(self, line):
        """
        ble-debug-log 0:1
          0: disable BLE debug log
          1: enable BLE debug log
        """
        if not self.bleMgr:
            self.bleMgr = BleManager(self.devCtrl)

        self.bleMgr.ble_debug_log(line)

        return

    def do_blescan(self, line):
        """
        ble-scan

        Start BLE scanning operations.
        """

        if not self.bleMgr:
            self.bleMgr = BleManager(self.devCtrl)

        self.bleMgr.scan(line)

        return

    def do_bleconnect(self, line):
        """
        ble-connect

        Connect to a BLE peripheral identified by line.
        """

        if not self.bleMgr:
            self.bleMgr = BleManager(self.devCtrl)
        self.bleMgr.connect(line)

        return

    def do_blescanconnect(self, line):
        """
        ble-scan-connect

        Scan and connect to a BLE peripheral identified by line.
        """

        if not self.bleMgr:
            self.bleMgr = BleManager(self.devCtrl)

        self.bleMgr.scan_connect(line)

        return

    def do_bledisconnect(self, line):
        """
        ble-disconnect

        Disconnect from a BLE peripheral.
        """

        if not self.bleMgr:
            self.bleMgr = BleManager(self.devCtrl)

        self.bleMgr.disconnect()

        return

    def do_btpconnect(self, line):
        """
        connect .

        """
        try:
            self.devCtrl.ConnectBle(bleConnection=FAKE_CONN_OBJ_VALUE)
        except ChipStack.ChipStackException as ex:
            print(str(ex))
            return

        print("BTP Connected")

    def do_connect(self, line):
        """
        connect (via BLE) <setup pin code>

        connect command is used for establishing a rendezvous session to the device.
        currently, only connect using setupPinCode is supported.

        TODO: Add more methods to connect to device (like cert for auth, and IP
              for connection)
        """

        try:
            args = shlex.split(line)
            if not args:
                print("Usage:")
                self.do_help("connect SetupPinCode")
                return
            if len(args) > 1:
                print("Unexpected argument: " + args[1])
                return
            self.devCtrl.Connect(FAKE_CONN_OBJ_VALUE, int(args[0]))
        except ChipStack.ChipStackException as ex:
            print(str(ex))
            return
        print("Connected")

    def do_setpairingwificredential(self, line):
        """
        set-pairing-wifi-credential

        Set WiFi credential for pairing, will sent to device
        """
        try:
            args = shlex.split(line)
            self.devCtrl.SetWifiCredential(args[0], args[1])
        except ChipStack.ChipStackException as ex:
            print(str(ex))
            return

        print("WiFi credential set")

    def do_history(self, line):
        """
        history

        Show previously executed commands.
        """

        try:
            import readline

            h = readline.get_current_history_length()
            for n in range(1, h + 1):
                print(readline.get_history_item(n))
        except ImportError:
            pass

    def do_h(self, line):
        self.do_history(line)

    def do_exit(self, line):
        return True

    def do_quit(self, line):
        return True

    def do_q(self, line):
        return True

    def do_EOF(self, line):
        print()
        return True

    def emptyline(self):
        pass
class DeviceMgrCmd(Cmd):
    def __init__(self, rendezvousAddr=None, controllerNodeId=0, bluetoothAdapter=None):
        self.lastNetworkId = None

        Cmd.__init__(self)

        Cmd.identchars = string.ascii_letters + string.digits + "-"

        if sys.stdin.isatty():
            self.prompt = "chip-device-ctrl > "
        else:
            self.use_rawinput = 0
            self.prompt = ""

        DeviceMgrCmd.command_names.sort()

        self.bleMgr = None

        self.devCtrl = ChipDeviceCtrl.ChipDeviceController(
            controllerNodeId=controllerNodeId, bluetoothAdapter=bluetoothAdapter)

        self.commissionableNodeCtrl = ChipCommissionableNodeCtrl.ChipCommissionableNodeController()
            
        # If we are on Linux and user selects non-default bluetooth adapter.
        if sys.platform.startswith("linux") and (bluetoothAdapter is not None):
            try:
                self.bleMgr = BleManager(self.devCtrl)
                self.bleMgr.ble_adapter_select("hci{}".format(bluetoothAdapter))
            except Exception as ex:
                traceback.print_exc()
                print("Failed to initialize BLE, if you don't have BLE, run chip-device-ctrl with --no-ble")
                raise ex

        self.historyFileName = os.path.expanduser(
            "~/.chip-device-ctrl-history")

        try:
            import readline

            if "libedit" in readline.__doc__:
                readline.parse_and_bind("bind ^I rl_complete")
            readline.set_completer_delims(" ")
            try:
                readline.read_history_file(self.historyFileName)
            except IOError:
                pass
        except ImportError:
            pass

    command_names = [
        "setup-payload",

        "ble-scan",
        "ble-adapter-select",
        "ble-adapter-print",
        "ble-debug-log",

        "connect",
        "close-ble",
        "resolve",
        "zcl",
        "zclread",
        "zclconfigure",

        "discover",

        "set-pairing-wifi-credential",
        "set-pairing-thread-credential",

        "get-fabricid",
    ]

    def parseline(self, line):
        cmd, arg, line = Cmd.parseline(self, line)
        if cmd:
            cmd = self.shortCommandName(cmd)
            line = cmd + " " + arg
        return cmd, arg, line

    def completenames(self, text, *ignored):
        return [
            name + " "
            for name in DeviceMgrCmd.command_names
            if name.startswith(text) or self.shortCommandName(name).startswith(text)
        ]

    def shortCommandName(self, cmd):
        return cmd.replace("-", "")

    def precmd(self, line):
        if not self.use_rawinput and line != "EOF" and line != "":
            print(">>> " + line)
        return line

    def postcmd(self, stop, line):
        if not stop and self.use_rawinput:
            self.prompt = "chip-device-ctrl > "
        return stop

    def postloop(self):
        try:
            import readline

            try:
                readline.write_history_file(self.historyFileName)
            except IOError:
                pass
        except ImportError:
            pass

    def do_help(self, line):
        if line:
            cmd, arg, unused = self.parseline(line)
            try:
                doc = getattr(self, "do_" + cmd).__doc__
            except AttributeError:
                doc = None
            if doc:
                self.stdout.write("%s\n" % textwrap.dedent(doc))
            else:
                self.stdout.write("No help on %s\n" % (line))
        else:
            self.print_topics(
                "\nAvailable commands (type help <name> for more information):",
                DeviceMgrCmd.command_names,
                15,
                80,
            )

    def do_closeble(self, line):
        """
        close-ble

        Close the ble connection to the device.
        """

        args = shlex.split(line)

        if len(args) != 0:
            print("Usage:")
            self.do_help("close")
            return

        try:
            self.devCtrl.CloseBLEConnection()
        except exceptions.ChipStackException as ex:
            print(str(ex))

    def do_setlogoutput(self, line):
        """
        set-log-output [ none | error | progress | detail ]

        Set the level of Chip logging output.
        """

        args = shlex.split(line)

        if len(args) == 0:
            print("Usage:")
            self.do_help("set-log-output")
            return
        if len(args) > 1:
            print("Unexpected argument: " + args[1])
            return

        category = args[0].lower()
        if category == "none":
            category = 0
        elif category == "error":
            category = 1
        elif category == "progress":
            category = 2
        elif category == "detail":
            category = 3
        else:
            print("Invalid argument: " + args[0])
            return

        try:
            self.devCtrl.SetLogFilter(category)
        except exceptions.ChipStackException as ex:
            print(str(ex))
            return


    def do_setuppayload(self, line):
        """
        setup-payload generate [options]

        Options:
          -v   Vendor ID
          -p   Product ID
          -cf  Custom Flow [Standard = 0, UserActionRequired = 1, Custom = 2]
          -dc  Discovery Capabilities [SoftAP = 1 | BLE = 2 | OnNetwork = 4]
          -dv  Discriminator Value
          -ps  Passcode

        setup-payload parse-manual <manual-pairing-code>
        setup-payload parse-qr <qr-code-payload>
        """
        try:
            arglist = shlex.split(line)
            if arglist[0] not in ("generate", "parse-manual", "parse-qr"):
                self.do_help("setup-payload")
                return

            if arglist[0] == "generate":
                parser = argparse.ArgumentParser()
                parser.add_argument("-v", type=int, default=0, dest='vendorId')
                parser.add_argument("-p", type=int, default=0, dest='productId')
                parser.add_argument('-cf', type=int, default=0, dest='customFlow')
                parser.add_argument("-dc", type=int, default=0, dest='capabilities')
                parser.add_argument("-dv", type=int, default=0, dest='discriminator')
                parser.add_argument("-ps", type=int, dest='passcode')                
                args = parser.parse_args(arglist[1:])

                SetupPayload().PrintOnboardingCodes(args.passcode, args.vendorId, args.productId, args.discriminator, args.customFlow, args.capabilities)

            if arglist[0] == "parse-manual":
                SetupPayload().ParseManualPairingCode(arglist[1]).Print()

            if arglist[0] == "parse-qr":
                SetupPayload().ParseQrCode(arglist[1]).Print()

        except exceptions.ChipStackException as ex:
            print(str(ex))
            return

    def do_bleadapterselect(self, line):
        """
        ble-adapter-select

        Start BLE adapter select, deprecated, you can select adapter by command line arguments.
        """
        if sys.platform.startswith("linux"):
            if not self.bleMgr:
                self.bleMgr = BleManager(self.devCtrl)

            self.bleMgr.ble_adapter_select(line)
            print(
                "This change only applies to ble-scan\n"
                "Please run device controller with --bluetooth-adapter=<adapter-name> to select adapter\n" +
                "e.g. chip-device-ctrl --bluetooth-adapter hci0"
            )
        else:
            print(
                "ble-adapter-select only works in Linux, ble-adapter-select mac_address"
            )

        return

    def do_bleadapterprint(self, line):
        """
        ble-adapter-print

        Print attached BLE adapter.
        """
        if sys.platform.startswith("linux"):
            if not self.bleMgr:
                self.bleMgr = BleManager(self.devCtrl)

            self.bleMgr.ble_adapter_print()
        else:
            print("ble-adapter-print only works in Linux")

        return

    def do_bledebuglog(self, line):
        """
        ble-debug-log 0:1
          0: disable BLE debug log
          1: enable BLE debug log
        """
        if not self.bleMgr:
            self.bleMgr = BleManager(self.devCtrl)

        self.bleMgr.ble_debug_log(line)

        return

    def do_blescan(self, line):
        """
        ble-scan

        Start BLE scanning operations.
        """

        if not self.bleMgr:
            self.bleMgr = BleManager(self.devCtrl)

        self.bleMgr.scan(line)

        return

    def ConnectFromSetupPayload(self, setupPayload, nodeid):
        # TODO(cecille): Get this from the C++ code?
        softap = 1 << 0
        ble = 1 << 1
        onnetwork = 1 << 2
        # Devices may be uncommissioned, or may already be on the network. Need to check both ways.
        # TODO(cecille): implement soft-ap connection.

        if int(setupPayload.attributes["RendezvousInformation"]) & onnetwork:
            print("Attempting to find device on Network")
            longDiscriminator = ctypes.c_uint16(int(setupPayload.attributes['Discriminator']))
            self.devCtrl.DiscoverCommissionableNodesLongDiscriminator(longDiscriminator)
            print("Waiting for device responses...")
            strlen = 100;
            addrStrStorage = ctypes.create_string_buffer(strlen)
            count = 0
            # If this device is on the network and we're looking specifically for 1 device,
            # expect a quick response.
            maxWaitTime = 1
            ok = False
            while count < maxWaitTime:
                ok = self.devCtrl.GetIPForDiscoveredDevice(0, addrStrStorage, strlen)
                if ok:
                    break
                time.sleep(0.2)
                count = count + 0.2
            if ok:
                addrStr = addrStrStorage.value.decode('utf-8')
                print("Connecting to device at " + addrStr)
                pincode = ctypes.c_uint32(int(setupPayload.attributes['SetUpPINCode']))
                if self.devCtrl.ConnectIP(addrStrStorage, pincode, nodeid):
                    print("Connected")
                    return 0
                else:
                    print("Unable to connect")
                    return 1
            else:
                print("Unable to locate device on network")

        if int(setupPayload.attributes["RendezvousInformation"]) & ble:
            print("Attempting to connect via BLE")
            longDiscriminator = ctypes.c_uint16(int(setupPayload.attributes['Discriminator']))
            pincode = ctypes.c_uint32(int(setupPayload.attributes['SetUpPINCode']))
            if self.devCtrl.ConnectBLE(longDiscriminator, pincode, nodeid):
                print("Connected")
                return 0
            else:
                print("Unable to connect")
        return -1

    def do_connect(self, line):
        """
        connect -ip <ip address> <setup pin code> [<nodeid>]
        connect -ble <discriminator> <setup pin code> [<nodeid>]
        connect -qr <qr code> [<nodeid>]

        connect command is used for establishing a rendezvous session to the device.
        currently, only connect using setupPinCode is supported.
        -qr option will connect to the first device with a matching long discriminator.

        TODO: Add more methods to connect to device (like cert for auth, and IP
              for connection)
        """

        try:
            args = shlex.split(line)
            if len(args) <= 1:
                print("Usage:")
                self.do_help("connect SetupPinCode")
                return

            nodeid = random.randint(1, 1000000)  # Just a random number
            if len(args) == 4:
                nodeid = int(args[3])
            print("Device is assigned with nodeid = {}".format(nodeid))

            if args[0] == "-ip" and len(args) >= 3:
                self.devCtrl.ConnectIP(args[1].encode(
                    "utf-8"), int(args[2]), nodeid)
            elif args[0] == "-ble" and len(args) >= 3:
                self.devCtrl.ConnectBLE(int(args[1]), int(args[2]), nodeid)
            elif args[0] == '-qr' and len(args) >=2:
                if len(args) == 3:
                    nodeid = int(args[2])
                print("Parsing QR code {}".format(args[1]))
                setupPayload = SetupPayload().ParseQrCode(args[1])
                self.ConnectFromSetupPayload(setupPayload, nodeid)
            else:
                print("Usage:")
                self.do_help("connect SetupPinCode")
                return
            print(
                "Device temporary node id (**this does not match spec**): {}".format(nodeid))
        except exceptions.ChipStackException as ex:
            print(str(ex))
            return

    def do_resolve(self, line):
        """
        resolve <fabricid> <nodeid>

        Resolve DNS-SD name corresponding with the given fabric and node IDs and
        update address of the node in the device controller.
        """
        try:
            args = shlex.split(line)
            if len(args) == 2:
                err = self.devCtrl.ResolveNode(int(args[0]), int(args[1]))
                if err == 0:
                    address = self.devCtrl.GetAddressAndPort(int(args[1]))
                    address = "{}:{}".format(
                        *address) if address else "unknown"
                    print("Current address: " + address)
            else:
                self.do_help("resolve")
        except exceptions.ChipStackException as ex:
            print(str(ex))
            return

    def wait_for_one_discovered_device(self):
        print("Waiting for device responses...")
        strlen = 100;
        addrStrStorage = ctypes.create_string_buffer(strlen)
        count = 0
        maxWaitTime = 2
        while (not self.devCtrl.GetIPForDiscoveredDevice(0, addrStrStorage, strlen) and count < maxWaitTime):
            time.sleep(0.2)
            count = count + 0.2

    def wait_for_many_discovered_devices(self):
        # Discovery happens through mdns, which means we need to wait for responses to come back.
        # TODO(cecille): I suppose we could make this a command line arg. Or Add a callback when
        # x number of responses are received. For now, just 2 seconds. We can all wait that long.
        print("Waiting for device responses...")
        time.sleep(2)

    def do_discover(self, line):
        """
        discover -qr qrcode
        discover -all
        discover -l long_discriminator
        discover -s short_discriminator
        discover -v vendor_id
        discover -t device_type
        discover -c commissioning_enabled
        discover -a

        discover command is used to discover available devices.
        """
        try:
            arglist = shlex.split(line)
            if len(arglist) < 1:
                print("Usage:")
                self.do_help("discover")
                return
            parser = argparse.ArgumentParser()
            group = parser.add_mutually_exclusive_group()
            group.add_argument('-all', help='discover all commissionable nodes and commissioners', action='store_true')
            group.add_argument('-qr', help='discover commissionable nodes matching provided QR code', type=str)
            group.add_argument('-l', help='discover commissionable nodes with given long discriminator', type=int)
            group.add_argument('-s', help='discover commissionable nodes with given short discriminator', type=int)
            group.add_argument('-v', help='discover commissionable nodes wtih given vendor ID', type=int)
            group.add_argument('-t', help='discover commissionable nodes with given device type', type=int)
            group.add_argument('-c', help='discover commissionable nodes with given commissioning mode', type=int)
            group.add_argument('-a', help='discover commissionable nodes put in commissioning mode from command', action='store_true')
            args=parser.parse_args(arglist)
            if args.all:
                self.commissionableNodeCtrl.DiscoverCommissioners()
                self.wait_for_many_discovered_devices()
                self.commissionableNodeCtrl.PrintDiscoveredCommissioners()
                self.devCtrl.DiscoverAllCommissioning()
                self.wait_for_many_discovered_devices()
            elif args.qr is not None:
                setupPayload = SetupPayload().ParseQrCode(args.qr)
                longDiscriminator = ctypes.c_uint16(int(setupPayload.attributes['Discriminator']))
                self.devCtrl.DiscoverCommissionableNodesLongDiscriminator(longDiscriminator)
                self.wait_for_one_discovered_device()
            elif args.l is not None:
                self.devCtrl.DiscoverCommissionableNodesLongDiscriminator(ctypes.c_uint16(args.l))
                self.wait_for_one_discovered_device()
            elif args.s is not None:
                self.devCtrl.DiscoverCommissionableNodesShortDiscriminator(ctypes.c_uint16(args.s))
                self.wait_for_one_discovered_device()
            elif args.v is not None:
                self.devCtrl.DiscoverCommissionableNodesVendor(ctypes.c_uint16(args.v))
                self.wait_for_many_discovered_devices()
            elif args.t is not None:
                self.devCtrl.DiscoverCommissionableNodesDeviceType(ctypes.c_uint16(args.t))
                self.wait_for_many_discovered_devices()
            elif args.c is not None:
                self.devCtrl.DiscoverCommissionableNodesCommissioningEnabled(ctypes.c_uint16(args.c))
                self.wait_for_many_discovered_devices()
            elif args.a is not None:
                self.devCtrl.DiscoverCommissionableNodesCommissioningEnabledFromCommand()
                self.wait_for_many_discovered_devices()
            else:
                self.do_help("discover")
                return
            self.devCtrl.PrintDiscoveredDevices()
        except exceptions.ChipStackException as ex:
            print('exception')
            print(str(ex))
            return
        except:
            self.do_help("discover")
            return

    def do_zcl(self, line):
        """
        To send ZCL message to device:
        zcl <cluster> <command> <nodeid> <endpoint> <groupid> [key=value]...
        To get a list of clusters:
        zcl ?
        To get a list of commands in cluster:
        zcl ? <cluster>

        Send ZCL command to device nodeid
        """
        try:
            args = shlex.split(line)
            all_commands = self.devCtrl.ZCLCommandList()
            if len(args) == 1 and args[0] == '?':
                print('\n'.join(all_commands.keys()))
            elif len(args) == 2 and args[0] == '?':
                if args[1] not in all_commands:
                    raise exceptions.UnknownCluster(args[1])
                for commands in all_commands.get(args[1]).items():
                    args = ", ".join(["{}: {}".format(argName, argType)
                                      for argName, argType in commands[1].items()])
                    print(commands[0])
                    if commands[1]:
                        print("  ", args)
                    else:
                        print("  <no arguments>")
            elif len(args) > 4:
                if args[0] not in all_commands:
                    raise exceptions.UnknownCluster(args[0])
                command = all_commands.get(args[0]).get(args[1], None)
                # When command takes no arguments, (not command) is True
                if command == None:
                    raise exceptions.UnknownCommand(args[0], args[1])
                err, res = self.devCtrl.ZCLSend(args[0], args[1], int(
                    args[2]), int(args[3]), int(args[4]), FormatZCLArguments(args[5:], command), blocking=True)
                if err != 0:
                    print("Failed to receive command response: {}".format(res))
                elif res != None:
                    print("Received command status response:")
                    print(res)
                else:
                    print("Success, no status code is attached with response.")
            else:
                self.do_help("zcl")
        except exceptions.ChipStackException as ex:
            print("An exception occurred during process ZCL command:")
            print(str(ex))
        except Exception as ex:
            import traceback
            print("An exception occurred during processing input:")
            traceback.print_exc()
            print(str(ex))

    def do_zclread(self, line):
        """
        To read ZCL attribute:
        zclread <cluster> <attribute> <nodeid> <endpoint> <groupid>
        """
        try:
            args = shlex.split(line)
            all_attrs = self.devCtrl.ZCLAttributeList()
            if len(args) == 1 and args[0] == '?':
                print('\n'.join(all_attrs.keys()))
            elif len(args) == 2 and args[0] == '?':
                if args[1] not in all_attrs:
                    raise exceptions.UnknownCluster(args[1])
                print('\n'.join(all_attrs.get(args[1]).keys()))
            elif len(args) == 5:
                if args[0] not in all_attrs:
                    raise exceptions.UnknownCluster(args[0])
                res = self.devCtrl.ZCLReadAttribute(args[0], args[1], int(
                    args[2]), int(args[3]), int(args[4]))
                if res != None:
                    print(repr(res))
            else:
                self.do_help("zclread")
        except exceptions.ChipStackException as ex:
            print("An exception occurred during reading ZCL attribute:")
            print(str(ex))
        except Exception as ex:
            print("An exception occurred during processing input:")
            print(str(ex))

    def do_zclwrite(self, line):
        """
        To write ZCL attribute:
        zclwrite <cluster> <attribute> <nodeid> <endpoint> <groupid> <value>
        """
        try:
            args = shlex.split(line)
            all_attrs = self.devCtrl.ZCLAttributeList()
            if len(args) == 1 and args[0] == '?':
                print('\n'.join(all_attrs.keys()))
            elif len(args) == 2 and args[0] == '?':
                if args[1] not in all_attrs:
                    raise exceptions.UnknownCluster(args[1])
                cluster_attrs = all_attrs.get(args[1], {})
                print('\n'.join(["{}: {}".format(key, cluster_attrs[key]["type"]) for key in cluster_attrs.keys() if cluster_attrs[key].get("writable", False)]))
            elif len(args) == 6:
                if args[0] not in all_attrs:
                    raise exceptions.UnknownCluster(args[0])
                attribute_type = all_attrs.get(args[0], {}).get(
                    args[1], {}).get("type", None)
                self.devCtrl.ZCLWriteAttribute(args[0], args[1], int(
                    args[2]), int(args[3]), int(args[4]), ParseValueWithType(args[5], attribute_type))
            else:
                self.do_help("zclwrite")
        except exceptions.ChipStackException as ex:
            print("An exception occurred during writing ZCL attribute:")
            print(str(ex))
        except Exception as ex:
            print("An exception occurred during processing input:")
            print(str(ex))

    def do_zclconfigure(self, line):
        """
        To configure ZCL attribute reporting:
        zclconfigure <cluster> <attribute> <nodeid> <endpoint> <minInterval> <maxInterval> <change>
        """
        try:
            args = shlex.split(line)
            all_attrs = self.devCtrl.ZCLAttributeList()
            if len(args) == 1 and args[0] == '?':
                print('\n'.join(all_attrs.keys()))
            elif len(args) == 2 and args[0] == '?':
                if args[1] not in all_attrs:
                    raise exceptions.UnknownCluster(args[1])
                cluster_attrs = all_attrs.get(args[1], {})
                print('\n'.join([key for key in cluster_attrs.keys() if cluster_attrs[key].get("reportable", False)]))
            elif len(args) == 7:
                if args[0] not in all_attrs:
                    raise exceptions.UnknownCluster(args[0])
                self.devCtrl.ZCLConfigureAttribute(args[0], args[1], int(
                    args[2]), int(args[3]), int(args[4]), int(args[5]), int(args[6]))
            else:
                self.do_help("zclconfigure")
        except exceptions.ChipStackException as ex:
            print("An exception occurred during configuring reporting of ZCL attribute:")
            print(str(ex))
        except Exception as ex:
            print("An exception occurred during processing input:")
            print(str(ex))

    def do_setpairingwificredential(self, line):
        """
        set-pairing-wifi-credential

        Removed, use network commissioning cluster instead.
        """
        print("Pairing WiFi Credential is nolonger available, use NetworkCommissioning cluster instead.")

    def do_setpairingthreadcredential(self, line):
        """
        set-pairing-thread-credential

        Removed, use network commissioning cluster instead.
        """
        print("Pairing Thread Credential is nolonger available, use NetworkCommissioning cluster instead.")

    def do_getfabricid(self, line):
        """
          get-fabricid

          Read the current Fabric Id of the controller device, return 0 if not available.
        """
        try:
            args = shlex.split(line)

            if (len(args) > 0):
                print("Unexpected argument: " + args[1])
                return

            fabricid = self.devCtrl.GetFabricId()
        except exceptions.ChipStackException as ex:
            print("An exception occurred during reading FabricID:")
            print(str(ex))
            return

        print("Get fabric ID complete")
        print("Fabric ID: " + hex(fabricid))

    def do_history(self, line):
        """
        history

        Show previously executed commands.
        """

        try:
            import readline

            h = readline.get_current_history_length()
            for n in range(1, h + 1):
                print(readline.get_history_item(n))
        except ImportError:
            pass

    def do_h(self, line):
        self.do_history(line)

    def do_exit(self, line):
        return True

    def do_quit(self, line):
        return True

    def do_q(self, line):
        return True

    def do_EOF(self, line):
        print()
        return True

    def emptyline(self):
        pass
Пример #15
0
    def __init__(self,
                 rendezvousAddr=None,
                 controllerNodeId=1,
                 bluetoothAdapter=None):
        self.lastNetworkId = None
        self.replHint = None

        pretty.install(indent_guides=True, expand_all=True)

        coloredlogs.install(level='DEBUG')
        chip.logging.RedirectToPythonLogging()

        logging.getLogger().setLevel(logging.DEBUG)
        warnings.showwarning = ShowColoredWarnings

        Cmd.__init__(self)

        Cmd.identchars = string.ascii_letters + string.digits + "-"

        if sys.stdin.isatty():
            self.prompt = "chip-device-ctrl > "
        else:
            self.use_rawinput = 0
            self.prompt = ""

        DeviceMgrCmd.command_names.sort()

        self.bleMgr = None

        self.chipStack = ChipStack.ChipStack(
            bluetoothAdapter=bluetoothAdapter,
            persistentStoragePath='/tmp/chip-device-ctrl-storage.json')
        self.fabricAdmin = FabricAdmin.FabricAdmin(0xFFF1)
        self.devCtrl = self.fabricAdmin.NewController(nodeId=controllerNodeId,
                                                      useTestCommissioner=True)

        self.commissionableNodeCtrl = ChipCommissionableNodeCtrl.ChipCommissionableNodeController(
            self.chipStack)

        # If we are on Linux and user selects non-default bluetooth adapter.
        if sys.platform.startswith("linux") and (bluetoothAdapter is not None):
            try:
                self.bleMgr = BleManager(self.devCtrl)
                self.bleMgr.ble_adapter_select(
                    "hci{}".format(bluetoothAdapter))
            except Exception as ex:
                traceback.print_exc()
                print(
                    "Failed to initialize BLE, if you don't have BLE, run chip-device-ctrl with --no-ble"
                )
                raise ex

        self.historyFileName = os.path.expanduser(
            "~/.chip-device-ctrl-history")

        try:
            import readline

            if "libedit" in readline.__doc__:
                readline.parse_and_bind("bind ^I rl_complete")
            readline.set_completer_delims(" ")
            try:
                readline.read_history_file(self.historyFileName)
            except IOError:
                pass
        except ImportError:
            pass
Пример #16
0
class DeviceMgrCmd(Cmd):
    def __init__(self,
                 rendezvousAddr=None,
                 controllerNodeId=1,
                 bluetoothAdapter=None):
        self.lastNetworkId = None
        self.replHint = None

        pretty.install(indent_guides=True, expand_all=True)

        coloredlogs.install(level='DEBUG')
        chip.logging.RedirectToPythonLogging()

        logging.getLogger().setLevel(logging.DEBUG)
        warnings.showwarning = ShowColoredWarnings

        Cmd.__init__(self)

        Cmd.identchars = string.ascii_letters + string.digits + "-"

        if sys.stdin.isatty():
            self.prompt = "chip-device-ctrl > "
        else:
            self.use_rawinput = 0
            self.prompt = ""

        DeviceMgrCmd.command_names.sort()

        self.bleMgr = None

        self.chipStack = ChipStack.ChipStack(
            bluetoothAdapter=bluetoothAdapter,
            persistentStoragePath='/tmp/chip-device-ctrl-storage.json')
        self.fabricAdmin = FabricAdmin.FabricAdmin(0xFFF1)
        self.devCtrl = self.fabricAdmin.NewController(nodeId=controllerNodeId,
                                                      useTestCommissioner=True)

        self.commissionableNodeCtrl = ChipCommissionableNodeCtrl.ChipCommissionableNodeController(
            self.chipStack)

        # If we are on Linux and user selects non-default bluetooth adapter.
        if sys.platform.startswith("linux") and (bluetoothAdapter is not None):
            try:
                self.bleMgr = BleManager(self.devCtrl)
                self.bleMgr.ble_adapter_select(
                    "hci{}".format(bluetoothAdapter))
            except Exception as ex:
                traceback.print_exc()
                print(
                    "Failed to initialize BLE, if you don't have BLE, run chip-device-ctrl with --no-ble"
                )
                raise ex

        self.historyFileName = os.path.expanduser(
            "~/.chip-device-ctrl-history")

        try:
            import readline

            if "libedit" in readline.__doc__:
                readline.parse_and_bind("bind ^I rl_complete")
            readline.set_completer_delims(" ")
            try:
                readline.read_history_file(self.historyFileName)
            except IOError:
                pass
        except ImportError:
            pass

    command_names = [
        "setup-payload",
        "ble-scan",
        "ble-adapter-select",
        "ble-adapter-print",
        "ble-debug-log",
        "connect",
        "close-ble",
        "close-session",
        "resolve",
        "paseonly",
        "commission",
        "zcl",
        "zclread",
        "zclsubscribe",
        "discover",
        "set-pairing-wifi-credential",
        "set-pairing-thread-credential",
        "open-commissioning-window",
        "get-fabricid",
    ]

    def parseline(self, line):
        cmd, arg, line = Cmd.parseline(self, line)
        if cmd:
            cmd = self.shortCommandName(cmd)
            line = cmd + " " + arg
        return cmd, arg, line

    def completenames(self, text, *ignored):
        return [
            name + " " for name in DeviceMgrCmd.command_names
            if name.startswith(text)
            or self.shortCommandName(name).startswith(text)
        ]

    def shortCommandName(self, cmd):
        return cmd.replace("-", "")

    def precmd(self, line):
        if not self.use_rawinput and line != "EOF" and line != "":
            print(">>> " + line)
        return line

    def postcmd(self, stop, line):
        if self.replHint is not None:
            print("Try the following command in repl: ")
            print(self.replHint)
            print("")
        self.replHint = None
        if not stop and self.use_rawinput:
            self.prompt = "chip-device-ctrl > "
        return stop

    def postloop(self):
        try:
            import readline

            try:
                readline.write_history_file(self.historyFileName)
            except IOError:
                pass
        except ImportError:
            pass

    def do_help(self, line):
        if line:
            cmd, arg, unused = self.parseline(line)
            try:
                doc = getattr(self, "do_" + cmd).__doc__
            except AttributeError:
                doc = None
            if doc:
                self.stdout.write("%s\n" % textwrap.dedent(doc))
            else:
                self.stdout.write("No help on %s\n" % (line))
        else:
            self.print_topics(
                "\nAvailable commands (type help <name> for more information):",
                DeviceMgrCmd.command_names,
                15,
                80,
            )

    def do_closeble(self, line):
        """
        close-ble

        Close the ble connection to the device.
        """

        warnings.warn(
            "This method is being deprecated. Please use the DeviceController.CloseBLEConnection method directly in the REPL",
            DeprecationWarning)

        args = shlex.split(line)

        if len(args) != 0:
            print("Usage:")
            self.do_help("close")
            return

        try:
            self.devCtrl.CloseBLEConnection()
        except exceptions.ChipStackException as ex:
            print(str(ex))

    def do_setlogoutput(self, line):
        """
        set-log-output [ none | error | progress | detail ]

        Set the level of Chip logging output.
        """

        warnings.warn(
            "This method is being deprecated. Please use the DeviceController.SetLogFilter method directly in the REPL",
            DeprecationWarning)

        args = shlex.split(line)

        if len(args) == 0:
            print("Usage:")
            self.do_help("set-log-output")
            return
        if len(args) > 1:
            print("Unexpected argument: " + args[1])
            return

        category = args[0].lower()
        if category == "none":
            category = 0
        elif category == "error":
            category = 1
        elif category == "progress":
            category = 2
        elif category == "detail":
            category = 3
        else:
            print("Invalid argument: " + args[0])
            return

        try:
            self.devCtrl.SetLogFilter(category)
        except exceptions.ChipStackException as ex:
            print(str(ex))
            return

    def do_setuppayload(self, line):
        """
        setup-payload generate [options]

        Options:
          -vr  Version
          -vi  Vendor ID
          -pi  Product ID
          -cf  Custom Flow [Standard = 0, UserActionRequired = 1, Custom = 2]
          -dc  Discovery Capabilities [SoftAP = 1 | BLE = 2 | OnNetwork = 4]
          -dv  Discriminator Value
          -ps  Passcode

        setup-payload parse-manual <manual-pairing-code>
        setup-payload parse-qr <qr-code-payload>
        """

        warnings.warn(
            "This method is being deprecated. Please use the SetupPayload function in the chip.setup_payload package directly",
            DeprecationWarning)

        try:
            arglist = shlex.split(line)
            if arglist[0] not in ("generate", "parse-manual", "parse-qr"):
                self.do_help("setup-payload")
                return

            if arglist[0] == "generate":
                parser = argparse.ArgumentParser()
                parser.add_argument("-vr", type=int, default=0, dest='version')
                parser.add_argument("-pi",
                                    type=int,
                                    default=0,
                                    dest='productId')
                parser.add_argument("-vi",
                                    type=int,
                                    default=0,
                                    dest='vendorId')
                parser.add_argument('-cf',
                                    type=int,
                                    default=0,
                                    dest='customFlow')
                parser.add_argument("-dc",
                                    type=int,
                                    default=0,
                                    dest='capabilities')
                parser.add_argument("-dv",
                                    type=int,
                                    default=0,
                                    dest='discriminator')
                parser.add_argument("-ps", type=int, dest='passcode')
                args = parser.parse_args(arglist[1:])

                SetupPayload().PrintOnboardingCodes(
                    args.passcode, args.vendorId, args.productId,
                    args.discriminator, args.customFlow, args.capabilities,
                    args.version)

            if arglist[0] == "parse-manual":
                SetupPayload().ParseManualPairingCode(arglist[1]).Print()

            if arglist[0] == "parse-qr":
                SetupPayload().ParseQrCode(arglist[1]).Print()

        except exceptions.ChipStackException as ex:
            print(str(ex))
            return

    def do_bleadapterselect(self, line):
        """
        ble-adapter-select

        Start BLE adapter select, deprecated, you can select adapter by command line arguments.
        """
        if sys.platform.startswith("linux"):
            if not self.bleMgr:
                self.bleMgr = BleManager(self.devCtrl)

            self.bleMgr.ble_adapter_select(line)
            print(
                "This change only applies to ble-scan\n"
                "Please run device controller with --bluetooth-adapter=<adapter-name> to select adapter\n"
                + "e.g. chip-device-ctrl --bluetooth-adapter hci0")
        else:
            print(
                "ble-adapter-select only works in Linux, ble-adapter-select mac_address"
            )

        return

    def do_bleadapterprint(self, line):
        """
        ble-adapter-print

        Print attached BLE adapter.
        """
        if sys.platform.startswith("linux"):
            if not self.bleMgr:
                self.bleMgr = BleManager(self.devCtrl)

            self.bleMgr.ble_adapter_print()
        else:
            print("ble-adapter-print only works in Linux")

        return

    def do_bledebuglog(self, line):
        """
        ble-debug-log 0:1
          0: disable BLE debug log
          1: enable BLE debug log
        """
        if not self.bleMgr:
            self.bleMgr = BleManager(self.devCtrl)

        self.bleMgr.ble_debug_log(line)

        return

    def do_blescan(self, line):
        """
        ble-scan

        Start BLE scanning operations.
        """

        if not self.bleMgr:
            self.bleMgr = BleManager(self.devCtrl)

        self.bleMgr.scan(line)

        return

    def ConnectFromSetupPayload(self, setupPayload, nodeid):
        # TODO(cecille): Get this from the C++ code?
        ble = 1 << 1
        # Devices may be uncommissioned, or may already be on the network. Need to check both ways.
        # TODO(cecille): implement soft-ap connection.

        # Any device that is already commissioned into a fabric needs to use on-network
        # pairing, so look first on the network regardless of the QR code contents.
        print("Attempting to find device on Network")
        longDiscriminator = ctypes.c_uint16(
            int(setupPayload.attributes['Discriminator']))
        self.devCtrl.DiscoverCommissionableNodesLongDiscriminator(
            longDiscriminator)
        print("Waiting for device responses...")
        strlen = 100
        addrStrStorage = ctypes.create_string_buffer(strlen)
        # If this device is on the network and we're looking specifically for 1 device,
        # expect a quick response.
        if self.wait_for_one_discovered_device():
            self.devCtrl.GetIPForDiscoveredDevice(0, addrStrStorage, strlen)
            addrStr = addrStrStorage.value.decode('utf-8')
            print("Connecting to device at " + addrStr)
            pincode = ctypes.c_uint32(
                int(setupPayload.attributes['SetUpPINCode']))
            try:
                self.devCtrl.CommissionIP(addrStrStorage, pincode, nodeid)
                print("Connected")
                return 0
            except Exception as ex:
                print(f"Unable to connect on network: {ex}")
        else:
            print("Unable to locate device on network")

        if int(setupPayload.attributes["RendezvousInformation"]) & ble:
            print("Attempting to connect via BLE")
            longDiscriminator = ctypes.c_uint16(
                int(setupPayload.attributes['Discriminator']))
            pincode = ctypes.c_uint32(
                int(setupPayload.attributes['SetUpPINCode']))
            try:
                self.devCtrl.ConnectBLE(longDiscriminator, pincode, nodeid)
                print("Connected")
                return 0
            except Exception as ex:
                print(f"Unable to connect: {ex}")
        return -1

    def do_paseonly(self, line):
        """
        paseonly -ip <ip address> <setup pin code> [<nodeid>]

        TODO: Add more methods to connect to device (like cert for auth, and IP
              for connection)
        """

        try:
            args = shlex.split(line)
            if len(args) <= 1:
                print("Usage:")
                self.do_help("paseonly")
                return
            nodeid = random.randint(1, 1000000)  # Just a random number
            if len(args) == 4:
                nodeid = int(args[3])
            print("Device is assigned with nodeid = {}".format(nodeid))
            self.replHint = f"devCtrl.EstablishPASESessionIP({repr(args[1])}, {int(args[2])}, {nodeid})"
            if args[0] == "-ip" and len(args) >= 3:
                self.devCtrl.EstablishPASESessionIP(args[1], int(args[2]),
                                                    nodeid)
            else:
                print("Usage:")
                self.do_help("paseonly")
                return
            print(
                "Device temporary node id (**this does not match spec**): {}".
                format(nodeid))
        except Exception as ex:
            print(str(ex))
            return

    def do_commission(self, line):
        """
        commission nodeid

        Runs commissioning on a device that has been connected with paseonly
        """
        try:
            args = shlex.split(line)
            if len(args) != 1:
                print("Usage:")
                self.do_help("commission")
                return
            nodeid = int(args[0])
            self.replHint = f"devCtrl.Commission({nodeid})"
            self.devCtrl.Commission(nodeid)
        except Exception as ex:
            print(str(ex))
            return

    def do_connect(self, line):
        """
        connect -ip <ip address> <setup pin code> [<nodeid>]
        connect -ble <discriminator> <setup pin code> [<nodeid>]
        connect -qr <qr code> [<nodeid>]
        connect -code <manual pairing code> [<nodeid>]

        connect command is used for establishing a rendezvous session to the device.
        currently, only connect using setupPinCode is supported.
        -qr option will connect to the first device with a matching long discriminator.

        TODO: Add more methods to connect to device (like cert for auth, and IP
              for connection)
        """

        warnings.warn(
            "This method is being deprecated. Please use the DeviceController.[ConnectBLE|CommissionIP] methods directly in the REPL",
            DeprecationWarning)

        try:
            args = shlex.split(line)
            if len(args) <= 1:
                print("Usage:")
                self.do_help("connect SetupPinCode")
                return

            nodeid = random.randint(1, 1000000)  # Just a random number
            if len(args) == 4:
                nodeid = int(args[3])
            print("Device is assigned with nodeid = {}".format(nodeid))

            if args[0] == "-ip" and len(args) >= 3:
                self.replHint = f"devCtrl.CommissionIP({repr(args[1])}, {int(args[2])}, {nodeid})"
                self.devCtrl.CommissionIP(args[1], int(args[2]), nodeid)
            elif args[0] == "-ble" and len(args) >= 3:
                self.replHint = f"devCtrl.ConnectBLE({int(args[1])}, {int(args[2])}, {nodeid})"
                self.devCtrl.ConnectBLE(int(args[1]), int(args[2]), nodeid)
            elif args[0] in ['-qr', '-code'] and len(args) >= 2:
                if len(args) == 3:
                    nodeid = int(args[2])
                print("Parsing QR code {}".format(args[1]))

                setupPayload = None
                if args[0] == '-qr':
                    setupPayload = SetupPayload().ParseQrCode(args[1])
                elif args[0] == '-code':
                    setupPayload = SetupPayload().ParseManualPairingCode(
                        args[1])

                if not int(
                        setupPayload.attributes.get("RendezvousInformation",
                                                    0)):
                    print(
                        "No rendezvous information provided, default to all.")
                    setupPayload.attributes["RendezvousInformation"] = 0b111
                setupPayload.Print()
                self.replHint = f"devCtrl.CommissionWithCode(setupPayload={repr(setupPayload)}, nodeid={nodeid})"
                self.ConnectFromSetupPayload(setupPayload, nodeid)
            else:
                print("Usage:")
                self.do_help("connect SetupPinCode")
                return
            print(
                "Device temporary node id (**this does not match spec**): {}".
                format(nodeid))
        except exceptions.ChipStackException as ex:
            print(str(ex))
            return

    def do_closesession(self, line):
        """
        close-session <nodeid>

        Close any session associated with a given node ID.
        """
        try:
            parser = argparse.ArgumentParser()
            parser.add_argument('nodeid', type=int, help='Peer node ID')
            args = parser.parse_args(shlex.split(line))
            self.replHint = f"devCtrl.CloseSession({args.nodeid})"
            self.devCtrl.CloseSession(args.nodeid)
        except exceptions.ChipStackException as ex:
            print(str(ex))
        except:
            self.do_help("close-session")

    def do_resolve(self, line):
        """
        resolve <nodeid>

        Resolve DNS-SD name corresponding with the given node ID and
        update address of the node in the device controller.
        """
        try:
            args = shlex.split(line)
            if len(args) == 1:
                try:
                    self.replHint = f"devCtrl.ResolveNode({int(args[0])});devCtrl.GetAddressAndPort({int(args[0])})"
                    self.devCtrl.ResolveNode(int(args[0]))
                    address = self.devCtrl.GetAddressAndPort(int(args[0]))
                    address = "{}:{}".format(
                        *address) if address else "unknown"
                    print("Current address: " + address)
                except exceptions.ChipStackException as ex:
                    print(str(ex))
            else:
                self.do_help("resolve")
        except exceptions.ChipStackException as ex:
            print(str(ex))
            return

    def wait_for_one_discovered_device(self):
        print("Waiting for device responses...")
        strlen = 100
        addrStrStorage = ctypes.create_string_buffer(strlen)
        count = 0
        maxWaitTime = 2
        while (not self.devCtrl.GetIPForDiscoveredDevice(
                0, addrStrStorage, strlen) and count < maxWaitTime):
            time.sleep(0.2)
            count = count + 0.2
        return count < maxWaitTime

    def wait_for_many_discovered_devices(self):
        # Discovery happens through mdns, which means we need to wait for responses to come back.
        # TODO(cecille): I suppose we could make this a command line arg. Or Add a callback when
        # x number of responses are received. For now, just 2 seconds. We can all wait that long.
        print("Waiting for device responses...")
        time.sleep(2)

    def do_discover(self, line):
        """
        discover -qr qrcode
        discover -all
        discover -l long_discriminator
        discover -s short_discriminator
        discover -v vendor_id
        discover -t device_type
        discover -c

        discover command is used to discover available devices.
        """
        try:
            arglist = shlex.split(line)
            if len(arglist) < 1:
                print("Usage:")
                self.do_help("discover")
                return
            parser = argparse.ArgumentParser()
            group = parser.add_mutually_exclusive_group()
            group.add_argument(
                '-all',
                help='discover all commissionable nodes and commissioners',
                action='store_true')
            group.add_argument(
                '-qr',
                help='discover commissionable nodes matching provided QR code',
                type=str)
            group.add_argument(
                '-l',
                help=
                'discover commissionable nodes with given long discriminator',
                type=int)
            group.add_argument(
                '-s',
                help=
                'discover commissionable nodes with given short discriminator',
                type=int)
            group.add_argument(
                '-v',
                help='discover commissionable nodes with given vendor ID',
                type=int)
            group.add_argument(
                '-t',
                help='discover commissionable nodes with given device type',
                type=int)
            group.add_argument(
                '-c',
                help='discover commissionable nodes in commissioning mode',
                action='store_true')
            args = parser.parse_args(arglist)
            if args.all:
                self.commissionableNodeCtrl.DiscoverCommissioners()
                self.wait_for_many_discovered_devices()
                self.commissionableNodeCtrl.PrintDiscoveredCommissioners()
                self.devCtrl.DiscoverAllCommissioning()
                self.wait_for_many_discovered_devices()
            elif args.qr is not None:
                setupPayload = SetupPayload().ParseQrCode(args.qr)
                longDiscriminator = ctypes.c_uint16(
                    int(setupPayload.attributes['Discriminator']))
                self.devCtrl.DiscoverCommissionableNodesLongDiscriminator(
                    longDiscriminator)
                self.wait_for_one_discovered_device()
            elif args.l is not None:
                self.devCtrl.DiscoverCommissionableNodesLongDiscriminator(
                    ctypes.c_uint16(args.l))
                self.wait_for_one_discovered_device()
            elif args.s is not None:
                self.devCtrl.DiscoverCommissionableNodesShortDiscriminator(
                    ctypes.c_uint16(args.s))
                self.wait_for_one_discovered_device()
            elif args.v is not None:
                self.devCtrl.DiscoverCommissionableNodesVendor(
                    ctypes.c_uint16(args.v))
                self.wait_for_many_discovered_devices()
            elif args.t is not None:
                self.devCtrl.DiscoverCommissionableNodesDeviceType(
                    ctypes.c_uint16(args.t))
                self.wait_for_many_discovered_devices()
            elif args.c is not None:
                self.devCtrl.DiscoverCommissionableNodesCommissioningEnabled()
                self.wait_for_many_discovered_devices()
            else:
                self.do_help("discover")
                return
            self.devCtrl.PrintDiscoveredDevices()
        except exceptions.ChipStackException as ex:
            print('exception')
            print(str(ex))
            return
        except:
            self.do_help("discover")
            return

    def do_zcl(self, line):
        """
        To send ZCL message to device:
        zcl <cluster> <command> <nodeid> <endpoint> <groupid> [key=value]...
        To get a list of clusters:
        zcl ?
        To get a list of commands in cluster:
        zcl ? <cluster>

        Send ZCL command to device nodeid
        """
        try:
            args = shlex.split(line)
            all_commands = self.devCtrl.ZCLCommandList()
            if len(args) == 1 and args[0] == '?':
                print('\n'.join(all_commands.keys()))
            elif len(args) == 2 and args[0] == '?':
                if args[1] not in all_commands:
                    raise exceptions.UnknownCluster(args[1])
                for commands in all_commands.get(args[1]).items():
                    args = ", ".join([
                        "{}: {}".format(argName, argType)
                        for argName, argType in commands[1].items()
                    ])
                    print(commands[0])
                    if commands[1]:
                        print("  ", args)
                    else:
                        print("  <no arguments>")
            elif len(args) > 4:
                if args[0] not in all_commands:
                    raise exceptions.UnknownCluster(args[0])
                command = all_commands.get(args[0]).get(args[1], None)
                # When command takes no arguments, (not command) is True
                if command is None:
                    raise exceptions.UnknownCommand(args[0], args[1])
                req = eval(f"Clusters.{args[0]}.Commands.{args[1]}")(
                    **FormatZCLArguments(args[5:], command))
                self.replHint = f"await devCtrl.SendCommand({int(args[2])}, {int(args[3])}, Clusters.{repr(req)})"
                err, res = self.devCtrl.ZCLSend(args[0],
                                                args[1],
                                                int(args[2]),
                                                int(args[3]),
                                                int(args[4]),
                                                FormatZCLArguments(
                                                    args[5:], command),
                                                blocking=True)
                if err != 0:
                    print("Failed to receive command response: {}".format(res))
                elif res != None:
                    print("Received command status response:")
                    print(res)
                else:
                    print("Success, no status code is attached with response.")
            else:
                self.do_help("zcl")
        except exceptions.ChipStackException as ex:
            print("An exception occurred during process ZCL command:")
            print(str(ex))
        except Exception as ex:
            print("An exception occurred during processing input:")
            traceback.print_exc()
            print(str(ex))

    def do_zclread(self, line):
        """
        To read ZCL attribute:
        zclread <cluster> <attribute> <nodeid> <endpoint> <groupid>
        """
        try:
            args = shlex.split(line)
            all_attrs = self.devCtrl.ZCLAttributeList()
            if len(args) == 1 and args[0] == '?':
                print('\n'.join(all_attrs.keys()))
            elif len(args) == 2 and args[0] == '?':
                if args[1] not in all_attrs:
                    raise exceptions.UnknownCluster(args[1])
                print('\n'.join(all_attrs.get(args[1]).keys()))
            elif len(args) == 5:
                if args[0] not in all_attrs:
                    raise exceptions.UnknownCluster(args[0])
                self.replHint = f"await devCtrl.ReadAttribute({int(args[2])}, [({int(args[3])}, Clusters.{args[0]}.Attributes.{args[1]})])"
                res = self.devCtrl.ZCLReadAttribute(args[0], args[1],
                                                    int(args[2]), int(args[3]),
                                                    int(args[4]))
                if res != None:
                    print(repr(res))
            else:
                self.do_help("zclread")
        except exceptions.ChipStackException as ex:
            print("An exception occurred during reading ZCL attribute:")
            print(str(ex))
        except Exception as ex:
            print("An exception occurred during processing input:")
            print(str(ex))

    def do_zclwrite(self, line):
        """
        To write ZCL attribute:
        zclwrite <cluster> <attribute> <nodeid> <endpoint> <groupid> <value>
        """
        try:
            args = shlex.split(line)
            all_attrs = self.devCtrl.ZCLAttributeList()
            if len(args) == 1 and args[0] == '?':
                print('\n'.join(all_attrs.keys()))
            elif len(args) == 2 and args[0] == '?':
                if args[1] not in all_attrs:
                    raise exceptions.UnknownCluster(args[1])
                cluster_attrs = all_attrs.get(args[1], {})
                print('\n'.join([
                    "{}: {}".format(key, cluster_attrs[key]["type"])
                    for key in cluster_attrs.keys()
                    if cluster_attrs[key].get("writable", False)
                ]))
            elif len(args) == 6:
                if args[0] not in all_attrs:
                    raise exceptions.UnknownCluster(args[0])
                attribute_type = all_attrs.get(args[0],
                                               {}).get(args[1],
                                                       {}).get("type", None)
                self.replHint = f"await devCtrl.WriteAttribute({int(args[2])}, [({int(args[3])}, Clusters.{args[0]}.Attributes.{args[1]}(value={repr(ParseValueWithType(args[5], attribute_type))}))])"
                res = self.devCtrl.ZCLWriteAttribute(
                    args[0], args[1], int(args[2]), int(args[3]), int(args[4]),
                    ParseValueWithType(args[5], attribute_type))
                print(repr(res))
            else:
                self.do_help("zclwrite")
        except exceptions.ChipStackException as ex:
            print("An exception occurred during writing ZCL attribute:")
            print(str(ex))
        except Exception as ex:
            print("An exception occurred during processing input:")
            print(str(ex))

    def do_zclsubscribe(self, line):
        """
        To subscribe ZCL attribute reporting:
        zclsubscribe <cluster> <attribute> <nodeid> <endpoint> <minInterval> <maxInterval>

        To shut down a subscription:
        zclsubscribe -shutdown <subscriptionId>
        """
        try:
            args = shlex.split(line)
            all_attrs = self.devCtrl.ZCLAttributeList()
            if len(args) == 1 and args[0] == '?':
                print('\n'.join(all_attrs.keys()))
            elif len(args) == 2 and args[0] == '?':
                if args[1] not in all_attrs:
                    raise exceptions.UnknownCluster(args[1])
                cluster_attrs = all_attrs.get(args[1], {})
                print('\n'.join([
                    key for key in cluster_attrs.keys()
                    if cluster_attrs[key].get("reportable", False)
                ]))
            elif len(args) == 6:
                if args[0] not in all_attrs:
                    raise exceptions.UnknownCluster(args[0])
                res = self.devCtrl.ZCLSubscribeAttribute(
                    args[0], args[1], int(args[2]), int(args[3]), int(args[4]),
                    int(args[5]))
                self.replHint = f"sub = await devCtrl.ReadAttribute({int(args[2])}, [({int(args[3])}, Clusters.{args[0]}.Attributes.{args[1]})], reportInterval=({int(args[4])}, {int(args[5])}))"
                print(res.GetAllValues())
                print(f"Subscription Established: {res}")
            elif len(args) == 2 and args[0] == '-shutdown':
                subscriptionId = int(args[1], base=0)
                self.replHint = "You can call sub.Shutdown() (sub is the return value of ReadAttribute() called before)"
                self.devCtrl.ZCLShutdownSubscription(subscriptionId)
            else:
                self.do_help("zclsubscribe")
        except exceptions.ChipStackException as ex:
            print(
                "An exception occurred during configuring reporting of ZCL attribute:"
            )
            print(str(ex))
        except Exception as ex:
            print("An exception occurred during processing input:")
            print(str(ex))

    def do_setpairingwificredential(self, line):
        """
        set-pairing-wifi-credential ssid credentials
        """
        try:
            args = shlex.split(line)
            if len(args) < 2:
                print("Usage:")
                self.do_help("set-pairing-wifi-credential")
                return
            self.devCtrl.SetWiFiCredentials(args[0], args[1])
            self.replHint = f"devCtrl.SetWiFiCredentials({repr(args[0])}, {repr(args[1])})"
        except Exception as ex:
            print(str(ex))
            return

    def do_setpairingthreadcredential(self, line):
        """
        set-pairing-thread-credential threadOperationalDataset
        """
        try:
            args = shlex.split(line)
            if len(args) < 1:
                print("Usage:")
                self.do_help("set-pairing-thread-credential")
                return
            self.replHint = f"devCtrl.SetThreadOperationalDataset(bytes.fromhex({repr(args[0])}))"
            self.devCtrl.SetThreadOperationalDataset(bytes.fromhex(args[0]))
        except Exception as ex:
            print(str(ex))
            return

    def do_opencommissioningwindow(self, line):
        """
        open-commissioning-window <nodeid> [options]

        Options:
          -t  Timeout (in seconds)
          -o  Option  [TokenWithRandomPIN = 1, TokenWithProvidedPIN = 2]
          -d  Discriminator Value
          -i  Iteration

          This command is used by a current Administrator to instruct a Node to go into commissioning mode
        """
        try:
            arglist = shlex.split(line)

            if len(arglist) <= 1:
                print("Usage:")
                self.do_help("open-commissioning-window")
                return
            parser = argparse.ArgumentParser()
            parser.add_argument("-t", type=int, default=0, dest='timeout')
            parser.add_argument("-o", type=int, default=1, dest='option')
            parser.add_argument("-i", type=int, default=0, dest='iteration')
            parser.add_argument("-d",
                                type=int,
                                default=0,
                                dest='discriminator')
            args = parser.parse_args(arglist[1:])

            if args.option < 1 or args.option > 2:
                print("Invalid option specified!")
                raise ValueError("Invalid option specified")

            self.replHint = f"devCtrl.OpenCommissioningWindow(nodeid={int(arglist[0])}, timeout={args.timeout}, iteration={args.iteration}, discriminator={args.discriminator}, option={args.option})"

            self.devCtrl.OpenCommissioningWindow(int(arglist[0]), args.timeout,
                                                 args.iteration,
                                                 args.discriminator,
                                                 args.option)

        except exceptions.ChipStackException as ex:
            print(str(ex))
            return
        except:
            self.do_help("open-commissioning-window")
            return

    def do_getfabricid(self, line):
        """
          get-fabricid

          Read the current Compressed Fabric Id of the controller device, return 0 if not available.
        """
        try:
            args = shlex.split(line)

            if (len(args) > 0):
                print("Unexpected argument: " + args[1])
                return

            compressed_fabricid = self.devCtrl.GetCompressedFabricId()
            raw_fabricid = self.devCtrl.fabricId

            self.replHint = f"devCtrl.GetCompressedFabricId(), devCtrl.fabricId"
        except exceptions.ChipStackException as ex:
            print("An exception occurred during reading FabricID:")
            print(str(ex))
            return

        print("Get fabric ID complete")

        print("Raw Fabric ID: 0x{:016x}".format(raw_fabricid) + " (" +
              str(raw_fabricid) + ")")

        print("Compressed Fabric ID: 0x{:016x}".format(compressed_fabricid) +
              " (" + str(compressed_fabricid) + ")")

    def do_history(self, line):
        """
        history

        Show previously executed commands.
        """

        try:
            import readline

            h = readline.get_current_history_length()
            for n in range(1, h + 1):
                print(readline.get_history_item(n))
        except ImportError:
            pass

    def do_h(self, line):
        self.do_history(line)

    def do_exit(self, line):
        return True

    def do_quit(self, line):
        return True

    def do_q(self, line):
        return True

    def do_EOF(self, line):
        print()
        return True

    def emptyline(self):
        pass
Пример #17
0
class DeviceMgrCmd(Cmd):
    def __init__(self, rendezvousAddr=None):
        self.lastNetworkId = None

        Cmd.__init__(self)

        Cmd.identchars = string.ascii_letters + string.digits + "-"

        if sys.stdin.isatty():
            self.prompt = "chip-device-ctrl > "
        else:
            self.use_rawinput = 0
            self.prompt = ""

        DeviceMgrCmd.command_names.sort()

        self.bleMgr = None

        self.devCtrl = ChipDeviceCtrl.ChipDeviceController()

        self.historyFileName = os.path.expanduser(
            "~/.chip-device-ctrl-history")

        try:
            import readline

            if "libedit" in readline.__doc__:
                readline.parse_and_bind("bind ^I rl_complete")
            readline.set_completer_delims(" ")
            try:
                readline.read_history_file(self.historyFileName)
            except IOError:
                pass
        except ImportError:
            pass

    command_names = [
        "close",

        "ble-scan",
        "ble-adapter-select",
        "ble-adapter-print",
        "ble-debug-log",

        "connect",
        "zcl",
        "set-pairing-wifi-credential",
    ]

    def parseline(self, line):
        cmd, arg, line = Cmd.parseline(self, line)
        if cmd:
            cmd = self.shortCommandName(cmd)
            line = cmd + " " + arg
        return cmd, arg, line

    def completenames(self, text, *ignored):
        return [
            name + " "
            for name in DeviceMgrCmd.command_names
            if name.startswith(text) or self.shortCommandName(name).startswith(text)
        ]

    def shortCommandName(self, cmd):
        return cmd.replace("-", "")

    def precmd(self, line):
        if not self.use_rawinput and line != "EOF" and line != "":
            print(">>> " + line)
        return line

    def postcmd(self, stop, line):
        if not stop and self.use_rawinput:
            self.prompt = "chip-device-ctrl > "
        return stop

    def postloop(self):
        try:
            import readline

            try:
                readline.write_history_file(self.historyFileName)
            except IOError:
                pass
        except ImportError:
            pass

    def do_help(self, line):
        if line:
            cmd, arg, unused = self.parseline(line)
            try:
                doc = getattr(self, "do_" + cmd).__doc__
            except AttributeError:
                doc = None
            if doc:
                self.stdout.write("%s\n" % textwrap.dedent(doc))
            else:
                self.stdout.write("No help on %s\n" % (line))
        else:
            self.print_topics(
                "\nAvailable commands (type help <name> for more information):",
                DeviceMgrCmd.command_names,
                15,
                80,
            )

    def do_close(self, line):
        """
        close

        Close the connection to the device.
        """

        args = shlex.split(line)

        if len(args) != 0:
            print("Usage:")
            self.do_help("close")
            return

        try:
            self.devCtrl.Close()
        except ChipExceptions.ChipStackException as ex:
            print(str(ex))

    def do_setlogoutput(self, line):
        """
        set-log-output [ none | error | progress | detail ]

        Set the level of Chip logging output.
        """

        args = shlex.split(line)

        if len(args) == 0:
            print("Usage:")
            self.do_help("set-log-output")
            return
        if len(args) > 1:
            print("Unexpected argument: " + args[1])
            return

        category = args[0].lower()
        if category == "none":
            category = 0
        elif category == "error":
            category = 1
        elif category == "progress":
            category = 2
        elif category == "detail":
            category = 3
        else:
            print("Invalid argument: " + args[0])
            return

        try:
            self.devCtrl.SetLogFilter(category)
        except ChipExceptions.ChipStackException as ex:
            print(str(ex))
            return

        print("Done.")

    def do_bleadapterselect(self, line):
        """
        ble-adapter-select

        Start BLE adapter select.
        """
        if sys.platform.startswith("linux"):
            if not self.bleMgr:
                self.bleMgr = BleManager(self.devCtrl)

            self.bleMgr.ble_adapter_select(line)
        else:
            print(
                "ble-adapter-select only works in Linux, ble-adapter-select mac_address"
            )

        return

    def do_bleadapterprint(self, line):
        """
        ble-adapter-print

        Print attached BLE adapter.
        """
        if sys.platform.startswith("linux"):
            if not self.bleMgr:
                self.bleMgr = BleManager(self.devCtrl)

            self.bleMgr.ble_adapter_print()
        else:
            print("ble-adapter-print only works in Linux")

        return

    def do_bledebuglog(self, line):
        """
        ble-debug-log 0:1
          0: disable BLE debug log
          1: enable BLE debug log
        """
        if not self.bleMgr:
            self.bleMgr = BleManager(self.devCtrl)

        self.bleMgr.ble_debug_log(line)

        return

    def do_blescan(self, line):
        """
        ble-scan

        Start BLE scanning operations.
        """

        if not self.bleMgr:
            self.bleMgr = BleManager(self.devCtrl)

        self.bleMgr.scan(line)

        return

    def do_connect(self, line):
        """
        connect -ip <ip address> <setup pin code>
        connect -ble <discriminator> <setup pin code>

        connect command is used for establishing a rendezvous session to the device.
        currently, only connect using setupPinCode is supported.

        TODO: Add more methods to connect to device (like cert for auth, and IP
              for connection)
        """

        try:
            args = shlex.split(line)
            if len(args) <= 1:
                print("Usage:")
                self.do_help("connect SetupPinCode")
                return
            if args[0] == "-ip" and len(args) == 3:
                self.devCtrl.ConnectIP(args[1].encode("utf-8"), int(args[2]))
            elif args[0] == "-ble" and len(args) == 3:
                self.devCtrl.ConnectBLE(int(args[1]), int(args[2]))
            else:
                print("Usage:")
                self.do_help("connect SetupPinCode")
                return
        except ChipExceptions.ChipStackException as ex:
            print(str(ex))
            return
        print("Connected")

    def do_zcl(self, line):
        """
        To send ZCL message to device:
        zcl <cluster> <command> <nodeid> <endpoint> <groupid> [key=value]...
        To get a list of clusters:
        zcl ?
        To get a list of commands in cluster:
        zcl ? <cluster>

        Send ZCL command to device nodeid
        """
        try:
            args = shlex.split(line)
            if len(args) == 1 and args[0] == '?':
                print(self.devCtrl.ZCLList().keys())
            elif len(args) == 2 and args[0] == '?':
                cluster = self.devCtrl.ZCLList().get(args[1], None)
                if not cluster:
                    raise ChipExceptions.UnknownCluster(args[1])
                for commands in cluster.items():
                    args = ", ".join(["{}: {}".format(argName, argType)
                                      for argName, argType in commands[1].items()])
                    print(commands[0])
                    if commands[1]:
                        print("  ", args)
                    else:
                        print("  <no arguments>")
            elif len(args) > 4:
                cluster = self.devCtrl.ZCLList().get(args[0], None)
                if not cluster:
                    raise ChipExceptions.UnknownCluster(args[0])
                command = cluster.get(args[1], None)
                # When command takes no arguments, (not command) is True
                if command == None:
                    raise ChipExceptions.UnknownCommand(args[0], args[1])
                self.devCtrl.ZCLSend(args[0], args[1], int(
                    args[2]), int(args[3]), int(args[4]), FormatZCLArguments(args[5:], command))
            else:
                self.do_help("zcl")
        except ChipExceptions.ChipStackException as ex:
            print("An exception occurred during process ZCL command:")
            print(str(ex))

    def do_setpairingwificredential(self, line):
        """
        set-pairing-wifi-credential

        Set WiFi credential for pairing, will sent to device
        """
        try:
            args = shlex.split(line)
            self.devCtrl.SetWifiCredential(args[0], args[1])
        except ChipExceptions.ChipStackException as ex:
            print(str(ex))
            return

        print("WiFi credential set")

    def do_history(self, line):
        """
        history

        Show previously executed commands.
        """

        try:
            import readline

            h = readline.get_current_history_length()
            for n in range(1, h + 1):
                print(readline.get_history_item(n))
        except ImportError:
            pass

    def do_h(self, line):
        self.do_history(line)

    def do_exit(self, line):
        return True

    def do_quit(self, line):
        return True

    def do_q(self, line):
        return True

    def do_EOF(self, line):
        print()
        return True

    def emptyline(self):
        pass