Skip to content

Commit

Permalink
add Greengrass V2 IPC samples using new IPC-Client V2
Browse files Browse the repository at this point in the history
  • Loading branch information
Kriechi committed Jul 17, 2023
1 parent f1a4369 commit f73345d
Show file tree
Hide file tree
Showing 12 changed files with 593 additions and 54 deletions.
82 changes: 82 additions & 0 deletions samples/greengrass_v2/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Greengrass V2 samples using AWS IoT Device SDK v2 for Python

You can find the full API documentation for the Greengrass V2 IPC interface using the Python SDK here: https://aws.github.io/aws-iot-device-sdk-python-v2/awsiot/greengrasscoreipc.html

## Sample: Low-Level IPC

Folder: `low_level_ipc/`

Once installed and running, this sample publishes messages to AWS IoT Core. It uses the low-level Greengrass v2 [Inter-Process-Communication API](https://docs.aws.amazon.com/greengrass/v2/developerguide/interprocess-communication.html).

See the other samples for higher-level APIs to reduce the amount of code you have to write and maintain.

## Sample: Publish/Subscribe to the cloud with AWS IoT Core

Folder: `pubsub_cloud`

Once installed and running, this sample subscribes to the `hello/world` topic. You can use the [MQTT Test Client](https://console.aws.amazon.com/iot/home#/test) to publish a message to this topic, while also subscribing to `hello/world/response` in the MQTT Test Client. The Greengrass device will receive the message and reply back on the response topic.

## Sample: Public/Subscribe on the local device between Greengrass components

Folder: `pubsub_local`

Once installed and running, this sample subscribes to the `hello/world` topic. You can use a second component to publish a message and receive a reply message on the `hello/world/response` topic. These messages are **not** sent to AWS IoT Core (the cloud). This pub/sub mechanism is only connecting different components running on the same Greengrass device. You can use the Greegrass CLI to publish or subscribe to these local topics:

* `greengrass-cli pubsub sub --topic hello/world/response`
* `greengrass-cli pubsub pub --topic hello/world --message Hi!`

## Sample: Shadow Management

Folder: `shadows`

Once installed and running, this sample will retrieve a named shadow `special_shadow` when the component first starts executing, and then periodically update the shadow document with a new reported state every few seconds.

This component depends on the [AWS-provided ShadowManager component](https://docs.aws.amazon.com/greengrass/v2/developerguide/shadow-manager-component.html#shadow-manager-component-configuration). You [need to configure](https://docs.aws.amazon.com/greengrass/v2/developerguide/shadow-manager-component.html#shadow-manager-component-configuration) it to synchronize named shadows from the local device to the cloud:

```yaml
strategy:
type: realTime
synchronize:
coreThing:
namedShadows:
- special_shadow
direction: betweenDeviceAndCloud
```
## Sample: Deployment Configuration
Folder: `deployment_configuration`

Once installed and running, this sample will retrieve the component's deployment configuration and start a web server based on the provided parameters. Re-deploying with different parameters will update the component and upon restart of the Python process, it will start the web server based on these new parameters.

## Deployment Helpers

Deploy component locally using Greengrass CLI:

```bash
func gg_deploy() {
COMPONENT_NAME=$(sed -nr 's/ComponentName: ([a-zA-Z.-_]+)/\1/p' recipe.yaml)
COMPONENT_VERSION=$(sed -nr 's/ComponentVersion: (.+)/\1/p' recipe.yaml | tr -d '"' | tr -d "'")
mkdir -p build/artifacts/$COMPONENT_NAME/$COMPONENT_VERSION/
command cp code.py build/artifacts/$COMPONENT_NAME/$COMPONENT_VERSION/
mkdir -p build/recipes/
command cp recipe.yaml build/recipes/$COMPONENT_NAME.yaml
RECIPES=$PWD/build/recipes
ARTIFACTS=$PWD/build/artifacts
sudo /greengrass/v2/bin/greengrass-cli deployment create \
--recipeDir=$RECIPES \
--artifactDir=$ARTIFACTS \
--merge=$COMPONENT_NAME=$COMPONENT_VERSION
}
func gg_remove() {
COMPONENT_NAME=$(sed -nr 's/ComponentName: ([a-zA-Z.-_]+)/\1/p' recipe.yaml)
sudo /greengrass/v2/bin/greengrass-cli deployment create \
--recipeDir=$RECIPES \
--artifactDir=$ARTIFACTS \
--remove=$COMPONENT_NAME
}
```
50 changes: 50 additions & 0 deletions samples/greengrass_v2/deployment_configuration/code.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#!/usr/bin/env python3

# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0.

import time
import os
import http.server
import socketserver

from awsiot.greengrasscoreipc.clientv2 import GreengrassCoreIPCClientV2

client = GreengrassCoreIPCClientV2()
thing_name = os.environ["AWS_IOT_THING_NAME"]


def main():
# https://docs.aws.amazon.com/greengrass/v2/developerguide/ipc-component-configuration.html
config = client.get_configuration().value

print("This component was deployed with the following configuration:", config)

# example use case that takes component configuration as arguments for a webserver
host = config["Webserver"]["Host"]
port = config["Webserver"]["Port"]
directory = config["Webserver"]["Directory"]

os.chdir(directory)

Handler = http.server.SimpleHTTPRequestHandler

with socketserver.TCPServer((host, port), Handler) as httpd:
print(f"serving at {host}:{port} ...")
httpd.serve_forever()


if __name__ == "__main__":
# Once we enter here, we know:
# * all dependencies are available (imports succeeded)
# * IPC Client created
# * AWS_IOT_THING_NAME environment variable is available
# This should be sufficient to consider this component `running` and the deployment will be completed.
# If any of these failed, the component will be `broken`, and the deployment might roll-back or report the error.
# Once the component is `running`, we need to try as hard as possible to keep it alive and running.
while True:
try:
main()
except Exception as e:
print("ERROR", e)
time.sleep(5)
25 changes: 25 additions & 0 deletions samples/greengrass_v2/deployment_configuration/recipe.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# recipe reference: https://docs.aws.amazon.com/greengrass/v2/developerguide/component-recipe-reference.html
---
RecipeFormatVersion: "2020-01-25"
ComponentName: com.example.greengrass_ipc.python.deployment_configuration
ComponentVersion: "1.0.0"
ComponentDescription: Greengrass IPC SDK component example
ComponentPublisher: Amazon
ComponentConfiguration:
DefaultConfiguration:
Webserver:
Host: "127.0.0.1"
Port: 8080
Directory: "/greengrass/v2/packages/"
Manifests:
- Platform:
os: linux
Lifecycle:
Install:
RequiresPrivilege: true
Script: |
apt-get update
apt-get install --yes python3 python3-pip
python3 -m pip install "awsiotsdk>=1.15.0"
Run: |
python3 -u {artifacts:path}/code.py
62 changes: 62 additions & 0 deletions samples/greengrass_v2/low_level_ipc/code.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#!/usr/bin/env python3

# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0.

import json
import time
import os
import random

import awsiot.greengrasscoreipc
import awsiot.greengrasscoreipc.model as model

ipc_client = awsiot.greengrasscoreipc.connect()
thing_name = os.environ["AWS_IOT_THING_NAME"]


def send_telemetry():
telemetry_data = {
"timestamp": int(time.time()),
"battery_state_of_charge": random.random() * 99.9,
"location": {
"longitude": 48.15743 + random.random() / 10.0,
"latitude": 11.57549 + random.random() / 10.0,
},
}

op = ipc_client.new_publish_to_iot_core()
op.activate(
model.PublishToIoTCoreRequest(
topic_name=f"my/iot/{thing_name}/telemetry",
qos=model.QOS.AT_LEAST_ONCE,
payload=json.dumps(telemetry_data).encode(),
)
)
try:
result = op.get_response().result(timeout=5.0)
print("successfully published message:", result)
except Exception as e:
print("failed to publish message:", e)


def main():
while True:
send_telemetry()
time.sleep(5)


if __name__ == "__main__":
# Once we enter here, we know:
# * all dependencies are available (imports succeeded)
# * IPC Client created
# * AWS_IOT_THING_NAME environment variable is available
# This should be sufficient to consider this component `running` and the deployment will be completed.
# If any of these failed, the component will be `broken`, and the deployment might roll-back or report the error.
# Once the component is `running`, we need to try as hard as possible to keep it alive and running.
while True:
try:
main()
except Exception as e:
print("ERROR", e)
time.sleep(5)
31 changes: 31 additions & 0 deletions samples/greengrass_v2/low_level_ipc/recipe.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# recipe reference: https://docs.aws.amazon.com/greengrass/v2/developerguide/component-recipe-reference.html
---
RecipeFormatVersion: "2020-01-25"
ComponentName: com.example.greengrass_ipc.python.low_level_ipc
ComponentVersion: "1.0.0"
ComponentDescription: Greengrass IPC SDK component example
ComponentPublisher: Amazon
ComponentConfiguration:
DefaultConfiguration:
accessControl:
# see https://docs.aws.amazon.com/greengrass/v2/developerguide/ipc-iot-core-mqtt.html
"aws.greengrass.ipc.mqttproxy":
"com.example.greengrass_ipc.python.low_level_ipc:mqttproxy:1":
policyDescription: Allow access to publish/subscribe to all topics on AWS IoT Core.
operations:
- "aws.greengrass#PublishToIoTCore"
- "aws.greengrass#SubscribeToIoTCore"
resources:
- "*" # CHANGE ME: scope down based on principle of least privilege
Manifests:
- Platform:
os: linux
Lifecycle:
Install:
RequiresPrivilege: true
Script: |
apt-get update
apt-get install --yes python3 python3-pip
python3 -m pip install "awsiotsdk>=1.15.0"
Run: |
python3 -u {artifacts:path}/code.py
63 changes: 63 additions & 0 deletions samples/greengrass_v2/pubsub_cloud/code.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#!/usr/bin/env python3

# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0.

import time
import os
import json

from awsiot.greengrasscoreipc.clientv2 import GreengrassCoreIPCClientV2
from awsiot.greengrasscoreipc.model import QOS, IoTCoreMessage

client = GreengrassCoreIPCClientV2()
thing_name = os.environ["AWS_IOT_THING_NAME"]


def on_stream_event(message: IoTCoreMessage):
print(f"Message received:", message)
reply = {
"pong": "sending back what was received",
"topic": message.message.topic_name,
"payload": message.message.payload.decode(),
}

print("Sending pong message back:", reply)
resp = client.publish_to_iot_core(
topic_name="hello/world/response",
qos=QOS.AT_LEAST_ONCE,
payload=json.dumps(reply),
)
print(resp)


def main():
print(f"Running pubsub-cloud sample for thing: {thing_name}")
topic_name = "hello/world"

print(f"Subscribing to AWS IoT Core topic {topic_name}")
resp, op = client.subscribe_to_iot_core(
topic_name=topic_name,
qos=QOS.AT_LEAST_ONCE,
on_stream_event=on_stream_event,
)
print(resp, op)

while True:
time.sleep(999) # wait for incoming messages


if __name__ == "__main__":
# Once we enter here, we know:
# * all dependencies are available (imports succeeded)
# * IPC Client created
# * AWS_IOT_THING_NAME environment variable is available
# This should be sufficient to consider this component `running` and the deployment will be completed.
# If any of these failed, the component will be `broken`, and the deployment might roll-back or report the error.
# Once the component is `running`, we need to try as hard as possible to keep it alive and running.
while True:
try:
main()
except Exception as e:
print("ERROR", e)
time.sleep(5)
31 changes: 31 additions & 0 deletions samples/greengrass_v2/pubsub_cloud/recipe.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# recipe reference: https://docs.aws.amazon.com/greengrass/v2/developerguide/component-recipe-reference.html
---
RecipeFormatVersion: "2020-01-25"
ComponentName: com.example.greengrass_ipc.python.pubsub_cloud
ComponentVersion: "1.0.0"
ComponentDescription: Greengrass IPC SDK component example
ComponentPublisher: Amazon
ComponentConfiguration:
DefaultConfiguration:
accessControl:
# see https://docs.aws.amazon.com/greengrass/v2/developerguide/ipc-iot-core-mqtt.html
"aws.greengrass.ipc.mqttproxy":
"com.example.greengrass_ipc.python.pubsub_cloud:mqttproxy:1":
policyDescription: Allow access to publish/subscribe to all topics on AWS IoT Core.
operations:
- "aws.greengrass#PublishToIoTCore"
- "aws.greengrass#SubscribeToIoTCore"
resources:
- "*" # CHANGE ME: scope down based on principle of least privilege
Manifests:
- Platform:
os: linux
Lifecycle:
Install:
RequiresPrivilege: true
Script: |
apt-get update
apt-get install --yes python3 python3-pip
python3 -m pip install "awsiotsdk>=1.15.0"
Run: |
python3 -u {artifacts:path}/code.py
Loading

0 comments on commit f73345d

Please sign in to comment.