def send_event(alexa_namespace, alexa_name, endpoint_id, token, payload):

        remove_endpoint = alexa_name is not "ChangeReport"
        alexa_response = AlexaResponse(namespace=alexa_namespace,
                                       name=alexa_name,
                                       endpoint_id=endpoint_id,
                                       token=token,
                                       remove_endpoint=remove_endpoint)
        alexa_response.set_payload(payload)
        payload = json.dumps(alexa_response.get())
        print('LOG api_handler_event.send_event.payload:')
        print(payload)

        # TODO Map to correct endpoint for Europe: https://api.eu.amazonalexa.com/v3/events
        # TODO Map to correct endpoint for Far East: https://api.fe.amazonalexa.com/v3/events
        alexa_event_gateway_uri = 'api.amazonalexa.com'
        connection = http.client.HTTPSConnection(alexa_event_gateway_uri)
        headers = {
            'Authorization': "Bearer " + token,
            'Content-Type': "application/json;charset=UTF-8",
            'Cache-Control': "no-cache"
        }
        connection.request('POST', '/v3/events', payload, headers)
        response = connection.getresponse()
        print('LOG api_handler_event.send_event HTTP Status code: ' +
              str(response.getcode()))
        return response
예제 #2
0
    def create(self, request):
        print("LOG event.create.request:", request)

        try:
            json_object = json.loads(request['body'])
            endpoint_user_id = json_object['event']['endpoint']['userId']  # Expect a Profile
            endpoint_name = json_object['event']['endpoint']['id']  # Expect a valid AWS IoT Thing Name
            endpoint_state = json_object['event']['endpoint']['state']  # Expect a state value, ex: ON or OFF
            sku = json_object['event']['endpoint']['sku']  # Expect a meaningful type, ex: SW00

            try:
                # Update the IoT Thing
                response = iot_aws.update_thing(
                    thingName=endpoint_name,
                    attributePayload={
                        'attributes': {
                            'state': endpoint_state,
                            'proactively_reported': 'True',
                            'user_id': endpoint_user_id
                        }
                    }
                )
                print('LOG event.create.iot_aws.update_thing.response:', str(response))

                # Update Alexa with a Proactive State Update
                if endpoint_user_id == 0:
                    print('LOG PSU: Not sent for user_id of 0')
                else:
                    response_psu = self.send_psu(endpoint_user_id, endpoint_name, endpoint_state)
                    print('LOG PSU response:', response_psu)

            except ClientError as e:
                alexa_response = AlexaResponse(name='ErrorResponse', message=e)
                alexa_response.set_payload(
                    {
                        'type': 'INTERNAL_ERROR',
                        'message': e
                    }
                )
                response = alexa_response.get()

            return response

        except KeyError as key_error:
            return "KeyError: " + str(key_error)
