Example #1
0
    def testGetSkoobotsByName(self):
        """
        Test the getSkoobotsByName() method

        The method should return a list of (addr, name) tuples
        for all skoobots matching name
        """
        setUpRegistry = SkoobotRegistry(self.tempPath)
        names = (self.skooName, self.skooDupName, "nobody", None)
        for name in names:
            with self.subTest(name=name):
                skoobots = setUpRegistry.getSkoobotsByName(name)
                if name == self.skooDupName:
                    self.assertEqual(2, len(skoobots))
                    for skoobot in skoobots:
                        self.assertEqual(self.skooDupName, skoobot[1])
                    # Make a list of just the addresses
                    skooDupAddrs = [skoo[0] for skoo in skoobots]
                    self.assertIn(self.skooDupAddr1, skooDupAddrs)
                    self.assertIn(self.skooDupAddr2, skooDupAddrs)
                elif name == self.skooName:
                    self.assertEqual(1, len(skoobots))
                    # There is only 1 skoobot, so test it
                    skoobot = skoobots[0]
                    self.assertEqual(self.skooName, skoobot[1])
                    self.assertEqual(self.skooAddr, skoobot[0])
                else:
                    self.assertEqual(0, len(skoobots))
Example #2
0
    def testGetSkoobotByAddress(self):
        """
        Test the getSkoobotsByAddress() method

        The method should return a list of (addr, name) tupes
        for the skoobot matching addr, if any. Addresses are unique
        so there cannot be more than one. We verify uniqueness in
        the adding tests.
        """
        registry = SkoobotRegistry(self.tempPath)
        addrs = (self.skooAddr, self.skooDupAddr1, self.skooDupAddr2,
                 "nomatch", None)
        matchExpected = (self.skooAddr, self.skooDupAddr1, self.skooDupAddr2)
        for addr in addrs:
            expectedLen = 1 if addr in matchExpected else 0
            with self.subTest(addr=addr, expectedLen=expectedLen):
                skoobots = registry.getSkoobotsByAddress(addr)
                self.assertEqual(expectedLen, len(skoobots))
                if expectedLen == 1:
                    # There is exactly 1 skoobot in the list, so use it.
                    skoobot = skoobots[0]
                    if addr == self.skooAddr:
                        self.assertEqual(addr, skoobot[0])
                        self.assertEqual(self.skooName, skoobot[1])
                    else:
                        self.assertEqual(addr, skoobot[0])
                        self.assertEqual(self.skooDupName, skoobot[1])
Example #3
0
    def testGetDefaultName(self):
        """
        Test for method getDefaultName()

        Method gets the default name.
        """
        registry = SkoobotRegistry(self.tempPath)
        self.assertEqual(self.skooName, registry.getDefaultName())
Example #4
0
    def __init__(self):
        self.transport = TransportBluepy()
        self.registry = SkoobotRegistry()

        # Table of characteristic name to uuid mappings.
        # The characteristic names used are the ones in the firmware.
        self.uuids = {
            "cmd": "00001525-1212-efde-1523-785feabcd123",
            "data": "00001524-1212-efde-1523-785feabcd123",
            "byte2": "00001526-1212-efde-1523-785feabcd123",
            "byte128": "00001527-1212-efde-1523-785feabcd123",
            "byte4": "00001528-1212-efde-1523-785feabcd123",
        }
        self.connectedSkoobot = None
Example #5
0
    def testBug11(self):
        """
        Tests the resolution of bug #11

        "Registry setDefault() does strange things if given a
        list of lists as a parameter"
        Check that it raises a TypeError when called with
        something other than String or None.
        It turns out that the error only triggers with tuples.
        """
        registry = SkoobotRegistry(self.tempPath)
        with self.subTest("Valid arguments"):
            registry.setDefault(None)
            self.assertEqual(None, registry.getDefaultName())

            registry.setDefault(self.skooName)
            self.assertEqual(self.skooName, registry.getDefaultName())

        with self.subTest("Invalid arguments"):
            with self.assertRaises(TypeError):
                registry.setDefault(("test", ))
