Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 586624e

Browse filesBrowse files
gguussJon Wayne Parrott
authored andcommitted
Final additions in private beta (GoogleCloudPlatform#1136)
* Final additions in private beta * Adds HTTP client and state support * Fixes rouge space * Remove invalid message on error.
1 parent e1f6883 commit 586624e
Copy full SHA for 586624e

File tree

Expand file treeCollapse file tree

7 files changed

+336
-90
lines changed
Filter options
Expand file treeCollapse file tree

7 files changed

+336
-90
lines changed

‎iot/api-client/http_example/README.md

Copy file name to clipboard
+50Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# Cloud IoT Core Python HTTP example
2+
3+
This sample app publishes data to Cloud Pub/Sub using the HTTP bridge provided
4+
as part of Google Cloud IoT Core. For detailed running instructions see the
5+
[HTTP code samples
6+
guide](https://cloud.google.com/iot/docs/protocol_bridge_guide).
7+
8+
# Setup
9+
10+
1. Use virtualenv to create a local Python environment.
11+
12+
```
13+
virtualenv env && source env/bin/activate
14+
```
15+
16+
1. Install the dependencies
17+
18+
```
19+
pip install -r requirements.txt
20+
```
21+
22+
# Running the Sample
23+
24+
The following snippet summarizes usage:
25+
26+
```
27+
cloudiot_http_example.py [-h]
28+
--project_id PROJECT_ID
29+
--registry_id REGISTRY_ID
30+
--device_id DEVICE_ID
31+
--private_key_file PRIVATE_KEY_FILE
32+
--algorithm {RS256,ES256}
33+
--message_type={event,state}
34+
[--cloud_region CLOUD_REGION]
35+
[--ca_certs CA_CERTS]
36+
[--num_messages NUM_MESSAGES]
37+
```
38+
39+
For example, if your project ID is `blue-jet-123`, the following example shows
40+
how you would execute using the configuration from the HTTP code samples guide:
41+
42+
```
43+
python cloudiot_http_example.py \
44+
--registry_id=my-registry \
45+
--project_id=blue-jet-123 \
46+
--device_id=my-python-device \
47+
--message_type=event \
48+
--algorithm=RS256 \
49+
--private_key_file=../rsa_private.pem
50+
```
+161Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
#!/usr/bin/env python
2+
3+
# Copyright 2017 Google Inc.
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 HTTP, using JWT.
17+
This example connects to Google Cloud IoT Core via HTTP, using a JWT for device
18+
authentication. After connecting, by default the device publishes 100 messages
19+
to the server at a rate of one per second, and then exits.
20+
Before you run the sample, you must register your device as described in the
21+
README in the parent folder.
22+
"""
23+
24+
import argparse
25+
import base64
26+
import datetime
27+
import json
28+
import time
29+
30+
import jwt
31+
import requests
32+
33+
34+
_BASE_URL = 'https://cloudiot-device.googleapis.com/v1beta1'
35+
36+
37+
def create_jwt(project_id, private_key_file, algorithm):
38+
"""Creates a JWT (https://jwt.io) to authenticate this device.
39+
Args:
40+
project_id: The cloud project ID this device belongs to
41+
private_key_file: A path to a file containing either an RSA256 or
42+
ES256 private key.
43+
algorithm: The encryption algorithm to use. Either 'RS256' or
44+
'ES256'
45+
Returns:
46+
A JWT generated from the given project_id and private key, which
47+
expires in 20 minutes. After 20 minutes, your client will be
48+
disconnected, and a new JWT will have to be generated.
49+
Raises:
50+
ValueError: If the private_key_file does not contain a known key.
51+
"""
52+
53+
token = {
54+
# The time the token was issued.
55+
'iat': datetime.datetime.utcnow(),
56+
# Token expiration time.
57+
'exp': datetime.datetime.utcnow() + datetime.timedelta(minutes=60),
58+
# The audience field should always be set to the GCP project id.
59+
'aud': project_id
60+
}
61+
62+
# Read the private key file.
63+
with open(private_key_file, 'r') as f:
64+
private_key = f.read()
65+
66+
print('Creating JWT using {} from private key file {}'.format(
67+
algorithm, private_key_file))
68+
69+
return jwt.encode(token, private_key, algorithm=algorithm)
70+
71+
72+
def parse_command_line_args():
73+
"""Parse command line arguments."""
74+
parser = argparse.ArgumentParser(description=(
75+
'Example Google Cloud IoT Core HTTP device connection code.'))
76+
parser.add_argument(
77+
'--project_id', required=True, help='GCP cloud project name')
78+
parser.add_argument(
79+
'--registry_id', required=True, help='Cloud IoT Core registry id')
80+
parser.add_argument(
81+
'--device_id', required=True, help='Cloud IoT Core device id')
82+
parser.add_argument(
83+
'--private_key_file',
84+
required=True,
85+
help='Path to private key file.')
86+
parser.add_argument(
87+
'--algorithm',
88+
choices=('RS256', 'ES256'),
89+
required=True,
90+
help='The encryption algorithm to use to generate the JWT.')
91+
parser.add_argument(
92+
'--cloud_region', default='us-central1', help='GCP cloud region')
93+
parser.add_argument(
94+
'--ca_certs',
95+
default='roots.pem',
96+
help=('CA root from https://pki.google.com/roots.pem'))
97+
parser.add_argument(
98+
'--num_messages',
99+
type=int,
100+
default=100,
101+
help='Number of messages to publish.')
102+
parser.add_argument(
103+
'--message_type',
104+
choices=('event', 'state'),
105+
default='event',
106+
required=True,
107+
help=('Indicates whether the message to be published is a '
108+
'telemetry event or a device state message.'))
109+
parser.add_argument(
110+
'--base_url',
111+
default=_BASE_URL,
112+
help=('Base URL for the Cloud IoT Core Device Service API'))
113+
114+
return parser.parse_args()
115+
116+
117+
def main():
118+
args = parse_command_line_args()
119+
120+
# Publish to the events or state topic based on the flag.
121+
url_suffix = 'publishEvent' if args.message_type == 'event' else 'setState'
122+
123+
publish_url = (
124+
'{}/projects/{}/locations/{}/registries/{}/devices/{}:{}').format(
125+
args.base_url, args.project_id, args.cloud_region,
126+
args.registry_id, args.device_id, url_suffix)
127+
128+
jwt_token = create_jwt(
129+
args.project_id, args.private_key_file, args.algorithm)
130+
131+
headers = {
132+
'Authorization': 'Bearer {}'.format(jwt_token),
133+
'Content-Type': 'application/json'
134+
}
135+
136+
# Publish num_messages mesages to the HTTP bridge once per second.
137+
for i in range(1, args.num_messages + 1):
138+
payload = '{}/{}-payload-{}'.format(
139+
args.registry_id, args.device_id, i)
140+
print('Publishing message {}/{}: \'{}\''.format(
141+
i, args.num_messages, payload))
142+
body = None
143+
if args.message_type == 'event':
144+
body = {'binary_data': base64.urlsafe_b64encode(payload)}
145+
else:
146+
body = {
147+
'state': {'binary_data': base64.urlsafe_b64encode(payload)}
148+
}
149+
150+
resp = requests.post(
151+
publish_url, data=json.dumps(body), headers=headers)
152+
153+
print('HTTP response: ', resp)
154+
155+
# Send events every second. State should not be updated as often
156+
time.sleep(1 if args.message_type == 'event' else 5)
157+
print('Finished.')
158+
159+
160+
if __name__ == '__main__':
161+
main()
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
cryptography==2.0.3
2+
pyjwt==1.5.3
3+
requests==2.18.4

‎iot/api-client/manager/manager.py

Copy file name to clipboardExpand all lines: iot/api-client/manager/manager.py
+33-5Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ def get_client(service_account_json, api_key):
6969
provided API key and creating a service object using the service account
7070
credentials JSON."""
7171
api_scopes = ['https://www.googleapis.com/auth/cloud-platform']
72-
api_version = 'v1beta1'
72+
api_version = 'v1'
7373
discovery_api = 'https://cloudiot.googleapis.com/$discovery/rest'
7474
service_name = 'cloudiotcore'
7575

@@ -173,7 +173,7 @@ def delete_device(
173173

174174

175175
def delete_registry(
176-
service_account_json, api_key, project_id, cloud_region, registry_id):
176+
service_account_json, api_key, project_id, cloud_region, registry_id):
177177
"""Deletes the specified registry."""
178178
print('Delete registry')
179179
client = get_client(service_account_json, api_key)
@@ -216,6 +216,23 @@ def get_device(
216216
return device
217217

218218

219+
def get_state(
220+
service_account_json, api_key, project_id, cloud_region, registry_id,
221+
device_id):
222+
"""Retrieve a device's state blobs."""
223+
client = get_client(service_account_json, api_key)
224+
registry_name = 'projects/{}/locations/{}/registries/{}'.format(
225+
project_id, cloud_region, registry_id)
226+
227+
device_name = '{}/devices/{}'.format(registry_name, device_id)
228+
devices = client.projects().locations().registries().devices()
229+
state = devices.states().list(name=device_name, numStates=5).execute()
230+
231+
print('State: {}\n'.format(state))
232+
233+
return state
234+
235+
219236
def list_devices(
220237
service_account_json, api_key, project_id, cloud_region, registry_id):
221238
"""List all devices in the registry."""
@@ -261,9 +278,9 @@ def create_registry(
261278
project_id,
262279
cloud_region)
263280
body = {
264-
'eventNotificationConfig': {
281+
'eventNotificationConfigs': [{
265282
'pubsubTopicName': pubsub_topic
266-
},
283+
}],
267284
'id': registry_id
268285
}
269286
request = client.projects().locations().registries().create(
@@ -274,6 +291,7 @@ def create_registry(
274291
print('Created registry')
275292
return response
276293
except HttpError:
294+
print('Error, registry not created')
277295
return ""
278296

279297

@@ -425,7 +443,8 @@ def parse_command_line_args():
425443
command.add_parser('delete-device', help=delete_device.__doc__)
426444
command.add_parser('delete-registry', help=delete_registry.__doc__)
427445
command.add_parser('get', help=get_device.__doc__)
428-
command.add_parser('get-registry', help=get_device.__doc__)
446+
command.add_parser('get-registry', help=get_registry.__doc__)
447+
command.add_parser('get-state', help=get_state.__doc__)
429448
command.add_parser('list', help=list_devices.__doc__)
430449
command.add_parser('list-registries', help=list_registries.__doc__)
431450
command.add_parser('patch-es256', help=patch_es256_auth.__doc__)
@@ -436,6 +455,10 @@ def parse_command_line_args():
436455

437456
def run_command(args):
438457
"""Calls the program using the specified command."""
458+
if args.project_id is None:
459+
print('You must specify a project ID or set the environment variable.')
460+
return
461+
439462
if args.command == 'create-rsa256':
440463
create_rs256_device(
441464
args.service_account_json, args.api_key, args.project_id,
@@ -476,6 +499,11 @@ def run_command(args):
476499
args.service_account_json, args.api_key, args.project_id,
477500
args.cloud_region, args.registry_id, args.device_id)
478501

502+
elif args.command == 'get-state':
503+
get_state(
504+
args.service_account_json, args.api_key, args.project_id,
505+
args.cloud_region, args.registry_id, args.device_id)
506+
479507
elif args.command == 'list':
480508
list_devices(
481509
args.service_account_json, args.api_key, args.project_id,

‎iot/api-client/manager/manager_test.py

Copy file name to clipboardExpand all lines: iot/api-client/manager/manager_test.py
+10Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,10 @@ def test_add_delete_rs256_device(test_topic, capsys):
101101
service_account_json, api_key, project_id, cloud_region,
102102
registry_id, device_id)
103103

104+
manager.get_state(
105+
service_account_json, api_key, project_id, cloud_region,
106+
registry_id, device_id)
107+
104108
manager.delete_device(
105109
service_account_json, api_key, project_id, cloud_region,
106110
registry_id, device_id)
@@ -111,6 +115,7 @@ def test_add_delete_rs256_device(test_topic, capsys):
111115

112116
out, _ = capsys.readouterr()
113117
assert 'format : RSA_X509_PEM' in out
118+
assert 'State: {' in out
114119

115120

116121
def test_add_delete_es256_device(test_topic, capsys):
@@ -127,6 +132,10 @@ def test_add_delete_es256_device(test_topic, capsys):
127132
service_account_json, api_key, project_id, cloud_region,
128133
registry_id, device_id)
129134

135+
manager.get_state(
136+
service_account_json, api_key, project_id, cloud_region,
137+
registry_id, device_id)
138+
130139
manager.delete_device(
131140
service_account_json, api_key, project_id, cloud_region,
132141
registry_id, device_id)
@@ -137,6 +146,7 @@ def test_add_delete_es256_device(test_topic, capsys):
137146

138147
out, _ = capsys.readouterr()
139148
assert 'format : ES256_PEM' in out
149+
assert 'State: {' in out
140150

141151

142152
def test_add_patch_delete_rs256(test_topic, capsys):

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.