├── .builder └── actions │ └── crt-ci-test.py ├── .github ├── ISSUE_TEMPLATE │ ├── bug-report.yml │ ├── config.yml │ ├── documentation.yml │ └── feature-request.yml ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── ci.yml │ ├── ci_run_basic_connect_cfg.json │ ├── ci_run_cognito_connect_cfg.json │ ├── ci_run_custom_authorizer_connect_cfg.json │ ├── ci_run_fleet_provisioning_cfg.json │ ├── ci_run_greengrass_discovery_cfg.json │ ├── ci_run_greengrass_ipc_cfg.json │ ├── ci_run_jobs_cfg.json │ ├── ci_run_mqtt5_custom_authorizer_cfg.json │ ├── ci_run_mqtt5_custom_authorizer_websockets_cfg.json │ ├── ci_run_mqtt5_fleet_provisioning_cfg.json │ ├── ci_run_mqtt5_jobs_cfg.json │ ├── ci_run_mqtt5_pkcs11_connect_cfg.json │ ├── ci_run_mqtt5_pubsub_cfg.json │ ├── ci_run_mqtt5_shadow_cfg.json │ ├── ci_run_mqtt5_shared_subscription_cfg.json │ ├── ci_run_pkcs11_connect_cfg.json │ ├── ci_run_pkcs12_connect_cfg.json │ ├── ci_run_pubsub_cfg.json │ ├── ci_run_shadow_cfg.json │ ├── ci_run_websocket_connect_cfg.json │ ├── ci_run_windows_cert_connect_cfg.json │ ├── ci_run_x509_connect_cfg.json │ ├── closed-issue-message.yml │ ├── handle-stale-discussions.yml │ ├── issue-regression-labeler.yml │ ├── release.yml │ └── stale-issue.yml ├── .gitignore ├── README.md ├── awsiot ├── __init__.py ├── eventstreamrpc.py ├── greengrass_discovery.py ├── greengrasscoreipc │ ├── __init__.py │ ├── client.py │ ├── clientv2.py │ └── model.py ├── iotidentity.py ├── iotjobs.py ├── iotshadow.py ├── mqtt5_client_builder.py └── mqtt_connection_builder.py ├── builder.json ├── codebuild ├── cd │ ├── pip-install-with-retry.py │ ├── publish-to-prod-pypi.yml │ ├── publish-to-test-pypi.yml │ ├── test-prod-pypi.yml │ ├── test-test-pypi.yml │ ├── test-version-exists.sh │ └── test-version-exists.yml └── samples │ ├── connect-linux.sh │ ├── custom-auth-linux.sh │ ├── linux-smoke-tests.yml │ ├── pkcs11-connect-linux.sh │ ├── pubsub-linux.sh │ ├── pubsub-mqtt5-linux.sh │ ├── setup-linux.sh │ └── shadow-linux.sh ├── deviceadvisor ├── script │ ├── DATestConfig.json │ └── DATestRun.py └── tests │ ├── da_test_utils.py │ ├── mqtt_connect.py │ ├── mqtt_publish.py │ ├── mqtt_subscribe.py │ └── shadow_update.py ├── docsrc ├── _static │ ├── .gitkeep │ └── css │ │ └── custom.css ├── _templates │ └── .gitkeep ├── awsiot │ ├── awsiot.rst │ ├── eventstreamrpc.rst │ ├── greengrass_discovery.rst │ ├── greengrasscoreipc.rst │ ├── iotidentity.rst │ ├── iotjobs.rst │ ├── iotshadow.rst │ ├── mqtt5_client_builder.rst │ └── mqtt_connection_builder.rst ├── conf.py └── index.rst ├── documents ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── FAQ.md ├── LICENSE ├── MIGRATION_GUIDE.md ├── MQTT5_Userguide.md ├── NOTICE └── PREREQUISITES.md ├── make-docs.py ├── samples ├── README.md ├── basic_connect.md ├── basic_connect.py ├── basic_discovery.md ├── basic_discovery.py ├── cognito_connect.md ├── cognito_connect.py ├── custom_authorizer_connect.md ├── custom_authorizer_connect.py ├── fleetprovisioning.md ├── fleetprovisioning.py ├── fleetprovisioning_mqtt5.md ├── fleetprovisioning_mqtt5.py ├── ipc_greengrass.md ├── ipc_greengrass.py ├── jobs.md ├── jobs.py ├── jobs_mqtt5.md ├── jobs_mqtt5.py ├── mqtt5_custom_authorizer_connect.md ├── mqtt5_custom_authorizer_connect.py ├── mqtt5_pkcs11_connect.md ├── mqtt5_pkcs11_connect.py ├── mqtt5_pubsub.md ├── mqtt5_pubsub.py ├── mqtt5_shared_subscription.md ├── mqtt5_shared_subscription.py ├── pkcs11_connect.md ├── pkcs11_connect.py ├── pkcs12_connect.md ├── pkcs12_connect.py ├── pubsub.md ├── pubsub.py ├── shadow.md ├── shadow.py ├── shadow_mqtt5.md ├── shadow_mqtt5.py ├── utils │ └── command_line_utils.py ├── websocket_connect.md ├── websocket_connect.py ├── windows_cert_connect.md ├── windows_cert_connect.py ├── x509_connect.md └── x509_connect.py ├── servicetests ├── test_cases │ ├── mqtt3_fleet_provisioning_cfg.json │ ├── mqtt3_fleet_provisioning_with_csr_cfg.json │ ├── mqtt3_jobs_cfg.json │ ├── mqtt3_named_shadow_cfg.json │ ├── mqtt3_shadow_cfg.json │ ├── mqtt5_fleet_provisioning_cfg.json │ ├── mqtt5_fleet_provisioning_with_csr_cfg.json │ ├── mqtt5_jobs_cfg.json │ ├── mqtt5_named_shadow_cfg.json │ ├── mqtt5_shadow_cfg.json │ ├── test_fleet_provisioning.py │ ├── test_jobs_execution.py │ └── test_shadow_update.py └── tests │ ├── FleetProvisioning │ └── fleet_provisioning.py │ ├── JobsExecution │ └── jobs.py │ └── ShadowUpdate │ └── shadow_update.py ├── setup.cfg ├── setup.py ├── test ├── __init__.py ├── echotestrpc │ ├── __init__.py │ ├── client.py │ └── model.py ├── greengrass │ ├── basic_discovery │ │ ├── README.md │ │ ├── copy_files.sh │ │ ├── gdk-config.json │ │ ├── gg-e2e-tests │ │ │ ├── pom.xml │ │ │ └── src │ │ │ │ └── main │ │ │ │ └── resources │ │ │ │ └── greengrass │ │ │ │ └── features │ │ │ │ └── component.feature │ │ ├── hello_world_subscriber.py │ │ └── recipe.yaml │ └── ipc │ │ ├── README.md │ │ ├── copy_files.sh │ │ ├── gdk-config.json │ │ ├── gg-e2e-tests │ │ ├── pom.xml │ │ └── src │ │ │ └── main │ │ │ └── resources │ │ │ └── greengrass │ │ │ └── features │ │ │ └── component.feature │ │ └── recipe.yaml ├── test_ggv2.py ├── test_mqtt.py ├── test_mqtt5.py ├── test_rpc.py ├── test_samples.py └── test_shadow_state.py └── utils ├── check_codegen_edits.py ├── ci_iot_thing.py ├── delete_iot_thing_ci.py ├── mqtt5_test_setup.sh ├── parse_cert_set_result.py ├── publish-release.sh ├── run_in_ci.py ├── run_sample_ci.py └── update_semantic_version.py /.builder/actions/crt-ci-test.py: -------------------------------------------------------------------------------- 1 | import Builder 2 | import re 3 | 4 | class CiTest(Builder.Action): 5 | 6 | def _write_environment_script_secret_to_env(self, env, secret_name): 7 | mqtt5_ci_environment_script = env.shell.get_secret(secret_name) 8 | env_line = re.compile('^export\s+(\w+)=(.+)') 9 | 10 | lines = mqtt5_ci_environment_script.splitlines() 11 | for line in lines: 12 | env_pair_match = env_line.match(line) 13 | if env_pair_match.group(1) and env_pair_match.group(2): 14 | env.shell.setenv(env_pair_match.group(1), env_pair_match.group(2), quiet=True) 15 | 16 | 17 | def run(self, env): 18 | 19 | actions = [] 20 | 21 | try: 22 | self._write_environment_script_secret_to_env(env, "ci/sdk-unit-testing") 23 | 24 | env.shell.exec(["python3", "-m", "unittest", "discover", "--verbose"], check=True) 25 | except: 26 | print(f'Failure while running tests') 27 | actions.append("exit 1") 28 | 29 | return Builder.Script(actions, name='ci-test') 30 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: "🐛 Bug Report" 3 | description: Report a bug 4 | title: "(short issue description)" 5 | labels: [bug, needs-triage] 6 | assignees: [] 7 | body: 8 | - type: textarea 9 | id: description 10 | attributes: 11 | label: Describe the bug 12 | description: What is the problem? A clear and concise description of the bug. 13 | validations: 14 | required: true 15 | - type: checkboxes 16 | id: regression 17 | attributes: 18 | label: Regression Issue 19 | description: What is a regression? If it worked in a previous version but doesn't in the latest version, it's considered a regression. In this case, please provide specific version number in the report. 20 | options: 21 | - label: Select this option if this issue appears to be a regression. 22 | required: false 23 | - type: textarea 24 | id: expected 25 | attributes: 26 | label: Expected Behavior 27 | description: | 28 | What did you expect to happen? 29 | validations: 30 | required: true 31 | - type: textarea 32 | id: current 33 | attributes: 34 | label: Current Behavior 35 | description: | 36 | What actually happened? 37 | 38 | Please include full errors, uncaught exceptions, stack traces, and relevant logs. 39 | If service responses are relevant, please include wire logs. 40 | validations: 41 | required: true 42 | - type: textarea 43 | id: reproduction 44 | attributes: 45 | label: Reproduction Steps 46 | description: | 47 | Provide a self-contained, concise snippet of code that can be used to reproduce the issue. 48 | For more complex issues provide a repo with the smallest sample that reproduces the bug. 49 | 50 | Avoid including business logic or unrelated code, it makes diagnosis more difficult. 51 | The code sample should be an SSCCE. See http://sscce.org/ for details. In short, please provide a code sample that we can copy/paste, run and reproduce. 52 | validations: 53 | required: true 54 | - type: textarea 55 | id: solution 56 | attributes: 57 | label: Possible Solution 58 | description: | 59 | Suggest a fix/reason for the bug 60 | validations: 61 | required: false 62 | - type: textarea 63 | id: context 64 | attributes: 65 | label: Additional Information/Context 66 | description: | 67 | Anything else that might be relevant for troubleshooting this bug. Providing context helps us come up with a solution that is most useful in the real world. 68 | validations: 69 | required: false 70 | - type: input 71 | id: sdk-version 72 | attributes: 73 | label: SDK version used 74 | validations: 75 | required: true 76 | - type: input 77 | id: environment 78 | attributes: 79 | label: Environment details (OS name and version, etc.) 80 | validations: 81 | required: true 82 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | --- 2 | blank_issues_enabled: false 3 | contact_links: 4 | - name: 💬 General Question 5 | url: https://github.com/aws/aws-iot-device-sdk-python-v2/discussions/categories/q-a 6 | about: Please ask and answer questions as a discussion thread 7 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/documentation.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: "📕 Documentation Issue" 3 | description: Report an issue in the API Reference documentation or Developer Guide 4 | title: "(short issue description)" 5 | labels: [documentation, needs-triage] 6 | assignees: [] 7 | body: 8 | - type: textarea 9 | id: description 10 | attributes: 11 | label: Describe the issue 12 | description: A clear and concise description of the issue. 13 | validations: 14 | required: true 15 | 16 | - type: textarea 17 | id: links 18 | attributes: 19 | label: Links 20 | description: | 21 | Include links to affected documentation page(s). 22 | validations: 23 | required: true 24 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🚀 Feature Request 3 | description: Suggest an idea for this project 4 | title: "(short issue description)" 5 | labels: [feature-request, needs-triage] 6 | assignees: [] 7 | body: 8 | - type: textarea 9 | id: description 10 | attributes: 11 | label: Describe the feature 12 | description: A clear and concise description of the feature you are proposing. 13 | validations: 14 | required: true 15 | - type: textarea 16 | id: use-case 17 | attributes: 18 | label: Use Case 19 | description: | 20 | Why do you need this feature? For example: "I'm always frustrated when..." 21 | validations: 22 | required: true 23 | - type: textarea 24 | id: solution 25 | attributes: 26 | label: Proposed Solution 27 | description: | 28 | Suggest how to implement the addition or change. Please include prototype/workaround/sketch/reference implementation. 29 | validations: 30 | required: false 31 | - type: textarea 32 | id: other 33 | attributes: 34 | label: Other Information 35 | description: | 36 | Any alternative solutions or features you considered, a more detailed explanation, stack traces, related issues, links for context, etc. 37 | validations: 38 | required: false 39 | - type: checkboxes 40 | id: ack 41 | attributes: 42 | label: Acknowledgements 43 | options: 44 | - label: I may be able to implement this feature request 45 | required: false 46 | - label: This feature might incur a breaking change 47 | required: false 48 | - type: input 49 | id: sdk-version 50 | attributes: 51 | label: SDK version used 52 | validations: 53 | required: true 54 | - type: input 55 | id: environment 56 | attributes: 57 | label: Environment details (OS name and version, etc.) 58 | validations: 59 | required: true 60 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | *Issue #, if available:* 2 | 3 | *Description of changes:* 4 | 5 | 6 | By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license. 7 | -------------------------------------------------------------------------------- /.github/workflows/ci_run_basic_connect_cfg.json: -------------------------------------------------------------------------------- 1 | { 2 | "language": "Python", 3 | "sample_file": "./aws-iot-device-sdk-python-v2/samples/basic_connect.py", 4 | "sample_region": "us-east-1", 5 | "sample_main_class": "", 6 | "arguments": [ 7 | { 8 | "name": "--endpoint", 9 | "secret": "ci/endpoint" 10 | }, 11 | { 12 | "name": "--cert", 13 | "secret": "ci/PubSub/cert", 14 | "filename": "tmp_certificate.pem" 15 | }, 16 | { 17 | "name": "--key", 18 | "secret": "ci/PubSub/key", 19 | "filename": "tmp_key.pem" 20 | }, 21 | { 22 | "name": "--is_ci", 23 | "data": "true" 24 | } 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /.github/workflows/ci_run_cognito_connect_cfg.json: -------------------------------------------------------------------------------- 1 | { 2 | "language": "Python", 3 | "sample_file": "./aws-iot-device-sdk-python-v2/samples/cognito_connect.py", 4 | "sample_region": "us-east-1", 5 | "sample_main_class": "", 6 | "arguments": [ 7 | { 8 | "name": "--endpoint", 9 | "secret": "ci/endpoint" 10 | }, 11 | { 12 | "name": "--signing_region", 13 | "data": "us-east-1" 14 | }, 15 | { 16 | "name": "--cognito_identity", 17 | "secret": "ci/Cognito/identity_id" 18 | }, 19 | { 20 | "name": "--is_ci", 21 | "data": "true" 22 | } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /.github/workflows/ci_run_custom_authorizer_connect_cfg.json: -------------------------------------------------------------------------------- 1 | { 2 | "language": "Python", 3 | "sample_file": "./aws-iot-device-sdk-python-v2/samples/custom_authorizer_connect.py", 4 | "sample_region": "us-east-1", 5 | "sample_main_class": "", 6 | "arguments": [ 7 | { 8 | "name": "--endpoint", 9 | "secret": "ci/endpoint" 10 | }, 11 | { 12 | "name": "--custom_auth_authorizer_name", 13 | "secret": "ci/CustomAuthorizer/name" 14 | }, 15 | { 16 | "name": "--custom_auth_password", 17 | "secret": "ci/CustomAuthorizer/password" 18 | }, 19 | { 20 | "name": "--is_ci", 21 | "data": "true" 22 | } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /.github/workflows/ci_run_fleet_provisioning_cfg.json: -------------------------------------------------------------------------------- 1 | { 2 | "language": "Python", 3 | "sample_file": "./aws-iot-device-sdk-python-v2/samples/fleetprovisioning.py", 4 | "sample_region": "us-east-1", 5 | "sample_main_class": "", 6 | "arguments": [ 7 | { 8 | "name": "--endpoint", 9 | "secret": "ci/endpoint" 10 | }, 11 | { 12 | "name": "--cert", 13 | "secret": "ci/FleetProvisioning/cert", 14 | "filename": "tmp_certificate.pem" 15 | }, 16 | { 17 | "name": "--key", 18 | "secret": "ci/FleetProvisioning/key", 19 | "filename": "tmp_key.pem" 20 | }, 21 | { 22 | "name": "--template_name", 23 | "data": "CI_FleetProvisioning_Template" 24 | }, 25 | { 26 | "name": "--template_parameters", 27 | "data": "{\"SerialNumber\":\"$INPUT_UUID\"}" 28 | }, 29 | { 30 | "name": "--is_ci", 31 | "data": "true" 32 | } 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /.github/workflows/ci_run_greengrass_discovery_cfg.json: -------------------------------------------------------------------------------- 1 | { 2 | "language": "Python", 3 | "runnable_file": "basic_discovery.py", 4 | "runnable_region": "us-east-1", 5 | "runnable_main_class": "", 6 | "arguments": [ 7 | { 8 | "name": "--cert", 9 | "secret": "ci/GreengrassDiscovery/cert", 10 | "filename": "tmp_certificate.pem" 11 | }, 12 | { 13 | "name": "--key", 14 | "secret": "ci/GreengrassDiscovery/key", 15 | "filename": "tmp_key.pem" 16 | }, 17 | { 18 | "name": "--thing_name", 19 | "data": "CI_Greengrass_Discovery_Thing" 20 | }, 21 | { 22 | "name": "--region", 23 | "data": "us-east-1" 24 | }, 25 | { 26 | "name": "--topic", 27 | "data": "clients/CI_Greengrass_Discovery_Thing/hello/world/$INPUT_UUID" 28 | }, 29 | { 30 | "name": "--mode", 31 | "data": "publish" 32 | } 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /.github/workflows/ci_run_greengrass_ipc_cfg.json: -------------------------------------------------------------------------------- 1 | { 2 | "language": "Python", 3 | "runnable_file": "ipc_greengrass.py", 4 | "runnable_region": "us-east-1", 5 | "runnable_main_class": "", 6 | "arguments": [ 7 | { 8 | "name": "--topic", 9 | "data": "test/gg-ipc-topic" 10 | }, 11 | { 12 | "name": "--message", 13 | "data": "hello" 14 | }, 15 | { 16 | "name": "--is_ci", 17 | "data": "true" 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /.github/workflows/ci_run_jobs_cfg.json: -------------------------------------------------------------------------------- 1 | { 2 | "language": "Python", 3 | "sample_file": "./aws-iot-device-sdk-python-v2/samples/jobs.py", 4 | "sample_region": "us-east-1", 5 | "sample_main_class": "", 6 | "arguments": [ 7 | { 8 | "name": "--endpoint", 9 | "secret": "ci/endpoint" 10 | }, 11 | { 12 | "name": "--cert", 13 | "secret": "ci/Jobs/cert", 14 | "filename": "tmp_certificate.pem" 15 | }, 16 | { 17 | "name": "--key", 18 | "secret": "ci/Jobs/key", 19 | "filename": "tmp_key.pem" 20 | }, 21 | { 22 | "name": "--thing_name", 23 | "data": "CI_Jobs_Thing" 24 | }, 25 | { 26 | "name": "--is_ci", 27 | "data": "true" 28 | } 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /.github/workflows/ci_run_mqtt5_custom_authorizer_cfg.json: -------------------------------------------------------------------------------- 1 | { 2 | "language": "Python", 3 | "sample_file": "./aws-iot-device-sdk-python-v2/samples/mqtt5_custom_authorizer_connect.py", 4 | "sample_region": "us-east-1", 5 | "sample_main_class": "", 6 | "arguments": [ 7 | { 8 | "name": "--endpoint", 9 | "secret": "ci/endpoint" 10 | }, 11 | { 12 | "name": "--custom_auth_authorizer_name", 13 | "secret": "ci/CustomAuthorizer/name" 14 | }, 15 | { 16 | "name": "--custom_auth_password", 17 | "secret": "ci/CustomAuthorizer/password" 18 | }, 19 | { 20 | "name": "--is_ci", 21 | "data": "true" 22 | } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /.github/workflows/ci_run_mqtt5_custom_authorizer_websockets_cfg.json: -------------------------------------------------------------------------------- 1 | { 2 | "language": "Python", 3 | "sample_file": "./aws-iot-device-sdk-python-v2/samples/mqtt5_custom_authorizer_connect.py", 4 | "sample_region": "us-east-1", 5 | "sample_main_class": "", 6 | "arguments": [ 7 | { 8 | "name": "--endpoint", 9 | "secret": "ci/endpoint" 10 | }, 11 | { 12 | "name": "--custom_auth_authorizer_name", 13 | "secret": "ci/CustomAuthorizer/name" 14 | }, 15 | { 16 | "name": "--custom_auth_password", 17 | "secret": "ci/CustomAuthorizer/password" 18 | }, 19 | { 20 | "name": "--use_websockets", 21 | "data": "true" 22 | }, 23 | { 24 | "name": "--is_ci", 25 | "data": "true" 26 | } 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /.github/workflows/ci_run_mqtt5_fleet_provisioning_cfg.json: -------------------------------------------------------------------------------- 1 | { 2 | "language": "Python", 3 | "sample_file": "./aws-iot-device-sdk-python-v2/samples/fleetprovisioning_mqtt5.py", 4 | "sample_region": "us-east-1", 5 | "sample_main_class": "", 6 | "arguments": [ 7 | { 8 | "name": "--endpoint", 9 | "secret": "ci/endpoint" 10 | }, 11 | { 12 | "name": "--cert", 13 | "secret": "ci/FleetProvisioning/cert", 14 | "filename": "tmp_certificate.pem" 15 | }, 16 | { 17 | "name": "--key", 18 | "secret": "ci/FleetProvisioning/key", 19 | "filename": "tmp_key.pem" 20 | }, 21 | { 22 | "name": "--template_name", 23 | "data": "CI_FleetProvisioning_Template" 24 | }, 25 | { 26 | "name": "--template_parameters", 27 | "data": "{\"SerialNumber\":\"$INPUT_UUID\"}" 28 | }, 29 | { 30 | "name": "--is_ci", 31 | "data": "true" 32 | } 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /.github/workflows/ci_run_mqtt5_jobs_cfg.json: -------------------------------------------------------------------------------- 1 | { 2 | "language": "Python", 3 | "sample_file": "./aws-iot-device-sdk-python-v2/samples/jobs_mqtt5.py", 4 | "sample_region": "us-east-1", 5 | "sample_main_class": "", 6 | "arguments": [ 7 | { 8 | "name": "--endpoint", 9 | "secret": "ci/endpoint" 10 | }, 11 | { 12 | "name": "--cert", 13 | "secret": "ci/Jobs/cert", 14 | "filename": "tmp_certificate.pem" 15 | }, 16 | { 17 | "name": "--key", 18 | "secret": "ci/Jobs/key", 19 | "filename": "tmp_key.pem" 20 | }, 21 | { 22 | "name": "--thing_name", 23 | "data": "CI_Jobs_Thing" 24 | }, 25 | { 26 | "name": "--is_ci", 27 | "data": "true" 28 | } 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /.github/workflows/ci_run_mqtt5_pkcs11_connect_cfg.json: -------------------------------------------------------------------------------- 1 | { 2 | "language": "Python", 3 | "sample_file": "./aws-iot-device-sdk-python-v2/samples/mqtt5_pkcs11_connect.py", 4 | "sample_region": "us-east-1", 5 | "sample_main_class": "", 6 | "arguments": [ 7 | { 8 | "name": "--endpoint", 9 | "secret": "ci/endpoint" 10 | }, 11 | { 12 | "name": "--cert", 13 | "secret": "ci/PubSub/cert", 14 | "filename": "tmp_certificate.pem" 15 | }, 16 | { 17 | "name": "--key", 18 | "secret": "ci/PubSub/keyp8", 19 | "filename": "tmp_key.pem", 20 | "pkcs11_key": "true" 21 | }, 22 | { 23 | "name": "--pkcs11_lib", 24 | "data": "/usr/lib/softhsm/libsofthsm2.so" 25 | }, 26 | { 27 | "name": "--pin", 28 | "data": "0000" 29 | }, 30 | { 31 | "name": "--token_label", 32 | "data": "my-token" 33 | }, 34 | { 35 | "name": "--key_label", 36 | "data": "my-key" 37 | }, 38 | { 39 | "name": "--is_ci", 40 | "data": "true" 41 | } 42 | ] 43 | } 44 | -------------------------------------------------------------------------------- /.github/workflows/ci_run_mqtt5_pubsub_cfg.json: -------------------------------------------------------------------------------- 1 | { 2 | "language": "Python", 3 | "sample_file": "./aws-iot-device-sdk-python-v2/samples/mqtt5_pubsub.py", 4 | "sample_region": "us-east-1", 5 | "sample_main_class": "mqtt5.pubsub.PubSub", 6 | "arguments": [ 7 | { 8 | "name": "--endpoint", 9 | "secret": "ci/endpoint" 10 | }, 11 | { 12 | "name": "--cert", 13 | "secret": "ci/mqtt5/us/mqtt5_thing/cert", 14 | "filename": "tmp_certificate.pem" 15 | }, 16 | { 17 | "name": "--key", 18 | "secret": "ci/mqtt5/us/mqtt5_thing/key", 19 | "filename": "tmp_key.pem" 20 | }, 21 | { 22 | "name": "--is_ci", 23 | "data": "true" 24 | } 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /.github/workflows/ci_run_mqtt5_shadow_cfg.json: -------------------------------------------------------------------------------- 1 | { 2 | "language": "Python", 3 | "sample_file": "./aws-iot-device-sdk-python-v2/samples/shadow_mqtt5.py", 4 | "sample_region": "us-east-1", 5 | "sample_main_class": "", 6 | "arguments": [ 7 | { 8 | "name": "--endpoint", 9 | "secret": "ci/endpoint" 10 | }, 11 | { 12 | "name": "--cert", 13 | "secret": "ci/Shadow/cert", 14 | "filename": "tmp_certificate.pem" 15 | }, 16 | { 17 | "name": "--key", 18 | "secret": "ci/Shadow/key", 19 | "filename": "tmp_key.pem" 20 | }, 21 | { 22 | "name": "--thing_name", 23 | "data": "CI_Shadow_Thing" 24 | }, 25 | { 26 | "name": "--is_ci", 27 | "data": "true" 28 | } 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /.github/workflows/ci_run_mqtt5_shared_subscription_cfg.json: -------------------------------------------------------------------------------- 1 | { 2 | "language": "Python", 3 | "sample_file": "./aws-iot-device-sdk-python-v2/samples/mqtt5_shared_subscription.py", 4 | "sample_region": "us-east-1", 5 | "sample_main_class": "", 6 | "arguments": [ 7 | { 8 | "name": "--endpoint", 9 | "secret": "ci/endpoint" 10 | }, 11 | { 12 | "name": "--cert", 13 | "secret": "ci/mqtt5/us/mqtt5_thing/cert", 14 | "filename": "tmp_certificate.pem" 15 | }, 16 | { 17 | "name": "--key", 18 | "secret": "ci/mqtt5/us/mqtt5_thing/key", 19 | "filename": "tmp_key.pem" 20 | }, 21 | { 22 | "name": "--is_ci", 23 | "data": "true" 24 | } 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /.github/workflows/ci_run_pkcs11_connect_cfg.json: -------------------------------------------------------------------------------- 1 | { 2 | "language": "Python", 3 | "sample_file": "./aws-iot-device-sdk-python-v2/samples/pkcs11_connect.py", 4 | "sample_region": "us-east-1", 5 | "sample_main_class": "", 6 | "arguments": [ 7 | { 8 | "name": "--endpoint", 9 | "secret": "ci/endpoint" 10 | }, 11 | { 12 | "name": "--cert", 13 | "secret": "ci/PubSub/cert", 14 | "filename": "tmp_certificate.pem" 15 | }, 16 | { 17 | "name": "--key", 18 | "secret": "ci/PubSub/keyp8", 19 | "filename": "tmp_key.pem", 20 | "pkcs11_key": "true" 21 | }, 22 | { 23 | "name": "--pkcs11_lib", 24 | "data": "/usr/lib/softhsm/libsofthsm2.so" 25 | }, 26 | { 27 | "name": "--pin", 28 | "data": "0000" 29 | }, 30 | { 31 | "name": "--token_label", 32 | "data": "my-token" 33 | }, 34 | { 35 | "name": "--key_label", 36 | "data": "my-key" 37 | }, 38 | { 39 | "name": "--is_ci", 40 | "data": "true" 41 | } 42 | ] 43 | } 44 | -------------------------------------------------------------------------------- /.github/workflows/ci_run_pkcs12_connect_cfg.json: -------------------------------------------------------------------------------- 1 | { 2 | "language": "Python", 3 | "sample_file": "./aws-iot-device-sdk-python-v2/samples/pkcs12_connect.py", 4 | "sample_region": "us-east-1", 5 | "sample_main_class": "", 6 | "arguments": [ 7 | { 8 | "name": "--endpoint", 9 | "secret": "ci/endpoint" 10 | }, 11 | { 12 | "name": "--pkcs12_file", 13 | "data": "./pkcs12-key.p12" 14 | }, 15 | { 16 | "name": "--pkcs12_password", 17 | "secret": "ci/PubSub/key_pkcs12_password" 18 | }, 19 | { 20 | "name": "--is_ci", 21 | "data": "true" 22 | } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /.github/workflows/ci_run_pubsub_cfg.json: -------------------------------------------------------------------------------- 1 | { 2 | "language": "Python", 3 | "sample_file": "./aws-iot-device-sdk-python-v2/samples/pubsub.py", 4 | "sample_region": "us-east-1", 5 | "sample_main_class": "", 6 | "arguments": [ 7 | { 8 | "name": "--endpoint", 9 | "secret": "ci/endpoint" 10 | }, 11 | { 12 | "name": "--cert", 13 | "secret": "ci/PubSub/cert", 14 | "filename": "tmp_certificate.pem" 15 | }, 16 | { 17 | "name": "--key", 18 | "secret": "ci/PubSub/key", 19 | "filename": "tmp_key.pem" 20 | }, 21 | { 22 | "name": "--is_ci", 23 | "data": "true" 24 | } 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /.github/workflows/ci_run_shadow_cfg.json: -------------------------------------------------------------------------------- 1 | { 2 | "language": "Python", 3 | "sample_file": "./aws-iot-device-sdk-python-v2/samples/shadow.py", 4 | "sample_region": "us-east-1", 5 | "sample_main_class": "", 6 | "arguments": [ 7 | { 8 | "name": "--endpoint", 9 | "secret": "ci/endpoint" 10 | }, 11 | { 12 | "name": "--cert", 13 | "secret": "ci/Shadow/cert", 14 | "filename": "tmp_certificate.pem" 15 | }, 16 | { 17 | "name": "--key", 18 | "secret": "ci/Shadow/key", 19 | "filename": "tmp_key.pem" 20 | }, 21 | { 22 | "name": "--thing_name", 23 | "data": "CI_Shadow_Thing" 24 | }, 25 | { 26 | "name": "--is_ci", 27 | "data": "true" 28 | } 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /.github/workflows/ci_run_websocket_connect_cfg.json: -------------------------------------------------------------------------------- 1 | { 2 | "language": "Python", 3 | "sample_file": "./aws-iot-device-sdk-python-v2/samples/websocket_connect.py", 4 | "sample_region": "us-east-1", 5 | "sample_main_class": "", 6 | "arguments": [ 7 | { 8 | "name": "--endpoint", 9 | "secret": "ci/endpoint" 10 | }, 11 | { 12 | "name": "--signing_region", 13 | "data": "us-east-1" 14 | }, 15 | { 16 | "name": "--is_ci", 17 | "data": "true" 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /.github/workflows/ci_run_windows_cert_connect_cfg.json: -------------------------------------------------------------------------------- 1 | { 2 | "language": "Python", 3 | "sample_file": "./aws-iot-device-sdk-python-v2/samples/windows_cert_connect.py", 4 | "sample_region": "us-east-1", 5 | "sample_main_class": "", 6 | "arguments": [ 7 | { 8 | "name": "--endpoint", 9 | "secret": "ci/endpoint" 10 | }, 11 | { 12 | "name": "--cert", 13 | "windows_cert_certificate": "ci/PubSub/cert", 14 | "windows_cert_certificate_path": "tmp_cert.pem", 15 | "windows_cert_key": "ci/PubSub/key", 16 | "windows_cert_key_path": "tmp_key.pem", 17 | "windows_cert_pfx_key_path": "tmp_pfx.pem" 18 | }, 19 | { 20 | "name": "--is_ci", 21 | "data": "true" 22 | } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /.github/workflows/ci_run_x509_connect_cfg.json: -------------------------------------------------------------------------------- 1 | { 2 | "language": "Python", 3 | "sample_file": "./aws-iot-device-sdk-python-v2/samples/x509_connect.py", 4 | "sample_region": "us-east-1", 5 | "sample_main_class": "", 6 | "arguments": [ 7 | { 8 | "name": "--endpoint", 9 | "secret": "ci/endpoint" 10 | }, 11 | { 12 | "name": "--x509_cert", 13 | "secret": "ci/PubSub/cert", 14 | "filename": "tmp_certificate.pem" 15 | }, 16 | { 17 | "name": "--x509_key", 18 | "secret": "ci/PubSub/key", 19 | "filename": "tmp_key.pem" 20 | }, 21 | { 22 | "name": "--x509_endpoint", 23 | "secret": "ci/X509/endpoint_credentials" 24 | }, 25 | { 26 | "name": "--x509_role_alias", 27 | "secret": "ci/X509/alias" 28 | }, 29 | { 30 | "name": "--signing_region", 31 | "data": "us-east-1" 32 | }, 33 | { 34 | "name": "--x509_thing_name", 35 | "data": "CI_PubSub_Thing" 36 | }, 37 | { 38 | "name": "--is_ci", 39 | "data": "true" 40 | } 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /.github/workflows/closed-issue-message.yml: -------------------------------------------------------------------------------- 1 | name: Closed Issue Message 2 | on: 3 | issues: 4 | types: [closed] 5 | jobs: 6 | auto_comment: 7 | runs-on: ubuntu-latest 8 | permissions: 9 | issues: write 10 | steps: 11 | - uses: aws-actions/closed-issue-message@v1 12 | with: 13 | # These inputs are both required 14 | repo-token: "${{ secrets.GITHUB_TOKEN }}" 15 | message: | 16 | This issue is now closed. Comments on closed issues are hard for our team to see. 17 | If you need more assistance, please open a new issue that references this one. 18 | -------------------------------------------------------------------------------- /.github/workflows/handle-stale-discussions.yml: -------------------------------------------------------------------------------- 1 | name: HandleStaleDiscussions 2 | on: 3 | schedule: 4 | - cron: '0 */4 * * *' 5 | discussion_comment: 6 | types: [created] 7 | 8 | jobs: 9 | handle-stale-discussions: 10 | name: Handle stale discussions 11 | runs-on: ubuntu-latest 12 | permissions: 13 | discussions: write 14 | steps: 15 | - name: Stale discussions action 16 | uses: aws-github-ops/handle-stale-discussions@v1 17 | env: 18 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} 19 | -------------------------------------------------------------------------------- /.github/workflows/issue-regression-labeler.yml: -------------------------------------------------------------------------------- 1 | # Apply potential regression label on issues 2 | name: issue-regression-label 3 | on: 4 | issues: 5 | types: [opened, edited] 6 | jobs: 7 | add-regression-label: 8 | runs-on: ubuntu-latest 9 | permissions: 10 | issues: write 11 | steps: 12 | - name: Fetch template body 13 | id: check_regression 14 | uses: actions/github-script@v7 15 | env: 16 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 17 | TEMPLATE_BODY: ${{ github.event.issue.body }} 18 | with: 19 | script: | 20 | const regressionPattern = /\[x\] Select this option if this issue appears to be a regression\./i; 21 | const template = `${process.env.TEMPLATE_BODY}` 22 | const match = regressionPattern.test(template); 23 | core.setOutput('is_regression', match); 24 | - name: Manage regression label 25 | env: 26 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 27 | run: | 28 | if [ "${{ steps.check_regression.outputs.is_regression }}" == "true" ]; then 29 | gh issue edit ${{ github.event.issue.number }} --add-label "potential-regression" -R ${{ github.repository }} 30 | else 31 | gh issue edit ${{ github.event.issue.number }} --remove-label "potential-regression" -R ${{ github.repository }} 32 | fi 33 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | env: 4 | GITHUB_TOKEN: ${{ secrets.TAG_PR_TOKEN }} 5 | 6 | on: 7 | workflow_dispatch: 8 | inputs: 9 | release_type: 10 | type: choice 11 | description: "Release type:" 12 | required: true 13 | options: 14 | - bug fix (PATCH) 15 | - new feature (MINOR) 16 | release_title: 17 | description: "The title of the release" 18 | required: true 19 | 20 | jobs: 21 | update-version: 22 | runs-on: ubuntu-20.04 # latest 23 | permissions: 24 | contents: write # allow push 25 | pull-requests: write # allow making PR 26 | id-token: write # This is required for requesting the JWT 27 | 28 | steps: 29 | - name: Checkout Sources 30 | uses: actions/checkout@v2 31 | with: 32 | fetch-depth: 0 33 | 34 | - name: Make new release 35 | env: 36 | Title: ${{ github.event.inputs.release_title }} 37 | run: | 38 | # Escape special characters 39 | Title=$(echo ${Title//[\"]\\\"}) 40 | Title=$(echo ${Title//[\']\\\'}) 41 | Title=$(echo ${Title//[\$]}) 42 | 43 | ./utils/publish-release.sh "${{ github.event.inputs.release_type }}" "$Title" 44 | 45 | - name: Generate documentation 46 | run: | 47 | python3 -m pip install sphinx 48 | python3 -m pip install --verbose . 49 | ./make-docs.py 50 | 51 | - name: Commit documentation 52 | run: | 53 | git config --local user.email "action@github.com" 54 | git config --local user.name "GitHub Action" 55 | git add --force docs/ 56 | current_version=$(git describe --tags --abbrev=0) 57 | git commit --message="Update Docs: ${current_version}" 58 | 59 | - name: Push generated documentation to docs branch 60 | uses: ad-m/github-push-action@v0.6.0 61 | with: 62 | github_token: ${{ github.token }} 63 | branch: docs 64 | # Force push so that `docs` branch always looks like `main`, 65 | # but with 1 additional "update docs" commit. 66 | # This seems simpler than trying to cleanly merge `main` into 67 | # `docs` each time. 68 | force: true 69 | 70 | - name: configure AWS credentials (Release) 71 | uses: aws-actions/configure-aws-credentials@v2 72 | with: 73 | role-to-assume: arn:aws:iam::180635532705:role/CI_V2_RELEASE_S3_ROLE 74 | aws-region: us-east-1 75 | 76 | - name: "Create VERSION file and trigger release" 77 | run: | 78 | version=$(git describe --tags --abbrev=0) 79 | version_without_v=$(echo ${version} | cut -f2 -dv) 80 | echo "${version_without_v}" > VERSION 81 | 82 | zip VERSION.zip VERSION 83 | export S3_URL=$(aws secretsmanager get-secret-value --secret-id ci/python_v2_version --query "SecretString" | cut -f2 -d\") 84 | aws s3 cp VERSION.zip $S3_URL 85 | -------------------------------------------------------------------------------- /.github/workflows/stale-issue.yml: -------------------------------------------------------------------------------- 1 | name: "Close stale issues" 2 | 3 | # Controls when the action will run. 4 | on: 5 | schedule: 6 | - cron: "*/60 * * * *" 7 | 8 | jobs: 9 | cleanup: 10 | runs-on: ubuntu-latest 11 | name: Stale issue job 12 | permissions: 13 | issues: write 14 | pull-requests: write 15 | steps: 16 | - uses: aws-actions/stale-issue-cleanup@v3 17 | with: 18 | # Setting messages to an empty string will cause the automation to skip 19 | # that category 20 | ancient-issue-message: Greetings! Sorry to say but this is a very old issue that is probably not getting as much attention as it deservers. We encourage you to check if this is still an issue in the latest release and if you find that this is still a problem, please feel free to open a new one. 21 | stale-issue-message: Greetings! It looks like this issue hasn’t been active in longer than a week. We encourage you to check if this is still an issue in the latest release. Because it has been longer than a week since the last update on this, and in the absence of more information, we will be closing this issue soon. If you find that this is still a problem, please feel free to provide a comment or add an upvote to prevent automatic closure, or if the issue is already closed, please feel free to open a new one. 22 | stale-pr-message: Greetings! It looks like this PR hasn’t been active in longer than a week, add a comment or an upvote to prevent automatic closure, or if the issue is already closed, please feel free to open a new one. 23 | 24 | # These labels are required 25 | stale-issue-label: closing-soon 26 | exempt-issue-label: investigating, automation-exempt 27 | stale-pr-label: closing-soon 28 | exempt-pr-label: pr/needs-review 29 | response-requested-label: response-requested 30 | 31 | # Don't set closed-for-staleness label to skip closing very old issues 32 | # regardless of label 33 | closed-for-staleness-label: closed-for-staleness 34 | 35 | # Issue timing 36 | days-before-stale: 10 37 | days-before-close: 4 38 | days-before-ancient: 36500 39 | 40 | # If you don't want to mark a issue as being ancient based on a 41 | # threshold of "upvotes", you can set this here. An "upvote" is 42 | # the total number of +1, heart, hooray, and rocket reactions 43 | # on an issue. 44 | minimum-upvotes-to-exempt: 1 45 | 46 | repo-token: ${{ secrets.GITHUB_TOKEN }} 47 | loglevel: DEBUG 48 | # Set dry-run to true to not perform label or close actions. 49 | # dry-run: true 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | docs/.buildinfo 3 | docs/.doctrees/ 4 | __pycache__/ 5 | *.egg-info 6 | build/* 7 | 8 | # docs are updated automatically by .github/workflows/docs.yml 9 | docs/ 10 | .vscode 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AWS IoT Device SDK v2 for Python 2 | 3 | [![Version](https://img.shields.io/pypi/v/awsiotsdk.svg?style=flat)](https://pypi.org/project/awsiotsdk/) 4 | 5 | This document provides information about the AWS IoT Device SDK v2 for Python. This SDK is built on the [AWS Common Runtime](https://docs.aws.amazon.com/sdkref/latest/guide/common-runtime.html) 6 | 7 | *__Jump To:__* 8 | * [Installation](#installation) 9 | * [Samples](samples) 10 | * [Mac-Only TLS Behavior](#mac-only-tls-behavior) 11 | * [Getting Help](#getting-help) 12 | * [FAQ](./documents/FAQ.md) 13 | * [API Docs](https://aws.github.io/aws-iot-device-sdk-python-v2/) 14 | * [MQTT5 User Guide](./documents/MQTT5_Userguide.md) 15 | * [Migration Guide from the AWS IoT SDK for Python v1](./documents/MIGRATION_GUIDE.md) 16 | 17 | 18 | ## Installation 19 | 20 | ### Minimum Requirements 21 | * Python 3.7+ 22 | 23 | [Step-by-step instructions](./documents/PREREQUISITES.md) 24 | 25 | ### Install from PyPI 26 | 27 | #### MacOS and Linux: 28 | 29 | ``` 30 | python3 -m pip install awsiotsdk 31 | ``` 32 | 33 | #### Windows: 34 | 35 | ``` 36 | python -m pip install awsiotsdk 37 | ``` 38 | 39 | ### Install from source 40 | 41 | ```bash 42 | # 1. Create a workspace directory to hold all the SDK files 43 | mkdir sdk-workspace 44 | cd sdk-workspace 45 | 46 | # 2. Clone the repository. You could select the version of the SDK you desire to use. 47 | git clone -b https://github.com/aws/aws-iot-device-sdk-python-v2.git 48 | 49 | # 3. (Optional) Setup the version number of your local build. The default version 50 | # for awsiotsdk is set to "1.0.0-dev", you can set the version number of the 51 | # local build in "aws-iot-device-sdk-python-v2/awsiot/__init__.py" 52 | sed -i "s/__version__ = '1.0.0-dev'/__version__ = ''/" \ 53 | aws-iot-device-sdk-python-v2/awsiot/__init__.py 54 | 55 | # 4. Install using Pip (use 'python' instead of 'python3' on Windows) 56 | python3 -m pip install ./aws-iot-device-sdk-python-v2 57 | ``` 58 | 59 | ## Samples 60 | 61 | [Samples README](samples) 62 | 63 | ### Mac-Only TLS Behavior 64 | 65 | Please note that on Mac, once a private key is used with a certificate, that certificate-key pair is imported into the Mac Keychain. All subsequent uses of that certificate will use the stored private key and ignore anything passed in programmatically. Beginning in v1.7.3, when a stored private key from the Keychain is used, the following will be logged at the "info" log level: 66 | 67 | ``` 68 | static: certificate has an existing certificate-key pair that was previously imported into the Keychain. Using key from Keychain instead of the one provided. 69 | ``` 70 | 71 | ## Getting Help 72 | 73 | The best way to interact with our team is through GitHub. You can open a [discussion](https://github.com/aws/aws-iot-device-sdk-python-v2/discussions) for guidance questions or an [issue](https://github.com/aws/aws-iot-device-sdk-python-v2/issues/new/choose) for bug reports, or feature requests. You may also find help on community resources such as [StackOverFlow](https://stackoverflow.com/questions/tagged/aws-iot) with the tag [#aws-iot](https://stackoverflow.com/questions/tagged/aws-iot) or if you have a support plan with [AWS Support](https://aws.amazon.com/premiumsupport/), you can also create a new support case. 74 | 75 | Please make sure to check out our resources too before opening an issue: 76 | 77 | * [FAQ](./documents/FAQ.md) 78 | * [API Docs](https://aws.github.io/aws-iot-device-sdk-python-v2/) 79 | * [IoT Guide](https://docs.aws.amazon.com/iot/latest/developerguide/what-is-aws-iot.html) ([source](https://github.com/awsdocs/aws-iot-docs)) 80 | * Check for similar [Issues](https://github.com/aws/aws-iot-device-sdk-python-v2/issues) 81 | * [AWS IoT Core Documentation](https://docs.aws.amazon.com/iot/) 82 | * [Dev Blog](https://aws.amazon.com/blogs/?awsf.blog-master-iot=category-internet-of-things%23amazon-freertos%7Ccategory-internet-of-things%23aws-greengrass%7Ccategory-internet-of-things%23aws-iot-analytics%7Ccategory-internet-of-things%23aws-iot-button%7Ccategory-internet-of-things%23aws-iot-device-defender%7Ccategory-internet-of-things%23aws-iot-device-management%7Ccategory-internet-of-things%23aws-iot-platform) 83 | * Integration with AWS IoT Services such as 84 | [Device Shadow](https://docs.aws.amazon.com/iot/latest/developerguide/iot-device-shadows.html) 85 | and [Jobs](https://docs.aws.amazon.com/iot/latest/developerguide/iot-jobs.html) 86 | is provided by code that been generated from a model of the service. 87 | * [Contributions Guidelines](./documents/CONTRIBUTING.md) 88 | 89 | ## License 90 | 91 | This library is licensed under the [Apache 2.0 License](./documents/LICENSE). 92 | 93 | Latest released version: v1.22.2 94 | -------------------------------------------------------------------------------- /awsiot/greengrasscoreipc/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0. 3 | 4 | import os 5 | from typing import Optional 6 | 7 | from awscrt.io import ( 8 | ClientBootstrap, 9 | DefaultHostResolver, 10 | EventLoopGroup, 11 | SocketDomain, 12 | SocketOptions, 13 | ) 14 | from awsiot.eventstreamrpc import ( 15 | Connection, 16 | LifecycleHandler, 17 | MessageAmendment, 18 | ) 19 | from awsiot.greengrasscoreipc.client import GreengrassCoreIPCClient 20 | 21 | 22 | def connect(*, 23 | ipc_socket: str=None, 24 | authtoken: str=None, 25 | lifecycle_handler: Optional[LifecycleHandler]=None, 26 | timeout: float=10.0) -> GreengrassCoreIPCClient: 27 | """ 28 | Creates an IPC client and connects to the GreengrassCoreIPC service. When finished with the client, 29 | you must call close() to free the client's native resources. 30 | 31 | Args: 32 | ipc_socket: Path to the Unix domain socket of Greengrass Nucleus, defaults to 33 | environment variable AWS_GG_NUCLEUS_DOMAIN_SOCKET_FILEPATH_FOR_COMPONENT 34 | authtoken: Authentication token, defaults to environment variable SVCUID 35 | lifecycle_handler: Handler for events over the course of this 36 | network connection. See :class:`awsiot.eventstreamrpc.LifecycleHandler` for more info. 37 | Handler methods will only be invoked if the connect attempt 38 | succeeds. 39 | timeout: The number of seconds to wait for establishing the connection. 40 | 41 | Returns: 42 | Client for the GreengrassCoreIPC service. 43 | """ 44 | 45 | if not ipc_socket: 46 | ipc_socket = os.environ["AWS_GG_NUCLEUS_DOMAIN_SOCKET_FILEPATH_FOR_COMPONENT"] 47 | if not authtoken: 48 | authtoken = os.environ["SVCUID"] 49 | if not lifecycle_handler: 50 | lifecycle_handler = LifecycleHandler() 51 | 52 | elg = EventLoopGroup(num_threads=1) 53 | resolver = DefaultHostResolver(elg) 54 | bootstrap = ClientBootstrap(elg, resolver) 55 | socket_options = SocketOptions() 56 | socket_options.domain = SocketDomain.Local 57 | amender = MessageAmendment.create_static_authtoken_amender(authtoken) 58 | 59 | connection = Connection( 60 | host_name=ipc_socket, 61 | port=0, # dummy port number, not needed for Unix domain sockets 62 | bootstrap=bootstrap, 63 | socket_options=socket_options, 64 | connect_message_amender=amender, 65 | ) 66 | connect_future = connection.connect(lifecycle_handler) 67 | connect_future.result(timeout) 68 | 69 | return GreengrassCoreIPCClient(connection) 70 | -------------------------------------------------------------------------------- /builder.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aws-iot-device-sdk-python-v2", 3 | "hosts": { 4 | "al2012": { 5 | "enabled": false, 6 | "_comment": "al2012 can't currently pull builder.py from github. SSL: CERTIFICATE_VERIFY_FAILED" 7 | }, 8 | "linux": { 9 | "architectures": { 10 | "x86": { 11 | "enabled": false, 12 | "_comment": "this 'x86' machine actually uses 64bit python, no point in running it" 13 | } 14 | } 15 | }, 16 | "windows": { 17 | "architectures": { 18 | "x86": { 19 | "enabled": false, 20 | "_comment": "this 'x86' machine actually uses 64bit python, no point in running it" 21 | } 22 | } 23 | }, 24 | "manylinux": { 25 | "post_build_steps": [ 26 | ["echo", "------ Python 3.11 ------"], 27 | ["/opt/python/cp311-cp311/bin/python", "-m", "pip", "install", "--upgrade", "pip", "setuptools"], 28 | ["/opt/python/cp311-cp311/bin/python", "-m", "pip", "install", ".", "--verbose"], 29 | ["/opt/python/cp311-cp311/bin/python", "-m", "pip", "install", "boto3", "autopep8"], 30 | ["/opt/python/cp311-cp311/bin/python", "-m", "unittest", "discover", "--verbose"], 31 | ["echo", "------ Python 3.10 ------"], 32 | ["/opt/python/cp310-cp310/bin/python", "-m", "pip", "install", "--upgrade", "pip", "setuptools"], 33 | ["/opt/python/cp310-cp310/bin/python", "-m", "pip", "install", ".", "--verbose"], 34 | ["/opt/python/cp310-cp310/bin/python", "-m", "pip", "install", "boto3", "autopep8"], 35 | ["/opt/python/cp310-cp310/bin/python", "-m", "unittest", "discover", "--verbose"], 36 | ["echo", "------ Python 3.9 ------"], 37 | ["/opt/python/cp39-cp39/bin/python", "-m", "pip", "install", "--upgrade", "pip", "setuptools"], 38 | ["/opt/python/cp39-cp39/bin/python", "-m", "pip", "install", ".", "--verbose"], 39 | ["/opt/python/cp39-cp39/bin/python", "-m", "pip", "install", "boto3", "autopep8"], 40 | ["/opt/python/cp39-cp39/bin/python", "-m", "unittest", "discover", "--verbose"], 41 | ["echo", "------ Python 3.8 ------"], 42 | ["/opt/python/cp38-cp38/bin/python", "-m", "pip", "install", "--upgrade", "pip", "setuptools"], 43 | ["/opt/python/cp38-cp38/bin/python", "-m", "pip", "install", ".", "--verbose"], 44 | ["/opt/python/cp38-cp38/bin/python", "-m", "pip", "install", "boto3", "autopep8"], 45 | ["/opt/python/cp38-cp38/bin/python", "-m", "unittest", "discover", "--verbose"], 46 | ["echo", "------ Python 3.7 ------"], 47 | ["/opt/python/cp37-cp37m/bin/python", "-m", "pip", "install", "--upgrade", "pip", "setuptools"], 48 | ["/opt/python/cp37-cp37m/bin/python", "-m", "pip", "install", ".", "--verbose"], 49 | ["/opt/python/cp37-cp37m/bin/python", "-m", "pip", "install", "boto3", "autopep8"], 50 | ["/opt/python/cp37-cp37m/bin/python", "-m", "unittest", "discover", "--verbose"] 51 | ], 52 | "run_tests": false, 53 | "_comment": "manylinux has all its own build steps, turn off 'tests' which is where normal build steps are declared. using data to program sucks" 54 | } 55 | }, 56 | "targets": { 57 | "android": { 58 | "enabled": false, 59 | "_comment": "disabled until we have a reason to support python on android" 60 | } 61 | }, 62 | "compilers": { 63 | "_comment": "don't need compiler variants because awsiot is pure python. It will use precompiled awscrt wheels pulled from pip.", 64 | "clang": { "enabled": false }, 65 | "gcc": { 66 | "versions": { 67 | "_comment": "run gcc 4.8 because that's what the linux wheels were made with", 68 | "5": { "enabled": false }, 69 | "6": { "enabled": false }, 70 | "7": { "enabled": false }, 71 | "8": { "enabled": false } 72 | } 73 | }, 74 | "msvc": { 75 | "versions": { 76 | "_comment": "run msvc 2015 because that's what the windows wheels were made with", 77 | "2017": { "enabled": false } 78 | } 79 | } 80 | }, 81 | "upstream": [], 82 | "downstream": [], 83 | "_comment": "doing everything in 'test' step so that manylinux can turn off tests and do its stuff in the 'post_build_step'. using data to program really sucks", 84 | "build": [ 85 | ["{python}", "-c", "print('ignore this fake build step')"] 86 | ], 87 | "build_steps": [ 88 | "python3 -m pip install ." 89 | ], 90 | "test_steps": [ 91 | "ci-test" 92 | ], 93 | "env": { 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /codebuild/cd/pip-install-with-retry.py: -------------------------------------------------------------------------------- 1 | import time 2 | import sys 3 | import subprocess 4 | 5 | DOCS = """Given cmdline args, executes: python3 -m pip install [args...] 6 | Keeps retrying until the new version becomes available in pypi (or we time out)""" 7 | if len(sys.argv) < 2: 8 | sys.exit(DOCS) 9 | 10 | RETRY_INTERVAL_SECS = 10 11 | GIVE_UP_AFTER_SECS = 60 * 15 12 | 13 | pip_install_args = [sys.executable, '-m', 'pip', 'install'] + sys.argv[1:] 14 | 15 | start_time = time.time() 16 | while True: 17 | print(subprocess.list2cmdline(pip_install_args)) 18 | result = subprocess.run(pip_install_args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 19 | 20 | stdout = result.stdout.decode().strip() 21 | if stdout: 22 | print(stdout) 23 | 24 | if result.returncode == 0: 25 | # success 26 | sys.exit(0) 27 | 28 | if "could not find a version" in stdout.lower(): 29 | elapsed_secs = time.time() - start_time 30 | if elapsed_secs < GIVE_UP_AFTER_SECS: 31 | # try again 32 | print("Retrying in", RETRY_INTERVAL_SECS, "secs...") 33 | time.sleep(RETRY_INTERVAL_SECS) 34 | continue 35 | else: 36 | print("Giving up on retries after", int(elapsed_secs), "total secs.") 37 | 38 | # fail 39 | sys.exit(result.returncode) 40 | -------------------------------------------------------------------------------- /codebuild/cd/publish-to-prod-pypi.yml: -------------------------------------------------------------------------------- 1 | # Assumes are running using the Ubuntu Codebuild standard image 2 | # Makes a release deployment to production PyPi repo. 3 | # 4 | # NOTE: This script assumes that the AWS CLI-V2 is pre-installed! 5 | # - AWS CLI-V2 is a requirement to run this script. 6 | version: 0.2 7 | phases: 8 | install: 9 | commands: 10 | - sudo apt-get update -y 11 | - sudo apt-get install python3 python3-pip -y 12 | - export PATH=$PATH:$HOME/.local/bin 13 | - python3 -m pip install --user --upgrade pip 14 | - python3 -m pip install --user --upgrade twine setuptools wheel awscli PyOpenSSL six 15 | - echo "\nBuild version data:" 16 | - echo "\nPython Version:"; python3 --version 17 | pre_build: 18 | commands: 19 | - cd $CODEBUILD_SRC_DIR/aws-iot-device-sdk-python-v2 20 | - pypirc=$(aws secretsmanager get-secret-value --secret-id "cd/aws-sdk-python-v2-prod/.pypirc" --query "SecretString" | cut -f2 -d\") && echo "$pypirc" > ~/.pypirc 21 | - export PKG_VERSION=$(cat $CODEBUILD_SRC_DIR/VERSION) 22 | - echo "Updating package version to ${PKG_VERSION}" 23 | - sed --in-place -E "s/__version__ = '.+'/__version__ = '${PKG_VERSION}'/" awsiot/__init__.py 24 | build: 25 | commands: 26 | - echo Build started on `date` 27 | - python3 setup.py sdist bdist_wheel 28 | - python3 -m twine upload -r pypi dist/* 29 | post_build: 30 | commands: 31 | - echo Build completed on `date` 32 | -------------------------------------------------------------------------------- /codebuild/cd/publish-to-test-pypi.yml: -------------------------------------------------------------------------------- 1 | # Assumes are running using the Ubuntu Codebuild standard image 2 | # Makes a release deployment to test PyPi repo. 3 | # 4 | # NOTE: This script assumes that the AWS CLI-V2 is pre-installed! 5 | # - AWS CLI-V2 is a requirement to run this script. 6 | version: 0.2 7 | phases: 8 | install: 9 | commands: 10 | - sudo apt-get update -y 11 | - sudo apt-get install python3 python3-pip -y 12 | - export PATH=$PATH:$HOME/.local/bin 13 | - python3 -m pip install --user --upgrade pip 14 | - python3 -m pip install --user --upgrade twine setuptools wheel awscli PyOpenSSL six 15 | - echo "\nBuild version data:" 16 | - echo "\nPython Version:"; python3 --version 17 | pre_build: 18 | commands: 19 | - cd $CODEBUILD_SRC_DIR/aws-iot-device-sdk-python-v2 20 | - pypirc=$(aws secretsmanager get-secret-value --secret-id "cd/aws-sdk-python-v2-test/.pypirc" --query "SecretString" | cut -f2 -d\") && echo "$pypirc" > ~/.pypirc 21 | - export PKG_VERSION=$(cat $CODEBUILD_SRC_DIR/VERSION) 22 | - echo "Updating package version to ${PKG_VERSION}" 23 | - sed --in-place -E "s/__version__ = '.+'/__version__ = '${PKG_VERSION}'/" awsiot/__init__.py 24 | build: 25 | commands: 26 | - echo Build started on `date` 27 | - python3 setup.py sdist bdist_wheel 28 | - python3 -m twine upload -r testpypi dist/* 29 | post_build: 30 | commands: 31 | - echo Build completed on `date` 32 | -------------------------------------------------------------------------------- /codebuild/cd/test-prod-pypi.yml: -------------------------------------------------------------------------------- 1 | # Assumes are running using the Ubuntu Codebuild standard image 2 | # Makes sure the release to test PyPi repository worked 3 | # 4 | # NOTE: This script assumes that the AWS CLI-V2 is pre-installed! 5 | # - AWS CLI-V2 is a requirement to run this script. 6 | version: 0.2 7 | phases: 8 | install: 9 | commands: 10 | - sudo apt-get update -y 11 | - sudo apt-get install python3 python3-pip -y 12 | - python3 -m pip install --upgrade pip 13 | - python3 -m pip install --upgrade setuptools 14 | - echo "\nBuild version data:" 15 | - echo "\nPython Version:"; python3 --version 16 | 17 | pre_build: 18 | commands: 19 | # Material for PubSub sample 20 | - curl https://www.amazontrust.com/repository/AmazonRootCA1.pem --output /tmp/AmazonRootCA1.pem 21 | - cert=$(aws secretsmanager get-secret-value --secret-id "ci/PubSub/cert" --query "SecretString" | cut -f2 -d":" | cut -f2 -d\") && echo "$cert" > /tmp/certificate.pem 22 | - key=$(aws secretsmanager get-secret-value --secret-id "ci/PubSub/key" --query "SecretString" | cut -f2 -d":" | cut -f2 -d\") && echo "$key" > /tmp/privatekey.pem 23 | - ENDPOINT=$(aws secretsmanager get-secret-value --secret-id "ci/endpoint" --query "SecretString" | cut -f2 -d":" | sed -e 's/[\\\"\}]//g') 24 | build: 25 | commands: 26 | - echo Build started on `date` 27 | - cd $CODEBUILD_SRC_DIR/aws-iot-device-sdk-python-v2 28 | - CURRENT_TAG_VERSION=$(cat $CODEBUILD_SRC_DIR/VERSION) 29 | - python3 codebuild/cd/pip-install-with-retry.py --no-cache-dir --user awsiotsdk==$CURRENT_TAG_VERSION 30 | # Run PubSub sample 31 | - python3 samples/pubsub.py --endpoint ${ENDPOINT} --cert /tmp/certificate.pem --key /tmp/privatekey.pem --ca_file /tmp/AmazonRootCA1.pem --verbosity Trace 32 | 33 | post_build: 34 | commands: 35 | - echo Build completed on `date` 36 | 37 | -------------------------------------------------------------------------------- /codebuild/cd/test-test-pypi.yml: -------------------------------------------------------------------------------- 1 | # Assumes are running using the Ubuntu Codebuild standard image 2 | # Makes sure the release to test PyPi repository worked 3 | # 4 | # NOTE: This script assumes that the AWS CLI-V2 is pre-installed! 5 | # - AWS CLI-V2 is a requirement to run this script. 6 | version: 0.2 7 | phases: 8 | install: 9 | commands: 10 | - sudo apt-get update -y 11 | - sudo apt-get install python3 python3-pip -y 12 | - python3 -m pip install --upgrade pip 13 | - python3 -m pip install --upgrade setuptools 14 | - echo "\nBuild version data:" 15 | - echo "\nPython Version:"; python3 --version 16 | 17 | pre_build: 18 | commands: 19 | - curl https://www.amazontrust.com/repository/AmazonRootCA1.pem --output /tmp/AmazonRootCA1.pem 20 | - cert=$(aws secretsmanager get-secret-value --secret-id "ci/PubSub/cert" --query "SecretString" | cut -f2 -d":" | cut -f2 -d\") && echo "$cert" > /tmp/certificate.pem 21 | - key=$(aws secretsmanager get-secret-value --secret-id "ci/PubSub/key" --query "SecretString" | cut -f2 -d":" | cut -f2 -d\") && echo "$key" > /tmp/privatekey.pem 22 | - ENDPOINT=$(aws secretsmanager get-secret-value --secret-id "ci/endpoint" --query "SecretString" | cut -f2 -d":" | sed -e 's/[\\\"\}]//g') 23 | build: 24 | commands: 25 | - echo Build started on `date` 26 | - cd $CODEBUILD_SRC_DIR/aws-iot-device-sdk-python-v2 27 | - CURRENT_TAG_VERSION=$(cat $CODEBUILD_SRC_DIR/VERSION) 28 | # this is here because typing isn't in test-pypi, so pull it from prod instead 29 | - python3 -m pip install typing 30 | - python3 codebuild/cd/pip-install-with-retry.py -i https://testpypi.python.org/simple --user awsiotsdk==$CURRENT_TAG_VERSION 31 | # Run PubSub sample 32 | - python3 samples/pubsub.py --endpoint ${ENDPOINT} --cert /tmp/certificate.pem --key /tmp/privatekey.pem --ca_file /tmp/AmazonRootCA1.pem --verbosity Trace 33 | 34 | post_build: 35 | commands: 36 | - echo Build completed on `date` 37 | -------------------------------------------------------------------------------- /codebuild/cd/test-version-exists.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | VERSION_FILE_PATH=$1 4 | if [ ! readlink -e "$VERSION_FILE_PATH" ]; then 5 | echo "No VERSION file found! Cannot make release!" 6 | exit 1 7 | else 8 | echo "VERSION file found..." 9 | fi 10 | VERSION=$(cat $VERSION_FILE_PATH) 11 | 12 | # Make sure the version variable is populated 13 | if [ -z "${VERSION}" ]; then 14 | echo "VERSION file is empty!" 15 | exit 1 16 | else 17 | echo "VERSION file contains: ${VERSION}" 18 | fi 19 | 20 | # Make sure the version follows the correct format: major.minor.patch 21 | LENGTH_CHECK="${VERSION//[^.]}" 22 | if [ ${#LENGTH_CHECK} != 2 ]; then 23 | echo "VERSION file contains invalid version (not in format major.minor.patch)" 24 | exit 1 25 | fi 26 | # Use RegX to ensure it only contains numbers and periods 27 | REGX_CHECK='^([0-9]+\.){0,2}(\*|[0-9]+)$' 28 | if [[ $VERSION =~ $REGX_CHECK ]]; then 29 | echo "VERSION file contains valid version" 30 | else 31 | echo "VERSION file contains invalid version (RegX validator failed)" 32 | exit 1 33 | fi 34 | 35 | # Does PyPi have the version? If so, do not allow it! 36 | if python3 -m pip install --no-cache-dir -vvv awsiotsdk==$VERSION; then 37 | echo "$VERSION is already in pypi, cut a new tag if you want to upload another version." 38 | exit 1 39 | fi 40 | 41 | echo "$VERSION currently does not exist in pypi, allowing pipeline to continue." 42 | exit 0 43 | -------------------------------------------------------------------------------- /codebuild/cd/test-version-exists.yml: -------------------------------------------------------------------------------- 1 | # Assumes are running using the Ubuntu Codebuild standard image 2 | # Makes sure the version in CD has not already been released. 3 | # Will fail the build and stop the pipeline if the version has already been released. 4 | # 5 | # NOTE: This script assumes that the AWS CLI-V2 is pre-installed! 6 | # - AWS CLI-V2 is a requirement to run this script. 7 | version: 0.2 8 | phases: 9 | install: 10 | commands: 11 | - sudo apt-get update -y 12 | - sudo apt-get install python3 python3-pip -y 13 | - pip3 install --upgrade setuptools 14 | - echo "\nBuild version data:" 15 | - echo "\nPython Version:"; python3 --version 16 | build: 17 | commands: 18 | - cd $CODEBUILD_SRC_DIR/aws-iot-device-sdk-python-v2 19 | - bash ./codebuild/cd/test-version-exists.sh $CODEBUILD_SRC_DIR/VERSION 20 | -------------------------------------------------------------------------------- /codebuild/samples/connect-linux.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | set -o pipefail 5 | 6 | env 7 | 8 | pushd $CODEBUILD_SRC_DIR/samples/ 9 | 10 | ENDPOINT=$(aws secretsmanager get-secret-value --secret-id "ci/endpoint" --query "SecretString" | cut -f2 -d":" | sed -e 's/[\\\"\}]//g') 11 | 12 | echo "Basic Connect test" 13 | python3 basic_connect.py --endpoint $ENDPOINT --key /tmp/privatekey.pem --cert /tmp/certificate.pem 14 | 15 | echo "Websocket Connect test" 16 | python3 websocket_connect.py --endpoint $ENDPOINT --signing_region us-east-1 17 | 18 | popd 19 | -------------------------------------------------------------------------------- /codebuild/samples/custom-auth-linux.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | set -o pipefail 5 | 6 | env 7 | 8 | pushd $CODEBUILD_SRC_DIR/samples/ 9 | 10 | ENDPOINT=$(aws secretsmanager get-secret-value --secret-id "ci/endpoint" --query "SecretString" | cut -f2 -d":" | sed -e 's/[\\\"\}]//g') 11 | AUTH_NAME=$(aws secretsmanager get-secret-value --secret-id "ci/CustomAuthorizer/name" --query "SecretString" | cut -f2 -d":" | sed -e 's/[\\\"\}]//g') 12 | AUTH_PASSWORD=$(aws secretsmanager get-secret-value --secret-id "ci/CustomAuthorizer/password" --query "SecretString" | cut -f2 -d":" | sed -e 's/[\\\"\}]//g') 13 | 14 | echo "Custom Authorizer test" 15 | python3 custom_authorizer_connect.py --endpoint $ENDPOINT --custom_auth_authorizer_name $AUTH_NAME --custom_auth_password $AUTH_PASSWORD 16 | 17 | popd 18 | -------------------------------------------------------------------------------- /codebuild/samples/linux-smoke-tests.yml: -------------------------------------------------------------------------------- 1 | # Assumes are running using the Ubuntu Codebuild standard image 2 | # NOTE: This script assumes that the AWS CLI-V2 is pre-installed! 3 | # - AWS CLI-V2 is a requirement to run this script. 4 | version: 0.2 5 | phases: 6 | install: 7 | commands: 8 | - add-apt-repository ppa:ubuntu-toolchain-r/test 9 | - apt-get update -y 10 | - apt-get install python3 softhsm -y 11 | - echo "\nBuild version data:" 12 | - echo "\nPython Version:"; python3 --version 13 | - echo "\nSoftHSM (PKCS11) version:"; softhsm2-util --version 14 | - echo "\n" 15 | build: 16 | commands: 17 | - echo Build started on `date` 18 | - $CODEBUILD_SRC_DIR/codebuild/samples/setup-linux.sh 19 | - $CODEBUILD_SRC_DIR/codebuild/samples/connect-linux.sh 20 | - $CODEBUILD_SRC_DIR/codebuild/samples/custom-auth-linux.sh 21 | - $CODEBUILD_SRC_DIR/codebuild/samples/pkcs11-connect-linux.sh 22 | - $CODEBUILD_SRC_DIR/codebuild/samples/pubsub-mqtt5-linux.sh 23 | - $CODEBUILD_SRC_DIR/codebuild/samples/pubsub-linux.sh 24 | - $CODEBUILD_SRC_DIR/codebuild/samples/shadow-linux.sh 25 | post_build: 26 | commands: 27 | - echo Build completed on `date` 28 | -------------------------------------------------------------------------------- /codebuild/samples/pkcs11-connect-linux.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | set -o pipefail 5 | 6 | pushd $CODEBUILD_SRC_DIR/samples/ 7 | 8 | ENDPOINT=$(aws secretsmanager get-secret-value --secret-id "ci/endpoint" --query "SecretString" | cut -f2 -d":" | sed -e 's/[\\\"\}]//g') 9 | 10 | # from hereon commands are echoed. don't leak secrets 11 | set -x 12 | 13 | softhsm2-util --version 14 | 15 | # SoftHSM2's default tokendir path might be invalid on this machine 16 | # so set up a conf file that specifies a known good tokendir path 17 | mkdir -p /tmp/tokens 18 | export SOFTHSM2_CONF=/tmp/softhsm2.conf 19 | echo "directories.tokendir = /tmp/tokens" > /tmp/softhsm2.conf 20 | 21 | # create token 22 | softhsm2-util --init-token --free --label my-token --pin 0000 --so-pin 0000 23 | 24 | # add private key to token (must be in PKCS#8 format) 25 | openssl pkcs8 -topk8 -in /tmp/privatekey.pem -out /tmp/privatekey.p8.pem -nocrypt 26 | softhsm2-util --import /tmp/privatekey.p8.pem --token my-token --label my-key --id BEEFCAFE --pin 0000 27 | 28 | # run sample 29 | python3 pkcs11_connect.py --endpoint $ENDPOINT --cert /tmp/certificate.pem --pkcs11_lib /usr/lib/softhsm/libsofthsm2.so --pin 0000 --token_label my-token --key_label my-key 30 | 31 | popd 32 | -------------------------------------------------------------------------------- /codebuild/samples/pubsub-linux.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | set -o pipefail 5 | 6 | env 7 | 8 | pushd $CODEBUILD_SRC_DIR/samples/ 9 | 10 | ENDPOINT=$(aws secretsmanager get-secret-value --secret-id "ci/endpoint" --query "SecretString" | cut -f2 -d":" | sed -e 's/[\\\"\}]//g') 11 | 12 | echo "PubSub test" 13 | python3 pubsub.py --endpoint $ENDPOINT --key /tmp/privatekey.pem --cert /tmp/certificate.pem --is_ci "true" 14 | 15 | popd 16 | -------------------------------------------------------------------------------- /codebuild/samples/pubsub-mqtt5-linux.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | set -o pipefail 5 | 6 | env 7 | 8 | pushd $CODEBUILD_SRC_DIR/samples/ 9 | 10 | ENDPOINT=$(aws secretsmanager get-secret-value --secret-id "ci/endpoint" --query "SecretString" | cut -f2 -d":" | sed -e 's/[\\\"\}]//g') 11 | 12 | echo "MQTT5 PubSub test" 13 | python3 mqtt5_pubsub.py --endpoint $ENDPOINT --key /tmp/privatekey.pem --cert /tmp/certificate.pem --is_ci "true" 14 | 15 | popd 16 | -------------------------------------------------------------------------------- /codebuild/samples/setup-linux.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | set -o pipefail 5 | 6 | env 7 | 8 | # build package 9 | cd $CODEBUILD_SRC_DIR 10 | 11 | ulimit -c unlimited 12 | python3 -m pip install . 13 | 14 | cert=$(aws secretsmanager get-secret-value --secret-id "ci/CodeBuild/cert" --query "SecretString" | cut -f2 -d":" | cut -f2 -d\") && echo -e "$cert" > /tmp/certificate.pem 15 | key=$(aws secretsmanager get-secret-value --secret-id "ci/CodeBuild/key" --query "SecretString" | cut -f2 -d":" | cut -f2 -d\") && echo -e "$key" > /tmp/privatekey.pem 16 | key_p8=$(aws secretsmanager get-secret-value --secret-id "ci/CodeBuild/keyp8" --query "SecretString" | cut -f2 -d":" | cut -f2 -d\") && echo -e "$key_p8" > /tmp/privatekey_p8.pem 17 | -------------------------------------------------------------------------------- /codebuild/samples/shadow-linux.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | set -o pipefail 5 | 6 | env 7 | 8 | pushd $CODEBUILD_SRC_DIR/samples/ 9 | 10 | ENDPOINT=$(aws secretsmanager get-secret-value --secret-id "ci/endpoint" --query "SecretString" | cut -f2 -d":" | sed -e 's/[\\\"\}]//g') 11 | 12 | echo "Shadow test" 13 | python3 shadow.py --endpoint $ENDPOINT --key /tmp/privatekey.pem --cert /tmp/certificate.pem --thing_name CI_CodeBuild_Thing --is_ci true 14 | python3 shadow_mqtt5.py --endpoint $ENDPOINT --key /tmp/privatekey.pem --cert /tmp/certificate.pem --thing_name CI_CodeBuild_Thing --is_ci true 15 | 16 | popd 17 | -------------------------------------------------------------------------------- /deviceadvisor/script/DATestConfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "tests" :["MQTT Connect", "MQTT Publish", "MQTT Subscribe", "Shadow Publish", "Shadow Update"], 3 | "test_suite_ids" : 4 | { 5 | "MQTT Connect" : "mxn32qkm8npn", 6 | "MQTT Publish" : "gcjhujhhz50p", 7 | "MQTT Subscribe" : "nyiuiwx5yxtj", 8 | "Shadow Publish" : "fttdr8ufljnf", 9 | "Shadow Update" : "ng9t8am2jnry" 10 | }, 11 | "test_exe_path" : 12 | { 13 | "MQTT Connect" : "mqtt_connect.py", 14 | "MQTT Publish" : "mqtt_publish.py", 15 | "MQTT Subscribe" : "mqtt_subscribe.py", 16 | "Shadow Publish" : "shadow_update.py", 17 | "Shadow Update" : "shadow_update.py" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /deviceadvisor/tests/da_test_utils.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0. 3 | import os 4 | from enum import Enum 5 | from uuid import uuid4 6 | 7 | class TestType(Enum): 8 | CONNECT = 1 9 | SUB_PUB = 2 10 | SHADOW = 3 11 | 12 | class DATestUtils: 13 | endpoint = os.getenv('DA_ENDPOINT') 14 | certificatePath = os.getenv('DA_CERTI') 15 | keyPath = os.getenv('DA_KEY') 16 | topic = os.getenv('DA_TOPIC') 17 | thing_name = os.getenv('DA_THING_NAME') 18 | shadowProperty = os.getenv('DA_SHADOW_PROPERTY') 19 | shadowValue = os.getenv('DA_SHADOW_VALUE_SET') 20 | 21 | @classmethod 22 | def valid(cls, test_type): 23 | if (not (cls.endpoint and cls.certificatePath and cls.keyPath)): 24 | return False 25 | 26 | if (not cls.topic and test_type == TestType.SUB_PUB): 27 | return False 28 | 29 | if (not (cls.thing_name and cls.shadowProperty and cls.shadowValue) and test_type == TestType.SHADOW): 30 | return False 31 | 32 | return True 33 | 34 | @classmethod 35 | def generate_client_id(_self, postfix): 36 | return "test-DA" + str(uuid4()) + postfix 37 | -------------------------------------------------------------------------------- /deviceadvisor/tests/mqtt_connect.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0. 3 | from awsiot import mqtt_connection_builder 4 | from da_test_utils import DATestUtils, TestType 5 | 6 | # This sample used to test mqtt connection through device advisor 7 | 8 | if __name__ == '__main__': 9 | # init variables 10 | utils = DATestUtils.valid(TestType.CONNECT) 11 | if not utils: 12 | quit(-1) 13 | 14 | mqtt_connection = mqtt_connection_builder.mtls_from_path( 15 | endpoint = DATestUtils.endpoint, 16 | cert_filepath = DATestUtils.certificatePath, 17 | pri_key_filepath = DATestUtils.keyPath, 18 | client_id = DATestUtils.generate_client_id("-connect"), 19 | clean_session = True, 20 | tcp_connect_timeout_ms = 60000, # 1 minute 21 | keep_alive_secs = 60000, # 1 minute 22 | ping_timeout_ms = 120000) # 2 minutes 23 | connect_future = mqtt_connection.connect() 24 | 25 | # Future.result() waits until a result is available 26 | connect_future.result() 27 | 28 | disconnect_future = mqtt_connection.disconnect() 29 | disconnect_future.result() 30 | quit(0) 31 | -------------------------------------------------------------------------------- /deviceadvisor/tests/mqtt_publish.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0. 3 | from awscrt import mqtt 4 | from awsiot import mqtt_connection_builder 5 | from da_test_utils import DATestUtils, TestType 6 | import json 7 | 8 | if __name__ == '__main__': 9 | # validate environment variables 10 | utils = DATestUtils.valid(TestType.SUB_PUB) 11 | if not utils: 12 | quit(-1) 13 | 14 | mqtt_connection = mqtt_connection_builder.mtls_from_path( 15 | endpoint = DATestUtils.endpoint, 16 | cert_filepath = DATestUtils.certificatePath, 17 | pri_key_filepath = DATestUtils.keyPath, 18 | client_id = DATestUtils.generate_client_id("-pub"), 19 | clean_session = True, 20 | tcp_connect_timeout_ms = 60000, # 1 minute 21 | keep_alive_secs = 60000, # 1 minute 22 | ping_timeout_ms = 120000) # 2 minutes 23 | connect_future = mqtt_connection.connect() 24 | 25 | # Future.result() waits until a result is available 26 | connect_future.result() 27 | 28 | message = "Hello World" 29 | message_json = json.dumps(message) 30 | # Device advisor test will not return PUBACK, therefore we use AT_MOST_ONCE so that 31 | # we dont busy wait for PUBACK 32 | publish_future, packet_id = mqtt_connection.publish( 33 | topic=DATestUtils.topic, 34 | payload=message_json, 35 | qos=mqtt.QoS.AT_MOST_ONCE) 36 | publish_future.result() 37 | 38 | # Disconnect 39 | disconnect_future = mqtt_connection.disconnect() 40 | disconnect_future.result() 41 | quit(0) 42 | -------------------------------------------------------------------------------- /deviceadvisor/tests/mqtt_subscribe.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0. 3 | 4 | from awscrt import io, mqtt, auth, http 5 | from awsiot import mqtt_connection_builder 6 | from da_test_utils import DATestUtils, TestType 7 | 8 | if __name__ == '__main__': 9 | # init variables 10 | utils = DATestUtils.valid(TestType.SUB_PUB) 11 | if not utils: 12 | quit(-1) 13 | 14 | mqtt_connection = mqtt_connection_builder.mtls_from_path( 15 | endpoint = DATestUtils.endpoint, 16 | cert_filepath = DATestUtils.certificatePath, 17 | pri_key_filepath = DATestUtils.keyPath, 18 | client_id = DATestUtils.generate_client_id("-sub"), 19 | clean_session = True, 20 | tcp_connect_timeout_ms = 60000, # 1 minute 21 | keep_alive_secs = 60000, # 1 minute 22 | ping_timeout_ms = 120000) # 2 minutes 23 | connect_future = mqtt_connection.connect() 24 | 25 | # Future.result() waits until a result is available 26 | connect_future.result() 27 | 28 | # Subscribe 29 | subscribe_future, packet_id = mqtt_connection.subscribe( 30 | topic=DATestUtils.topic, 31 | qos=mqtt.QoS.AT_MOST_ONCE) 32 | subscribe_future.result() 33 | 34 | # Disconnect 35 | disconnect_future = mqtt_connection.disconnect() 36 | disconnect_future.result() 37 | quit(0) 38 | -------------------------------------------------------------------------------- /deviceadvisor/tests/shadow_update.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0. 3 | 4 | from awscrt import mqtt 5 | from awsiot import iotshadow, mqtt_connection_builder 6 | from concurrent.futures import Future 7 | from da_test_utils import DATestUtils, TestType 8 | from uuid import uuid4 9 | 10 | if __name__ == '__main__': 11 | # init variables 12 | utils = DATestUtils.valid(TestType.SUB_PUB) 13 | if not utils: 14 | quit(-1) 15 | 16 | mqtt_connection = mqtt_connection_builder.mtls_from_path( 17 | endpoint = DATestUtils.endpoint, 18 | cert_filepath = DATestUtils.certificatePath, 19 | pri_key_filepath = DATestUtils.keyPath, 20 | client_id = DATestUtils.generate_client_id("-shadow"), 21 | clean_session = True, 22 | tcp_connect_timeout_ms = 60000, # 1 minute 23 | keep_alive_secs = 60000, # 1 minute 24 | ping_timeout_ms = 120000) # 2 minutes 25 | 26 | connect_future = mqtt_connection.connect() 27 | connect_future.result() 28 | shadow_client = iotshadow.IotShadowClient(mqtt_connection) 29 | 30 | # Publish shadow value 31 | request = iotshadow.UpdateShadowRequest( 32 | thing_name=DATestUtils.thing_name, 33 | state=iotshadow.ShadowState( 34 | reported={ DATestUtils.shadowProperty: DATestUtils.shadowValue }, 35 | desired={ DATestUtils.shadowProperty: DATestUtils.shadowValue }, 36 | ) 37 | ) 38 | # Device advisor test will not return PUBACK, therefore we use AT_MOST_ONCE so that 39 | # we dont busy wait for PUBACK 40 | shadow_future = shadow_client.publish_update_shadow(request, mqtt.QoS.AT_MOST_ONCE) 41 | shadow_future.result() 42 | 43 | disconnect_future = mqtt_connection.disconnect() 44 | disconnect_future.result() 45 | quit(0) 46 | -------------------------------------------------------------------------------- /docsrc/_static/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws/aws-iot-device-sdk-python-v2/efb8102a072194fc50f6951a642a2e7e5337c421/docsrc/_static/.gitkeep -------------------------------------------------------------------------------- /docsrc/_static/css/custom.css: -------------------------------------------------------------------------------- 1 | @import "../bizstyle.css"; 2 | 3 | span.pre { 4 | white-space: pre-wrap !important; 5 | } 6 | -------------------------------------------------------------------------------- /docsrc/_templates/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws/aws-iot-device-sdk-python-v2/efb8102a072194fc50f6951a642a2e7e5337c421/docsrc/_templates/.gitkeep -------------------------------------------------------------------------------- /docsrc/awsiot/awsiot.rst: -------------------------------------------------------------------------------- 1 | awsiot 2 | ====== 3 | 4 | .. automodule:: awsiot 5 | -------------------------------------------------------------------------------- /docsrc/awsiot/eventstreamrpc.rst: -------------------------------------------------------------------------------- 1 | awsiot.eventstreamrpc 2 | =========================== 3 | 4 | .. automodule:: awsiot.eventstreamrpc 5 | -------------------------------------------------------------------------------- /docsrc/awsiot/greengrass_discovery.rst: -------------------------------------------------------------------------------- 1 | awsiot.greengrass_discovery 2 | =========================== 3 | 4 | .. automodule:: awsiot.greengrass_discovery 5 | -------------------------------------------------------------------------------- /docsrc/awsiot/greengrasscoreipc.rst: -------------------------------------------------------------------------------- 1 | awsiot.greengrasscoreipc 2 | =========================== 3 | 4 | .. automodule:: awsiot.greengrasscoreipc 5 | 6 | .. automodule:: awsiot.greengrasscoreipc.client 7 | 8 | .. automodule:: awsiot.greengrasscoreipc.clientv2 9 | 10 | .. automodule:: awsiot.greengrasscoreipc.model 11 | -------------------------------------------------------------------------------- /docsrc/awsiot/iotidentity.rst: -------------------------------------------------------------------------------- 1 | awsiot.iotidentity 2 | ================== 3 | 4 | .. automodule:: awsiot.iotidentity 5 | -------------------------------------------------------------------------------- /docsrc/awsiot/iotjobs.rst: -------------------------------------------------------------------------------- 1 | awsiot.iotjobs 2 | ============== 3 | 4 | .. automodule:: awsiot.iotjobs 5 | -------------------------------------------------------------------------------- /docsrc/awsiot/iotshadow.rst: -------------------------------------------------------------------------------- 1 | awsiot.iotshadow 2 | ================ 3 | 4 | .. automodule:: awsiot.iotshadow 5 | -------------------------------------------------------------------------------- /docsrc/awsiot/mqtt5_client_builder.rst: -------------------------------------------------------------------------------- 1 | awsiot.mqtt5_client_builder 2 | =========================== 3 | 4 | .. automodule:: awsiot.mqtt5_client_builder 5 | -------------------------------------------------------------------------------- /docsrc/awsiot/mqtt_connection_builder.rst: -------------------------------------------------------------------------------- 1 | awsiot.mqtt_connection_builder 2 | ============================== 3 | 4 | .. automodule:: awsiot.mqtt_connection_builder 5 | -------------------------------------------------------------------------------- /docsrc/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | # 3 | # This file only contains a selection of the most common options. For a full 4 | # list see the documentation: 5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 6 | 7 | from datetime import datetime 8 | 9 | # -- Path setup -------------------------------------------------------------- 10 | 11 | # If extensions (or modules to document with autodoc) are in another directory, 12 | # add these directories to sys.path here. If the directory is relative to the 13 | # documentation root, use os.path.abspath to make it absolute, like shown here. 14 | # 15 | import os 16 | import sys 17 | sys.path.insert(0, os.path.abspath('..')) 18 | 19 | 20 | # -- Project information ----------------------------------------------------- 21 | 22 | project = 'AWS IoT Device SDK v2 for Python' 23 | copyright = '%s, Amazon Web Services, Inc' % datetime.now().year 24 | author = 'Amazon Web Services, Inc' 25 | 26 | 27 | # -- General configuration --------------------------------------------------- 28 | 29 | # Add any Sphinx extension module names here, as strings. They can be 30 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 31 | # ones. 32 | extensions = [ 33 | 'sphinx.ext.autodoc', 34 | 'sphinx.ext.autodoc.typehints', 35 | 'sphinx.ext.napoleon', 36 | 'sphinx.ext.intersphinx', # for linking external docs (ex: aws-crt-python) 37 | ] 38 | 39 | # Add any paths that contain templates here, relative to this directory. 40 | templates_path = ['_templates'] 41 | 42 | # List of patterns, relative to source directory, that match files and 43 | # directories to ignore when looking for source files. 44 | # This pattern also affects html_static_path and html_extra_path. 45 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] 46 | 47 | # For cross-linking to types from other libraries 48 | intersphinx_mapping = { 49 | 'python': ('https://docs.python.org/3', None), 50 | 'awscrt': ('https://awslabs.github.io/aws-crt-python', None), 51 | } 52 | 53 | # A string that determines how domain objects (e.g. functions, classes, 54 | # attributes, etc.) are displayed in their table of contents entry. 55 | toc_object_entries_show_parents = 'hide' 56 | 57 | # -- Options for HTML output ------------------------------------------------- 58 | 59 | autoclass_content = "both" 60 | #autodoc_default_flags = ['show-inheritance','members','undoc-members'] 61 | autodoc_default_options = { 62 | "show-inheritance": True, 63 | "members": True, 64 | "member-order": "bysource", 65 | } 66 | autodoc_typehints = 'description' 67 | 68 | # The theme to use for HTML and HTML Help pages. See the documentation for 69 | # a list of builtin themes. 70 | # 71 | html_theme = 'bizstyle' 72 | 73 | html_theme_options = { 74 | 'sidebarwidth': 300 75 | } 76 | 77 | # Add any paths that contain custom static files (such as style sheets) here, 78 | # relative to this directory. They are copied after the builtin static files, 79 | # so a file named "default.css" will overwrite the builtin "default.css". 80 | html_static_path = ['_static'] 81 | 82 | html_style = 'css/custom.css' 83 | 84 | # Extra warnings 85 | nitpicky = True 86 | nitpick_ignore_regex = [ 87 | # sphinx gets tripped up by classes in concurrent.futures.* 88 | ('py:class', r'concurrent\.futures\._base\..*'), 89 | 90 | # complains because public Operation classes have _private base classes, 91 | # and private classes aren't documented, but we want sphinx to document base classes by default. 92 | # Ignoring the warnings seems like a reasonable compromise 93 | ('py:class', r'awsiot\.greengrasscoreipc\.model\._.*Operation'), 94 | ] 95 | -------------------------------------------------------------------------------- /docsrc/index.rst: -------------------------------------------------------------------------------- 1 | .. AWS IoT Device SDK v2 for Python documentation master file, created by 2 | sphinx-quickstart on Mon Aug 17 14:57:36 2020. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | AWS IoT Device SDK v2 for Python 7 | ================================ 8 | 9 | Python bindings for the AWS IoT Device API. 10 | 11 | Developer Guide: https://docs.aws.amazon.com/iot/latest/developerguide 12 | 13 | GitHub: https://github.com/aws/aws-iot-device-sdk-python-v2 14 | 15 | PyPI: https://pypi.org/project/awsiotsdk/ 16 | 17 | API Reference 18 | ------------- 19 | .. toctree:: 20 | :maxdepth: 2 21 | 22 | awsiot/awsiot 23 | awsiot/eventstreamrpc 24 | awsiot/greengrasscoreipc 25 | awsiot/greengrass_discovery 26 | awsiot/mqtt_connection_builder 27 | awsiot/mqtt5_client_builder 28 | awsiot/iotidentity 29 | awsiot/iotjobs 30 | awsiot/iotshadow 31 | 32 | 33 | 34 | Indices and tables 35 | ================== 36 | 37 | * :ref:`genindex` 38 | * :ref:`modindex` 39 | * :ref:`search` 40 | -------------------------------------------------------------------------------- /documents/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct 2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 4 | opensource-codeofconduct@amazon.com with any additional questions or comments. 5 | -------------------------------------------------------------------------------- /documents/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional 4 | documentation, we greatly value feedback and contributions from our community. 5 | 6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary 7 | information to effectively respond to your bug report or contribution. 8 | 9 | 10 | ## Reporting Bugs/Feature Requests 11 | 12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features. 13 | 14 | When filing an issue, please check [existing open](https://github.com/awslabs/aws-iot-device-sdk-python-v2/issues), or [recently closed](https://github.com/awslabs/aws-iot-device-sdk-python-v2/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aclosed%20), issues to make sure somebody else hasn't already 15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: 16 | 17 | * A reproducible test case or series of steps 18 | * The version of our code being used 19 | * Any modifications you've made relevant to the bug 20 | * Anything unusual about your environment or deployment 21 | 22 | 23 | ## Contributing via Pull Requests 24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: 25 | 26 | 1. You are working against the latest source on the *main* branch. 27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted. 29 | 30 | To send us a pull request, please: 31 | 32 | 1. Fork the repository. 33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. 34 | 3. Ensure local tests pass. 35 | 4. Commit to your fork using clear commit messages. 36 | 5. Send us a pull request, answering any default questions in the pull request interface. 37 | 6. Wait for a repository collaborator to look at your pull request, run the automated tests, and review. If additional changes or discussion is needed, a collaborator will get back to you, so please stay involved in the conversation. 38 | * Note: pull requests from forks will not run the automated tests without collaborator involvement for security reasons. If you make a pull request and see that the tests are pending, this is normal and expected. 39 | 40 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and 41 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). 42 | 43 | 44 | ## Finding contributions to work on 45 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any ['help wanted'](https://github.com/awslabs/aws-iot-device-sdk-python-v2/labels/help%20wanted) issues is a great place to start. 46 | 47 | 48 | ## Code of Conduct 49 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 50 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 51 | opensource-codeofconduct@amazon.com with any additional questions or comments. 52 | 53 | 54 | ## Security issue notifications 55 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. 56 | 57 | 58 | ## Licensing 59 | 60 | See the [LICENSE](https://github.com/awslabs/aws-iot-device-sdk-python-v2/blob/main/LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. 61 | 62 | We may ask you to sign a [Contributor License Agreement (CLA)](http://en.wikipedia.org/wiki/Contributor_License_Agreement) for larger changes. 63 | -------------------------------------------------------------------------------- /documents/NOTICE: -------------------------------------------------------------------------------- 1 | AWS IoT Device SDK v2 for Python 2 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | SPDX-License-Identifier: Apache-2.0. 4 | -------------------------------------------------------------------------------- /make-docs.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import shutil 4 | import subprocess 5 | import sys 6 | 7 | try: 8 | import sphinx 9 | except Exception: 10 | exit('sphinx must be installed via pip') 11 | 12 | # Delete existing docs/, so that old no-longer-used files don't sit around forever 13 | # This slows iteration down, so pass --cache to disable 14 | if '--cache' not in sys.argv: 15 | shutil.rmtree('docs', ignore_errors=True) 16 | 17 | # Run sphinx 18 | if subprocess.run(['sphinx-build', 19 | '-j', 'auto', # parallel build 20 | '-W', # warnings as errors 21 | 'docsrc', 22 | 'docs']).returncode != 0: 23 | exit(1) 24 | 25 | # Create empty .nojekyll file, so that Github Pages shows plain old HTML 26 | with open('docs/.nojekyll', 'w'): 27 | pass 28 | -------------------------------------------------------------------------------- /samples/README.md: -------------------------------------------------------------------------------- 1 | # Sample apps for the AWS IoT Device SDK v2 for Python 2 | ## MQTT5 Samples 3 | #### MQTT5 is the recommended MQTT Client. It has many benefits over MQTT311 outlined in the [MQTT5 User Guide](../documents/MQTT5_Userguide.md) 4 | * [MQTT5 PubSub](./mqtt5_pubsub.md) 5 | + [Direct MQTT with X509-based mutual TLS](./mqtt5_pubsub.md#direct-mqtt-with-x509-based-mutual-tls) 6 | + [Direct MQTT with PKCS12 Method](./mqtt5_pubsub.md#direct-mqtt-with-pkcs12-method) 7 | + [MQTT over Websockets with Sigv4 authentication](./mqtt5_pubsub.md#mqtt-over-websockets-with-sigv4-authentication) 8 | + [MQTT over Websockets with Cognito authentication](./mqtt5_pubsub.md#mqtt-over-websockets-with-cognito-authentication) 9 | * [MQTT5 Shared Subscription](./mqtt5_shared_subscription.md) 10 | * [MQTT5 PKCS#11 Connect](./mqtt5_pkcs11_connect.md) 11 | * [MQTT5 Custom Authorizer Connect](./mqtt5_custom_authorizer_connect.md) 12 | * [MQTT5 Shadow](./shadow_mqtt5.md) 13 | * [MQTT5 Jobs](./jobs_mqtt5.md) 14 | * [MQTT5 Fleet Provisioning](./fleetprovisioning_mqtt5.md) 15 | ## MQTT311 Samples 16 | * [PubSub](./pubsub.md) 17 | * [Basic Connect](./basic_connect.md) 18 | * [Websocket Connect](./websocket_connect.md) 19 | * [PKCS#11 Connect](./pkcs11_connect.md) 20 | * [PKCS#12 Connect](./pkcs12_connect.md) 21 | * [Windows Certificate Connect](./windows_cert_connect/README.md) 22 | * [Custom Authorizer Connect](./custom_authorizer_connect.md) 23 | * [Cognito Connect](./cognito_connect.md) 24 | * [X509 Connect](./x509_connect.md) 25 | * [Shadow](./shadow.md) 26 | * [Jobs](./jobs.md) 27 | * [Fleet Provisioning](./fleetprovisioning.md) 28 | ## Other 29 | * [Greengrass Discovery](./basic_discovery.md) 30 | * [Greengrass IPC](./ipc_greengrass.md) 31 | 32 | ### Build instructions 33 | 34 | First, install the `aws-iot-devices-sdk-python-v2` with following the instructions from [Installation](../README.md#Installation). 35 | 36 | Each sample README has instructions on how to run each sample with the same name as the sample itself. For example, the [MQTT5 PubSub README](./mqtt5_pubsub.md) is `mqtt5_pubsub.md` and it can be run with the following: 37 | 38 | ``` sh 39 | # For Windows: replace 'python3' with 'python' and '/' with '\' 40 | python3 mqtt5_pubsub.py --endpoint --cert --key 41 | ``` 42 | 43 | ### Sample Help 44 | 45 | All samples will show their options by passing in `--help`. For example: 46 | 47 | ``` sh 48 | # For Windows: replace 'python3' with 'python' and '/' with '\' 49 | python3 mqtt5_pubsub.py --help 50 | ``` 51 | 52 | Which will result in output showing all of the options that can be passed in at the command line, along with descriptions of what each does and whether they are optional or not. 53 | 54 | ### Enable logging in samples 55 | 56 | To enable logging in the samples, you need to pass the `--verbosity` as an additional argument. `--verbosity` controls the level of logging shown. `--verbosity` can be set to `Trace`, `Debug`, `Info`, `Warn`, `Error`, `Fatal`, or `None`. 57 | 58 | For example, to run [MQTT5 PubSub](./mqtt5_pubsub.md) sample with logging you could use the following: 59 | 60 | ``` sh 61 | # For Windows: replace 'python3' with 'python' and '/' with '\' 62 | python3 mqtt5_pubsub.py --verbosity Debug 63 | ``` 64 | -------------------------------------------------------------------------------- /samples/basic_connect.md: -------------------------------------------------------------------------------- 1 | # Basic Connect 2 | 3 | [**Return to main sample list**](./README.md) 4 | 5 | This sample makes an MQTT connection using a certificate and key file using Mutual TLS (mTLS). On startup, the device connects to the server using the certificate and key files, and then disconnects. This sample is for reference on connecting via certificate and key files. Using a certificate and key file pair is the easiest and most straightforward way to authenticate a connection to AWS IoT Core. 6 | 7 | Your IoT Core Thing's [Policy](https://docs.aws.amazon.com/iot/latest/developerguide/iot-policies.html) must provide privileges for this sample to connect. Below is a sample policy that can be used on your IoT Core Thing that will allow this sample to run as intended. 8 | 9 |
10 | (see sample policy) 11 |
12 | {
13 |   "Version": "2012-10-17",
14 |   "Statement": [
15 |     {
16 |       "Effect": "Allow",
17 |       "Action": [
18 |         "iot:Connect"
19 |       ],
20 |       "Resource": [
21 |         "arn:aws:iot:region:account:client/test-*"
22 |       ]
23 |     }
24 |   ]
25 | }
26 | 
27 | 28 | Replace with the following with the data from your AWS account: 29 | * ``: The AWS IoT Core region where you created your AWS IoT Core thing you wish to use with this sample. For example `us-east-1`. 30 | * ``: Your AWS IoT Core account ID. This is the set of numbers in the top right next to your AWS account name when using the AWS IoT Core website. 31 | 32 | Note that in a real application, you may want to avoid the use of wildcards in your ClientID or use them selectively. Please follow best practices when working with AWS on production applications using the SDK. Also, for the purposes of this sample, please make sure your policy allows a client ID of `test-*` to connect or use `--client_id ` to send the client ID your policy supports. 33 | 34 |
35 | 36 | ## How to run 37 | 38 | To run the basic connect sample from the `samples` folder, use the following command: 39 | 40 | ```sh 41 | # For Windows: replace 'python3' with 'python' and '/' with '\' 42 | python3 basic_connect.py --endpoint --cert --key 43 | ``` 44 | 45 | You can also pass a Certificate Authority file (CA) if your certificate and key combination requires it: 46 | 47 | ```sh 48 | # For Windows: replace 'python3' with 'python' and '/' with '\' 49 | python3 basic_connect.py --endpoint --cert --key --ca_file 50 | ``` 51 | -------------------------------------------------------------------------------- /samples/basic_connect.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0. 3 | 4 | from awscrt import http 5 | from awsiot import mqtt_connection_builder 6 | from utils.command_line_utils import CommandLineUtils 7 | 8 | # This sample shows how to create a MQTT connection using a certificate file and key file. 9 | # This sample is intended to be used as a reference for making MQTT connections. 10 | 11 | # Callback when connection is accidentally lost. 12 | def on_connection_interrupted(connection, error, **kwargs): 13 | print("Connection interrupted. error: {}".format(error)) 14 | 15 | # Callback when an interrupted connection is re-established. 16 | def on_connection_resumed(connection, return_code, session_present, **kwargs): 17 | print("Connection resumed. return_code: {} session_present: {}".format(return_code, session_present)) 18 | 19 | 20 | if __name__ == '__main__': 21 | 22 | # cmdData is the arguments/input from the command line placed into a single struct for 23 | # use in this sample. This handles all of the command line parsing, validating, etc. 24 | # See the Utils/CommandLineUtils for more information. 25 | cmdData = CommandLineUtils.parse_sample_input_basic_connect() 26 | 27 | # Create the proxy options if the data is present in cmdData 28 | proxy_options = None 29 | if cmdData.input_proxy_host is not None and cmdData.input_proxy_port != 0: 30 | proxy_options = http.HttpProxyOptions( 31 | host_name=cmdData.input_proxy_host, 32 | port=cmdData.input_proxy_port) 33 | 34 | # Create a MQTT connection from the command line data 35 | mqtt_connection = mqtt_connection_builder.mtls_from_path( 36 | endpoint=cmdData.input_endpoint, 37 | port=cmdData.input_port, 38 | cert_filepath=cmdData.input_cert, 39 | pri_key_filepath=cmdData.input_key, 40 | ca_filepath=cmdData.input_ca, 41 | on_connection_interrupted=on_connection_interrupted, 42 | on_connection_resumed=on_connection_resumed, 43 | client_id=cmdData.input_clientId, 44 | clean_session=False, 45 | keep_alive_secs=30, 46 | http_proxy_options=proxy_options) 47 | 48 | if not cmdData.input_is_ci: 49 | print(f"Connecting to {cmdData.input_endpoint} with client ID '{cmdData.input_clientId}'...") 50 | else: 51 | print("Connecting to endpoint with client ID") 52 | 53 | connect_future = mqtt_connection.connect() 54 | # Future.result() waits until a result is available 55 | connect_future.result() 56 | print("Connected!") 57 | 58 | # Disconnect 59 | print("Disconnecting...") 60 | disconnect_future = mqtt_connection.disconnect() 61 | disconnect_future.result() 62 | print("Disconnected!") 63 | -------------------------------------------------------------------------------- /samples/basic_discovery.md: -------------------------------------------------------------------------------- 1 | # Greengrass Discovery 2 | 3 | [**Return to main sample list**](./README.md) 4 | 5 | This sample is intended for use with the following tutorials in the AWS IoT Greengrass documentation: 6 | 7 | * [Connect and test client devices](https://docs.aws.amazon.com/greengrass/v2/developerguide/client-devices-tutorial.html) (Greengrass V2) 8 | * [Test client device communications](https://docs.aws.amazon.com/greengrass/v2/developerguide/test-client-device-communications.html) (Greengrass V2) 9 | * [Getting Started with AWS IoT Greengrass](https://docs.aws.amazon.com/greengrass/latest/developerguide/gg-gs.html) (Greengrass V1) 10 | -------------------------------------------------------------------------------- /samples/cognito_connect.md: -------------------------------------------------------------------------------- 1 | # Cognito Connect 2 | 3 | [**Return to main sample list**](./README.md) 4 | 5 | This sample is similar to the [Websocket Connect sample](./websocket_connect.md), but instead of sourcing the AWS credentials from the environment files or local files, it instead uses a [Cognito](https://aws.amazon.com/cognito/) identity to authorize the connection. This has the advantage of not requiring the needing to store AWS credentials on the device itself with permissions to perform the IoT actions your device requires, but instead just having AWS credentials for the [Cognito](https://aws.amazon.com/cognito/) identity instead. This provides a layer of security and indirection that gives you better security. 6 | 7 | On startup, the device connects to the server and then disconnects. This sample is for reference on connecting using Cognito. 8 | 9 | Your IoT Core Thing's [Policy](https://docs.aws.amazon.com/iot/latest/developerguide/iot-policies.html) must provide privileges for this sample to connect. Below is a sample policy that can be used on your IoT Core Thing that will allow this sample to run as intended. 10 | 11 |
12 | (see sample policy) 13 |
14 | {
15 |   "Version": "2012-10-17",
16 |   "Statement": [
17 |     {
18 |       "Effect": "Allow",
19 |       "Action": [
20 |         "iot:Connect"
21 |       ],
22 |       "Resource": [
23 |         "arn:aws:iot:region:account:client/test-*"
24 |       ]
25 |     }
26 |   ]
27 | }
28 | 
29 | 30 | Replace with the following with the data from your AWS account: 31 | * ``: The AWS IoT Core region where you created your AWS IoT Core thing you wish to use with this sample. For example `us-east-1`. 32 | * ``: Your AWS IoT Core account ID. This is the set of numbers in the top right next to your AWS account name when using the AWS IoT Core website. 33 | 34 | Note that in a real application, you may want to avoid the use of wildcards in your ClientID or use them selectively. Please follow best practices when working with AWS on production applications using the SDK. Also, for the purposes of this sample, please make sure your policy allows a client ID of `test-*` to connect or use `--client_id ` to send the client ID your policy supports. 35 | 36 |
37 | 38 | ## How to run 39 | 40 | To run this sample, you need to have a Cognito identifier ID. You can get a Cognito identifier ID by creating a Cognito identity pool. For creating Cognito identity pools, please see the following page on the AWS documentation: [Tutorial: Creating an identity pool](https://docs.aws.amazon.com/cognito/latest/developerguide/tutorial-create-identity-pool.html) 41 | You should also add _iot:Connect_ permission to the role added to congnito or the default role created automatically when creating the new identity (or create a 42 |
43 | (see sample policy) 44 |
45 | {
46 |   "Version": "2012-10-17",
47 |   "Statement": [
48 |     {
49 |       "Effect": "Allow",
50 |       "Action": [
51 |         "cognito-identity:GetCredentialsForIdentity",
52 |         "iot:Connect"
53 |       ],
54 |       "Resource": [
55 |         "*"
56 |       ]
57 |     }
58 |   ]
59 | }
60 | 
61 |
62 | 63 | **Note:** This sample assumes using an identity pool with unauthenticated identity access for the sake of convenience. Please follow best practices in a real world application based on the needs of your application and the intended use case. 64 | 65 | Once you have a Cognito identity pool, you can run the following CLI command to get the Cognito identity pool ID: 66 | ```sh 67 | aws cognito-identity get-id --identity-pool-id 68 | # result from above command 69 | { 70 | "IdentityId": "" 71 | } 72 | ``` 73 | 74 | You can then use the returned ID in the `IdentityId` result as the input for the `--cognito_identity` argument. Please note that the Cognito identity pool ID is **not** the same as a Cognito identity ID and the sample will not work if you pass a Cognito pool id. 75 | 76 | To run the Cognito connect sample from the `samples` folder, use the following command: 77 | 78 | ``` sh 79 | # For Windows: replace 'python3' with 'python' and '/' with '\' 80 | python3 cognito_connect.py --endpoint --signing_region --cognito_identity 81 | ``` 82 | -------------------------------------------------------------------------------- /samples/cognito_connect.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0. 3 | 4 | from awscrt import http, auth, io 5 | from awsiot import mqtt_connection_builder 6 | from utils.command_line_utils import CommandLineUtils 7 | 8 | # This sample shows how to create a MQTT connection using Cognito. 9 | # This sample is intended to be used as a reference for making MQTT connections. 10 | 11 | # cmdData is the arguments/input from the command line placed into a single struct for 12 | # use in this sample. This handles all of the command line parsing, validating, etc. 13 | # See the Utils/CommandLineUtils for more information. 14 | cmdData = CommandLineUtils.parse_sample_input_cognito_connect() 15 | 16 | # Callback when connection is accidentally lost. 17 | def on_connection_interrupted(connection, error, **kwargs): 18 | print("Connection interrupted. error: {}".format(error)) 19 | 20 | # Callback when an interrupted connection is re-established. 21 | def on_connection_resumed(connection, return_code, session_present, **kwargs): 22 | print("Connection resumed. return_code: {} session_present: {}".format(return_code, session_present)) 23 | 24 | 25 | if __name__ == '__main__': 26 | # Create the proxy options if the data is present in cmdData 27 | proxy_options = None 28 | if cmdData.input_proxy_host is not None and cmdData.input_proxy_port != 0: 29 | proxy_options = http.HttpProxyOptions( 30 | host_name=cmdData.input_proxy_host, 31 | port=cmdData.input_proxy_port) 32 | 33 | # Create the cognito credentials provider 34 | # Note: This sample and code assumes that you are using a Cognito identity 35 | # in the same region as you pass to "--signing_region". 36 | # If not, you may need to adjust the Cognito endpoint in the cmdUtils. 37 | # See https://docs.aws.amazon.com/general/latest/gr/cognito_identity.html 38 | # for all Cognito endpoints. 39 | cognito_endpoint = f"cognito-identity.{cmdData.input_signing_region}.amazonaws.com" 40 | credentials_provider = auth.AwsCredentialsProvider.new_cognito( 41 | endpoint=cognito_endpoint, 42 | identity=cmdData.input_cognito_identity, 43 | tls_ctx=io.ClientTlsContext(io.TlsContextOptions())) 44 | 45 | # Create a MQTT connection from the command line data 46 | mqtt_connection = mqtt_connection_builder.websockets_with_default_aws_signing( 47 | endpoint=cmdData.input_endpoint, 48 | region=cmdData.input_signing_region, 49 | credentials_provider=credentials_provider, 50 | http_proxy_options=proxy_options, 51 | on_connection_interrupted=on_connection_interrupted, 52 | on_connection_resumed=on_connection_resumed, 53 | client_id=cmdData.input_clientId, 54 | clean_session=False, 55 | keep_alive_secs=30) 56 | 57 | if not cmdData.input_is_ci: 58 | print(f"Connecting to {cmdData.input_endpoint} with client ID '{cmdData.input_clientId}'...") 59 | else: 60 | print("Connecting to endpoint with client ID...") 61 | 62 | connect_future = mqtt_connection.connect() 63 | 64 | # Future.result() waits until a result is available 65 | connect_future.result() 66 | print("Connected!") 67 | 68 | # Disconnect 69 | print("Disconnecting...") 70 | disconnect_future = mqtt_connection.disconnect() 71 | disconnect_future.result() 72 | print("Disconnected!") 73 | -------------------------------------------------------------------------------- /samples/custom_authorizer_connect.md: -------------------------------------------------------------------------------- 1 | # Custom Authorizer Connect 2 | 3 | [**Return to main sample list**](./README.md) 4 | 5 | This sample makes an MQTT connection and connects through a [Custom Authorizer](https://docs.aws.amazon.com/iot/latest/developerguide/custom-authentication.html). On startup, the device connects to the server and then disconnects. This sample is for reference on connecting using a Custom Authorizer. Using a Custom Authorizer allows you to perform your own authorization using an AWS Lambda function. See [Custom Authorizer](https://docs.aws.amazon.com/iot/latest/developerguide/custom-authentication.html) for more information. 6 | 7 | You will need to setup your Custom Authorizer so that the lambda function returns a policy document. See [this page on the documentation](https://docs.aws.amazon.com/iot/latest/developerguide/config-custom-auth.html) for more details and example return result. You can customize this lambda function as needed for your application to provide your own security measures based on the needs of your application. 8 | 9 | Your IoT Core Thing's [Policy](https://docs.aws.amazon.com/iot/latest/developerguide/iot-policies.html) must provide privileges for this sample to connect. Below is a sample policy that can be used on your IoT Core Thing that will allow this sample to run as intended. 10 | 11 |
12 | (see sample policy) 13 |
14 | {
15 |   "Version": "2012-10-17",
16 |   "Statement": [
17 |     {
18 |       "Effect": "Allow",
19 |       "Action": [
20 |         "iot:Connect"
21 |       ],
22 |       "Resource": [
23 |         "arn:aws:iot:region:account:client/test-*"
24 |       ]
25 |     }
26 |   ]
27 | }
28 | 
29 | 30 | Replace with the following with the data from your AWS account: 31 | * ``: The AWS IoT Core region where you created your AWS IoT Core thing you wish to use with this sample. For example `us-east-1`. 32 | * ``: Your AWS IoT Core account ID. This is the set of numbers in the top right next to your AWS account name when using the AWS IoT Core website. 33 | 34 | Note that in a real application, you may want to avoid the use of wildcards in your ClientID or use them selectively. Please follow best practices when working with AWS on production applications using the SDK. Also, for the purposes of this sample, please make sure your policy allows a client ID of `test-*` to connect or use `--client_id ` to send the client ID your policy supports. 35 | 36 |
37 | 38 | # How to run 39 | 40 | **Note** The sample also allows passing arguments to specify additional data your custom authorizer may need. The snippet below assumes that the custom authorizer does not need these additional parameters, but in the general case, you will almost always need some of them depending on the authorizer's configuration and the associated Lambda function's internals. 41 | * `--custom_auth_username` - opaque string value passed to the authorizer via an MQTT Connect packet. The authorizer's Lambda can check this value from the event JSON value it receives as input: `event.protocolData.mqtt.username` 42 | * `--custom_auth_password` - opaque binary value passed to the authorizer via an MQTT Connect packet. The authorizer's Lambda can check this value from the event JSON value it receives as input: `event.protocolData.mqtt.password` 43 | * `--custom_auth_token_key_name` - (Signed authorizers only) The query string parameter name that the token value should be bound to in the MQTT Connect packet. 44 | * `--custom_auth_token_value` - (Signed authorizers only) An arbitrary value chosen by the user. The user must also submit a digital signature of this value using the private key associated with the authorizer. 45 | * `--custom_auth_authorizer_signature` - (Signed authorizers only) a digital signature of the value of the `--custom_auth_token_value` parameter using the private key associated with the authorizer. The binary signature value must be base64 encoded and then URI encoded; the SDK will not do this for you. 46 | 47 | ## MQTT over TCP with TLS 48 | 49 | To run the Custom Authorizer connect sample from the `samples` folder, use the following command: 50 | 51 | ``` sh 52 | # For Windows: replace 'python3' with 'python' 53 | python3 custom_authorizer_connect.py --endpoint --custom_auth_authorizer_name 54 | ``` 55 | -------------------------------------------------------------------------------- /samples/custom_authorizer_connect.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0. 3 | 4 | from awsiot import mqtt_connection_builder 5 | from utils.command_line_utils import CommandLineUtils 6 | 7 | # This sample is similar to `samples/basic_connect.py` but it connects 8 | # through a custom authorizer rather than using a key and certificate. 9 | 10 | # cmdData is the arguments/input from the command line placed into a single struct for 11 | # use in this sample. This handles all of the command line parsing, validating, etc. 12 | # See the Utils/CommandLineUtils for more information. 13 | cmdData = CommandLineUtils.parse_sample_input_custom_authorizer_connect() 14 | 15 | 16 | def on_connection_interrupted(connection, error, **kwargs): 17 | # Callback when connection is accidentally lost. 18 | print("Connection interrupted. error: {}".format(error)) 19 | 20 | 21 | def on_connection_resumed(connection, return_code, session_present, **kwargs): 22 | # Callback when an interrupted connection is re-established. 23 | print("Connection resumed. return_code: {} session_present: {}".format(return_code, session_present)) 24 | 25 | 26 | if __name__ == '__main__': 27 | # Create MQTT connection with a custom authorizer 28 | mqtt_connection = mqtt_connection_builder.direct_with_custom_authorizer( 29 | endpoint=cmdData.input_endpoint, 30 | auth_username=cmdData.input_custom_auth_username, 31 | auth_authorizer_name=cmdData.input_custom_authorizer_name, 32 | auth_authorizer_signature=cmdData.input_custom_authorizer_signature, 33 | auth_password=cmdData.input_custom_auth_password, 34 | auth_token_key_name=cmdData.input_custom_authorizer_token_key_name, 35 | auth_token_value=cmdData.input_custom_authorizer_token_value, 36 | on_connection_interrupted=on_connection_interrupted, 37 | on_connection_resumed=on_connection_resumed, 38 | client_id=cmdData.input_clientId, 39 | clean_session=False, 40 | keep_alive_secs=30) 41 | 42 | if not cmdData.input_is_ci: 43 | print(f"Connecting to {cmdData.input_endpoint} with client ID '{cmdData.input_clientId}'...") 44 | else: 45 | print("Connecting to endpoint with client ID") 46 | 47 | connect_future = mqtt_connection.connect() 48 | 49 | # Future.result() waits until a result is available 50 | connect_future.result() 51 | print("Connected!") 52 | 53 | # Disconnect 54 | print("Disconnecting...") 55 | disconnect_future = mqtt_connection.disconnect() 56 | disconnect_future.result() 57 | print("Disconnected!") 58 | -------------------------------------------------------------------------------- /samples/ipc_greengrass.md: -------------------------------------------------------------------------------- 1 | # Greengrass IPC 2 | 3 | [**Return to main sample list**](./README.md) 4 | 5 | This sample uses [AWS IoT Greengrass V2](https://docs.aws.amazon.com/greengrass/index.html) to publish messages from the Greengrass device to the AWS IoT MQTT broker. 6 | 7 | This sample can be deployed as a Greengrass V2 component and it will publish 10 MQTT messages over the course of 10 seconds. The IPC integration with Greengrass V2 allows this code to run without additional IoT certificates or secrets, because it directly communicates with the Greengrass core running on the device. As such, to run this sample you need Greengrass Core running. 8 | 9 | Some Greengrass references: 10 | 11 | * See this page for more information on Greengrass v2 components: https://docs.aws.amazon.com/greengrass/v2/developerguide/create-components.html 12 | * See this page for more information on IPC in Greengrass v2: https://docs.aws.amazon.com/greengrass/v2/developerguide/interprocess-communication.html 13 | * See this page for more information on how to make a local deployment: https://docs.aws.amazon.com/greengrass/v2/developerguide/test-components.html 14 | 15 | ## How to run 16 | 17 | To run the sample, create a new AWS IoT Greengrass component and deploy it to your device with the following recipe snippet to allow your device to publish to AWS IoT Core: 18 | 19 |
20 | 21 | ```yaml 22 | --- 23 | RecipeFormatVersion: "2020-01-25" 24 | ComponentName: "GreengrassIPC" 25 | ComponentVersion: "1.0.0" 26 | ComponentDescription: "IPC Greengrass sample." 27 | ComponentPublisher: "" 28 | ComponentConfiguration: 29 | DefaultConfiguration: 30 | accessControl: 31 | aws.greengrass.ipc.mqttproxy: 32 | software.amazon.awssdk.iotdevicesdk.GreengrassIPC:1: 33 | policyDescription: "Allows access to publish to all AWS IoT Core topics. For demonstration only - use best practices in a real application" 34 | operations: 35 | - aws.greengrass#PublishToIoTCore 36 | resources: 37 | - "*" 38 | Manifests: 39 | - Platform: 40 | os: all 41 | Artifacts: 42 | - URI: "/GreengrassIPC/1.0.0/GreengrassIPC.zip" 43 | Lifecycle: 44 | RequiresPrivilege: true 45 | Run: "python3 -u {artifacts:path}/GreengrassIPC/main.py " 46 | ``` 47 | 48 | Replace the following with your information: 49 | * `` with the name you wish to publish your component under. 50 | * `` with the S3 bucket URL for your account to store your Greengrass components under 51 | 52 | You can add the following to the recipe `Manifest` snippet to install Python and `AWS IoT Device SDK v2 for Python` as a dependency on Linux: 53 | 54 | ```yaml 55 | ... 56 | Manifests: 57 | - Platform: 58 | ... 59 | Lifecycle: 60 | Install: 61 | RequiresPrivilege: true 62 | Script: "apt-get update --quiet && apt-get --yes install python3 python3-pip && pip3 install awsiotsdk" 63 | ... 64 | ``` 65 | 66 |
67 |
68 | 69 | As an example, you could use the following `gdk-config.json` below in your component with this sample and the recipe yaml shown above: 70 |
71 | 72 | ```json 73 | { 74 | "component": { 75 | "software.amazon.awssdk.iotdevicesdk.GreengrassIPC": { 76 | "author": "", 77 | "version": "1.0.0", 78 | "build": { 79 | "build_system": "zip" 80 | }, 81 | "publish": { 82 | "bucket": "", 83 | "region": "" 84 | } 85 | } 86 | }, 87 | "gdk_version": "1.0.0" 88 | } 89 | ``` 90 | 91 | Replace the following with your information: 92 | * `` with the name you wish to publish your component under. 93 | * `` with the S3 bucket URL for your account to store your Greengrass components under. 94 | * `` the region of your S3 bucket and Greengrass device. 95 | 96 |
97 |
98 | 99 | Note that you will need to have the Python V2 SDK installed on the Greengrass device in order to compile the sample on the Greengrass device. 100 | -------------------------------------------------------------------------------- /samples/ipc_greengrass.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0. 3 | 4 | """ 5 | This sample uses AWS IoT Greengrass v2 to publish messages from the device to 6 | the AWS IoT Core MQTT broker. 7 | 8 | This example can be deployed as Greengrass v2 component and it will start 9 | publishing telemetry data as MQTT messages in periodic intervals. The IPC 10 | integration with Greegrass v2 allows this code to run without additional IoT 11 | certificates or secrets, because it directly communicates with Greengrass Core 12 | on the device. 13 | 14 | See this page for more information on Greengrass v2 components: 15 | https://docs.aws.amazon.com/greengrass/v2/developerguide/create-components.html 16 | 17 | See this page for more information on IPC in Greengrass v2: 18 | https://docs.aws.amazon.com/greengrass/v2/developerguide/interprocess-communication.html 19 | 20 | """ 21 | 22 | import json 23 | import time 24 | import os 25 | import sys 26 | 27 | import awsiot.greengrasscoreipc 28 | import awsiot.greengrasscoreipc.model as model 29 | 30 | if __name__ == '__main__': 31 | try: 32 | print("Connecting to Greengrass...") 33 | ipc_client = awsiot.greengrasscoreipc.connect() 34 | 35 | for i in range(0, 10): 36 | telemetry_data = { 37 | "timestamp": int(round(time.time() * 1000)), 38 | "battery_state_of_charge": 42.5, 39 | "location": { 40 | "longitude": 48.15743, 41 | "latitude": 11.57549, 42 | }, 43 | } 44 | 45 | op = ipc_client.new_publish_to_iot_core() 46 | op.activate(model.PublishToIoTCoreRequest( 47 | topic_name="my/iot/{}/telemetry".format(os.getenv("AWS_IOT_THING_NAME")), 48 | qos=model.QOS.AT_LEAST_ONCE, 49 | payload=json.dumps(telemetry_data).encode(), 50 | )) 51 | try: 52 | result = op.get_response().result(timeout=5.0) 53 | print("Successfully published message: {}".format(result)) 54 | except Exception as e: 55 | print("Failed to publish message: {}".format(e)) 56 | sys.exit(1) 57 | 58 | except Exception as e: 59 | print("Sample failed: {}".format(e)) 60 | sys.exit(1) 61 | -------------------------------------------------------------------------------- /samples/jobs.md: -------------------------------------------------------------------------------- 1 | # Jobs 2 | 3 | [**Return to main sample list**](./README.md) 4 | 5 | This sample uses the AWS IoT [Jobs](https://docs.aws.amazon.com/iot/latest/developerguide/iot-jobs.html) Service to describe jobs to execute. [Jobs](https://docs.aws.amazon.com/iot/latest/developerguide/iot-jobs.html) is a service that allows you to define and respond to remote operation requests defined through the AWS IoT Core website or via any other device (or CLI command) that can access the [Jobs](https://docs.aws.amazon.com/iot/latest/developerguide/iot-jobs.html) service. 6 | 7 | Note: This sample requires you to create jobs for your device to execute. See 8 | [instructions here](https://docs.aws.amazon.com/iot/latest/developerguide/create-manage-jobs.html) for how to make jobs. 9 | 10 | On startup, the sample describes the jobs that are pending execution and pretends to process them, marking each job as complete as it does so. 11 | 12 | Your IoT Core Thing's [Policy](https://docs.aws.amazon.com/iot/latest/developerguide/iot-policies.html) must provide privileges for this sample to connect, subscribe, publish, and receive. Below is a sample policy that can be used on your IoT Core Thing that will allow this sample to run as intended. 13 | 14 |
15 | Sample Policy 16 |
17 | {
18 |   "Version": "2012-10-17",
19 |   "Statement": [
20 |     {
21 |       "Effect": "Allow",
22 |       "Action": "iot:Publish",
23 |       "Resource": [
24 |         "arn:aws:iot:region:account:topic/$aws/things/thingname/jobs/start-next",
25 |         "arn:aws:iot:region:account:topic/$aws/things/thingname/jobs/*/update",
26 |         "arn:aws:iot:region:account:topic/$aws/things/thingname/jobs/*/get",
27 |         "arn:aws:iot:region:account:topic/$aws/things/thingname/jobs/get"
28 |       ]
29 |     },
30 |     {
31 |       "Effect": "Allow",
32 |       "Action": "iot:Receive",
33 |       "Resource": [
34 |         "arn:aws:iot:region:account:topic/$aws/things/thingname/jobs/notify-next",
35 |         "arn:aws:iot:region:account:topic/$aws/things/thingname/jobs/start-next/*",
36 |         "arn:aws:iot:region:account:topic/$aws/things/thingname/jobs/*/update/*",
37 |         "arn:aws:iot:region:account:topic/$aws/things/thingname/jobs/get/*",
38 |         "arn:aws:iot:region:account:topic/$aws/things/thingname/jobs/*/get/*"
39 |       ]
40 |     },
41 |     {
42 |       "Effect": "Allow",
43 |       "Action": "iot:Subscribe",
44 |       "Resource": [
45 |         "arn:aws:iot:region:account:topicfilter/$aws/things/thingname/jobs/notify-next",
46 |         "arn:aws:iot:region:account:topicfilter/$aws/things/thingname/jobs/start-next/*",
47 |         "arn:aws:iot:region:account:topicfilter/$aws/things/thingname/jobs/*/update/*",
48 |         "arn:aws:iot:region:account:topicfilter/$aws/things/thingname/jobs/get/*",
49 |         "arn:aws:iot:region:account:topicfilter/$aws/things/thingname/jobs/*/get/*"
50 |       ]
51 |     },
52 |     {
53 |       "Effect": "Allow",
54 |       "Action": "iot:Connect",
55 |       "Resource": "arn:aws:iot:region:account:client/test-*"
56 |     }
57 |   ]
58 | }
59 | 
60 | 61 | Replace with the following with the data from your AWS account: 62 | * ``: The AWS IoT Core region where you created your AWS IoT Core thing you wish to use with this sample. For example `us-east-1`. 63 | * ``: Your AWS IoT Core account ID. This is the set of numbers in the top right next to your AWS account name when using the AWS IoT Core website. 64 | * ``: The name of your AWS IoT Core thing you want the device connection to be associated with 65 | 66 | Note that in a real application, you may want to avoid the use of wildcards in your ClientID or use them selectively. Please follow best practices when working with AWS on production applications using the SDK. Also, for the purposes of this sample, please make sure your policy allows a client ID of `test-*` to connect or use `--client_id ` to send the client ID your policy supports. 67 | 68 |
69 | 70 | ## How to run 71 | 72 | Use the following command to run the Jobs sample from the `samples` folder: 73 | 74 | ``` sh 75 | # For Windows: replace 'python3' with 'python' and '/' with '\' 76 | python3 jobs.py --endpoint --cert --key --thing_name 77 | ``` 78 | 79 | You can also pass a Certificate Authority file (CA) if your certificate and key combination requires it: 80 | 81 | ``` sh 82 | # For Windows: replace 'python3' with 'python' and '/' with '\' 83 | python3 jobs.py --endpoint --cert --key --thing_name --ca_file 84 | ``` 85 | -------------------------------------------------------------------------------- /samples/mqtt5_custom_authorizer_connect.md: -------------------------------------------------------------------------------- 1 | # Custom Authorizer Connect 2 | 3 | [**Return to main sample list**](./README.md) 4 | 5 | This sample makes an MQTT5 connection and connects through a [Custom Authorizer](https://docs.aws.amazon.com/iot/latest/developerguide/custom-authentication.html). On startup, the device connects to the server and then disconnects. This sample is for reference on connecting using a Custom Authorizer with MQTT5. Using a Custom Authorizer allows you to perform your own authorization using an AWS Lambda function. See [Custom Authorizer](https://docs.aws.amazon.com/iot/latest/developerguide/custom-authentication.html) for more information. 6 | 7 | You will need to setup your Custom Authorizer so that the lambda function returns a policy document. See [this page on the documentation](https://docs.aws.amazon.com/iot/latest/developerguide/config-custom-auth.html) for more details and example return result. You can customize this lambda function as needed for your application to provide your own security measures based on the needs of your application. 8 | 9 | MQTT5 introduces additional features and enhancements that improve the development experience with MQTT. You can read more about MQTT5 in the Python V2 SDK by checking out the [MQTT5 user guide](../documents/MQTT5_Userguide.md). 10 | 11 | Your IoT Core Thing's [Policy](https://docs.aws.amazon.com/iot/latest/developerguide/iot-policies.html) must provide privileges for this sample to connect. Below is a sample policy that can be used on your IoT Core Thing that will allow this sample to run as intended. 12 | 13 |
14 | (see sample policy) 15 |
16 | {
17 |   "Version": "2012-10-17",
18 |   "Statement": [
19 |     {
20 |       "Effect": "Allow",
21 |       "Action": [
22 |         "iot:Connect"
23 |       ],
24 |       "Resource": [
25 |         "arn:aws:iot:region:account:client/test-*"
26 |       ]
27 |     }
28 |   ]
29 | }
30 | 
31 | 32 | Replace with the following with the data from your AWS account: 33 | * ``: The AWS IoT Core region where you created your AWS IoT Core thing you wish to use with this sample. For example `us-east-1`. 34 | * ``: Your AWS IoT Core account ID. This is the set of numbers in the top right next to your AWS account name when using the AWS IoT Core website. 35 | 36 | Note that in a real application, you may want to avoid the use of wildcards in your ClientID or use them selectively. Please follow best practices when working with AWS on production applications using the SDK. Also, for the purposes of this sample, please make sure your policy allows a client ID of `test-*` to connect or use `--client_id ` to send the client ID your policy supports. 37 | 38 |
39 | 40 | # How to run 41 | 42 | **Note** The sample also allows passing arguments to specify additional data your custom authorizer may need. The snippets below assume that the custom authorizer does not need these additional parameters, but in the general case, you will almost always need some of them depending on the authorizer's configuration and the associated Lambda function's internals. 43 | * `--custom_auth_username` - opaque string value passed to the authorizer via an MQTT Connect packet. The authorizer's Lambda can check this value from the event JSON value it receives as input: `event.protocolData.mqtt.username` 44 | * `--custom_auth_password` - opaque binary value passed to the authorizer via an MQTT Connect packet. The authorizer's Lambda can check this value from the event JSON value it receives as input: `event.protocolData.mqtt.password` 45 | * `--custom_auth_token_key_name` - (Signed authorizers only) The query string parameter name that the token value should be bound to in the MQTT Connect packet. 46 | * `--custom_auth_token_value` - (Signed authorizers only) An arbitrary value chosen by the user. The user must also submit a digital signature of this value using the private key associated with the authorizer. 47 | * `--custom_auth_authorizer_signature` - (Signed authorizers only) a digital signature of the value of the `--custom_auth_token_value` parameter using the private key associated with the authorizer. The binary signature value must be base64 encoded and then URI encoded; the SDK will not do this for you. 48 | 49 | ## MQTT over TCP with TLS 50 | 51 | To run the MQTT5 Custom Authorizer connect sample from the `samples` folder using TCP, use the following command: 52 | 53 | ``` sh 54 | # For Windows: replace 'python3' with 'python' 55 | python3 mqtt5_custom_authorizer_connect.py --endpoint --custom_auth_authorizer_name 56 | ``` 57 | 58 | ## MQTT over Websockets with TLS 59 | 60 | To run the MQTT5 Custom Authorizer connect sample from the `samples` folder using Websockets, use the following command: 61 | 62 | ``` sh 63 | # For Windows: replace 'python3' with 'python' 64 | python3 mqtt5_custom_authorizer_connect.py --endpoint --use_websockets "true" --custom_auth_authorizer_name 65 | ``` 66 | -------------------------------------------------------------------------------- /samples/mqtt5_custom_authorizer_connect.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0. 3 | 4 | from awsiot import mqtt5_client_builder 5 | from awscrt import mqtt5 6 | from concurrent.futures import Future 7 | from utils.command_line_utils import CommandLineUtils 8 | 9 | TIMEOUT = 100 10 | 11 | # cmdData is the arguments/input from the command line placed into a single struct for 12 | # use in this sample. This handles all of the command line parsing, validating, etc. 13 | # See the Utils/CommandLineUtils for more information. 14 | cmdData = CommandLineUtils.parse_sample_input_mqtt5_custom_authorizer_connect() 15 | 16 | future_stopped = Future() 17 | future_connection_success = Future() 18 | 19 | # Callback for the lifecycle event Stopped 20 | def on_lifecycle_stopped(lifecycle_stopped_data: mqtt5.LifecycleStoppedData): 21 | print("Lifecycle Stopped") 22 | global future_stopped 23 | future_stopped.set_result(lifecycle_stopped_data) 24 | 25 | 26 | # Callback for the lifecycle event Connection Success 27 | def on_lifecycle_connection_success(lifecycle_connect_success_data: mqtt5.LifecycleConnectSuccessData): 28 | print("Lifecycle Connection Success") 29 | global future_connection_success 30 | future_connection_success.set_result(lifecycle_connect_success_data) 31 | 32 | 33 | if __name__ == '__main__': 34 | 35 | # Create MQTT5 Client with a custom authorizer 36 | if cmdData.input_use_websockets is None: 37 | client = mqtt5_client_builder.direct_with_custom_authorizer( 38 | endpoint=cmdData.input_endpoint, 39 | ca_filepath=cmdData.input_ca, 40 | auth_username=cmdData.input_custom_auth_username, 41 | auth_authorizer_name=cmdData.input_custom_authorizer_name, 42 | auth_authorizer_signature=cmdData.input_custom_authorizer_signature, 43 | auth_password=cmdData.input_custom_auth_password, 44 | auth_token_key_name=cmdData.input_custom_authorizer_token_key_name, 45 | auth_token_value=cmdData.input_custom_authorizer_token_value, 46 | on_lifecycle_stopped=on_lifecycle_stopped, 47 | on_lifecycle_connection_success=on_lifecycle_connection_success, 48 | client_id=cmdData.input_clientId) 49 | else: 50 | client = mqtt5_client_builder.websockets_with_custom_authorizer( 51 | endpoint=cmdData.input_endpoint, 52 | auth_username=cmdData.input_custom_auth_username, 53 | auth_authorizer_name=cmdData.input_custom_authorizer_name, 54 | auth_authorizer_signature=cmdData.input_custom_authorizer_signature, 55 | auth_password=cmdData.input_custom_auth_password, 56 | auth_token_key_name=cmdData.input_custom_authorizer_token_key_name, 57 | auth_token_value=cmdData.input_custom_authorizer_token_value, 58 | on_lifecycle_stopped=on_lifecycle_stopped, 59 | on_lifecycle_connection_success=on_lifecycle_connection_success, 60 | client_id=cmdData.input_clientId) 61 | 62 | if not cmdData.input_is_ci: 63 | print(f"Connecting to {cmdData.input_endpoint} with client ID '{cmdData.input_clientId}'...") 64 | else: 65 | print("Connecting to endpoint with client ID") 66 | 67 | client.start() 68 | future_connection_success.result(TIMEOUT) 69 | print("Client Connected") 70 | 71 | print("Stopping Client") 72 | client.stop() 73 | 74 | future_stopped.result(TIMEOUT) 75 | print("Client Stopped!") 76 | -------------------------------------------------------------------------------- /samples/mqtt5_pkcs11_connect.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0. 3 | 4 | from awscrt import mqtt5, io 5 | from awsiot import mqtt5_client_builder 6 | from concurrent.futures import Future 7 | from utils.command_line_utils import CommandLineUtils 8 | 9 | TIMEOUT = 100 10 | 11 | # cmdData is the arguments/input from the command line placed into a single struct for 12 | # use in this sample. This handles all of the command line parsing, validating, etc. 13 | # See the Utils/CommandLineUtils for more information. 14 | cmdData = CommandLineUtils.parse_sample_input_mqtt5_pkcs11_connect() 15 | 16 | future_stopped = Future() 17 | future_connection_success = Future() 18 | 19 | # Callback for the lifecycle event Stopped 20 | def on_lifecycle_stopped(lifecycle_stopped_data: mqtt5.LifecycleStoppedData): 21 | print("Lifecycle Stopped") 22 | global future_stopped 23 | future_stopped.set_result(lifecycle_stopped_data) 24 | 25 | 26 | # Callback for the lifecycle event Connection Success 27 | def on_lifecycle_connection_success(lifecycle_connect_success_data: mqtt5.LifecycleConnectSuccessData): 28 | print("Lifecycle Connection Success") 29 | global future_connection_success 30 | future_connection_success.set_result(lifecycle_connect_success_data) 31 | 32 | 33 | if __name__ == '__main__': 34 | print("\nStarting MQTT5 pkcs11 connect Sample\n") 35 | 36 | print(f"Loading PKCS#11 library '{cmdData.input_pkcs11_lib_path}' ...") 37 | pkcs11_lib = io.Pkcs11Lib( 38 | file=cmdData.input_pkcs11_lib_path, 39 | behavior=io.Pkcs11Lib.InitializeFinalizeBehavior.STRICT) 40 | print("Loaded!") 41 | 42 | pkcs11_slot_id = None 43 | if (cmdData.input_pkcs11_slot_id is not None): 44 | pkcs11_slot_id = int(cmdData.input_pkcs11_slot_id) 45 | 46 | # Create MQTT5 client 47 | client = mqtt5_client_builder.mtls_with_pkcs11( 48 | pkcs11_lib=pkcs11_lib, 49 | user_pin=cmdData.input_pkcs11_user_pin, 50 | slot_id=pkcs11_slot_id, 51 | token_label=cmdData.input_pkcs11_token_label, 52 | private_key_label=cmdData.input_pkcs11_key_label, 53 | cert_filepath=cmdData.input_cert, 54 | endpoint=cmdData.input_endpoint, 55 | port=cmdData.input_port, 56 | ca_filepath=cmdData.input_ca, 57 | on_lifecycle_stopped=on_lifecycle_stopped, 58 | on_lifecycle_connection_success=on_lifecycle_connection_success, 59 | client_id=cmdData.input_clientId) 60 | 61 | print("MQTT5 Client Created") 62 | 63 | if not cmdData.input_is_ci: 64 | print(f"Connecting to {cmdData.input_endpoint} with client ID '{cmdData.input_clientId}'...") 65 | else: 66 | print("Connecting to endpoint with client ID") 67 | 68 | client.start() 69 | future_connection_success.result(TIMEOUT) 70 | print("Clint Connected") 71 | 72 | print("Stopping Client") 73 | client.stop() 74 | 75 | future_stopped.result(TIMEOUT) 76 | print("Client Stopped!") 77 | -------------------------------------------------------------------------------- /samples/pkcs11_connect.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0. 3 | 4 | from awscrt import io 5 | from awsiot import mqtt_connection_builder 6 | from utils.command_line_utils import CommandLineUtils 7 | 8 | # This sample is similar to `samples/basic_connect.py` but the private key 9 | # for mutual TLS is stored on a PKCS#11 compatible smart card or 10 | # Hardware Security Module (HSM). 11 | # 12 | # See `samples/README.md` for instructions on setting up your PKCS#11 device 13 | # to run this sample. 14 | # 15 | # WARNING: Unix only. Currently, TLS integration with PKCS#11 is only available on Unix devices. 16 | 17 | # cmdData is the arguments/input from the command line placed into a single struct for 18 | # use in this sample. This handles all of the command line parsing, validating, etc. 19 | # See the Utils/CommandLineUtils for more information. 20 | cmdData = CommandLineUtils.parse_sample_input_pkcs11_connect() 21 | 22 | # Callback when connection is accidentally lost. 23 | def on_connection_interrupted(connection, error, **kwargs): 24 | print("Connection interrupted. error: {}".format(error)) 25 | 26 | # Callback when an interrupted connection is re-established. 27 | def on_connection_resumed(connection, return_code, session_present, **kwargs): 28 | print("Connection resumed. return_code: {} session_present: {}".format(return_code, session_present)) 29 | 30 | 31 | if __name__ == '__main__': 32 | 33 | print(f"Loading PKCS#11 library '{cmdData.input_pkcs11_lib_path}' ...") 34 | pkcs11_lib = io.Pkcs11Lib( 35 | file=cmdData.input_pkcs11_lib_path, 36 | behavior=io.Pkcs11Lib.InitializeFinalizeBehavior.STRICT) 37 | print("Loaded!") 38 | 39 | pkcs11_slot_id = None 40 | if (cmdData.input_pkcs11_slot_id): 41 | pkcs11_slot_id = int(cmdData.input_pkcs11_slot_id) 42 | 43 | # Create MQTT connection 44 | mqtt_connection = mqtt_connection_builder.mtls_with_pkcs11( 45 | pkcs11_lib=pkcs11_lib, 46 | user_pin=cmdData.input_pkcs11_user_pin, 47 | slot_id=pkcs11_slot_id, 48 | token_label=cmdData.input_pkcs11_token_label, 49 | private_key_label=cmdData.input_pkcs11_key_label, 50 | cert_filepath=cmdData.input_cert, 51 | endpoint=cmdData.input_endpoint, 52 | port=cmdData.input_port, 53 | ca_filepath=cmdData.input_ca, 54 | on_connection_interrupted=on_connection_interrupted, 55 | on_connection_resumed=on_connection_resumed, 56 | client_id=cmdData.input_clientId, 57 | clean_session=False, 58 | keep_alive_secs=30) 59 | 60 | if not cmdData.input_is_ci: 61 | print(f"Connecting to {cmdData.input_endpoint} with client ID '{cmdData.input_clientId}'...") 62 | else: 63 | print("Connecting to endpoint with client ID") 64 | 65 | connect_future = mqtt_connection.connect() 66 | 67 | # Future.result() waits until a result is available 68 | connect_future.result() 69 | print("Connected!") 70 | 71 | # Disconnect 72 | print("Disconnecting...") 73 | disconnect_future = mqtt_connection.disconnect() 74 | disconnect_future.result() 75 | print("Disconnected!") 76 | -------------------------------------------------------------------------------- /samples/pkcs12_connect.md: -------------------------------------------------------------------------------- 1 | # PKCS12 Connect 2 | 3 | [**Return to main sample list**](../README.md) 4 | 5 | This sample is similar to the [Basic Connect](../BasicConnect/README.md) sample, in that it connects via Mutual TLS (mTLS) using a certificate and key file. However, unlike the Basic Connect where the certificate and private key file are stored on disk, this sample uses a PKCS#12 file instead. 6 | 7 | **WARNING: MacOS only**. Currently, TLS integration with PKCS12 is only available on MacOS devices. 8 | 9 | Your IoT Core Thing's [Policy](https://docs.aws.amazon.com/iot/latest/developerguide/iot-policies.html) must provide privileges for this sample to connect. Below is a sample policy that can be used on your IoT Core Thing that will allow this sample to run as intended. 10 | 11 |
12 | (see sample policy) 13 |
14 | {
15 |   "Version": "2012-10-17",
16 |   "Statement": [
17 |     {
18 |       "Effect": "Allow",
19 |       "Action": [
20 |         "iot:Connect"
21 |       ],
22 |       "Resource": [
23 |         "arn:aws:iot:region:account:client/test-*"
24 |       ]
25 |     }
26 |   ]
27 | }
28 | 
29 | 30 | Replace with the following with the data from your AWS account: 31 | * ``: The AWS IoT Core region where you created your AWS IoT Core thing you wish to use with this sample. For example `us-east-1`. 32 | * ``: Your AWS IoT Core account ID. This is the set of numbers in the top right next to your AWS account name when using the AWS IoT Core website. 33 | 34 | Note that in a real application, you may want to avoid the use of wildcards in your ClientID or use them selectively. Please follow best practices when working with AWS on production applications using the SDK. Also, for the purposes of this sample, please make sure your policy allows a client ID of `test-*` to connect or use `--client_id ` to send the client ID your policy supports. 35 | 36 |
37 | 38 | ## How to run 39 | 40 | To run the PKCS12 connect use the following command: 41 | 42 | ```sh 43 | python3 pkcs12_connect --endpoint --pkcs12_file --pkcs12_password 44 | ``` 45 | 46 | You can also pass a Certificate Authority file (CA) if your certificate and key combination requires it: 47 | 48 | ```sh 49 | python3 pkcs12_connect --endpoint --pkcs12_file --pkcs12_password --ca_file 50 | ``` 51 | 52 | ### How to setup and run 53 | 54 | To use the certificate and key files provided by AWS IoT Core, you will need to convert them into PKCS#12 format and then import them into your Java keystore. You can convert the certificate and key file to PKCS12 using the following command: 55 | 56 | ```sh 57 | openssl pkcs12 -export -in -inkey -out -name -password pass: 58 | ``` 59 | 60 | Once converted, you can then run the PKCS12 connect sample with the following: 61 | 62 | ```sh 63 | python3 pkcs12_connect --endpoint --pkcs12_file --pkcs12_password 64 | ``` 65 | -------------------------------------------------------------------------------- /samples/pkcs12_connect.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0. 3 | 4 | from awscrt import http, io 5 | from awsiot import mqtt_connection_builder 6 | from utils.command_line_utils import CommandLineUtils 7 | 8 | # This sample shows how to create a MQTT connection using a certificate file and key file. 9 | # This sample is intended to be used as a reference for making MQTT connections. 10 | 11 | # Callback when connection is accidentally lost. 12 | def on_connection_interrupted(connection, error, **kwargs): 13 | print("Connection interrupted. error: {}".format(error)) 14 | 15 | # Callback when an interrupted connection is re-established. 16 | def on_connection_resumed(connection, return_code, session_present, **kwargs): 17 | print("Connection resumed. return_code: {} session_present: {}".format(return_code, session_present)) 18 | 19 | 20 | if __name__ == '__main__': 21 | 22 | io.init_logging(log_level=io.LogLevel.Trace, file_name="stderr") 23 | 24 | # cmdData is the arguments/input from the command line placed into a single struct for 25 | # use in this sample. This handles all of the command line parsing, validating, etc. 26 | # See the Utils/CommandLineUtils for more information. 27 | cmdData = CommandLineUtils.parse_sample_input_pkcs12_connect() 28 | 29 | # Create the proxy options if the data is present in cmdData 30 | proxy_options = None 31 | if cmdData.input_proxy_host is not None and cmdData.input_proxy_port != 0: 32 | proxy_options = http.HttpProxyOptions( 33 | host_name=cmdData.input_proxy_host, 34 | port=cmdData.input_proxy_port) 35 | 36 | # Create a MQTT connection from the command line data 37 | mqtt_connection = mqtt_connection_builder.mtls_with_pkcs12( 38 | endpoint=cmdData.input_endpoint, 39 | port=cmdData.input_port, 40 | pkcs12_filepath=cmdData.input_pkcs12_file, 41 | pkcs12_password=cmdData.input_pkcs12_password, 42 | on_connection_interrupted=on_connection_interrupted, 43 | on_connection_resumed=on_connection_resumed, 44 | client_id=cmdData.input_clientId, 45 | clean_session=False, 46 | keep_alive_secs=30, 47 | http_proxy_options=proxy_options) 48 | 49 | if not cmdData.input_is_ci: 50 | print(f"Connecting to {cmdData.input_endpoint} with client ID '{cmdData.input_clientId}'...") 51 | else: 52 | print("Connecting to endpoint with client ID") 53 | 54 | connect_future = mqtt_connection.connect() 55 | # Future.result() waits until a result is available 56 | connect_future.result() 57 | print("Connected!") 58 | 59 | # Disconnect 60 | print("Disconnecting...") 61 | disconnect_future = mqtt_connection.disconnect() 62 | disconnect_future.result() 63 | print("Disconnected!") 64 | -------------------------------------------------------------------------------- /samples/pubsub.md: -------------------------------------------------------------------------------- 1 | # PubSub 2 | 3 | [**Return to main sample list**](./README.md) 4 | 5 | This sample uses the 6 | [Message Broker](https://docs.aws.amazon.com/iot/latest/developerguide/iot-message-broker.html) 7 | for AWS IoT to send and receive messages through an MQTT connection. 8 | 9 | On startup, the device connects to the server, subscribes to a topic, and begins publishing messages to that topic. The device should receive those same messages back from the message broker, since it is subscribed to that same topic. Status updates are continually printed to the console. This sample demonstrates how to send and receive messages on designated IoT Core topics, an essential task that is the backbone of many IoT applications that need to send data over the internet. This sample simply subscribes and publishes to a topic, printing the messages it just sent as it is received from AWS IoT Core, but this can be used as a reference point for more complex Pub-Sub applications. 10 | 11 | Your IoT Core Thing's [Policy](https://docs.aws.amazon.com/iot/latest/developerguide/iot-policies.html) must provide privileges for this sample to connect, subscribe, publish, and receive. Below is a sample policy that can be used on your IoT Core Thing that will allow this sample to run as intended. 12 | 13 |
14 | (see sample policy) 15 |
16 | {
17 |   "Version": "2012-10-17",
18 |   "Statement": [
19 |     {
20 |       "Effect": "Allow",
21 |       "Action": [
22 |         "iot:Publish",
23 |         "iot:Receive"
24 |       ],
25 |       "Resource": [
26 |         "arn:aws:iot:region:account:topic/test/topic"
27 |       ]
28 |     },
29 |     {
30 |       "Effect": "Allow",
31 |       "Action": [
32 |         "iot:Subscribe"
33 |       ],
34 |       "Resource": [
35 |         "arn:aws:iot:region:account:topicfilter/test/topic"
36 |       ]
37 |     },
38 |     {
39 |       "Effect": "Allow",
40 |       "Action": [
41 |         "iot:Connect"
42 |       ],
43 |       "Resource": [
44 |         "arn:aws:iot:region:account:client/test-*"
45 |       ]
46 |     }
47 |   ]
48 | }
49 | 
50 | 51 | Replace with the following with the data from your AWS account: 52 | * ``: The AWS IoT Core region where you created your AWS IoT Core thing you wish to use with this sample. For example `us-east-1`. 53 | * ``: Your AWS IoT Core account ID. This is the set of numbers in the top right next to your AWS account name when using the AWS IoT Core website. 54 | 55 | Note that in a real application, you may want to avoid the use of wildcards in your ClientID or use them selectively. Please follow best practices when working with AWS on production applications using the SDK. Also, for the purposes of this sample, please make sure your policy allows a client ID of `test-*` to connect or use `--client_id ` to send the client ID your policy supports. 56 | 57 |
58 | 59 | ## How to run 60 | 61 | To Run this sample from the `samples` folder, use the following command: 62 | 63 | ```sh 64 | # For Windows: replace 'python3' with 'python' and '/' with '\' 65 | python3 pubsub.py --endpoint --cert --key 66 | ``` 67 | 68 | You can also pass a Certificate Authority file (CA) if your certificate and key combination requires it: 69 | 70 | ```sh 71 | # For Windows: replace 'python3' with 'python' and '/' with '\' 72 | python3 pubsub.py --endpoint --cert --key --ca_file 73 | ``` 74 | -------------------------------------------------------------------------------- /samples/shadow.md: -------------------------------------------------------------------------------- 1 | # Shadow 2 | 3 | [**Return to main sample list**](./README.md) 4 | 5 | This sample uses the AWS IoT [Device Shadow](https://docs.aws.amazon.com/iot/latest/developerguide/iot-device-shadows.html) Service to keep a property in sync between device and server. Imagine a light whose color may be changed through an app, or set by a local user. 6 | 7 | Once connected, type a value in the terminal and press Enter to update the property's "reported" value. The sample also responds when the "desired" value changes on the server. To observe this, edit the Shadow document in the AWS Console and set a new "desired" value. 8 | 9 | On startup, the sample requests the shadow document to learn the property's initial state. The sample also subscribes to "delta" events from the server, which are sent when a property's "desired" value differs from its "reported" value. When the sample learns of a new desired value, that value is changed on the device and an update is sent to the server with the new "reported" value. 10 | 11 | Your IoT Core Thing's [Policy](https://docs.aws.amazon.com/iot/latest/developerguide/iot-policies.html) must provide privileges for this sample to connect, subscribe, publish, and receive. Below is a sample policy that can be used on your IoT Core Thing that will allow this sample to run as intended. 12 | 13 |
14 | Sample Policy 15 |
16 | {
17 |   "Version": "2012-10-17",
18 |   "Statement": [
19 |     {
20 |       "Effect": "Allow",
21 |       "Action": [
22 |         "iot:Publish"
23 |       ],
24 |       "Resource": [
25 |         "arn:aws:iot:region:account:topic/$aws/things/thingname/shadow/get",
26 |         "arn:aws:iot:region:account:topic/$aws/things/thingname/shadow/update"
27 |       ]
28 |     },
29 |     {
30 |       "Effect": "Allow",
31 |       "Action": [
32 |         "iot:Receive"
33 |       ],
34 |       "Resource": [
35 |         "arn:aws:iot:region:account:topic/$aws/things/thingname/shadow/get/accepted",
36 |         "arn:aws:iot:region:account:topic/$aws/things/thingname/shadow/get/rejected",
37 |         "arn:aws:iot:region:account:topic/$aws/things/thingname/shadow/update/accepted",
38 |         "arn:aws:iot:region:account:topic/$aws/things/thingname/shadow/update/rejected",
39 |         "arn:aws:iot:region:account:topic/$aws/things/thingname/shadow/update/delta"
40 |       ]
41 |     },
42 |     {
43 |       "Effect": "Allow",
44 |       "Action": [
45 |         "iot:Subscribe"
46 |       ],
47 |       "Resource": [
48 |         "arn:aws:iot:region:account:topicfilter/$aws/things/thingname/shadow/get/accepted",
49 |         "arn:aws:iot:region:account:topicfilter/$aws/things/thingname/shadow/get/rejected",
50 |         "arn:aws:iot:region:account:topicfilter/$aws/things/thingname/shadow/update/accepted",
51 |         "arn:aws:iot:region:account:topicfilter/$aws/things/thingname/shadow/update/rejected",
52 |         "arn:aws:iot:region:account:topicfilter/$aws/things/thingname/shadow/update/delta"
53 |       ]
54 |     },
55 |     {
56 |       "Effect": "Allow",
57 |       "Action": "iot:Connect",
58 |       "Resource": "arn:aws:iot:region:account:client/test-*"
59 |     }
60 |   ]
61 | }
62 | 
63 | 64 | Replace with the following with the data from your AWS account: 65 | * ``: The AWS IoT Core region where you created your AWS IoT Core thing you wish to use with this sample. For example `us-east-1`. 66 | * ``: Your AWS IoT Core account ID. This is the set of numbers in the top right next to your AWS account name when using the AWS IoT Core website. 67 | * ``: The name of your AWS IoT Core thing you want the device connection to be associated with 68 | 69 | Note that in a real application, you may want to avoid the use of wildcards in your ClientID or use them selectively. Please follow best practices when working with AWS on production applications using the SDK. Also, for the purposes of this sample, please make sure your policy allows a client ID of `test-*` to connect or use `--client_id ` to send the client ID your policy supports. 70 | 71 |
72 | 73 | ## How to run 74 | 75 | To run the Shadow sample from the `samples` folder, use the following command: 76 | 77 | ``` sh 78 | # For Windows: replace 'python3' with 'python' and '/' with '\' 79 | python3 shadow.py --endpoint --cert --key --thing_name 80 | ``` 81 | 82 | You can also pass a Certificate Authority file (CA) if your certificate and key combination requires it: 83 | 84 | ``` sh 85 | # For Windows: replace 'python3' with 'python' and '/' with '\' 86 | python3 shadow.py --endpoint --cert --key --thing_name --ca_file 87 | ``` 88 | -------------------------------------------------------------------------------- /samples/websocket_connect.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0. 3 | 4 | from awscrt import http, auth 5 | from awsiot import mqtt_connection_builder 6 | from utils.command_line_utils import CommandLineUtils 7 | 8 | # This sample shows how to create a MQTT connection using websockets. 9 | # This sample is intended to be used as a reference for making MQTT connections. 10 | 11 | 12 | # Callback when connection is accidentally lost. 13 | def on_connection_interrupted(connection, error, **kwargs): 14 | print("Connection interrupted. error: {}".format(error)) 15 | 16 | # Callback when an interrupted connection is re-established. 17 | def on_connection_resumed(connection, return_code, session_present, **kwargs): 18 | print("Connection resumed. return_code: {} session_present: {}".format(return_code, session_present)) 19 | 20 | def connection_setup(): 21 | # cmdData is the arguments/input from the command line placed into a single struct for 22 | # use in this sample. This handles all of the command line parsing, validating, etc. 23 | # See the Utils/CommandLineUtils for more information. 24 | cmdData = CommandLineUtils.parse_sample_input_websocket_connect() 25 | 26 | # Create the proxy options if the data is present in cmdData 27 | proxy_options = None 28 | if cmdData.input_proxy_host is not None and cmdData.input_proxy_port != 0: 29 | proxy_options = http.HttpProxyOptions( 30 | host_name=cmdData.input_proxy_host, 31 | port=cmdData.input_proxy_port) 32 | 33 | # Create a default credentials provider and a MQTT connection from the command line data 34 | credentials_provider = auth.AwsCredentialsProvider.new_default_chain() 35 | mqtt_connection = mqtt_connection_builder.websockets_with_default_aws_signing( 36 | endpoint=cmdData.input_endpoint, 37 | region=cmdData.input_signing_region, 38 | credentials_provider=credentials_provider, 39 | http_proxy_options=proxy_options, 40 | on_connection_interrupted=on_connection_interrupted, 41 | on_connection_resumed=on_connection_resumed, 42 | client_id=cmdData.input_clientId, 43 | clean_session=False, 44 | keep_alive_secs=30) 45 | return mqtt_connection, cmdData 46 | 47 | if __name__ == '__main__': 48 | mqtt_connection, cmdData = connection_setup() 49 | 50 | if not cmdData.input_is_ci: 51 | print(f"Connecting to {cmdData.input_endpoint} with client ID '{cmdData.input_clientId}'...") 52 | else: 53 | print("Connecting to endpoint with client ID...") 54 | 55 | connect_future = mqtt_connection.connect() 56 | 57 | # Future.result() waits until a result is available 58 | connect_future.result() 59 | print("Connected!") 60 | 61 | # Disconnect 62 | print("Disconnecting...") 63 | disconnect_future = mqtt_connection.disconnect() 64 | disconnect_future.result() 65 | print("Disconnected!") 66 | -------------------------------------------------------------------------------- /samples/windows_cert_connect.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0. 3 | 4 | from awsiot import mqtt_connection_builder 5 | from utils.command_line_utils import CommandLineUtils 6 | 7 | # This sample is similar to `samples/basic_connect.py` but the certificate 8 | # for mutual TLS is stored in a Windows certificate store. 9 | # 10 | # See `samples/README.md` for instructions on setting up your PC 11 | # to run this sample. 12 | # 13 | # WARNING: Windows only. 14 | 15 | # cmdData is the arguments/input from the command line placed into a single struct for 16 | # use in this sample. This handles all of the command line parsing, validating, etc. 17 | # See the Utils/CommandLineUtils for more information. 18 | cmdData = CommandLineUtils.parse_sample_input_windows_cert_connect() 19 | 20 | 21 | def on_connection_interrupted(connection, error, **kwargs): 22 | # Callback when connection is accidentally lost. 23 | print("Connection interrupted. error: {}".format(error)) 24 | 25 | 26 | def on_connection_resumed(connection, return_code, session_present, **kwargs): 27 | # Callback when an interrupted connection is re-established. 28 | print("Connection resumed. return_code: {} session_present: {}".format(return_code, session_present)) 29 | 30 | 31 | if __name__ == '__main__': 32 | # Create MQTT connection 33 | mqtt_connection = mqtt_connection_builder.mtls_with_windows_cert_store_path( 34 | cert_store_path=cmdData.input_cert, 35 | endpoint=cmdData.input_endpoint, 36 | port=cmdData.input_port, 37 | ca_filepath=cmdData.input_ca, 38 | on_connection_interrupted=on_connection_interrupted, 39 | on_connection_resumed=on_connection_resumed, 40 | client_id=cmdData.input_clientId, 41 | clean_session=False, 42 | keep_alive_secs=30) 43 | 44 | if not cmdData.input_is_ci: 45 | print(f"Connecting to {cmdData.input_endpoint} with client ID '{cmdData.input_clientId}'...") 46 | else: 47 | print("Connecting to endpoint with client ID") 48 | 49 | connect_future = mqtt_connection.connect() 50 | 51 | # Future.result() waits until a result is available 52 | connect_future.result() 53 | print("Connected!") 54 | 55 | # Disconnect 56 | print("Disconnecting...") 57 | disconnect_future = mqtt_connection.disconnect() 58 | disconnect_future.result() 59 | print("Disconnected!") 60 | -------------------------------------------------------------------------------- /samples/x509_connect.md: -------------------------------------------------------------------------------- 1 | # x509 Credentials Provider Connect 2 | 3 | [**Return to main sample list**](./README.md) 4 | 5 | This sample is similar to the [Basic Connect](./basic_connect.md), but the connection uses a X.509 certificate 6 | to source the AWS credentials when connecting. 7 | 8 | See the [Authorizing direct calls to AWS services using AWS IoT Core credential provider](https://docs.aws.amazon.com/iot/latest/developerguide/authorizing-direct-aws.html) page for instructions on how to setup the IAM roles, the trust policy for the IAM roles, how to setup the IoT Core Role alias, and how to get the credential provider endpoint for your AWS account. 9 | 10 | Your IoT Core Thing's [Policy](https://docs.aws.amazon.com/iot/latest/developerguide/iot-policies.html) must provide privileges for this sample to connect. Below is a sample policy that can be used on your IoT Core Thing that will allow this sample to run as intended. 11 | 12 |
13 | (see sample policy) 14 |
15 | {
16 |   "Version": "2012-10-17",
17 |   "Statement": [
18 |     {
19 |       "Effect": "Allow",
20 |       "Action": [
21 |         "iot:Connect"
22 |       ],
23 |       "Resource": [
24 |         "arn:aws:iot:region:account:client/test-*"
25 |       ]
26 |     },
27 |     {
28 |       "Effect":"Allow",
29 |       "Action":"iot:AssumeRoleWithCertificate",
30 |       "Resource":"arn:aws:iot:region:account:rolealias/role-alias"
31 |     }
32 |   ]
33 | }
34 | 
35 | 36 | Replace with the following with the data from your AWS account: 37 | * ``: The AWS IoT Core region where you created your AWS IoT Core thing you wish to use with this sample. For example `us-east-1`. 38 | * ``: Your AWS IoT Core account ID. This is the set of numbers in the top right next to your AWS account name when using the AWS IoT Core website. 39 | * ``: The X509 role alias you created and wish to connect using. 40 | 41 | Note that in a real application, you may want to avoid the use of wildcards in your ClientID or use them selectively. Please follow best practices when working with AWS on production applications using the SDK. Also, for the purposes of this sample, please make sure your policy allows a client ID of `test-*` to connect or use `--client_id ` to send the client ID your policy supports. 42 | 43 |
44 | 45 | ## How to run 46 | 47 | To run the x509 Credentials Provider Connect sample use the following command: 48 | 49 | ``` sh 50 | # For Windows: replace 'python3' with 'python' and '/' with '\' 51 | python3 x509_connect.py --endpoint --signing_region --x509_cert --x509_endpoint --x509_key --x509_role_alias -x509_thing_name 52 | ``` 53 | 54 | You can also pass a Certificate Authority file (CA) if your X509 certificate and key combination requires it: 55 | 56 | ``` sh 57 | # For Windows: replace 'python3' with 'python' and '/' with '\' 58 | python3 x509_connect.py --endpoint --signing_region --x509_cert --x509_endpoint --x509_key --x509_role_alias -x509_thing_name --x509_ca_file 59 | ``` 60 | -------------------------------------------------------------------------------- /samples/x509_connect.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0. 3 | 4 | from awscrt import io, http, auth 5 | from awsiot import mqtt_connection_builder 6 | from utils.command_line_utils import CommandLineUtils 7 | 8 | # This sample shows how to create a MQTT connection using X509 files to connect. 9 | # This sample is intended to be used as a reference for making MQTT connections via X509. 10 | 11 | # cmdData is the arguments/input from the command line placed into a single struct for 12 | # use in this sample. This handles all of the command line parsing, validating, etc. 13 | # See the Utils/CommandLineUtils for more information. 14 | cmdData = CommandLineUtils.parse_sample_input_x509_connect() 15 | 16 | # Callback when connection is accidentally lost. 17 | def on_connection_interrupted(connection, error, **kwargs): 18 | print(f"Connection interrupted. error: {error}") 19 | 20 | # Callback when an interrupted connection is re-established. 21 | def on_connection_resumed(connection, return_code, session_present, **kwargs): 22 | print(f"Connection resumed. return_code: {return_code} session_present: {session_present}") 23 | 24 | 25 | if __name__ == '__main__': 26 | 27 | ############################################################ 28 | # Set up and create the MQTT connection 29 | ############################################################ 30 | 31 | # Set up the config needed to make a MQTT connection 32 | 33 | proxy_options = None 34 | if cmdData.input_proxy_host is not None and cmdData.input_proxy_port != 0: 35 | proxy_options = http.HttpProxyOptions( 36 | host_name=cmdData.input_proxy_host, 37 | port=cmdData.input_proxy_port) 38 | 39 | x509_tls_options = io.TlsContextOptions.create_client_with_mtls_from_path( 40 | cmdData.input_x509_cert, cmdData.input_x509_key) 41 | x509_tls_options.ca_dirpath = cmdData.input_x509_ca 42 | x509_tls_context = io.ClientTlsContext(x509_tls_options) 43 | 44 | x509_provider = auth.AwsCredentialsProvider.new_x509( 45 | endpoint=cmdData.input_x509_endpoint, 46 | thing_name=cmdData.input_x509_thing_name, 47 | role_alias=cmdData.input_x509_role, 48 | tls_ctx=x509_tls_context, 49 | http_proxy_options=proxy_options 50 | ) 51 | 52 | # Create the MQTT connection from the configuration 53 | mqtt_connection = mqtt_connection_builder.websockets_with_default_aws_signing( 54 | endpoint=cmdData.input_endpoint, 55 | region=cmdData.input_signing_region, 56 | credentials_provider=x509_provider, 57 | http_proxy_options=proxy_options, 58 | on_connection_interrupted=on_connection_interrupted, 59 | on_connection_resumed=on_connection_resumed, 60 | client_id=cmdData.input_clientId, 61 | clean_session=False, 62 | keep_alive_secs=30) 63 | 64 | ############################################################ 65 | # Use the MQTT connection to connect and disconnect 66 | ############################################################ 67 | 68 | if not cmdData.input_is_ci: 69 | print(f"Connecting to {cmdData.input_endpoint} with client ID '{cmdData.input_clientId}'...") 70 | else: 71 | print("Connecting to endpoint with client ID") 72 | 73 | # Connect 74 | connect_future = mqtt_connection.connect() 75 | 76 | # Future.result() waits until a result is available 77 | connect_future.result() 78 | print("Connected!") 79 | 80 | # Disconnect 81 | print("Disconnecting...") 82 | disconnect_future = mqtt_connection.disconnect() 83 | disconnect_future.result() 84 | print("Disconnected!") 85 | -------------------------------------------------------------------------------- /servicetests/test_cases/mqtt3_fleet_provisioning_cfg.json: -------------------------------------------------------------------------------- 1 | { 2 | "language": "Python", 3 | "runnable_file": "./tests/FleetProvisioning/fleet_provisioning.py", 4 | "runnable_region": "us-east-1", 5 | "runnable_main_class": "", 6 | "arguments": [ 7 | { 8 | "name": "--mqtt_version", 9 | "data": "3" 10 | }, 11 | { 12 | "name": "--endpoint", 13 | "secret": "ci/endpoint" 14 | }, 15 | { 16 | "name": "--cert", 17 | "secret": "ci/FleetProvisioning/cert", 18 | "filename": "tmp_certificate.pem" 19 | }, 20 | { 21 | "name": "--key", 22 | "secret": "ci/FleetProvisioning/key", 23 | "filename": "tmp_key.pem" 24 | }, 25 | { 26 | "name": "--template_name", 27 | "data": "CI_FleetProvisioning_Template" 28 | }, 29 | { 30 | "name": "--template_parameters", 31 | "data": "{\"SerialNumber\":\"$INPUT_UUID\"}" 32 | } 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /servicetests/test_cases/mqtt3_fleet_provisioning_with_csr_cfg.json: -------------------------------------------------------------------------------- 1 | { 2 | "language": "Python", 3 | "runnable_file": "./tests/FleetProvisioning/fleet_provisioning.py", 4 | "runnable_region": "us-east-1", 5 | "runnable_main_class": "", 6 | "arguments": [ 7 | { 8 | "name": "--mqtt_version", 9 | "data": "3" 10 | }, 11 | { 12 | "name": "--endpoint", 13 | "secret": "ci/endpoint" 14 | }, 15 | { 16 | "name": "--cert", 17 | "secret": "ci/FleetProvisioning/cert", 18 | "filename": "tmp_certificate.pem" 19 | }, 20 | { 21 | "name": "--key", 22 | "secret": "ci/FleetProvisioning/key", 23 | "filename": "tmp_key.pem" 24 | }, 25 | { 26 | "name": "--csr", 27 | "secret": "ci/FleetProvisioning/csr", 28 | "filename": "tmp_csr.pem" 29 | }, 30 | { 31 | "name": "--template_name", 32 | "data": "CI_FleetProvisioning_Template" 33 | }, 34 | { 35 | "name": "--template_parameters", 36 | "data": "{\"SerialNumber\":\"$INPUT_UUID\"}" 37 | } 38 | ] 39 | } 40 | -------------------------------------------------------------------------------- /servicetests/test_cases/mqtt3_jobs_cfg.json: -------------------------------------------------------------------------------- 1 | { 2 | "language": "Python", 3 | "runnable_file": "./tests/JobsExecution/jobs.py", 4 | "runnable_region": "us-east-1", 5 | "runnable_main_class": "", 6 | "arguments": [ 7 | { 8 | "name": "--mqtt_version", 9 | "data": "3" 10 | }, 11 | { 12 | "name": "--endpoint", 13 | "secret": "ci/endpoint" 14 | }, 15 | { 16 | "name": "--cert", 17 | "data": "tests/JobsExecution/certificate.pem.crt" 18 | }, 19 | { 20 | "name": "--key", 21 | "data": "tests/JobsExecution/private.pem.key" 22 | }, 23 | { 24 | "name": "--thing_name", 25 | "data": "ServiceTest_Jobs_$INPUT_UUID" 26 | } 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /servicetests/test_cases/mqtt3_named_shadow_cfg.json: -------------------------------------------------------------------------------- 1 | { 2 | "language": "Python", 3 | "runnable_file": "./tests/ShadowUpdate/shadow_update.py", 4 | "runnable_region": "us-east-1", 5 | "runnable_main_class": "", 6 | "arguments": [ 7 | { 8 | "name": "--mqtt_version", 9 | "data": "3" 10 | }, 11 | { 12 | "name": "--endpoint", 13 | "secret": "ci/endpoint" 14 | }, 15 | { 16 | "name": "--cert", 17 | "data": "tests/ShadowUpdate/certificate.pem.crt" 18 | }, 19 | { 20 | "name": "--key", 21 | "data": "tests/ShadowUpdate/private.pem.key" 22 | }, 23 | { 24 | "name": "--thing_name", 25 | "data": "ServiceTest_Shadow_$INPUT_UUID" 26 | }, 27 | { 28 | "name": "--shadow_property", 29 | "data": "color" 30 | }, 31 | { 32 | "name": "--shadow_value", 33 | "data": "on" 34 | }, 35 | { 36 | "name": "--shadow_name", 37 | "data": "testShadow" 38 | } 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /servicetests/test_cases/mqtt3_shadow_cfg.json: -------------------------------------------------------------------------------- 1 | { 2 | "language": "Python", 3 | "runnable_file": "./tests/ShadowUpdate/shadow_update.py", 4 | "runnable_region": "us-east-1", 5 | "runnable_main_class": "", 6 | "arguments": [ 7 | { 8 | "name": "--mqtt_version", 9 | "data": "3" 10 | }, 11 | { 12 | "name": "--endpoint", 13 | "secret": "ci/endpoint" 14 | }, 15 | { 16 | "name": "--cert", 17 | "data": "tests/ShadowUpdate/certificate.pem.crt" 18 | }, 19 | { 20 | "name": "--key", 21 | "data": "tests/ShadowUpdate/private.pem.key" 22 | }, 23 | { 24 | "name": "--thing_name", 25 | "data": "ServiceTest_Shadow_$INPUT_UUID" 26 | }, 27 | { 28 | "name": "--shadow_property", 29 | "data": "color" 30 | }, 31 | { 32 | "name": "--shadow_value", 33 | "data": "on" 34 | } 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /servicetests/test_cases/mqtt5_fleet_provisioning_cfg.json: -------------------------------------------------------------------------------- 1 | { 2 | "language": "Python", 3 | "runnable_file": "./tests/FleetProvisioning/fleet_provisioning.py", 4 | "runnable_region": "us-east-1", 5 | "runnable_main_class": "", 6 | "arguments": [ 7 | { 8 | "name": "--mqtt_version", 9 | "data": "5" 10 | }, 11 | { 12 | "name": "--endpoint", 13 | "secret": "ci/endpoint" 14 | }, 15 | { 16 | "name": "--cert", 17 | "secret": "ci/FleetProvisioning/cert", 18 | "filename": "tmp_certificate.pem" 19 | }, 20 | { 21 | "name": "--key", 22 | "secret": "ci/FleetProvisioning/key", 23 | "filename": "tmp_key.pem" 24 | }, 25 | { 26 | "name": "--template_name", 27 | "data": "CI_FleetProvisioning_Template" 28 | }, 29 | { 30 | "name": "--template_parameters", 31 | "data": "{\"SerialNumber\":\"$INPUT_UUID\"}" 32 | } 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /servicetests/test_cases/mqtt5_fleet_provisioning_with_csr_cfg.json: -------------------------------------------------------------------------------- 1 | { 2 | "language": "Python", 3 | "runnable_file": "./tests/FleetProvisioning/fleet_provisioning.py", 4 | "runnable_region": "us-east-1", 5 | "runnable_main_class": "", 6 | "arguments": [ 7 | { 8 | "name": "--mqtt_version", 9 | "data": "5" 10 | }, 11 | { 12 | "name": "--endpoint", 13 | "secret": "ci/endpoint" 14 | }, 15 | { 16 | "name": "--cert", 17 | "secret": "ci/FleetProvisioning/cert", 18 | "filename": "tmp_certificate.pem" 19 | }, 20 | { 21 | "name": "--key", 22 | "secret": "ci/FleetProvisioning/key", 23 | "filename": "tmp_key.pem" 24 | }, 25 | { 26 | "name": "--csr", 27 | "secret": "ci/FleetProvisioning/csr", 28 | "filename": "tmp_csr.pem" 29 | }, 30 | { 31 | "name": "--template_name", 32 | "data": "CI_FleetProvisioning_Template" 33 | }, 34 | { 35 | "name": "--template_parameters", 36 | "data": "{\"SerialNumber\":\"$INPUT_UUID\"}" 37 | } 38 | ] 39 | } 40 | -------------------------------------------------------------------------------- /servicetests/test_cases/mqtt5_jobs_cfg.json: -------------------------------------------------------------------------------- 1 | { 2 | "language": "Python", 3 | "runnable_file": "./tests/JobsExecution/jobs.py", 4 | "runnable_region": "us-east-1", 5 | "runnable_main_class": "", 6 | "arguments": [ 7 | { 8 | "name": "--mqtt_version", 9 | "data": "5" 10 | }, 11 | { 12 | "name": "--endpoint", 13 | "secret": "ci/endpoint" 14 | }, 15 | { 16 | "name": "--cert", 17 | "data": "tests/JobsExecution/certificate.pem.crt" 18 | }, 19 | { 20 | "name": "--key", 21 | "data": "tests/JobsExecution/private.pem.key" 22 | }, 23 | { 24 | "name": "--thing_name", 25 | "data": "ServiceTest_Jobs_$INPUT_UUID" 26 | } 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /servicetests/test_cases/mqtt5_named_shadow_cfg.json: -------------------------------------------------------------------------------- 1 | { 2 | "language": "Python", 3 | "runnable_file": "./tests/ShadowUpdate/shadow_update.py", 4 | "runnable_region": "us-east-1", 5 | "runnable_main_class": "", 6 | "arguments": [ 7 | { 8 | "name": "--mqtt_version", 9 | "data": "5" 10 | }, 11 | { 12 | "name": "--endpoint", 13 | "secret": "ci/endpoint" 14 | }, 15 | { 16 | "name": "--cert", 17 | "data": "tests/ShadowUpdate/certificate.pem.crt" 18 | }, 19 | { 20 | "name": "--key", 21 | "data": "tests/ShadowUpdate/private.pem.key" 22 | }, 23 | { 24 | "name": "--thing_name", 25 | "data": "ServiceTest_Shadow_$INPUT_UUID" 26 | }, 27 | { 28 | "name": "--shadow_property", 29 | "data": "color" 30 | }, 31 | { 32 | "name": "--shadow_value", 33 | "data": "on" 34 | }, 35 | { 36 | "name": "--shadow_name", 37 | "data": "testShadow" 38 | } 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /servicetests/test_cases/mqtt5_shadow_cfg.json: -------------------------------------------------------------------------------- 1 | { 2 | "language": "Python", 3 | "runnable_file": "./tests/ShadowUpdate/shadow_update.py", 4 | "runnable_region": "us-east-1", 5 | "runnable_main_class": "", 6 | "arguments": [ 7 | { 8 | "name": "--mqtt_version", 9 | "data": "5" 10 | }, 11 | { 12 | "name": "--endpoint", 13 | "secret": "ci/endpoint" 14 | }, 15 | { 16 | "name": "--cert", 17 | "data": "tests/ShadowUpdate/certificate.pem.crt" 18 | }, 19 | { 20 | "name": "--key", 21 | "data": "tests/ShadowUpdate/private.pem.key" 22 | }, 23 | { 24 | "name": "--thing_name", 25 | "data": "ServiceTest_Shadow_$INPUT_UUID" 26 | }, 27 | { 28 | "name": "--shadow_property", 29 | "data": "color" 30 | }, 31 | { 32 | "name": "--shadow_value", 33 | "data": "on" 34 | } 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /servicetests/test_cases/test_fleet_provisioning.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0. 3 | 4 | import argparse 5 | import uuid 6 | import os 7 | import sys 8 | import run_in_ci 9 | import ci_iot_thing 10 | 11 | def main(): 12 | argument_parser = argparse.ArgumentParser( 13 | description="Run Fleet Provisioning test in CI") 14 | argument_parser.add_argument( 15 | "--config-file", required=True, 16 | help="JSON file providing command-line arguments to a test") 17 | argument_parser.add_argument( 18 | "--thing-name-prefix", required=False, default="", 19 | help="Prefix for a thing name, should be the same that Fleet Provisioning template uses") 20 | argument_parser.add_argument( 21 | "--region", required=False, default="us-east-1", help="The name of the region to use") 22 | argument_parser.add_argument( 23 | "--input-uuid", required=False, help="UUID for thing name. UUID will be generated if this option is omit") 24 | parsed_commands = argument_parser.parse_args() 25 | 26 | input_uuid = parsed_commands.input_uuid if parsed_commands.input_uuid else str(uuid.uuid4()) 27 | 28 | # Perform fleet provisioning. If it's successful, a newly created thing should appear. 29 | try: 30 | test_result = run_in_ci.setup_and_launch(parsed_commands.config_file, input_uuid) 31 | except Exception as e: 32 | print(f"ERROR: Failed to execute Fleet Provisioning test: {e}") 33 | test_result = -1 34 | 35 | # Delete a thing created by fleet provisioning. If this fails, we assume that's because fleet provisioning failed to 36 | # create a thing. 37 | # NOTE We want to try to delete thing even if test was unsuccessful. 38 | try: 39 | thing_name = parsed_commands.thing_name_prefix + input_uuid 40 | ci_iot_thing.delete_iot_thing(thing_name, parsed_commands.region) 41 | except Exception as e: 42 | print(f"ERROR: Failed to delete thing: {e}") 43 | test_result = -1 44 | 45 | if test_result != 0: 46 | sys.exit(-1) 47 | 48 | if __name__ == "__main__": 49 | main() 50 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [pep8] 2 | max-line-length = 120 3 | aggressive = 2 4 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | # SPDX-License-Identifier: Apache-2.0. 5 | 6 | import codecs 7 | import re 8 | import os 9 | from setuptools import setup, find_packages 10 | 11 | VERSION_RE = re.compile(r""".*__version__ = ["'](.*?)['"]""", re.S) 12 | PROJECT_DIR = os.path.dirname(os.path.realpath(__file__)) 13 | 14 | 15 | def _load_readme(): 16 | readme_path = os.path.join(PROJECT_DIR, 'README.md') 17 | with codecs.open(readme_path, 'r', 'utf-8') as f: 18 | return f.read() 19 | 20 | 21 | def _load_version(): 22 | init_path = os.path.join(PROJECT_DIR, 'awsiot', '__init__.py') 23 | with open(init_path) as fp: 24 | return VERSION_RE.match(fp.read()).group(1) 25 | 26 | 27 | setup( 28 | name='awsiotsdk', 29 | version=_load_version(), 30 | license='License :: OSI Approved :: Apache Software License', 31 | description='AWS IoT SDK based on the AWS Common Runtime', 32 | long_description=_load_readme(), 33 | long_description_content_type='text/markdown', 34 | author='AWS SDK Common Runtime Team', 35 | url='https://github.com/aws/aws-iot-device-sdk-python-v2', 36 | packages=find_packages(include=['awsiot*']), 37 | classifiers=[ 38 | "Programming Language :: Python :: 3", 39 | "License :: OSI Approved :: Apache Software License", 40 | "Operating System :: OS Independent", 41 | ], 42 | install_requires=[ 43 | 'awscrt==0.24.1', 44 | ], 45 | python_requires='>=3.7', 46 | ) 47 | -------------------------------------------------------------------------------- /test/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0. 3 | -------------------------------------------------------------------------------- /test/echotestrpc/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws/aws-iot-device-sdk-python-v2/efb8102a072194fc50f6951a642a2e7e5337c421/test/echotestrpc/__init__.py -------------------------------------------------------------------------------- /test/greengrass/basic_discovery/README.md: -------------------------------------------------------------------------------- 1 | # Test for Greengrass Discovery Sample 2 | 3 | Greengrass discovery test runs using [Greengrass Development Kit Command-Line Interface](https://docs.aws.amazon.com/greengrass/v2/developerguide/greengrass-development-kit-cli.html) (GDK CLI). 4 | 5 | ### Greengrass discovery component 6 | 7 | For Greengrass discovery sample to work, a Greengrass component subscribed to a particular topic is required. 8 | The following files defines this custom component: 9 | 10 | - [gdk-config.json](./gdk-config.json) - `gdk` reads this file to build and publish component. 11 | - [copy_files.sh](./copy_files.sh) - utility to copy all required files for `gdk` to be able to build the component. 12 | - [recipe.yaml](./recipe.yaml) - defines a component's details, dependencies, artifacts, and lifecycles. 13 | - [hello_world_subscriber.py](./hello_world_subscriber.py) - a simple Greengrass client that subscribes to a given topic using Greengrass IPC. 14 | 15 | ### How the test runs 16 | 17 | The first step is to build GreengrassV2 component artifacts and recipes from its source code: 18 | 19 | ```shell 20 | gdk component build 21 | ``` 22 | 23 | Then the following command builds the testing module: 24 | 25 | ```shell 26 | gdk test-e2e build 27 | ``` 28 | 29 | Finally, the test can run: 30 | 31 | ```shell 32 | gdk test-e2e run 33 | ``` 34 | 35 | The test behavior is defined in the [component.feature](./gg-e2e-tests/src/main/resources/greengrass/features/component.feature) 36 | config file using a domain-specific language called [Gherkin](https://docs.aws.amazon.com/greengrass/v2/developerguide/gg-testing-framework.html). 37 | 38 | The test spins up Greengrass core, installs and configures Greengrass component dependencies (including the custom 39 | Greengrass component described in the previous section). After everything is set up, it performs checks. They are defined 40 | at the very bottom of the file and basically grep a log file for specific messages. 41 | 42 | On completion, the test creates log files in `testResult` directory with the run details. The component's logs are stored 43 | in `testResult/gg-/software.amazon.awssdk.sdk-gg-test-discovery.log` file. Though, if error occurred before 44 | the component started its execution, this file might be absent. 45 | -------------------------------------------------------------------------------- /test/greengrass/basic_discovery/copy_files.sh: -------------------------------------------------------------------------------- 1 | cp ../../../samples/basic_discovery.py . 2 | cp ../../../utils/run_in_ci.py . 3 | cp ../../../.github/workflows/ci_run_greengrass_discovery_cfg.json . 4 | -------------------------------------------------------------------------------- /test/greengrass/basic_discovery/gdk-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "component": { 3 | "software.amazon.awssdk.sdk-gg-test-discovery": { 4 | "author": "iot-device-sdk", 5 | "version": "NEXT_PATCH", 6 | "build": { 7 | "build_system": "custom", 8 | "custom_build_command": ["bash", "copy_files.sh"] 9 | }, 10 | "publish": { 11 | "bucket": "iot-sdk-ci-bucket-us-east-1", 12 | "region": "us-east-1" 13 | } 14 | } 15 | }, 16 | "gdk_version": "1.3.0", 17 | "test-e2e": { 18 | "gtf_options": { 19 | "tags": "testgg" 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /test/greengrass/basic_discovery/gg-e2e-tests/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.aws.greengrass 7 | uat-features 8 | jar 9 | 1.0.0 10 | OTF 11 | 12 | 13 | 1.2.0-SNAPSHOT 14 | 1.8 15 | 1.8 16 | 17 | 18 | 19 | 20 | greengrass-common 21 | greengrass common 22 | 23 | https://d2jrmugq4soldf.cloudfront.net/snapshots 24 | 25 | 26 | 27 | 28 | 29 | 30 | com.aws.greengrass 31 | aws-greengrass-testing-standalone 32 | ${otf.version} 33 | compile 34 | 35 | 36 | 37 | 38 | 39 | 40 | org.apache.maven.plugins 41 | maven-shade-plugin 42 | 3.2.2 43 | 44 | 45 | package 46 | 47 | shade 48 | 49 | 50 | 51 | 52 | 53 | 54 | com.aws.greengrass.testing.launcher.TestLauncher 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /test/greengrass/basic_discovery/gg-e2e-tests/src/main/resources/greengrass/features/component.feature: -------------------------------------------------------------------------------- 1 | Feature: Testing features of Greengrassv2 basic discovery sample 2 | 3 | @testgg 4 | Scenario: As a developer, I can create a component and deploy it on my device 5 | Given my device is registered as a Thing 6 | And my device is running Greengrass 7 | When I create a Greengrass deployment with components 8 | | aws.greengrass.clientdevices.Auth | LATEST | 9 | | aws.greengrass.clientdevices.mqtt.Moquette | LATEST | 10 | | aws.greengrass.clientdevices.mqtt.Bridge | LATEST | 11 | | aws.greengrass.clientdevices.IPDetector | LATEST | 12 | | software.amazon.awssdk.sdk-gg-test-discovery | file:recipe.yaml | 13 | When I update my Greengrass deployment configuration, setting the component aws.greengrass.clientdevices.Auth configuration to: 14 | """ 15 | { 16 | "MERGE": { 17 | "deviceGroups": { 18 | "formatVersion": "2021-03-05", 19 | "definitions": { 20 | "MyDeviceGroup": { 21 | "selectionRule": "thingName: CI_Greengrass_Discovery_Thing", 22 | "policyName": "MyRestrictivePolicy" 23 | } 24 | }, 25 | "policies": { 26 | "MyRestrictivePolicy": { 27 | "AllowConnect": { 28 | "statementDescription": "Allow client devices to connect.", 29 | "operations": [ 30 | "mqtt:connect" 31 | ], 32 | "resources": [ 33 | "*" 34 | ] 35 | }, 36 | "AllowPublish": { 37 | "statementDescription": "Allow client devices to publish on topic.", 38 | "operations": [ 39 | "mqtt:publish" 40 | ], 41 | "resources": [ 42 | "*clients/*/hello/world/*" 43 | ] 44 | } 45 | } 46 | } 47 | } 48 | } 49 | } 50 | """ 51 | When I update my Greengrass deployment configuration, setting the component aws.greengrass.clientdevices.mqtt.Bridge configuration to: 52 | """ 53 | { 54 | "MERGE": { 55 | "mqttTopicMapping": { 56 | "HelloWorldCoreMapping": { 57 | "topic": "clients/+/hello/world/+", 58 | "source": "LocalMqtt", 59 | "target": "IotCore" 60 | }, 61 | "HelloWorldPubsubMapping": { 62 | "topic": "clients/+/hello/world/+", 63 | "source": "LocalMqtt", 64 | "target": "Pubsub" 65 | } 66 | } 67 | } 68 | } 69 | """ 70 | And I deploy the Greengrass deployment configuration 71 | Then the Greengrass deployment is COMPLETED on the device after 300 seconds 72 | And the software.amazon.awssdk.sdk-gg-test-discovery log on the device contains the line "Successfully subscribed to topic" within 180 seconds 73 | And the software.amazon.awssdk.sdk-gg-test-discovery log on the device contains the line "Received new message" within 240 seconds 74 | And the software.amazon.awssdk.sdk-gg-test-discovery log on the device contains the line "disassociated CI_Greengrass_Discovery_Thing" within 260 seconds 75 | -------------------------------------------------------------------------------- /test/greengrass/basic_discovery/hello_world_subscriber.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0. 3 | 4 | import argparse 5 | import sys 6 | import time 7 | import traceback 8 | import uuid 9 | 10 | from awsiot.greengrasscoreipc.clientv2 import GreengrassCoreIPCClientV2 11 | 12 | 13 | def on_message(event): 14 | try: 15 | print('Topic: {}'.format(event.binary_message.context.topic)) 16 | message = str(event.binary_message.message, 'utf-8') 17 | print('Received new message: {}'.format(message)) 18 | except: 19 | traceback.print_exc() 20 | 21 | 22 | def main(): 23 | argument_parser = argparse.ArgumentParser( 24 | description="Run Greengrass subscriber component") 25 | argument_parser.add_argument( 26 | "--input_uuid", required=False, help="UUID for unique topic name. UUID will be generated if this option is omit") 27 | parsed_commands = argument_parser.parse_args() 28 | 29 | input_uuid = parsed_commands.input_uuid if parsed_commands.input_uuid else str(uuid.uuid4()) 30 | 31 | try: 32 | ipc_client = GreengrassCoreIPCClientV2() 33 | 34 | client_device_hello_world_topic = 'clients/+/hello/world/{}'.format(input_uuid) 35 | 36 | # SubscribeToTopic returns a tuple with the response and the operation. 37 | _, operation = ipc_client.subscribe_to_topic( 38 | topic=client_device_hello_world_topic, on_stream_event=on_message) 39 | print('Successfully subscribed to topic: {}'.format(client_device_hello_world_topic)) 40 | 41 | # Keep the main thread alive, or the process will exit. 42 | try: 43 | while True: 44 | time.sleep(10) 45 | except InterruptedError: 46 | print('Subscribe interrupted.') 47 | 48 | operation.close() 49 | except Exception: 50 | print('Exception occurred when using IPC.', file=sys.stderr) 51 | traceback.print_exc() 52 | exit(1) 53 | 54 | print("Subscriber done") 55 | 56 | 57 | if __name__ == "__main__": 58 | main() 59 | -------------------------------------------------------------------------------- /test/greengrass/basic_discovery/recipe.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | RecipeFormatVersion: "2020-01-25" 3 | ComponentName: software.amazon.awssdk.sdk-gg-test-discovery 4 | ComponentVersion: "1.0.0" 5 | ComponentDescription: "This is test for the Greengrass basic discovery sample" 6 | ComponentPublisher: "iot-device-sdk" 7 | ComponentConfiguration: 8 | DefaultConfiguration: 9 | accessControl: 10 | aws.greengrass.ipc.pubsub: 11 | software.amazon.awssdk.sdk-gg-test-discovery:pubsub:1: 12 | policyDescription: "Allows access to subscribe to a Greengrass IPC test topic" 13 | operations: 14 | - aws.greengrass#SubscribeToTopic 15 | - aws.greengrass#PublishToTopic 16 | resources: 17 | - "clients/*/hello/world/*" 18 | Manifests: 19 | - Platform: 20 | os: all 21 | Artifacts: 22 | - URI: "file:hello_world_subscriber.py" 23 | - URI: "file:run_in_ci.py" 24 | - URI: "file:ci_run_greengrass_discovery_cfg.json" 25 | - URI: "file:basic_discovery.py" 26 | Lifecycle: 27 | Install: | 28 | echo "GG core:" {iot:thingName} 29 | aws greengrassv2 batch-associate-client-device-with-core-device --core-device-thing-name {iot:thingName} --entries thingName=CI_Greengrass_Discovery_Thing 30 | aws greengrassv2 list-client-devices-associated-with-core-device --core-device-thing-name {iot:thingName} 31 | Run: | 32 | UUID=$(python3 -c "import uuid; print (uuid.uuid4())") 33 | echo "Starting subscriber" 34 | python3 -u {artifacts:path}/hello_world_subscriber.py --input_uuid ${UUID} & 35 | sleep 10 36 | echo "Starting discovery" 37 | python3 {artifacts:path}/run_in_ci.py --runnable_dir {artifacts:path} --input_uuid ${UUID} --file {artifacts:path}/ci_run_greengrass_discovery_cfg.json 38 | aws greengrassv2 batch-disassociate-client-device-from-core-device --core-device-thing-name {iot:thingName} --entries thingName=CI_Greengrass_Discovery_Thing 39 | echo "Run: disassociated CI_Greengrass_Discovery_Thing" 40 | Shutdown: | 41 | echo "Shutdown: disassociating CI_Greengrass_Discovery_Thing" 42 | aws greengrassv2 batch-disassociate-client-device-from-core-device --core-device-thing-name {iot:thingName} --entries thingName=CI_Greengrass_Discovery_Thing 43 | Recover: | 44 | echo "Recover: disassociating CI_Greengrass_Discovery_Thing" 45 | aws greengrassv2 batch-disassociate-client-device-from-core-device --core-device-thing-name {iot:thingName} --entries thingName=CI_Greengrass_Discovery_Thing 46 | -------------------------------------------------------------------------------- /test/greengrass/ipc/README.md: -------------------------------------------------------------------------------- 1 | # Test for Greengrass IPC Sample 2 | 3 | Greengrass IPC test runs using [Greengrass Development Kit Command-Line Interface](https://docs.aws.amazon.com/greengrass/v2/developerguide/greengrass-development-kit-cli.html) (GDK CLI). 4 | 5 | ### Greengrass IPC component 6 | 7 | For Greengrass IPC sample to work, it should be deployed as a Greengrass component. 8 | The following files defines this component: 9 | 10 | - [gdk-config.json](./gdk-config.json) - `gdk` reads this file to build and publish component. 11 | - [copy_files.sh](./copy_files.sh) - utility to copy all required files for `gdk` to be able to build the component. 12 | - [recipe.yaml](./recipe.yaml) - defines a component's details, dependencies, artifacts, and lifecycles. 13 | 14 | ### How the test runs 15 | 16 | The first step is to build GreengrassV2 component artifacts and recipes from its source code: 17 | 18 | ```shell 19 | gdk component build 20 | ``` 21 | 22 | Then the following command builds the testing module: 23 | 24 | ```shell 25 | gdk test-e2e build 26 | ``` 27 | 28 | Finally, the test can run: 29 | 30 | ```shell 31 | gdk test-e2e run 32 | ``` 33 | 34 | The test behavior is defined in the [component.feature](./gg-e2e-tests/src/main/resources/greengrass/features/component.feature) 35 | config file using a domain-specific language called [Gherkin](https://docs.aws.amazon.com/greengrass/v2/developerguide/gg-testing-framework.html). 36 | 37 | The test spins up Greengrass core, installs and configures Greengrass component dependencies (including the custom 38 | Greengrass component described in the previous section). After everything is set up, it performs checks. They are defined 39 | at the very bottom of the file and basically grep a log file for specific messages. 40 | 41 | On completion, the test creates log files in `testResult` directory with the run details. The component's logs are stored 42 | in `testResult/gg-/software.amazon.awssdk.sdk-gg-ipc.log` file. Though, if error occurred before the 43 | component started its execution, this file might be absent. 44 | -------------------------------------------------------------------------------- /test/greengrass/ipc/copy_files.sh: -------------------------------------------------------------------------------- 1 | cp ../../../samples/ipc_greengrass.py . 2 | cp ../../../utils/run_in_ci.py . 3 | cp ../../../.github/workflows/ci_run_greengrass_ipc_cfg.json . 4 | -------------------------------------------------------------------------------- /test/greengrass/ipc/gdk-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "component": { 3 | "software.amazon.awssdk.sdk-gg-ipc": { 4 | "author": "iot-device-sdk", 5 | "version": "NEXT_PATCH", 6 | "build": { 7 | "build_system": "custom", 8 | "custom_build_command": ["bash", "copy_files.sh"] 9 | }, 10 | "publish": { 11 | "bucket": "", 12 | "region": "" 13 | } 14 | } 15 | }, 16 | "gdk_version": "1.3.0", 17 | "test-e2e": { 18 | "gtf_options": { 19 | "tags": "testgg" 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /test/greengrass/ipc/gg-e2e-tests/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.aws.greengrass 7 | uat-features 8 | jar 9 | 1.0.0 10 | OTF 11 | 12 | 13 | 1.2.0-SNAPSHOT 14 | 1.8 15 | 1.8 16 | 17 | 18 | 19 | 20 | greengrass-common 21 | greengrass common 22 | 23 | https://d2jrmugq4soldf.cloudfront.net/snapshots 24 | 25 | 26 | 27 | 28 | 29 | 30 | com.aws.greengrass 31 | aws-greengrass-testing-standalone 32 | ${otf.version} 33 | compile 34 | 35 | 36 | 37 | 38 | 39 | 40 | org.apache.maven.plugins 41 | maven-shade-plugin 42 | 3.2.2 43 | 44 | 45 | package 46 | 47 | shade 48 | 49 | 50 | 51 | 52 | 53 | 54 | com.aws.greengrass.testing.launcher.TestLauncher 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /test/greengrass/ipc/gg-e2e-tests/src/main/resources/greengrass/features/component.feature: -------------------------------------------------------------------------------- 1 | Feature: Testing features of Greengrassv2 IPC sample 2 | 3 | @testgg 4 | Scenario: As a developer, I can create a component and deploy it on my device 5 | Given my device is registered as a Thing 6 | And my device is running Greengrass 7 | When I create a Greengrass deployment with components 8 | | software.amazon.awssdk.sdk-gg-ipc | file:recipe.yaml | 9 | And I deploy the Greengrass deployment configuration 10 | Then the Greengrass deployment is COMPLETED on the device after 180 seconds 11 | And the software.amazon.awssdk.sdk-gg-ipc log on the device contains the line "Successfully published message" within 20 seconds 12 | -------------------------------------------------------------------------------- /test/greengrass/ipc/recipe.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | RecipeFormatVersion: "2020-01-25" 3 | ComponentName: software.amazon.awssdk.sdk-gg-ipc 4 | ComponentVersion: "1.0.0" 5 | ComponentDescription: "This is test for the Greengrass IPC sample" 6 | ComponentPublisher: "iot-device-sdk" 7 | ComponentConfiguration: 8 | DefaultConfiguration: 9 | accessControl: 10 | aws.greengrass.ipc.mqttproxy: 11 | software.amazon.awssdk.sdk-gg-ipc:mqttproxy:1: 12 | policyDescription: "Allows access to publish and subscribe to a Greengrass IPC test topic" 13 | operations: 14 | - aws.greengrass#PublishToIoTCore 15 | - aws.greengrass#SubscribeToIoTCore 16 | resources: 17 | - "my/iot/*/telemetry" 18 | Manifests: 19 | - Platform: 20 | os: all 21 | Artifacts: 22 | - URI: "file:ipc_greengrass.py" 23 | - URI: "file:run_in_ci.py" 24 | - URI: "file:ci_run_greengrass_ipc_cfg.json" 25 | Lifecycle: 26 | Run: | 27 | echo "GG core:" {iot:thingName} 28 | python3 {artifacts:path}/run_in_ci.py --runnable_dir {artifacts:path} --file {artifacts:path}/ci_run_greengrass_ipc_cfg.json 29 | -------------------------------------------------------------------------------- /test/test_shadow_state.py: -------------------------------------------------------------------------------- 1 | from pydoc import describe 2 | from unittest import TestCase 3 | 4 | from awsiot.iotshadow import ShadowState 5 | 6 | 7 | class ShadowTest(TestCase): 8 | 9 | def test_shadow_state_payload_filled(self): 10 | test_state = ShadowState.from_payload({ 11 | "reported": {"Color": "Red"}, 12 | "desired": {"Color": "Blue"} 13 | }) 14 | compareState = ShadowState( 15 | reported={"Color": "Red"}, 16 | desired={"Color": "Blue"} 17 | ) 18 | self.assertTrue(test_state.to_payload() == compareState.to_payload()) 19 | 20 | def test_shadow_state_payload_partial_filled(self): 21 | test_state = ShadowState.from_payload({ 22 | "reported": {"Color": "Red"} 23 | }) 24 | self.assertEqual(test_state.to_payload(), {"reported": {"Color": "Red"}}) 25 | 26 | def test_shadow_state_payload_can_send_null(self): 27 | test_state = ShadowState( 28 | reported={"Color": "Red"}, 29 | desired=None, 30 | desired_is_nullable=True 31 | ) 32 | self.assertEqual(test_state.to_payload(), {"reported": {"Color": "Red"}, "desired": None}) 33 | 34 | def test_shadow_state_payload_with_none(self): 35 | test_state = ShadowState.from_payload({"reported": {"Color": "Red"}, "desired": None}) 36 | self.assertTrue(test_state.desired_is_nullable) 37 | self.assertEqual(test_state.to_payload(), {"reported": {"Color": "Red"}, "desired": None}) 38 | 39 | def test_shadow_state_payload_without_none(self): 40 | test_state = ShadowState.from_payload({"reported": {"deviceAgent": {"rebootRequired": True}}}) 41 | self.assertEqual(test_state.to_payload(), {"reported": {"deviceAgent": {"rebootRequired": True}}}) 42 | -------------------------------------------------------------------------------- /utils/check_codegen_edits.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import argparse 3 | import fnmatch 4 | from genericpath import isfile 5 | import os 6 | import subprocess 7 | import sys 8 | 9 | CODEGEN_PHRASES = [ 10 | 'This file is generated', 11 | ] 12 | 13 | IGNORE_PATTERNS = [ 14 | 'utils/*', # this script has the phrase in it 15 | ] 16 | 17 | ERROR_MSG = """ 18 | ERROR: You have changed code-generated files. 19 | 20 | If you edited these files by hand, your changes will be erased when the 21 | code-generator is run again. An SDK team member MUST update the code-gen 22 | templates with corresponding changes before merging this Pull Request. 23 | 24 | You can ignore this error if you are in fact running the code generator. 25 | """ 26 | 27 | 28 | def main(): 29 | parser = argparse.ArgumentParser( 30 | description="Detect edits to code-generated files") 31 | parser.add_argument('--diff-branch', default='main', 32 | help="Branch/commit to diff against") 33 | parser.add_argument('--diff-repo', default='origin', 34 | help="Repository to diff against") 35 | args = parser.parse_args() 36 | 37 | # chdir to project root 38 | os.chdir(os.path.join(os.path.dirname(__file__), '..')) 39 | 40 | # get all files with diffs 41 | git_cmd = ['git', 'diff', '--name-only', 42 | f"{args.diff_repo}/{args.diff_branch}"] 43 | git_result = subprocess.run(git_cmd, check=True, stdout=subprocess.PIPE) 44 | diff_files = git_result.stdout.decode().splitlines() 45 | 46 | # figure out which files were code-generated 47 | print('Checking files with diffs...') 48 | any_codegen = False 49 | for filepath in diff_files: 50 | is_codegen = False 51 | ignore = False 52 | if not os.path.isfile(filepath): 53 | ignore = True 54 | if any([fnmatch.fnmatch(filepath, pat) 55 | for pat in IGNORE_PATTERNS]): 56 | ignore = True 57 | if not ignore: 58 | with open(filepath) as f: 59 | text = f.read() 60 | for phrase in CODEGEN_PHRASES: 61 | if phrase in text: 62 | is_codegen = True 63 | any_codegen = True 64 | break 65 | if is_codegen: 66 | print(f" ⛔️ GENERATED - {filepath}") 67 | elif ignore: 68 | print(f" ✅ ignored - {filepath}") 69 | else: 70 | print(f" ✅ normal - {filepath}") 71 | 72 | if any_codegen: 73 | print(ERROR_MSG) 74 | sys.exit(-1) 75 | else: 76 | print("No code-generated files were changed.") 77 | 78 | 79 | if __name__ == '__main__': 80 | main() 81 | -------------------------------------------------------------------------------- /utils/delete_iot_thing_ci.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0. 3 | 4 | # Built-in 5 | import argparse 6 | import sys 7 | # Needs to be installed via pip 8 | import boto3 # - for launching sample 9 | 10 | def DeleteIoTThing(parsed_commands): 11 | try: 12 | iot_client = boto3.client('iot', region_name=parsed_commands.region) 13 | except Exception: 14 | print("Error - could not make Boto3 client. Credentials likely could not be sourced") 15 | return -1 16 | 17 | thing_principals = None 18 | try: 19 | thing_principals = iot_client.list_thing_principals(thingName=parsed_commands.thing_name) 20 | except Exception: 21 | print ("Could not get thing principals!") 22 | return -1 23 | 24 | try: 25 | if (thing_principals != None): 26 | if (thing_principals["principals"] != None): 27 | if (len(thing_principals["principals"]) > 0 and parsed_commands.delete_certificate == "true"): 28 | for principal in thing_principals["principals"]: 29 | certificate_id = principal.split("/")[1] 30 | iot_client.detach_thing_principal(thingName=parsed_commands.thing_name, principal=principal) 31 | iot_client.update_certificate(certificateId=certificate_id, newStatus ='INACTIVE') 32 | iot_client.delete_certificate(certificateId=certificate_id, forceDelete=True) 33 | except Exception as exception: 34 | print (exception) 35 | print ("Could not delete certificate!") 36 | return -1 37 | 38 | try: 39 | iot_client.delete_thing(thingName=parsed_commands.thing_name) 40 | except Exception as exception: 41 | print (exception) 42 | print ("Could not delete IoT thing!") 43 | return -1 44 | 45 | print ("IoT thing deleted successfully") 46 | return 0 47 | 48 | 49 | 50 | def main(): 51 | argument_parser = argparse.ArgumentParser( 52 | description="Delete IoT Thing") 53 | argument_parser.add_argument("--thing_name", metavar="", required=True, 54 | help="The name of the IoT thing to delete") 55 | argument_parser.add_argument("--region", metavar="", 56 | required=True, default="us-east-1", help="The name of the region to use") 57 | argument_parser.add_argument("--delete_certificate", metavar="", 58 | required=False, default="true", help="Will delete the certificate after detaching it from the IoT thing") 59 | parsed_commands = argument_parser.parse_args() 60 | 61 | print ("Deleting IoT thing...") 62 | delete_result = DeleteIoTThing(parsed_commands) 63 | sys.exit(delete_result) 64 | 65 | 66 | if __name__ == "__main__": 67 | main() 68 | -------------------------------------------------------------------------------- /utils/parse_cert_set_result.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0. 3 | 4 | """ 5 | 6 | A simply utility script to parse out certificates and keys from some IoT operations. 7 | 8 | For example, you may be doing fleet provisioning and want to have a simple way of setting up the results from create-provisioning-claim 9 | into usable pem files that you can make IoT connections with. 10 | 11 | Example usage: 12 | 13 | aws iot create-provisioning-claim --template-name | python3 parse_cert_set_result.py --path --filename 14 | 15 | """ 16 | 17 | import argparse 18 | import json 19 | import os 20 | import re 21 | import sys 22 | 23 | parser = argparse.ArgumentParser(description="Utility script to generate valid .cert.pem, .private.key, .public.key files from the JSON response of CreateProvisioningClaim, CreateCertificateFromCsr") 24 | parser.add_argument('--path', required=True, help="Path to extract the certificate set files to. Created if does not exist") 25 | parser.add_argument('--filename', required=True, help="Filename (prefix) to use for the generated files") 26 | 27 | if __name__ == '__main__': 28 | # Process input args 29 | args = parser.parse_args() 30 | 31 | path = args.path 32 | filename = args.filename 33 | 34 | if not os.path.exists(path): 35 | os.makedirs(path) 36 | 37 | body = json.load(sys.stdin) 38 | 39 | raw_pem = body['certificatePem'] 40 | if raw_pem: 41 | pem = re.sub("\\n", "\n", raw_pem) 42 | pem_filename = os.path.join(path, filename + ".cert.pem") 43 | with open(pem_filename, 'w') as file: 44 | file.write(pem) 45 | 46 | try: 47 | raw_pub_key = body['keyPair']['PublicKey'] 48 | if raw_pub_key: 49 | pub_key = re.sub("\\n", "\n", raw_pub_key) 50 | pub_key_filename = os.path.join(path, filename + ".public.key") 51 | with open(pub_key_filename, 'w') as file: 52 | file.write(pub_key) 53 | 54 | raw_private_key = body['keyPair']['PrivateKey'] 55 | if raw_private_key: 56 | private_key = re.sub("\\n", "\n", raw_private_key) 57 | private_key_filename = os.path.join(path, filename + ".private.key") 58 | with open(private_key_filename, 'w') as file: 59 | file.write(private_key) 60 | except KeyError: 61 | pass 62 | 63 | print("Success!") 64 | 65 | 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /utils/publish-release.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euxo pipefail 4 | 5 | # Redirect output to stderr. 6 | exec 1>&2 7 | 8 | RELEASE_TYPE="$1" 9 | RELEASE_TITLE="$2" 10 | 11 | # Make sure there are ONLY two arguments 12 | if [ "$#" != "2" ]; then 13 | echo "ERROR: Arguments passed is NOT equal to two!" 14 | exit 1 15 | fi 16 | 17 | pushd $(dirname $0) > /dev/null 18 | 19 | # Get the current version 20 | git checkout main 21 | 22 | git_tags=$(git tag) 23 | current_version=$(python3 ./update_semantic_version.py --version "${git_tags}" --type MINOR --parse_latest_version true) 24 | current_version_without_v=$(echo ${current_version} | cut -f2 -dv) 25 | 26 | echo "Current release version is ${current_version_without_v}" 27 | 28 | # Validate that RELEASE_TYPE is what we expect and bump the version 29 | new_version=$(python3 ./update_semantic_version.py --version "${current_version_without_v}" --type "${RELEASE_TYPE}") 30 | if [ "$new_version" == "0.0.0" ]; then 31 | echo "ERROR: Unknown release type! Exitting..." 32 | exit -1 33 | fi 34 | echo "New version is ${new_version}" 35 | 36 | # Validate that the title is set 37 | if [ "$RELEASE_TITLE" == "" ]; then 38 | echo "ERROR: No title set! Cannot make release. Exitting..." 39 | exit -1 40 | fi 41 | 42 | # Setup Github credentials 43 | git config --local user.email "aws-sdk-common-runtime@amazon.com" 44 | git config --local user.name "GitHub Actions" 45 | 46 | # --==-- 47 | new_version_branch=AutoTag-v${new_version} 48 | git checkout -b ${new_version_branch} 49 | 50 | # Update the version in the README to show the latest 51 | sed -i -r "s/.*Latest released version:.*/Latest released version: v${new_version}/" ../README.md 52 | git add ../README.md 53 | # Make the commit 54 | git commit -m "[v$new_version] $RELEASE_TITLE" 55 | 56 | # # push the commit and create a PR 57 | git push -u "https://${GITHUB_ACTOR}:${GITHUB_TOKEN}@github.com/aws/aws-iot-device-sdk-python-v2.git" ${new_version_branch} 58 | gh pr create --title "AutoTag PR for v${new_version}" --body "AutoTag PR for v${new_version}" --head ${new_version_branch} 59 | 60 | # # Merge the PR 61 | gh pr merge --admin --squash 62 | # --==-- 63 | 64 | # Update local state with the merged pr (if one was made) and just generally make sure we're up to date 65 | git fetch 66 | git checkout main 67 | git pull "https://${GITHUB_ACTOR}:${GITHUB_TOKEN}@github.com/aws/aws-iot-device-sdk-python-v2.git" main 68 | 69 | # Create new tag on latest commit (lightweight tag - we do NOT want an annotated tag) 70 | git tag -f v${new_version} 71 | # Push new tag to github 72 | git push "https://${GITHUB_ACTOR}:${GITHUB_TOKEN}@github.com/aws/aws-iot-device-sdk-python-v2.git" --tags 73 | 74 | # Determine if this is a pre-release or not based on the major version 75 | IS_PRE_RELEASE="false" 76 | VERSION_STRING_DELIMITER=. 77 | VERSION_STRING_ARRAY=($(echo "$new_version" | tr $VERSION_STRING_DELIMITER '\n')) 78 | if [ "${VERSION_STRING_ARRAY[0]}" == "0" ]; then 79 | IS_PRE_RELEASE="true" 80 | else 81 | IS_PRE_RELEASE="false" 82 | fi 83 | 84 | # Create the release with auto-generated notes as the description 85 | # - NOTE: This will only add notes if there is at least one PR. If there is no PRs, 86 | # - then this will be blank and need manual input/changing after running. 87 | if [ $IS_PRE_RELEASE == "true" ]; then 88 | gh release create "v${new_version}" -p --generate-notes --notes-start-tag "$current_version" --target main -t "${RELEASE_TITLE}" 89 | else 90 | gh release create "v${new_version}" --generate-notes --notes-start-tag "$current_version" --target main -t "${RELEASE_TITLE}" 91 | fi 92 | 93 | popd > /dev/null 94 | -------------------------------------------------------------------------------- /utils/update_semantic_version.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import sys 3 | 4 | # A simple little helper script for increasing the semantic version passed in. 5 | # Used in the CD workflow. 6 | 7 | 8 | def main(): 9 | argument_parser = argparse.ArgumentParser( 10 | description="Get a new semantic version bumped by the passed-in string") 11 | argument_parser.add_argument("--version", metavar="<1.2.3 for example>", 12 | required=True, help="The version string to update") 13 | argument_parser.add_argument("--type", metavar="", 14 | required=True, help="Which version number to bump") 15 | argument_parser.add_argument("--parse_latest_version", metavar="", 16 | help="Takes '$(git tag)' and returns the highest version in the list", default="false") 17 | parsed_commands = argument_parser.parse_args() 18 | 19 | if (parsed_commands.parse_latest_version == "true"): 20 | version_list = parsed_commands.version.split("\n") 21 | highest = [0, 0, 0] 22 | 23 | for i in range(0, len(version_list)): 24 | i_version = version_list[i] 25 | i_version = i_version.replace("v", "") 26 | 27 | i_version_tuple = i_version.split(".") 28 | if (len(i_version_tuple) != 3): 29 | continue 30 | 31 | i_version_tuple[0] = int(i_version_tuple[0]) 32 | i_version_tuple[1] = int(i_version_tuple[1]) 33 | i_version_tuple[2] = int(i_version_tuple[2]) 34 | 35 | if (highest == None): 36 | highest = i_version_tuple 37 | continue 38 | else: 39 | if (i_version_tuple[0] > highest[0]): 40 | highest = i_version_tuple 41 | continue 42 | if (i_version_tuple[0] >= highest[0] and i_version_tuple[1] > highest[1]): 43 | highest = i_version_tuple 44 | continue 45 | if (i_version_tuple[0] >= highest[0] and i_version_tuple[1] >= highest[1] and i_version_tuple[2] >= highest[2]): 46 | highest = i_version_tuple 47 | continue 48 | 49 | if (highest[0] != 0 or highest[1] != 0 or highest[2] != 0): 50 | print(f"v{highest[0]}.{highest[1]}.{highest[2]}") 51 | sys.exit(0) 52 | else: 53 | sys.exit(-1) 54 | 55 | version_tuple = parsed_commands.version.split(".") 56 | if len(version_tuple) != 3: 57 | print("0.0.0") # Error 58 | sys.exit(1) 59 | 60 | if "PATCH" in parsed_commands.type: 61 | version_tuple[2] = str(int(version_tuple[2]) + 1) 62 | elif "MINOR" in parsed_commands.type: 63 | version_tuple[1] = str(int(version_tuple[1]) + 1) 64 | version_tuple[2] = "0" 65 | elif "MAJOR" in parsed_commands.type: 66 | version_tuple[0] = str(int(version_tuple[0]) + 1) 67 | version_tuple[1] = "0" 68 | version_tuple[2] = "0" 69 | else: 70 | print("0.0.0") # error 71 | sys.exit(1) 72 | 73 | print(f"{version_tuple[0]}.{version_tuple[1]}.{version_tuple[2]}") 74 | sys.exit(0) 75 | 76 | 77 | if __name__ == "__main__": 78 | main() 79 | --------------------------------------------------------------------------------