Example #6
0
    def testGenerateName(self):
        """
        Tests for the generateName() method
        """
        registry = SkoobotRegistry(self.tempPath)
        altSkooAddr = "aa:aa:aa:aa:aa:aa"
        altSkooName = "Alt"

        with self.subTest("Generate name from default list"):
            name = registry.generateName()
            self.assertIn(name, registry.skoobotNames)

        with self.subTest("Generate Alt name"):
            registry.skoobotNames = set([altSkooName])
            name = registry.generateName()
            self.assertEqual(altSkooName, name)

        with self.subTest("Names all used"):
            registry.skoobotNames = set([altSkooName])
            registry.addSkoobot(altSkooAddr)
            with self.assertRaises(KeyError):
                name = registry.generateName()
Example #7
0
    def testConstruct(self):
        """
        Test construction with a non-existent file
        and the JSON file created during setup
        """
        with self.subTest("Empty registry"):
            emptyRegistry = SkoobotRegistry("~/nonexistent.json")
            self.assertEqual(dict(), emptyRegistry.registry)
            self.assertEqual(True, emptyRegistry.valid)
            self.assertEqual(None, emptyRegistry.getDefaultName())

            # Make sure that ~ in the filename was expanded
            self.assertNotIn("~", emptyRegistry.registryPath)

        with self.subTest("setUp() registry"):
            registry = SkoobotRegistry(self.tempPath)
            self.assertEqual(3, len(registry.registry))
            self.assertEqual(True, registry.valid)
            self.assertEqual(self.skooName, registry.getDefaultName())
Example #8
0
def scan():
    transport = TransportBluepy()
    registry = SkoobotRegistry()

    rawDevices = transport.findRawDevices()
    skoobots = []
    for device in rawDevices:
        scanList = device.getScanData()
        for scanItem in scanList:
            if scanItem[0] == 9 and scanItem[2] == "Skoobot":
                skoobots.append(device)

    for skoobot in skoobots:
        # print(transport.rawDeviceInfoStr(skoobot))
        addr = skoobot.addr
        registry.addSkoobot(addr)
        name = registry.getSkoobotsByAddress(addr)[0][1]
        if registry.getDefaultName() == None:
            registry.setDefault(name)
        defaultText = " (default)" if registry.getDefaultName() == name else ""
        msg = "Added Skoobot {0:s} to registry with name {1:s}{2:s}"
        print(msg.format(addr, name, defaultText))
    print("Saving to list of Skoobots to registry {0:s}".format(
        registry.registryPath))
    registry.save()
    shutil.chown(registry.registryPath, os.getlogin())
Example #9
0
class SkoobotController:
    """
    Control API for Skoobots
    """
    def __init__(self):
        self.transport = TransportBluepy()
        self.registry = SkoobotRegistry()

        # Table of characteristic name to uuid mappings.
        # The characteristic names used are the ones in the firmware.
        self.uuids = {
            "cmd": "00001525-1212-efde-1523-785feabcd123",
            "data": "00001524-1212-efde-1523-785feabcd123",
            "byte2": "00001526-1212-efde-1523-785feabcd123",
            "byte128": "00001527-1212-efde-1523-785feabcd123",
            "byte4": "00001528-1212-efde-1523-785feabcd123",
        }
        self.connectedSkoobot = None

    def connect(self, name=None, addr=None):
        """
        Connect to the given Skoobot.
        If no Skoobot is given, connects to the default.
        Returns the address of the connected Skoobot if successful;
        None otherwise.
        """
        addrList = []

        if addr != None:
            if isinstance(addr, str):
                addrList.append(addr)
            else:
                raise TypeError("addr should be a string")
        else:
            if name == None:
                name = self.registry.getDefaultName()
            elif not isinstance(name, str):
                raise TypeError("name should be a string")
            if name != None:
                skoobots = self.registry.getSkoobotsByName(name)
                addrList = [bot[0] for bot in skoobots]
            else:
                raise RuntimeError("No default skoobot defined")

            if len(addrList) == 0:
                raise ValueError(
                    "No Skoobot with name {0:s} found".format(name))

        if self.connectedSkoobot != None:
            self.disconnect()

        for botAddr in addrList:
            try:
                self.transport.connect(botAddr)
                self.connectedSkoobot = botAddr
                break
            except BTLEException:
                pass

        return self.connectedSkoobot

    def disconnect(self):
        self.transport.disconnect()
        self.connectedSkoobot = None

    def sendCommand(self, data, waitForResponse=False):
        if self.connectedSkoobot == None:
            raise RuntimeError("BLE not connected")
        data = int(data)
        cmdBytes = data.to_bytes(1, byteorder="little")
        characteristics = self.transport.getRawCharacteristicsByUUID(
            self.uuids["cmd"])
        if len(characteristics) == 0:
            raise RuntimeError("cmd characteristic not supported by firmware")
        cmd = characteristics[0]
        cmd.write(cmdBytes, waitForResponse)

    def readBytes(self, charName="data"):
        """
        Read an array of bytes from the named characteristic

        returns a bytearray of the data
        """
        if self.connectedSkoobot == None:
            raise RuntimeError("BLE not connected")
        characteristics = self.transport.getRawCharacteristicsByUUID(
            self.uuids[charName])
        if len(characteristics) == 0:
            raise RuntimeError(
                "{0:s} characteristic not supported by firmware".format(
                    charName))
        charac = characteristics[0]
        dataBytes = charac.read()
        return dataBytes

    def readData(self, charName="data"):
        dataBytes = self.readBytes(charName)
        value = int.from_bytes(dataBytes, byteorder="little")
        return value

    def cmdRight(self):
        self.sendCommand(CMD_RIGHT, True)

    def cmdLeft(self):
        self.sendCommand(CMD_LEFT, True)

    def cmdForward(self):
        self.sendCommand(CMD_FORWARD, True)

    def cmdBackward(self):
        self.sendCommand(CMD_BACKWARD, True)

    def cmdStop(self):
        self.sendCommand(CMD_STOP, True)

    def cmdSleep(self):
        self.sendCommand(CMD_SLEEP, True)

    def cmdRoverMode(self):
        self.sendCommand(CMD_ROVER_MODE, True)

    def requestDistance(self):
        self.sendCommand(CMD_GET_DISTANCE, True)
        return self.readData()

    def requestAmbientLight(self):
        self.sendCommand(CMD_GET_AMBIENT, True)
        return self.readData("byte2")
