Skip to content

Commit 820c29a

Browse files
authored
Iot commands beta (GoogleCloudPlatform#1746)
* Adds beta example for Cloud IoT Core device commands
1 parent 59a5073 commit 820c29a

15 files changed

+3053
-0
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Cloud IoT Core Python Samples
2+
This folder contains Python samples that demonstrate an overview of the
3+
commands beta feature.
4+
5+
## Quickstart
6+
1. Install the Cloud SDK as described in [the device manager guide](https://cloud.google.com/iot/docs/device_manager_guide).
7+
2. Create a PubSub topic:
8+
9+
gcloud beta pubsub topics create projects/my-iot-project/topics/device-events
10+
11+
3. Create a registry:
12+
13+
gcloud iot registries create my-registry \
14+
--project=my-iot-project \
15+
--region=us-central1 \
16+
--event-notification-config=topic=projects/intense-wavelet-343/topics/device-events
17+
18+
4. Use the `generate_keys.sh` script to generate your signing keys:
19+
20+
<path-to>/python-docs-samples/iot/api-client/generate_keys.sh
21+
22+
5. Register a device:
23+
24+
gcloud iot devices create my-python-device \
25+
--project=my-iot-project \
26+
--region=us-central1 \
27+
--registry=my-registry \
28+
--public-key path=rsa_cert.pem,type=rs256
29+
30+
6. Connect a virtual device using the sample app in the `receive` folder.
31+
7. While the virtual device is connected, send a commmand using the sample app in the `send` folder.
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
.. This file is automatically generated. Do not edit this file directly.
2+
3+
Google Cloud IoT Core Python Samples
4+
===============================================================================
5+
6+
.. image:: https://gstatic.com/cloudssh/images/open-btn.png
7+
:target: https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/GoogleCloudPlatform/python-docs-samples&page=editor&open_in_editor=iot/api-client/mqtt_example/README.rst
8+
9+
10+
This directory contains samples for Google Cloud IoT Core. `Google Cloud IoT Core`_ allows developers to easily integrate Publish and Subscribe functionality with devices and programmatically manage device authorization.
11+
Before you run the sample, you must retrieve the Google root certificate. For example, ``wget https://pki.goog/roots.pem`` or ``curl https://pki.goog/roots.pem > roots.pem``.
12+
The following example runs the sample using the project ID ``blue-jet-123`` and the device name ``my-python-device``::
13+
14+
python receive.py \
15+
--registry_id=my-registry \
16+
--project_id=blue-jet-123 \
17+
--device_id=my-python-device \
18+
--algorithm=RS256 \
19+
--private_key_file=../rsa_private.pem
20+
21+
22+
23+
24+
.. _Google Cloud IoT Core: https://cloud.google.com/iot/docs
25+
26+
Setup
27+
-------------------------------------------------------------------------------
28+
29+
30+
Install Dependencies
31+
++++++++++++++++++++
32+
33+
#. Install `pip`_ and `virtualenv`_ if you do not already have them. You may want to refer to the `Python Development Environment Setup Guide`_ for Google Cloud Platform for instructions.
34+
35+
.. _Python Development Environment Setup Guide:
36+
https://cloud.google.com/python/setup
37+
38+
#. Create a virtualenv. Samples are compatible with Python 2.7 and 3.4+.
39+
40+
.. code-block:: bash
41+
42+
$ virtualenv env
43+
$ source env/bin/activate
44+
45+
#. Install the dependencies needed to run the samples.
46+
47+
.. code-block:: bash
48+
49+
$ pip install -r requirements.txt
50+
51+
.. _pip: https://pip.pypa.io/
52+
.. _virtualenv: https://virtualenv.pypa.io/
53+
54+
Samples
55+
-------------------------------------------------------------------------------
56+
57+
MQTT Device Client Example
58+
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
59+
60+
.. image:: https://gstatic.com/cloudssh/images/open-btn.png
61+
:target: https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/GoogleCloudPlatform/python-docs-samples&page=editor&open_in_editor=iot/api-client/mqtt_example/receive.py;iot/api-client/mqtt_example/README.rst
62+
63+
64+
65+
66+
To run this sample:
67+
68+
.. code-block:: bash
69+
70+
$ python receive.py
71+
72+
usage: receive.py [-h] [--project_id PROJECT_ID] --registry_id REGISTRY_ID
73+
--device_id DEVICE_ID --private_key_file PRIVATE_KEY_FILE
74+
--algorithm {RS256,ES256} [--cloud_region CLOUD_REGION]
75+
[--ca_certs CA_CERTS]
76+
[--mqtt_bridge_hostname MQTT_BRIDGE_HOSTNAME]
77+
[--mqtt_bridge_port {8883,443}]
78+
[--jwt_expires_minutes JWT_EXPIRES_MINUTES]
79+
80+
Example Google Cloud IoT Core MQTT device connection code.
81+
82+
optional arguments:
83+
-h, --help show this help message and exit
84+
--project_id PROJECT_ID
85+
GCP cloud project name
86+
--registry_id REGISTRY_ID
87+
Cloud IoT Core registry id
88+
--device_id DEVICE_ID
89+
Cloud IoT Core device id
90+
--private_key_file PRIVATE_KEY_FILE
91+
Path to private key file.
92+
--algorithm {RS256,ES256}
93+
Which encryption algorithm to use to generate the JWT.
94+
--cloud_region CLOUD_REGION
95+
GCP cloud region
96+
--ca_certs CA_CERTS CA root from https://pki.google.com/roots.pem
97+
--mqtt_bridge_hostname MQTT_BRIDGE_HOSTNAME
98+
MQTT bridge hostname.
99+
--mqtt_bridge_port {8883,443}
100+
MQTT bridge port.
101+
--jwt_expires_minutes JWT_EXPIRES_MINUTES
102+
Expiration time, in minutes, for JWT tokens.
103+
104+
105+
106+
107+
108+
.. _Google Cloud SDK: https://cloud.google.com/sdk/
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# This file is used to generate README.rst
2+
3+
product:
4+
name: Google Cloud IoT Core
5+
short_name: Cloud IoT Core
6+
url: https://cloud.google.com/iot/docs
7+
description: >
8+
`Google Cloud IoT Core`_ allows developers to easily integrate Publish and
9+
Subscribe functionality with devices and programmatically manage device
10+
authorization.
11+
12+
Before you run the sample, you must retrieve the Google root certificate.
13+
For example, ``wget https://pki.goog/roots.pem`` or
14+
``curl https://pki.goog/roots.pem > roots.pem``.
15+
16+
The following example runs the sample using the project ID ``blue-jet-123``
17+
and the device name ``my-python-device``::
18+
19+
python receive.py \
20+
--registry_id=my-registry \
21+
--project_id=blue-jet-123 \
22+
--device_id=my-python-device \
23+
--algorithm=RS256 \
24+
--private_key_file=../rsa_private.pem
25+
26+
setup:
27+
- install_deps
28+
29+
samples:
30+
- name: MQTT Device Client Example
31+
file: receive.py
32+
show_help: True
33+
34+
cloud_client_library: false
35+
36+
folder: iot/api-client/mqtt_example
Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
#!/usr/bin/env python
2+
3+
# Copyright 2018 Google LLC
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
"""Python sample for connecting to Google Cloud IoT Core via MQTT, using JWT.
17+
This example connects to Google Cloud IoT Core via MQTT, using a JWT for device
18+
authentication. After connecting, by default the device publishes 100 messages
19+
to the device's MQTT topic at a rate of one per second, and then exits.
20+
Before you run the sample, you must follow the instructions in the README
21+
for this sample.
22+
"""
23+
24+
# [START iot_mqtt_includes]
25+
import argparse
26+
import datetime
27+
import os
28+
import ssl
29+
import time
30+
31+
import jwt
32+
import paho.mqtt.client as mqtt
33+
# [END iot_mqtt_includes]
34+
35+
# The initial backoff time after a disconnection occurs, in seconds.
36+
minimum_backoff_time = 1
37+
38+
# The maximum backoff time before giving up, in seconds.
39+
MAXIMUM_BACKOFF_TIME = 32
40+
41+
# Whether to wait with exponential backoff before publishing.
42+
should_backoff = False
43+
44+
45+
# [START iot_mqtt_jwt]
46+
def create_jwt(project_id, private_key_file, algorithm):
47+
"""Creates a JWT (https://jwt.io) to establish an MQTT connection.
48+
Args:
49+
project_id: The cloud project ID this device belongs to
50+
private_key_file: A path to a file containing either an RSA256 or
51+
ES256 private key.
52+
algorithm: The encryption algorithm to use. Either 'RS256' or 'ES256'
53+
Returns:
54+
A str for the JWT from the given project id and private key path,
55+
set to expire in 60 minutes. After 60 minutes, your client will be
56+
disconnected, and a new JWT will have to be generated.
57+
Raises:
58+
ValueError: If the private_key_file does not contain a known key.
59+
"""
60+
61+
token = {
62+
# The time that the token was issued at
63+
'iat': datetime.datetime.utcnow(),
64+
# The time the token expires.
65+
'exp': datetime.datetime.utcnow() + datetime.timedelta(minutes=60),
66+
# The audience field should always be set to the GCP project id.
67+
'aud': project_id
68+
}
69+
70+
# Read the private key file.
71+
with open(private_key_file, 'r') as f:
72+
private_key = f.read()
73+
74+
print('Creating JWT using {} from private key file {}'.format(
75+
algorithm, private_key_file))
76+
77+
return jwt.encode(token, private_key, algorithm=algorithm)
78+
# [END iot_mqtt_jwt]
79+
80+
81+
# [START iot_mqtt_config]
82+
def error_str(rc):
83+
"""Convert a Paho error to a human readable string."""
84+
return '{}: {}'.format(rc, mqtt.error_string(rc))
85+
86+
87+
def on_connect(unused_client, unused_userdata, unused_flags, rc):
88+
"""Callback for when a device connects."""
89+
print('on_connect', mqtt.connack_string(rc))
90+
91+
# After a successful connect, reset backoff time and stop backing off.
92+
global should_backoff
93+
global minimum_backoff_time
94+
should_backoff = False
95+
minimum_backoff_time = 1
96+
97+
98+
def on_disconnect(unused_client, unused_userdata, rc):
99+
"""Paho callback for when a device disconnects."""
100+
print('on_disconnect', error_str(rc))
101+
102+
# Since a disconnect occurred, the next loop iteration will wait with
103+
# exponential backoff.
104+
global should_backoff
105+
should_backoff = True
106+
107+
108+
def on_publish(unused_client, unused_userdata, unused_mid):
109+
"""Paho callback when a message is sent to the broker."""
110+
print('on_publish')
111+
112+
113+
def on_message(unused_client, unused_userdata, message):
114+
"""Callback when the device receives a message on a subscription."""
115+
payload = str(message.payload)
116+
print('Received message \'{}\' on topic \'{}\' with Qos {}'.format(
117+
payload, message.topic, str(message.qos)))
118+
119+
120+
def get_client(
121+
project_id, cloud_region, registry_id, device_id, private_key_file,
122+
algorithm, ca_certs, mqtt_bridge_hostname, mqtt_bridge_port):
123+
"""Create our MQTT client. The client_id is a unique string that identifies
124+
this device. For Google Cloud IoT Core, it must be in the format below."""
125+
client_id = 'projects/{}/locations/{}/registries/{}/devices/{}'.format(
126+
project_id, cloud_region, registry_id, device_id)
127+
client = mqtt.Client(client_id=client_id)
128+
129+
password = create_jwt(project_id, private_key_file, algorithm)
130+
131+
# With Google Cloud IoT Core, the username field is ignored, and the
132+
# password field is used to transmit a JWT to authorize the device.
133+
client.username_pw_set(username='unused', password=password)
134+
135+
# Enable SSL/TLS support.
136+
client.tls_set(ca_certs=ca_certs, tls_version=ssl.PROTOCOL_TLSv1_2)
137+
138+
# Register message callbacks. https://eclipse.org/paho/clients/python/docs/
139+
# describes additional callbacks that Paho supports. In this example, the
140+
# callbacks just print to standard out.
141+
client.on_connect = on_connect
142+
client.on_publish = on_publish
143+
client.on_disconnect = on_disconnect
144+
client.on_message = on_message
145+
146+
# Connect to the Google MQTT bridge.
147+
print('Connecting with id: {} and pass: {}'.format(client_id, password))
148+
client.connect(mqtt_bridge_hostname, mqtt_bridge_port)
149+
150+
# This is the topic that the device will receive configuration updates on.
151+
mqtt_command_topic = '/devices/{}/commands/#'.format(device_id)
152+
153+
# Subscribe to the config topic.
154+
print('Subscribing to {}'.format(mqtt_command_topic))
155+
client.subscribe(mqtt_command_topic, qos=1)
156+
157+
return client
158+
# [END iot_mqtt_config]
159+
160+
161+
if __name__ == '__main__':
162+
# [START iot_mqtt_run]
163+
"""Parse command line arguments."""
164+
parser = argparse.ArgumentParser(description=(
165+
'Example Google Cloud IoT Core MQTT device connection code.'))
166+
parser.add_argument(
167+
'--project_id',
168+
default=os.environ.get('GOOGLE_CLOUD_PROJECT'),
169+
help='GCP cloud project name')
170+
parser.add_argument(
171+
'--registry_id', required=True, help='Cloud IoT Core registry id')
172+
parser.add_argument(
173+
'--device_id', required=True, help='Cloud IoT Core device id')
174+
parser.add_argument(
175+
'--private_key_file',
176+
required=True, help='Path to private key file.')
177+
parser.add_argument(
178+
'--algorithm',
179+
choices=('RS256', 'ES256'),
180+
required=True,
181+
help='Which encryption algorithm to use to generate the JWT.')
182+
parser.add_argument(
183+
'--cloud_region', default='us-central1', help='GCP cloud region')
184+
parser.add_argument(
185+
'--ca_certs',
186+
default='roots.pem',
187+
help=('CA root from https://pki.google.com/roots.pem'))
188+
parser.add_argument(
189+
'--mqtt_bridge_hostname',
190+
default='mqtt.googleapis.com',
191+
help='MQTT bridge hostname.')
192+
parser.add_argument(
193+
'--mqtt_bridge_port',
194+
choices=(8883, 443),
195+
default=8883,
196+
type=int,
197+
help='MQTT bridge port.')
198+
parser.add_argument(
199+
'--jwt_expires_minutes',
200+
default=20,
201+
type=int,
202+
help=('Expiration time, in minutes, for JWT tokens.'))
203+
204+
args = parser.parse_args()
205+
206+
# Add any JWT refresh logic here
207+
client = get_client(
208+
args.project_id, args.cloud_region, args.registry_id, args.device_id,
209+
args.private_key_file, args.algorithm, args.ca_certs,
210+
args.mqtt_bridge_hostname, args.mqtt_bridge_port)
211+
212+
# Wait two minutes for commands, for production you may want while True
213+
for i in range(1, 120):
214+
# Process network events.
215+
client.loop()
216+
print('Sleeping...')
217+
time.sleep(1)
218+
219+
print('Finished.')
220+
# [END iot_mqtt_run]

0 commit comments

Comments
 (0)