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

Adds token refresh to MQTT example. #1197

Merged
merged 2 commits into from
Nov 6, 2017
Merged
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
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