Skip to content

Commit

Permalink
Adds token refresh to MQTT example. (GoogleCloudPlatform#1197)
Browse files Browse the repository at this point in the history
* Adds token refresh to MQTT example.

* Fixes lint error.
  • Loading branch information
gguuss authored and Jon Wayne Parrott committed Nov 6, 2017
1 parent 0715f6d commit 83d1c5b
Show file tree
Hide file tree
Showing 4 changed files with 205 additions and 84 deletions.
51 changes: 0 additions & 51 deletions iot/api-client/mqtt_example/README.md

This file was deleted.

107 changes: 107 additions & 0 deletions iot/api-client/mqtt_example/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
.. This file is automatically generated. Do not edit this file directly.
Google Cloud IoT Core API Python Samples
===============================================================================

This directory contains samples for Google Cloud IoT Core API. `Google Cloud IoT Core`_ allows developers to easily integrate Publish and Subscribe functionality with devices and programmatically manage device authorization.
The following example runs the sample using the project ID `blue-jet-123` and the device name `my-python-device`:

python cloudiot_mqtt_example.py \
--registry_id=my-registry \
--project_id=blue-jet-123 \
--device_id=my-python-device \
--algorithm=RS256 \
--private_key_file=../rsa_private.pem




.. _Google Cloud IoT Core API: https://cloud.google.com/iot/docs

Setup
-------------------------------------------------------------------------------


Install Dependencies
++++++++++++++++++++

#. 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.

.. _Python Development Environment Setup Guide:
https://cloud.google.com/python/setup

#. Create a virtualenv. Samples are compatible with Python 2.7 and 3.4+.

.. code-block:: bash
$ virtualenv env
$ source env/bin/activate
#. Install the dependencies needed to run the samples.

.. code-block:: bash
$ pip install -r requirements.txt
.. _pip: https://pip.pypa.io/
.. _virtualenv: https://virtualenv.pypa.io/

Samples
-------------------------------------------------------------------------------

MQTT Device Client Example
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++



To run this sample:

.. code-block:: bash
$ python cloudiot_mqtt_example.py
usage: cloudiot_mqtt_example.py [-h] [--project_id PROJECT_ID] --registry_id
REGISTRY_ID --device_id DEVICE_ID
--private_key_file PRIVATE_KEY_FILE
--algorithm {RS256,ES256}
[--cloud_region CLOUD_REGION]
[--ca_certs CA_CERTS]
[--num_messages NUM_MESSAGES]
[--message_type {event,state}]
[--mqtt_bridge_hostname MQTT_BRIDGE_HOSTNAME]
[--mqtt_bridge_port {8883,443}]
[--jwt_expires_minutes JWT_EXPIRES_MINUTES]
Example Google Cloud IoT Core MQTT device connection code.
optional arguments:
-h, --help show this help message and exit
--project_id PROJECT_ID
GCP cloud project name
--registry_id REGISTRY_ID
Cloud IoT Core registry id
--device_id DEVICE_ID
Cloud IoT Core device id
--private_key_file PRIVATE_KEY_FILE
Path to private key file.
--algorithm {RS256,ES256}
Which encryption algorithm to use to generate the JWT.
--cloud_region CLOUD_REGION
GCP cloud region
--ca_certs CA_CERTS CA root from https://pki.google.com/roots.pem
--num_messages NUM_MESSAGES
Number of messages to publish.
--message_type {event,state}
Indicates whether the message to be published is a
telemetry event or a device state message.
--mqtt_bridge_hostname MQTT_BRIDGE_HOSTNAME
MQTT bridge hostname.
--mqtt_bridge_port {8883,443}
MQTT bridge port.
--jwt_expires_minutes JWT_EXPIRES_MINUTES
Expiration time, in minutes, for JWT tokens.
.. _Google Cloud SDK: https://cloud.google.com/sdk/
30 changes: 30 additions & 0 deletions iot/api-client/mqtt_example/README.rst.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# This file is used to generate README.rst

product:
name: Google Cloud IoT Core API
short_name: Cloud IoT Core
url: https://cloud.google.com/iot/docs
description: >
`Google Cloud IoT Core`_ allows developers to easily integrate Publish and
Subscribe functionality with devices and programmatically manage device
authorization.

The following example runs the sample using the project ID `blue-jet-123`
and the device name `my-python-device`:

python cloudiot_mqtt_example.py \
--registry_id=my-registry \
--project_id=blue-jet-123 \
--device_id=my-python-device \
--algorithm=RS256 \
--private_key_file=../rsa_private.pem

setup:
- install_deps

samples:
- name: MQTT Device Client Example
file: cloudiot_mqtt_example.py
show_help: True

cloud_client_library: false
101 changes: 68 additions & 33 deletions iot/api-client/mqtt_example/cloudiot_mqtt_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import jwt
import paho.mqtt.client as mqtt


# [START iot_mqtt_jwt]
def create_jwt(project_id, private_key_file, algorithm):
"""Creates a JWT (https://jwt.io) to establish an MQTT connection.
Expand Down Expand Up @@ -64,6 +65,8 @@ def create_jwt(project_id, private_key_file, algorithm):
return jwt.encode(token, private_key, algorithm=algorithm)
# [END iot_mqtt_jwt]


# [START iot_mqtt_config]
def error_str(rc):
"""Convert a Paho error to a human readable string."""
return '{}: {}'.format(rc, mqtt.error_string(rc))
Expand All @@ -84,6 +87,46 @@ def on_publish(unused_client, unused_userdata, unused_mid):
print('on_publish')


def get_client(
project_id, cloud_region, registry_id, device_id, private_key_file,
algorithm, ca_certs, mqtt_bridge_hostname, mqtt_bridge_port):
"""Create our MQTT client. The client_id is a unique string that identifies
this device. For Google Cloud IoT Core, it must be in the format below."""
client = mqtt.Client(
client_id=('projects/{}/locations/{}/registries/{}/devices/{}'
.format(
project_id,
cloud_region,
registry_id,
device_id)))

# With Google Cloud IoT Core, the username field is ignored, and the
# password field is used to transmit a JWT to authorize the device.
client.username_pw_set(
username='unused',
password=create_jwt(
project_id, private_key_file, algorithm))

# Enable SSL/TLS support.
client.tls_set(ca_certs=ca_certs)

# Register message callbacks. https://eclipse.org/paho/clients/python/docs/
# describes additional callbacks that Paho supports. In this example, the
# callbacks just print to standard out.
client.on_connect = on_connect
client.on_publish = on_publish
client.on_disconnect = on_disconnect

# Connect to the Google MQTT bridge.
client.connect(mqtt_bridge_hostname, mqtt_bridge_port)

# Start the network loop.
client.loop_start()

return client
# [END iot_mqtt_config]


def parse_command_line_args():
"""Parse command line arguments."""
parser = argparse.ArgumentParser(description=(
Expand Down Expand Up @@ -131,57 +174,48 @@ def parse_command_line_args():
default=8883,
type=int,
help='MQTT bridge port.')
parser.add_argument(
'--jwt_expires_minutes',
default=20,
type=int,
help=('Expiration time, in minutes, for JWT tokens.'))

return parser.parse_args()


# [START iot_mqtt_run]
def main():
args = parse_command_line_args()

# Create our MQTT client. The client_id is a unique string that identifies
# this device. For Google Cloud IoT Core, it must be in the format below.
client = mqtt.Client(
client_id=('projects/{}/locations/{}/registries/{}/devices/{}'
.format(
args.project_id,
args.cloud_region,
args.registry_id,
args.device_id)))

# With Google Cloud IoT Core, the username field is ignored, and the
# password field is used to transmit a JWT to authorize the device.
client.username_pw_set(
username='unused',
password=create_jwt(
args.project_id, args.private_key_file, args.algorithm))

# Enable SSL/TLS support.
client.tls_set(ca_certs=args.ca_certs)

# Register message callbacks. https://eclipse.org/paho/clients/python/docs/
# describes additional callbacks that Paho supports. In this example, the
# callbacks just print to standard out.
client.on_connect = on_connect
client.on_publish = on_publish
client.on_disconnect = on_disconnect

# Connect to the Google MQTT bridge.
client.connect(args.mqtt_bridge_hostname, args.mqtt_bridge_port)

# Start the network loop.
client.loop_start()

# Publish to the events or state topic based on the flag.
sub_topic = 'events' if args.message_type == 'event' else 'state'

mqtt_topic = '/devices/{}/{}'.format(args.device_id, sub_topic)

jwt_iat = datetime.datetime.utcnow()
jwt_exp_mins = args.jwt_expires_minutes
client = get_client(
args.project_id, args.cloud_region, args.registry_id, args.device_id,
args.private_key_file, args.algorithm, args.ca_certs,
args.mqtt_bridge_hostname, args.mqtt_bridge_port)

# Publish num_messages mesages to the MQTT bridge once per second.
for i in range(1, args.num_messages + 1):
payload = '{}/{}-payload-{}'.format(
args.registry_id, args.device_id, i)
print('Publishing message {}/{}: \'{}\''.format(
i, args.num_messages, payload))
seconds_since_issue = (datetime.datetime.utcnow() - jwt_iat).seconds
if seconds_since_issue > 60 * jwt_exp_mins:
print('Refreshing token after {}s').format(seconds_since_issue)
client.loop_stop()
jwt_iat = datetime.datetime.utcnow()
client = get_client(
args.project_id, args.cloud_region,
args.registry_id, args.device_id, args.private_key_file,
args.algorithm, args.ca_certs, args.mqtt_bridge_hostname,
args.mqtt_bridge_port)

# Publish "payload" to the MQTT topic. qos=1 means at least once
# delivery. Cloud IoT Core also supports qos=0 for at most once
# delivery.
Expand All @@ -193,6 +227,7 @@ def main():
# End the network loop and finish.
client.loop_stop()
print('Finished.')
# [END iot_mqtt_run]


if __name__ == '__main__':
Expand Down

0 comments on commit 83d1c5b

Please sign in to comment.