예제 #3
0
    def process(self, request, client_id, client_secret, redirect_uri):
        print('LOG api_handler_directive.process -----')
        # print(json.dumps(request))

        response = None
        # Process an Alexa directive and route to the right namespace
        # Only process if there is an actual body to process otherwise return an ErrorResponse
        json_body = request['body']
        if json_body:
            json_object = json.loads(json_body)
            namespace = json_object['directive']['header']['namespace']

            if namespace == "Alexa":
                name = json_object['directive']['header']['name']
                correlation_token = json_object['directive']['header'][
                    'correlationToken']
                token = json_object['directive']['endpoint']['scope']['token']
                endpoint_id = json_object['directive']['endpoint'][
                    'endpointId']

                if name == 'ReportState':
                    # Get the User ID from the access_token
                    response_user_id = json.loads(
                        ApiAuth.get_user_id(token).read().decode('utf-8'))
                    result = dynamodb_aws.get_item(
                        TableName='SampleEndpointDetails',
                        Key={'EndpointId': {
                            'S': endpoint_id
                        }})
                    capabilities_string = self.get_db_value(
                        result['Item']['Capabilities'])
                    capabilities = json.loads(capabilities_string)
                    props = []
                    for c in capabilities:
                        if not 'properties' in c:
                            continue
                        retrievable = c['properties'].get('retrievable', False)
                        if retrievable:
                            props.append(c)
                    state = {}
                    try:
                        res = iot_data_aws.get_thing_shadow(
                            thingName=endpoint_id)
                        shadow = json.loads(res['payload'].read())
                        state = shadow['state']['desired']
                    except ClientError as e:
                        print('LOG ', e)

                    print('Sending StateReport for', response_user_id,
                          'on endpoint', endpoint_id)
                    statereport_response = AlexaResponse(
                        name='StateReport',
                        endpoint_id=endpoint_id,
                        correlation_token=correlation_token,
                        token=token)

                    for p in props:
                        key = p['properties']['supported'][0]['name']
                        if 'instance' in p:
                            key = p['instance'] + '.' + key
                        current_state = state.get(key,
                                                  DEFAULT_VAL[p['interface']])
                        if 'instance' in p:
                            statereport_response.add_context_property(
                                namespace=p['interface'],
                                name=p['properties']['supported'][0]['name'],
                                value=current_state,
                                instance=p['instance'])
                        else:
                            statereport_response.add_context_property(
                                namespace=p['interface'],
                                name=p['properties']['supported'][0]['name'],
                                value=current_state)

                    response = statereport_response.get()

            if namespace == "Alexa.Authorization":
                grant_code = json_object['directive']['payload']['grant'][
                    'code']
                grantee_token = json_object['directive']['payload']['grantee'][
                    'token']

                # Spot the default from the Alexa.Discovery sample. Use as a default for development.
                if grantee_token == 'access-token-from-skill':
                    user_id = "0"  # <- Useful for development
                    response_object = {
                        'access_token': 'INVALID',
                        'refresh_token': 'INVALID',
                        'token_type': 'Bearer',
                        'expires_in': 9000
                    }
                else:
                    # Get the User ID
                    response_user_id = json.loads(
                        ApiAuth.get_user_id(grantee_token).read().decode(
                            'utf-8'))
                    if 'error' in response_user_id:
                        print(
                            'ERROR api_handler_directive.process.authorization.user_id:',
                            response_user_id['error_description'])
                        return AlexaResponse(name='ErrorResponse',
                                             payload={
                                                 'type': 'INTERNAL_ERROR',
                                                 'message': response_user_id
                                             })

                    user_id = response_user_id['user_id']
                    print(
                        'LOG api_handler_directive.process.authorization.user_id:',
                        user_id)

                # Get the Access and Refresh Tokens
                api_auth = ApiAuth()
                print('grant_code', grant_code, 'client_id', client_id,
                      'client_secret', client_secret, 'redirect_uri',
                      redirect_uri)
                response_token = api_auth.get_access_token(
                    grant_code, client_id, client_secret, redirect_uri)
                response_token_string = response_token.read().decode('utf-8')
                print(
                    'LOG api_handler_directive.process.authorization.response_token_string:',
                    response_token_string)
                response_object = json.loads(response_token_string)

                if 'error' in response_object:
                    return AlexaResponse(name='ErrorResponse',
                                         payload={
                                             'type': 'INTERNAL_ERROR',
                                             'response_object': response_object
                                         })

                # Store the retrieved from the Authorization Server
                access_token = response_object['access_token']
                refresh_token = response_object['refresh_token']
                token_type = response_object['token_type']
                expires_in = response_object['expires_in']

                # Calculate expiration
                expiration_utc = datetime.utcnow() + timedelta(
                    seconds=(int(expires_in) - 5))

                # Store the User Information - This is useful for inspection during development
                table = boto3.resource('dynamodb').Table('SampleUsers')
                result = table.put_item(
                    Item={
                        'UserId':
                        user_id,
                        'GrantCode':
                        grant_code,
                        'GranteeToken':
                        grantee_token,
                        'AccessToken':
                        access_token,
                        'ClientId':
                        client_id,
                        'ClientSecret':
                        client_secret,
                        'ExpirationUTC':
                        expiration_utc.strftime("%Y-%m-%dT%H:%M:%S.00Z"),
                        'RedirectUri':
                        redirect_uri,
                        'RefreshToken':
                        refresh_token,
                        'TokenType':
                        token_type
                    })

                if result['ResponseMetadata']['HTTPStatusCode'] == 200:
                    print(
                        'LOG api_handler_directive.process.authorization.SampleUsers.put_item:',
                        result)
                    alexa_accept_grant_response = AlexaResponse(
                        namespace='Alexa.Authorization',
                        name='AcceptGrant.Response')
                    response = alexa_accept_grant_response.get()
                else:
                    error_message = 'Error creating User'
                    print('ERR api_handler_directive.process.authorization',
                          error_message)
                    alexa_error_response = AlexaResponse(name='ErrorResponse')
                    alexa_error_response.set_payload({
                        'type': 'INTERNAL_ERROR',
                        'message': error_message
                    })
                    response = alexa_error_response.get()

            if namespace == "Alexa.Cooking":
                name = json_object['directive']['header']['name']
                correlation_token = json_object['directive']['header'][
                    'correlationToken']
                token = json_object['directive']['endpoint']['scope']['token']
                endpoint_id = json_object['directive']['endpoint'][
                    'endpointId']
                if name == "SetCookingMode":
                    alexa_error_response = AlexaResponse(
                        endpoint_id=endpoint_id,
                        correlation_token=correlation_token,
                        token=token)
                    response = alexa_error_response.get()

            if namespace == "Alexa.Discovery":
                # Given the Access Token, get the User ID
                access_token = json_object['directive']['payload']['scope'][
                    'token']

                # Spot the default from the Alexa.Discovery sample. Use as a default for development.
                if access_token == 'access-token-from-skill':
                    print(
                        'WARN api_handler_directive.process.discovery.user_id: Using development user_id of 0'
                    )
                    user_id = "0"  # <- Useful for development
                else:
                    response_user_id = json.loads(
                        ApiAuth.get_user_id(access_token).read().decode(
                            'utf-8'))
                    if 'error' in response_user_id:
                        print(
                            'ERROR api_handler_directive.process.discovery.user_id: '
                            + response_user_id['error_description'])
                    user_id = response_user_id['user_id']
                    print(
                        'LOG api_handler_directive.process.discovery.user_id:',
                        user_id)

                adr = AlexaResponse(namespace='Alexa.Discovery',
                                    name='Discover.Response')

                # Get the list of endpoints to return for a User ID and add them to the response
                # Use the AWS IoT entries for state but get the discovery details from DynamoDB
                # Wanted to list by group name but that requires a second lookup for the details
                # iot_aws.list_things_in_thing_group(thingGroupName="Samples")
                list_response = iot_aws.list_things()

                # Get a list of sample things by the user_id attribute
                for thing in list_response['things']:
                    if 'user_id' in thing['attributes']:
                        if thing['attributes']['user_id'] == user_id:
                            # We have an endpoint thing!
                            endpoint_details = ApiHandlerEndpoint.EndpointDetails(
                            )
                            endpoint_details.id = str(thing['thingName'])
                            print(
                                'LOG api_handler_directive.process.discovery: Found:',
                                endpoint_details.id, 'for user:'******'SampleEndpointDetails',
                                Key={'EndpointId': {
                                    'S': endpoint_details.id
                                }})
                            capabilities_string = self.get_db_value(
                                result['Item']['Capabilities'])
                            endpoint_details.capabilities = json.loads(
                                capabilities_string)
                            endpoint_details.description = self.get_db_value(
                                result['Item']['Description'])
                            endpoint_details.display_categories = json.loads(
                                self.get_db_value(
                                    result['Item']['DisplayCategories']))
                            endpoint_details.friendly_name = self.get_db_value(
                                result['Item']['FriendlyName'])
                            endpoint_details.manufacturer_name = self.get_db_value(
                                result['Item']['ManufacturerName'])
                            endpoint_details.sku = self.get_db_value(
                                result['Item']['SKU'])
                            endpoint_details.user_id = self.get_db_value(
                                result['Item']['UserId'])

                            adr.add_payload_endpoint(
                                friendly_name=endpoint_details.friendly_name,
                                endpoint_id=endpoint_details.id,
                                capabilities=endpoint_details.capabilities,
                                display_categories=endpoint_details.
                                display_categories,
                                manufacturer_name=endpoint_details.
                                manufacturer_name)

                response = adr.get()

            if namespace == "Alexa.PowerController":
                name = json_object['directive']['header']['name']
                correlation_token = None
                if 'correlationToken' in json_object['directive']['header']:
                    correlation_token = json_object['directive']['header'][
                        'correlationToken']
                token = json_object['directive']['endpoint']['scope']['token']
                endpoint_id = json_object['directive']['endpoint'][
                    'endpointId']

                response_user_id = json.loads(
                    ApiAuth.get_user_id(token).read().decode('utf-8'))
                if 'error' in response_user_id:
                    print(
                        'ERROR api_handler_directive.process.power_controller.user_id: '
                        + response_user_id['error_description'])
                user_id = response_user_id['user_id']
                print(
                    'LOG api_handler_directive.process.power_controller.user_id:',
                    user_id)

                # Convert to a local stored state
                power_state_value = 'OFF' if name == "TurnOff" else 'ON'
                msg = {'state': {'desired': {'powerState': 'ON'}}}

                msg['state']['desired']['powerState'] = power_state_value
                mqtt_msg = json.dumps(msg)
                # Send the state to the Thing Shadow
                try:
                    response_update = iot_data_aws.update_thing_shadow(
                        thingName=endpoint_id, payload=mqtt_msg.encode())
                    print(
                        'LOG api_handler_directive.process.power_controller.response_update -----'
                    )
                    print(response_update)
                    alexa_response = AlexaResponse(
                        token=token,
                        correlation_token=correlation_token,
                        endpoint_id=endpoint_id)
                    alexa_response.add_context_property(
                        namespace='Alexa.PowerController',
                        name='powerState',
                        value=power_state_value)
                    alexa_response.add_context_property()
                    response = alexa_response.get()

                except ClientError as e:
                    print(
                        'ERR api_handler_directive.process.power_controller Exception:ClientError:',
                        e)
                    response = AlexaResponse(name='ErrorResponse',
                                             message=e).get()

            if namespace == "Alexa.ModeController":
                alexa_error_response = AlexaResponse(name='ErrorResponse')
                alexa_error_response.set_payload({
                    'type':
                    'INTERNAL_ERROR',
                    'message':
                    'Not Yet Implemented'
                })
                response = alexa_error_response.get()

            if namespace == "Alexa.RangeController":
                name = json_object['directive']['header']['name']
                correlation_token = json_object['directive']['header'][
                    'correlationToken']
                instance = json_object['directive']['header']['instance']
                token = json_object['directive']['endpoint']['scope']['token']
                endpoint_id = json_object['directive']['endpoint'][
                    'endpointId']

                result = dynamodb_aws.get_item(
                    TableName='SampleEndpointDetails',
                    Key={'EndpointId': {
                        'S': endpoint_id
                    }})
                capabilities_string = self.get_db_value(
                    result['Item']['Capabilities'])
                capabilities = json.loads(capabilities_string)

                for c in capabilities:
                    if 'instance' in c and c['instance'] == instance:
                        MIN_VAL = c['configuration']['supportedRange'][
                            'minimumValue']
                        MAX_VAL = c['configuration']['supportedRange'][
                            'maximumValue']
                        PREC = c['configuration']['supportedRange'][
                            'precision']
                        break

                alexa_response = AlexaResponse(
                    endpoint_id=endpoint_id,
                    correlation_token=correlation_token,
                    token=token)
                value = 0
                if name == "AdjustRangeValue":
                    range_value_delta = json_object['directive']['payload'][
                        'rangeValueDelta']
                    range_value_delta_default = json_object['directive'][
                        'payload']['rangeValueDeltaDefault']
                    reported_range_value = 0

                    # Check to see if we need to use the delta default value (The user did not give a precision)
                    if range_value_delta_default:
                        range_value_delta = PREC

                    # Lookup the existing value of the endpoint by endpoint_id and limit ranges as appropriate - for this sample, expecting 1-6
                    try:
                        response = iot_data_aws.get_thing_shadow(
                            thingName=endpoint_id)
                        payload = json.loads(response['payload'].read())
                        reported_range_value = payload['state']['reported'][
                            instance + '.rangeValue']
                        print(
                            'LOG api_handler_directive.process.range_controller.range_value:',
                            reported_range_value)
                    except ClientError as e:
                        print(e)
                    except KeyError as errorKey:
                        print('Could not find key:', errorKey)

                    new_range_value = reported_range_value + range_value_delta

                    value = max(min(new_range_value, MAX_VAL), MIN_VAL)

                if name == "SetRangeValue":
                    range_value = json_object['directive']['payload'][
                        'rangeValue']

                    value = max(min(range_value, MAX_VAL), MIN_VAL)
                    alexa_response.add_context_property(
                        namespace='Alexa.RangeController',
                        name='rangeValue',
                        value=value)

                # Update the Thing Shadow
                msg = {'state': {'desired': {}}}
                # NOTE: The instance is used to keep the stored value unique
                msg['state']['desired'][instance + '.rangeValue'] = value
                mqtt_msg = json.dumps(msg)
                response_update = iot_data_aws.update_thing_shadow(
                    thingName=endpoint_id, payload=mqtt_msg.encode())
                print(
                    'LOG api_handler_directive.process.range_controller.response_update -----'
                )
                print(response_update)

                # Send back the response
                response = alexa_response.get()

            if namespace == "Alexa.ToggleController":
                name = json_object['directive']['header']['name']
                correlation_token = json_object['directive']['header'][
                    'correlationToken']
                instance = json_object['directive']['header']['instance']
                token = json_object['directive']['endpoint']['scope']['token']
                endpoint_id = json_object['directive']['endpoint'][
                    'endpointId']

                # Convert to a local stored state
                toggle_state_value = 'OFF' if name == "TurnOff" else 'ON'
                state_name = instance + '.state'
                msg = {'state': {'desired': {state_name: 'ON'}}}
                msg['state']['desired'][state_name] = toggle_state_value
                mqtt_msg = json.dumps(msg)
                # Send the state to the Thing Shadow
                try:
                    response_update = iot_data_aws.update_thing_shadow(
                        thingName=endpoint_id, payload=mqtt_msg.encode())
                    print(
                        'LOG api_handler_directive.process.toggle_controller.response_update -----'
                    )
                    print(response_update)
                    alexa_response = AlexaResponse(
                        token=token,
                        correlation_token=correlation_token,
                        endpoint_id=endpoint_id)
                    alexa_response.add_context_property(
                        namespace='Alexa.ToggleController',
                        name='toggleState',
                        instance=instance,
                        value=toggle_state_value)
                    alexa_response.add_context_property()
                    response = alexa_response.get()

                except ClientError as e:
                    print(
                        'ERR api_handler_directive.process.toggle_controller Exception:ClientError:',
                        e)
                    response = AlexaResponse(name='ErrorResponse',
                                             message=e).get()

        else:
            alexa_error_response = AlexaResponse(name='ErrorResponse')
            alexa_error_response.set_payload({
                'type': 'INTERNAL_ERROR',
                'message': 'Empty Body'
            })
            response = alexa_error_response.get()

        if response is None:
            # response set to None indicates an unhandled directive, review the logs
            alexa_error_response = AlexaResponse(name='ErrorResponse')
            alexa_error_response.set_payload({
                'type':
                'INTERNAL_ERROR',
                'message':
                'Empty Response: No response processed. Unhandled Directive.'
            })
            response = alexa_error_response.get()

        print('LOG api_handler_directive.process.response -----')
        print(json.dumps(response))
        return response
    def process(self, request, client_id, client_secret, redirect_uri):
        print('LOG directive.process.request:', request)

        response = None
        # Process an Alexa directive and route to the right namespace
        # Only process if there is an actual body to process otherwise return an ErrorResponse
        json_body = request['body']
        if json_body:
            json_object = json.loads(json_body)
            namespace = json_object['directive']['header']['namespace']

            if namespace == "Alexa":
                name = json_object['directive']['header']['name']
                correlation_token = json_object['directive']['header'][
                    'correlationToken']
                endpoint_id = json_object['directive']['endpoint'][
                    'endpointId']
                if name == 'ReportState':
                    # TODO Get the User ID from the access_token
                    # TODO Lookup the endpoint and get state
                    print('Sending StateReport for endpoint', endpoint_id)
                    alexa_reportstate_response = AlexaResponse(
                        name='StateReport',
                        endpoint_id=endpoint_id,
                        correlation_token=correlation_token)
                    response = alexa_reportstate_response.get()

            if namespace == "Alexa.Authorization":
                grant_code = json_object['directive']['payload']['grant'][
                    'code']
                grantee_token = json_object['directive']['payload']['grantee'][
                    'token']

                # Spot the default from the Alexa.Discovery sample. Use as a default for development.
                if grantee_token == 'access-token-from-skill':
                    user_id = "0"  # <- Useful for development
                    response_object = {
                        'access_token': 'INVALID',
                        'refresh_token': 'INVALID',
                        'token_type': 'Bearer',
                        'expires_in': 9000
                    }
                else:
                    # Get the User ID
                    response_user_id = json.loads(
                        ApiAuth.get_user_id(grantee_token).read().decode(
                            'utf-8'))
                    if 'error' in response_user_id:
                        print('ERROR directive.process.authorization.user_id:',
                              response_user_id['error_description'])
                    user_id = response_user_id['user_id']
                    print('LOG directive.process.authorization.user_id:',
                          user_id)

                    # Get the Access and Refresh Tokens
                    api_auth = ApiAuth()
                    response_token = api_auth.get_access_token(
                        grant_code, client_id, client_secret, redirect_uri)
                    response_token_string = response_token.read().decode(
                        'utf-8')
                    print(
                        'LOG directive.process.authorization.response_token_string:',
                        response_token_string)
                    response_object = json.loads(response_token_string)

                # Store the retrieved from the Authorization Server
                access_token = response_object['access_token']
                refresh_token = response_object['refresh_token']
                token_type = response_object['token_type']
                expires_in = response_object['expires_in']

                # Calculate expiration
                expiration_utc = datetime.utcnow() + timedelta(
                    seconds=(int(expires_in) - 5))

                # Store the User Information - This is useful for inspection during development
                table = boto3.resource('dynamodb').Table('SampleUsers')
                result = table.put_item(
                    Item={
                        'UserId':
                        user_id,
                        'GrantCode':
                        grant_code,
                        'GranteeToken':
                        grantee_token,
                        'AccessToken':
                        access_token,
                        'ClientId':
                        client_id,
                        'ClientSecret':
                        client_secret,
                        'ExpirationUTC':
                        expiration_utc.strftime("%Y-%m-%dT%H:%M:%S.00Z"),
                        'RedirectUri':
                        redirect_uri,
                        'RefreshToken':
                        refresh_token,
                        'TokenType':
                        token_type
                    })

                if result['ResponseMetadata']['HTTPStatusCode'] == 200:
                    print(
                        'LOG directive.process.authorization.SampleUsers.put_item:',
                        result)
                    alexa_accept_grant_response = AlexaResponse(
                        namespace='Alexa.Authorization',
                        name='AcceptGrant.Response')
                    response = alexa_accept_grant_response.get()

                else:
                    error_message = 'Error creating User'
                    print('ERR directive.process.authorization', error_message)
                    alexa_error_response = AlexaResponse(name='ErrorResponse')
                    alexa_error_response.set_payload({
                        'type': 'INTERNAL_ERROR',
                        'message': error_message
                    })
                    response = alexa_error_response.get()

            if namespace == "Alexa.Cooking":
                name = json_object['directive']['header']['name']
                correlation_token = json_object['directive']['header'][
                    'correlationToken']
                token = json_object['directive']['endpoint']['scope']['token']
                endpoint_id = json_object['directive']['endpoint'][
                    'endpointId']
                if name == "SetCookingMode":
                    alexa_error_response = AlexaResponse(
                        endpoint_id=endpoint_id,
                        correlation_token=correlation_token,
                        token=token)
                    response = alexa_error_response.get()

            if namespace == "Alexa.Discovery":
                # Given the Access Token, get the User ID
                access_token = json_object['directive']['payload']['scope'][
                    'token']

                # Spot the default from the Alexa.Discovery sample. Use as a default for development.
                if access_token == 'access-token-from-skill':
                    print(
                        'WARN directive.process.discovery.user_id: Using development user_id of 0'
                    )
                    user_id = "0"  # <- Useful for development
                else:
                    response_user_id = json.loads(
                        ApiAuth.get_user_id(access_token).read().decode(
                            'utf-8'))
                    if 'error' in response_user_id:
                        print('ERROR directive.process.discovery.user_id: ' +
                              response_user_id['error_description'])
                    user_id = response_user_id['user_id']
                    print('LOG directive.process.discovery.user_id:', user_id)

                alexa_discover_response = AlexaDiscoverResponse(json_object)

                # Get the list of endpoints to return for a User ID and add them to the response
                # Use the AWS IoT entries for state but get the discovery details from DynamoDB
                # HACK Boto3 1.4.8 attributeName and attributeValue stopped working, raw list_things() works however
                # Not Working: list_response = iot_aws.list_things(attributeName='user_id', attributeValue=user_id)
                list_response = iot_aws.list_things()
                for thing in list_response['things']:
                    if thing['attributes']['user_id'] == user_id:
                        endpoint_details = ApiHandlerEndpoint.EndpointDetails()
                        endpoint_details.id = str(
                            thing['thingName']
                        )  # Add attribute endpoint_id to free thingName?
                        print('LOG directive.process.discovery: Found:',
                              endpoint_details.id, 'for user:'******'SampleEndpointDetails',
                            Key={'EndpointId': {
                                'S': endpoint_details.id
                            }})
                        capabilities_string = self.get_db_value(
                            result['Item']['Capabilities'])
                        endpoint_details.capabilities = json.loads(
                            capabilities_string)
                        endpoint_details.description = self.get_db_value(
                            result['Item']['Description'])
                        endpoint_details.display_categories = json.loads(
                            self.get_db_value(
                                result['Item']['DisplayCategories']))
                        endpoint_details.friendly_name = self.get_db_value(
                            result['Item']['FriendlyName'])
                        endpoint_details.manufacturer_name = self.get_db_value(
                            result['Item']['ManufacturerName'])
                        endpoint_details.sku = self.get_db_value(
                            result['Item']['SKU'])
                        endpoint_details.user_id = self.get_db_value(
                            result['Item']['UserId'])
                        alexa_discover_response.add_endpoint(endpoint_details)

                response = alexa_discover_response.get_response()

            if namespace == "Alexa.PowerController":
                name = json_object['directive']['header']['name']
                correlation_token = None
                if 'correlationToken' in json_object['directive']['header']:
                    correlation_token = json_object['directive']['header'][
                        'correlationToken']
                token = json_object['directive']['endpoint']['scope']['token']
                endpoint_id = json_object['directive']['endpoint'][
                    'endpointId']

                response_user_id = json.loads(
                    ApiAuth.get_user_id(token).read().decode('utf-8'))
                if 'error' in response_user_id:
                    print(
                        'ERROR directive.process.power_controller.user_id: ' +
                        response_user_id['error_description'])
                user_id = response_user_id['user_id']
                print('LOG directive.process.power_controller.user_id',
                      user_id)

                # Convert to a local stored state
                power_state_value = 'OFF' if name == "TurnOff" else 'ON'
                try:
                    # Send the state to the Thing
                    response_update = iot_aws.update_thing(
                        thingName=endpoint_id,
                        attributePayload={
                            'attributes': {
                                'state': power_state_value,
                                'user_id': user_id
                            }
                        })
                    print(
                        'LOG directive.process.power_controller.response_update:',
                        response_update)
                    alexa_power_controller_response = AlexaResponse(
                        namespace='Alexa.PowerController',
                        name=name,
                        token=token,
                        correlation_token=correlation_token,
                        endpoint_id=endpoint_id)
                    response = alexa_power_controller_response.get()
                except ClientError as e:
                    response = AlexaResponse(name='ErrorResponse',
                                             message=e).get()

        else:
            response = AlexaResponse(name='ErrorResponse').get()

        if response is None:
            # response set to None indicates an unhandled directive, review the logs
            response = AlexaResponse(
                name='ErrorResponse',
                message='Empty Response: No response processed').get()
        # else:
        # TODO Validate the Response once the schema is updated
        # if not self.validate_response(response):
        #     response = AlexaError(message='Failed to validate message against the schema').get_response()

        # print('LOG directive.process.response', response)
        return json.dumps(response)
