예제 #1
0
    def __init__(self, rPacket, translations, hub_variables, cloud_ep):
        self.rPacket = rPacket
        self.hubVariables = hub_variables
        self.translations = translations
        self.cloud_ep = cloud_ep
        self.returnPacket = RadioPacket(rPacket)

        #for now, hack hub_variables into the code the uses PI_HEADER
        PI_HEADER['school-id'] = self.hubVariables['query_string']['school-id']
        PI_HEADER['pi-id'] = self.hubVariables['query_string']['pi-id']
예제 #2
0
 def __init__(self, rPacket, translations, hub_variables, cloud_ep):
     self.rPacket = rPacket
     self.hubVariables = hub_variables
     self.translations = translations
     self.cloud_ep = cloud_ep
     self.returnPacket = RadioPacket(rPacket)
     file = Path(PERSIST_FILE)
     if file.is_file():
         file = open(PERSIST_FILE, 'r')
         self.PI_ID = pickle.load(file)
         file.close()
         PI_HEADER['school-id'] = self.PI_ID['schoolId']
         PI_HEADER['pi-id'] = self.PI_ID['piId']
     else:
         PI_ID['schoolId'] = PI_HEADER['school-id'] = None
         PI_ID['piId'] = PI_HEADER['pi-id'] = None
         self.PI_ID = PI_ID
     print PI_HEADER
예제 #3
0
    def drain(self):
        if self.changes == []:
            return ''

        if len(self.changes) == 1:
            head, changes = self.changes[0], []
        else:
            head, changes = self.changes[0], self.changes[1:]

        # broadcast packet indicates we don't expect an ACK -- We've received this from a different hub.
        r = RadioPacket(
            None, head["appId"], head["uid"],
            RadioPacket.REQUEST_TYPE_CLOUD_VARIABLE
            | RadioPacket.REQUEST_TYPE_BROADCAST)
        r.append(head["namespace"])
        r.append(head["variable_name"])
        r.append(head["value"])

        self.changes = changes

        return r.marshall()
예제 #4
0
파일: hub.py 프로젝트: yarsanich/rest-radio
serial_handler = SerialHandler(selected)

# load translations JSON file
translationsFile = open("./translations.json")
translations = json.load(translationsFile)
github_poller = URLPoller(hub_variables["translations_json"]["url"],
                          hub_variables["translations_json"]["poll_time"])

packet_count = 0

try:
    while (True):

        # if the bridged micro:bit has sent us data, process
        if serial_handler.buffered() > 0:
            rPacket = RadioPacket(serial_handler.read_packet())

            packet_count += 1
            now = datetime.datetime.now()
            print "Request Time: %d:%d:%d; Packets Seen: %d" % (
                now.hour, now.minute, now.second, packet_count)

            requestHandler = RequestHandler(rPacket, translations, shared_dict,
                                            None)

            # spawn a new thread to handle the request
            p = Process(target=handleRequest,
                        args=(
                            requestHandler,
                            serial_handler,
                        ))