Example #10
0
    def testBug8(self):
        """
        Tests the resolution of bug #8
        """
        badDefaultName = "gremlin"
        registry = SkoobotRegistry(self.tempPath)
        with self.subTest("Setting bad default"):
            oldDefault = registry.getDefaultName()
            with self.assertRaises(ValueError):
                registry.setDefault(badDefaultName)
            self.assertEqual(oldDefault, registry.getDefaultName())

        with self.subTest("Loading bad default"):
            self.registryDict["default"] = badDefaultName
            with open(self.tempPath, "w") as registryFile:
                json.dump(self.registryDict, registryFile, indent=4)
            registry.load()
            self.assertEqual(None, registry.getDefaultName())

        with self.subTest("Loading good default"):
            self.registryDict["default"] = self.skooName
            with open(self.tempPath, "w") as registryFile:
                json.dump(self.registryDict, registryFile, indent=4)
            registry.load()
            self.assertEqual(self.skooName, registry.getDefaultName())
Example #11
0
    def testSave(self):
        """
        Tests for the save() method

        Make sure that save() works, except when the registry is
        marked invalid.
        """
        registry = SkoobotRegistry(self.tempPath)
        altSkooAddr = "aa:aa:aa:aa:aa:aa"
        altSkooName = "Alt"
        extraSkooAddr = "ee:ee:ee:ee:ee:ee"
        extraSkooName = "Extra"

        with self.subTest("Undo alterations"):
            registry.addSkoobot(altSkooAddr, altSkooName)
            registry.setDefault(altSkooAddr)
            self.assertEqual(4, len(registry.registry))

            registry.load()

            self.assertEqual(3, len(registry.registry))
            self.assertEqual(self.skooName, registry.getDefaultName())

        with self.subTest("Alter and save"):
            registry.addSkoobot(altSkooAddr, altSkooName)
            registry.setDefault(altSkooAddr)
            self.assertEqual(4, len(registry.registry))

            # Save the state with the AltSkootbot entry
            registry.save()

            registry.addSkoobot(extraSkooAddr, extraSkooName)
            registry.setDefault(extraSkooAddr)
            self.assertEqual(5, len(registry.registry))
            self.assertEqual(extraSkooName, registry.getDefaultName())

            # Restore to the save() state
            registry.load()

            self.assertEqual(4, len(registry.registry))
            self.assertEqual(altSkooName, registry.getDefaultName())

        with self.subTest("Don't save invalid"):
            registry.addSkoobot(extraSkooAddr, altSkooName)
            registry.setDefault(extraSkooAddr)
            self.assertEqual(5, len(registry.registry))
            registry.valid = False

            # Fail to save the state with the Extra entry
            registry.save()

            # Restore to the previous save() state
            registry.load()

            self.assertEqual(4, len(registry.registry))
            self.assertEqual(altSkooName, registry.getDefaultName())