예제 #5
0
    def send_psu(self, endpoint_user_id, endpoint_id, endpoint_state):

        # Get the User Information
        table = boto3.resource('dynamodb').Table('SampleUsers')
        result = table.get_item(
            Key={
                'UserId': endpoint_user_id
            },
            AttributesToGet=[
                'UserId',
                'AccessToken',
                'ClientId',
                'ClientSecret',
                'ExpirationUTC',
                'RedirectUri',
                'RefreshToken',
                'TokenType'
            ]
        )

        if result['ResponseMetadata']['HTTPStatusCode'] == 200:
            print('LOG event.create.send_psu.SampleUsers.get_item:', str(result))

            if 'Item' in result:
                expiration_utc = result['Item']['ExpirationUTC']
                token_is_expired = self.is_token_expired(expiration_utc)
                print('LOG event.create.send_psu.token_is_expired:', token_is_expired)
                if token_is_expired:
                    # The token has expired so get a new access token using the refresh token
                    refresh_token = result['Item']['RefreshToken']
                    client_id = result['Item']['ClientId']
                    client_secret = result['Item']['ClientSecret']
                    redirect_uri = result['Item']['RedirectUri']

                    api_auth = ApiAuth()
                    response_refresh_token = api_auth.refresh_access_token(refresh_token, client_id, client_secret,
                                                                           redirect_uri)
                    response_refresh_token_string = response_refresh_token.read().decode('utf-8')
                    response_refresh_token_object = json.loads(response_refresh_token_string)

                    # Store the new values from the refresh
                    access_token = response_refresh_token_object['access_token']
                    refresh_token = response_refresh_token_object['refresh_token']
                    token_type = response_refresh_token_object['token_type']
                    expires_in = response_refresh_token_object['expires_in']

                    # Calculate expiration
                    expiration_utc = datetime.utcnow() + timedelta(seconds=(int(expires_in) - 5))

                    print('access_token', access_token)
                    print('expiration_utc', expiration_utc)

                    result = table.update_item(
                        Key={
                            'UserId': endpoint_user_id
                        },
                        UpdateExpression="set AccessToken=:a, RefreshToken=:r, TokenType=:t, ExpirationUTC=:e",
                        ExpressionAttributeValues={
                            ':a': access_token,
                            ':r': refresh_token,
                            ':t': token_type,
                            ':e': expiration_utc.strftime("%Y-%m-%dT%H:%M:%S.00Z")
                        },
                        ReturnValues="UPDATED_NEW"
                    )
                    print('LOG event.create.send_psu.SampleUsers.update_item:', str(result))

                    # TODO Return an error here if the token could not be refreshed
                else:
                    # Use the stored access token
                    access_token = result['Item']['AccessToken']
                    print('LOG Using stored access_token:', access_token)

                alexa_changereport_response = AlexaResponse(name='ChangeReport', endpoint_id=endpoint_id, token=access_token)
                alexa_changereport_response.set_payload(
                    {
                        'change': {
                            'cause': {
                                'type': 'PHYSICAL_INTERACTION'
                            },
                            "properties": [
                                alexa_changereport_response.create_property(namespace='Alexa.PowerController', name='powerState', value='ON')
                            ]
                        }
                    }
                )
                payload = json.dumps(alexa_changereport_response.get())
                print('LOG AlexaChangeReport.get_response:', payload)

                # TODO Map to correct endpoint for Europe: https://api.eu.amazonalexa.com/v3/events
                # TODO Map to correct endpoint for Far East: https://api.fe.amazonalexa.com/v3/events
                alexa_event_gateway_uri = 'api.amazonalexa.com'
                connection = http.client.HTTPSConnection(alexa_event_gateway_uri)
                headers = {
                    'content-type': "application/json;charset=UTF-8",
                    'cache-control': "no-cache"
                }
                connection.request('POST', '/v3/events', payload, headers)
                code = connection.getresponse().getcode()
                return 'LOG PSU HTTP Status code: ' + str(code)
