├── sample_lambda
├── python
│ ├── __init__.py
│ ├── jsonschema
│ │ ├── tests
│ │ │ ├── __init__.py
│ │ │ ├── compat.py
│ │ │ ├── test_format.py
│ │ │ └── test_cli.py
│ │ ├── __main__.py
│ │ ├── __pycache__
│ │ │ ├── __init__.cpython-36.pyc
│ │ │ ├── _format.cpython-36.pyc
│ │ │ ├── _utils.cpython-36.pyc
│ │ │ ├── compat.cpython-36.pyc
│ │ │ ├── exceptions.cpython-36.pyc
│ │ │ ├── validators.cpython-36.pyc
│ │ │ └── _validators.cpython-36.pyc
│ │ ├── __init__.py
│ │ ├── compat.py
│ │ ├── cli.py
│ │ ├── schemas
│ │ │ └── draft3.json
│ │ ├── _reflect.py
│ │ └── _utils.py
│ └── validation.py
└── README.md
├── sample_backend
├── lambda
│ ├── lambda_api
│ │ └── python
│ │ │ ├── alexa
│ │ │ ├── __init__.py
│ │ │ └── skills
│ │ │ │ ├── __init__.py
│ │ │ │ └── smarthome
│ │ │ │ ├── __init__.py
│ │ │ │ ├── alexa_utils.py
│ │ │ │ ├── alexa_acceptgrant_response.py
│ │ │ │ ├── alexa_response.py
│ │ │ │ ├── alexa_error.py
│ │ │ │ ├── alexa_power_controller.py
│ │ │ │ ├── alexa_change_report.py
│ │ │ │ └── alexa_discover_response.py
│ │ │ ├── jsonschema
│ │ │ ├── tests
│ │ │ │ ├── __init__.py
│ │ │ │ ├── compat.py
│ │ │ │ ├── test_format.py
│ │ │ │ └── test_cli.py
│ │ │ ├── __main__.py
│ │ │ ├── _version.py
│ │ │ ├── __init__.py
│ │ │ ├── compat.py
│ │ │ ├── cli.py
│ │ │ ├── schemas
│ │ │ │ └── draft3.json
│ │ │ ├── _reflect.py
│ │ │ └── _utils.py
│ │ │ ├── endpoint_cloud
│ │ │ ├── __init__.py
│ │ │ ├── api_utils.py
│ │ │ ├── api_message.py
│ │ │ ├── api_response_body.py
│ │ │ ├── api_response.py
│ │ │ └── api_auth.py
│ │ │ └── index.py
│ ├── README.md
│ └── lambda_smarthome
│ │ └── python
│ │ └── index.py
├── docs
│ ├── img
│ │ ├── 3.4.3-lambda-trigger.png
│ │ ├── 5.2.5-linking-dialog.png
│ │ ├── 2.1.10-lwa-web-settings.png
│ │ ├── 2.1.14-lwa-web-settings.png
│ │ ├── 5.2.3-smart-home-skill.png
│ │ ├── 6.2.4-thing-inspection.png
│ │ ├── alexa-sample-smarthome-108x.png
│ │ ├── alexa-sample-smarthome-150x.png
│ │ ├── alexa-sample-smarthome-512x.png
│ │ ├── 2.1.7-lwa-profile-configuration.png
│ │ └── 6.1.3.1-postman-manage-environments.png
│ ├── README.md
│ ├── config.txt
│ ├── 009-setup-cleanup.md
│ ├── 008-setup-send-an-event.md
│ ├── 007-setup-test-endpoints.md
│ ├── 000-setup-requirements.md
│ ├── 001-setup-create-backend.md
│ ├── 002-setup-lwa.md
│ ├── 005-setup-link-skill-smarthome.md
│ ├── 003-setup-create-skill-smarthome.md
│ └── 006-setup-create-endpoints.md
└── README.md
├── sample_async
└── README.md
├── sample_messages
├── README.md
├── ErrorResponse
│ ├── README.md
│ ├── ErrorResponse.General.json
│ ├── ErrorResponse.General.ENDPOINT_LOW_POWER.json
│ ├── ErrorResponse.General.VALUE_OUT_OF_RANGE.json
│ └── ErrorResponse.General.TEMPERATURE_VALUE_OUT_OF_RANGE.json
├── LockController
│ ├── README.md
│ ├── LockController.Lock.request.json
│ ├── LockController.Unlock.request.json
│ ├── LockController.Lock.response.json
│ └── LockController.Unlock.response.json
├── Authorization
│ ├── Authorization.AcceptGrant.response.json
│ ├── ErrorResponse.Authorization.ACCEPT_GRANT_FAILED.json
│ └── Authorization.AcceptGrant.request.json
├── DeferredResponse
│ └── DeferredResponse.json
├── Discovery
│ └── Discovery.request.json
├── StateReport
│ ├── ReportState.json
│ └── StateReport.json
├── PowerController
│ ├── PowerController.TurnOff.request.json
│ ├── PowerController.TurnOn.request.json
│ ├── PowerController.TurnOff.response.json
│ └── PowerController.TurnOn.response.json
├── PlaybackController
│ ├── PlaybackController.Next.request.json
│ ├── PlaybackController.Pause.request.json
│ ├── PlaybackController.Play.request.json
│ ├── PlaybackController.Stop.request.json
│ ├── PlaybackController.Rewind.request.json
│ ├── PlaybackController.Previous.request.json
│ ├── PlaybackController.StartOver.request.json
│ ├── PlaybackController.FastForward.request.json
│ └── PlaybackController.response.json
├── SceneController
│ ├── SceneController.Activate.request.json
│ ├── SceneController.Deactivate.request.json
│ ├── SceneController.ActivationStarted.message.json
│ └── SceneController.DeactivationStarted.message.json
├── Speaker
│ ├── Speaker.SetMute.request.json
│ ├── Speaker.SetVolume.request.json
│ ├── Speaker.AdjustVolume.request.json
│ └── Speaker.response.json
├── StepSpeaker
│ ├── StepSpeaker.SetMute.request.json
│ ├── StepSpeaker.AdjustVolume.request.json
│ └── StepSpeaker.response.json
├── InputController
│ ├── InputController.SelectInput.request.json
│ └── InputController.SelectInput.response.json
├── ChannelController
│ ├── ChannelController.SkipChannels.request.json
│ ├── ChannelController.ChangeChannel.request.json
│ └── ChannelController.response.json
├── ColorTemperatureController
│ ├── ColorTemperatureController.DecreaseColorTemperature.request.json
│ ├── ColorTemperatureController.IncreaseColorTemperature.request.json
│ ├── ColorTemperatureController.SetColorTemperature.request.json
│ └── ColorTemperatureController.response.json
├── BrightnessController
│ ├── BrightnessController.SetBrightness.request.json
│ ├── BrightnessController.AdjustBrightness.request.json
│ ├── BrightnessController.SetBrightness.response.json
│ └── BrightnessController.AdjustBrightness.response.json
├── PercentageController
│ ├── PercentageController.SetPercentage.request.json
│ ├── PercentageController.AdjustPercentage.request.json
│ └── PercentageController.response.json
├── PowerLevelController
│ ├── PowerLevelController.SetPowerLevel.request.json
│ ├── PowerLevelController.AdjustPowerLevel.request.json
│ └── PowerLevelController.response.json
├── ThermostatController
│ ├── ErrorResponse.ThermostatController.General.json
│ ├── ThermostatController.SetThermostatMode.request.json
│ ├── ThermostatController.SetTargetTemperature.SingleMode.request.json
│ ├── ThermostatController.AdjustTargetTemperature.request.json
│ ├── ErrorResponse.ThermostatController.REQUESTED_SETPOINTS_TOO_CLOSE.json
│ ├── ThermostatController.SetTargetTemperature.DualMode.request.json
│ ├── ThermostatController.SetTargetTemperature.TripleMode.request.json
│ ├── ThermostatController.SetTargetTemperature.SingleMode.response.json
│ ├── ThermostatController.SetTargetTemperature.DualMode.response.json
│ └── ThermostatController.SetTargetTemperature.TripleMode.response.json
├── ColorController
│ ├── ColorController.SetColor.request.json
│ └── ColorController.SetColor.response.json
├── TemperatureSensor
│ └── TemperatureSensor.response.json
├── CameraStreamController
│ ├── CameraStreamController.request.json
│ └── CameraStreamController.response.json
└── ChangeReport
│ └── ChangeReport.json
├── validation_schemas
└── README.md
├── NOTICE.txt
├── .gitignore
├── README.md
└── LICENSE.txt
/sample_lambda/python/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/sample_lambda/python/jsonschema/tests/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/sample_backend/lambda/lambda_api/python/alexa/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/sample_backend/lambda/lambda_api/python/jsonschema/tests/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/sample_backend/lambda/lambda_api/python/alexa/skills/__init__.py:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/sample_lambda/python/jsonschema/__main__.py:
--------------------------------------------------------------------------------
1 | from jsonschema.cli import main
2 | main()
3 |
--------------------------------------------------------------------------------
/sample_lambda/README.md:
--------------------------------------------------------------------------------
1 | Readme: https://github.com/alexa/alexa-smarthome/wiki/Sample-Lambda
2 |
--------------------------------------------------------------------------------
/sample_async/README.md:
--------------------------------------------------------------------------------
1 | Readme: https://github.com/alexa/alexa-smarthome/wiki/Sample-Async-Messaging
2 |
--------------------------------------------------------------------------------
/sample_messages/README.md:
--------------------------------------------------------------------------------
1 | Readme: https://github.com/alexa/alexa-smarthome/wiki/Sample-Messages
2 |
--------------------------------------------------------------------------------
/validation_schemas/README.md:
--------------------------------------------------------------------------------
1 | Readme: https://github.com/alexa/alexa-smarthome/wiki/Validation-Schemas
2 |
--------------------------------------------------------------------------------
/sample_backend/lambda/lambda_api/python/jsonschema/__main__.py:
--------------------------------------------------------------------------------
1 | from jsonschema.cli import main
2 | main()
3 |
--------------------------------------------------------------------------------
/NOTICE.txt:
--------------------------------------------------------------------------------
1 | Alexa Smart Home Skill API PRIVATE Repository
2 |
3 | Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
4 |
--------------------------------------------------------------------------------
/sample_backend/docs/img/3.4.3-lambda-trigger.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ilhan-mstf/alexa-smarthome/master/sample_backend/docs/img/3.4.3-lambda-trigger.png
--------------------------------------------------------------------------------
/sample_backend/docs/img/5.2.5-linking-dialog.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ilhan-mstf/alexa-smarthome/master/sample_backend/docs/img/5.2.5-linking-dialog.png
--------------------------------------------------------------------------------
/sample_backend/docs/img/2.1.10-lwa-web-settings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ilhan-mstf/alexa-smarthome/master/sample_backend/docs/img/2.1.10-lwa-web-settings.png
--------------------------------------------------------------------------------
/sample_backend/docs/img/2.1.14-lwa-web-settings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ilhan-mstf/alexa-smarthome/master/sample_backend/docs/img/2.1.14-lwa-web-settings.png
--------------------------------------------------------------------------------
/sample_backend/docs/img/5.2.3-smart-home-skill.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ilhan-mstf/alexa-smarthome/master/sample_backend/docs/img/5.2.3-smart-home-skill.png
--------------------------------------------------------------------------------
/sample_backend/docs/img/6.2.4-thing-inspection.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ilhan-mstf/alexa-smarthome/master/sample_backend/docs/img/6.2.4-thing-inspection.png
--------------------------------------------------------------------------------
/sample_messages/ErrorResponse/README.md:
--------------------------------------------------------------------------------
1 | These ErrorResponses can be sent as a response to any directive from Alexa, and can opportunistically include context properties.
2 |
--------------------------------------------------------------------------------
/sample_backend/docs/img/alexa-sample-smarthome-108x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ilhan-mstf/alexa-smarthome/master/sample_backend/docs/img/alexa-sample-smarthome-108x.png
--------------------------------------------------------------------------------
/sample_backend/docs/img/alexa-sample-smarthome-150x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ilhan-mstf/alexa-smarthome/master/sample_backend/docs/img/alexa-sample-smarthome-150x.png
--------------------------------------------------------------------------------
/sample_backend/docs/img/alexa-sample-smarthome-512x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ilhan-mstf/alexa-smarthome/master/sample_backend/docs/img/alexa-sample-smarthome-512x.png
--------------------------------------------------------------------------------
/sample_backend/docs/img/2.1.7-lwa-profile-configuration.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ilhan-mstf/alexa-smarthome/master/sample_backend/docs/img/2.1.7-lwa-profile-configuration.png
--------------------------------------------------------------------------------
/sample_messages/LockController/README.md:
--------------------------------------------------------------------------------
1 | LockController provides some flexibilty for both fast and slow lock hardware. The directives are the same, but the responses can be different.
--------------------------------------------------------------------------------
/sample_backend/docs/img/6.1.3.1-postman-manage-environments.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ilhan-mstf/alexa-smarthome/master/sample_backend/docs/img/6.1.3.1-postman-manage-environments.png
--------------------------------------------------------------------------------
/sample_lambda/python/jsonschema/__pycache__/__init__.cpython-36.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ilhan-mstf/alexa-smarthome/master/sample_lambda/python/jsonschema/__pycache__/__init__.cpython-36.pyc
--------------------------------------------------------------------------------
/sample_lambda/python/jsonschema/__pycache__/_format.cpython-36.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ilhan-mstf/alexa-smarthome/master/sample_lambda/python/jsonschema/__pycache__/_format.cpython-36.pyc
--------------------------------------------------------------------------------
/sample_lambda/python/jsonschema/__pycache__/_utils.cpython-36.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ilhan-mstf/alexa-smarthome/master/sample_lambda/python/jsonschema/__pycache__/_utils.cpython-36.pyc
--------------------------------------------------------------------------------
/sample_lambda/python/jsonschema/__pycache__/compat.cpython-36.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ilhan-mstf/alexa-smarthome/master/sample_lambda/python/jsonschema/__pycache__/compat.cpython-36.pyc
--------------------------------------------------------------------------------
/sample_lambda/python/jsonschema/__pycache__/exceptions.cpython-36.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ilhan-mstf/alexa-smarthome/master/sample_lambda/python/jsonschema/__pycache__/exceptions.cpython-36.pyc
--------------------------------------------------------------------------------
/sample_lambda/python/jsonschema/__pycache__/validators.cpython-36.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ilhan-mstf/alexa-smarthome/master/sample_lambda/python/jsonschema/__pycache__/validators.cpython-36.pyc
--------------------------------------------------------------------------------
/sample_backend/lambda/lambda_api/python/jsonschema/_version.py:
--------------------------------------------------------------------------------
1 |
2 | # This file is automatically generated by setup.py.
3 | __version__ = '2.6.0'
4 | __sha__ = 'gd16713a'
5 | __revision__ = 'gd16713a'
6 |
--------------------------------------------------------------------------------
/sample_lambda/python/jsonschema/__pycache__/_validators.cpython-36.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ilhan-mstf/alexa-smarthome/master/sample_lambda/python/jsonschema/__pycache__/_validators.cpython-36.pyc
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | endpoint-package.zip
3 | skill-package.zip
4 | sample_lambda/python/python.zip
5 | __pycache__
6 | *.txt
7 | sample_lambda/python/alexa_smart_home_message_schema.json
8 | sample_lambda/python/lambda1.py
9 |
--------------------------------------------------------------------------------
/sample_backend/lambda/lambda_api/python/endpoint_cloud/__init__.py:
--------------------------------------------------------------------------------
1 | from .api_auth import ApiAuth
2 | from .api_handler import ApiHandler
3 | from .api_response import ApiResponse
4 | from .api_response_body import ApiResponseBody
5 | from .api_utils import ApiUtils
6 |
--------------------------------------------------------------------------------
/sample_backend/lambda/README.md:
--------------------------------------------------------------------------------
1 | # Lambda Functions for Sample Smart Home Backend
2 |
3 | ### lambda_api
4 | This is written in python and functions as the device cloud for an Alexa Smart Home Skill.
5 |
6 | ### lambda_smarthome
7 | The Smart Home Skill Lambda that routes directives from Alexa to the customer endpoint.
8 |
9 |
--------------------------------------------------------------------------------
/sample_lambda/python/jsonschema/tests/compat.py:
--------------------------------------------------------------------------------
1 | import sys
2 |
3 |
4 | if sys.version_info[:2] < (2, 7): # pragma: no cover
5 | import unittest2 as unittest
6 | else:
7 | import unittest
8 |
9 | try:
10 | from unittest import mock
11 | except ImportError:
12 | import mock
13 |
14 |
15 | # flake8: noqa
16 |
--------------------------------------------------------------------------------
/sample_backend/lambda/lambda_api/python/jsonschema/tests/compat.py:
--------------------------------------------------------------------------------
1 | import sys
2 |
3 |
4 | if sys.version_info[:2] < (2, 7): # pragma: no cover
5 | import unittest2 as unittest
6 | else:
7 | import unittest
8 |
9 | try:
10 | from unittest import mock
11 | except ImportError:
12 | import mock
13 |
14 |
15 | # flake8: noqa
16 |
--------------------------------------------------------------------------------
/sample_backend/docs/README.md:
--------------------------------------------------------------------------------
1 | # Build an Alexa Smart Home Skill and Backend
2 | These instructions build a Sample Alexa Smart Home skill and backend using Cloud Formation using the API Gateway as an access layer to AWS IoT Things that represent the state of customer endpoints.
3 |
4 |
5 | To begin, start with [Step 0: Set Up the Required Accounts](000-setup-requirements.md)
6 |
7 |
--------------------------------------------------------------------------------
/sample_messages/Authorization/Authorization.AcceptGrant.response.json:
--------------------------------------------------------------------------------
1 | {
2 | "event": {
3 | "header": {
4 | "namespace": "Alexa.Authorization",
5 | "name": "AcceptGrant.Response",
6 | "payloadVersion": "3",
7 | "messageId": "5f8a426e-01e4-4cc9-8b79-65f8bd0fd8a4"
8 | },
9 | "payload": {}
10 | }
11 | }
--------------------------------------------------------------------------------
/sample_backend/README.md:
--------------------------------------------------------------------------------
1 | # Build an Alexa Smart Home Skill and Backend
2 | These instructions build a Sample Alexa Smart Home skill and backend using Cloud Formation using the Amazon API Gateway as an access layer to AWS IoT Things that represent the state of customer endpoints.
3 |
4 |
5 | To begin, start with [Step 0: Set Up the Required Accounts](docs/000-setup-requirements.md)
6 |
7 |
--------------------------------------------------------------------------------
/sample_backend/lambda/lambda_api/python/alexa/skills/smarthome/__init__.py:
--------------------------------------------------------------------------------
1 | from .alexa_acceptgrant_response import AlexaAcceptGrantResponse
2 | from .alexa_change_report import AlexaChangeReport
3 | from .alexa_discover_response import AlexaDiscoverResponse
4 | from .alexa_error import AlexaError
5 | from .alexa_power_controller import AlexaPowerController
6 | from .alexa_response import AlexaResponse
7 | from .alexa_utils import get_utc_timestamp
8 |
--------------------------------------------------------------------------------
/sample_messages/DeferredResponse/DeferredResponse.json:
--------------------------------------------------------------------------------
1 | {
2 | "event": {
3 | "header": {
4 | "namespace": "Alexa",
5 | "name": "DeferredResponse",
6 | "payloadVersion": "3",
7 | "messageId": "5f8a426e-01e4-4cc9-8b79-65f8bd0fd8a4",
8 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
9 | },
10 | "payload": {
11 | "estimatedDeferralInSeconds": 20
12 | }
13 | }
14 | }
--------------------------------------------------------------------------------
/sample_messages/Discovery/Discovery.request.json:
--------------------------------------------------------------------------------
1 | {
2 | "directive": {
3 | "header": {
4 | "namespace": "Alexa.Discovery",
5 | "name": "Discover",
6 | "payloadVersion": "3",
7 | "messageId": "1bd5d003-31b9-476f-ad03-71d471922820"
8 | },
9 | "payload": {
10 | "scope": {
11 | "type": "BearerToken",
12 | "token": "access-token-from-skill"
13 | }
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/sample_messages/Authorization/ErrorResponse.Authorization.ACCEPT_GRANT_FAILED.json:
--------------------------------------------------------------------------------
1 | {
2 | "event": {
3 | "header": {
4 | "namespace": "Alexa.Authorization",
5 | "name": "ErrorResponse",
6 | "payloadVersion": "3",
7 | "messageId": "5f8a426e-01e4-4cc9-8b79-65f8bd0fd8a4",
8 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
9 | },
10 | "payload": {
11 | "type": "ACCEPT_GRANT_FAILED",
12 | "message": "Failed to handle the AcceptGrant directive because request to Login with Amazon failed"
13 | }
14 | }
15 | }
--------------------------------------------------------------------------------
/sample_messages/StateReport/ReportState.json:
--------------------------------------------------------------------------------
1 | {
2 | "directive": {
3 | "header": {
4 | "namespace": "Alexa",
5 | "name": "ReportState",
6 | "payloadVersion": "3",
7 | "messageId": "1bd5d003-31b9-476f-ad03-71d471922820",
8 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
9 | },
10 | "endpoint": {
11 | "endpointId": "endpoint-001",
12 | "cookie": {},
13 | "scope": {
14 | "type": "BearerToken",
15 | "token": "access-token-from-skill"
16 | }
17 | },
18 | "payload": {}
19 | }
20 | }
--------------------------------------------------------------------------------
/sample_messages/LockController/LockController.Lock.request.json:
--------------------------------------------------------------------------------
1 | {
2 | "directive": {
3 | "header": {
4 | "namespace": "Alexa.LockController",
5 | "name": "Lock",
6 | "payloadVersion": "3",
7 | "messageId": "1bd5d003-31b9-476f-ad03-71d471922820",
8 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
9 | },
10 | "endpoint": {
11 | "scope": {
12 | "type": "BearerToken",
13 | "token": "access-token-from-skill"
14 | },
15 | "endpointId": "endpoint-001",
16 | "cookie": {}
17 | },
18 | "payload": {}
19 | }
20 | }
--------------------------------------------------------------------------------
/sample_messages/LockController/LockController.Unlock.request.json:
--------------------------------------------------------------------------------
1 | {
2 | "directive": {
3 | "header": {
4 | "namespace": "Alexa.LockController",
5 | "name": "Unlock",
6 | "payloadVersion": "3",
7 | "messageId": "1bd5d003-31b9-476f-ad03-71d471922820",
8 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
9 | },
10 | "endpoint": {
11 | "scope": {
12 | "type": "BearerToken",
13 | "token": "access-token-from-skill"
14 | },
15 | "endpointId": "endpoint-001",
16 | "cookie": {}
17 | },
18 | "payload": {}
19 | }
20 | }
--------------------------------------------------------------------------------
/sample_messages/PowerController/PowerController.TurnOff.request.json:
--------------------------------------------------------------------------------
1 | {
2 | "directive": {
3 | "header": {
4 | "namespace": "Alexa.PowerController",
5 | "name": "TurnOff",
6 | "payloadVersion": "3",
7 | "messageId": "1bd5d003-31b9-476f-ad03-71d471922820",
8 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
9 | },
10 | "endpoint": {
11 | "scope": {
12 | "type": "BearerToken",
13 | "token": "access-token-from-skill"
14 | },
15 | "endpointId": "endpoint-001",
16 | "cookie": {}
17 | },
18 | "payload": {}
19 | }
20 | }
--------------------------------------------------------------------------------
/sample_messages/PowerController/PowerController.TurnOn.request.json:
--------------------------------------------------------------------------------
1 | {
2 | "directive": {
3 | "header": {
4 | "namespace": "Alexa.PowerController",
5 | "name": "TurnOn",
6 | "payloadVersion": "3",
7 | "messageId": "1bd5d003-31b9-476f-ad03-71d471922820",
8 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
9 | },
10 | "endpoint": {
11 | "scope": {
12 | "type": "BearerToken",
13 | "token": "access-token-from-skill"
14 | },
15 | "endpointId": "endpoint-001",
16 | "cookie": {}
17 | },
18 | "payload": {}
19 | }
20 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Welcome to the Alexa Smart Home GitHub
2 |
3 | This repository contains sample code and resources for Alexa Smart Home developers.
4 |
5 | To get started, https://github.com/alexa/alexa-smarthome/wiki.
6 |
7 | For full developer documentation including how to get started and API references, please visit the [Alexa Smart Home developer pages](https://developer.amazon.com/alexa/smart-home).
8 |
9 | Questions? Comments? Please add to [Issues](https://github.com/alexa/alexa-smarthome/issues), thanks!
10 |
11 | # AWS re:Invent
12 |
13 | In 2017, we held a few sessions at AWS re:Invent, and you'll find the companion content to those sessions [here](https://github.com/alexa/alexa-smarthome/wiki/reinvent).
14 |
--------------------------------------------------------------------------------
/sample_messages/PlaybackController/PlaybackController.Next.request.json:
--------------------------------------------------------------------------------
1 | {
2 | "directive": {
3 | "header": {
4 | "namespace": "Alexa.PlaybackController",
5 | "name": "Next",
6 | "payloadVersion": "3",
7 | "messageId": "1bd5d003-31b9-476f-ad03-71d471922820",
8 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
9 | },
10 | "endpoint": {
11 | "scope": {
12 | "type": "BearerToken",
13 | "token": "access-token-from-skill"
14 | },
15 | "endpointId": "endpoint-001",
16 | "cookie": {}
17 | },
18 | "payload": {}
19 | }
20 | }
--------------------------------------------------------------------------------
/sample_messages/PlaybackController/PlaybackController.Pause.request.json:
--------------------------------------------------------------------------------
1 | {
2 | "directive": {
3 | "header": {
4 | "namespace": "Alexa.PlaybackController",
5 | "name": "Pause",
6 | "payloadVersion": "3",
7 | "messageId": "1bd5d003-31b9-476f-ad03-71d471922820",
8 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
9 | },
10 | "endpoint": {
11 | "scope": {
12 | "type": "BearerToken",
13 | "token": "access-token-from-skill"
14 | },
15 | "endpointId": "endpoint-001",
16 | "cookie": {}
17 | },
18 | "payload": {}
19 | }
20 | }
--------------------------------------------------------------------------------
/sample_messages/PlaybackController/PlaybackController.Play.request.json:
--------------------------------------------------------------------------------
1 | {
2 | "directive": {
3 | "header": {
4 | "namespace": "Alexa.PlaybackController",
5 | "name": "Play",
6 | "payloadVersion": "3",
7 | "messageId": "1bd5d003-31b9-476f-ad03-71d471922820",
8 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
9 | },
10 | "endpoint": {
11 | "scope": {
12 | "type": "BearerToken",
13 | "token": "access-token-from-skill"
14 | },
15 | "endpointId": "endpoint-001",
16 | "cookie": {}
17 | },
18 | "payload": {}
19 | }
20 | }
--------------------------------------------------------------------------------
/sample_messages/PlaybackController/PlaybackController.Stop.request.json:
--------------------------------------------------------------------------------
1 | {
2 | "directive": {
3 | "header": {
4 | "namespace": "Alexa.PlaybackController",
5 | "name": "Stop",
6 | "payloadVersion": "3",
7 | "messageId": "1bd5d003-31b9-476f-ad03-71d471922820",
8 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
9 | },
10 | "endpoint": {
11 | "scope": {
12 | "type": "BearerToken",
13 | "token": "access-token-from-skill"
14 | },
15 | "endpointId": "endpoint-001",
16 | "cookie": {}
17 | },
18 | "payload": {}
19 | }
20 | }
--------------------------------------------------------------------------------
/sample_messages/SceneController/SceneController.Activate.request.json:
--------------------------------------------------------------------------------
1 | {
2 | "directive": {
3 | "header": {
4 | "namespace": "Alexa.SceneController",
5 | "name": "Activate",
6 | "payloadVersion": "3",
7 | "messageId": "1bd5d003-31b9-476f-ad03-71d471922820",
8 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
9 | },
10 | "endpoint": {
11 | "scope": {
12 | "type": "BearerToken",
13 | "token": "access-token-from-skill"
14 | },
15 | "endpointId": "endpoint-001",
16 | "cookie": {}
17 | },
18 | "payload": {}
19 | }
20 | }
--------------------------------------------------------------------------------
/sample_messages/PlaybackController/PlaybackController.Rewind.request.json:
--------------------------------------------------------------------------------
1 | {
2 | "directive": {
3 | "header": {
4 | "namespace": "Alexa.PlaybackController",
5 | "name": "Rewind",
6 | "payloadVersion": "3",
7 | "messageId": "1bd5d003-31b9-476f-ad03-71d471922820",
8 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
9 | },
10 | "endpoint": {
11 | "scope": {
12 | "type": "BearerToken",
13 | "token": "access-token-from-skill"
14 | },
15 | "endpointId": "endpoint-001",
16 | "cookie": {}
17 | },
18 | "payload": {}
19 | }
20 | }
--------------------------------------------------------------------------------
/sample_messages/SceneController/SceneController.Deactivate.request.json:
--------------------------------------------------------------------------------
1 | {
2 | "directive": {
3 | "header": {
4 | "namespace": "Alexa.SceneController",
5 | "name": "Deactivate",
6 | "payloadVersion": "3",
7 | "messageId": "1bd5d003-31b9-476f-ad03-71d471922820",
8 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
9 | },
10 | "endpoint": {
11 | "scope": {
12 | "type": "BearerToken",
13 | "token": "access-token-from-skill"
14 | },
15 | "endpointId": "endpoint-001",
16 | "cookie": {}
17 | },
18 | "payload": {}
19 | }
20 | }
--------------------------------------------------------------------------------
/sample_messages/PlaybackController/PlaybackController.Previous.request.json:
--------------------------------------------------------------------------------
1 | {
2 | "directive": {
3 | "header": {
4 | "namespace": "Alexa.PlaybackController",
5 | "name": "Previous",
6 | "payloadVersion": "3",
7 | "messageId": "1bd5d003-31b9-476f-ad03-71d471922820",
8 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
9 | },
10 | "endpoint": {
11 | "scope": {
12 | "type": "BearerToken",
13 | "token": "access-token-from-skill"
14 | },
15 | "endpointId": "endpoint-001",
16 | "cookie": {}
17 | },
18 | "payload": {}
19 | }
20 | }
--------------------------------------------------------------------------------
/sample_messages/PlaybackController/PlaybackController.StartOver.request.json:
--------------------------------------------------------------------------------
1 | {
2 | "directive": {
3 | "header": {
4 | "namespace": "Alexa.PlaybackController",
5 | "name": "StartOver",
6 | "payloadVersion": "3",
7 | "messageId": "1bd5d003-31b9-476f-ad03-71d471922820",
8 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
9 | },
10 | "endpoint": {
11 | "scope": {
12 | "type": "BearerToken",
13 | "token": "access-token-from-skill"
14 | },
15 | "endpointId": "endpoint-001",
16 | "cookie": {}
17 | },
18 | "payload": {}
19 | }
20 | }
--------------------------------------------------------------------------------
/sample_messages/PlaybackController/PlaybackController.FastForward.request.json:
--------------------------------------------------------------------------------
1 | {
2 | "directive": {
3 | "header": {
4 | "namespace": "Alexa.PlaybackController",
5 | "name": "FastForward",
6 | "payloadVersion": "3",
7 | "messageId": "1bd5d003-31b9-476f-ad03-71d471922820",
8 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
9 | },
10 | "endpoint": {
11 | "scope": {
12 | "type": "BearerToken",
13 | "token": "access-token-from-skill"
14 | },
15 | "endpointId": "endpoint-001",
16 | "cookie": {}
17 | },
18 | "payload": {}
19 | }
20 | }
--------------------------------------------------------------------------------
/sample_messages/Speaker/Speaker.SetMute.request.json:
--------------------------------------------------------------------------------
1 | {
2 | "directive": {
3 | "header": {
4 | "namespace": "Alexa.Speaker",
5 | "name": "SetMute",
6 | "payloadVersion": "3",
7 | "messageId": "1bd5d003-31b9-476f-ad03-71d471922820",
8 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
9 | },
10 | "endpoint": {
11 | "scope": {
12 | "type": "BearerToken",
13 | "token": "access-token-from-skill"
14 | },
15 | "endpointId": "endpoint-001",
16 | "cookie": {}
17 | },
18 | "payload": {
19 | "mute": true
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/sample_messages/Speaker/Speaker.SetVolume.request.json:
--------------------------------------------------------------------------------
1 | {
2 | "directive": {
3 | "header": {
4 | "namespace": "Alexa.Speaker",
5 | "name": "SetVolume",
6 | "payloadVersion": "3",
7 | "messageId": "1bd5d003-31b9-476f-ad03-71d471922820",
8 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
9 | },
10 | "endpoint": {
11 | "scope": {
12 | "type": "BearerToken",
13 | "token": "access-token-from-skill"
14 | },
15 | "endpointId": "endpoint-001",
16 | "cookie": {}
17 | },
18 | "payload": {
19 | "volume": 50
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/sample_messages/StepSpeaker/StepSpeaker.SetMute.request.json:
--------------------------------------------------------------------------------
1 | {
2 | "directive": {
3 | "header": {
4 | "namespace": "Alexa.StepSpeaker",
5 | "name": "SetMute",
6 | "payloadVersion": "3",
7 | "messageId": "1bd5d003-31b9-476f-ad03-71d471922820",
8 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
9 | },
10 | "endpoint": {
11 | "scope": {
12 | "type": "BearerToken",
13 | "token": "access-token-from-skill"
14 | },
15 | "endpointId": "endpoint-001",
16 | "cookie": {}
17 | },
18 | "payload": {
19 | "mute": true
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/sample_messages/StepSpeaker/StepSpeaker.AdjustVolume.request.json:
--------------------------------------------------------------------------------
1 | {
2 | "directive": {
3 | "header": {
4 | "namespace": "Alexa.StepSpeaker",
5 | "name": "AdjustVolume",
6 | "payloadVersion": "3",
7 | "messageId": "1bd5d003-31b9-476f-ad03-71d471922820",
8 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
9 | },
10 | "endpoint": {
11 | "scope": {
12 | "type": "BearerToken",
13 | "token": "access-token-from-skill"
14 | },
15 | "endpointId": "endpoint-001",
16 | "cookie": {}
17 | },
18 | "payload": {
19 | "volumeSteps": -20
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/sample_messages/Authorization/Authorization.AcceptGrant.request.json:
--------------------------------------------------------------------------------
1 | {
2 | "directive": {
3 | "header": {
4 | "namespace": "Alexa.Authorization",
5 | "name": "AcceptGrant",
6 | "payloadVersion": "3",
7 | "messageId": "1bd5d003-31b9-476f-ad03-71d471922820",
8 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
9 | },
10 | "payload": {
11 | "grant": {
12 | "type": "OAuth2.AuthorizationCode",
13 | "code": "ANUbUKCJqlBOpMhwYWxU"
14 | },
15 | "grantee": {
16 | "type": "BearerToken",
17 | "token": "access-token-from-skill"
18 | }
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/sample_messages/InputController/InputController.SelectInput.request.json:
--------------------------------------------------------------------------------
1 | {
2 | "directive": {
3 | "header": {
4 | "namespace": "Alexa.InputController",
5 | "name": "SelectInput",
6 | "payloadVersion": "3",
7 | "messageId": "1bd5d003-31b9-476f-ad03-71d471922820",
8 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
9 | },
10 | "endpoint": {
11 | "scope": {
12 | "type": "BearerToken",
13 | "token": "access-token-from-skill"
14 | },
15 | "endpointId": "endpoint-001",
16 | "cookie": {}
17 | },
18 | "payload": {
19 | "input": "HDMI1"
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/sample_messages/ChannelController/ChannelController.SkipChannels.request.json:
--------------------------------------------------------------------------------
1 | {
2 | "directive": {
3 | "header": {
4 | "namespace": "Alexa.ChannelController",
5 | "name": "SkipChannels",
6 | "payloadVersion": "3",
7 | "messageId": "1bd5d003-31b9-476f-ad03-71d471922820",
8 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
9 | },
10 | "endpoint": {
11 | "scope": {
12 | "type": "BearerToken",
13 | "token": "access-token-from-skill"
14 | },
15 | "endpointId": "endpoint-001",
16 | "cookie": {}
17 | },
18 | "payload": {
19 | "channelCount": 5
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/sample_messages/ColorTemperatureController/ColorTemperatureController.DecreaseColorTemperature.request.json:
--------------------------------------------------------------------------------
1 | {
2 | "directive": {
3 | "header": {
4 | "namespace": "Alexa.ColorTemperatureController",
5 | "name": "DecreaseColorTemperature",
6 | "payloadVersion": "3",
7 | "messageId": "1bd5d003-31b9-476f-ad03-71d471922820",
8 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
9 | },
10 | "endpoint": {
11 | "scope": {
12 | "type": "BearerToken",
13 | "token": "access-token-from-skill"
14 | },
15 | "endpointId": "endpoint-001",
16 | "cookie": {}
17 | },
18 | "payload": {}
19 | }
20 | }
--------------------------------------------------------------------------------
/sample_messages/ColorTemperatureController/ColorTemperatureController.IncreaseColorTemperature.request.json:
--------------------------------------------------------------------------------
1 | {
2 | "directive": {
3 | "header": {
4 | "namespace": "Alexa.ColorTemperatureController",
5 | "name": "IncreaseColorTemperature",
6 | "payloadVersion": "3",
7 | "messageId": "1bd5d003-31b9-476f-ad03-71d471922820",
8 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
9 | },
10 | "endpoint": {
11 | "scope": {
12 | "type": "BearerToken",
13 | "token": "access-token-from-skill"
14 | },
15 | "endpointId": "endpoint-001",
16 | "cookie": {}
17 | },
18 | "payload": {}
19 | }
20 | }
--------------------------------------------------------------------------------
/sample_messages/BrightnessController/BrightnessController.SetBrightness.request.json:
--------------------------------------------------------------------------------
1 | {
2 | "directive": {
3 | "header": {
4 | "namespace": "Alexa.BrightnessController",
5 | "name": "SetBrightness",
6 | "payloadVersion": "3",
7 | "messageId": "1bd5d003-31b9-476f-ad03-71d471922820",
8 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
9 | },
10 | "endpoint": {
11 | "scope": {
12 | "type": "BearerToken",
13 | "token": "access-token-from-skill"
14 | },
15 | "endpointId": "endpoint-001",
16 | "cookie": {}
17 | },
18 | "payload": {
19 | "brightness": 75
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/sample_messages/PercentageController/PercentageController.SetPercentage.request.json:
--------------------------------------------------------------------------------
1 | {
2 | "directive": {
3 | "header": {
4 | "namespace": "Alexa.PercentageController",
5 | "name": "SetPercentage",
6 | "payloadVersion": "3",
7 | "messageId": "1bd5d003-31b9-476f-ad03-71d471922820",
8 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
9 | },
10 | "endpoint": {
11 | "scope": {
12 | "type": "BearerToken",
13 | "token": "access-token-from-skill"
14 | },
15 | "endpointId": "endpoint-001",
16 | "cookie": {}
17 | },
18 | "payload": {
19 | "percentage": 74
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/sample_messages/PowerLevelController/PowerLevelController.SetPowerLevel.request.json:
--------------------------------------------------------------------------------
1 | {
2 | "directive": {
3 | "header": {
4 | "namespace": "Alexa.PowerLevelController",
5 | "name": "SetPowerLevel",
6 | "payloadVersion": "3",
7 | "messageId": "1bd5d003-31b9-476f-ad03-71d471922820",
8 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
9 | },
10 | "endpoint": {
11 | "scope": {
12 | "type": "BearerToken",
13 | "token": "access-token-from-skill"
14 | },
15 | "endpointId": "endpoint-001",
16 | "cookie": {}
17 | },
18 | "payload": {
19 | "powerLevel": 42
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/sample_messages/Speaker/Speaker.AdjustVolume.request.json:
--------------------------------------------------------------------------------
1 | {
2 | "directive": {
3 | "header": {
4 | "namespace": "Alexa.Speaker",
5 | "name": "AdjustVolume",
6 | "payloadVersion": "3",
7 | "messageId": "1bd5d003-31b9-476f-ad03-71d471922820",
8 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
9 | },
10 | "endpoint": {
11 | "scope": {
12 | "type": "BearerToken",
13 | "token": "access-token-from-skill"
14 | },
15 | "endpointId": "endpoint-001",
16 | "cookie": {}
17 | },
18 | "payload": {
19 | "volume": -20,
20 | "volumeDefault": false
21 | }
22 | }
23 | }
--------------------------------------------------------------------------------
/sample_messages/PowerLevelController/PowerLevelController.AdjustPowerLevel.request.json:
--------------------------------------------------------------------------------
1 | {
2 | "directive": {
3 | "header": {
4 | "namespace": "Alexa.PowerLevelController",
5 | "name": "AdjustPowerLevel",
6 | "payloadVersion": "3",
7 | "messageId": "1bd5d003-31b9-476f-ad03-71d471922820",
8 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
9 | },
10 | "endpoint": {
11 | "scope": {
12 | "type": "BearerToken",
13 | "token": "access-token-from-skill"
14 | },
15 | "endpointId": "endpoint-001",
16 | "cookie": {}
17 | },
18 | "payload": {
19 | "powerLevelDelta": 3
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/sample_messages/BrightnessController/BrightnessController.AdjustBrightness.request.json:
--------------------------------------------------------------------------------
1 | {
2 | "directive": {
3 | "header": {
4 | "namespace": "Alexa.BrightnessController",
5 | "name": "AdjustBrightness",
6 | "payloadVersion": "3",
7 | "messageId": "1bd5d003-31b9-476f-ad03-71d471922820",
8 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
9 | },
10 | "endpoint": {
11 | "scope": {
12 | "type": "BearerToken",
13 | "token": "access-token-from-skill"
14 | },
15 | "endpointId": "endpoint-001",
16 | "cookie": {}
17 | },
18 | "payload": {
19 | "brightnessDelta": -25
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/sample_messages/PercentageController/PercentageController.AdjustPercentage.request.json:
--------------------------------------------------------------------------------
1 | {
2 | "directive": {
3 | "header": {
4 | "namespace": "Alexa.PercentageController",
5 | "name": "AdjustPercentage",
6 | "payloadVersion": "3",
7 | "messageId": "1bd5d003-31b9-476f-ad03-71d471922820",
8 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
9 | },
10 | "endpoint": {
11 | "scope": {
12 | "type": "BearerToken",
13 | "token": "access-token-from-skill"
14 | },
15 | "endpointId": "endpoint-001",
16 | "cookie": {}
17 | },
18 | "payload": {
19 | "percentageDelta": -20
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/sample_messages/ErrorResponse/ErrorResponse.General.json:
--------------------------------------------------------------------------------
1 | {
2 | "event": {
3 | "header": {
4 | "namespace": "Alexa",
5 | "name": "ErrorResponse",
6 | "payloadVersion": "3",
7 | "messageId": "5f8a426e-01e4-4cc9-8b79-65f8bd0fd8a4",
8 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
9 | },
10 | "endpoint": {
11 | "scope": {
12 | "type": "BearerToken",
13 | "token": "access-token-from-Amazon"
14 | },
15 | "endpointId": "endpoint-001"
16 | },
17 | "payload": {
18 | "type": "ENDPOINT_UNREACHABLE",
19 | "message": "Unable to reach endpoint-001 because it appears to be offline"
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/sample_lambda/python/jsonschema/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | An implementation of JSON Schema for Python
3 |
4 | The main functionality is provided by the validator classes for each of the
5 | supported JSON Schema versions.
6 |
7 | Most commonly, :func:`validate` is the quickest way to simply validate a given
8 | instance under a schema, and will create a validator for you.
9 |
10 | """
11 |
12 | from jsonschema.exceptions import (
13 | ErrorTree, FormatError, RefResolutionError, SchemaError, ValidationError
14 | )
15 | from jsonschema._format import (
16 | FormatChecker, draft3_format_checker, draft4_format_checker,
17 | )
18 | from jsonschema.validators import (
19 | Draft3Validator, Draft4Validator, RefResolver, validate
20 | )
21 |
22 | #from jsonschema._version import __version__
23 |
24 | # flake8: noqa
25 |
--------------------------------------------------------------------------------
/sample_messages/ErrorResponse/ErrorResponse.General.ENDPOINT_LOW_POWER.json:
--------------------------------------------------------------------------------
1 | {
2 | "event": {
3 | "header": {
4 | "namespace": "Alexa",
5 | "name": "ErrorResponse",
6 | "payloadVersion": "3",
7 | "messageId": "5f8a426e-01e4-4cc9-8b79-65f8bd0fd8a4",
8 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
9 | },
10 | "endpoint": {
11 | "scope": {
12 | "type": "BearerToken",
13 | "token": "access-token-from-Amazon"
14 | },
15 | "endpointId": "endpoint-001"
16 | },
17 | "payload": {
18 | "type": "ENDPOINT_LOW_POWER",
19 | "message": "The lock battery is low",
20 | "percentageState": 5
21 | }
22 | }
23 | }
--------------------------------------------------------------------------------
/sample_backend/lambda/lambda_api/python/jsonschema/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | An implementation of JSON Schema for Python
3 |
4 | The main functionality is provided by the validator classes for each of the
5 | supported JSON Schema versions.
6 |
7 | Most commonly, :func:`validate` is the quickest way to simply validate a given
8 | instance under a schema, and will create a validator for you.
9 |
10 | """
11 |
12 | from jsonschema.exceptions import (
13 | ErrorTree, FormatError, RefResolutionError, SchemaError, ValidationError
14 | )
15 | from jsonschema._format import (
16 | FormatChecker, draft3_format_checker, draft4_format_checker,
17 | )
18 | from jsonschema.validators import (
19 | Draft3Validator, Draft4Validator, RefResolver, validate
20 | )
21 |
22 | from jsonschema._version import __version__
23 |
24 | # flake8: noqa
25 |
--------------------------------------------------------------------------------
/sample_messages/ColorTemperatureController/ColorTemperatureController.SetColorTemperature.request.json:
--------------------------------------------------------------------------------
1 | {
2 | "directive": {
3 | "header": {
4 | "namespace": "Alexa.ColorTemperatureController",
5 | "name": "SetColorTemperature",
6 | "payloadVersion": "3",
7 | "messageId": "1bd5d003-31b9-476f-ad03-71d471922820",
8 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
9 | },
10 | "endpoint": {
11 | "scope": {
12 | "type": "BearerToken",
13 | "token": "access-token-from-skill"
14 | },
15 | "endpointId": "endpoint-001",
16 | "cookie": {}
17 | },
18 | "payload": {
19 | "colorTemperatureInKelvin": 5000
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/sample_backend/lambda/lambda_api/python/alexa/skills/smarthome/alexa_utils.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
4 | #
5 | # Licensed under the Amazon Software License (the "License"). You may not use this file except in
6 | # compliance with the License. A copy of the License is located at
7 | #
8 | # http://aws.amazon.com/asl/
9 | #
10 | # or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific
12 | # language governing permissions and limitations under the License.
13 |
14 | import time
15 |
16 |
17 | def get_utc_timestamp(seconds=None):
18 | return time.strftime('%Y-%m-%dT%H:%M:%S.00Z', time.gmtime(seconds))
19 |
20 |
21 |
--------------------------------------------------------------------------------
/sample_messages/ThermostatController/ErrorResponse.ThermostatController.General.json:
--------------------------------------------------------------------------------
1 | {
2 | "event": {
3 | "header": {
4 | "namespace": "Alexa.ThermostatController",
5 | "name": "ErrorResponse",
6 | "payloadVersion": "3",
7 | "messageId": "5f8a426e-01e4-4cc9-8b79-65f8bd0fd8a4",
8 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
9 | },
10 | "endpoint": {
11 | "scope": {
12 | "type": "BearerToken",
13 | "token": "access-token-from-Amazon"
14 | },
15 | "endpointId": "endpoint-001"
16 | },
17 | "payload": {
18 | "type": "THERMOSTAT_IS_OFF",
19 | "message": "The thermostat is off, cannot turn on due to safety reasons"
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/sample_messages/ThermostatController/ThermostatController.SetThermostatMode.request.json:
--------------------------------------------------------------------------------
1 | {
2 | "directive": {
3 | "header": {
4 | "namespace": "Alexa.ThermostatController",
5 | "name": "SetThermostatMode",
6 | "payloadVersion": "3",
7 | "messageId": "1bd5d003-31b9-476f-ad03-71d471922820",
8 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
9 | },
10 | "endpoint": {
11 | "scope": {
12 | "type": "BearerToken",
13 | "token": "access-token-from-skill"
14 | },
15 | "endpointId": "endpoint-001",
16 | "cookie": {}
17 | },
18 | "payload": {
19 | "thermostatMode": {
20 | "value": "COOL"
21 | }
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/sample_messages/ColorController/ColorController.SetColor.request.json:
--------------------------------------------------------------------------------
1 | {
2 | "directive": {
3 | "header": {
4 | "namespace": "Alexa.ColorController",
5 | "name": "SetColor",
6 | "payloadVersion": "3",
7 | "messageId": "1bd5d003-31b9-476f-ad03-71d471922820",
8 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
9 | },
10 | "endpoint": {
11 | "scope": {
12 | "type": "BearerToken",
13 | "token": "access-token-from-skill"
14 | },
15 | "endpointId": "endpoint-001",
16 | "cookie": {}
17 | },
18 | "payload": {
19 | "color": {
20 | "hue": 350.5,
21 | "saturation": 0.7138,
22 | "brightness": 0.6524
23 | }
24 | }
25 | }
26 | }
--------------------------------------------------------------------------------
/sample_messages/ThermostatController/ThermostatController.SetTargetTemperature.SingleMode.request.json:
--------------------------------------------------------------------------------
1 | {
2 | "directive": {
3 | "header": {
4 | "namespace": "Alexa.ThermostatController",
5 | "name": "SetTargetTemperature",
6 | "payloadVersion": "3",
7 | "messageId": "1bd5d003-31b9-476f-ad03-71d471922820",
8 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
9 | },
10 | "endpoint": {
11 | "scope": {
12 | "type": "BearerToken",
13 | "token": "access-token-from-skill"
14 | },
15 | "endpointId": "endpoint-001",
16 | "cookie": {}
17 | },
18 | "payload": {
19 | "targetSetpoint": {
20 | "value": 25.0,
21 | "scale": "CELSIUS"
22 | }
23 | }
24 | }
25 | }
--------------------------------------------------------------------------------
/sample_messages/ThermostatController/ThermostatController.AdjustTargetTemperature.request.json:
--------------------------------------------------------------------------------
1 | {
2 | "directive": {
3 | "header": {
4 | "namespace": "Alexa.ThermostatController",
5 | "name": "AdjustTargetTemperature",
6 | "payloadVersion": "3",
7 | "messageId": "1bd5d003-31b9-476f-ad03-71d471922820",
8 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
9 | },
10 | "endpoint": {
11 | "scope": {
12 | "type": "BearerToken",
13 | "token": "access-token-from-skill"
14 | },
15 | "endpointId": "endpoint-001",
16 | "cookie": {}
17 | },
18 | "payload": {
19 | "targetSetpointDelta": {
20 | "value": -2.0,
21 | "scale": "FAHRENHEIT"
22 | }
23 | }
24 | }
25 | }
--------------------------------------------------------------------------------
/sample_messages/ErrorResponse/ErrorResponse.General.VALUE_OUT_OF_RANGE.json:
--------------------------------------------------------------------------------
1 | {
2 | "event": {
3 | "header": {
4 | "namespace": "Alexa",
5 | "name": "ErrorResponse",
6 | "payloadVersion": "3",
7 | "messageId": "5f8a426e-01e4-4cc9-8b79-65f8bd0fd8a4",
8 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
9 | },
10 | "endpoint": {
11 | "scope": {
12 | "type": "BearerToken",
13 | "token": "access-token-from-Amazon"
14 | },
15 | "endpointId": "endpoint-001"
16 | },
17 | "payload": {
18 | "type": "VALUE_OUT_OF_RANGE",
19 | "message": "The color temperature cannot be set to 500",
20 | "validRange": {
21 | "minimumValue": 1000,
22 | "maximumValue": 10000
23 | }
24 | }
25 | }
26 | }
--------------------------------------------------------------------------------
/sample_backend/lambda/lambda_api/python/endpoint_cloud/api_utils.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
4 | #
5 | # Licensed under the Amazon Software License (the "License"). You may not use this file except in
6 | # compliance with the License. A copy of the License is located at
7 | #
8 | # http://aws.amazon.com/asl/
9 | #
10 | # or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific
12 | # language governing permissions and limitations under the License.
13 |
14 | import datetime
15 |
16 |
17 | class ApiUtils:
18 |
19 | @staticmethod
20 | def get_time_utc():
21 | """
22 | An ISO 8601 formatted string in UTC (e.g. YYYY-MM-DDThh:mm:ss.sD)
23 | :return: string date time
24 | """
25 | return datetime.datetime.utcnow().isoformat()
26 |
--------------------------------------------------------------------------------
/sample_messages/ThermostatController/ErrorResponse.ThermostatController.REQUESTED_SETPOINTS_TOO_CLOSE.json:
--------------------------------------------------------------------------------
1 | {
2 | "event": {
3 | "header": {
4 | "namespace": "Alexa.ThermostatController",
5 | "name": "ErrorResponse",
6 | "payloadVersion": "3",
7 | "messageId": "5f8a426e-01e4-4cc9-8b79-65f8bd0fd8a4",
8 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
9 | },
10 | "endpoint": {
11 | "scope": {
12 | "type": "BearerToken",
13 | "token": "access-token-from-Amazon"
14 | },
15 | "endpointId": "endpoint-001"
16 | },
17 | "payload": {
18 | "type": "REQUESTED_SETPOINTS_TOO_CLOSE",
19 | "message": "The requested temperature results in setpoints too close",
20 | "minimumTemperatureDelta": {
21 | "value": 2.0,
22 | "scale": "CELSIUS"
23 | }
24 | }
25 | }
26 | }
--------------------------------------------------------------------------------
/sample_backend/lambda/lambda_api/python/endpoint_cloud/api_message.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
4 | #
5 | # Licensed under the Amazon Software License (the "License"). You may not use this file except in
6 | # compliance with the License. A copy of the License is located at
7 | #
8 | # http://aws.amazon.com/asl/
9 | #
10 | # or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific
12 | # language governing permissions and limitations under the License.
13 |
14 |
15 | class ApiMessage:
16 |
17 | def __init__(self, **kwargs):
18 | self.context = kwargs.get('context', {})
19 | self.header = kwargs.get('header', {})
20 | self.endpoint = kwargs.get('endpoint', {})
21 | self.payload = kwargs.get('payload', {})
22 |
23 | def validate(self):
24 |
25 | return False
--------------------------------------------------------------------------------
/sample_messages/ThermostatController/ThermostatController.SetTargetTemperature.DualMode.request.json:
--------------------------------------------------------------------------------
1 | {
2 | "directive": {
3 | "header": {
4 | "namespace": "Alexa.ThermostatController",
5 | "name": "SetTargetTemperature",
6 | "payloadVersion": "3",
7 | "messageId": "1bd5d003-31b9-476f-ad03-71d471922820",
8 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
9 | },
10 | "endpoint": {
11 | "scope": {
12 | "type": "BearerToken",
13 | "token": "access-token-from-skill"
14 | },
15 | "endpointId": "endpoint-001",
16 | "cookie": {}
17 | },
18 | "payload": {
19 | "lowerSetpoint": {
20 | "value": 68.0,
21 | "scale": "FAHRENHEIT"
22 | },
23 | "upperSetpoint": {
24 | "value": 78.0,
25 | "scale": "FAHRENHEIT"
26 | }
27 | }
28 | }
29 | }
--------------------------------------------------------------------------------
/sample_messages/StepSpeaker/StepSpeaker.response.json:
--------------------------------------------------------------------------------
1 | {
2 | "context": {
3 | "properties": [
4 | {
5 | "namespace": "Alexa.EndpointHealth",
6 | "name": "connectivity",
7 | "value": {
8 | "value": "OK"
9 | },
10 | "timeOfSample": "2017-09-27T18:30:30.45Z",
11 | "uncertaintyInMilliseconds": 200
12 | }
13 | ]
14 | },
15 | "event": {
16 | "header": {
17 | "namespace": "Alexa",
18 | "name": "Response",
19 | "payloadVersion": "3",
20 | "messageId": "5f8a426e-01e4-4cc9-8b79-65f8bd0fd8a4",
21 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
22 | },
23 | "endpoint": {
24 | "scope": {
25 | "type": "BearerToken",
26 | "token": "access-token-from-Amazon"
27 | },
28 | "endpointId": "endpoint-001"
29 | },
30 | "payload": {}
31 | }
32 | }
--------------------------------------------------------------------------------
/sample_messages/PlaybackController/PlaybackController.response.json:
--------------------------------------------------------------------------------
1 | {
2 | "context": {
3 | "properties": [
4 | {
5 | "namespace": "Alexa.EndpointHealth",
6 | "name": "connectivity",
7 | "value": {
8 | "value": "OK"
9 | },
10 | "timeOfSample": "2017-09-27T18:30:30.45Z",
11 | "uncertaintyInMilliseconds": 200
12 | }
13 | ]
14 | },
15 | "event": {
16 | "header": {
17 | "namespace": "Alexa",
18 | "name": "Response",
19 | "payloadVersion": "3",
20 | "messageId": "5f8a426e-01e4-4cc9-8b79-65f8bd0fd8a4",
21 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
22 | },
23 | "endpoint": {
24 | "scope": {
25 | "type": "BearerToken",
26 | "token": "access-token-from-Amazon"
27 | },
28 | "endpointId": "endpoint-001"
29 | },
30 | "payload": {}
31 | }
32 | }
--------------------------------------------------------------------------------
/sample_messages/ChannelController/ChannelController.ChangeChannel.request.json:
--------------------------------------------------------------------------------
1 | {
2 | "directive": {
3 | "header": {
4 | "namespace": "Alexa.ChannelController",
5 | "name": "ChangeChannel",
6 | "payloadVersion": "3",
7 | "messageId": "1bd5d003-31b9-476f-ad03-71d471922820",
8 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
9 | },
10 | "endpoint": {
11 | "scope": {
12 | "type": "BearerToken",
13 | "token": "access-token-from-skill"
14 | },
15 | "endpointId": "endpoint-001",
16 | "cookie": {}
17 | },
18 | "payload": {
19 | "channel": {
20 | "number": "1234",
21 | "callSign": "KSTATION1",
22 | "affiliateCallSign": "KSTATION2",
23 | "uri": "someUrl"
24 | },
25 | "channelMetadata": {
26 | "name": "Alternate Channel Name",
27 | "image": "urlToImage"
28 | }
29 | }
30 | }
31 | }
--------------------------------------------------------------------------------
/sample_backend/docs/config.txt:
--------------------------------------------------------------------------------
1 | Set in Step 1 - A unique API ID and name for the Alexa Smart Home Skill Lambda function
2 | [EndpointApiId]
3 | XXXXXXXXXX
4 |
5 | [SkillLambdaArn]
6 | arn:aws:lambda:us-east-1:XXXXXXXXXXXX:function:SampleSkillAdapter
7 |
8 | Set in Step 2 - The Client ID and Secret from the LWA Security Profile
9 | [Login with Amazon Client ID]
10 | amzn1.application-oa2-client.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
11 |
12 | [Login with Amazon Client Secret]
13 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
14 |
15 | Set in Step 3 - A unique ID for the Alexa Smart Home Skill
16 | [Alexa Skill Application Id]
17 | amzn1.ask.skill.XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
18 |
19 | Set in Step 4 - The Messaging Client and Secret from the Alexa Smart Home Skill
20 | [Alexa Skill Messaging Client Id]
21 | amzn1.application-oa2-client.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
22 |
23 | [Alexa Skill Messaging Client Secret]
24 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
25 |
26 | Set in Step 6 - The profile user_id of the User
27 | [user_id]
28 | amzn1.account.XXXXXXXXXXXXXXXXXXXXXXXXXXXX
--------------------------------------------------------------------------------
/sample_messages/ErrorResponse/ErrorResponse.General.TEMPERATURE_VALUE_OUT_OF_RANGE.json:
--------------------------------------------------------------------------------
1 | {
2 | "event": {
3 | "header": {
4 | "namespace": "Alexa",
5 | "name": "ErrorResponse",
6 | "payloadVersion": "3",
7 | "messageId": "5f8a426e-01e4-4cc9-8b79-65f8bd0fd8a4",
8 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
9 | },
10 | "endpoint": {
11 | "scope": {
12 | "type": "BearerToken",
13 | "token": "access-token-from-Amazon"
14 | },
15 | "endpointId": "endpoint-001"
16 | },
17 | "payload": {
18 | "type": "TEMPERATURE_VALUE_OUT_OF_RANGE",
19 | "message": "The requested temperature of -15 is out of range",
20 | "validRange": {
21 | "minimumValue": {
22 | "value": 15.0,
23 | "scale": "CELSIUS"
24 | },
25 | "maximumValue": {
26 | "value": 30.0,
27 | "scale": "CELSIUS"
28 | }
29 | }
30 | }
31 | }
32 | }
--------------------------------------------------------------------------------
/sample_messages/ThermostatController/ThermostatController.SetTargetTemperature.TripleMode.request.json:
--------------------------------------------------------------------------------
1 | {
2 | "directive": {
3 | "header": {
4 | "namespace": "Alexa.ThermostatController",
5 | "name": "SetTargetTemperature",
6 | "payloadVersion": "3",
7 | "messageId": "1bd5d003-31b9-476f-ad03-71d471922820",
8 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
9 | },
10 | "endpoint": {
11 | "scope": {
12 | "type": "BearerToken",
13 | "token": "access-token-from-skill"
14 | },
15 | "endpointId": "endpoint-001",
16 | "cookie": {}
17 | },
18 | "payload": {
19 | "targetSetpoint": {
20 | "value": 73.0,
21 | "scale": "FAHRENHEIT"
22 | },
23 | "lowerSetpoint": {
24 | "value": 68.0,
25 | "scale": "FAHRENHEIT"
26 | },
27 | "upperSetpoint": {
28 | "value": 78.0,
29 | "scale": "FAHRENHEIT"
30 | }
31 | }
32 | }
33 | }
--------------------------------------------------------------------------------
/sample_backend/lambda/lambda_api/python/endpoint_cloud/api_response_body.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
4 | #
5 | # Licensed under the Amazon Software License (the "License"). You may not use this file except in
6 | # compliance with the License. A copy of the License is located at
7 | #
8 | # http://aws.amazon.com/asl/
9 | #
10 | # or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific
12 | # language governing permissions and limitations under the License.
13 |
14 | import json
15 |
16 |
17 | class ApiResponseBody:
18 |
19 | def __init__(self, **kwargs):
20 | self.body = {}
21 | self.result = kwargs.get('result', "OK")
22 | self.message = kwargs.get('message', "")
23 |
24 | def __repr__(self):
25 | return self.create()
26 |
27 | def create(self):
28 | self.body['result'] = self.result
29 | if self.message: # Check for an empty message
30 | self.body['message'] = self.message
31 |
32 | return json.dumps(self.body)
33 |
34 |
--------------------------------------------------------------------------------
/sample_backend/docs/009-setup-cleanup.md:
--------------------------------------------------------------------------------
1 | # Step 9: Clean Up
2 | Clean up the resources used during the workshop and tear down the Endpoint Device backend.
3 |
4 |
5 | #### 9.1 Delete the _Sample-Smart-Home-Backend_ Stack and Alexa Skill
6 |
7 | 9.1.1 Browse to Navigate to the Cloud Formation Console at https://console.aws.amazon.com/cloudformation/home?region=us-east-1.
8 |
9 | 9.1.2 From the list of Stacks, check the box next to only the _Sample-Smart-Home-Backend_ stack.
10 |
11 | 9.1.3 From the Action drop down menu, select **Delete Stack**.
12 |
13 | 9.1.4 From the list of Stacks, check the box next to only the _Sample-Smart-Home-Backend_ stack.
14 |
15 | 9.1.5 Go to your list of Alexa skills at https://developer.amazon.com/edw/home.html#/skills and locate the _Sample Smart Home Skill_ and click the **Delete** link.
16 |
17 | #### 9.2 Delete local files
18 |
19 | 9.2.1 Delete the _Alexa-SmartHome-Sample_ directory and its contents from the desktop.
20 |
21 | ____
22 | Return to the [README](../README.md).
23 |
--------------------------------------------------------------------------------
/sample_messages/SceneController/SceneController.ActivationStarted.message.json:
--------------------------------------------------------------------------------
1 | {
2 | "context": {
3 | "properties": [
4 | {
5 | "namespace": "Alexa.EndpointHealth",
6 | "name": "connectivity",
7 | "value": {
8 | "value": "OK"
9 | },
10 | "timeOfSample": "2017-09-27T18:30:30.45Z",
11 | "uncertaintyInMilliseconds": 200
12 | }
13 | ]
14 | },
15 | "event": {
16 | "header": {
17 | "namespace": "Alexa.SceneController",
18 | "name": "ActivationStarted",
19 | "payloadVersion": "3",
20 | "messageId": "5f8a426e-01e4-4cc9-8b79-65f8bd0fd8a4",
21 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
22 | },
23 | "endpoint": {
24 | "scope": {
25 | "type": "BearerToken",
26 | "token": "access-token-from-Amazon"
27 | },
28 | "endpointId": "endpoint-001"
29 | },
30 | "payload": {
31 | "cause": {
32 | "type": "VOICE_INTERACTION"
33 | },
34 | "timestamp": "2017-09-27T18:30:30.45Z"
35 | }
36 | }
37 | }
--------------------------------------------------------------------------------
/sample_messages/SceneController/SceneController.DeactivationStarted.message.json:
--------------------------------------------------------------------------------
1 | {
2 | "context": {
3 | "properties": [
4 | {
5 | "namespace": "Alexa.EndpointHealth",
6 | "name": "connectivity",
7 | "value": {
8 | "value": "OK"
9 | },
10 | "timeOfSample": "2017-09-27T18:30:30.45Z",
11 | "uncertaintyInMilliseconds": 200
12 | }
13 | ]
14 | },
15 | "event": {
16 | "header": {
17 | "namespace": "Alexa.SceneController",
18 | "name": "DeactivationStarted",
19 | "payloadVersion": "3",
20 | "messageId": "5f8a426e-01e4-4cc9-8b79-65f8bd0fd8a4",
21 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
22 | },
23 | "endpoint": {
24 | "scope": {
25 | "type": "BearerToken",
26 | "token": "access-token-from-Amazon"
27 | },
28 | "endpointId": "endpoint-001"
29 | },
30 | "payload": {
31 | "cause": {
32 | "type": "APP_INTERACTION"
33 | },
34 | "timestamp": "2017-09-27T18:30:30.45Z"
35 | }
36 | }
37 | }
--------------------------------------------------------------------------------
/sample_messages/LockController/LockController.Lock.response.json:
--------------------------------------------------------------------------------
1 | {
2 | "context": {
3 | "properties": [
4 | {
5 | "namespace": "Alexa.LockController",
6 | "name": "lockState",
7 | "value": "LOCKED",
8 | "timeOfSample": "2017-09-27T18:30:30.45Z",
9 | "uncertaintyInMilliseconds": 200
10 | },
11 | {
12 | "namespace": "Alexa.EndpointHealth",
13 | "name": "connectivity",
14 | "value": {
15 | "value": "OK"
16 | },
17 | "timeOfSample": "2017-09-27T18:30:30.45Z",
18 | "uncertaintyInMilliseconds": 200
19 | }
20 | ]
21 | },
22 | "event": {
23 | "header": {
24 | "namespace": "Alexa",
25 | "name": "Response",
26 | "payloadVersion": "3",
27 | "messageId": "5f8a426e-01e4-4cc9-8b79-65f8bd0fd8a4",
28 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
29 | },
30 | "endpoint": {
31 | "scope": {
32 | "type": "BearerToken",
33 | "token": "access-token-from-Amazon"
34 | },
35 | "endpointId": "endpoint-001"
36 | },
37 | "payload": {}
38 | }
39 | }
--------------------------------------------------------------------------------
/sample_messages/LockController/LockController.Unlock.response.json:
--------------------------------------------------------------------------------
1 | {
2 | "context": {
3 | "properties": [
4 | {
5 | "namespace": "Alexa.LockController",
6 | "name": "lockState",
7 | "value": "UNLOCKED",
8 | "timeOfSample": "2017-09-27T18:30:30.45Z",
9 | "uncertaintyInMilliseconds": 200
10 | },
11 | {
12 | "namespace": "Alexa.EndpointHealth",
13 | "name": "connectivity",
14 | "value": {
15 | "value": "OK"
16 | },
17 | "timeOfSample": "2017-09-27T18:30:30.45Z",
18 | "uncertaintyInMilliseconds": 200
19 | }
20 | ]
21 | },
22 | "event": {
23 | "header": {
24 | "namespace": "Alexa",
25 | "name": "Response",
26 | "payloadVersion": "3",
27 | "messageId": "5f8a426e-01e4-4cc9-8b79-65f8bd0fd8a4",
28 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
29 | },
30 | "endpoint": {
31 | "scope": {
32 | "type": "BearerToken",
33 | "token": "access-token-from-Amazon"
34 | },
35 | "endpointId": "endpoint-001"
36 | },
37 | "payload": {}
38 | }
39 | }
--------------------------------------------------------------------------------
/sample_messages/PowerController/PowerController.TurnOff.response.json:
--------------------------------------------------------------------------------
1 | {
2 | "context": {
3 | "properties": [
4 | {
5 | "namespace": "Alexa.PowerController",
6 | "name": "powerState",
7 | "value": "OFF",
8 | "timeOfSample": "2017-09-27T18:30:30.45Z",
9 | "uncertaintyInMilliseconds": 200
10 | },
11 | {
12 | "namespace": "Alexa.EndpointHealth",
13 | "name": "connectivity",
14 | "value": {
15 | "value": "OK"
16 | },
17 | "timeOfSample": "2017-09-27T18:30:30.45Z",
18 | "uncertaintyInMilliseconds": 200
19 | }
20 | ]
21 | },
22 | "event": {
23 | "header": {
24 | "namespace": "Alexa",
25 | "name": "Response",
26 | "payloadVersion": "3",
27 | "messageId": "5f8a426e-01e4-4cc9-8b79-65f8bd0fd8a4",
28 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
29 | },
30 | "endpoint": {
31 | "scope": {
32 | "type": "BearerToken",
33 | "token": "access-token-from-Amazon"
34 | },
35 | "endpointId": "endpoint-001"
36 | },
37 | "payload": {}
38 | }
39 | }
--------------------------------------------------------------------------------
/sample_messages/PowerController/PowerController.TurnOn.response.json:
--------------------------------------------------------------------------------
1 | {
2 | "context": {
3 | "properties": [
4 | {
5 | "namespace": "Alexa.PowerController",
6 | "name": "powerState",
7 | "value": "ON",
8 | "timeOfSample": "2017-09-27T18:30:30.45Z",
9 | "uncertaintyInMilliseconds": 200
10 | },
11 | {
12 | "namespace": "Alexa.EndpointHealth",
13 | "name": "connectivity",
14 | "value": {
15 | "value": "OK"
16 | },
17 | "timeOfSample": "2017-09-27T18:30:30.45Z",
18 | "uncertaintyInMilliseconds": 200
19 | }
20 | ]
21 | },
22 | "event": {
23 | "header": {
24 | "namespace": "Alexa",
25 | "name": "Response",
26 | "payloadVersion": "3",
27 | "messageId": "5f8a426e-01e4-4cc9-8b79-65f8bd0fd8a4",
28 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
29 | },
30 | "endpoint": {
31 | "scope": {
32 | "type": "BearerToken",
33 | "token": "access-token-from-Amazon"
34 | },
35 | "endpointId": "endpoint-001"
36 | },
37 | "payload": {}
38 | }
39 | }
--------------------------------------------------------------------------------
/sample_messages/InputController/InputController.SelectInput.response.json:
--------------------------------------------------------------------------------
1 | {
2 | "context": {
3 | "properties": [
4 | {
5 | "namespace": "Alexa.InputController",
6 | "name": "input",
7 | "value": "HDMI1",
8 | "timeOfSample": "2017-09-27T18:30:30.45Z",
9 | "uncertaintyInMilliseconds": 200
10 | },
11 | {
12 | "namespace": "Alexa.EndpointHealth",
13 | "name": "connectivity",
14 | "value": {
15 | "value": "OK"
16 | },
17 | "timeOfSample": "2017-09-27T18:30:30.45Z",
18 | "uncertaintyInMilliseconds": 200
19 | }
20 | ]
21 | },
22 | "event": {
23 | "header": {
24 | "namespace": "Alexa",
25 | "name": "Response",
26 | "payloadVersion": "3",
27 | "messageId": "5f8a426e-01e4-4cc9-8b79-65f8bd0fd8a4",
28 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
29 | },
30 | "endpoint": {
31 | "scope": {
32 | "type": "BearerToken",
33 | "token": "access-token-from-Amazon"
34 | },
35 | "endpointId": "endpoint-001"
36 | },
37 | "payload": {}
38 | }
39 | }
--------------------------------------------------------------------------------
/sample_messages/PercentageController/PercentageController.response.json:
--------------------------------------------------------------------------------
1 | {
2 | "context": {
3 | "properties": [
4 | {
5 | "namespace": "Alexa.PercentageController",
6 | "name": "percentage",
7 | "value": 74,
8 | "timeOfSample": "2017-09-27T18:30:30.45Z",
9 | "uncertaintyInMilliseconds": 200
10 | },
11 | {
12 | "namespace": "Alexa.EndpointHealth",
13 | "name": "connectivity",
14 | "value": {
15 | "value": "OK"
16 | },
17 | "timeOfSample": "2017-09-27T18:30:30.45Z",
18 | "uncertaintyInMilliseconds": 200
19 | }
20 | ]
21 | },
22 | "event": {
23 | "header": {
24 | "namespace": "Alexa",
25 | "name": "Response",
26 | "payloadVersion": "3",
27 | "messageId": "5f8a426e-01e4-4cc9-8b79-65f8bd0fd8a4",
28 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
29 | },
30 | "endpoint": {
31 | "scope": {
32 | "type": "BearerToken",
33 | "token": "access-token-from-Amazon"
34 | },
35 | "endpointId": "endpoint-001"
36 | },
37 | "payload": {}
38 | }
39 | }
--------------------------------------------------------------------------------
/sample_messages/PowerLevelController/PowerLevelController.response.json:
--------------------------------------------------------------------------------
1 | {
2 | "context": {
3 | "properties": [
4 | {
5 | "namespace": "Alexa.PowerLevelController",
6 | "name": "powerLevel",
7 | "value": 42,
8 | "timeOfSample": "2017-09-27T18:30:30.45Z",
9 | "uncertaintyInMilliseconds": 200
10 | },
11 | {
12 | "namespace": "Alexa.EndpointHealth",
13 | "name": "connectivity",
14 | "value": {
15 | "value": "OK"
16 | },
17 | "timeOfSample": "2017-09-27T18:30:30.45Z",
18 | "uncertaintyInMilliseconds": 200
19 | }
20 | ]
21 | },
22 | "event": {
23 | "header": {
24 | "namespace": "Alexa",
25 | "name": "Response",
26 | "payloadVersion": "3",
27 | "messageId": "5f8a426e-01e4-4cc9-8b79-65f8bd0fd8a4",
28 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
29 | },
30 | "endpoint": {
31 | "scope": {
32 | "type": "BearerToken",
33 | "token": "access-token-from-Amazon"
34 | },
35 | "endpointId": "endpoint-001"
36 | },
37 | "payload": {}
38 | }
39 | }
--------------------------------------------------------------------------------
/sample_messages/BrightnessController/BrightnessController.SetBrightness.response.json:
--------------------------------------------------------------------------------
1 | {
2 | "context": {
3 | "properties": [
4 | {
5 | "namespace": "Alexa.BrightnessController",
6 | "name": "brightness",
7 | "value": 75,
8 | "timeOfSample": "2017-09-27T18:30:30.45Z",
9 | "uncertaintyInMilliseconds": 200
10 | },
11 | {
12 | "namespace": "Alexa.EndpointHealth",
13 | "name": "connectivity",
14 | "value": {
15 | "value": "OK"
16 | },
17 | "timeOfSample": "2017-09-27T18:30:30.45Z",
18 | "uncertaintyInMilliseconds": 200
19 | }
20 | ]
21 | },
22 | "event": {
23 | "header": {
24 | "namespace": "Alexa",
25 | "name": "Response",
26 | "payloadVersion": "3",
27 | "messageId": "5f8a426e-01e4-4cc9-8b79-65f8bd0fd8a4",
28 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
29 | },
30 | "endpoint": {
31 | "scope": {
32 | "type": "BearerToken",
33 | "token": "access-token-from-Amazon"
34 | },
35 | "endpointId": "endpoint-001"
36 | },
37 | "payload": {}
38 | }
39 | }
--------------------------------------------------------------------------------
/sample_messages/BrightnessController/BrightnessController.AdjustBrightness.response.json:
--------------------------------------------------------------------------------
1 | {
2 | "context": {
3 | "properties": [
4 | {
5 | "namespace": "Alexa.BrightnessController",
6 | "name": "brightness",
7 | "value": 50,
8 | "timeOfSample": "2017-02-03T16:20:50.52Z",
9 | "uncertaintyInMilliseconds": 200
10 | },
11 | {
12 | "namespace": "Alexa.EndpointHealth",
13 | "name": "connectivity",
14 | "value": {
15 | "value": "OK"
16 | },
17 | "timeOfSample": "2017-09-27T18:30:30.45Z",
18 | "uncertaintyInMilliseconds": 200
19 | }
20 | ]
21 | },
22 | "event": {
23 | "header": {
24 | "namespace": "Alexa",
25 | "name": "Response",
26 | "payloadVersion": "3",
27 | "messageId": "5f8a426e-01e4-4cc9-8b79-65f8bd0fd8a4",
28 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
29 | },
30 | "endpoint": {
31 | "scope": {
32 | "type": "BearerToken",
33 | "token": "access-token-from-Amazon"
34 | },
35 | "endpointId": "endpoint-001"
36 | },
37 | "payload": {}
38 | }
39 | }
--------------------------------------------------------------------------------
/sample_messages/ColorTemperatureController/ColorTemperatureController.response.json:
--------------------------------------------------------------------------------
1 | {
2 | "context": {
3 | "properties": [
4 | {
5 | "namespace": "Alexa.ColorTemperatureController",
6 | "name": "colorTemperatureInKelvin",
7 | "value": 5000,
8 | "timeOfSample": "2017-09-27T18:30:30.45Z",
9 | "uncertaintyInMilliseconds": 200
10 | },
11 | {
12 | "namespace": "Alexa.EndpointHealth",
13 | "name": "connectivity",
14 | "value": {
15 | "value": "OK"
16 | },
17 | "timeOfSample": "2017-09-27T18:30:30.45Z",
18 | "uncertaintyInMilliseconds": 200
19 | }
20 | ]
21 | },
22 | "event": {
23 | "header": {
24 | "namespace": "Alexa",
25 | "name": "Response",
26 | "payloadVersion": "3",
27 | "messageId": "5f8a426e-01e4-4cc9-8b79-65f8bd0fd8a4",
28 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
29 | },
30 | "endpoint": {
31 | "scope": {
32 | "type": "BearerToken",
33 | "token": "access-token-from-Amazon"
34 | },
35 | "endpointId": "endpoint-001"
36 | },
37 | "payload": {}
38 | }
39 | }
--------------------------------------------------------------------------------
/sample_messages/TemperatureSensor/TemperatureSensor.response.json:
--------------------------------------------------------------------------------
1 | {
2 | "context": {
3 | "properties": [
4 | {
5 | "namespace": "Alexa.TemperatureSensor",
6 | "name": "temperature",
7 | "value": {
8 | "value": 24.0,
9 | "scale": "CELSIUS"
10 | },
11 | "timeOfSample": "2017-09-27T18:30:30.45Z",
12 | "uncertaintyInMilliseconds": 200
13 | },
14 | {
15 | "namespace": "Alexa.EndpointHealth",
16 | "name": "connectivity",
17 | "value": {
18 | "value": "OK"
19 | },
20 | "timeOfSample": "2017-09-27T18:30:30.45Z",
21 | "uncertaintyInMilliseconds": 200
22 | }
23 | ]
24 | },
25 | "event": {
26 | "header": {
27 | "namespace": "Alexa",
28 | "name": "StateReport",
29 | "payloadVersion": "3",
30 | "messageId": "5f8a426e-01e4-4cc9-8b79-65f8bd0fd8a4",
31 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
32 | },
33 | "endpoint": {
34 | "scope": {
35 | "type": "BearerToken",
36 | "token": "access-token-from-Amazon"
37 | },
38 | "endpointId": "endpoint-001"
39 | },
40 | "payload": {}
41 | }
42 | }
--------------------------------------------------------------------------------
/sample_backend/lambda/lambda_api/python/endpoint_cloud/api_response.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
4 | #
5 | # Licensed under the Amazon Software License (the "License"). You may not use this file except in
6 | # compliance with the License. A copy of the License is located at
7 | #
8 | # http://aws.amazon.com/asl/
9 | #
10 | # or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific
12 | # language governing permissions and limitations under the License.
13 |
14 | from .api_response_body import ApiResponseBody
15 |
16 |
17 | class ApiResponse:
18 |
19 | def __init__(self, **kwargs):
20 | self.isBase64Encoded = kwargs.get('isBase64Encoded', False)
21 | self.statusCode = kwargs.get('statusCode', 200)
22 | self.headers = {}
23 | self.body = ApiResponseBody()
24 | self.response = {}
25 |
26 | def __repr__(self):
27 | return self.create()
28 |
29 | def create(self):
30 | self.headers['Content-Type'] = 'application/json'
31 |
32 | self.response['isBase64Encoded'] = str(self.isBase64Encoded)
33 | self.response['statusCode'] = str(self.statusCode)
34 | self.response['headers'] = self.headers
35 | self.response['body'] = str(self.body)
36 |
37 | return self.response
38 |
--------------------------------------------------------------------------------
/sample_messages/ColorController/ColorController.SetColor.response.json:
--------------------------------------------------------------------------------
1 | {
2 | "context": {
3 | "properties": [
4 | {
5 | "namespace": "Alexa.ColorController",
6 | "name": "color",
7 | "value": {
8 | "hue": 350.5,
9 | "saturation": 0.7138,
10 | "brightness": 0.6524
11 | },
12 | "timeOfSample": "2017-09-27T18:30:30.45Z",
13 | "uncertaintyInMilliseconds": 200
14 | },
15 | {
16 | "namespace": "Alexa.EndpointHealth",
17 | "name": "connectivity",
18 | "value": {
19 | "value": "OK"
20 | },
21 | "timeOfSample": "2017-09-27T18:30:30.45Z",
22 | "uncertaintyInMilliseconds": 200
23 | }
24 | ]
25 | },
26 | "event": {
27 | "header": {
28 | "namespace": "Alexa",
29 | "name": "Response",
30 | "payloadVersion": "3",
31 | "messageId": "5f8a426e-01e4-4cc9-8b79-65f8bd0fd8a4",
32 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
33 | },
34 | "endpoint": {
35 | "scope": {
36 | "type": "BearerToken",
37 | "token": "access-token-from-Amazon"
38 | },
39 | "endpointId": "endpoint-001"
40 | },
41 | "payload": {}
42 | }
43 | }
--------------------------------------------------------------------------------
/sample_messages/ChannelController/ChannelController.response.json:
--------------------------------------------------------------------------------
1 | {
2 | "context": {
3 | "properties": [
4 | {
5 | "namespace": "Alexa.ChannelController",
6 | "name": "channel",
7 | "value": {
8 | "number": "1234",
9 | "callSign": "callsign1",
10 | "affiliateCallSign": "callsign2"
11 | },
12 | "timeOfSample": "2017-09-27T18:30:30.45Z",
13 | "uncertaintyInMilliseconds": 200
14 | },
15 | {
16 | "namespace": "Alexa.EndpointHealth",
17 | "name": "connectivity",
18 | "value": {
19 | "value": "OK"
20 | },
21 | "timeOfSample": "2017-09-27T18:30:30.45Z",
22 | "uncertaintyInMilliseconds": 200
23 | }
24 | ]
25 | },
26 | "event": {
27 | "header": {
28 | "namespace": "Alexa",
29 | "name": "Response",
30 | "payloadVersion": "3",
31 | "messageId": "5f8a426e-01e4-4cc9-8b79-65f8bd0fd8a4",
32 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
33 | },
34 | "endpoint": {
35 | "scope": {
36 | "type": "BearerToken",
37 | "token": "access-token-from-Amazon"
38 | },
39 | "endpointId": "endpoint-001"
40 | },
41 | "payload": {}
42 | }
43 | }
--------------------------------------------------------------------------------
/sample_messages/CameraStreamController/CameraStreamController.request.json:
--------------------------------------------------------------------------------
1 | {
2 | "directive": {
3 | "header": {
4 | "namespace": "Alexa.CameraStreamController",
5 | "name": "InitializeCameraStreams",
6 | "payloadVersion": "3",
7 | "messageId": "1bd5d003-31b9-476f-ad03-71d471922820",
8 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
9 | },
10 | "endpoint": {
11 | "scope": {
12 | "type": "BearerToken",
13 | "token": "access-token-from-skill"
14 | },
15 | "endpointId": "endpoint-001",
16 | "cookie": {}
17 | },
18 | "payload": {
19 | "cameraStreams": [
20 | {
21 | "protocol": "RTSP",
22 | "resolution": {
23 | "width": 1920,
24 | "height": 1080
25 | },
26 | "authorizationType": "BEARER",
27 | "videoCodec": "H264",
28 | "audioCodec": "AAC"
29 | },
30 | {
31 | "protocol": "RTSP",
32 | "resolution": {
33 | "width": 1280,
34 | "height": 720
35 | },
36 | "authorizationType": "BEARER",
37 | "videoCodec": "MPEG2",
38 | "audioCodec": "G711"
39 | }
40 | ]
41 | }
42 | }
43 | }
--------------------------------------------------------------------------------
/sample_messages/Speaker/Speaker.response.json:
--------------------------------------------------------------------------------
1 | {
2 | "context": {
3 | "properties": [
4 | {
5 | "namespace": "Alexa.Speaker",
6 | "name": "volume",
7 | "value": 50,
8 | "timeOfSample": "2017-09-27T18:30:30.45Z",
9 | "uncertaintyInMilliseconds": 200
10 | },
11 | {
12 | "namespace": "Alexa.Speaker",
13 | "name": "muted",
14 | "value": false,
15 | "timeOfSample": "2017-09-27T18:30:30.45Z",
16 | "uncertaintyInMilliseconds": 200
17 | },
18 | {
19 | "namespace": "Alexa.EndpointHealth",
20 | "name": "connectivity",
21 | "value": {
22 | "value": "OK"
23 | },
24 | "timeOfSample": "2017-09-27T18:30:30.45Z",
25 | "uncertaintyInMilliseconds": 200
26 | }
27 | ]
28 | },
29 | "event": {
30 | "header": {
31 | "namespace": "Alexa",
32 | "name": "Response",
33 | "payloadVersion": "3",
34 | "messageId": "5f8a426e-01e4-4cc9-8b79-65f8bd0fd8a4",
35 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
36 | },
37 | "endpoint": {
38 | "scope": {
39 | "type": "BearerToken",
40 | "token": "access-token-from-Amazon"
41 | },
42 | "endpointId": "endpoint-001"
43 | },
44 | "payload": {}
45 | }
46 | }
--------------------------------------------------------------------------------
/sample_lambda/python/jsonschema/compat.py:
--------------------------------------------------------------------------------
1 | import operator
2 | import sys
3 |
4 |
5 | try:
6 | from collections import MutableMapping, Sequence # noqa
7 | except ImportError:
8 | from collections.abc import MutableMapping, Sequence # noqa
9 |
10 | PY3 = sys.version_info[0] >= 3
11 |
12 | if PY3:
13 | zip = zip
14 | from functools import lru_cache
15 | from io import StringIO
16 | from urllib.parse import (
17 | unquote, urljoin, urlunsplit, SplitResult, urlsplit as _urlsplit
18 | )
19 | from urllib.request import urlopen
20 | str_types = str,
21 | int_types = int,
22 | iteritems = operator.methodcaller("items")
23 | else:
24 | from itertools import izip as zip # noqa
25 | from StringIO import StringIO
26 | from urlparse import (
27 | urljoin, urlunsplit, SplitResult, urlsplit as _urlsplit # noqa
28 | )
29 | from urllib import unquote # noqa
30 | from urllib2 import urlopen # noqa
31 | str_types = basestring
32 | int_types = int, long
33 | iteritems = operator.methodcaller("iteritems")
34 |
35 | from functools32 import lru_cache
36 |
37 |
38 | # On python < 3.3 fragments are not handled properly with unknown schemes
39 | def urlsplit(url):
40 | scheme, netloc, path, query, fragment = _urlsplit(url)
41 | if "#" in path:
42 | path, fragment = path.split("#", 1)
43 | return SplitResult(scheme, netloc, path, query, fragment)
44 |
45 |
46 | def urldefrag(url):
47 | if "#" in url:
48 | s, n, p, q, frag = urlsplit(url)
49 | defrag = urlunsplit((s, n, p, q, ''))
50 | else:
51 | defrag = url
52 | frag = ''
53 | return defrag, frag
54 |
55 |
56 | # flake8: noqa
57 |
--------------------------------------------------------------------------------
/sample_backend/lambda/lambda_api/python/jsonschema/compat.py:
--------------------------------------------------------------------------------
1 | import operator
2 | import sys
3 |
4 |
5 | try:
6 | from collections import MutableMapping, Sequence # noqa
7 | except ImportError:
8 | from collections.abc import MutableMapping, Sequence # noqa
9 |
10 | PY3 = sys.version_info[0] >= 3
11 |
12 | if PY3:
13 | zip = zip
14 | from functools import lru_cache
15 | from io import StringIO
16 | from urllib.parse import (
17 | unquote, urljoin, urlunsplit, SplitResult, urlsplit as _urlsplit
18 | )
19 | from urllib.request import urlopen
20 | str_types = str,
21 | int_types = int,
22 | iteritems = operator.methodcaller("items")
23 | else:
24 | from itertools import izip as zip # noqa
25 | from StringIO import StringIO
26 | from urlparse import (
27 | urljoin, urlunsplit, SplitResult, urlsplit as _urlsplit # noqa
28 | )
29 | from urllib import unquote # noqa
30 | from urllib2 import urlopen # noqa
31 | str_types = basestring
32 | int_types = int, long
33 | iteritems = operator.methodcaller("iteritems")
34 |
35 | from functools32 import lru_cache
36 |
37 |
38 | # On python < 3.3 fragments are not handled properly with unknown schemes
39 | def urlsplit(url):
40 | scheme, netloc, path, query, fragment = _urlsplit(url)
41 | if "#" in path:
42 | path, fragment = path.split("#", 1)
43 | return SplitResult(scheme, netloc, path, query, fragment)
44 |
45 |
46 | def urldefrag(url):
47 | if "#" in url:
48 | s, n, p, q, frag = urlsplit(url)
49 | defrag = urlunsplit((s, n, p, q, ''))
50 | else:
51 | defrag = url
52 | frag = ''
53 | return defrag, frag
54 |
55 |
56 | # flake8: noqa
57 |
--------------------------------------------------------------------------------
/sample_backend/docs/008-setup-send-an-event.md:
--------------------------------------------------------------------------------
1 | # Step 8: Send an Event
2 | Send an external event into Alexa from the Endpoint Device backend. This simulates an external event on the endpoint that will need to be updated with Alexa.
3 |
4 |
5 | #### 8.1 Send a Proactive State Update
6 |
7 | 8.1.1 In Postman, select the **POST** _/events_ resource from the left menu.
8 |
9 | 8.1.2 Select the _Body_ tab and view the raw JSON. It should look like the following:
10 | ```
11 | {
12 | "event": {
13 | "endpoint": {
14 | "userId" : "0",
15 | "id": "black_switch",
16 | "state": "OFF",
17 | "type": "SWITCH"
18 | }
19 | }
20 | }
21 | ```
22 | 8.1.3 Update the JSON by replacing the `"userId"` "0" value with the [user_id] stored in the `config.txt` file. When edited, it should something like the following:
23 | ```
24 | {
25 | "event": {
26 | "endpoint": {
27 | "userId" : "amzn1.account.XXXXXXXXXXXXXXXXXXXXXXXXXXXX",
28 | "id": "black_switch",
29 | "state": "OFF",
30 | "type": "SWITCH"
31 | }
32 | }
33 | }
34 | ```
35 |
36 | 8.1.1 Click **Save** in the top right and then and then click the **Send** button.
37 |
38 | 8.1.2 Return to the [AWS IoT Things console](https://console.aws.amazon.com/iotv2/home?region=us-east-1#/thinghub) and note the _state_ value of the _black_switch_. The state should reflect the _"state"_ value passed in the body. For instance, if set to _"OFF"_, the _black_switch_ attribute _state_ will be set to _OFF_.
39 |
40 |
41 |
42 | ____
43 | Go to [Step 9: Clean Up](009-setup-cleanup.md).
--------------------------------------------------------------------------------
/sample_messages/ThermostatController/ThermostatController.SetTargetTemperature.SingleMode.response.json:
--------------------------------------------------------------------------------
1 | {
2 | "context": {
3 | "properties": [
4 | {
5 | "namespace": "Alexa.ThermostatController",
6 | "name": "targetSetpoint",
7 | "value": {
8 | "value": 25,
9 | "scale": "CELSIUS"
10 | },
11 | "timeOfSample": "2017-09-27T18:30:30.45Z",
12 | "uncertaintyInMilliseconds": 200
13 | },
14 | {
15 | "namespace": "Alexa.ThermostatController",
16 | "name": "thermostatMode",
17 | "value": "HEAT",
18 | "timeOfSample": "2017-09-27T18:30:30.45Z",
19 | "uncertaintyInMilliseconds": 200
20 | },
21 | {
22 | "namespace": "Alexa.EndpointHealth",
23 | "name": "connectivity",
24 | "value": {
25 | "value": "OK"
26 | },
27 | "timeOfSample": "2017-09-27T18:30:30.45Z",
28 | "uncertaintyInMilliseconds": 200
29 | }
30 | ]
31 | },
32 | "event": {
33 | "header": {
34 | "namespace": "Alexa",
35 | "name": "Response",
36 | "payloadVersion": "3",
37 | "messageId": "5f8a426e-01e4-4cc9-8b79-65f8bd0fd8a4",
38 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
39 | },
40 | "endpoint": {
41 | "scope": {
42 | "type": "BearerToken",
43 | "token": "access-token-from-Amazon"
44 | },
45 | "endpointId": "endpoint-001"
46 | },
47 | "payload": {}
48 | }
49 | }
--------------------------------------------------------------------------------
/sample_messages/ChangeReport/ChangeReport.json:
--------------------------------------------------------------------------------
1 | {
2 | "context": {
3 | "properties": [
4 | {
5 | "namespace": "Alexa.BrightnessController",
6 | "name": "brightness",
7 | "value": 85,
8 | "timeOfSample": "2017-09-27T18:30:30.45Z",
9 | "uncertaintyInMilliseconds": 200
10 | },
11 | {
12 | "namespace": "Alexa.EndpointHealth",
13 | "name": "connectivity",
14 | "value": {
15 | "value": "OK"
16 | },
17 | "timeOfSample": "2017-09-27T18:30:30.45Z",
18 | "uncertaintyInMilliseconds": 200
19 | }
20 | ]
21 | },
22 | "event": {
23 | "header": {
24 | "namespace": "Alexa",
25 | "name": "ChangeReport",
26 | "payloadVersion": "3",
27 | "messageId": "5f8a426e-01e4-4cc9-8b79-65f8bd0fd8a4"
28 | },
29 | "endpoint": {
30 | "scope": {
31 | "type": "BearerToken",
32 | "token": "access-token-from-Amazon"
33 | },
34 | "endpointId": "endpoint-001"
35 | },
36 | "payload": {
37 | "change": {
38 | "cause": {
39 | "type": "PHYSICAL_INTERACTION"
40 | },
41 | "properties": [
42 | {
43 | "namespace": "Alexa.PowerController",
44 | "name": "powerState",
45 | "value": "ON",
46 | "timeOfSample": "2017-09-27T18:30:30.45Z",
47 | "uncertaintyInMilliseconds": 200
48 | }
49 | ]
50 | }
51 | }
52 | }
53 | }
--------------------------------------------------------------------------------
/sample_backend/lambda/lambda_api/python/alexa/skills/smarthome/alexa_acceptgrant_response.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
4 | #
5 | # Licensed under the Amazon Software License (the "License"). You may not use this file except in
6 | # compliance with the License. A copy of the License is located at
7 | #
8 | # http://aws.amazon.com/asl/
9 | #
10 | # or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific
12 | # language governing permissions and limitations under the License.
13 |
14 | import uuid
15 |
16 |
17 | class AlexaAcceptGrantResponse:
18 |
19 | def __init__(self, **kwargs):
20 |
21 | self.namespace = kwargs.get('namespace', 'Alexa.Authorization')
22 | self.name = kwargs.get('name', 'AcceptGrant.Response')
23 | self.payload_version = kwargs.get('payload_version', "3")
24 | self.message_id = kwargs.get('message_id', str(uuid.uuid4()))
25 | self.type = kwargs.get('type', None)
26 | self.message = kwargs.get('message', None)
27 |
28 | def get_response(self):
29 | response = {}
30 |
31 | header = {}
32 | header['namespace'] = self.namespace
33 | header['name'] = self.name
34 | header['payloadVersion'] = self.payload_version
35 | header['messageId'] = self.message_id
36 |
37 | response['event'] = {}
38 | response['event']['header'] = header
39 | response['event']['payload'] = {}
40 |
41 | if self.type and self.message:
42 | response['event']['payload']['type'] = self.type
43 | response['event']['payload']['message'] = self.message
44 |
45 | return response
46 |
--------------------------------------------------------------------------------
/sample_lambda/python/validation.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
4 | #
5 | # Licensed under the Amazon Software License (the "License"). You may not use this file except in
6 | # compliance with the License. A copy of the License is located at
7 | #
8 | # http://aws.amazon.com/asl/
9 | #
10 | # or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific
12 | # language governing permissions and limitations under the License.
13 |
14 | """Alexa Smart Home Validation Package Sample Code.
15 |
16 | This module is used by Alexa Smart Home skills to validate their Lambda responses and async
17 | messages before sending them back to Alexa. If an error is found, an exception is thrown so that
18 | the developer can catch the error and do something about it, instead of sending it back to Alexa
19 | and causing an error on the Alexa side.
20 |
21 | This specific package uses the jsonschema (https://github.com/Julian/jsonschema) Python implementation
22 | of the JSON Schema Draft 4 to perform the actual validation against the validation schema.
23 |
24 | """
25 |
26 | import json
27 |
28 | from jsonschema import validate
29 |
30 | def validate_message(request, response):
31 |
32 | # update below with path to your validation schema
33 | # this path works if you copy the latest validation schema into the same directory as this file
34 | # validation schema: https://github.com/alexa/alexa-smarthome/wiki/Validation-Schema
35 | path_to_validation_schema = "alexa_smart_home_message_schema.json"
36 |
37 | with open(path_to_validation_schema) as json_file:
38 | schema = json.load(json_file)
39 | validate(response, schema)
40 |
--------------------------------------------------------------------------------
/sample_messages/StateReport/StateReport.json:
--------------------------------------------------------------------------------
1 | {
2 | "context": {
3 | "properties": [
4 | {
5 | "namespace": "Alexa.EndpointHealth",
6 | "name": "connectivity",
7 | "value": {
8 | "value": "OK"
9 | },
10 | "timeOfSample": "2017-09-27T18:30:30.45Z",
11 | "uncertaintyInMilliseconds": 200
12 | },
13 | {
14 | "name": "targetSetpoint",
15 | "namespace": "Alexa.ThermostatController",
16 | "value": {
17 | "scale": "CELSIUS",
18 | "value": 25
19 | },
20 | "timeOfSample": "2017-09-27T18:30:30.45Z",
21 | "uncertaintyInMilliseconds": 200
22 | },
23 | {
24 | "name": "thermostatMode",
25 | "namespace": "Alexa.ThermostatController",
26 | "value": "AUTO",
27 | "timeOfSample": "2017-09-27T18:30:30.45Z",
28 | "uncertaintyInMilliseconds": 200
29 | },
30 | {
31 | "name": "temperature",
32 | "namespace": "Alexa.TemperatureSensor",
33 | "value": {
34 | "scale": "CELSIUS",
35 | "value": 20
36 | },
37 | "timeOfSample": "2017-09-27T18:30:30.45Z",
38 | "uncertaintyInMilliseconds": 200
39 | }
40 | ]
41 | },
42 | "event": {
43 | "header": {
44 | "namespace": "Alexa",
45 | "name": "StateReport",
46 | "payloadVersion": "3",
47 | "messageId": "5f8a426e-01e4-4cc9-8b79-65f8bd0fd8a4",
48 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
49 | },
50 | "endpoint": {
51 | "scope": {
52 | "type": "BearerToken",
53 | "token": "access-token-from-Amazon"
54 | },
55 | "endpointId": "endpoint-001"
56 | },
57 | "payload": {}
58 | }
59 | }
--------------------------------------------------------------------------------
/sample_backend/lambda/lambda_api/python/alexa/skills/smarthome/alexa_response.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
4 | #
5 | # Licensed under the Amazon Software License (the "License"). You may not use this file except in
6 | # compliance with the License. A copy of the License is located at
7 | #
8 | # http://aws.amazon.com/asl/
9 | #
10 | # or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific
12 | # language governing permissions and limitations under the License.
13 |
14 | import uuid
15 |
16 |
17 | class AlexaResponse:
18 |
19 | def __init__(self, **kwargs):
20 | self.message_id = str(uuid.uuid4())
21 | self.name = 'Response'
22 | self.payload_version = kwargs.get('payload_version', '3')
23 | self.correlation_token = kwargs.get('correlation_token', None)
24 | self.endpoint_id = kwargs.get('endpoint_id', None)
25 | self.token = kwargs.get('token', None)
26 |
27 | def get_response(self):
28 | response = {}
29 | event = {}
30 |
31 | header = {}
32 | header['namespace'] = 'Alexa'
33 | header['name'] = self.name
34 | header['payloadVersion'] = self.payload_version
35 | header['messageId'] = self.message_id
36 | if self.correlation_token:
37 | header["correlationToken"] = self.correlation_token
38 |
39 | endpoint = {}
40 | if not self.endpoint_id:
41 | self.endpoint_id = "INVALID"
42 | endpoint['endpointId'] = self.endpoint_id
43 |
44 | # If this is an asynchronous response, include the token
45 | if self.token:
46 | scope = {}
47 | scope['type'] = 'BearerToken'
48 | scope['type'] = self.token
49 | endpoint['scope'] = scope
50 |
51 | event['header'] = header
52 | event['endpoint'] = endpoint
53 | event['payload'] = {}
54 |
55 | response['event'] = event
56 |
57 | return response
58 |
--------------------------------------------------------------------------------
/sample_messages/ThermostatController/ThermostatController.SetTargetTemperature.DualMode.response.json:
--------------------------------------------------------------------------------
1 | {
2 | "context": {
3 | "properties": [
4 | {
5 | "namespace": "Alexa.ThermostatController",
6 | "name": "lowerSetpoint",
7 | "value": {
8 | "value": 68.0,
9 | "scale": "FAHRENHEIT"
10 | },
11 | "timeOfSample": "2017-09-27T18:30:30.45Z",
12 | "uncertaintyInMilliseconds": 200
13 | },
14 | {
15 | "namespace": "Alexa.ThermostatController",
16 | "name": "upperSetpoint",
17 | "value": {
18 | "value": 74.0,
19 | "scale": "FAHRENHEIT"
20 | },
21 | "timeOfSample": "2017-09-27T18:30:30.45Z",
22 | "uncertaintyInMilliseconds": 200
23 | },
24 | {
25 | "namespace": "Alexa.ThermostatController",
26 | "name": "thermostatMode",
27 | "value": "AUTO",
28 | "timeOfSample": "2017-09-27T18:30:30.45Z",
29 | "uncertaintyInMilliseconds": 200
30 | },
31 | {
32 | "namespace": "Alexa.EndpointHealth",
33 | "name": "connectivity",
34 | "value": {
35 | "value": "OK"
36 | },
37 | "timeOfSample": "2017-09-27T18:30:30.45Z",
38 | "uncertaintyInMilliseconds": 200
39 | }
40 | ]
41 | },
42 | "event": {
43 | "header": {
44 | "namespace": "Alexa",
45 | "name": "Response",
46 | "payloadVersion": "3",
47 | "messageId": "5f8a426e-01e4-4cc9-8b79-65f8bd0fd8a4",
48 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
49 | },
50 | "endpoint": {
51 | "scope": {
52 | "type": "BearerToken",
53 | "token": "access-token-from-Amazon"
54 | },
55 | "endpointId": "endpoint-001"
56 | },
57 | "payload": {}
58 | }
59 | }
--------------------------------------------------------------------------------
/sample_backend/lambda/lambda_api/python/endpoint_cloud/api_auth.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
4 | #
5 | # Licensed under the Amazon Software License (the "License"). You may not use this file except in
6 | # compliance with the License. A copy of the License is located at
7 | #
8 | # http://aws.amazon.com/asl/
9 | #
10 | # or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific
12 | # language governing permissions and limitations under the License.
13 |
14 | import http.client
15 | from urllib.parse import urlencode
16 |
17 |
18 | class ApiAuth:
19 |
20 | def post_to_api(self, payload):
21 | connection = http.client.HTTPSConnection("api.amazon.com")
22 | headers = {
23 | 'content-type': "application/x-www-form-urlencoded",
24 | 'cache-control': "no-cache"
25 | }
26 | connection.request('POST', '/auth/o2/token', urlencode(payload), headers)
27 | return connection.getresponse()
28 |
29 | def get_access_token(self, code, client_id, client_secret, redirect_uri):
30 | payload = {
31 | 'grant_type': 'authorization_code',
32 | 'code': code,
33 | 'client_id': client_id,
34 | 'client_secret': client_secret,
35 | 'redirect_uri': redirect_uri
36 | }
37 | return self.post_to_api(payload)
38 |
39 | @staticmethod
40 | def get_user_id(access_token):
41 | connection = http.client.HTTPSConnection('api.amazon.com')
42 | connection.request('GET', '/user/profile?access_token=' + access_token)
43 | return connection.getresponse()
44 |
45 | def refresh_access_token(self, refresh_token, client_id, client_secret, redirect_uri):
46 | payload = {
47 | 'grant_type': 'refresh_token',
48 | 'refresh_token': refresh_token,
49 | 'client_id': client_id,
50 | 'client_secret': client_secret,
51 | 'redirect_uri': redirect_uri
52 | }
53 | return self.post_to_api(payload)
54 |
55 |
56 |
--------------------------------------------------------------------------------
/sample_backend/docs/007-setup-test-endpoints.md:
--------------------------------------------------------------------------------
1 | # Step 7: Test the Endpoints
2 | Now that the environment is in place and a test device has been created, test out the endpoints.
3 |
4 |
5 | #### Step 7.1 Discover devices via a voice command
6 |
7 | 7.1.1 Browse to https://echosim.io and login with your Amazon account.
8 |
9 | 7.1.2 Activate echosim.io by clicking and holding the speaker icon and give the command "Discover Devices". Alexa should respond with "Starting Discovery..." with a description of the discovery process.
10 |
11 | 7.1.3 Return to https://alexa.amazon.com/ and select **Smart Home** from the left menu and then select **Devices** in the main window.
12 |
13 | 7.1.4 From the list of devices, not the inclusion of a **black switch** with a description of _Endpoint Description_ in the list of _Devices_.
14 |
15 | > Note If you are using an Echo device to issue a "Discover Devices" command, once discovery is complete, Alexa should respond "I found one new switch called black switch." or possibly with additional devices discovered. Note the addition of a _black switch_ device to your list of devices.
16 |
17 | > Optionally, for device discovery, you can browse to https://alexa.amazon.com/spa/index.html#appliances and click the **Discover** button at the bottom of the page.
18 |
19 | #### Step 7.2 Send at Turn On voice command
20 |
21 | 7.2.1 Activate echosim and give the command "Turn on Black Switch". Alexa should respond with "OK".
22 |
23 | 7.2.2 Return to the [AWS IoT Things console](https://console.aws.amazon.com/iotv2/home?region=us-east-1#/thinghub) and click on the `black_switch` Thing instance to inspect its attributes.
24 |
25 | 7.2.3 That `black_switch` Thing should reflect the state of the "ON" or "OFF" power voice command when the Thing is refreshed.
26 |
27 |
28 |
29 |
30 | ____
31 | Go to [Step 8: Send an Event](008-setup-send-an-event.md).
32 |
--------------------------------------------------------------------------------
/sample_backend/lambda/lambda_api/python/alexa/skills/smarthome/alexa_error.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
4 | #
5 | # Licensed under the Amazon Software License (the "License"). You may not use this file except in
6 | # compliance with the License. A copy of the License is located at
7 | #
8 | # http://aws.amazon.com/asl/
9 | #
10 | # or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific
12 | # language governing permissions and limitations under the License.
13 |
14 | import uuid
15 |
16 |
17 | class AlexaError:
18 |
19 | def __init__(self, **kwargs):
20 | self.message_id = str(uuid.uuid4())
21 | self.name = 'ErrorResponse'
22 | self.payload_version = kwargs.get('payload_version', '3')
23 | self.correlation_token = kwargs.get('correlation_token', None)
24 | self.endpoint_id = kwargs.get('endpoint_id', None)
25 | self.token = kwargs.get('token', None)
26 | self.type = kwargs.get('type', 'INTERNAL_ERROR')
27 | self.message = kwargs.get('message', 'An Internal Error has occurred')
28 |
29 | def get_response(self):
30 | response = {}
31 | event = {}
32 |
33 | header = {}
34 | header['namespace'] = 'Alexa'
35 | header['name'] = self.name
36 | header['payloadVersion'] = self.payload_version
37 | header['messageId'] = self.message_id
38 | if self.correlation_token:
39 | header["correlationToken"] = self.correlation_token
40 |
41 | endpoint = {}
42 | if not self.endpoint_id:
43 | self.endpoint_id = "INVALID"
44 | endpoint['endpointId'] = self.endpoint_id
45 |
46 | # If this is an asynchronous response, include the token
47 | if self.token:
48 | scope = {}
49 | scope['type'] = 'BearerToken'
50 | scope['type'] = self.token
51 | endpoint['scope'] = scope
52 |
53 | payload = {}
54 | payload['type'] = self.type
55 | payload['message'] = self.message
56 |
57 | event['header'] = header
58 | event['endpoint'] = endpoint
59 | event['payload'] = payload
60 |
61 | response['event'] = event
62 |
63 | return response
64 |
--------------------------------------------------------------------------------
/sample_messages/CameraStreamController/CameraStreamController.response.json:
--------------------------------------------------------------------------------
1 | {
2 | "context": {
3 | "properties": [
4 | {
5 | "namespace": "Alexa.EndpointHealth",
6 | "name": "connectivity",
7 | "value": {
8 | "value": "OK"
9 | },
10 | "timeOfSample": "2017-09-27T18:30:30.45Z",
11 | "uncertaintyInMilliseconds": 200
12 | }
13 | ]
14 | },
15 | "event": {
16 | "header": {
17 | "namespace": "Alexa.CameraStreamController",
18 | "name": "Response",
19 | "payloadVersion": "3",
20 | "messageId": "5f8a426e-01e4-4cc9-8b79-65f8bd0fd8a4",
21 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
22 | },
23 | "endpoint": {
24 | "scope": {
25 | "type": "BearerToken",
26 | "token": "access-token-from-Amazon"
27 | },
28 | "endpointId": "endpoint-001"
29 | },
30 | "payload": {
31 | "cameraStreams": [
32 | {
33 | "uri": "rtsp://username:password@link.to.video:443/feed1.mp4",
34 | "expirationTime": "2017-09-27T20:30:30.45Z",
35 | "idleTimeoutSeconds": 30,
36 | "protocol": "RTSP",
37 | "resolution": {
38 | "width": 1920,
39 | "height": 1080
40 | },
41 | "authorizationType": "BASIC",
42 | "videoCodec": "H264",
43 | "audioCodec": "AAC"
44 | },
45 | {
46 | "uri": "rtsp://username:password@link.to.video:443/feed2.mp4",
47 | "expirationTime": "2017-09-27T20:30:30.45Z",
48 | "idleTimeoutSeconds": 60,
49 | "protocol": "RTSP",
50 | "resolution": {
51 | "width": 1280,
52 | "height": 720
53 | },
54 | "authorizationType": "DIGEST",
55 | "videoCodec": "MPEG2",
56 | "audioCodec": "G711"
57 | }
58 | ],
59 | "imageUri": "https://username:password@link.to.image/image.jpg"
60 | }
61 | }
62 | }
--------------------------------------------------------------------------------
/sample_lambda/python/jsonschema/tests/test_format.py:
--------------------------------------------------------------------------------
1 | """
2 | Tests for the parts of jsonschema related to the :validator:`format` property.
3 |
4 | """
5 |
6 | from jsonschema.tests.compat import mock, unittest
7 |
8 | from jsonschema import FormatError, ValidationError, FormatChecker
9 | from jsonschema.validators import Draft4Validator
10 |
11 |
12 | class TestFormatChecker(unittest.TestCase):
13 | def setUp(self):
14 | self.fn = mock.Mock()
15 |
16 | def test_it_can_validate_no_formats(self):
17 | checker = FormatChecker(formats=())
18 | self.assertFalse(checker.checkers)
19 |
20 | def test_it_raises_a_key_error_for_unknown_formats(self):
21 | with self.assertRaises(KeyError):
22 | FormatChecker(formats=["o noes"])
23 |
24 | def test_it_can_register_cls_checkers(self):
25 | with mock.patch.dict(FormatChecker.checkers, clear=True):
26 | FormatChecker.cls_checks("new")(self.fn)
27 | self.assertEqual(FormatChecker.checkers, {"new": (self.fn, ())})
28 |
29 | def test_it_can_register_checkers(self):
30 | checker = FormatChecker()
31 | checker.checks("new")(self.fn)
32 | self.assertEqual(
33 | checker.checkers,
34 | dict(FormatChecker.checkers, new=(self.fn, ()))
35 | )
36 |
37 | def test_it_catches_registered_errors(self):
38 | checker = FormatChecker()
39 | cause = self.fn.side_effect = ValueError()
40 |
41 | checker.checks("foo", raises=ValueError)(self.fn)
42 |
43 | with self.assertRaises(FormatError) as cm:
44 | checker.check("bar", "foo")
45 |
46 | self.assertIs(cm.exception.cause, cause)
47 | self.assertIs(cm.exception.__cause__, cause)
48 |
49 | # Unregistered errors should not be caught
50 | self.fn.side_effect = AttributeError
51 | with self.assertRaises(AttributeError):
52 | checker.check("bar", "foo")
53 |
54 | def test_format_error_causes_become_validation_error_causes(self):
55 | checker = FormatChecker()
56 | checker.checks("foo", raises=ValueError)(self.fn)
57 | cause = self.fn.side_effect = ValueError()
58 | validator = Draft4Validator({"format": "foo"}, format_checker=checker)
59 |
60 | with self.assertRaises(ValidationError) as cm:
61 | validator.validate("bar")
62 |
63 | self.assertIs(cm.exception.__cause__, cause)
64 |
--------------------------------------------------------------------------------
/sample_backend/lambda/lambda_api/python/jsonschema/tests/test_format.py:
--------------------------------------------------------------------------------
1 | """
2 | Tests for the parts of jsonschema related to the :validator:`format` property.
3 |
4 | """
5 |
6 | from jsonschema.tests.compat import mock, unittest
7 |
8 | from jsonschema import FormatError, ValidationError, FormatChecker
9 | from jsonschema.validators import Draft4Validator
10 |
11 |
12 | class TestFormatChecker(unittest.TestCase):
13 | def setUp(self):
14 | self.fn = mock.Mock()
15 |
16 | def test_it_can_validate_no_formats(self):
17 | checker = FormatChecker(formats=())
18 | self.assertFalse(checker.checkers)
19 |
20 | def test_it_raises_a_key_error_for_unknown_formats(self):
21 | with self.assertRaises(KeyError):
22 | FormatChecker(formats=["o noes"])
23 |
24 | def test_it_can_register_cls_checkers(self):
25 | with mock.patch.dict(FormatChecker.checkers, clear=True):
26 | FormatChecker.cls_checks("new")(self.fn)
27 | self.assertEqual(FormatChecker.checkers, {"new": (self.fn, ())})
28 |
29 | def test_it_can_register_checkers(self):
30 | checker = FormatChecker()
31 | checker.checks("new")(self.fn)
32 | self.assertEqual(
33 | checker.checkers,
34 | dict(FormatChecker.checkers, new=(self.fn, ()))
35 | )
36 |
37 | def test_it_catches_registered_errors(self):
38 | checker = FormatChecker()
39 | cause = self.fn.side_effect = ValueError()
40 |
41 | checker.checks("foo", raises=ValueError)(self.fn)
42 |
43 | with self.assertRaises(FormatError) as cm:
44 | checker.check("bar", "foo")
45 |
46 | self.assertIs(cm.exception.cause, cause)
47 | self.assertIs(cm.exception.__cause__, cause)
48 |
49 | # Unregistered errors should not be caught
50 | self.fn.side_effect = AttributeError
51 | with self.assertRaises(AttributeError):
52 | checker.check("bar", "foo")
53 |
54 | def test_format_error_causes_become_validation_error_causes(self):
55 | checker = FormatChecker()
56 | checker.checks("foo", raises=ValueError)(self.fn)
57 | cause = self.fn.side_effect = ValueError()
58 | validator = Draft4Validator({"format": "foo"}, format_checker=checker)
59 |
60 | with self.assertRaises(ValidationError) as cm:
61 | validator.validate("bar")
62 |
63 | self.assertIs(cm.exception.__cause__, cause)
64 |
--------------------------------------------------------------------------------
/sample_backend/lambda/lambda_smarthome/python/index.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
4 | #
5 | # Licensed under the Amazon Software License (the "License"). You may not use this file except in
6 | # compliance with the License. A copy of the License is located at
7 | #
8 | # http://aws.amazon.com/asl/
9 | #
10 | # or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific
12 | # language governing permissions and limitations under the License.
13 |
14 | import json
15 | import os
16 | import urllib.request
17 | from urllib.request import HTTPError
18 |
19 |
20 | def get_api_url(api_id, aws_region, resource):
21 | return 'https://{0}.execute-api.{1}.amazonaws.com/prod/{2}'.format(api_id, aws_region, resource)
22 |
23 |
24 | def handler(request, context):
25 | try:
26 | print("LOG skill.index.handler.request:", request)
27 |
28 | # Get the Environment Variables, these are used to dynamically compose the API URI
29 |
30 | # Get the Region
31 | env_aws_default_region = os.environ.get('AWS_DEFAULT_REGION', None)
32 | if env_aws_default_region is None:
33 | print("ERROR skill.index.handler.aws_default_region is None default to us-east-1")
34 | env_aws_default_region = 'us-east-1'
35 |
36 | # Get the API ID
37 | env_api_id = os.environ.get('api_id', None)
38 | if env_api_id is None:
39 | print("ERROR skill.index.handler.env_api_id is None")
40 | return '{}'
41 |
42 | # Pass the requested directive to the backend Endpoint API
43 | url = get_api_url(env_api_id, env_aws_default_region, 'directives')
44 | data = bytes(json.dumps(request), encoding="utf-8")
45 | headers = {'Content-Type': 'application/json'}
46 | req = urllib.request.Request(url, data, headers)
47 | result = urllib.request.urlopen(req).read().decode("utf-8")
48 | response = json.loads(result)
49 | print("LOG skill.index.handler.response:", response)
50 | return response
51 |
52 | except HTTPError as error:
53 | print("ERROR skill.index.handler.error:", error)
54 | return error
55 |
56 | except ValueError as error:
57 | print("ERROR skill.index.handler.error:", error)
58 | return error
59 |
--------------------------------------------------------------------------------
/sample_lambda/python/jsonschema/cli.py:
--------------------------------------------------------------------------------
1 | from __future__ import absolute_import
2 | import argparse
3 | import json
4 | import sys
5 |
6 | from jsonschema._reflect import namedAny
7 | from jsonschema.validators import validator_for
8 |
9 |
10 | def _namedAnyWithDefault(name):
11 | if "." not in name:
12 | name = "jsonschema." + name
13 | return namedAny(name)
14 |
15 |
16 | def _json_file(path):
17 | with open(path) as file:
18 | return json.load(file)
19 |
20 |
21 | parser = argparse.ArgumentParser(
22 | description="JSON Schema Validation CLI",
23 | )
24 | parser.add_argument(
25 | "-i", "--instance",
26 | action="append",
27 | dest="instances",
28 | type=_json_file,
29 | help=(
30 | "a path to a JSON instance (i.e. filename.json)"
31 | "to validate (may be specified multiple times)"
32 | ),
33 | )
34 | parser.add_argument(
35 | "-F", "--error-format",
36 | default="{error.instance}: {error.message}\n",
37 | help=(
38 | "the format to use for each error output message, specified in "
39 | "a form suitable for passing to str.format, which will be called "
40 | "with 'error' for each error"
41 | ),
42 | )
43 | parser.add_argument(
44 | "-V", "--validator",
45 | type=_namedAnyWithDefault,
46 | help=(
47 | "the fully qualified object name of a validator to use, or, for "
48 | "validators that are registered with jsonschema, simply the name "
49 | "of the class."
50 | ),
51 | )
52 | parser.add_argument(
53 | "schema",
54 | help="the JSON Schema to validate with (i.e. filename.schema)",
55 | type=_json_file,
56 | )
57 |
58 |
59 | def parse_args(args):
60 | arguments = vars(parser.parse_args(args=args or ["--help"]))
61 | if arguments["validator"] is None:
62 | arguments["validator"] = validator_for(arguments["schema"])
63 | return arguments
64 |
65 |
66 | def main(args=sys.argv[1:]):
67 | sys.exit(run(arguments=parse_args(args=args)))
68 |
69 |
70 | def run(arguments, stdout=sys.stdout, stderr=sys.stderr):
71 | error_format = arguments["error_format"]
72 | validator = arguments["validator"](schema=arguments["schema"])
73 |
74 | validator.check_schema(arguments["schema"])
75 |
76 | errored = False
77 | for instance in arguments["instances"] or ():
78 | for error in validator.iter_errors(instance):
79 | stderr.write(error_format.format(error=error))
80 | errored = True
81 | return errored
82 |
--------------------------------------------------------------------------------
/sample_backend/lambda/lambda_api/python/jsonschema/cli.py:
--------------------------------------------------------------------------------
1 | from __future__ import absolute_import
2 | import argparse
3 | import json
4 | import sys
5 |
6 | from jsonschema._reflect import namedAny
7 | from jsonschema.validators import validator_for
8 |
9 |
10 | def _namedAnyWithDefault(name):
11 | if "." not in name:
12 | name = "jsonschema." + name
13 | return namedAny(name)
14 |
15 |
16 | def _json_file(path):
17 | with open(path) as file:
18 | return json.load(file)
19 |
20 |
21 | parser = argparse.ArgumentParser(
22 | description="JSON Schema Validation CLI",
23 | )
24 | parser.add_argument(
25 | "-i", "--instance",
26 | action="append",
27 | dest="instances",
28 | type=_json_file,
29 | help=(
30 | "a path to a JSON instance (i.e. filename.json)"
31 | "to validate (may be specified multiple times)"
32 | ),
33 | )
34 | parser.add_argument(
35 | "-F", "--error-format",
36 | default="{error.instance}: {error.message}\n",
37 | help=(
38 | "the format to use for each error output message, specified in "
39 | "a form suitable for passing to str.format, which will be called "
40 | "with 'error' for each error"
41 | ),
42 | )
43 | parser.add_argument(
44 | "-V", "--validator",
45 | type=_namedAnyWithDefault,
46 | help=(
47 | "the fully qualified object name of a validator to use, or, for "
48 | "validators that are registered with jsonschema, simply the name "
49 | "of the class."
50 | ),
51 | )
52 | parser.add_argument(
53 | "schema",
54 | help="the JSON Schema to validate with (i.e. filename.schema)",
55 | type=_json_file,
56 | )
57 |
58 |
59 | def parse_args(args):
60 | arguments = vars(parser.parse_args(args=args or ["--help"]))
61 | if arguments["validator"] is None:
62 | arguments["validator"] = validator_for(arguments["schema"])
63 | return arguments
64 |
65 |
66 | def main(args=sys.argv[1:]):
67 | sys.exit(run(arguments=parse_args(args=args)))
68 |
69 |
70 | def run(arguments, stdout=sys.stdout, stderr=sys.stderr):
71 | error_format = arguments["error_format"]
72 | validator = arguments["validator"](schema=arguments["schema"])
73 |
74 | validator.check_schema(arguments["schema"])
75 |
76 | errored = False
77 | for instance in arguments["instances"] or ():
78 | for error in validator.iter_errors(instance):
79 | stderr.write(error_format.format(error=error))
80 | errored = True
81 | return errored
82 |
--------------------------------------------------------------------------------
/sample_messages/ThermostatController/ThermostatController.SetTargetTemperature.TripleMode.response.json:
--------------------------------------------------------------------------------
1 | {
2 | "context": {
3 | "properties": [
4 | {
5 | "namespace": "Alexa.ThermostatController",
6 | "name": "lowerSetpoint",
7 | "value": {
8 | "value": 68.0,
9 | "scale": "FAHRENHEIT"
10 | },
11 | "timeOfSample": "2017-09-27T18:30:30.45Z",
12 | "uncertaintyInMilliseconds": 200
13 | },
14 | {
15 | "namespace": "Alexa.ThermostatController",
16 | "name": "targetSetpoint",
17 | "value": {
18 | "value": 72.0,
19 | "scale": "FAHRENHEIT"
20 | },
21 | "timeOfSample": "2017-09-27T18:30:30.45Z",
22 | "uncertaintyInMilliseconds": 200
23 | },
24 | {
25 | "namespace": "Alexa.ThermostatController",
26 | "name": "upperSetpoint",
27 | "value": {
28 | "value": 76.0,
29 | "scale": "FAHRENHEIT"
30 | },
31 | "timeOfSample": "2017-09-27T18:30:30.45Z",
32 | "uncertaintyInMilliseconds": 200
33 | },
34 | {
35 | "namespace": "Alexa.ThermostatController",
36 | "name": "thermostatMode",
37 | "value": "AUTO",
38 | "timeOfSample": "2017-09-27T18:30:30.45Z",
39 | "uncertaintyInMilliseconds": 200
40 | },
41 | {
42 | "namespace": "Alexa.EndpointHealth",
43 | "name": "connectivity",
44 | "value": {
45 | "value": "OK"
46 | },
47 | "timeOfSample": "2017-09-27T18:30:30.45Z",
48 | "uncertaintyInMilliseconds": 200
49 | }
50 | ]
51 | },
52 | "event": {
53 | "header": {
54 | "namespace": "Alexa",
55 | "name": "Response",
56 | "payloadVersion": "3",
57 | "messageId": "5f8a426e-01e4-4cc9-8b79-65f8bd0fd8a4",
58 | "correlationToken": "dFMb0z+PgpgdDmluhJ1LddFvSqZ/jCc8ptlAKulUj90jSqg=="
59 | },
60 | "endpoint": {
61 | "scope": {
62 | "type": "BearerToken",
63 | "token": "access-token-from-Amazon"
64 | },
65 | "endpointId": "endpoint-001"
66 | },
67 | "payload": {}
68 | }
69 | }
--------------------------------------------------------------------------------
/sample_backend/docs/000-setup-requirements.md:
--------------------------------------------------------------------------------
1 | # Step 0: Set Up the Required Accounts
2 | These are the required accounts needed for working through these instructions.
3 |
4 | #### 0.1 Verify the Required Accounts
5 | To work with these instructions, you will need both an Amazon Developer account and an Amazon Web Services account.
6 |
7 | ##### Amazon Developer Account
8 | Go to [https://developer.amazon.com](https://developer.amazon.com) and establish an account if you do not already have one.
9 |
10 | ##### Amazon Web Services Account
11 | Go to [https://aws.amazon.com](https://aws.amazon.com) and register for an Amazon Web Services (AWS) account if you do not already have one.
12 |
13 | #### 0.2 Open a Configuration File
14 | This configuration file is useful to store temporary IDs and other values during configuration of the environment.
15 |
16 | 0.2.1 Create a folder on your Desktop called `Alexa-SmartHome-Sample`.
17 |
18 | 0.2.2 Download https://raw.githubusercontent.com/alexa/alexa-smarthome/master/sample_backend/docs/config.txt into the `Alexa-SmartHome-Sample` folder.
19 |
20 | 0.2.3 Open and review the `config.txt` file:
21 | ```
22 | Set in Step 1 - A unique API ID and name for the Alexa Smart Home Skill Lambda function
23 | [EndpointApiId]
24 | XXXXXXXXXX
25 |
26 | [SkillLambdaArn]
27 | arn:aws:lambda:us-east-1:XXXXXXXXXXXX:function:SampleSkillAdapter
28 |
29 | Set in Step 2 - The Client ID and Secret from the LWA Security Profile
30 | [Login with Amazon Client ID]
31 | amzn1.application-oa2-client.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
32 |
33 | [Login with Amazon Client Secret]
34 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
35 |
36 | Set in Step 3 - A unique ID for the Alexa Smart Home Skill
37 | [Alexa Skill Application Id]
38 | amzn1.ask.skill.XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
39 |
40 | Set in Step 4 - The Messaging Client and Secret from the Alexa Smart Home Skill
41 | [Alexa Skill Messaging Client Id]
42 | amzn1.application-oa2-client.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
43 |
44 | [Alexa Skill Messaging Client Secret]
45 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
46 |
47 | Set in Step 6 - The profile user_id of the User
48 | [user_id]
49 | amzn1.account.XXXXXXXXXXXXXXXXXXXXXXXXXXXX
50 | ```
51 | These placeholders represent the configuration entities to be collected or created for the environment.
52 |
53 | > TIP Keep secrets safe. If a Client Secret is compromised or needs to be reset, you will have to discard the secret and regenerate the Client ID and Secret again or recreate the profile. This will immediately sever the existing access relationships and customers will have to re-authenticate or re-link their account or skill.
54 |
55 |
56 |
57 | ____
58 | Go to [Step 1: Set Up the Smart Home Skill Backend](001-setup-create-backend.md).
59 |
--------------------------------------------------------------------------------
/sample_backend/docs/001-setup-create-backend.md:
--------------------------------------------------------------------------------
1 | # Step 1: Set Up the Smart Home Skill Backend
2 | These instructions create the backend services needed by the Smart Home Skill using a Cloud Formation stack.
3 |
4 | #### 1.1 Create the Backend Stack
5 |
6 | 1.1.1 Navigate to the Cloud Formation Console at https://console.aws.amazon.com/cloudformation/home?region=us-east-1 and authenticate using your AWS account.
7 |
8 | 1.1.2 Verify you are in the N. Virginia (us-east-1) region and click the **Create Stack** button in the top left of the page.
9 |
10 | 1.1.3 In the _Select Template_ section, select the **Specify an Amazon S3 template URL** radio button and enter the following URL `https://s3.amazonaws.com/endpoint-code-us/backend.template.us` into the text field.
11 |
12 | 1.1.4 Click **Next** on the bottom right of the page.
13 |
14 | 1.1.5 On the _Specify Details_ page and in the **Stack name** text input box, enter `Sample-Smart-Home-Backend` and then click **Next**.
15 |
16 | 1.1.6 When on the _Options_ page, click **Next** without making any changes.
17 |
18 | 1.1.7 On the _Review_ page, click the checkbox "I acknowledge that AWS CloudFormation might create IAM resources with custom names." and then click the **Create** button.
19 |
20 | > This IAM resource warning comes from the need for the Stack to create an Execution Role for the created Lambda functions.
21 |
22 | 1.1.8 In the Stacks list in the Cloud Formation console, the newly created Sample-Smart-Home-Backend stack will be created and initially have a status of _CREATE\_IN\_PROGRESS_. When the backend stack has been created and is ready, the status will change to _CREATE\_COMPLETE_. This may take 1-3 minutes.
23 |
24 | > If any issues arise, you should see a ROLLBACK\_IN\_PROGRESS message and ultimately a ROLLBACK\_COMPLETE status.
25 |
26 | #### 1.2 Locate the _Sample-Smart-Home-Backend_ Stack Outputs
27 |
28 | 1.2.1 While still in the Cloud Formation console, select the check box for _Sample-Smart-Home-Backend_ and then select the _Outputs_ details tab. If the tabs are not visible along the bottom of the Cloud Formation console, click the _Restore_ or _Maximize_ buttons in the bottom right.
29 |
30 | 1.2.2 Locate the _EndpointApiId_ **Value** that will look something like the following example: `y053kmfr5m` and copy it to the `config.txt` file into the [EndpointApiId] section.
31 |
32 | 1.2.3 Locate the _SkillLambdaArn_ **Value** that is formatted like the following example: `arn:aws:lambda:us-east-1:############:function:SampleSkillAdapter` and copy it into the [SkillLambdaArn] section of `config.txt`.
33 |
34 |
35 |
36 |
37 | ____
38 | Go to [Step 2: Set Up Login with Amazon](002-setup-lwa.md).
39 |
--------------------------------------------------------------------------------
/sample_backend/docs/002-setup-lwa.md:
--------------------------------------------------------------------------------
1 | # Step 2: Set Up Login with Amazon
2 | For the sample environment, a development Login with Amazon (LWA) security profile will be used for configuring Account Linking, which is required for a Smart Home Skill.
3 |
4 | #### 2.1 Create a Login with Amazon Security Profile
5 |
6 | 2.1.1 In your web browser, go to [https://developer.amazon.com/lwa/sp/overview.html](https://developer.amazon.com/lwa/sp/overview.html) and make sure _APPS & SERVICES_ is selected in the top menu and _Login with Amazon_ is selected in the sub menu.
7 |
8 | 2.1.2 On the _Login with Amazon_ page, click the **Create a New Security Profile** button.
9 |
10 | 2.1.3 On the Security Profile Management page, enter `Sample Alexa Smart Home` for the **Security Profile Name**.
11 |
12 | 2.1.4 For the **Security Profile Description** enter `A sample security profile for Alexa Smart Home Skill development`.
13 |
14 | 2.1.5 For the **Consent Privacy Notice URL** enter `http://example.com/privacy.html` for illustrative purposes or use your own if you already have a public consent privacy policy.
15 |
16 | > For a production Smart Home Skill, a valid consent privacy notice will be required.
17 |
18 | 2.1.6 For the **Consent Logo Image** download https://raw.githubusercontent.com/alexa/alexa-smarthome/master/sample_backend/docs/img/alexa-sample-smarthome-150x.png into the `Alexa-SmartHome-Sample` directory created on your Desktop and then click the **Upload Image** area to load the file from where you saved it.
19 |
20 | 2.1.7 If your profile configuration looks like the following, click **Save** on the Security Profile Management page.
21 |
22 | 
23 |
24 |
25 | > If successful, a message similar to 'Login with Amazon successfully enabled for Security Profile. Click (gear) to manage Security Profile.' will be returned.
26 |
27 | 2.1.8 From the list of Security Profiles, click the **Show Client ID and Client Secret** link for the _Sample Alexa Smart Home_ profile.
28 |
29 | 2.1.9 Copy the displayed Client ID and Client Secret values to the `config.txt` file replacing the template entries for [Login with Amazon Client ID] and [Login with Amazon Client Secret] respectively:
30 |
31 | ```
32 | [Login with Amazon Client ID]
33 | amzn1.application-oa2-client.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
34 |
35 | [Login with Amazon Client Secret]
36 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
37 | ```
38 | > Further configuration of the Security Profile Allowed Return URLs will be done during configuration of the Alexa Smart Home Skill.
39 |
40 |
41 |
42 | ____
43 | Go to [Step 3: Create the Alexa Smart Home Skill](003-setup-create-skill-smarthome.md).
44 |
--------------------------------------------------------------------------------
/sample_backend/lambda/lambda_api/python/alexa/skills/smarthome/alexa_power_controller.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
4 | #
5 | # Licensed under the Amazon Software License (the "License"). You may not use this file except in
6 | # compliance with the License. A copy of the License is located at
7 | #
8 | # http://aws.amazon.com/asl/
9 | #
10 | # or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific
12 | # language governing permissions and limitations under the License.
13 |
14 | import uuid
15 | from .alexa_utils import get_utc_timestamp
16 |
17 |
18 | class AlexaPowerController:
19 |
20 | def __init__(self, **kwargs):
21 | self.namespace = kwargs.get('namespace', 'Alexa')
22 | self.name = kwargs.get('name', 'Response')
23 | self.payload_version = kwargs.get('payload_version', '3')
24 | self.message_id = kwargs.get('message_id', str(uuid.uuid4()))
25 | self.correlation_token = kwargs.get('correlation_token', None)
26 |
27 | self.type = "BearerToken"
28 | self.token = kwargs.get('token', None)
29 |
30 | self.endpoint_id = kwargs.get('endpoint_id', 'INVALID')
31 |
32 | self.value = kwargs.get('value', 'TurnOff')
33 |
34 | def create_property(self, **kwargs):
35 | p = {}
36 | p['namespace'] = kwargs.get('namespace', 'Alexa.EndpointHealth')
37 | p['name'] = kwargs.get('name', 'connectivity')
38 | p['value'] = kwargs.get('value', {'value': 'OK'})
39 | p['timeOfSample'] = get_utc_timestamp()
40 | p['uncertaintyInMilliseconds'] = kwargs.get('uncertainty_in_milliseconds', 0)
41 | return p
42 |
43 | def get_response(self):
44 |
45 | powerStateValue = 'OFF' if self.value == "TurnOff" else 'ON'
46 |
47 | properties = []
48 | properties.append(self.create_property(namespace='Alexa.PowerController', name='powerState', value=powerStateValue))
49 | properties.append(self.create_property(namespace='Alexa.EndpointHealth', name='connectivity', value={'value': 'OK'}))
50 |
51 | header = {}
52 | header['namespace'] = 'Alexa'
53 | header['name'] = 'Response'
54 | header['payloadVersion'] = self.payload_version
55 | header['messageId'] = self.message_id
56 | header['correlationToken'] = self.correlation_token
57 |
58 | endpoint = {}
59 | endpoint['scope'] = {}
60 | endpoint['scope']['type'] = 'BearerToken'
61 | endpoint['scope']['token'] = self.token
62 | endpoint['endpointId'] = self.endpoint_id
63 |
64 | payload = {}
65 |
66 | response = {}
67 |
68 | response['context'] = {}
69 | response['context']['properties'] = properties
70 |
71 | response['event'] = {}
72 | response['event']['header'] = header
73 | response['event']['endpoint'] = endpoint
74 | response['event']['payload'] = payload
75 |
76 | return response
--------------------------------------------------------------------------------
/sample_backend/lambda/lambda_api/python/alexa/skills/smarthome/alexa_change_report.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
4 | #
5 | # Licensed under the Amazon Software License (the "License"). You may not use this file except in
6 | # compliance with the License. A copy of the License is located at
7 | #
8 | # http://aws.amazon.com/asl/
9 | #
10 | # or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific
12 | # language governing permissions and limitations under the License.
13 |
14 | import uuid
15 | from .alexa_utils import get_utc_timestamp
16 |
17 |
18 | class AlexaChangeReport:
19 |
20 | def __init__(self, **kwargs):
21 | self.namespace = kwargs.get('namespace', 'Alexa')
22 | self.name = kwargs.get('name', 'ChangeReport')
23 | self.payload_version = kwargs.get('payload_version', '3')
24 | self.message_id = kwargs.get('message_id', str(uuid.uuid4()))
25 |
26 | self.type = "BearerToken"
27 | self.token = kwargs.get('token', 'INVALID')
28 |
29 | self.endpoint_id = kwargs.get('endpoint_id', 'INVALID')
30 |
31 | self.cause_type = kwargs.get('cause_type', 'PHYSICAL_INTERACTION')
32 |
33 | def create_property(self, **kwargs):
34 | p = {}
35 | p['namespace'] = kwargs.get('namespace', 'Alexa')
36 | p['name'] = kwargs.get('name', 'powerState')
37 | p['value'] = kwargs.get('value', 'ON')
38 | p['timeOfSample'] = get_utc_timestamp()
39 | p['uncertaintyInMilliseconds'] = kwargs.get('uncertainty_in_milliseconds', 0)
40 | return p
41 |
42 | def get_response(self):
43 | response = {}
44 | response['context'] = {}
45 | response['context']['properties'] = []
46 | response['context']['properties'].append(self.create_property(namespace='Alexa.PowerController', name='powerState', value='ON'))
47 | response['context']['properties'].append(self.create_property(namespace='Alexa.EndpointHealth', name='connectivity', value={'value': 'OK'}))
48 | response['event'] = {}
49 | response['event']['header'] = {}
50 | response['event']['header']['namespace'] = self.namespace
51 | response['event']['header']['name'] = self.name
52 | response['event']['header']['payloadVersion'] = self.payload_version
53 | response['event']['header']['messageId'] = self.message_id
54 | response['event']['endpoint'] = {}
55 | response['event']['endpoint']['scope'] = {}
56 | response['event']['endpoint']['scope']['type'] = self.type
57 | response['event']['endpoint']['scope']['token'] = self.token
58 | response['event']['endpoint']['endpointId'] = self.endpoint_id
59 | response['event']['payload'] = {}
60 | response['event']['payload']['change'] = {}
61 | response['event']['payload']['change']['cause'] = {}
62 | response['event']['payload']['change']['cause']['type'] = self.cause_type
63 | response['event']['payload']['change']['properties'] = []
64 | response['event']['payload']['change']['properties'].append(self.create_property(namespace='Alexa.BrightnessController', name='brightness', value=65))
65 |
66 | return response
67 |
--------------------------------------------------------------------------------
/sample_backend/docs/005-setup-link-skill-smarthome.md:
--------------------------------------------------------------------------------
1 | # Step 5: Link the Alexa Smart Home Skill
2 | Finalize the Lambda configuration and link the Alexa Smart Home Skill to your account.
3 |
4 | #### 5.1 Update the Skill Lambda Environment variables
5 | With a completed Alexa Smart Home Skill configuration, the Alexa Skill Client ID and Client Secret will need to be passed to the backend. To accomplish this, you can set the environment variables of the Lambda that is handling the Endpoint interactions.
6 |
7 | 5.1.2 Browse to https://us-east-1.console.aws.amazon.com/lambda/home?region=us-east-1#/functions/SampleEndpointAdapter?tab=configuration.
8 |
9 | 5.1.3 Expand the **Environment variables** section add a Key called `client_id`.
10 |
11 | 5.1.4 For the _client\_id_ value enter the value stored in [Alexa Skill Messaging Client Id] from the `config.txt` file.
12 |
13 | 5.1.5 In the **Environment variables** section add another Key called `client_secret`.
14 |
15 | 5.1.6 For the _client\_secret_ value enter the value stored in [Alexa Messaging Skill Client Secret] from the `config.txt` file.
16 |
17 | 5.1.7 When both the _client\_id_ and _client\_secret_ environment variables are added, click **Save** at the top of the page.
18 |
19 | #### 5.2 Link the Alexa Smart Home Skill
20 |
21 | 5.2.1 Go to https://alexa.amazon.com/ and select **Skills** from the left menu.
22 |
23 | > Tip: Replace the skill value at the end of https://alexa.amazon.com/spa/index.html#skills/beta/ALEXA_SKILL_ID to go directly to a skill.
24 | > For example, https://alexa.amazon.com/spa/index.html#skills/beta/amzn1.ask.skill.203e1508-e33b-4b63-8e0e-70b97e45408d
25 |
26 |
27 | 5.2.2 Click **Your Skills** from the top right of the section.
28 |
29 | 5.2.3 Locate your Sample Smart Home Skill in the list of skills and click on it.
30 |
31 | 
32 |
33 | 5.2.4 On the _Sample Smart Home Skill_ page, click **Enable** in the top right and authenticate with your Amazon account.
34 |
35 | 5.2.5 On authentication, verify you are presented with the _Sample Alexa Smart Home_ authentication dialog and authenticate using your Amazon account. The transitional page will look something like the following in a browser:
36 |
37 | 
38 |
39 | 5.2.6 Click **Allow** to link your Account with the _Sample Alexa Smart Home_ skill.
40 |
41 | 5.2.7 On success, you should be presented with a window that reads "To continue, close this window to discover devices you can control with Alexa." Close this page and return to the _Sample Smart Home Skill_.
42 |
43 | 5.2.8 When redirected back to the Skill page, you will be prompted 'Discover Devices'. Click **Discover Devices** to start the device discovery process. This may take up to 20 seconds to complete.
44 |
45 | > No new devices from the Sample Smart Home Skill will be returned. This is expected.
46 |
47 |
48 |
49 | ____
50 | Go to [Step 6: Create the Endpoints](006-setup-create-endpoints.md).
51 |
--------------------------------------------------------------------------------
/sample_backend/docs/003-setup-create-skill-smarthome.md:
--------------------------------------------------------------------------------
1 | # Step 3: Create the Alexa Smart Home Skill
2 | Create an Alexa Smart Home Skill that will process the Smart Home commands.
3 |
4 | #### 3.1 Navigate to the Alexa Skills page
5 |
6 | 3.1.1 In a web browser to the Amazon Developer Console at [https://developer.amazon.com/home.html](https://developer.amazon.com/home.html). If not already authenticated, you may have to _Sign In_ with your Amazon Developer Account .
7 |
8 | 3.1.2 Select the _Alexa_ tab from the top menu.
9 |
10 | 3.1.3 On the _Get started with Alexa_ page and in the _Alexa Skills Kit_ box, click the **Get Started >** button.
11 |
12 | 3.1.4 From the _Building Alexa Skills with the Alexa Skills Kit_ page, click the **Add a New Skill** button on the top right of the page.
13 |
14 | #### 3.2 Create a New Alexa Skill
15 |
16 | 3.2.1 On the _Skill Information_ tab, from the **Skill Type** radio buttons select **Smart Home Skill API**
17 |
18 | 3.2.2 Leave the language as English.
19 |
20 | > For more information on adding another language to your skill, see [Develop Smart Home Skills in Multiple Languages](https://developer.amazon.com/docs/smarthome/develop-smart-home-skills-in-multiple-languages.html).
21 |
22 | 3.2.3 Enter `Sample Smart Home Skill` as the Name of the skill.
23 |
24 | 3.2.4 Under Payload Version, verify **v3 (preferred)** is selected.
25 |
26 | 3.2.5 Click **Save**
27 |
28 | #### 3.3 Collect the Application Id of _Sample Smart Home Skill_
29 |
30 | 3.3.1 When the skill is saved, the page will refresh. From the refreshed skill information, copy the _Application Id_ of the Alexa Skill to the `config.txt` file into the [Alexa Skill Application Id] value. The format of the Application Id will look like following:
31 |
32 | ```
33 | amzn1.ask.skill.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
34 | ```
35 |
36 | 3.3.2 Copy the Application Id value to the clipboard.
37 |
38 | #### 3.4 Add a Smart Home Trigger to the Alexa Smart Home Skill Lambda
39 |
40 | 3.4.1 In a new tab, browse to https://us-east-1.console.aws.amazon.com/lambda/home?region=us-east-1#/functions/SampleSkillAdapter?tab=triggers.
41 |
42 | 3.4.2 Click the **+ Add trigger** button.
43 |
44 | 3.4.3 In the _Add trigger_ dialog, click the empty box and then select **Alexa Smart Home** from the drop down menu.
45 |
46 | 
47 |
48 | 3.4.4 Paste the Alexa Skill Application Id value from the clipboard into the _Application Id_ text box. If you no longer have the value on your clipboard, you can retrieve it from the [Alexa Skill Application Id] section of the `config.txt` file.
49 |
50 | 3.4.5 Verify **Enable trigger** is checked and then click **Submit**. If successful, a "Successfully added the trigger..." message will be returned.
51 |
52 | 3.4.6 Close this tab and return to the _Sample Smart Home Skill_ in the Alexa Skills section of the Amazon Developer Console.
53 |
54 | 3.4.7 Click **Next** to move to the _Sample Smart Home Skill_ Interaction model and then click **Next** again to move to the Configuration section.
55 |
56 | > TIP: If you need to return to the configuration page, you can replace SKILL_APPLICATION_ID in the following URL with your copied Application Id for a direct link.
57 | > ```
58 | > https://developer.amazon.com/edw/home.html#/skill/SKILL_APPLICATION_ID/en_US/configuration
59 | > ```
60 |
61 |
62 |
63 | ____
64 | Go to [Step 4: Configure the Alexa Smart Home Skill](004-setup-configure-skill-smarthome.md).
65 |
--------------------------------------------------------------------------------
/sample_backend/lambda/lambda_api/python/alexa/skills/smarthome/alexa_discover_response.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
4 | #
5 | # Licensed under the Amazon Software License (the "License"). You may not use this file except in
6 | # compliance with the License. A copy of the License is located at
7 | #
8 | # http://aws.amazon.com/asl/
9 | #
10 | # or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific
12 | # language governing permissions and limitations under the License.
13 |
14 | import random
15 | import uuid
16 |
17 | from .alexa_utils import get_utc_timestamp
18 |
19 |
20 | class AlexaDiscoverResponse:
21 |
22 | def __init__(self, request):
23 | self.message_id = str(uuid.uuid4())
24 | self.name = 'Discover.Response'
25 | self.payload_version = request["directive"]["header"]["payloadVersion"]
26 | self.endpoints = []
27 |
28 | def add_endpoint(self, thing):
29 | # Translate the AWS IoT Thing attributes
30 | endpoint_id_value = thing['thingName']
31 | # HACK Using the thing name as the friendly name!
32 | friendly_name_value = thing['thingName'].replace('_', ' ')
33 | # NOTE SWITCH is currently hardcoded into the endpoint
34 | self.endpoints.append(self.create_endpoint(endpoint_id=endpoint_id_value, friendly_name=friendly_name_value, display_categories=['SWITCH']))
35 |
36 | def create_capability(self, **kwargs):
37 | capability = {}
38 | capability['type'] = kwargs.get('type', 'AlexaInterface')
39 | capability['interface'] = kwargs.get('interface', 'Alexa')
40 | capability['version'] = kwargs.get('version', '3')
41 | supported = kwargs.get('supported', None) # [{"name": "powerState"}]
42 | if supported:
43 | capability['properties'] = {}
44 | capability['properties']['supported'] = supported
45 | capability['properties']['proactivelyReported'] = kwargs.get('proactively_reported', True)
46 | capability['properties']['retrievable'] = kwargs.get('retrievable', True)
47 |
48 | return capability
49 |
50 | def create_endpoint(self, **kwargs):
51 | endpoint = {}
52 | endpoint['endpointId'] = kwargs.get('endpoint_id', 'endpoint_' + "%0.6d" % random.randint(0, 999999))
53 | endpoint['friendlyName'] = kwargs.get('friendly_name', 'Endpoint')
54 | endpoint['description'] = kwargs.get('description', 'Endpoint Description')
55 | endpoint['manufacturerName'] = kwargs.get('manufacturer_name', 'Unknown Manufacturer')
56 | endpoint['displayCategories'] = kwargs.get('display_categories', ['OTHER'])
57 |
58 | # NOTE: These capabilities are hardcoded, how might we expose them differently?
59 | endpoint['capabilities'] = []
60 | endpoint['capabilities'].append(self.create_capability())
61 | endpoint['capabilities'].append(self.create_capability(interface='Alexa.PowerController', supported=[{"name": "powerState"}]))
62 | endpoint['capabilities'].append(self.create_capability(interface='Alexa.EndpointHealth', supported=[{"name": "connectivity"}]))
63 | return endpoint
64 |
65 | def create_property(self, **kwargs):
66 | p = {}
67 | p['namespace'] = kwargs.get('namespace', 'Alexa')
68 | p['name'] = 'powerState'
69 | p['value'] = 'ON'
70 | p['timeOfSample'] = get_utc_timestamp()
71 | p['uncertaintyInMilliseconds'] = kwargs.get('uncertainty_in_milliseconds', 0)
72 | return p
73 |
74 | def get_response(self):
75 | response = {}
76 | # context = {}
77 | event = {}
78 |
79 | # properties = []
80 | # properties.append(self.create_property(namespace='Alexa.PowerController'))
81 | # context['properties'] = properties
82 |
83 | header = {}
84 | header['namespace'] = 'Alexa.Discovery'
85 | header['name'] = self.name
86 | header['payloadVersion'] = self.payload_version
87 | header['messageId'] = self.message_id
88 |
89 | # self.endpoints.append(self.create_endpoint())
90 |
91 | payload = {}
92 | payload['endpoints'] = self.endpoints
93 |
94 | event['header'] = header
95 | event['payload'] = payload
96 |
97 | # response['context'] = context
98 | response['event'] = event
99 |
100 | return response
101 |
--------------------------------------------------------------------------------
/sample_lambda/python/jsonschema/tests/test_cli.py:
--------------------------------------------------------------------------------
1 | from jsonschema import Draft4Validator, ValidationError, cli
2 | from jsonschema.compat import StringIO
3 | from jsonschema.exceptions import SchemaError
4 | from jsonschema.tests.compat import mock, unittest
5 |
6 |
7 | def fake_validator(*errors):
8 | errors = list(reversed(errors))
9 |
10 | class FakeValidator(object):
11 | def __init__(self, *args, **kwargs):
12 | pass
13 |
14 | def iter_errors(self, instance):
15 | if errors:
16 | return errors.pop()
17 | return []
18 |
19 | def check_schema(self, schema):
20 | pass
21 |
22 | return FakeValidator
23 |
24 |
25 | class TestParser(unittest.TestCase):
26 | FakeValidator = fake_validator()
27 |
28 | def setUp(self):
29 | mock_open = mock.mock_open()
30 | patch_open = mock.patch.object(cli, "open", mock_open, create=True)
31 | patch_open.start()
32 | self.addCleanup(patch_open.stop)
33 |
34 | mock_json_load = mock.Mock()
35 | mock_json_load.return_value = {}
36 | patch_json_load = mock.patch("json.load")
37 | patch_json_load.start()
38 | self.addCleanup(patch_json_load.stop)
39 |
40 | def test_find_validator_by_fully_qualified_object_name(self):
41 | arguments = cli.parse_args(
42 | [
43 | "--validator",
44 | "jsonschema.tests.test_cli.TestParser.FakeValidator",
45 | "--instance", "foo.json",
46 | "schema.json",
47 | ]
48 | )
49 | self.assertIs(arguments["validator"], self.FakeValidator)
50 |
51 | def test_find_validator_in_jsonschema(self):
52 | arguments = cli.parse_args(
53 | [
54 | "--validator", "Draft4Validator",
55 | "--instance", "foo.json",
56 | "schema.json",
57 | ]
58 | )
59 | self.assertIs(arguments["validator"], Draft4Validator)
60 |
61 |
62 | class TestCLI(unittest.TestCase):
63 | def test_draft3_schema_draft4_validator(self):
64 | stdout, stderr = StringIO(), StringIO()
65 | with self.assertRaises(SchemaError):
66 | cli.run(
67 | {
68 | "validator": Draft4Validator,
69 | "schema": {
70 | "anyOf": [
71 | {"minimum": 20},
72 | {"type": "string"},
73 | {"required": True},
74 | ],
75 | },
76 | "instances": [1],
77 | "error_format": "{error.message}",
78 | },
79 | stdout=stdout,
80 | stderr=stderr,
81 | )
82 |
83 | def test_successful_validation(self):
84 | stdout, stderr = StringIO(), StringIO()
85 | exit_code = cli.run(
86 | {
87 | "validator": fake_validator(),
88 | "schema": {},
89 | "instances": [1],
90 | "error_format": "{error.message}",
91 | },
92 | stdout=stdout,
93 | stderr=stderr,
94 | )
95 | self.assertFalse(stdout.getvalue())
96 | self.assertFalse(stderr.getvalue())
97 | self.assertEqual(exit_code, 0)
98 |
99 | def test_unsuccessful_validation(self):
100 | error = ValidationError("I am an error!", instance=1)
101 | stdout, stderr = StringIO(), StringIO()
102 | exit_code = cli.run(
103 | {
104 | "validator": fake_validator([error]),
105 | "schema": {},
106 | "instances": [1],
107 | "error_format": "{error.instance} - {error.message}",
108 | },
109 | stdout=stdout,
110 | stderr=stderr,
111 | )
112 | self.assertFalse(stdout.getvalue())
113 | self.assertEqual(stderr.getvalue(), "1 - I am an error!")
114 | self.assertEqual(exit_code, 1)
115 |
116 | def test_unsuccessful_validation_multiple_instances(self):
117 | first_errors = [
118 | ValidationError("9", instance=1),
119 | ValidationError("8", instance=1),
120 | ]
121 | second_errors = [ValidationError("7", instance=2)]
122 | stdout, stderr = StringIO(), StringIO()
123 | exit_code = cli.run(
124 | {
125 | "validator": fake_validator(first_errors, second_errors),
126 | "schema": {},
127 | "instances": [1, 2],
128 | "error_format": "{error.instance} - {error.message}\t",
129 | },
130 | stdout=stdout,
131 | stderr=stderr,
132 | )
133 | self.assertFalse(stdout.getvalue())
134 | self.assertEqual(stderr.getvalue(), "1 - 9\t1 - 8\t2 - 7\t")
135 | self.assertEqual(exit_code, 1)
136 |
--------------------------------------------------------------------------------
/sample_backend/lambda/lambda_api/python/jsonschema/tests/test_cli.py:
--------------------------------------------------------------------------------
1 | from jsonschema import Draft4Validator, ValidationError, cli
2 | from jsonschema.compat import StringIO
3 | from jsonschema.exceptions import SchemaError
4 | from jsonschema.tests.compat import mock, unittest
5 |
6 |
7 | def fake_validator(*errors):
8 | errors = list(reversed(errors))
9 |
10 | class FakeValidator(object):
11 | def __init__(self, *args, **kwargs):
12 | pass
13 |
14 | def iter_errors(self, instance):
15 | if errors:
16 | return errors.pop()
17 | return []
18 |
19 | def check_schema(self, schema):
20 | pass
21 |
22 | return FakeValidator
23 |
24 |
25 | class TestParser(unittest.TestCase):
26 | FakeValidator = fake_validator()
27 |
28 | def setUp(self):
29 | mock_open = mock.mock_open()
30 | patch_open = mock.patch.object(cli, "open", mock_open, create=True)
31 | patch_open.start()
32 | self.addCleanup(patch_open.stop)
33 |
34 | mock_json_load = mock.Mock()
35 | mock_json_load.return_value = {}
36 | patch_json_load = mock.patch("json.load")
37 | patch_json_load.start()
38 | self.addCleanup(patch_json_load.stop)
39 |
40 | def test_find_validator_by_fully_qualified_object_name(self):
41 | arguments = cli.parse_args(
42 | [
43 | "--validator",
44 | "jsonschema.tests.test_cli.TestParser.FakeValidator",
45 | "--instance", "foo.json",
46 | "schema.json",
47 | ]
48 | )
49 | self.assertIs(arguments["validator"], self.FakeValidator)
50 |
51 | def test_find_validator_in_jsonschema(self):
52 | arguments = cli.parse_args(
53 | [
54 | "--validator", "Draft4Validator",
55 | "--instance", "foo.json",
56 | "schema.json",
57 | ]
58 | )
59 | self.assertIs(arguments["validator"], Draft4Validator)
60 |
61 |
62 | class TestCLI(unittest.TestCase):
63 | def test_draft3_schema_draft4_validator(self):
64 | stdout, stderr = StringIO(), StringIO()
65 | with self.assertRaises(SchemaError):
66 | cli.run(
67 | {
68 | "validator": Draft4Validator,
69 | "schema": {
70 | "anyOf": [
71 | {"minimum": 20},
72 | {"type": "string"},
73 | {"required": True},
74 | ],
75 | },
76 | "instances": [1],
77 | "error_format": "{error.message}",
78 | },
79 | stdout=stdout,
80 | stderr=stderr,
81 | )
82 |
83 | def test_successful_validation(self):
84 | stdout, stderr = StringIO(), StringIO()
85 | exit_code = cli.run(
86 | {
87 | "validator": fake_validator(),
88 | "schema": {},
89 | "instances": [1],
90 | "error_format": "{error.message}",
91 | },
92 | stdout=stdout,
93 | stderr=stderr,
94 | )
95 | self.assertFalse(stdout.getvalue())
96 | self.assertFalse(stderr.getvalue())
97 | self.assertEqual(exit_code, 0)
98 |
99 | def test_unsuccessful_validation(self):
100 | error = ValidationError("I am an error!", instance=1)
101 | stdout, stderr = StringIO(), StringIO()
102 | exit_code = cli.run(
103 | {
104 | "validator": fake_validator([error]),
105 | "schema": {},
106 | "instances": [1],
107 | "error_format": "{error.instance} - {error.message}",
108 | },
109 | stdout=stdout,
110 | stderr=stderr,
111 | )
112 | self.assertFalse(stdout.getvalue())
113 | self.assertEqual(stderr.getvalue(), "1 - I am an error!")
114 | self.assertEqual(exit_code, 1)
115 |
116 | def test_unsuccessful_validation_multiple_instances(self):
117 | first_errors = [
118 | ValidationError("9", instance=1),
119 | ValidationError("8", instance=1),
120 | ]
121 | second_errors = [ValidationError("7", instance=2)]
122 | stdout, stderr = StringIO(), StringIO()
123 | exit_code = cli.run(
124 | {
125 | "validator": fake_validator(first_errors, second_errors),
126 | "schema": {},
127 | "instances": [1, 2],
128 | "error_format": "{error.instance} - {error.message}\t",
129 | },
130 | stdout=stdout,
131 | stderr=stderr,
132 | )
133 | self.assertFalse(stdout.getvalue())
134 | self.assertEqual(stderr.getvalue(), "1 - 9\t1 - 8\t2 - 7\t")
135 | self.assertEqual(exit_code, 1)
136 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Amazon Software License 1.0
2 |
3 | This Amazon Software License ("License") governs your use, reproduction, and
4 | distribution of the accompanying software as specified below.
5 |
6 | 1. Definitions
7 |
8 | "Licensor" means any person or entity that distributes its Work.
9 |
10 | "Software" means the original work of authorship made available under this
11 | License.
12 |
13 | "Work" means the Software and any additions to or derivative works of the
14 | Software that are made available under this License.
15 |
16 | The terms "reproduce," "reproduction," "derivative works," and
17 | "distribution" have the meaning as provided under U.S. copyright law;
18 | provided, however, that for the purposes of this License, derivative works
19 | shall not include works that remain separable from, or merely link (or bind
20 | by name) to the interfaces of, the Work.
21 |
22 | Works, including the Software, are "made available" under this License by
23 | including in or with the Work either (a) a copyright notice referencing the
24 | applicability of this License to the Work, or (b) a copy of this License.
25 |
26 | 2. License Grants
27 |
28 | 2.1 Copyright Grant. Subject to the terms and conditions of this License,
29 | each Licensor grants to you a perpetual, worldwide, non-exclusive,
30 | royalty-free, copyright license to reproduce, prepare derivative works of,
31 | publicly display, publicly perform, sublicense and distribute its Work and
32 | any resulting derivative works in any form.
33 |
34 | 2.2 Patent Grant. Subject to the terms and conditions of this License, each
35 | Licensor grants to you a perpetual, worldwide, non-exclusive, royalty-free
36 | patent license to make, have made, use, sell, offer for sale, import, and
37 | otherwise transfer its Work, in whole or in part. The foregoing license
38 | applies only to the patent claims licensable by Licensor that would be
39 | infringed by Licensor's Work (or portion thereof) individually and
40 | excluding any combinations with any other materials or technology.
41 |
42 | 3. Limitations
43 |
44 | 3.1 Redistribution. You may reproduce or distribute the Work only if
45 | (a) you do so under this License, (b) you include a complete copy of this
46 | License with your distribution, and (c) you retain without modification
47 | any copyright, patent, trademark, or attribution notices that are present
48 | in the Work.
49 |
50 | 3.2 Derivative Works. You may specify that additional or different terms
51 | apply to the use, reproduction, and distribution of your derivative works
52 | of the Work ("Your Terms") only if (a) Your Terms provide that the use
53 | limitation in Section 3.3 applies to your derivative works, and (b) you
54 | identify the specific derivative works that are subject to Your Terms.
55 | Notwithstanding Your Terms, this License (including the redistribution
56 | requirements in Section 3.1) will continue to apply to the Work itself.
57 |
58 | 3.3 Use Limitation. The Work and any derivative works thereof only may be
59 | used or intended for use with the web services, computing platforms or
60 | applications provided by Amazon.com, Inc. or its affiliates, including
61 | Amazon Web Services, Inc.
62 |
63 | 3.4 Patent Claims. If you bring or threaten to bring a patent claim against
64 | any Licensor (including any claim, cross-claim or counterclaim in a
65 | lawsuit) to enforce any patents that you allege are infringed by any Work,
66 | then your rights under this License from such Licensor (including the
67 | grants in Sections 2.1 and 2.2) will terminate immediately.
68 |
69 | 3.5 Trademarks. This License does not grant any rights to use any
70 | Licensor's or its affiliates' names, logos, or trademarks, except as
71 | necessary to reproduce the notices described in this License.
72 |
73 | 3.6 Termination. If you violate any term of this License, then your rights
74 | under this License (including the grants in Sections 2.1 and 2.2) will
75 | terminate immediately.
76 |
77 | 4. Disclaimer of Warranty.
78 |
79 | THE WORK IS PROVIDED "AS IS" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
80 | EITHER EXPRESS OR IMPLIED, INCLUDING WARRANTIES OR CONDITIONS OF
81 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE OR
82 | NON-INFRINGEMENT. YOU BEAR THE RISK OF UNDERTAKING ANY ACTIVITIES UNDER
83 | THIS LICENSE. SOME STATES' CONSUMER LAWS DO NOT ALLOW EXCLUSION OF AN
84 | IMPLIED WARRANTY, SO THIS DISCLAIMER MAY NOT APPLY TO YOU.
85 |
86 | 5. Limitation of Liability.
87 |
88 | EXCEPT AS PROHIBITED BY APPLICABLE LAW, IN NO EVENT AND UNDER NO LEGAL
89 | THEORY, WHETHER IN TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE
90 | SHALL ANY LICENSOR BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY DIRECT,
91 | INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR
92 | RELATED TO THIS LICENSE, THE USE OR INABILITY TO USE THE WORK (INCLUDING
93 | BUT NOT LIMITED TO LOSS OF GOODWILL, BUSINESS INTERRUPTION, LOST PROFITS
94 | OR DATA, COMPUTER FAILURE OR MALFUNCTION, OR ANY OTHER COMM ERCIAL DAMAGES
95 | OR LOSSES), EVEN IF THE LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF
96 | SUCH DAMAGES.
97 |
--------------------------------------------------------------------------------
/sample_backend/lambda/lambda_api/python/index.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
4 | #
5 | # Licensed under the Amazon Software License (the "License"). You may not use this file except in
6 | # compliance with the License. A copy of the License is located at
7 | #
8 | # http://aws.amazon.com/asl/
9 | #
10 | # or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific
12 | # language governing permissions and limitations under the License.
13 |
14 | import json
15 | import os
16 | from endpoint_cloud import ApiAuth, ApiHandler, ApiResponse, ApiResponseBody
17 |
18 |
19 | def get_api_url(api_id, aws_region, resource):
20 | return 'https://{0}.execute-api.{1}.amazonaws.com/prod/{2}'.format(api_id, aws_region, resource)
21 |
22 |
23 | def handler(request, context):
24 | """
25 | Main Lambda Handler
26 | :param request Incoming API Request
27 | :param context Context for the Request
28 | """
29 |
30 | print("LOG api.index.handler.request:", request)
31 |
32 | # An API Handler to handle internal operations to the endpoints
33 | api_handler = ApiHandler()
34 |
35 | # An API Response crafted to return to the caller - in this case the API Gateway
36 | # The API Gateway expects a specially formatted response
37 | api_response = ApiResponse()
38 |
39 | try:
40 | # Get the Environment Variables, these are used to dynamically compose the API URI and pass Alexa Skill Messaging credentials
41 |
42 | # Get the Region
43 | env_aws_default_region = os.environ.get('AWS_DEFAULT_REGION', None)
44 | if env_aws_default_region is None:
45 | print("ERROR skill.index.handler.aws_default_region is None default to us-east-1")
46 | env_aws_default_region = 'us-east-1'
47 |
48 | # Get the API ID, Client ID, and Client Secret
49 | env_api_id = os.environ.get('api_id', None)
50 | env_client_id = os.environ.get('client_id', None)
51 | env_client_secret = os.environ.get('client_secret', None)
52 | if env_api_id is None or env_client_id is None or env_client_secret is None:
53 | api_response.statusCode = 403
54 | api_response.body = ApiResponseBody(result="ERR", message="Environment variable is not set: api_id:{0} client_id:{1} client_secret:{2}".format(env_api_id, env_client_id, env_client_secret))
55 | return api_response.create()
56 |
57 | # Reject the request if it isn't from our API
58 | api_id = request['requestContext']['apiId']
59 | if api_id != env_api_id:
60 | api_response.statusCode = 403
61 | api_response.body = ApiResponseBody(result="ERR", message="api_id did not match")
62 | return api_response.create()
63 |
64 | # Route the inbound request by evaluating for the resource and HTTP method
65 | resource = request["resource"]
66 | http_method = request["httpMethod"]
67 |
68 | # POST to directives : Process an Alexa Directive - This will be used to implement Endpoint behavior and state
69 | if http_method == 'POST' and resource == '/directives':
70 | response = api_handler.directive.process(request, env_client_id, env_client_secret, get_api_url(env_api_id, env_aws_default_region, 'auth-redirect'))
71 | print('LOG api.index.handler.request.api_handler.directive.process.response:', response)
72 | response_name = json.loads(response)
73 | if response_name['event']['header']['name'] == 'ErrorResponse':
74 | api_response.statusCode = 500
75 | else:
76 | api_response.statusCode = 200
77 | api_response.body = response
78 |
79 | # POST to endpoints : Create an Endpoint
80 | if http_method == 'POST' and resource == '/endpoints':
81 | response = api_handler.endpoint.create(request)
82 | api_response.statusCode = 200
83 | api_response.body = json.dumps(response)
84 |
85 | # GET endpoints : List Endpoints
86 | if http_method == 'GET' and resource == '/endpoints':
87 | response = api_handler.endpoint.read(request)
88 | api_response.statusCode = 200
89 | api_response.body = json.dumps(response)
90 |
91 | # POST to event : Create an Event - This will be used to trigger a Proactive State Update
92 | if http_method == 'POST' and resource == '/events':
93 | response = api_handler.event.create(request)
94 | print('LOG api.index.handler.request.api_handler.event.create.response:', response)
95 | api_response.statusCode = 200
96 | api_response.body = json.dumps(response)
97 |
98 | except KeyError as key_error:
99 | # For a key Error, return an error message and HTTP Status of 400 Bad Request
100 | message_string = "KeyError: " + str(key_error)
101 | api_response.statusCode = 400
102 | api_response.body = ApiResponseBody(result="ERR", message=message_string)
103 |
104 | return api_response.create()
105 |
--------------------------------------------------------------------------------
/sample_backend/docs/006-setup-create-endpoints.md:
--------------------------------------------------------------------------------
1 | # Step 6: Create the Endpoints
2 | Create endpoints to be discovered during the Alexa Smart Home Skill Discovery.
3 |
4 |
5 | #### 6.1 Set Up Postman
6 | Postman is a tool for managing and executing HTTP requests and is very useful for API development and usage.
7 |
8 | ##### 6.1.1 Install Postman
9 | 6.1.1 Go to [getpostman.com](https://www.getpostman.com) and download and install the correct Postman application for your platform.
10 |
11 | 6.1.2 Download the Postman Sample Smart Home Collection from https://raw.githubusercontent.com/alexa/alexa-smarthome/master/sample_backend/lambda/lambda_api/sample_backend.postman_collection.json into the `Alexa-SmartHome-Sample` directory on your Desktop.
12 |
13 | ##### 6.1.2 Import the _Alexa Smart Home (sample_backend)_ Postman collection
14 |
15 | 6.1.2.1 Open Postman.
16 |
17 | 6.1.2.2 In Postman, click **Import** from the main menu and browse to the `sample_backend.postman_collection.json` file or drag it onto the _Import_ dialog.
18 |
19 | ##### 6.1.3 Create a Postman environment
20 | To fill out the variable values of the configuration use a Postman environment to store configuration-specific values.
21 |
22 | 6.1.3.1 In the top right of Postman, click the gear icon to open the _Environment options_ drop down menu and select **Manage Environments**.
23 |
24 | 
25 |
26 | 6.1.3.2 In opened _MANAGE ENVIRONMENTS_ dialog, click the **Add** in the bottom right.
27 |
28 | 6.1.3.3 For the _Environment Name_ enter `Alexa Smart Home (sample_backend)`.
29 |
30 | 6.1.3.4 Add a **Key** value called `aws_region` and set its **Value** to `us-east-1`.
31 |
32 | 6.1.3.5 Add another **Key** value called `endpoint_api_id` and set its **Value** to the [EndpointApiId] value from the `config.txt` file.
33 |
34 | 6.1.3.6 Click the **Add** button again to save the environment settings.
35 |
36 | 6.1.3.6 Close the _MANAGE ENVIRONMENTS_ dialog and in the top right of Postman select the newly created _Alexa Smart Home (sample_backend)_ environment from the environment drop down menu.
37 |
38 | #### 6.2 Create Endpoints
39 | Use Postman to generate endpoints.
40 |
41 | 6.2.1 Browse to the AWS IoT console at https://console.aws.amazon.com/iotv2/home?region=us-east-1#/thinghub and note the existing _Things_, if any.
42 |
43 | 6.2.2 In Postman, select the **POST** _/endpoints_ resource from the left menu and then click the **Send** button in the top right.
44 |
45 | 6.2.3 Return to the [AWS IoT Things console](https://console.aws.amazon.com/iotv2/home?region=us-east-1#/thinghub) and refresh the page. A new `black_switch` Thing should be available.
46 |
47 | 6.2.4 Click on the `black_switch` Thing instance to inspect its attributes. They should look like the following:
48 |
49 | 
50 |
51 | > Note that the user_id is set to 0. This is a default value useful for development. However, Discovery for the Smart Home Skill would not find this device since it is expecting a user_id in the form of a profile from Login with Amazon.
52 |
53 | 6.2.5 Go to https://console.aws.amazon.com/dynamodb/home?region=us-east-1#tables:selected=SampleUsers and select the **Items** tab.
54 |
55 | 6.2.6 In the list of _SampleUsers_ select the first entry UserId that looks like the following format:
56 | ```
57 | amzn1.account.XXXXXXXXXXXXXXXXXXXXXXXXXXXX
58 | ```
59 | 6.2.7 Copy the UserId value and save it to the [user_id] section of the `config.txt` file.
60 |
61 | 6.2.8 Return to the [AWS IoT Things console](https://console.aws.amazon.com/iotv2/home?region=us-east-1#/thinghub) and select the `black_switch` Thing to view its attributes.
62 |
63 | 6.2.9 In the top right of the `black_switch` attributes click **Edit**.
64 |
65 | 6.2.10 On the _Edit black_switch_ attributes page, set the value of the _user_id_ **Attribute key** to the [user_id] stored in the `config.txt` file. This associates the `black_switch` thing with that user profile.
66 |
67 | > If you want to create other devices, repeat this section and update the POST body of the /endpoints call to look something like the following and click **Send** to POST it to the endpoint API:
68 |
69 | ```
70 | {
71 | "event": {
72 | "endpoint": {
73 | "userId" : "INSERT_YOUR_USER_ID_FROM_CONFIG.TXT",
74 | "id": "white_switch",
75 | "state": "OFF",
76 | "type": "SWITCH"
77 | }
78 | }
79 | }
80 | ```
81 |
82 |
83 |
84 | ____
85 | Go to [Step 7: Test the Endpoints](007-setup-test-endpoints.md).
86 |
--------------------------------------------------------------------------------
/sample_lambda/python/jsonschema/schemas/draft3.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json-schema.org/draft-03/schema#",
3 | "dependencies": {
4 | "exclusiveMaximum": "maximum",
5 | "exclusiveMinimum": "minimum"
6 | },
7 | "id": "http://json-schema.org/draft-03/schema#",
8 | "properties": {
9 | "$ref": {
10 | "format": "uri",
11 | "type": "string"
12 | },
13 | "$schema": {
14 | "format": "uri",
15 | "type": "string"
16 | },
17 | "additionalItems": {
18 | "default": {},
19 | "type": [
20 | {
21 | "$ref": "#"
22 | },
23 | "boolean"
24 | ]
25 | },
26 | "additionalProperties": {
27 | "default": {},
28 | "type": [
29 | {
30 | "$ref": "#"
31 | },
32 | "boolean"
33 | ]
34 | },
35 | "default": {
36 | "type": "any"
37 | },
38 | "dependencies": {
39 | "additionalProperties": {
40 | "items": {
41 | "type": "string"
42 | },
43 | "type": [
44 | "string",
45 | "array",
46 | {
47 | "$ref": "#"
48 | }
49 | ]
50 | },
51 | "default": {},
52 | "type": [
53 | "string",
54 | "array",
55 | "object"
56 | ]
57 | },
58 | "description": {
59 | "type": "string"
60 | },
61 | "disallow": {
62 | "items": {
63 | "type": [
64 | "string",
65 | {
66 | "$ref": "#"
67 | }
68 | ]
69 | },
70 | "type": [
71 | "string",
72 | "array"
73 | ],
74 | "uniqueItems": true
75 | },
76 | "divisibleBy": {
77 | "default": 1,
78 | "exclusiveMinimum": true,
79 | "minimum": 0,
80 | "type": "number"
81 | },
82 | "enum": {
83 | "minItems": 1,
84 | "type": "array",
85 | "uniqueItems": true
86 | },
87 | "exclusiveMaximum": {
88 | "default": false,
89 | "type": "boolean"
90 | },
91 | "exclusiveMinimum": {
92 | "default": false,
93 | "type": "boolean"
94 | },
95 | "extends": {
96 | "default": {},
97 | "items": {
98 | "$ref": "#"
99 | },
100 | "type": [
101 | {
102 | "$ref": "#"
103 | },
104 | "array"
105 | ]
106 | },
107 | "format": {
108 | "type": "string"
109 | },
110 | "id": {
111 | "format": "uri",
112 | "type": "string"
113 | },
114 | "items": {
115 | "default": {},
116 | "items": {
117 | "$ref": "#"
118 | },
119 | "type": [
120 | {
121 | "$ref": "#"
122 | },
123 | "array"
124 | ]
125 | },
126 | "maxDecimal": {
127 | "minimum": 0,
128 | "type": "number"
129 | },
130 | "maxItems": {
131 | "minimum": 0,
132 | "type": "integer"
133 | },
134 | "maxLength": {
135 | "type": "integer"
136 | },
137 | "maximum": {
138 | "type": "number"
139 | },
140 | "minItems": {
141 | "default": 0,
142 | "minimum": 0,
143 | "type": "integer"
144 | },
145 | "minLength": {
146 | "default": 0,
147 | "minimum": 0,
148 | "type": "integer"
149 | },
150 | "minimum": {
151 | "type": "number"
152 | },
153 | "pattern": {
154 | "format": "regex",
155 | "type": "string"
156 | },
157 | "patternProperties": {
158 | "additionalProperties": {
159 | "$ref": "#"
160 | },
161 | "default": {},
162 | "type": "object"
163 | },
164 | "properties": {
165 | "additionalProperties": {
166 | "$ref": "#",
167 | "type": "object"
168 | },
169 | "default": {},
170 | "type": "object"
171 | },
172 | "required": {
173 | "default": false,
174 | "type": "boolean"
175 | },
176 | "title": {
177 | "type": "string"
178 | },
179 | "type": {
180 | "default": "any",
181 | "items": {
182 | "type": [
183 | "string",
184 | {
185 | "$ref": "#"
186 | }
187 | ]
188 | },
189 | "type": [
190 | "string",
191 | "array"
192 | ],
193 | "uniqueItems": true
194 | },
195 | "uniqueItems": {
196 | "default": false,
197 | "type": "boolean"
198 | }
199 | },
200 | "type": "object"
201 | }
202 |
--------------------------------------------------------------------------------
/sample_backend/lambda/lambda_api/python/jsonschema/schemas/draft3.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json-schema.org/draft-03/schema#",
3 | "dependencies": {
4 | "exclusiveMaximum": "maximum",
5 | "exclusiveMinimum": "minimum"
6 | },
7 | "id": "http://json-schema.org/draft-03/schema#",
8 | "properties": {
9 | "$ref": {
10 | "format": "uri",
11 | "type": "string"
12 | },
13 | "$schema": {
14 | "format": "uri",
15 | "type": "string"
16 | },
17 | "additionalItems": {
18 | "default": {},
19 | "type": [
20 | {
21 | "$ref": "#"
22 | },
23 | "boolean"
24 | ]
25 | },
26 | "additionalProperties": {
27 | "default": {},
28 | "type": [
29 | {
30 | "$ref": "#"
31 | },
32 | "boolean"
33 | ]
34 | },
35 | "default": {
36 | "type": "any"
37 | },
38 | "dependencies": {
39 | "additionalProperties": {
40 | "items": {
41 | "type": "string"
42 | },
43 | "type": [
44 | "string",
45 | "array",
46 | {
47 | "$ref": "#"
48 | }
49 | ]
50 | },
51 | "default": {},
52 | "type": [
53 | "string",
54 | "array",
55 | "object"
56 | ]
57 | },
58 | "description": {
59 | "type": "string"
60 | },
61 | "disallow": {
62 | "items": {
63 | "type": [
64 | "string",
65 | {
66 | "$ref": "#"
67 | }
68 | ]
69 | },
70 | "type": [
71 | "string",
72 | "array"
73 | ],
74 | "uniqueItems": true
75 | },
76 | "divisibleBy": {
77 | "default": 1,
78 | "exclusiveMinimum": true,
79 | "minimum": 0,
80 | "type": "number"
81 | },
82 | "enum": {
83 | "minItems": 1,
84 | "type": "array",
85 | "uniqueItems": true
86 | },
87 | "exclusiveMaximum": {
88 | "default": false,
89 | "type": "boolean"
90 | },
91 | "exclusiveMinimum": {
92 | "default": false,
93 | "type": "boolean"
94 | },
95 | "extends": {
96 | "default": {},
97 | "items": {
98 | "$ref": "#"
99 | },
100 | "type": [
101 | {
102 | "$ref": "#"
103 | },
104 | "array"
105 | ]
106 | },
107 | "format": {
108 | "type": "string"
109 | },
110 | "id": {
111 | "format": "uri",
112 | "type": "string"
113 | },
114 | "items": {
115 | "default": {},
116 | "items": {
117 | "$ref": "#"
118 | },
119 | "type": [
120 | {
121 | "$ref": "#"
122 | },
123 | "array"
124 | ]
125 | },
126 | "maxDecimal": {
127 | "minimum": 0,
128 | "type": "number"
129 | },
130 | "maxItems": {
131 | "minimum": 0,
132 | "type": "integer"
133 | },
134 | "maxLength": {
135 | "type": "integer"
136 | },
137 | "maximum": {
138 | "type": "number"
139 | },
140 | "minItems": {
141 | "default": 0,
142 | "minimum": 0,
143 | "type": "integer"
144 | },
145 | "minLength": {
146 | "default": 0,
147 | "minimum": 0,
148 | "type": "integer"
149 | },
150 | "minimum": {
151 | "type": "number"
152 | },
153 | "pattern": {
154 | "format": "regex",
155 | "type": "string"
156 | },
157 | "patternProperties": {
158 | "additionalProperties": {
159 | "$ref": "#"
160 | },
161 | "default": {},
162 | "type": "object"
163 | },
164 | "properties": {
165 | "additionalProperties": {
166 | "$ref": "#",
167 | "type": "object"
168 | },
169 | "default": {},
170 | "type": "object"
171 | },
172 | "required": {
173 | "default": false,
174 | "type": "boolean"
175 | },
176 | "title": {
177 | "type": "string"
178 | },
179 | "type": {
180 | "default": "any",
181 | "items": {
182 | "type": [
183 | "string",
184 | {
185 | "$ref": "#"
186 | }
187 | ]
188 | },
189 | "type": [
190 | "string",
191 | "array"
192 | ],
193 | "uniqueItems": true
194 | },
195 | "uniqueItems": {
196 | "default": false,
197 | "type": "boolean"
198 | }
199 | },
200 | "type": "object"
201 | }
202 |
--------------------------------------------------------------------------------
/sample_lambda/python/jsonschema/_reflect.py:
--------------------------------------------------------------------------------
1 | # -*- test-case-name: twisted.test.test_reflect -*-
2 | # Copyright (c) Twisted Matrix Laboratories.
3 | # See LICENSE for details.
4 |
5 | """
6 | Standardized versions of various cool and/or strange things that you can do
7 | with Python's reflection capabilities.
8 | """
9 |
10 | import sys
11 |
12 | from jsonschema.compat import PY3
13 |
14 |
15 | class _NoModuleFound(Exception):
16 | """
17 | No module was found because none exists.
18 | """
19 |
20 |
21 |
22 | class InvalidName(ValueError):
23 | """
24 | The given name is not a dot-separated list of Python objects.
25 | """
26 |
27 |
28 |
29 | class ModuleNotFound(InvalidName):
30 | """
31 | The module associated with the given name doesn't exist and it can't be
32 | imported.
33 | """
34 |
35 |
36 |
37 | class ObjectNotFound(InvalidName):
38 | """
39 | The object associated with the given name doesn't exist and it can't be
40 | imported.
41 | """
42 |
43 |
44 |
45 | if PY3:
46 | def reraise(exception, traceback):
47 | raise exception.with_traceback(traceback)
48 | else:
49 | exec("""def reraise(exception, traceback):
50 | raise exception.__class__, exception, traceback""")
51 |
52 | reraise.__doc__ = """
53 | Re-raise an exception, with an optional traceback, in a way that is compatible
54 | with both Python 2 and Python 3.
55 |
56 | Note that on Python 3, re-raised exceptions will be mutated, with their
57 | C{__traceback__} attribute being set.
58 |
59 | @param exception: The exception instance.
60 | @param traceback: The traceback to use, or C{None} indicating a new traceback.
61 | """
62 |
63 |
64 | def _importAndCheckStack(importName):
65 | """
66 | Import the given name as a module, then walk the stack to determine whether
67 | the failure was the module not existing, or some code in the module (for
68 | example a dependent import) failing. This can be helpful to determine
69 | whether any actual application code was run. For example, to distiguish
70 | administrative error (entering the wrong module name), from programmer
71 | error (writing buggy code in a module that fails to import).
72 |
73 | @param importName: The name of the module to import.
74 | @type importName: C{str}
75 | @raise Exception: if something bad happens. This can be any type of
76 | exception, since nobody knows what loading some arbitrary code might
77 | do.
78 | @raise _NoModuleFound: if no module was found.
79 | """
80 | try:
81 | return __import__(importName)
82 | except ImportError:
83 | excType, excValue, excTraceback = sys.exc_info()
84 | while excTraceback:
85 | execName = excTraceback.tb_frame.f_globals["__name__"]
86 | # in Python 2 execName is None when an ImportError is encountered,
87 | # where in Python 3 execName is equal to the importName.
88 | if execName is None or execName == importName:
89 | reraise(excValue, excTraceback)
90 | excTraceback = excTraceback.tb_next
91 | raise _NoModuleFound()
92 |
93 |
94 |
95 | def namedAny(name):
96 | """
97 | Retrieve a Python object by its fully qualified name from the global Python
98 | module namespace. The first part of the name, that describes a module,
99 | will be discovered and imported. Each subsequent part of the name is
100 | treated as the name of an attribute of the object specified by all of the
101 | name which came before it. For example, the fully-qualified name of this
102 | object is 'twisted.python.reflect.namedAny'.
103 |
104 | @type name: L{str}
105 | @param name: The name of the object to return.
106 |
107 | @raise InvalidName: If the name is an empty string, starts or ends with
108 | a '.', or is otherwise syntactically incorrect.
109 |
110 | @raise ModuleNotFound: If the name is syntactically correct but the
111 | module it specifies cannot be imported because it does not appear to
112 | exist.
113 |
114 | @raise ObjectNotFound: If the name is syntactically correct, includes at
115 | least one '.', but the module it specifies cannot be imported because
116 | it does not appear to exist.
117 |
118 | @raise AttributeError: If an attribute of an object along the way cannot be
119 | accessed, or a module along the way is not found.
120 |
121 | @return: the Python object identified by 'name'.
122 | """
123 | if not name:
124 | raise InvalidName('Empty module name')
125 |
126 | names = name.split('.')
127 |
128 | # if the name starts or ends with a '.' or contains '..', the __import__
129 | # will raise an 'Empty module name' error. This will provide a better error
130 | # message.
131 | if '' in names:
132 | raise InvalidName(
133 | "name must be a string giving a '.'-separated list of Python "
134 | "identifiers, not %r" % (name,))
135 |
136 | topLevelPackage = None
137 | moduleNames = names[:]
138 | while not topLevelPackage:
139 | if moduleNames:
140 | trialname = '.'.join(moduleNames)
141 | try:
142 | topLevelPackage = _importAndCheckStack(trialname)
143 | except _NoModuleFound:
144 | moduleNames.pop()
145 | else:
146 | if len(names) == 1:
147 | raise ModuleNotFound("No module named %r" % (name,))
148 | else:
149 | raise ObjectNotFound('%r does not name an object' % (name,))
150 |
151 | obj = topLevelPackage
152 | for n in names[1:]:
153 | obj = getattr(obj, n)
154 |
155 | return obj
156 |
--------------------------------------------------------------------------------
/sample_backend/lambda/lambda_api/python/jsonschema/_reflect.py:
--------------------------------------------------------------------------------
1 | # -*- test-case-name: twisted.test.test_reflect -*-
2 | # Copyright (c) Twisted Matrix Laboratories.
3 | # See LICENSE for details.
4 |
5 | """
6 | Standardized versions of various cool and/or strange things that you can do
7 | with Python's reflection capabilities.
8 | """
9 |
10 | import sys
11 |
12 | from jsonschema.compat import PY3
13 |
14 |
15 | class _NoModuleFound(Exception):
16 | """
17 | No module was found because none exists.
18 | """
19 |
20 |
21 |
22 | class InvalidName(ValueError):
23 | """
24 | The given name is not a dot-separated list of Python objects.
25 | """
26 |
27 |
28 |
29 | class ModuleNotFound(InvalidName):
30 | """
31 | The module associated with the given name doesn't exist and it can't be
32 | imported.
33 | """
34 |
35 |
36 |
37 | class ObjectNotFound(InvalidName):
38 | """
39 | The object associated with the given name doesn't exist and it can't be
40 | imported.
41 | """
42 |
43 |
44 |
45 | if PY3:
46 | def reraise(exception, traceback):
47 | raise exception.with_traceback(traceback)
48 | else:
49 | exec("""def reraise(exception, traceback):
50 | raise exception.__class__, exception, traceback""")
51 |
52 | reraise.__doc__ = """
53 | Re-raise an exception, with an optional traceback, in a way that is compatible
54 | with both Python 2 and Python 3.
55 |
56 | Note that on Python 3, re-raised exceptions will be mutated, with their
57 | C{__traceback__} attribute being set.
58 |
59 | @param exception: The exception instance.
60 | @param traceback: The traceback to use, or C{None} indicating a new traceback.
61 | """
62 |
63 |
64 | def _importAndCheckStack(importName):
65 | """
66 | Import the given name as a module, then walk the stack to determine whether
67 | the failure was the module not existing, or some code in the module (for
68 | example a dependent import) failing. This can be helpful to determine
69 | whether any actual application code was run. For example, to distiguish
70 | administrative error (entering the wrong module name), from programmer
71 | error (writing buggy code in a module that fails to import).
72 |
73 | @param importName: The name of the module to import.
74 | @type importName: C{str}
75 | @raise Exception: if something bad happens. This can be any type of
76 | exception, since nobody knows what loading some arbitrary code might
77 | do.
78 | @raise _NoModuleFound: if no module was found.
79 | """
80 | try:
81 | return __import__(importName)
82 | except ImportError:
83 | excType, excValue, excTraceback = sys.exc_info()
84 | while excTraceback:
85 | execName = excTraceback.tb_frame.f_globals["__name__"]
86 | # in Python 2 execName is None when an ImportError is encountered,
87 | # where in Python 3 execName is equal to the importName.
88 | if execName is None or execName == importName:
89 | reraise(excValue, excTraceback)
90 | excTraceback = excTraceback.tb_next
91 | raise _NoModuleFound()
92 |
93 |
94 |
95 | def namedAny(name):
96 | """
97 | Retrieve a Python object by its fully qualified name from the global Python
98 | module namespace. The first part of the name, that describes a module,
99 | will be discovered and imported. Each subsequent part of the name is
100 | treated as the name of an attribute of the object specified by all of the
101 | name which came before it. For example, the fully-qualified name of this
102 | object is 'twisted.python.reflect.namedAny'.
103 |
104 | @type name: L{str}
105 | @param name: The name of the object to return.
106 |
107 | @raise InvalidName: If the name is an empty string, starts or ends with
108 | a '.', or is otherwise syntactically incorrect.
109 |
110 | @raise ModuleNotFound: If the name is syntactically correct but the
111 | module it specifies cannot be imported because it does not appear to
112 | exist.
113 |
114 | @raise ObjectNotFound: If the name is syntactically correct, includes at
115 | least one '.', but the module it specifies cannot be imported because
116 | it does not appear to exist.
117 |
118 | @raise AttributeError: If an attribute of an object along the way cannot be
119 | accessed, or a module along the way is not found.
120 |
121 | @return: the Python object identified by 'name'.
122 | """
123 | if not name:
124 | raise InvalidName('Empty module name')
125 |
126 | names = name.split('.')
127 |
128 | # if the name starts or ends with a '.' or contains '..', the __import__
129 | # will raise an 'Empty module name' error. This will provide a better error
130 | # message.
131 | if '' in names:
132 | raise InvalidName(
133 | "name must be a string giving a '.'-separated list of Python "
134 | "identifiers, not %r" % (name,))
135 |
136 | topLevelPackage = None
137 | moduleNames = names[:]
138 | while not topLevelPackage:
139 | if moduleNames:
140 | trialname = '.'.join(moduleNames)
141 | try:
142 | topLevelPackage = _importAndCheckStack(trialname)
143 | except _NoModuleFound:
144 | moduleNames.pop()
145 | else:
146 | if len(names) == 1:
147 | raise ModuleNotFound("No module named %r" % (name,))
148 | else:
149 | raise ObjectNotFound('%r does not name an object' % (name,))
150 |
151 | obj = topLevelPackage
152 | for n in names[1:]:
153 | obj = getattr(obj, n)
154 |
155 | return obj
156 |
--------------------------------------------------------------------------------
/sample_lambda/python/jsonschema/_utils.py:
--------------------------------------------------------------------------------
1 | import itertools
2 | import json
3 | import pkgutil
4 | import re
5 |
6 | from jsonschema.compat import str_types, MutableMapping, urlsplit
7 |
8 |
9 | class URIDict(MutableMapping):
10 | """
11 | Dictionary which uses normalized URIs as keys.
12 |
13 | """
14 |
15 | def normalize(self, uri):
16 | return urlsplit(uri).geturl()
17 |
18 | def __init__(self, *args, **kwargs):
19 | self.store = dict()
20 | self.store.update(*args, **kwargs)
21 |
22 | def __getitem__(self, uri):
23 | return self.store[self.normalize(uri)]
24 |
25 | def __setitem__(self, uri, value):
26 | self.store[self.normalize(uri)] = value
27 |
28 | def __delitem__(self, uri):
29 | del self.store[self.normalize(uri)]
30 |
31 | def __iter__(self):
32 | return iter(self.store)
33 |
34 | def __len__(self):
35 | return len(self.store)
36 |
37 | def __repr__(self):
38 | return repr(self.store)
39 |
40 |
41 | class Unset(object):
42 | """
43 | An as-of-yet unset attribute or unprovided default parameter.
44 |
45 | """
46 |
47 | def __repr__(self):
48 | return ""
49 |
50 |
51 | def load_schema(name):
52 | """
53 | Load a schema from ./schemas/``name``.json and return it.
54 |
55 | """
56 |
57 | data = pkgutil.get_data('jsonschema', "schemas/{0}.json".format(name))
58 | return json.loads(data.decode("utf-8"))
59 |
60 |
61 | def indent(string, times=1):
62 | """
63 | A dumb version of :func:`textwrap.indent` from Python 3.3.
64 |
65 | """
66 |
67 | return "\n".join(" " * (4 * times) + line for line in string.splitlines())
68 |
69 |
70 | def format_as_index(indices):
71 | """
72 | Construct a single string containing indexing operations for the indices.
73 |
74 | For example, [1, 2, "foo"] -> [1][2]["foo"]
75 |
76 | Arguments:
77 |
78 | indices (sequence):
79 |
80 | The indices to format.
81 |
82 | """
83 |
84 | if not indices:
85 | return ""
86 | return "[%s]" % "][".join(repr(index) for index in indices)
87 |
88 |
89 | def find_additional_properties(instance, schema):
90 | """
91 | Return the set of additional properties for the given ``instance``.
92 |
93 | Weeds out properties that should have been validated by ``properties`` and
94 | / or ``patternProperties``.
95 |
96 | Assumes ``instance`` is dict-like already.
97 |
98 | """
99 |
100 | properties = schema.get("properties", {})
101 | patterns = "|".join(schema.get("patternProperties", {}))
102 | for property in instance:
103 | if property not in properties:
104 | if patterns and re.search(patterns, property):
105 | continue
106 | yield property
107 |
108 |
109 | def extras_msg(extras):
110 | """
111 | Create an error message for extra items or properties.
112 |
113 | """
114 |
115 | if len(extras) == 1:
116 | verb = "was"
117 | else:
118 | verb = "were"
119 | return ", ".join(repr(extra) for extra in extras), verb
120 |
121 |
122 | def types_msg(instance, types):
123 | """
124 | Create an error message for a failure to match the given types.
125 |
126 | If the ``instance`` is an object and contains a ``name`` property, it will
127 | be considered to be a description of that object and used as its type.
128 |
129 | Otherwise the message is simply the reprs of the given ``types``.
130 |
131 | """
132 |
133 | reprs = []
134 | for type in types:
135 | try:
136 | reprs.append(repr(type["name"]))
137 | except Exception:
138 | reprs.append(repr(type))
139 | return "%r is not of type %s" % (instance, ", ".join(reprs))
140 |
141 |
142 | def flatten(suitable_for_isinstance):
143 | """
144 | isinstance() can accept a bunch of really annoying different types:
145 | * a single type
146 | * a tuple of types
147 | * an arbitrary nested tree of tuples
148 |
149 | Return a flattened tuple of the given argument.
150 |
151 | """
152 |
153 | types = set()
154 |
155 | if not isinstance(suitable_for_isinstance, tuple):
156 | suitable_for_isinstance = (suitable_for_isinstance,)
157 | for thing in suitable_for_isinstance:
158 | if isinstance(thing, tuple):
159 | types.update(flatten(thing))
160 | else:
161 | types.add(thing)
162 | return tuple(types)
163 |
164 |
165 | def ensure_list(thing):
166 | """
167 | Wrap ``thing`` in a list if it's a single str.
168 |
169 | Otherwise, return it unchanged.
170 |
171 | """
172 |
173 | if isinstance(thing, str_types):
174 | return [thing]
175 | return thing
176 |
177 |
178 | def unbool(element, true=object(), false=object()):
179 | """
180 | A hack to make True and 1 and False and 0 unique for ``uniq``.
181 |
182 | """
183 |
184 | if element is True:
185 | return true
186 | elif element is False:
187 | return false
188 | return element
189 |
190 |
191 | def uniq(container):
192 | """
193 | Check if all of a container's elements are unique.
194 |
195 | Successively tries first to rely that the elements are hashable, then
196 | falls back on them being sortable, and finally falls back on brute
197 | force.
198 |
199 | """
200 |
201 | try:
202 | return len(set(unbool(i) for i in container)) == len(container)
203 | except TypeError:
204 | try:
205 | sort = sorted(unbool(i) for i in container)
206 | sliced = itertools.islice(sort, 1, None)
207 | for i, j in zip(sort, sliced):
208 | if i == j:
209 | return False
210 | except (NotImplementedError, TypeError):
211 | seen = []
212 | for e in container:
213 | e = unbool(e)
214 | if e in seen:
215 | return False
216 | seen.append(e)
217 | return True
218 |
--------------------------------------------------------------------------------
/sample_backend/lambda/lambda_api/python/jsonschema/_utils.py:
--------------------------------------------------------------------------------
1 | import itertools
2 | import json
3 | import pkgutil
4 | import re
5 |
6 | from jsonschema.compat import str_types, MutableMapping, urlsplit
7 |
8 |
9 | class URIDict(MutableMapping):
10 | """
11 | Dictionary which uses normalized URIs as keys.
12 |
13 | """
14 |
15 | def normalize(self, uri):
16 | return urlsplit(uri).geturl()
17 |
18 | def __init__(self, *args, **kwargs):
19 | self.store = dict()
20 | self.store.update(*args, **kwargs)
21 |
22 | def __getitem__(self, uri):
23 | return self.store[self.normalize(uri)]
24 |
25 | def __setitem__(self, uri, value):
26 | self.store[self.normalize(uri)] = value
27 |
28 | def __delitem__(self, uri):
29 | del self.store[self.normalize(uri)]
30 |
31 | def __iter__(self):
32 | return iter(self.store)
33 |
34 | def __len__(self):
35 | return len(self.store)
36 |
37 | def __repr__(self):
38 | return repr(self.store)
39 |
40 |
41 | class Unset(object):
42 | """
43 | An as-of-yet unset attribute or unprovided default parameter.
44 |
45 | """
46 |
47 | def __repr__(self):
48 | return ""
49 |
50 |
51 | def load_schema(name):
52 | """
53 | Load a schema from ./schemas/``name``.json and return it.
54 |
55 | """
56 |
57 | data = pkgutil.get_data('jsonschema', "schemas/{0}.json".format(name))
58 | return json.loads(data.decode("utf-8"))
59 |
60 |
61 | def indent(string, times=1):
62 | """
63 | A dumb version of :func:`textwrap.indent` from Python 3.3.
64 |
65 | """
66 |
67 | return "\n".join(" " * (4 * times) + line for line in string.splitlines())
68 |
69 |
70 | def format_as_index(indices):
71 | """
72 | Construct a single string containing indexing operations for the indices.
73 |
74 | For example, [1, 2, "foo"] -> [1][2]["foo"]
75 |
76 | Arguments:
77 |
78 | indices (sequence):
79 |
80 | The indices to format.
81 |
82 | """
83 |
84 | if not indices:
85 | return ""
86 | return "[%s]" % "][".join(repr(index) for index in indices)
87 |
88 |
89 | def find_additional_properties(instance, schema):
90 | """
91 | Return the set of additional properties for the given ``instance``.
92 |
93 | Weeds out properties that should have been validated by ``properties`` and
94 | / or ``patternProperties``.
95 |
96 | Assumes ``instance`` is dict-like already.
97 |
98 | """
99 |
100 | properties = schema.get("properties", {})
101 | patterns = "|".join(schema.get("patternProperties", {}))
102 | for property in instance:
103 | if property not in properties:
104 | if patterns and re.search(patterns, property):
105 | continue
106 | yield property
107 |
108 |
109 | def extras_msg(extras):
110 | """
111 | Create an error message for extra items or properties.
112 |
113 | """
114 |
115 | if len(extras) == 1:
116 | verb = "was"
117 | else:
118 | verb = "were"
119 | return ", ".join(repr(extra) for extra in extras), verb
120 |
121 |
122 | def types_msg(instance, types):
123 | """
124 | Create an error message for a failure to match the given types.
125 |
126 | If the ``instance`` is an object and contains a ``name`` property, it will
127 | be considered to be a description of that object and used as its type.
128 |
129 | Otherwise the message is simply the reprs of the given ``types``.
130 |
131 | """
132 |
133 | reprs = []
134 | for type in types:
135 | try:
136 | reprs.append(repr(type["name"]))
137 | except Exception:
138 | reprs.append(repr(type))
139 | return "%r is not of type %s" % (instance, ", ".join(reprs))
140 |
141 |
142 | def flatten(suitable_for_isinstance):
143 | """
144 | isinstance() can accept a bunch of really annoying different types:
145 | * a single type
146 | * a tuple of types
147 | * an arbitrary nested tree of tuples
148 |
149 | Return a flattened tuple of the given argument.
150 |
151 | """
152 |
153 | types = set()
154 |
155 | if not isinstance(suitable_for_isinstance, tuple):
156 | suitable_for_isinstance = (suitable_for_isinstance,)
157 | for thing in suitable_for_isinstance:
158 | if isinstance(thing, tuple):
159 | types.update(flatten(thing))
160 | else:
161 | types.add(thing)
162 | return tuple(types)
163 |
164 |
165 | def ensure_list(thing):
166 | """
167 | Wrap ``thing`` in a list if it's a single str.
168 |
169 | Otherwise, return it unchanged.
170 |
171 | """
172 |
173 | if isinstance(thing, str_types):
174 | return [thing]
175 | return thing
176 |
177 |
178 | def unbool(element, true=object(), false=object()):
179 | """
180 | A hack to make True and 1 and False and 0 unique for ``uniq``.
181 |
182 | """
183 |
184 | if element is True:
185 | return true
186 | elif element is False:
187 | return false
188 | return element
189 |
190 |
191 | def uniq(container):
192 | """
193 | Check if all of a container's elements are unique.
194 |
195 | Successively tries first to rely that the elements are hashable, then
196 | falls back on them being sortable, and finally falls back on brute
197 | force.
198 |
199 | """
200 |
201 | try:
202 | return len(set(unbool(i) for i in container)) == len(container)
203 | except TypeError:
204 | try:
205 | sort = sorted(unbool(i) for i in container)
206 | sliced = itertools.islice(sort, 1, None)
207 | for i, j in zip(sort, sliced):
208 | if i == j:
209 | return False
210 | except (NotImplementedError, TypeError):
211 | seen = []
212 | for e in container:
213 | e = unbool(e)
214 | if e in seen:
215 | return False
216 | seen.append(e)
217 | return True
218 |
--------------------------------------------------------------------------------