Note: Sign in to the developer console to build or publish your skill.
When you create a smart home skill, you enable Alexa to invoke your skill to fulfill a user request to control a smart home device. Smart home skills use the pre-built voice interaction model and Alexa defines the set of utterances for you. You can create an Alexa smart home skill for most device types.
A smart device, such as a light, thermostat, camera, lock, that connects to the device cloud. Your skill communicates with your device, or device cloud, by using communication channels supported by your device.
An Alexa-enabled device, such as an Amazon Echo. Make sure that you sign in to the Echo with the same credentials as your Alexa developer account.
The Amazon Alexa app on your mobile device. For instructions about how to download the Alexa app, see Download the Alexa app on the Amazon website. Make sure that you sign in to the Alexa app on your mobile device with the same credentials as your Alexa developer account.
Knowledge of JSON and one of the supported programming languages for AWS Lambda skill code: Node.js, Java, Python, C#, Go, Ruby, or PowerShell.
You create a smart home skill in the Alexa developer console. When prompted to choose a model to add to your skill, select the Smart Home model.
You can choose English(US) as your default language and region, or you can create your skill in another language and region. Make sure to select the region where you plan to make your skill available. You can create your skill in other languages. For details, see Develop Smart Home Skills for Multiple Languages. Language support varies by interface. For more details, see List of Alexa Interfaces and Supported Languages.
After you create your skill, copy the skill ID to a convenient place, such as Notepad on Windows or TextEdit on Mac. You use the skill ID to connect your skill to your skill code hosted on Lambda.
Note: You come back to the developer console to configure Smart Home service endpoint after you implement skill code as a Lambda function. And, you come back to configure Account Linking after you implement your skill code.
// -*- coding: utf-8 -*-// Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.//// SPDX-License-Identifier: LicenseRef-.amazon.com.-AmznSL-1.0// Licensed under the Amazon Software License (the "License")// You may not use this file except in compliance with the License.// A copy of the License is located at http://aws.amazon.com/asl///// This file is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific// language governing permissions and limitations under the License.exports.handler=function(request,context){if(request.directive.header.namespace==='Alexa.Discovery'&&request.directive.header.name==='Discover'){log("DEBUG:","Discover request",JSON.stringify(request));handleDiscovery(request,context,"");}elseif(request.directive.header.namespace==='Alexa.PowerController'){if(request.directive.header.name==='TurnOn'||request.directive.header.name==='TurnOff'){log("DEBUG:","TurnOn or TurnOff Request",JSON.stringify(request));handlePowerControl(request,context);}}elseif(request.directive.header.namespace==='Alexa.Authorization'&&request.directive.header.name==='AcceptGrant'){handleAuthorization(request,context)}functionhandleAuthorization(request,context){// Send the AcceptGrant responsevarpayload={};varheader=request.directive.header;header.name="AcceptGrant.Response";log("DEBUG","AcceptGrant Response: ",JSON.stringify({header:header,payload:payload}));context.succeed({event:{header:header,payload:payload}});}functionhandleDiscovery(request,context){// Send the discovery responsevarpayload={"endpoints":[{"endpointId":"sample-bulb-01","manufacturerName":"Smart Device Company","friendlyName":"Livingroom lamp","description":"Virtual smart light bulb","displayCategories":["LIGHT"],"additionalAttributes":{"manufacturer":"Sample Manufacturer","model":"Sample Model","serialNumber":"U11112233456","firmwareVersion":"1.24.2546","softwareVersion":"1.036","customIdentifier":"Sample custom ID"},"cookie":{"key1":"arbitrary key/value pairs for skill to reference this endpoint.","key2":"There can be multiple entries","key3":"but they should only be used for reference purposes.","key4":"This is not a suitable place to maintain current endpoint state."},"capabilities":[{"interface":"Alexa.PowerController","version":"3","type":"AlexaInterface","properties":{"supported":[{"name":"powerState"}],"retrievable":true}},{"type":"AlexaInterface","interface":"Alexa.EndpointHealth","version":"3.2","properties":{"supported":[{"name":"connectivity"}],"retrievable":true}},{"type":"AlexaInterface","interface":"Alexa","version":"3"}]}]};varheader=request.directive.header;header.name="Discover.Response";log("DEBUG","Discovery Response: ",JSON.stringify({header:header,payload:payload}));context.succeed({event:{header:header,payload:payload}});}functionlog(message,message1,message2){console.log(message+message1+message2);}functionhandlePowerControl(request,context){// get device ID passed in during discoveryvarrequestMethod=request.directive.header.name;varresponseHeader=request.directive.header;responseHeader.namespace="Alexa";responseHeader.name="Response";responseHeader.messageId=responseHeader.messageId+"-R";// get user token pass in requestvarrequestToken=request.directive.endpoint.scope.token;varpowerResult;if(requestMethod==="TurnOn"){// Make the call to your device cloud for control// powerResult = stubControlFunctionToYourCloud(endpointId, token, request);powerResult="ON";}elseif(requestMethod==="TurnOff"){// Make the call to your device cloud for control and check for success// powerResult = stubControlFunctionToYourCloud(endpointId, token, request);powerResult="OFF";}// Return the updated powerState. Always include EndpointHealth in your Alexa.Response// Datetime format for timeOfSample is ISO 8601, `YYYY-MM-DDThh:mm:ssZ`.varcontextResult={"properties":[{"namespace":"Alexa.PowerController","name":"powerState","value":powerResult,"timeOfSample":"2022-09-03T16:20:50.52Z",//retrieve from result."uncertaintyInMilliseconds":50},{"namespace":"Alexa.EndpointHealth","name":"connectivity","value":{"value":"OK"},"timeOfSample":"2022-09-03T22:43:17.877738+00:00","uncertaintyInMilliseconds":0}]};varresponse={context:contextResult,event:{header:responseHeader,endpoint:{scope:{type:"BearerToken",token:requestToken},endpointId:"sample-bulb-01"},payload:{}}};log("DEBUG","Alexa.PowerController ",JSON.stringify(response));context.succeed(response);}};
Copied to clipboard.
# -*- coding: utf-8 -*-# Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.## SPDX-License-Identifier: LicenseRef-.amazon.com.-AmznSL-1.0# Licensed under the Amazon Software License (the "License")# You may not use this file except in# compliance with the License. A copy of the License is located at http://aws.amazon.com/asl/## This file is distributed on an "AS IS" BASIS,# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific# language governing permissions and limitations under the License.importjsonimportmathimportrandomimportuuidimportloggingimportdatetimefromdatetimeimportdatetime,timezonelogger=logging.getLogger(__name__)logger.setLevel(logging.INFO)deflambda_handler(request,context):# Dump the request for logging - check the CloudWatch logs.print('lambda_handler request -----')print(json.dumps(request))ifcontextisnotNone:print('lambda_handler context -----')print(context)# Validate the request is an Alexa smart home directive.if'directive'notinrequest:alexa_response=AlexaResponse(name='ErrorResponse',payload={'type':'INVALID_DIRECTIVE','message':'Missing key: directive, Is the request a valid Alexa Directive?'})returnsend_response(alexa_response.get())# Check the payload version.payload_version=request['directive']['header']['payloadVersion']ifpayload_version!='3':alexa_response=AlexaResponse(name='ErrorResponse',payload={'type':'INTERNAL_ERROR','message':'This skill only supports Smart Home API version 3'})returnsend_response(alexa_response.get())# Crack open the request to see the request.name=request['directive']['header']['name']namespace=request['directive']['header']['namespace']# Handle the incoming request from Alexa based on the namespace.ifnamespace=='Alexa.Authorization':ifname=='AcceptGrant':# Note: This example code accepts any grant request.# In your implementation, invoke Login With Amazon with the grant code to get access and refresh tokens.grant_code=request['directive']['payload']['grant']['code']grantee_token=request['directive']['payload']['grantee']['token']auth_response=AlexaResponse(namespace='Alexa.Authorization',name='AcceptGrant.Response')returnsend_response(auth_response.get())ifnamespace=='Alexa.Discovery':ifname=='Discover':# The request to discover the devices the skill controls.discovery_response=AlexaResponse(namespace='Alexa.Discovery',name='Discover.Response')# Create the response and add the light bulb capabilities.capability_alexa=discovery_response.create_payload_endpoint_capability()capability_alexa_powercontroller=discovery_response.create_payload_endpoint_capability(interface='Alexa.PowerController',supported=[{'name':'powerState'}])capability_alexa_endpointhealth=discovery_response.create_payload_endpoint_capability(interface='Alexa.EndpointHealth',supported=[{'name':'connectivity'}])discovery_response.add_payload_endpoint(friendly_name='Sample Light Bulb',endpoint_id='sample-bulb-01',capabilities=[capability_alexa,capability_alexa_endpointhealth,capability_alexa_powercontroller])returnsend_response(discovery_response.get())ifnamespace=='Alexa.PowerController':# The directive to TurnOff or TurnOn the light bulb.# Note: This example code always returns a success response.endpoint_id=request['directive']['endpoint']['endpointId']power_state_value='OFF'ifname=='TurnOff'else'ON'correlation_token=request['directive']['header']['correlationToken']# Check for an error when setting the state.device_set=update_device_state(endpoint_id=endpoint_id,state='powerState',value=power_state_value)ifnotdevice_set:returnAlexaResponse(name='ErrorResponse',payload={'type':'ENDPOINT_UNREACHABLE','message':'Unable to reach endpoint database.'}).get()directive_response=AlexaResponse(correlation_token=correlation_token)directive_response.add_context_property(namespace='Alexa.PowerController',name='powerState',value=power_state_value)returnsend_response(directive_response.get())# Send the responsedefsend_response(response):print('lambda_handler response -----')print(json.dumps(response))returnresponse# Make the call to your device cloud for controldefupdate_device_state(endpoint_id,state,value):attribute_key=state+'Value'# result = stubControlFunctionToYourCloud(endpointId, token, request);returnTrue# Datetime format for timeOfSample is ISO 8601, `YYYY-MM-DDThh:mm:ssZ`.defget_utc_timestamp(seconds=None):returndatetime.now(timezone.utc).isoformat()classAlexaResponse:def__init__(self,**kwargs):self.context_properties=[]self.payload_endpoints=[]# Set up the response structure.self.context={}self.event={'header':{'namespace':kwargs.get('namespace','Alexa'),'name':kwargs.get('name','Response'),'messageId':str(uuid.uuid4()),'payloadVersion':kwargs.get('payload_version','3')},'endpoint':{"scope":{"type":"BearerToken","token":kwargs.get('token','INVALID')},"endpointId":kwargs.get('endpoint_id','INVALID')},'payload':kwargs.get('payload',{})}if'correlation_token'inkwargs:self.event['header']['correlation_token']=kwargs.get('correlation_token','INVALID')if'cookie'inkwargs:self.event['endpoint']['cookie']=kwargs.get('cookie','{}')# No endpoint property in an AcceptGrant or Discover request.ifself.event['header']['name']=='AcceptGrant.Response'orself.event['header']['name']=='Discover.Response':self.event.pop('endpoint')defadd_context_property(self,**kwargs):self.context_properties.append(self.create_context_property(**kwargs))self.context_properties.append(self.create_context_property())defadd_cookie(self,key,value):if"cookies"inselfisNone:self.cookies={}self.cookies[key]=valuedefadd_payload_endpoint(self,**kwargs):self.payload_endpoints.append(self.create_payload_endpoint(**kwargs))defcreate_context_property(self,**kwargs):return{'namespace':kwargs.get('namespace','Alexa.EndpointHealth'),'name':kwargs.get('name','connectivity'),'value':kwargs.get('value',{'value':'OK'}),'timeOfSample':get_utc_timestamp(),'uncertaintyInMilliseconds':kwargs.get('uncertainty_in_milliseconds',0)}defcreate_payload_endpoint(self,**kwargs):# Return the proper structure expected for the endpoint.# All discovery responses must include the additionAttributesadditionalAttributes={'manufacturer':kwargs.get('manufacturer','Sample Manufacturer'),'model':kwargs.get('model_name','Sample Model'),'serialNumber':kwargs.get('serial_number','U11112233456'),'firmwareVersion':kwargs.get('firmware_version','1.24.2546'),'softwareVersion':kwargs.get('software_version','1.036'),'customIdentifier':kwargs.get('custom_identifier','Sample custom ID')}endpoint={'capabilities':kwargs.get('capabilities',[]),'description':kwargs.get('description','Smart Home Tutorial: Virtual smart light bulb'),'displayCategories':kwargs.get('display_categories',['LIGHT']),'endpointId':kwargs.get('endpoint_id','endpoint_'+"%0.6d"%random.randint(0,999999)),'friendlyName':kwargs.get('friendly_name','Sample light'),'manufacturerName':kwargs.get('manufacturer_name','Sample Manufacturer')}endpoint['additionalAttributes']=kwargs.get('additionalAttributes',additionalAttributes)if'cookie'inkwargs:endpoint['cookie']=kwargs.get('cookie',{})returnendpointdefcreate_payload_endpoint_capability(self,**kwargs):# All discovery responses must include the Alexa interfacecapability={'type':kwargs.get('type','AlexaInterface'),'interface':kwargs.get('interface','Alexa'),'version':kwargs.get('version','3')}supported=kwargs.get('supported',None)ifsupported:capability['properties']={}capability['properties']['supported']=supportedcapability['properties']['proactivelyReported']=kwargs.get('proactively_reported',False)capability['properties']['retrievable']=kwargs.get('retrievable',False)returncapabilitydefget(self,remove_empty=True):response={'context':self.context,'event':self.event}iflen(self.context_properties)>0:response['context']['properties']=self.context_propertiesiflen(self.payload_endpoints)>0:response['event']['payload']['endpoints']=self.payload_endpointsifremove_empty:iflen(response['context'])<1:response.pop('context')returnresponsedefset_payload(self,payload):self.event['payload']=payloaddefset_payload_endpoint(self,payload_endpoints):self.payload_endpoints=payload_endpointsdefset_payload_endpoints(self,payload_endpoints):if'endpoints'notinself.event['payload']:self.event['payload']['endpoints']=[]self.event['payload']['endpoints']=payload_endpoints
Step 3: Configure account linking
All smart home skills must enable account linking to connect the identity of the Alexa user with the user's identity in your system. For a light bulb, your system might have a user account that stores the light bulb model and capabilities, such as whether you can dim the bulb. When the user enables your skill in the Alexa app, Alexa starts the account linking process and receives an access token from your system. Later, when the user asks Alexa to dim the light bulb, Alexa sends the access token to your skill. The token enables your skill to access the user's account in your system to know whether their light bulb is dimmable.
Alexa uses OAuth 2.0 authorization code grant type for smart home and video skills. To configure account linking, you need the following information from your OAuth provider. For details about these fields, see Configure an Authorization Code Grant.
URI of the login page on your authorization server — When a customer enables your skill in the Alexa app, Alexa redirects the customer to this URI to enter login credentials. On successful login, the server returns an authorization code to Alexa.
URI of your access token server — Endpoint of your authorization server that can exchange an authorization code for access tokens.
Client ID — Unique string that identifies the skill that makes requests to your resource server on behalf of the Alexa user.
Your client secret — Alexa uses the client ID and client secret to authenticate with the Access token URI. These credentials identify the request from Alexa.
Authentication scheme — Identifies the type of authentication that Alexa uses to request access and refresh tokens from your access token server.
Scope — (Optional) List of credentials to request from the skill user, such as user id and password. You can provide up to 15 scopes.
Domains — (Optional) List of domains that the authorization server requests content from. You can provide up to 30 domains.
Default access token expiration — (Optional) If your server doesn't provide the expiration time for access tokens in the messaging, you must provide the number of seconds for which the access token is valid.
You configure the authorization server and security profile for the skill in the Alexa developer console. On the Smart Home page, from the left menu, click ACCOUNT LINKING. Enter the required information, and then click Save. You can see the redirection endpoints that Alexa uses in the Alexa Redirect URLs field. Register these redirection URLs with your authorization server.
Note: To test your skill code in the Lambda console, click Deploy to build and deploy the code.
When your implementation is complete, you can enable your skill and begin testing.
To enable your skill and begin testing
In the Alexa app, enable your skill. Alexa performs account linking, sends an Alexa.Authorization.AcceptGrant to your skill to connect your skill to the user's Amazon account, and then sends an Alexa.Discover request to your skill to discover devices.
If your skill successfully discovers devices, in the developer console, complete the steps to enable your skill for testing.
In the developer console, on the Test tab, test your skill in the Alexa Simulator.
Give Alexa commands using the friendly names found in your discovery response and in the Alexa app. Use utterances specific to the device capabilities that your skill supports. Make sure to test your skill with valid and invalid utterances. To verify change reporting and endpoint health, power your device off and back on.
To debug issues, in the CloudWatch console, find the logs in the log group for your skill.
You can iterate on your skill code and continue testing. If you change the account linking configuration, disable and re-enable the skill in the Alexa app to test the new changes. If you change your discover response code, make sure that you disable and re-enable your skill in the Alexa app. For options to test and debug your skill, see Test and Debug Your Smart Home Skill.
When you're satisfied with your skill, you can provide the skill distribution information.
Step 5: Provide publishing information
To distribute your skill to the Amazon Alexa Skills Store, you must fill out the required metadata for your skill on the Distribution tab in the developer console. Make sure that you provide links to a privacy policy and terms of use page and that all clickable links open and display properly on iOS , Android and desktop devices.
After you complete and save the skill information, you can distribute your skill for beta testing or submit it for certification.
Step 6: Beta test your skill (optional)
You have the option to set up a beta test for your skill. With a beta test, you make your skill available to a limited group of testers that you personally select, rather than to the public.
For details, see Skills Beta Testing.
Important: During certification, your device must be online and discoverable until you receive certification feedback on the skill or certification is complete. To avoid certification failure, make sure that your device is available 24 hours a day, seven days a week.
To submit your skill for certification
Run the validation tests on the Certification page in the developer console. These tests help you identify issues that you must fix before you submit the skill.
Make sure that your skill meets the Alexa policy guidelines. These guidelines help make sure that your skill is appropriate for all customers. Adherence to these guidelines guards the privacy and welfare of Alexa users.
Make sure that your skill meets the Alexa security requirements. Customer trust is important. To protect customer data, your skill must meet these security requirements.
When you're ready to publish your skill, and you verified that your skill meets the policy and security requirements, submit your skill for certification.
Note: Skill certification is different than Works with Alexa (WWA) certification for devices. WWA is a separate certification program that establishes your product's compatibility with Alexa and enables your product to carry the WWA badge on product packaging. After your skill receives skill certification, you can apply for WWA certification. For details, see Works with Alexa Overview.
Add additional features to your skill
If your device supports inventory sensors, you can add the Amazon Dash replenishment service to your smart home device to enable Alexa to monitor and reorder supplies or replacement parts for your device. For details, see About Dash Replenishment.
Revise and update your skill after publication
After Amazon publishes your skill to end users, the skill status changes to live and a new development version is created in the developer console automatically. This version has the same information as the original live version. Any changes that you make to the skill configuration or the Lambda function must be certified. When you submit your updated skill to the developer console, in the Testing Instructions field, add a change log that provides a detailed description of all changes. For details, see Revise and update your skill in the developer console.
To make changes to your Lambda function
Create a new Lambda function with a new ARN.
Copy your live Lambda function skill code to the new Lambda function.
In the new Lambda function, make updates to your skill code.
In the development stage for your skill, update the Lambda ARN configuration to point to the new Lambda function ARN, and then submit your skill for certification.
After Amazon certifies your updated skill, Alexa sends requests to the new Lambda ARN. You can deprecate the previous Lambda function or use the function for future development or updates.