Example #12
0
    def testLoad(self):
        """
        Test for loading the registry.

        Most of this is already tested by the constructor tests,
        however, we need to check that a reload works and that a
        failed load sets the valid flag to false
        """
        registry = SkoobotRegistry(self.tempPath)

        with self.subTest("Empty dict"):
            emptyDict = {}
            with open(self.tempPath, "w") as registryFile:
                json.dump(emptyDict, registryFile)
            self.assertEqual(3, len(registry.registry))
            registry.load()
            self.assertEqual(0, len(registry.registry))
            self.assertEqual(True, registry.valid)
            self.assertEqual(None, registry.getDefaultName())

        with self.subTest("Invalid dict"):
            with open(self.tempPath, "w") as registryFile:
                registryFile.write("rubbish")
            registry.addSkoobot(self.skooAddr)
            self.assertEqual(True, registry.valid)

            with self.assertRaises(json.JSONDecodeError):
                registry.load()

            self.assertEqual(0, len(registry.registry))
            self.assertEqual(False, registry.valid)
            self.assertEqual(None, registry.getDefaultName())

        with self.subTest("Reload good dict"):
            with open(self.tempPath, "w") as registryFile:
                json.dump(self.registryDict, registryFile)
            self.assertEqual(0, len(registry.registry))
            registry.load()
            self.assertEqual(3, len(registry.registry))
            self.assertEqual(True, registry.valid)
            self.assertEqual(self.skooName, registry.getDefaultName())
Example #13
0
    def testSetDefault(self):
        """
        Test for method setDefault()

        Method sets the default name. It takes one parameter, which is either
        the address or the name.
        """
        registry = SkoobotRegistry(self.tempPath)

        registry.setDefault(self.skooDupName)
        self.assertEqual(self.skooDupName, registry.getDefaultName())

        registry.setDefault(self.skooAddr)
        self.assertEqual(self.skooName, registry.getDefaultName())

        registry.setDefault(self.skooDupAddr1)
        self.assertEqual(self.skooDupName, registry.getDefaultName())
Example #14
0
    def testAddSkoobot(self):
        """
        Test addition of skoobots using the addSkoobot() method

        The method adds a skoobot to the registry using an address
        and an optional name.
        """
        registry = SkoobotRegistry(self.tempPath)
        namedAddr = "ff:ff:ff:ff:ff:ff"
        namedName = "newSkoobot"
        unnamedAddr = "ff:ff:ff:ff:ff:fe"

        with self.subTest("Add named Skoobot"):
            registry.addSkoobot(namedAddr, namedName)
            self.assertEqual(4, len(registry.registry))
            self.assertEqual(1, len(registry.getSkoobotsByAddress(namedAddr)))
            self.assertEqual(1, len(registry.getSkoobotsByName(namedName)))

        with self.subTest("Add unnamed Skoobot"):
            registry.addSkoobot(unnamedAddr)
            self.assertEqual(5, len(registry.registry))
            skoobots = registry.getSkoobotsByAddress(unnamedAddr)
            self.assertEqual(1, len(skoobots))
            self.assertIn(skoobots[0][1], registry.skoobotNames)

        with self.subTest("Add duplicate Skoobot"):
            # Bug #7: By default this replaces the existing
            # skoobot. If the replace=False parameter is set,
            # it raises a RuntimeError unless the parameters
            # are compatible with the existing entry.
            #
            # It is always true that it does not result in a
            # duplicate address.
            registry.addSkoobot(namedAddr, namedName)
            self.assertEqual(5, len(registry.registry))

            registry.addSkoobot(namedAddr, replace=False)
            self.assertEqual(5, len(registry.registry))

            with self.assertRaises(RuntimeError):
                registry.addSkoobot(unnamedAddr, namedName, replace=False)

        with self.subTest("Test invalid parameters"):
            with self.assertRaises(TypeError):
                registry.addSkoobot((namedAddr, namedName))

            with self.assertRaises(TypeError):
                registry.addSkoobot(namedAddr, (namedAddr, namedName))