예제 #5
0
class RequestHandler:
    def __init__(self, rPacket, translations, hub_variables, cloud_ep):
        self.rPacket = rPacket
        self.hubVariables = hub_variables
        self.translations = translations
        self.cloud_ep = cloud_ep
        self.returnPacket = RadioPacket(rPacket)

        #for now, hack hub_variables into the code the uses PI_HEADER
        PI_HEADER['school-id'] = self.hubVariables['query_string']['school-id']
        PI_HEADER['pi-id'] = self.hubVariables['query_string']['pi-id']

    """
    Recursively traverse a python json structure given a dot separated path. Array indices also work here.

    query.results.channel.item.condition

    or

    query.results.channel.item.forecast[0]
    """

    def __json_recursive_find__(self, parts, json):
        if parts == []:
            return json

        if len(parts) == 1:
            head, rest = parts[0], []
        else:
            head, rest = parts[0], parts[1:]

        if isinstance(head, basestring):
            arrayMatch = head.find("[")

            if arrayMatch > -1:
                rest += [int(head[arrayMatch + 1:head.find("]")])]
                head = head[:arrayMatch]

                # print rest, head

            if head not in json.keys():
                return {}

        return self.__json_recursive_find__(rest, json[head])

    """
    util to join two dicts
    """

    def __join_dicts(self, dict1, dict2):
        dict3 = dict1.copy()
        dict3.update(dict2)
        return dict3

    def replace_template_with_values(self, regular_expressions, object,
                                     values):
        # foreach regexp result from our regexps, map values from the packet into the query object
        for regExp in regular_expressions:
            for tup in regular_expressions[regExp]:
                match, key, default = tup

                value = None

                if key in values.keys():
                    value = values[key]
                elif default == "":
                    # optional
                    del object[regExp]
                    continue
                elif default:
                    value = default
                else:
                    # error
                    return self.rPacket.marshall(False)

                # coerce all into strings for now?
                if regExp in object.keys():
                    object[regExp] = object[regExp].replace(match, str(value))

        # print str(object)

    """
    given a start index, extract further objects from a radio packet, mapping them into the params object

    Returns an object with params as keys, and values from the packet as values
    """

    def extractFurtherObjects(self, index, parameters):

        ret = {}
        obj = self.rPacket.get(index)
        count = 0
        while obj is not None:
            ret[parameters[count]["name"]] = obj
            index += 1
            count += 1
            obj = self.rPacket.get(index)

        return ret

    """
    mapQueryString

    maps a microbit url string into the url format expected by the rest endpoint, extracting each into a corresponding variable:

    translations.json:
    /weather/%location%/temperature

    microbit querystring:
    /weather/lancaster,uk/temperature

    output:
    {
        "location":"lancaster,uk"
    }

    """

    def mapQueryString(self, url, urlFormat):
        part, rest = url[0], url[1:]
        out = {}

        # map parts of the packet into the querystring first.
        for format in urlFormat:
            name = re.search("%(.*)%", format)
            key = name.group(1)

            if key[-1] == "?":
                if part == None:
                    break

                key = key[:-1]

            out[key] = part

            # the line after won't work if rest is empty...
            if len(rest) == 0:
                part = None
                continue

            part, rest = rest[0], rest[1:]

        return out

    def processRESTRequest(self, url, request_type, translation, part):
        operation = translation[request_type]
        if "baseURL" in operation:
            baseURL = operation["baseURL"]

        urlFormat = [
            x for x in operation["microbitQueryString"].split("/") if x
        ]

        #print "baseURL"
        #print baseURL

        # the following giant mess needs to be refactored at some point... but not now.
        if part == PKG_CARBON:
            res = 'OK'
            #print "Handle carbon package here"
            #print "method url"
            #print url
            if url[0] == "index":
                URLreq = baseURL + "intensity"
                try:
                    r = requests.get(URLreq)
                except requests.exceptions.RequestException as e:
                    print "Connection error: {}".format(e)
                    self.returnPacket.append("API CONNECTION ERROR")
                    return self.returnPacket.marshall(True)
                response = json.loads(r.text)
                #print response['data'][0]['intensity'][url[0]]
                res = response['data'][0]['intensity'][url[0]]

            if url[0] == "value":
                URLreq = baseURL + "intensity"
                try:
                    r = requests.get(URLreq)
                except requests.exceptions.RequestException as e:
                    print "Connection error: {}".format(e)
                    self.returnPacket.append("API CONNECTION ERROR")
                    return self.returnPacket.marshall(True)
                response = json.loads(r.text)
                if response['data'][0]['intensity']['actual'] is not None:
                    res = str(response['data'][0]['intensity']['actual'])
                else:
                    res = str(response['data'][0]['intensity']['forecast'])

            if url[0] == 'genmix':
                URLreq = baseURL + "generation"
                try:
                    r = requests.get(URLreq)
                except requests.exceptions.RequestException as e:
                    print "Connection error: {}".format(e)
                    self.returnPacket.append("API CONNECTION ERROR")
                    return self.returnPacket.marshall(True)
                response = json.loads(r.text)
                genmix = response['data']['generationmix']
                for gendata in genmix:
                    if gendata['fuel'] == url[1]:
                        res = str(gendata['perc'])
                        break

            #print res
            self.returnPacket.append(res)
            return self.returnPacket.marshall(True)

        if part == PKG_ENERGY:
            res = "OK"
            #print "Handle energy package here"
            #print baseURL
            #print url
            if url[0] == "energyLevel":
                if url[1] == "0":
                    URLreq = baseURL + "energy_type=ELECTRICITY"
                elif url[1] == "1":
                    URLreq = baseURL + "energy_type=GAS"
                elif url[1] == "2":
                    URLreq = baseURL + "energy_type=SOLAR"

            if url[2] == "local":
                URLreq = URLreq + "&location_uid=" + PI_HEADER['school-id']
            else:
                URLreq = URLreq + "&location_uid=" + url[2]

            if len(url) > 3:
                if url[3] == "historical":
                    today = datetime.datetime.today()
                    if url[4] == "hour":
                        n_hours = int(url[5])
                        FromDay = datetime.datetime.today() - timedelta(
                            hours=n_hours)
                    if url[4] == "day":
                        n_days = int(url[5])
                        FromDay = datetime.datetime.today() - timedelta(
                            days=n_days)
                    if url[4] == "week":
                        n_weeks = int(url[5])
                        FromDay = datetime.datetime.today() - timedelta(
                            weeks=n_weeks)
                    if url[4] == "month":
                        n_months = int(url[5])
                        n_days = 30 * n_months
                        FromDay = datetime.datetime.today() - timedelta(
                            days=n_days)
                    URLreq = URLreq + "&from=" + FromDay.strftime(
                        '%Y-%m-%d %H:%M:%S.%f') + "&to=" + today.strftime(
                            '%Y-%m-%d %H:%M:%S.%f')

            print URLreq

            try:
                resp = requests.get(URLreq, headers=PI_HEADER)
                print resp.status_code
                if resp.status_code == 200:
                    resJson = json.loads(resp.text)
                    if 'value' in resJson:
                        res = str(resJson['value'])
                    else:
                        res = resJson
                else:
                    res = 0

            except requests.exceptions.RequestException as e:
                print "Connection error: {}".format(e)
                self.returnPacket.append("API CONNECTION ERROR")
                return self.returnPacket.marshall(True)

            self.returnPacket.append(res)
            return self.returnPacket.marshall(True)

        if part == PKG_ISS:
            res = "OK"
            #print url[0]
            try:
                resp = requests.get(baseURL)
                resJson = json.loads(resp.text)
                #print resJson
                if url[0] == "location":
                    res = "Lat:" + \
                        str(round(resJson['latitude'], 4)) + \
                        ", Lon:" + str(round(resJson['longitude'], 4))
                elif url[0] == "solarlocation":
                    res = "Lat:" + \
                        str(round(resJson['solar_lat'], 4)) + \
                        ", Lon:" + str(round(resJson['solar_lon'], 4))
                elif url[0] == "velocity":
                    res = int(round(resJson[url[0]], 2))
                elif url[0] == "altitude":
                    res = int(round(resJson[url[0]], 2))
                elif url[0] == "daynum":
                    epoch = datetime.datetime.utcfromtimestamp(0)
                    today = datetime.datetime.today()
                    d = today - epoch
                    res = d.days
                    #res = int(round(resJson[url[0]],2))
                elif url[0] in resJson:
                    res = resJson[url[0]]

            except requests.exceptions.RequestException as e:
                print "Connection error: {}".format(e)
                self.returnPacket.append("API CONNECTION ERROR")
                return self.returnPacket.marshall(True)

            self.returnPacket.append(res)
            return self.returnPacket.marshall(True)

        if part == PKG_INIT:
            res = "OK"
            #print "Handle Init package here"
            #print "method url"
            #print url
            #print PI_ID
            id = self.rPacket.get(1)
            if id == "reset":
                if os.path.isfile(PERSIST_FILE):
                    os.remove(PERSIST_FILE)
                    #print "done"
                # else:
                #print "not done"

            elif url[0] == "piId" or url[0] == "schoolId":
                if self.PI_ID[url[0]] is None:
                    self.PI_ID[url[0]] = id
                    file = open(PERSIST_FILE, 'w')
                    pickle.dump(self.PI_ID, file)
                    file.close()
                    print "OK"
                else:
                    res = self.PI_ID[url[0]]
                    print "Not OK"
            #print PI_ID[url[0]]
            self.returnPacket.append(res)
            return self.returnPacket.marshall(True)

        if part == PKG_SHARE:
            res = "OK"
            if PI_HEADER['school-id'] == None or PI_HEADER['pi-id'] == None:
                print "Check headers"
                print PI_HEADER
            #print "Handle share package here"
            #print "method url"
            #print url

            if url[0] == "fetchData":
                URLreq = baseURL + url[1]
                try:

                    resp = requests.get(URLreq, headers=PI_HEADER)

                except requests.exceptions.RequestException as e:
                    print "Connection error: {}".format(e)
                    self.returnPacket.append("API CONNECTION ERROR")
                    return self.returnPacket.marshall(True)
                #print URLreq
                resJson = json.loads(resp.text)
                print resJson
                if 'value' in resJson:
                    res = resJson['value']
                else:
                    res = "NOT FOUND"

            if url[0] == "shareData":
                jsonData = {'shared_with': 'SCHOOL', 'value': '0'}
                jsonData['value'] = self.rPacket.get(1)
                #jsonData['value'] = jsonData['description']
                name = self.rPacket.get(2)
                URLreq = baseURL + name + "/"
                varType = self.rPacket.get(2)
                if varType == 0:
                    jsonData['shared_with'] = 'ALL'
                else:
                    jsonData['shared_with'] = 'SCHOOL'
                print jsonData
                try:
                    resp = requests.post(URLreq,
                                         headers=PI_HEADER,
                                         data=jsonData)
                except requests.exceptions.RequestException as e:
                    print "Connection error: {}".format(e)
                    self.returnPacket.append("API CONNECTION ERROR")
                    return self.returnPacket.marshall(True)
            if url[0] == "historicalData":
                jsonData = {
                    'namespace': 'histery',
                    'name': ' ',
                    'type': 0,
                    'unit': ' ',
                    'time': ' ',
                    'value': ' '
                }
                jsonData['value'] = self.rPacket.get(1)
                jsonData['name'] = self.rPacket.get(2)
                jsonData['namespace'] = self.rPacket.get(3)
                jsonData['unit'] = self.rPacket.get(4)
                jsonData['time'] = datetime.datetime.today().strftime(
                    "%Y-%m-%d %H:%M:%S")
                URLreq = operation["extraURL"]
                #print jsonData
                try:
                    resp = requests.post(URLreq,
                                         headers=PI_HEADER,
                                         data=jsonData)
