Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add Greengrass V2 IPC samples using new IPC-Client V2 #487

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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