def handle_discovery(request): print('- Discovery initiated!') token = request['directive']['payload']['scope']['token'] intof_id = umap.token_intof_map(token) if (intof_id == None): response = get_error_response( 'REGISTRATION_ERROR', 'Unable to get the Intof user id.' ) # INVALID_AUTHORIZATION_CREDENTIAL / EXPIRD #return send_response (response.get()) return response devices = device.get_devices(intof_id) if (devices == None): response = get_error_response( 'DISCOVERY_ERROR', 'Unable to get your registered devices.') if len(devices) == 0: response = get_error_response( 'DISCOVERY_ERROR', 'You do not have any registered devices.') return send_response(response.get()) response = AlexaResponse(namespace='Alexa.Discovery', name='Discover.Response') # create a basic alexa capability... capability_alexa = response.create_payload_endpoint_capability() # .. and a more specific capability capability_powercontroller = response.create_payload_endpoint_capability( interface='Alexa.PowerController', supported=[{ 'name': 'powerState' }]) for i in range(len(devices)): response.add_payload_endpoint( friendly_name=devices[i][1], endpoint_id=devices[i][0], capabilities=[capability_alexa, capability_powercontroller]) return response
def test_discovery(self): adr = AlexaResponse(namespace="Alexa.Discovery", name="Discover.Response") capability_alexa = adr.create_payload_endpoint_capability() capability_alexa_powercontroller = adr.create_payload_endpoint_capability( interface="Alexa.PowerController", supported=[{"name": "powerState"}]) adr.add_payload_endpoint(capabilities=[capability_alexa, capability_alexa_powercontroller]) response = adr.get() self.assertEqual(response['event']['header']['namespace'], 'Alexa.Discovery') self.assertEqual(response['event']['header']['name'], 'Discover.Response') self.assertEqual(response['event']['payload']['endpoints'][0]['friendlyName'], 'Sample Endpoint') self.assertEqual(response['event']['payload']['endpoints'][0]['capabilities'][0]['type'], 'AlexaInterface')
def discover_devices(request): """ Discover the declared devices this handler supports """ command = request['directive']['header']['name'] if command == 'Discover': adr = AlexaResponse(namespace='Alexa.Discovery', name='Discover.Response') capability_alexa = adr.create_payload_endpoint_capability() capability_alexa_powercontroller = adr.create_payload_endpoint_capability( interface='Alexa.PowerController', supported=[{ 'name': 'powerState' }]) for device in _DEVICES: adr.add_payload_endpoint(capabilities=[ capability_alexa, capability_alexa_powercontroller ], **device) return success(adr.get())
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 lambda_handler(request, context): # Dump the request for logging - check the CloudWatch logs print('lambda_handler request -----') print(json.dumps(request)) if context is not None: print('lambda_handler context -----') print(context) # Validate we have an Alexa directive if 'directive' not in request: aer = AlexaResponse( name='ErrorResponse', payload={ 'type': 'INVALID_DIRECTIVE', 'message': 'Missing key: directive, Is the request a valid Alexa Directive?' }) return send_response(aer.get()) # Check the payload version payload_version = request['directive']['header']['payloadVersion'] if payload_version != '3': aer = AlexaResponse( name='ErrorResponse', payload={ 'type': 'INTERNAL_ERROR', 'message': 'This skill only supports Smart Home API version 3' }) return send_response(aer.get()) # Crack open the request and see what is being requested name = request['directive']['header']['name'] namespace = request['directive']['header']['namespace'] print('Name:' + name) print('Namespace:' + namespace) # Handle the incoming request from Alexa based on the namespace if namespace == 'Alexa.Authorization': if name == 'AcceptGrant': # Note: This sample accepts any grant request # In your implementation you would use the code and token to get and store access tokens grant_code = request['directive']['payload']['grant']['code'] grantee_token = request['directive']['payload']['grantee']['token'] aar = AlexaResponse(namespace='Alexa.Authorization', name='AcceptGrant.Response') return send_response(aar.get()) if namespace == 'Alexa.Discovery': if name == 'Discover': adr = AlexaResponse(namespace='Alexa.Discovery', name='Discover.Response') capability_alexa = adr.create_payload_endpoint_capability() capability_alexa_powercontroller = adr.create_payload_endpoint_capability( interface='Alexa.PowerController', supported=[{ 'name': 'powerState' }]) capability_alexa_temperaturesensor = adr.create_payload_endpoint_capability( interface='Alexa.TemperatureSensor', supported=[{ 'name': 'temperature' }], retrievable='true', proactively_reported='true', ) capability_alexa_endpointhealth = adr.create_payload_endpoint_capability( interface='Alexa.EndpointHealth', supported=[{ 'name': 'connectivity' }], proactively_reported='true', retrievable='true') # adr.add_payload_endpoint( # friendly_name='Termostato Sala', # endpoint_id='temperaturesensor-001', # description='Termostato Sala', # display_categories=["TEMPERATURE_SENSOR"], # manufacturer_name = 'myself manufacter', # capabilities=[capability_alexa, capability_alexa_temperaturesensor, capability_alexa_endpointhealth ]) # adr.add_payload_endpoint( # friendly_name='Termostato Cucina', # endpoint_id='temperaturesensor-002', # description='Termostato Cucina', # display_categories=["TEMPERATURE_SENSOR"], # manufacturer_name = 'myself manufacter', # capabilities=[capability_alexa, capability_alexa_temperaturesensor, capability_alexa_endpointhealth ]) adr.add_payload_endpoint(friendly_name='Luce 01', endpoint_id='light-001', description='Luce 01', display_categories=["LIGHT"], manufacturer_name='myself manufacter', capabilities=[ capability_alexa, capability_alexa_powercontroller ]) adr.add_payload_endpoint(friendly_name='Luce 02', endpoint_id='light-002', description='Luce 02', display_categories=["LIGHT"], manufacturer_name='myself manufacter', capabilities=[ capability_alexa, capability_alexa_powercontroller ]) adr.add_payload_endpoint(friendly_name='Luce 03', endpoint_id='light-003', description='Luce 03', display_categories=["LIGHT"], manufacturer_name='myself manufacter', capabilities=[ capability_alexa, capability_alexa_powercontroller ]) adr.add_payload_endpoint(friendly_name='Luce 04', endpoint_id='light-004', description='Luce 04', display_categories=["LIGHT"], manufacturer_name='myself manufacter', capabilities=[ capability_alexa, capability_alexa_powercontroller ]) adr.add_payload_endpoint(friendly_name='Luce 05', endpoint_id='light-005', description='Luce 05', display_categories=["LIGHT"], manufacturer_name='myself manufacter', capabilities=[ capability_alexa, capability_alexa_powercontroller ]) adr.add_payload_endpoint(friendly_name='Luce 06', endpoint_id='light-006', description='Luce 06', display_categories=["LIGHT"], manufacturer_name='myself manufacter', capabilities=[ capability_alexa, capability_alexa_powercontroller ]) adr.add_payload_endpoint(friendly_name='Luce 07', endpoint_id='light-007', description='Luce 07', display_categories=["LIGHT"], manufacturer_name='myself manufacter', capabilities=[ capability_alexa, capability_alexa_powercontroller ]) adr.add_payload_endpoint(friendly_name='Luce 08', endpoint_id='light-008', description='Luce 08', display_categories=["LIGHT"], manufacturer_name='myself manufacter', capabilities=[ capability_alexa, capability_alexa_powercontroller ]) adr.add_payload_endpoint(friendly_name='Cancellino', endpoint_id='gate-001', description='Cancellino', display_categories=["DOOR"], manufacturer_name='myself manufacter', capabilities=[ capability_alexa, capability_alexa_powercontroller ]) return send_response(adr.get()) if namespace == 'Alexa': if name == 'ReportState': print('Richiesta tipo ReportState -----') # Note: This sample always returns a success response for either a request to TurnOff or TurnOn endpoint_id = request['directive']['endpoint']['endpointId'] print('ENDPOINT ID: ' + endpoint_id) correlation_token = request['directive']['header'][ 'correlationToken'] token = request['directive']['endpoint']['scope']['token'] print('Correlation_Token: ' + correlation_token) print('Token: ' + token) apcr = AlexaResponse(name='StateReport', endpoint_id=endpoint_id, correlation_token=correlation_token, token=token) if (endpoint_id == 'temperaturesensor-001'): apcr.add_context_property(namespace='Alexa.TemperatureSensor', name='temperature', value={ 'value': '19.9', 'scale': 'CELSIUS' }) elif (endpoint_id == 'temperaturesensor-002'): apcr.add_context_property(namespace='Alexa.TemperatureSensor', name='temperature', value={ 'value': '22.3', 'scale': 'CELSIUS' }) return send_response(apcr.get()) if namespace == 'Alexa.PowerController' or namespace == 'Alexa.ToogleController': print('Richiesta tipo PowerController -----') # Note: This sample always returns a success response for either a request to TurnOff or TurnOn endpoint_id = request['directive']['endpoint']['endpointId'] power_state_value = 'OFF' if name == 'TurnOff' else 'ON' correlation_token = request['directive']['header']['correlationToken'] # Check for an error when setting the state state_set = set_device_state(endpoint_id=endpoint_id, state='powerState', value=power_state_value) if not state_set: return AlexaResponse(name='ErrorResponse', payload={ 'type': 'ENDPOINT_UNREACHABLE', 'message': 'Unable to reach endpoint database.' }).get() apcr = AlexaResponse(correlation_token=correlation_token) apcr.add_context_property(namespace='Alexa.PowerController', name='powerState', value=power_state_value) return send_response(apcr.get())
def lambda_handler(request, context): # Dump the request for logging - check the CloudWatch logs print('lambda_handler request -----') print(json.dumps(request)) if context is not None: print('lambda_handler context -----') print(context) # Validate we have an Alexa directive if 'directive' not in request: aer = AlexaResponse( name='ErrorResponse', payload={'type': 'INVALID_DIRECTIVE', 'message': 'Missing key: directive, Is the request a valid Alexa Directive?'}) return send_response(aer.get()) # Check the payload version payload_version = request['directive']['header']['payloadVersion'] if payload_version != '3': aer = AlexaResponse( name='ErrorResponse', payload={'type': 'INTERNAL_ERROR', 'message': 'This skill only supports Smart Home API version 3'}) return send_response(aer.get()) # Crack open the request and see what is being requested name = request['directive']['header']['name'] namespace = request['directive']['header']['namespace'] # Handle the incoming request from Alexa based on the namespace if namespace == 'Alexa.Authorization': if name == 'AcceptGrant': # Note: This sample accepts any grant request # In your implementation you would use the code and token to get and store access tokens grant_code = request['directive']['payload']['grant']['code'] grantee_token = request['directive']['payload']['grantee']['token'] aar = AlexaResponse(namespace='Alexa.Authorization', name='AcceptGrant.Response') return send_response(aar.get()) if namespace == 'Alexa.Discovery': if name == 'Discover': adr = AlexaResponse(namespace='Alexa.Discovery', name='Discover.Response') capability_alexa = adr.create_payload_endpoint_capability() capability_alexa_powercontroller = adr.create_payload_endpoint_capability( interface='Alexa.PowerController', supported=[{'name': 'powerState'}]) adr.add_payload_endpoint( friendly_name='Sample Switch', endpoint_id='sample-switch-01', capabilities=[capability_alexa, capability_alexa_powercontroller]) return send_response(adr.get()) if namespace == 'Alexa.PowerController': # Note: This sample always returns a success response for either a request to TurnOff or TurnOn endpoint_id = request['directive']['endpoint']['endpointId'] power_state_value = 'OFF' if name == 'TurnOff' else 'ON' correlation_token = request['directive']['header']['correlationToken'] # Check for an error when setting the state state_set = set_device_state(endpoint_id=endpoint_id, state='powerState', value=power_state_value) if not state_set: return AlexaResponse( name='ErrorResponse', payload={'type': 'ENDPOINT_UNREACHABLE', 'message': 'Unable to reach endpoint database.'}).get() # Check for an error when setting TurnOn/TurnOff time if power_state_value == 'ON': TurnOn_set = set_device_time(endpoint_id=endpoint_id, state='TurnOnTime', value=get_seconds_timestamp()) if not TurnOn_set: return AlexaResponse( name='ErrorResponse', payload={'type': 'ENDPOINT_UNREACHABLE', 'message': 'Unable to reach endpoint database.'}).get() global turned_on turned_on = get_seconds_timestamp() time.sleep(random.uniform(1.500,2.500)) TurnOff_set = set_device_state(endpoint_id=endpoint_id, state='powerState', value='OFF') print("turnedoff") # if not TurnOff_set: # return AlexaResponse( # name='ErrorResponse', # payload={'type': 'ENDPOINT_UNREACHABLE', 'message': 'Unable to reach endpoint database.'}).get() if power_state_value == 'OFF': TurnOff_set = set_device_time(endpoint_id=endpoint_id, state='TurnOffTime', value=get_seconds_timestamp()) if not TurnOff_set: return AlexaResponse( name='ErrorResponse', payload={'type': 'ENDPOINT_UNREACHABLE', 'message': 'Unable to reach endpoint database.'}).get() global turned_off turned_off = get_seconds_timestamp() uptime_length = get_uptime_length(turned_on, turned_off) last_uptime_length = set_device_time(endpoint_id=endpoint_id, state='LastUptimeLength', value=uptime_length) if not last_uptime_length: return AlexaResponse( name='ErrorResponse', payload={'type': 'ENDPOINT_UNREACHABLE', 'message': 'Unable to reach endpoint database.'}).get() add_value_to_file(uptime_length) apcr = AlexaResponse(correlation_token=correlation_token) apcr.add_context_property(namespace='Alexa.PowerController', name='powerState', value=power_state_value) return send_response(apcr.get())
def lambda_handler(request, context): # Dump the request for logging - check the CloudWatch logs print('lambda_handler request -----') print(json.dumps(request)) if context is not None: print('lambda_handler context -----') print(context) # Validate we have an Alexa directive if 'directive' not in request: aer = AlexaResponse( name='ErrorResponse', payload={ 'type': 'INVALID_DIRECTIVE', 'message': 'Missing key: directive, Is the request a valid Alexa Directive?' }) return send_response(aer.get()) # Check the payload version payload_version = request['directive']['header']['payloadVersion'] if payload_version != '3': aer = AlexaResponse( name='ErrorResponse', payload={ 'type': 'INTERNAL_ERROR', 'message': 'This skill only supports Smart Home API version 3' }) return send_response(aer.get()) # Crack open the request and see what is being requested name = request['directive']['header']['name'] namespace = request['directive']['header']['namespace'] # Handle the incoming request from Alexa based on the namespace if namespace == 'Alexa.Authorization': if name == 'AcceptGrant': # Note: This sample accepts any grant request # In your implementation you would use the code and token to get and store access tokens grant_code = request['directive']['payload']['grant']['code'] grantee_token = request['directive']['payload']['grantee']['token'] aar = AlexaResponse(namespace='Alexa.Authorization', name='AcceptGrant.Response') return send_response(aar.get()) if namespace == 'Alexa.Discovery': if name == 'Discover': adr = AlexaResponse(namespace='Alexa.Discovery', name='Discover.Response') capability_alexa = adr.create_payload_endpoint_capability() capability_alexa_powercontroller = adr.create_payload_endpoint_capability( interface='Alexa.PowerController', supported=[{ 'name': 'powerState' }]) adr.add_payload_endpoint(endpoint_id='LED', friendly_name='LED', description='これはただのLEDです。', display_categories=['LIGHT'], capabilities=[ capability_alexa, capability_alexa_powercontroller ]) return send_response(adr.get()) if namespace == 'Alexa.PowerController': # Note: This sample always returns a success response for either a request to TurnOff or TurnOn endpoint_id = request['directive']['endpoint']['endpointId'] power_state_value = 'OFF' if name == 'TurnOff' else 'ON' correlation_token = request['directive']['header']['correlationToken'] apcr = AlexaResponse(correlation_token=correlation_token) apcr.add_context_property(namespace='Alexa.PowerController', name='powerState', value=power_state_value) # Update Thing Shadow Desired Value update_thing_shadow("led", False if power_state_value == 'OFF' else True) return send_response(apcr.get())
def handle_discovery(event): adr = AlexaResponse(namespace=NAMESPACE_DISCOVERY, name=RESPONSE_DISCOVER) capability_alexa = adr.create_payload_endpoint_capability() capability_alexa_powercontroller = adr.create_payload_endpoint_capability( interface=NAMESPACE_CONTROL, supported=[{ 'name': 'powerState' }]) capability_alexa_brightnesscontroller = adr.create_payload_endpoint_capability( interface=NAMESPACE_BRIGHTNESS, supported=[{ 'name': 'brightness' }]) capability_alexa_colorcontroller = adr.create_payload_endpoint_capability( interface=NAMESPACE_COLOR, supported=[{ 'name': 'color' }]) capability_alexa_modecontroller = adr.create_payload_endpoint_capability( interface=NAMESPACE_MODE, supported=[{ 'name': 'mode' }]) capability_alexa_modecontroller['instance'] = 'CharlotteLight.LightMode' capability_alexa_modecontroller['capabilityResources'] = { "friendlyNames": [{ "@type": "text", "value": { "text": "mode", "locale": "en-GB" } }, { "@type": "text", "value": { "text": "light mode", "locale": "en-GB" } }, { "@type": "text", "value": { "text": "pattern", "locale": "en-GB" } }, { "@type": "text", "value": { "text": "color mode", "locale": "en-GB" } }] } capability_alexa_modecontroller['configuration'] = { "ordered": False, "supportedModes": [{ "value": "LightMode.SingleColor", "modeResources": { "friendlyNames": [{ "@type": "text", "value": { "text": "normal", "locale": "en-GB" } }, { "@type": "text", "value": { "text": "single color", "locale": "en-GB" } }] } }, { "value": "LightMode.Knightrider", "modeResources": { "friendlyNames": [{ "@type": "text", "value": { "text": "knightrider", "locale": "en-GB" } }, { "@type": "text", "value": { "text": "night rider", "locale": "en-GB" } }, { "@type": "text", "value": { "text": "scroll", "locale": "en-GB" } }, { "@type": "text", "value": { "text": "cylon", "locale": "en-GB" } }] } }, { "value": "LightMode.Starburst", "modeResources": { "friendlyNames": [{ "@type": "text", "value": { "text": "stars", "locale": "en-GB" } }, { "@type": "text", "value": { "text": "starburst", "locale": "en-GB" } }] } }, { "value": "LightMode.SlowRainbow", "modeResources": { "friendlyNames": [{ "@type": "text", "value": { "text": "rainbow", "locale": "en-GB" } }, { "@type": "text", "value": { "text": "slow rainbow", "locale": "en-GB" } }] } }, { "value": "LightMode.FastRainbow", "modeResources": { "friendlyNames": [{ "@type": "text", "value": { "text": "fast rainbow", "locale": "en-GB" } }, { "@type": "text", "value": { "text": "chase rainbow", "locale": "en-GB" } }, { "@type": "text", "value": { "text": "cinema chase rainbow", "locale": "en-GB" } }] } }, { "value": "LightMode.Emergency", "modeResources": { "friendlyNames": [{ "@type": "text", "value": { "text": "emergency", "locale": "en-GB" } }, { "@type": "text", "value": { "text": "emergency lights", "locale": "en-GB" } }] } }] } capability_alexa_scenecontroller = adr.create_payload_endpoint_capability( interface=NAMESPACE_SCENE) adr.add_payload_endpoint(friendly_name='Charlotte Light', endpoint_id='charlotte-light-01', manufacturer_name='Larssen Inc', description='NeoPixel Light', display_categories=["LIGHT"], capabilities=[ capability_alexa, capability_alexa_powercontroller, capability_alexa_brightnesscontroller, capability_alexa_colorcontroller, capability_alexa_modecontroller, capability_alexa_scenecontroller ]) return adr.get()
def lambda_handler(request, context): # Dump the request for logging - check the CloudWatch logs print('lambda_handler request -----') print(json.dumps(request)) if context is not None: print('lambda_handler context -----') print(context) # Validate we have an Alexa directive if 'directive' not in request: aer = AlexaResponse( name='ErrorResponse', payload={ 'type': 'INVALID_DIRECTIVE', 'message': 'Missing key: directive, Is the request a valid Alexa Directive?' }) return send_response(aer.get()) # Check the payload version payload_version = request['directive']['header']['payloadVersion'] if payload_version != '3': aer = AlexaResponse( name='ErrorResponse', payload={ 'type': 'INTERNAL_ERROR', 'message': 'This skill only supports Smart Home API version 3' }) return send_response(aer.get()) # Crack open the request and see what is being requested name = request['directive']['header']['name'] namespace = request['directive']['header']['namespace'] # Handle the incoming request from Alexa based on the namespace if namespace == 'Alexa.Authorization': if name == 'AcceptGrant': # Note: This sample accepts any grant request # In your implementation you would use the code and token to get and store access tokens grant_code = request['directive']['payload']['grant']['code'] grantee_token = request['directive']['payload']['grantee']['token'] aar = AlexaResponse(namespace='Alexa.Authorization', name='AcceptGrant.Response') return send_response(aar.get()) if namespace == 'Alexa.Discovery': if name == 'Discover': adr = AlexaResponse(namespace='Alexa.Discovery', name='Discover.Response') capability_alexa = adr.create_payload_endpoint_capability() capability_alexa_power_controller = adr.create_payload_endpoint_capability( interface='Alexa.PowerController', supported=[{ 'name': 'powerState' }]) capability_alexa_stepSpeaker_controller = adr.create_payload_endpoint_capability( interface='Alexa.StepSpeaker') capability_alexa_playback_controller = adr.create_payload_endpoint_capability( interface='Alexa.PlaybackController', supportedOperations=['Play', 'Pause', 'Stop']) adr.add_payload_endpoint( friendly_name='TV', endpoint_id='ps3-tv', manufacturer_name='Sony', description='PlayStation 3 TV connected with Raspberry Pi', displayCategories=['TV'], capabilities=[ capability_alexa, capability_alexa_power_controller, capability_alexa_stepSpeaker_controller, capability_alexa_playback_controller ]) return send_response(adr.get()) if namespace == 'Alexa.PowerController': # Note: This sample always returns a success response for either a request to TurnOff or TurnOn endpoint_id = request['directive']['endpoint']['endpointId'] power_state_value = 'OFF' if name == 'TurnOff' else 'ON' correlation_token = request['directive']['header']['correlationToken'] try: response = requests.post('{}toggle-power'.format(constants.url), json={'powerState': power_state_value}) if response.status_code == 200: speech = response.text else: return AlexaResponse( name='ErrorResponse', payload={ 'type': 'INTERNAL_ERROR', 'message': 'There was an error with the endpoint.' }).get() except: return AlexaResponse(name='ErrorResponse', payload={ 'type': 'ENDPOINT_UNREACHABLE', 'message': 'Unable to reach endpoint.' }).get() apcr = AlexaResponse(correlation_token=correlation_token) apcr.add_context_property(namespace='Alexa.PowerController', name='powerState', value=power_state_value) elif namespace == 'Alexa.StepSpeaker': endpoint_id = request['directive']['endpoint']['endpointId'] correlation_token = request['directive']['header']['correlationToken'] try: if name == 'AdjustVolume': response = requests.post( '{}adjust-volume'.format(constants.url), json={ 'volumeSteps': request['directive']['payload']['volumeSteps'] }) else: response = None if response and response.status_code == 200: speech = response.text else: return AlexaResponse( name='ErrorResponse', payload={ 'type': 'INTERNAL_ERROR', 'message': 'There was an error with the endpoint.' }).get() except: return AlexaResponse(name='ErrorResponse', payload={ 'type': 'ENDPOINT_UNREACHABLE', 'message': 'Unable to reach endpoint.' }).get() apcr = AlexaResponse(correlation_token=correlation_token) apcr.add_context_property( namespace='Alexa.StepSpeaker', name='volumeSteps', value=request['directive']['payload']['volumeSteps']) elif namespace == 'Alexa.PlaybackController': endpoint_id = request['directive']['endpoint']['endpointId'] correlation_token = request['directive']['header']['correlationToken'] try: if name == 'Play' or name == 'Pause' or name == 'Stop': response = requests.post( '{}play-pause-stop-chromecast-youtube'.format( constants.url), json={'action': name}) else: response = None if response and response.status_code == 200: speech = response.text else: return AlexaResponse( name='ErrorResponse', payload={ 'type': 'INTERNAL_ERROR', 'message': 'There was an error with the endpoint.' }).get() except: return AlexaResponse(name='ErrorResponse', payload={ 'type': 'ENDPOINT_UNREACHABLE', 'message': 'Unable to reach endpoint.' }).get() apcr = AlexaResponse(correlation_token=correlation_token) apcr.add_context_property(namespace='Alexa.PlaybackController', name='action', value=name) return send_response(apcr.get())
def lambda_handler(request, context): # Dump the request for logging - check the CloudWatch logs logger.debug('lambda_handler request -----') logger.debug(json.dumps(request)) if context is not None: logger.debug('lambda_handler context -----') logger.debug(context) # Validate we have an Alexa directive if 'directive' not in request: aer = AlexaResponse( name='ErrorResponse', payload={ 'type': 'INVALID_DIRECTIVE', 'message': 'Missing key: directive, Is the request a valid Alexa Directive?' }) return send_response(aer.get()) # Check the payload version payload_version = request['directive']['header']['payloadVersion'] if payload_version != '3': aer = AlexaResponse( name='ErrorResponse', payload={ 'type': 'INTERNAL_ERROR', 'message': 'This skill only supports Smart Home API version 3' }) return send_response(aer.get()) # Crack open the request and see what is being requested name = request['directive']['header']['name'] namespace = request['directive']['header']['namespace'] # Handle the incoming request from Alexa based on the namespace if namespace == 'Alexa.Authorization': if name == 'AcceptGrant': # Note: This sample accepts any grant request # In your implementation you would use the code and token to get and store access tokens grant_code = request['directive']['payload']['grant']['code'] grantee_token = request['directive']['payload']['grantee']['token'] aar = AlexaResponse(namespace='Alexa.Authorization', name='AcceptGrant.Response') return send_response(aar.get()) if namespace == 'Alexa.Discovery': if name == 'Discover': adr = AlexaResponse(namespace='Alexa.Discovery', name='Discover.Response') capability_alexa = adr.create_payload_endpoint_capability() capability_alexa_powercontroller = adr.create_payload_endpoint_capability( interface='Alexa.PowerController', supported=[{ 'name': 'powerState' }], proactively_reported=False, retrievable=False) capability_alexa_colorcontroller = adr.create_payload_endpoint_capability( interface='Alexa.ColorController', supported=[{ 'name': 'color' }], proactively_reported=False, retrievable=False) adr.add_payload_endpoint( friendly_name= 'Den Lights', # TODO remove hardcoded friendly name endpoint_id= 'thomas-den-lights', # TODO removed hardcoded endpoint_id capabilities=[ capability_alexa, capability_alexa_powercontroller, capability_alexa_colorcontroller ]) return send_response(adr.get()) if namespace == 'Alexa.PowerController': # Note: This sample always returns a success response for either a request to TurnOff or TurnOn endpoint_id = request['directive']['endpoint']['endpointId'] power_state_value = 'OFF' if name == 'TurnOff' else 'ON' correlation_token = request['directive']['header']['correlationToken'] # Check for an error when setting the state state_set = set_device_state(endpoint_id=endpoint_id, state='powerState', value=power_state_value) if not state_set: return AlexaResponse(name='ErrorResponse', payload={ 'type': 'ENDPOINT_UNREACHABLE', 'message': 'Unable to reach endpoint database.' }).get() apcr = AlexaResponse(correlation_token=correlation_token) apcr.add_context_property(namespace='Alexa.PowerController', name='powerState', value=power_state_value) return send_response(apcr.get()) if namespace == 'Alexa.ColorController' and name == 'SetColor': # Note: This sample always returns a success response for either a request to TurnOff or TurnOn endpoint_id = request['directive']['endpoint']['endpointId'] correlation_token = request['directive']['header']['correlationToken'] # Check for an error when setting the state color_set = set_color_state( endpoint_id=endpoint_id, state='color', value=request['directive']['payload']['color']) if not color_set: return AlexaResponse(name='ErrorResponse', payload={ 'type': 'ENDPOINT_UNREACHABLE', 'message': 'Unable to reach endpoint database.' }).get() apcr = AlexaResponse(correlation_token=correlation_token) apcr.add_context_property( namespace='Alexa.ColorController', name='color', value=request['directive']['payload']['color']) return send_response(apcr.get())
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