#print resp
                except requests.exceptions.RequestException as e:
                    print "Connection error: {}".format(e)
                    self.returnPacket.append("API CONNECTION ERROR")
                    return self.returnPacket.marshall(True)

            #print resp
            self.returnPacket.append(res)
            return self.returnPacket.marshall(True)

        # map micro:bit query string to variables
        out = self.mapQueryString(url, urlFormat)

        #print "out maping"
        #print str(out)

        #print urlFormat

        # auth_token ='ddca3062-11ff-4116-87dc-36da9f01afe6'
        # hed = {'Authorization': 'Bearer ' + auth_token}
        # dataOn = {"commands":[{"component":"main","capability": "switch", "command":"on"}]}
        # dataOff = {"commands":[{"component":"main","capability": "switch", "command":"off"}]}

        # if part == PKG_IOT :

        # baseURL = "https://api.smartthings.com/v1/devices/1439773a-c144-41cd-9c5d-d1b03d3fe0a1/commands"

        # data1 = self.rPacket.get(1)
        # data2 = self.rPacket.get(2)

        # try:
        #     if data2 == 1:
        #         requests.post(baseURL, json=dataOn,headers=hed)
        #     else:
        #         requests.post(baseURL, json=dataOff,headers=hed)

        # except requests.exceptions.RequestException as e:
        #     print "Connection error: {}".format(e)
        #     self.returnPacket.append("API CONNECTION ERROR")
        #     return self.returnPacket.marshall(True)

        # self.returnPacket.append("OK")
        # return self.returnPacket.marshall(True)

        if part == PKG_IOT:
            res = "OK"
            if PI_HEADER['school-id'] == None or PI_HEADER['pi-id'] == None:
                print "Check headers"
                print PI_HEADER

            if request_type == "GET":
                URLreq = baseURL + url[1]

                if url[0] == "bulbState" or url[0] == "switchState":
                    URLreq = URLreq + "/switch/"

                elif url[0] == "bulbLevel":
                    URLreq = URLreq + "/switch-level/"

                elif url[0] == "bulbTemp":
                    URLreq = URLreq + "/color-temperature/"

                elif url[0] == "sensorState":
                    URLreq = URLreq + "/motion/"

                elif url[0] == "sensorTemp":
                    URLreq = URLreq + "/temperature/"

                elif url[0] == "bulbColour":
                    URLreq = URLreq + "/color-control/"

                else:
                    print "Unknown request!!!"
                    self.returnPacket.append("Unknown request!!!")
                    return self.returnPacket.marshall(True)

                try:
                    #print "URLreq:", URLreq

                    resp = requests.get(URLreq, headers=PI_HEADER)

                except requests.exceptions.RequestException as e:
                    print "Connection error: {}".format(e)
                    self.returnPacket.append("API CONNECTION ERROR")
                    return self.returnPacket.marshall(True)
                #print URLreq
                # response = {"device": "bulb", "status": {"level": "90","color": "unknown"}}
                response = json.loads(resp.text)
                #print response
                res = str(response['value'])
                # print res

            elif request_type == "POST":
                jsonData = {'value': 'off'}

                name = self.rPacket.get(1)
                URLreq = baseURL + name

                if url[0] == "bulbState" or url[0] == "switchState":
                    URLreq = URLreq + "/switch/"
                    switchState = self.rPacket.get(2)
                    if switchState == 0:
                        jsonData['value'] = 'off'
                    else:
                        jsonData['value'] = 'on'

                elif url[0] == "bulbLevel":
                    URLreq = URLreq + "/switch-level/"
                    level = self.rPacket.get(2)
                    jsonData['value'] = level

                elif url[0] == "bulbTemp":
                    URLreq = URLreq + "/color-temperature/"
                    level = self.rPacket.get(2)
                    jsonData['value'] = level

                elif url[0] == "bulbColour":
                    URLreq = URLreq + "/color-control/"
                    level = self.rPacket.get(2)
                    jsonData['value'] = level

                else:
                    print "Unknown request!!!"
                    self.returnPacket.append("Unknown request!!!")
                    return self.returnPacket.marshall(True)

                try:
                    resp = requests.post(URLreq,
                                         headers=PI_HEADER,
                                         data=jsonData)

                except requests.exceptions.RequestException as e:
                    print "Connection error: {}".format(e)
                    self.returnPacket.append("API CONNECTION ERROR")
                    return self.returnPacket.marshall(True)

            #print resp
            self.returnPacket.append(res)
            return self.returnPacket.marshall(True)

        # any code above needs to be refactored.

        # if no endpoint is specified, set a default key value of none
        if out["endpoint"] is None:
            out["endpoint"] = "none"

        # if there is no matching endpoint return error packet
        if out["endpoint"] not in operation["endpoint"].keys():
            raise self.rPacket.marshall(False)

        endpoint = operation["endpoint"][out["endpoint"]]

        # extract further objects from the packet against the keys specified in the parameters part of the translation, and join with `out`
        if "parameters" in endpoint.keys():
            out = self.__join_dicts(
                out, self.extractFurtherObjects(1, endpoint["parameters"]))

        regexStrings = {}

        queryObject = safe_extract("queryObject", operation, {})
        headers = safe_extract("headers", operation, {})

        # for each query field in the queryobject extract the %variable_name% pattern.
        for param in queryObject:
            regexStrings[param] = re.findall(hub_regexp, queryObject[param])

        # for each query field in the queryobject extract the %variable_name% pattern.
        for param in headers:
            regexStrings[param] = re.findall(hub_regexp, headers[param])

        # attach any hub variables that may be required in the query string
        for param in self.hubVariables["query_string"]:
            # p is the value
            p = self.hubVariables["query_string"][param]
            # provide the regexp for each enter in the regex strings, and key, with no default
            for reg in regexStrings:
                regexStrings[reg] += [("%" + param + "%", param, '')]
            # set the corresponding value in the out obj
            out[param] = p

        # to simplify code, lets lump the base url (that may require regex'ing) into the queryobj
        regexStrings["baseURL"] = re.findall(hub_regexp, baseURL)
        queryObject["baseURL"] = baseURL

        self.replace_template_with_values(regexStrings, queryObject, out)
        self.replace_template_with_values(regexStrings, headers, out)

        # remove our now regexp'd baseURL from the query object
        baseURL = queryObject["baseURL"]
        del queryObject["baseURL"]

        try:
            if request_type == "GET":
                r = requests.get(baseURL, headers=headers, params=queryObject)
            elif request_type == "POST":
                r = requests.post(baseURL, headers=headers, data=queryObject)

            print "Request sent to: %s with headers: %s and data: %s" % (
                baseURL, str(headers), str(queryObject))
            print str(r)
            #TODO: handle bad status codes...
        except requests.exceptions.RequestException as e:
            print "Connection error: {}".format(e)
            return self.returnPacket.marshall(False)

        if "jsonPath" in endpoint.keys():
            path = [x for x in endpoint["jsonPath"].split(".") if x]

            print path

            response = json.loads(r.text)

            jsonObj = self.__json_recursive_find__(path, response)

            returnVariables = endpoint["returns"]

            for ret in returnVariables:
                print jsonObj
                if ret["name"] in jsonObj:
                    print jsonObj[ret["name"]]
                    self.returnPacket.append(str(jsonObj[ret["name"]]))

        return self.returnPacket.marshall(True)

    def handleRESTRequest(self):
        # every rest request should have the URL as the first item.
        url = self.rPacket.get(0)

        pieces = [x for x in url.split("/") if x is not '']

        part, rest = pieces[0], pieces[1:]

        request_type = None

        if part not in self.translations.keys():
            return self.rPacket.marshall(False)

        translation = self.translations[part]

        if self.rPacket.request_type == RadioPacket.REQUEST_TYPE_GET_REQUEST:
            request_type = "GET"

        if self.rPacket.request_type == RadioPacket.REQUEST_TYPE_POST_REQUEST:
            request_type = "POST"

        return self.processRESTRequest(rest, request_type, translation, part)

    '''
    A hello packet has a status and optionally the hub or school id (at the moment..)
    0 is the status
    1 is the school id,
    2 is the hub id
    '''

    def handleHelloPacket(self):

        print "HELLO PACKET!"
        status = self.rPacket.get(0)

        print "REMEMBER TO RESTORE SCHOOL ID..."
        self.hubVariables["query_string"] = {
            "school-id": "655BD",  #self.rPacket.get(1)
            "pi-id": self.rPacket.get(2)
        }

        self.hubVariables["authenticated"] = True

        self.returnPacket.append(0)  # 0  means ok
        return self.returnPacket.marshall(True)

    def handleCloudVariable(self):
        namespaceHash = self.rPacket.get(0)
        variableNameHash = self.rPacket.get(1)
        value = self.rPacket.get(2)
        appId = self.rPacket.app_id

        self.cloud_ep.emit({
            "appId": appId,
            "uid": self.rPacket.uid,
            "namespace": namespaceHash,
            "variable_name": variableNameHash,
            "value": value
        })

        return self.returnPacket.marshall(True)

    def handleRequest(self):

        if self.rPacket.request_type & RadioPacket.REQUEST_TYPE_HELLO:
            return self.handleHelloPacket()

        # check packet type in order to handle request correctly
        if self.rPacket.request_type & (RadioPacket.REQUEST_TYPE_GET_REQUEST |
                                        RadioPacket.REQUEST_TYPE_POST_REQUEST):
            return self.handleRESTRequest()

        if self.rPacket.request_type & (
                RadioPacket.REQUEST_TYPE_CLOUD_VARIABLE):
            return self.handleCloudVariable()

        print "unrecognised packet type"
        exit(1)