예제 #6
0
    def process(self, request, client_id, client_secret):
        print('LOG api_handler_directive.process -----')
        # print(json.dumps(request))

        response = None
        # Process an Alexa directive and route to the right namespace
        # Only process if there is an actual body to process otherwise return an ErrorResponse
        json_body = request['body']
        if json_body:
            json_object = json.loads(json_body)
            namespace = json_object['directive']['header']['namespace']

            if namespace == "Alexa":
                name = json_object['directive']['header']['name']
                correlation_token = json_object['directive']['header'][
                    'correlationToken']
                token = json_object['directive']['endpoint']['scope']['token']
                endpoint_id = json_object['directive']['endpoint'][
                    'endpointId']

                if name == 'ReportState':
                    # Get the User ID from the access_token
                    response_user_id = json.loads(
                        ApiAuth.get_user_id(token).read().decode('utf-8'))
                    result = dynamodb_aws.get_item(
                        TableName='APISensorEndpointDetails',
                        Key={'EndpointId': {
                            'S': endpoint_id
                        }})
                    capabilities_string = self.get_db_value(
                        result['Item']['Capabilities'])
                    capabilities = json.loads(capabilities_string)
                    props = []
                    for c in capabilities:
                        if not 'properties' in c:
                            continue
                        retrievable = c['properties'].get('retrievable', False)
                        if retrievable:
                            props.append(c)

                    print('Sending StateReport for', response_user_id,
                          'on endpoint', endpoint_id)
                    statereport_response = AlexaResponse(
                        name='StateReport',
                        endpoint_id=endpoint_id,
                        correlation_token=correlation_token,
                        token=token)

                    for p in props:
                        statereport_response.add_context_property(
                            namespace=p['interface'],
                            name=p['properties']['supported'][0]['name'],
                            value=DEFAULT_VAL[p['interface']])

                    response = statereport_response.get()

            if namespace == "Alexa.Authorization":
                grant_code = json_object['directive']['payload']['grant'][
                    'code']
                grantee_token = json_object['directive']['payload']['grantee'][
                    'token']

                # Spot the default from the Alexa.Discovery sample. Use as a default for development.
                if grantee_token == 'access-token-from-skill':
                    user_id = "0"  # <- Useful for development
                    response_object = {
                        'access_token': 'INVALID',
                        'refresh_token': 'INVALID',
                        'token_type': 'Bearer',
                        'expires_in': 9000
                    }
                else:
                    # Get the User ID
                    response_user_id = json.loads(
                        ApiAuth.get_user_id(grantee_token).read().decode(
                            'utf-8'))
                    if 'error' in response_user_id:
                        print(
                            'ERROR api_handler_directive.process.authorization.user_id:',
                            response_user_id['error_description'])
                        return AlexaResponse(name='ErrorResponse',
                                             payload={
                                                 'type': 'INTERNAL_ERROR',
                                                 'message': response_user_id
                                             })

                    user_id = response_user_id['user_id']
                    print(
                        'LOG api_handler_directive.process.authorization.user_id:',
                        user_id)

                # Get the Access and Refresh Tokens
                api_auth = ApiAuth()
                print('grant_code', grant_code, 'client_id', client_id,
                      'client_secret', client_secret)
                response_token = api_auth.get_access_token(
                    grant_code, client_id, client_secret)
                response_token_string = response_token.read().decode('utf-8')
                print(
                    'LOG api_handler_directive.process.authorization.response_token_string:',
                    response_token_string)
                response_object = json.loads(response_token_string)

                if 'error' in response_object:
                    return AlexaResponse(name='ErrorResponse',
                                         payload={
                                             'type': 'INTERNAL_ERROR',
                                             'response_object': response_object
                                         })

                # Store the retrieved from the Authorization Server
                access_token = response_object['access_token']
                refresh_token = response_object['refresh_token']
                token_type = response_object['token_type']
                expires_in = response_object['expires_in']

                # Calculate expiration
                expiration_utc = datetime.utcnow() + timedelta(
                    seconds=(int(expires_in) - 5))

                # Store the User Information - This is useful for inspection during development
                table = boto3.resource('dynamodb').Table('APISensorUsers')
                result = table.put_item(
                    Item={
                        'UserId':
                        user_id,
                        'GrantCode':
                        grant_code,
                        'GranteeToken':
                        grantee_token,
                        'AccessToken':
                        access_token,
                        'ClientId':
                        client_id,
                        'ClientSecret':
                        client_secret,
                        'ExpirationUTC':
                        expiration_utc.strftime("%Y-%m-%dT%H:%M:%S.00Z"),
                        'RefreshToken':
                        refresh_token,
                        'TokenType':
                        token_type
                    })

                if result['ResponseMetadata']['HTTPStatusCode'] == 200:
                    print(
                        'LOG api_handler_directive.process.authorization.SampleUsers.put_item:',
                        result)
                    alexa_accept_grant_response = AlexaResponse(
                        namespace='Alexa.Authorization',
                        name='AcceptGrant.Response')
                    response = alexa_accept_grant_response.get()
                else:
                    error_message = 'Error creating User'
                    print('ERR api_handler_directive.process.authorization',
                          error_message)
                    alexa_error_response = AlexaResponse(name='ErrorResponse')
                    alexa_error_response.set_payload({
                        'type': 'INTERNAL_ERROR',
                        'message': error_message
                    })
                    response = alexa_error_response.get()

            if namespace == "Alexa.Discovery":
                # Given the Access Token, get the User ID
                access_token = json_object['directive']['payload']['scope'][
                    'token']

                # Spot the default from the Alexa.Discovery sample. Use as a default for development.
                if access_token == 'access-token-from-skill':
                    print(
                        'WARN api_handler_directive.process.discovery.user_id: Using development user_id of 0'
                    )
                    user_id = "0"  # <- Useful for development
                else:
                    response_user_id = json.loads(
                        ApiAuth.get_user_id(access_token).read().decode(
                            'utf-8'))
                    if 'error' in response_user_id:
                        print(
                            'ERROR api_handler_directive.process.discovery.user_id: '
                            + response_user_id['error_description'])
                    user_id = response_user_id['user_id']
                    print(
                        'LOG api_handler_directive.process.discovery.user_id:',
                        user_id)

                adr = AlexaResponse(namespace='Alexa.Discovery',
                                    name='Discover.Response')

                # Get the list of endpoints associated with the user
                table = boto3.resource('dynamodb').Table(
                    'APISensorEndpointDetails')
                result = table.scan(FilterExpression=boto3.dynamodb.conditions.
                                    Attr('UserId').eq(user_id))

                for endpoint_details in result['Items']:

                    # We have an endpoint
                    print(
                        'LOG api_handler_directive.process.discovery: Found:',
                        endpoint_details['EndpointId'], 'for user:'******'FriendlyName'],
                        endpoint_id=endpoint_details['EndpointId'],
                        capabilities=json.loads(
                            endpoint_details['Capabilities']),
                        display_categories=json.loads(
                            endpoint_details['DisplayCategories']),
                        manufacturer_name=endpoint_details['ManufacturerName'])

                response = adr.get()

        else:
            alexa_error_response = AlexaResponse(name='ErrorResponse')
            alexa_error_response.set_payload({
                'type': 'INTERNAL_ERROR',
                'message': 'Empty Body'
            })
            response = alexa_error_response.get()

        if response is None:
            # response set to None indicates an unhandled directive, review the logs
            alexa_error_response = AlexaResponse(name='ErrorResponse')
            alexa_error_response.set_payload({
                'type':
                'INTERNAL_ERROR',
                'message':
                'Empty Response: No response processed. Unhandled Directive.'
            })
            response = alexa_error_response.get()

        print('LOG api_handler_directive.process.response -----')
        print(json.dumps(response))
        return response