예제 #6
0
class RequestHandler:
    def __init__(self, rPacket, translations, hub_variables, cloud_ep):
        self.rPacket = rPacket
        self.hubVariables = hub_variables
        self.translations = translations
        self.cloud_ep = cloud_ep
        self.returnPacket = RadioPacket(rPacket)
        file = Path(PERSIST_FILE)
        if file.is_file():
            file = open(PERSIST_FILE, 'r')
            self.PI_ID = pickle.load(file)
            file.close()
            PI_HEADER['school-id'] = self.PI_ID['schoolId']
            PI_HEADER['pi-id'] = self.PI_ID['piId']
        else:
            PI_ID['schoolId'] = PI_HEADER['school-id'] = None
            PI_ID['piId'] = PI_HEADER['pi-id'] = None
            self.PI_ID = PI_ID
        print PI_HEADER

    """
    Recursively traverse a python json structure given a dot separated path. Array indices also work here.

    query.results.channel.item.condition

    or

    query.results.channel.item.forecast[0]
    """

    def __json_recursive_find__(self, parts, json):
        if parts == []:
            return json

        if len(parts) == 1:
            head, rest = parts[0], []
        else:
            head, rest = parts[0], parts[1:]

        if isinstance(head, basestring):
            arrayMatch = head.find("[")

            if arrayMatch > -1:
                rest += [int(head[arrayMatch + 1:head.find("]")])]
                head = head[:arrayMatch]

                print rest, head

            if head not in json.keys():
                return {}

        return self.__json_recursive_find__(rest, json[head])

    """
    util to join two dicts
    """

    def __join_dicts(self, dict1, dict2):
        dict3 = dict1.copy()
        dict3.update(dict2)
        return dict3

    """
    given a start index, extract further objects from a radio packet, mapping them into the params object

    Returns an object with params as keys, and values from the packet as values
    """

    def extractFurtherObjects(self, index, parameters):

        ret = {}
        obj = self.rPacket.get(index)
        count = 0
        while obj is not None:
            ret[parameters[count]["name"]] = obj
            index += 1
            count += 1
            obj = self.rPacket.get(index)

        return ret

    """
    mapQueryString

    maps a microbit url string into the url format expected by the rest endpoint, extracting each into a corresponding variable:

    translations.json:
    /weather/%location%/temperature

    microbit querystring:
    /weather/lancaster,uk/temperature

    output:
    {
        "location":"lancaster,uk"
    }

    """

    def mapQueryString(self, url, urlFormat):
        part, rest = url[0], url[1:]
        out = {}

        # map parts of the packet into the querystring first.
        for format in urlFormat:
            name = re.search("%(.*)%", format)
            key = name.group(1)

            if key[-1] == "?":
                if part == None:
                    break

                key = key[:-1]

            out[key] = part

            # the line after won't work if rest is empty...
            if len(rest) == 0:
                part = None
                continue

            part, rest = rest[0], rest[1:]

        return out

    def processRESTRequest(self, url, request_type, translation, part):
        operation = translation[request_type]
        if "baseURL" in operation:
            baseURL = operation["baseURL"]

        urlFormat = [
            x for x in operation["microbitQueryString"].split("/") if x
        ]

        #print "baseURL"
        #print baseURL

        if part == PKG_CARBON:
            res = 'OK'
            #print "Handle carbon package here"
            #print "method url"
            #print url
            if url[0] == "index":
                URLreq = baseURL + "intensity"
                try:
                    r = requests.get(URLreq)
                except requests.exceptions.RequestException as e:
                    print "Connection error: {}".format(e)
                    self.returnPacket.append("API CONNECTION ERROR")
                    return self.returnPacket.marshall(True)
                response = json.loads(r.text)
                #print response['data'][0]['intensity'][url[0]]
                res = response['data'][0]['intensity'][url[0]]

            if url[0] == "value":
                URLreq = baseURL + "intensity"
                try:
                    r = requests.get(URLreq)
                except requests.exceptions.RequestException as e:
                    print "Connection error: {}".format(e)
                    self.returnPacket.append("API CONNECTION ERROR")
                    return self.returnPacket.marshall(True)
                response = json.loads(r.text)
                if response['data'][0]['intensity']['actual'] is not None:
                    res = str(response['data'][0]['intensity']['actual'])
                else:
                    res = str(response['data'][0]['intensity']['forecast'])

            if url[0] == 'genmix':
                URLreq = baseURL + "generation"
                try:
                    r = requests.get(URLreq)
                except requests.exceptions.RequestException as e:
                    print "Connection error: {}".format(e)
                    self.returnPacket.append("API CONNECTION ERROR")
                    return self.returnPacket.marshall(True)
                response = json.loads(r.text)
                genmix = response['data']['generationmix']
                for gendata in genmix:
                    if gendata['fuel'] == url[1]:
                        res = str(gendata['perc'])
                        break

            #print res
            self.returnPacket.append(res)
            return self.returnPacket.marshall(True)

        if part == PKG_ENERGY:
            res = "OK"
            #print "Handle energy package here"
            #print baseURL
            #print url
            if url[0] == "energyLevel":
                if url[1] == "0":
                    URLreq = baseURL + "electricity/"
                elif url[1] == "1":
                    URLreq = baseURL + "gas/"

            if url[2] == "local":
                URLreq = URLreq + PI_HEADER['school-id']
            else:
                URLreq = URLreq + url[2]

            print URLreq

            try:
                resp = requests.get(URLreq, headers=PI_HEADER)
                resJson = json.loads(resp.text)

                if 'value' in resJson:
                    res = str(resJson['value'])
                else:
                    res = resJson

            except requests.exceptions.RequestException as e:
                print "Connection error: {}".format(e)
                self.returnPacket.append("API CONNECTION ERROR")
                return self.returnPacket.marshall(True)

            self.returnPacket.append(res)
            return self.returnPacket.marshall(True)

        if part == PKG_ISS:
            res = "OK"
            #print url[0]
            try:
                resp = requests.get(baseURL)
                resJson = json.loads(resp.text)
                #print resJson
                if url[0] == "location":
                    res = "Lat:" + \
                        str(round(resJson['latitude'], 4)) + \
                        ", Lon:" + str(round(resJson['longitude'], 4))
                elif url[0] == "solarlocation":
                    res = "Lat:" + \
                        str(round(resJson['solar_lat'], 4)) + \
                        ", Lon:" + str(round(resJson['solar_lon'], 4))
                elif url[0] == "velocity":
                    res = int(round(resJson[url[0]], 2))
                elif url[0] == "altitude":
                    res = int(round(resJson[url[0]], 2))
                elif url[0] == "daynum":
                    epoch = datetime.datetime.utcfromtimestamp(0)
                    today = datetime.datetime.today()
                    d = today - epoch
                    res = d.days
                    #res = int(round(resJson[url[0]],2))
                elif url[0] in resJson:
                    res = resJson[url[0]]

            except requests.exceptions.RequestException as e:
                print "Connection error: {}".format(e)
                self.returnPacket.append("API CONNECTION ERROR")
                return self.returnPacket.marshall(True)

            self.returnPacket.append(res)
            return self.returnPacket.marshall(True)

        if part == PKG_INIT:
            res = "OK"
            #print "Handle Init package here"
            #print "method url"
            #print url
            #print PI_ID
            id = self.rPacket.get(1)
            if id == "reset":
                if os.path.isfile(PERSIST_FILE):
                    os.remove(PERSIST_FILE)
                    #print "done"
                # else:
                #print "not done"

            elif url[0] == "piId" or url[0] == "schoolId":
                if self.PI_ID[url[0]] is None:
                    self.PI_ID[url[0]] = id
                    file = open(PERSIST_FILE, 'w')
                    pickle.dump(self.PI_ID, file)
                    file.close()
                    print "OK"
                else:
                    res = self.PI_ID[url[0]]
                    print "Not OK"
            #print PI_ID[url[0]]
            self.returnPacket.append(res)
            return self.returnPacket.marshall(True)

        if part == PKG_SHARE:
            res = "OK"
            if PI_HEADER['school-id'] == None or PI_HEADER['pi-id'] == None:
                print "Check headers"
                print PI_HEADER
            #print "Handle share package here"
            #print "method url"
            #print url

            if url[0] == "fetchData":
                URLreq = baseURL + url[1]
                try:

                    resp = requests.get(URLreq, headers=PI_HEADER)

                except requests.exceptions.RequestException as e:
                    print "Connection error: {}".format(e)
                    self.returnPacket.append("API CONNECTION ERROR")
                    return self.returnPacket.marshall(True)
                #print URLreq
                resJson = json.loads(resp.text)
                print resJson
                if 'value' in resJson:
                    res = resJson['value']
                else:
                    res = "NOT FOUND"

            if url[0] == "shareData":
                jsonData = {'shared_with': 'SCHOOL', 'value': '0'}
                jsonData['value'] = self.rPacket.get(1)
                #jsonData['value'] = jsonData['description']
                name = self.rPacket.get(2)
                URLreq = baseURL + name + "/"
                varType = self.rPacket.get(2)
                if varType == 0:
                    jsonData['shared_with'] = 'ALL'
                else:
                    jsonData['shared_with'] = 'SCHOOL'
                print jsonData
                try:
                    resp = requests.post(URLreq,
                                         headers=PI_HEADER,
                                         data=jsonData)
                except requests.exceptions.RequestException as e:
                    print "Connection error: {}".format(e)
                    self.returnPacket.append("API CONNECTION ERROR")
                    return self.returnPacket.marshall(True)

            print resp
            self.returnPacket.append(res)
            return self.returnPacket.marshall(True)

        # map micro:bit query string to variables
        out = self.mapQueryString(url, urlFormat)

        #print "out maping"
        #print str(out)

        #print urlFormat

        # auth_token ='ddca3062-11ff-4116-87dc-36da9f01afe6'
        # hed = {'Authorization': 'Bearer ' + auth_token}
        # dataOn = {"commands":[{"component":"main","capability": "switch", "command":"on"}]}
        # dataOff = {"commands":[{"component":"main","capability": "switch", "command":"off"}]}

        # if part == PKG_IOT :

        # baseURL = "https://api.smartthings.com/v1/devices/1439773a-c144-41cd-9c5d-d1b03d3fe0a1/commands"

        # data1 = self.rPacket.get(1)
        # data2 = self.rPacket.get(2)

        # try:
        #     if data2 == 1:
        #         requests.post(baseURL, json=dataOn,headers=hed)
        #     else:
        #         requests.post(baseURL, json=dataOff,headers=hed)

        # except requests.exceptions.RequestException as e:
        #     print "Connection error: {}".format(e)
        #     self.returnPacket.append("API CONNECTION ERROR")
        #     return self.returnPacket.marshall(True)

        # self.returnPacket.append("OK")
        # return self.returnPacket.marshall(True)

        if part == PKG_IOT:
            res = "OK"
            if PI_HEADER['school-id'] == None or PI_HEADER['pi-id'] == None:
                print "Check headers"
                print PI_HEADER

            if request_type == "GET":
                URLreq = baseURL + url[1]
                valueTag = 'level'

                if url[0] == "bulbState":
                    URLreq = URLreq + "/bulb"
                elif url[0] == "bulbLevel":
                    URLreq = URLreq + "/bulb"
                elif url[0] == "switchState":
                    URLreq = URLreq + "/switch"
                    valueTag = 'state'

                URLreq = URLreq + "/status/"

                try:
                    print "URLreq:", URLreq

                    resp = requests.get(URLreq, headers=PI_HEADER)

                except requests.exceptions.RequestException as e:
                    print "Connection error: {}".format(e)
                    self.returnPacket.append("API CONNECTION ERROR")
                    return self.returnPacket.marshall(True)
                #print URLreq
                # response = {"device": "bulb", "status": {"level": "90","color": "unknown"}}
                response = json.loads(resp.text)
                print response
                res = str(response['status'][valueTag])
                # print res

            elif request_type == "POST":
                jsonData = {
                    'device': 'switch',
                    'command': 'Switch',
                    'value': 'off'
                }

                name = self.rPacket.get(1)
                URLreq = baseURL + name

                if url[0] == "bulbState":
                    URLreq = URLreq + "/bulb"
                    switchState = self.rPacket.get(2)
                    if switchState == 0:
                        jsonData['value'] = 'off'
                    else:
                        jsonData['value'] = 'on'
                elif url[0] == "bulbLevel":
                    URLreq = URLreq + "/bulb"
                    jsonData['device'] = 'bulb'
                    jsonData['command'] = 'SetLevel'
                    level = self.rPacket.get(2)
                    jsonData['value'] = level
                elif url[0] == "switchState":
                    URLreq = URLreq + "/switch"
                    switchState = self.rPacket.get(2)
                    if switchState == 0:
                        jsonData['value'] = 'off'
                    else:
                        jsonData['value'] = 'on'

                URLreq = URLreq + "/command/"

                try:
                    print "URLreq:", URLreq
                    print "jsonData:", jsonData

                    resp = requests.post(URLreq,
                                         headers=PI_HEADER,
                                         data=jsonData)
                except requests.exceptions.RequestException as e:
                    print "Connection error: {}".format(e)
                    self.returnPacket.append("API CONNECTION ERROR")
                    return self.returnPacket.marshall(True)

            print resp
            self.returnPacket.append(res)
            return self.returnPacket.marshall(True)

        # if no endpoint is specified, set a default key value of none
        if out["endpoint"] is None:
            out["endpoint"] = "none"

        # if there is no matching endpoint return error packet
        if out["endpoint"] not in operation["endpoint"].keys():
            raise self.rPacket.marshall(False)

        endpoint = operation["endpoint"][out["endpoint"]]

        # extract further objects from the packet against the keys specified in the parameters part of the translation, and join with `out`
        if "parameters" in endpoint.keys():
            out = self.__join_dicts(
                out, self.extractFurtherObjects(1, endpoint["parameters"]))

        regexStrings = {}

        if "queryObject" in operation:
            queryObject = operation["queryObject"]

        # for each query field in the queryobject extract the %variable_name% pattern.
        for param in queryObject:
            regexStrings[param] = re.findall(hub_regexp, queryObject[param])

        # attach any hub variables that may be required in the query string
        for param in self.hubVariables["query_string"]:
            # p is the value
            p = self.hubVariables["query_string"][param]
            # provide the regexp for each enter in the regex strings, and key, with no default
            for reg in regexStrings:
                regexStrings[reg] += [("%" + param + "%", param, '')]
            # set the corresponding value in the out obj
            out[param] = p

        # to simplify code, lets lump the base url (that may require regex'ing) into the queryobj
        regexStrings["baseURL"] = re.findall(hub_regexp, baseURL)
        queryObject["baseURL"] = baseURL

        # foreach regexp result from our regexps, map values from the packet into the query object
        for regExp in regexStrings:
            for tup in regexStrings[regExp]:
                match, key, default = tup

                value = None

                if key in out.keys():
                    value = out[key]
                elif default == "":
                    # optional
                    del queryObject[regExp]
                    continue
                elif default:
                    value = default
                else:
                    # error
                    return self.rPacket.marshall(False)

                # coerce all into strings for now?
                queryObject[regExp] = queryObject[regExp].replace(
                    match, str(value))

        print str(queryObject)

        # remove our now regexp'd baseURL from the query object
        baseURL = queryObject["baseURL"]
        del queryObject["baseURL"]

        try:

            if request_type == "GET":
                r = requests.get(baseURL, params=queryObject)
            elif request_type == "POST":
                r = requests.post(baseURL, data=queryObject)

        except requests.exceptions.RequestException as e:
            print "Connection error: {}".format(e)
            self.returnPacket.append("API CONNECTION ERROR")
            return self.returnPacket.marshall(True)

        if "jsonPath" in endpoint.keys():
            path = [x for x in endpoint["jsonPath"].split(".") if x]

            print path

            response = json.loads(r.text)

            jsonObj = self.__json_recursive_find__(path, response)

            returnVariables = endpoint["returns"]

            for ret in returnVariables:
                print jsonObj
                if ret["name"] in jsonObj:
                    print jsonObj[ret["name"]]
                    self.returnPacket.append(str(jsonObj[ret["name"]]))

        return self.returnPacket.marshall(True)

    def handleRESTRequest(self):

        # every rest request should have the URL as the first item.
        url = self.rPacket.get(0)

        #print "url"

        #print url

        now = datetime.datetime.now()
        print "------------------Time:", now.hour, now.minute, now.second

        pieces = [x for x in url.split("/") if x is not '']

        #print "pieces"

        #print pieces

        part, rest = pieces[0], pieces[1:]

        request_type = None

        translation = self.translations[part]

        if self.rPacket.request_type == RadioPacket.REQUEST_TYPE_GET_REQUEST:
            request_type = "GET"

        if self.rPacket.request_type == RadioPacket.REQUEST_TYPE_POST_REQUEST:
            request_type = "POST"

        return self.processRESTRequest(rest, request_type, translation, part)

    def handleCloudVariable(self):
        namespaceHash = self.rPacket.get(0)
        variableNameHash = self.rPacket.get(1)
        value = self.rPacket.get(2)
        appId = self.rPacket.app_id

        self.cloud_ep.emit({
            "appId": appId,
            "uid": self.rPacket.uid,
            "namespace": namespaceHash,
            "variable_name": variableNameHash,
            "value": value
        })

        return self.returnPacket.marshall(True)

    def handleRequest(self):
        # check packet type in order to handle request correctly
        if self.rPacket.request_type & (RadioPacket.REQUEST_TYPE_GET_REQUEST |
                                        RadioPacket.REQUEST_TYPE_POST_REQUEST):
            return self.handleRESTRequest()

        if self.rPacket.request_type & (
                RadioPacket.REQUEST_TYPE_CLOUD_VARIABLE):
            return self.handleCloudVariable()
예제 #7
0
    if selected is None:
        raise Exception("No Bridge Detected")

#cloud_variable_ep = CloudVariableEp(hub_variables)

#this class can be used to poll an endpoint, I didn't find a real use for it.
# ep_poller = EndpointPoller(translations, hub_variables)

serial_handler = SerialHandler(selected)

# while true swap between polling EPs and receiving / sending.
while (True):

    # if the bridged micro:bit has sent us data, process
    if serial_handler.buffered() > 0:
        rPacket = RadioPacket(serial_handler.read_packet())
        translationsFile = open("./translations.json")
        translations = json.load(translationsFile)
        requestHandler = RequestHandler(rPacket, translations, hub_variables,
                                        None)
        bytes = requestHandler.handleRequest()
        serial_handler.write_packet(bytes)
#   else:
# otherwise check if our asynchronous socket io ep has packets to transmit.
#      bytes = cloud_variable_ep.drain()

#     if len(bytes) > 0:
#        serial_handler.write_packet(bytes)

# prevent burning the processor :)
    sleep(0.01)
예제 #8
0
 def error(self, originalPacket):
     returnPacket = RadioPacket(originalPacket)
     self.s.write(returnPacket.marshall(False))