Demo application for using the AT&T IoT Starter Kit Powered by AWS.
Dependencies: SDFileSystem
Fork of ATT_AWS_IoT_demo by
IoT Starter Kit Powered by AWS Demo
This program demonstrates the AT&T IoT Starter Kit sending data directly into AWS IoT. It's explained and used in the Getting Started with the IoT Starter Kit Powered by AWS on starterkit.att.com.
What's required
- AT&T IoT LTE Add-on (also known as the Cellular Shield)
- NXP K64F - for programming
- microSD card - used to store your AWS security credentials
- AWS account
- Python, locally installed
If you don't already have an IoT Starter Kit, you can purchase a kit here. The IoT Starter Kit Powered by AWS includes the LTE cellular shield, K64F, and a microSD card.
PythonGUI/ATT_AWS_IoT_Demo_GUI.py@25:91d771247ac8, 2016-12-19 (annotated)
- Committer:
- ampembeng
- Date:
- Mon Dec 19 20:52:28 2016 +0000
- Revision:
- 25:91d771247ac8
- Parent:
- 20:ee34856ae510
Updated the look/feel of the Python GUI to match the AT&T locker demo S3 page. Added "assets" folder to support this. Updated the README.
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
ampembeng | 17:46780b2de3e4 | 1 | ''' |
ampembeng | 17:46780b2de3e4 | 2 | /* |
ampembeng | 17:46780b2de3e4 | 3 | * Copyright 2010-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. |
ampembeng | 17:46780b2de3e4 | 4 | * |
ampembeng | 17:46780b2de3e4 | 5 | * Licensed under the Apache License, Version 2.0 (the "License"). |
ampembeng | 17:46780b2de3e4 | 6 | * You may not use this file except in compliance with the License. |
ampembeng | 17:46780b2de3e4 | 7 | * A copy of the License is located at |
ampembeng | 17:46780b2de3e4 | 8 | * |
ampembeng | 17:46780b2de3e4 | 9 | * http://aws.amazon.com/apache2.0 |
ampembeng | 17:46780b2de3e4 | 10 | * |
ampembeng | 17:46780b2de3e4 | 11 | * or in the "license" file accompanying this file. This file is distributed |
ampembeng | 17:46780b2de3e4 | 12 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either |
ampembeng | 17:46780b2de3e4 | 13 | * express or implied. See the License for the specific language governing |
ampembeng | 17:46780b2de3e4 | 14 | * permissions and limitations under the License. |
ampembeng | 17:46780b2de3e4 | 15 | */ |
ampembeng | 17:46780b2de3e4 | 16 | ''' |
ampembeng | 17:46780b2de3e4 | 17 | |
ampembeng | 17:46780b2de3e4 | 18 | from AWSIoTPythonSDK.MQTTLib import AWSIoTMQTTShadowClient |
ampembeng | 17:46780b2de3e4 | 19 | import sys |
ampembeng | 17:46780b2de3e4 | 20 | import os.path |
ampembeng | 17:46780b2de3e4 | 21 | import logging |
ampembeng | 17:46780b2de3e4 | 22 | import time |
ampembeng | 17:46780b2de3e4 | 23 | import json |
ampembeng | 19:488dad1e168e | 24 | import re |
ampembeng | 17:46780b2de3e4 | 25 | import getopt |
ampembeng | 17:46780b2de3e4 | 26 | import Tkinter |
ampembeng | 17:46780b2de3e4 | 27 | import tkFont |
ampembeng | 17:46780b2de3e4 | 28 | import time |
ampembeng | 17:46780b2de3e4 | 29 | from threading import Timer |
ampembeng | 17:46780b2de3e4 | 30 | from Tkinter import * |
ampembeng | 25:91d771247ac8 | 31 | from PIL import Image, ImageTk |
ampembeng | 25:91d771247ac8 | 32 | |
ampembeng | 25:91d771247ac8 | 33 | exePath = os.path.dirname(os.path.realpath(__file__)) |
ampembeng | 25:91d771247ac8 | 34 | assetPath = exePath + "\\assets\\" |
ampembeng | 17:46780b2de3e4 | 35 | |
ampembeng | 17:46780b2de3e4 | 36 | ####################################################################################################################### |
ampembeng | 17:46780b2de3e4 | 37 | # |
ampembeng | 17:46780b2de3e4 | 38 | # Set up our Tkinter based GUI |
ampembeng | 17:46780b2de3e4 | 39 | # |
ampembeng | 17:46780b2de3e4 | 40 | ####################################################################################################################### |
ampembeng | 17:46780b2de3e4 | 41 | accepted = ["0", "1", "2", "4", "7"] |
ampembeng | 17:46780b2de3e4 | 42 | colorDict = {0 : 'off', 1 : 'red', 2 : 'green', 4 : "blue", 7 : "white"} |
ampembeng | 25:91d771247ac8 | 43 | ledImgPaths = {0 : "LEDOff.png", 1 : 'LEDRed.png', 2 : 'LEDGreen.png', 4 : "LEDBlue.png", 7 : "LEDWhite.png"} |
ampembeng | 17:46780b2de3e4 | 44 | |
ampembeng | 17:46780b2de3e4 | 45 | window = Tkinter.Tk() |
ampembeng | 25:91d771247ac8 | 46 | window.wm_title("AT&T AWS IoT Starter Kit Demo") |
ampembeng | 25:91d771247ac8 | 47 | window.iconbitmap(assetPath + "ATT_Icon.ico") |
ampembeng | 25:91d771247ac8 | 48 | window.resizable(width=False, height=False) |
ampembeng | 25:91d771247ac8 | 49 | |
ampembeng | 25:91d771247ac8 | 50 | buttonColor = 'dark slate gray' |
ampembeng | 25:91d771247ac8 | 51 | buttonTxtColor = 'tv white' |
ampembeng | 25:91d771247ac8 | 52 | headerBgColor = 'snow' |
ampembeng | 25:91d771247ac8 | 53 | bgColor = 'steel blue' |
ampembeng | 25:91d771247ac8 | 54 | fgTextColor = 'white' |
ampembeng | 17:46780b2de3e4 | 55 | |
ampembeng | 20:ee34856ae510 | 56 | # Setup frames |
ampembeng | 25:91d771247ac8 | 57 | def initFrames(_window, side, color=bgColor): |
ampembeng | 25:91d771247ac8 | 58 | frame = Frame(_window, background=color) |
ampembeng | 25:91d771247ac8 | 59 | frame.pack(fill=X, side=side) |
ampembeng | 25:91d771247ac8 | 60 | return frame |
ampembeng | 25:91d771247ac8 | 61 | |
ampembeng | 25:91d771247ac8 | 62 | headerframe = initFrames(window, TOP, headerBgColor) |
ampembeng | 25:91d771247ac8 | 63 | frameR1 = initFrames(window, TOP) |
ampembeng | 25:91d771247ac8 | 64 | frameR2 = initFrames(window, TOP) |
ampembeng | 25:91d771247ac8 | 65 | frameR3 = initFrames(window, TOP) |
ampembeng | 25:91d771247ac8 | 66 | frameR4 = initFrames(window, TOP) |
ampembeng | 25:91d771247ac8 | 67 | frameR5 = initFrames(window, TOP) |
ampembeng | 25:91d771247ac8 | 68 | frameR6 = initFrames(window, TOP) |
ampembeng | 25:91d771247ac8 | 69 | frameR7 = initFrames(window, TOP) |
ampembeng | 25:91d771247ac8 | 70 | frameR8 = initFrames(window, TOP) |
ampembeng | 25:91d771247ac8 | 71 | frameR9 = initFrames(window, TOP) |
ampembeng | 25:91d771247ac8 | 72 | buttonFrame = initFrames(window, BOTTOM) |
ampembeng | 25:91d771247ac8 | 73 | |
ampembeng | 25:91d771247ac8 | 74 | # So we can loop through our frames |
ampembeng | 25:91d771247ac8 | 75 | frameIndex = 0 |
ampembeng | 25:91d771247ac8 | 76 | frames = [headerframe, |
ampembeng | 25:91d771247ac8 | 77 | frameR1, frameR2, frameR3, frameR4, frameR5, |
ampembeng | 25:91d771247ac8 | 78 | frameR6, frameR7, frameR8, frameR9, |
ampembeng | 25:91d771247ac8 | 79 | buttonFrame] |
ampembeng | 25:91d771247ac8 | 80 | |
ampembeng | 25:91d771247ac8 | 81 | def curFrame(): |
ampembeng | 25:91d771247ac8 | 82 | return frames[frameIndex] |
ampembeng | 25:91d771247ac8 | 83 | |
ampembeng | 25:91d771247ac8 | 84 | def nextFrame(): |
ampembeng | 25:91d771247ac8 | 85 | global frameIndex |
ampembeng | 25:91d771247ac8 | 86 | frameIndex += 1 |
ampembeng | 25:91d771247ac8 | 87 | return frames[frameIndex] |
ampembeng | 17:46780b2de3e4 | 88 | |
ampembeng | 20:ee34856ae510 | 89 | # Label vars (controls shown text) |
ampembeng | 20:ee34856ae510 | 90 | ledStaticLabelTxt = StringVar() |
ampembeng | 20:ee34856ae510 | 91 | ledStatusLabelTxt = StringVar() |
ampembeng | 20:ee34856ae510 | 92 | tempStaticLabelTxt = StringVar() |
ampembeng | 20:ee34856ae510 | 93 | tempStatusLabelTxt = StringVar() |
ampembeng | 20:ee34856ae510 | 94 | humidStaticLabelTxt = StringVar() |
ampembeng | 20:ee34856ae510 | 95 | humidStatusLabelTxt = StringVar() |
ampembeng | 20:ee34856ae510 | 96 | versionStaticLabelTxt = StringVar() |
ampembeng | 20:ee34856ae510 | 97 | versionStatusLabelTxt = StringVar() |
ampembeng | 20:ee34856ae510 | 98 | versionLastValue = -1 |
ampembeng | 17:46780b2de3e4 | 99 | |
ampembeng | 17:46780b2de3e4 | 100 | # Control callbacks |
ampembeng | 20:ee34856ae510 | 101 | def setStatus(ledStatus, tempStatus, humidStatus, version): |
ampembeng | 20:ee34856ae510 | 102 | global versionLastValue |
ampembeng | 20:ee34856ae510 | 103 | |
ampembeng | 25:91d771247ac8 | 104 | ledStatusLabelTxt.set(colorDict[ledStatus]) |
ampembeng | 25:91d771247ac8 | 105 | updateLEDImage(ledImgPaths[ledStatus]) |
ampembeng | 20:ee34856ae510 | 106 | tempStatusLabelTxt.set(str(tempStatus) + " (F)") |
ampembeng | 20:ee34856ae510 | 107 | humidStatusLabelTxt.set(str(humidStatus) + " %") |
ampembeng | 20:ee34856ae510 | 108 | |
ampembeng | 20:ee34856ae510 | 109 | isStale = "" |
ampembeng | 20:ee34856ae510 | 110 | if (versionLastValue == version): |
ampembeng | 25:91d771247ac8 | 111 | isStale = " (stale data)" |
ampembeng | 17:46780b2de3e4 | 112 | else: |
ampembeng | 20:ee34856ae510 | 113 | versionLastValue = version |
ampembeng | 20:ee34856ae510 | 114 | versionStatusLabelTxt.set(str(version) + isStale) |
ampembeng | 17:46780b2de3e4 | 115 | |
ampembeng | 25:91d771247ac8 | 116 | def updateLEDImage(path): |
ampembeng | 25:91d771247ac8 | 117 | global ledPicture |
ampembeng | 25:91d771247ac8 | 118 | img2 = ImageTk.PhotoImage(Image.open(assetPath + path)) |
ampembeng | 25:91d771247ac8 | 119 | ledPicture.configure(image=img2) |
ampembeng | 25:91d771247ac8 | 120 | ledPicture.image = img2 |
ampembeng | 25:91d771247ac8 | 121 | |
ampembeng | 17:46780b2de3e4 | 122 | def offButtonCallBack(): |
ampembeng | 17:46780b2de3e4 | 123 | sendLEDRequest("0") |
ampembeng | 17:46780b2de3e4 | 124 | |
ampembeng | 17:46780b2de3e4 | 125 | def redButtonCallBack(): |
ampembeng | 17:46780b2de3e4 | 126 | sendLEDRequest("1") |
ampembeng | 17:46780b2de3e4 | 127 | |
ampembeng | 17:46780b2de3e4 | 128 | def greenButtonCallBack(): |
ampembeng | 17:46780b2de3e4 | 129 | sendLEDRequest("2") |
ampembeng | 17:46780b2de3e4 | 130 | |
ampembeng | 17:46780b2de3e4 | 131 | def blueButtonCallBack(): |
ampembeng | 17:46780b2de3e4 | 132 | sendLEDRequest("4") |
ampembeng | 17:46780b2de3e4 | 133 | |
ampembeng | 17:46780b2de3e4 | 134 | def whiteButtonCallBack(): |
ampembeng | 17:46780b2de3e4 | 135 | sendLEDRequest("7") |
ampembeng | 17:46780b2de3e4 | 136 | |
ampembeng | 25:91d771247ac8 | 137 | # Photos |
ampembeng | 25:91d771247ac8 | 138 | def initPhotoLabels(frame, photoName, color=bgColor, side=LEFT, pad=0): |
ampembeng | 25:91d771247ac8 | 139 | image = Image.open(assetPath + photoName) |
ampembeng | 25:91d771247ac8 | 140 | photo = ImageTk.PhotoImage(image) |
ampembeng | 25:91d771247ac8 | 141 | |
ampembeng | 25:91d771247ac8 | 142 | label = Label(frame, image=photo, background=color) |
ampembeng | 25:91d771247ac8 | 143 | label.image = photo # keep a reference! |
ampembeng | 25:91d771247ac8 | 144 | label.pack(side=side, padx=pad) |
ampembeng | 25:91d771247ac8 | 145 | return label |
ampembeng | 25:91d771247ac8 | 146 | |
ampembeng | 17:46780b2de3e4 | 147 | # Create our Labels |
ampembeng | 25:91d771247ac8 | 148 | headerLabel1 = initPhotoLabels(curFrame(), "attLogo.png", headerBgColor) |
ampembeng | 25:91d771247ac8 | 149 | headerLabel2 = initPhotoLabels(curFrame(), "awsLogo.png", headerBgColor, pad=10) |
ampembeng | 25:91d771247ac8 | 150 | |
ampembeng | 25:91d771247ac8 | 151 | starterKitBoxLabel = initPhotoLabels(nextFrame(), "starterKitBox.png") |
ampembeng | 25:91d771247ac8 | 152 | |
ampembeng | 25:91d771247ac8 | 153 | labelFont = tkFont.Font(family='Times', size=14, weight='bold') |
ampembeng | 25:91d771247ac8 | 154 | def initLargeTextLabels(frame, text): |
ampembeng | 25:91d771247ac8 | 155 | label = Label(frame, font=labelFont, text=text, anchor=W, relief=FLAT, fg=fgTextColor, background=bgColor) |
ampembeng | 25:91d771247ac8 | 156 | label.pack(side=LEFT) |
ampembeng | 25:91d771247ac8 | 157 | |
ampembeng | 25:91d771247ac8 | 158 | setLEDColorLabel = initLargeTextLabels(nextFrame(),"Reported Values") |
ampembeng | 20:ee34856ae510 | 159 | |
ampembeng | 25:91d771247ac8 | 160 | def initLabels(frame, lableVar, text): |
ampembeng | 25:91d771247ac8 | 161 | lableVar.set(text) |
ampembeng | 25:91d771247ac8 | 162 | label = Label(frame, textvariable=lableVar, anchor=W, relief=FLAT, fg=fgTextColor, background=bgColor) |
ampembeng | 25:91d771247ac8 | 163 | label.pack(side = LEFT) |
ampembeng | 25:91d771247ac8 | 164 | return label |
ampembeng | 25:91d771247ac8 | 165 | |
ampembeng | 25:91d771247ac8 | 166 | ledStaticLabel = initLabels(nextFrame(), ledStaticLabelTxt, "LED Color: ") |
ampembeng | 25:91d771247ac8 | 167 | ledStatusLabel = initLabels(curFrame(), ledStatusLabelTxt, "unknown (needs to sync)") |
ampembeng | 25:91d771247ac8 | 168 | |
ampembeng | 25:91d771247ac8 | 169 | tempStaticLabel = initLabels(nextFrame(), tempStaticLabelTxt, "Temperature: ") |
ampembeng | 25:91d771247ac8 | 170 | tempStatusLabel = initLabels(curFrame(), tempStatusLabelTxt, "unknown (needs to sync)") |
ampembeng | 17:46780b2de3e4 | 171 | |
ampembeng | 25:91d771247ac8 | 172 | humidStaticLabel = initLabels(nextFrame(), humidStaticLabelTxt, "Humidity: ") |
ampembeng | 25:91d771247ac8 | 173 | humidStatusLabel = initLabels(curFrame(), humidStatusLabelTxt, "unknown (needs to sync)") |
ampembeng | 25:91d771247ac8 | 174 | |
ampembeng | 25:91d771247ac8 | 175 | versionStaticLabel = initLabels(nextFrame(), versionStaticLabelTxt, "Shadow Version: ") |
ampembeng | 25:91d771247ac8 | 176 | versionStatusLabel = initLabels(curFrame(), versionStatusLabelTxt, "unknown (needs to sync)") |
ampembeng | 20:ee34856ae510 | 177 | |
ampembeng | 25:91d771247ac8 | 178 | lineLabel = initLargeTextLabels(nextFrame(), "_______________________________________") |
ampembeng | 25:91d771247ac8 | 179 | setLEDColorLabel = initLargeTextLabels(nextFrame(),"Set LED Color") |
ampembeng | 25:91d771247ac8 | 180 | |
ampembeng | 25:91d771247ac8 | 181 | starterKitBoardLabel = initPhotoLabels(nextFrame(), "startKitBoard.png", side=TOP) |
ampembeng | 25:91d771247ac8 | 182 | |
ampembeng | 25:91d771247ac8 | 183 | # Creates an LED label (image) that we can update. |
ampembeng | 25:91d771247ac8 | 184 | ledPicture = Label(starterKitBoardLabel, background=bgColor, relief=FLAT, width=10, height=12) |
ampembeng | 25:91d771247ac8 | 185 | ledPicture.place(relx=1.0, rely=1.0, x=(40-300), y=(85-245), anchor="se") |
ampembeng | 25:91d771247ac8 | 186 | updateLEDImage("LEDOff.png") |
ampembeng | 17:46780b2de3e4 | 187 | |
ampembeng | 17:46780b2de3e4 | 188 | # Create our Buttons |
ampembeng | 25:91d771247ac8 | 189 | def buttonInit(text, bg, command): |
ampembeng | 25:91d771247ac8 | 190 | return Tkinter.Button(buttonFrame, text =text, fg="snow", bg=bg, height=1, width=6, command=command) |
ampembeng | 25:91d771247ac8 | 191 | |
ampembeng | 25:91d771247ac8 | 192 | O = buttonInit("OFF", buttonColor, offButtonCallBack) |
ampembeng | 25:91d771247ac8 | 193 | R = buttonInit("RED", buttonColor, redButtonCallBack) |
ampembeng | 25:91d771247ac8 | 194 | G = buttonInit("GREEN", buttonColor, greenButtonCallBack) |
ampembeng | 25:91d771247ac8 | 195 | B = buttonInit("BLUE", buttonColor, blueButtonCallBack) |
ampembeng | 25:91d771247ac8 | 196 | W = buttonInit("WHITE", buttonColor, whiteButtonCallBack) |
ampembeng | 25:91d771247ac8 | 197 | ledButtons = [O, R, G, B, W] |
ampembeng | 17:46780b2de3e4 | 198 | |
ampembeng | 17:46780b2de3e4 | 199 | # Set font |
ampembeng | 25:91d771247ac8 | 200 | buttonFont = tkFont.Font(family='Arial', size=12, weight='bold') |
ampembeng | 25:91d771247ac8 | 201 | for button in ledButtons: |
ampembeng | 25:91d771247ac8 | 202 | button['font'] = buttonFont |
ampembeng | 17:46780b2de3e4 | 203 | |
ampembeng | 25:91d771247ac8 | 204 | buttonPadX = 5 |
ampembeng | 25:91d771247ac8 | 205 | for button in ledButtons: |
ampembeng | 25:91d771247ac8 | 206 | button.pack(side=LEFT, padx=buttonPadX) |
ampembeng | 25:91d771247ac8 | 207 | |
ampembeng | 25:91d771247ac8 | 208 | # NOTE: If you want to test the GUI layout without running the AWS code comment these lines in |
ampembeng | 25:91d771247ac8 | 209 | #window.mainloop() |
ampembeng | 25:91d771247ac8 | 210 | #exit(0) |
ampembeng | 17:46780b2de3e4 | 211 | |
ampembeng | 17:46780b2de3e4 | 212 | ####################################################################################################################### |
ampembeng | 17:46780b2de3e4 | 213 | # |
ampembeng | 17:46780b2de3e4 | 214 | # These functions are for AWS |
ampembeng | 17:46780b2de3e4 | 215 | # |
ampembeng | 17:46780b2de3e4 | 216 | ####################################################################################################################### |
ampembeng | 17:46780b2de3e4 | 217 | # Shadow JSON schema: |
ampembeng | 20:ee34856ae510 | 218 | ''' |
ampembeng | 20:ee34856ae510 | 219 | Name: AIT (AWS IoT Thing) |
ampembeng | 20:ee34856ae510 | 220 | { |
ampembeng | 20:ee34856ae510 | 221 | "state": { |
ampembeng | 20:ee34856ae510 | 222 | "desired": { |
ampembeng | 20:ee34856ae510 | 223 | "ledColor": <UINT8> |
ampembeng | 20:ee34856ae510 | 224 | }, |
ampembeng | 20:ee34856ae510 | 225 | "reported": { |
ampembeng | 20:ee34856ae510 | 226 | "ledColor": <UINT8>, |
ampembeng | 20:ee34856ae510 | 227 | "temperature": <FLOAT>, |
ampembeng | 20:ee34856ae510 | 228 | "humidity": <INT16> |
ampembeng | 20:ee34856ae510 | 229 | } |
ampembeng | 20:ee34856ae510 | 230 | } |
ampembeng | 20:ee34856ae510 | 231 | } |
ampembeng | 20:ee34856ae510 | 232 | ''' |
ampembeng | 17:46780b2de3e4 | 233 | |
ampembeng | 17:46780b2de3e4 | 234 | def sendLEDRequest(color_input): |
ampembeng | 17:46780b2de3e4 | 235 | if (color_input in accepted): |
ampembeng | 17:46780b2de3e4 | 236 | JSONPayload = '{"state":{"desired":{"ledColor":' + str(color_input) + '}}}' |
ampembeng | 20:ee34856ae510 | 237 | AIT.shadowUpdate(JSONPayload, customShadowCallback_Update, 5) |
ampembeng | 17:46780b2de3e4 | 238 | else: |
ampembeng | 17:46780b2de3e4 | 239 | print("WARN: Color input invalid - " + color_input) |
ampembeng | 17:46780b2de3e4 | 240 | |
ampembeng | 17:46780b2de3e4 | 241 | # Custom Shadow callback |
ampembeng | 17:46780b2de3e4 | 242 | def customShadowCallback_Delta(payload, responseStatus, token): |
ampembeng | 17:46780b2de3e4 | 243 | # payload is a JSON string ready to be parsed using json.loads(...) |
ampembeng | 17:46780b2de3e4 | 244 | # in both Py2.x and Py3.x |
ampembeng | 17:46780b2de3e4 | 245 | print(responseStatus) |
ampembeng | 17:46780b2de3e4 | 246 | payloadDict = json.loads(payload) |
ampembeng | 17:46780b2de3e4 | 247 | print("++++++++DELTA++++++++++") |
ampembeng | 17:46780b2de3e4 | 248 | print("ledColor: " + str(payloadDict["state"]["ledColor"])) |
ampembeng | 17:46780b2de3e4 | 249 | print("version: " + str(payloadDict["version"])) |
ampembeng | 17:46780b2de3e4 | 250 | print("+++++++++++++++++++++++\n\n") |
ampembeng | 17:46780b2de3e4 | 251 | |
ampembeng | 17:46780b2de3e4 | 252 | # Custom Shadow callback |
ampembeng | 17:46780b2de3e4 | 253 | def customShadowCallback_Update(payload, responseStatus, token): |
ampembeng | 17:46780b2de3e4 | 254 | # payload is a JSON string ready to be parsed using json.loads(...) |
ampembeng | 17:46780b2de3e4 | 255 | # in both Py2.x and Py3.x |
ampembeng | 17:46780b2de3e4 | 256 | if responseStatus == "timeout": |
ampembeng | 17:46780b2de3e4 | 257 | print("Update request " + token + " time out!") |
ampembeng | 17:46780b2de3e4 | 258 | if responseStatus == "accepted": |
ampembeng | 17:46780b2de3e4 | 259 | payloadDict = json.loads(payload) |
ampembeng | 17:46780b2de3e4 | 260 | print("~~~~~~~~~~~~~~~~~~~~~~~") |
ampembeng | 17:46780b2de3e4 | 261 | print("Update request with token: " + token + " accepted!") |
ampembeng | 17:46780b2de3e4 | 262 | print("ledColor: " + str(payloadDict["state"]["desired"]["ledColor"])) |
ampembeng | 17:46780b2de3e4 | 263 | print("~~~~~~~~~~~~~~~~~~~~~~~\n\n") |
ampembeng | 17:46780b2de3e4 | 264 | if responseStatus == "rejected": |
ampembeng | 17:46780b2de3e4 | 265 | print("Update request " + token + " rejected!") |
ampembeng | 17:46780b2de3e4 | 266 | |
ampembeng | 17:46780b2de3e4 | 267 | def customShadowCallback_Delete(payload, responseStatus, token): |
ampembeng | 17:46780b2de3e4 | 268 | if responseStatus == "timeout": |
ampembeng | 17:46780b2de3e4 | 269 | print("Delete request " + token + " time out!") |
ampembeng | 17:46780b2de3e4 | 270 | if responseStatus == "accepted": |
ampembeng | 17:46780b2de3e4 | 271 | print("~~~~~~~~~~~~~~~~~~~~~~~") |
ampembeng | 17:46780b2de3e4 | 272 | print("Delete request with token: " + token + " accepted!") |
ampembeng | 17:46780b2de3e4 | 273 | print("~~~~~~~~~~~~~~~~~~~~~~~\n\n") |
ampembeng | 17:46780b2de3e4 | 274 | if responseStatus == "rejected": |
ampembeng | 17:46780b2de3e4 | 275 | print("Delete request " + token + " rejected!") |
ampembeng | 17:46780b2de3e4 | 276 | |
ampembeng | 17:46780b2de3e4 | 277 | # Custom Shadow callback |
ampembeng | 17:46780b2de3e4 | 278 | def customShadowCallback_Get(payload, responseStatus, token): |
ampembeng | 17:46780b2de3e4 | 279 | print(responseStatus) |
ampembeng | 17:46780b2de3e4 | 280 | payloadDict = json.loads(payload) |
ampembeng | 17:46780b2de3e4 | 281 | reportedColor = payloadDict["state"]["reported"]["ledColor"] |
ampembeng | 20:ee34856ae510 | 282 | reportedTemp = payloadDict["state"]["reported"]["temperature"] |
ampembeng | 20:ee34856ae510 | 283 | reportedHumid = payloadDict["state"]["reported"]["humidity"] |
ampembeng | 20:ee34856ae510 | 284 | reportedVersion = payloadDict["version"] |
ampembeng | 17:46780b2de3e4 | 285 | print("++++++++GET++++++++++") |
ampembeng | 20:ee34856ae510 | 286 | print("ledColor : " + str(reportedColor) + " (" + colorDict[reportedColor] + ")") |
ampembeng | 20:ee34856ae510 | 287 | print("temperature: " + str(reportedTemp)) |
ampembeng | 20:ee34856ae510 | 288 | print("humidity : " + str(reportedHumid)) |
ampembeng | 20:ee34856ae510 | 289 | print("version: " + str(reportedVersion)) |
ampembeng | 17:46780b2de3e4 | 290 | print("+++++++++++++++++++++\n\n") |
ampembeng | 17:46780b2de3e4 | 291 | |
ampembeng | 17:46780b2de3e4 | 292 | # Send payload to our status |
ampembeng | 25:91d771247ac8 | 293 | setStatus(reportedColor, reportedTemp, reportedHumid, reportedVersion) |
ampembeng | 17:46780b2de3e4 | 294 | |
ampembeng | 17:46780b2de3e4 | 295 | getTimerAlive = TRUE |
ampembeng | 17:46780b2de3e4 | 296 | def customShadowTimer_Get(): |
ampembeng | 17:46780b2de3e4 | 297 | while (getTimerAlive): |
ampembeng | 20:ee34856ae510 | 298 | AIT.shadowGet(customShadowCallback_Get, 5) |
ampembeng | 17:46780b2de3e4 | 299 | time.sleep(3) |
ampembeng | 17:46780b2de3e4 | 300 | |
ampembeng | 17:46780b2de3e4 | 301 | ####################################################################################################################### |
ampembeng | 17:46780b2de3e4 | 302 | # |
ampembeng | 17:46780b2de3e4 | 303 | # Vars |
ampembeng | 17:46780b2de3e4 | 304 | # |
ampembeng | 17:46780b2de3e4 | 305 | ####################################################################################################################### |
ampembeng | 17:46780b2de3e4 | 306 | # Usage |
ampembeng | 17:46780b2de3e4 | 307 | usageInfo = """Usage: |
ampembeng | 17:46780b2de3e4 | 308 | |
ampembeng | 17:46780b2de3e4 | 309 | Use certificate based mutual authentication: |
ampembeng | 17:46780b2de3e4 | 310 | python basicShadowDeltaListener.py -e <endpoint> -r <rootCAFilePath> -c <certFilePath> -k <privateKeyFilePath> |
ampembeng | 17:46780b2de3e4 | 311 | |
ampembeng | 17:46780b2de3e4 | 312 | Use MQTT over WebSocket: |
ampembeng | 17:46780b2de3e4 | 313 | python basicShadowDeltaListener.py -e <endpoint> -r <rootCAFilePath> -w |
ampembeng | 17:46780b2de3e4 | 314 | |
ampembeng | 17:46780b2de3e4 | 315 | Type "python basicShadowDeltaListener.py -h" for available options. |
ampembeng | 17:46780b2de3e4 | 316 | |
ampembeng | 17:46780b2de3e4 | 317 | |
ampembeng | 17:46780b2de3e4 | 318 | """ |
ampembeng | 17:46780b2de3e4 | 319 | # Help info |
ampembeng | 17:46780b2de3e4 | 320 | helpInfo = """-e, --endpoint |
ampembeng | 17:46780b2de3e4 | 321 | Your AWS IoT custom endpoint |
ampembeng | 17:46780b2de3e4 | 322 | -r, --rootCA |
ampembeng | 17:46780b2de3e4 | 323 | Root CA file path |
ampembeng | 17:46780b2de3e4 | 324 | -c, --cert |
ampembeng | 17:46780b2de3e4 | 325 | Certificate file path |
ampembeng | 17:46780b2de3e4 | 326 | -k, --key |
ampembeng | 17:46780b2de3e4 | 327 | Private key file path |
ampembeng | 17:46780b2de3e4 | 328 | -w, --websocket |
ampembeng | 17:46780b2de3e4 | 329 | Use MQTT over WebSocket |
ampembeng | 17:46780b2de3e4 | 330 | -h, --help |
ampembeng | 17:46780b2de3e4 | 331 | Help information |
ampembeng | 17:46780b2de3e4 | 332 | |
ampembeng | 17:46780b2de3e4 | 333 | |
ampembeng | 17:46780b2de3e4 | 334 | """ |
ampembeng | 17:46780b2de3e4 | 335 | |
ampembeng | 17:46780b2de3e4 | 336 | ####################################################################################################################### |
ampembeng | 17:46780b2de3e4 | 337 | # |
ampembeng | 17:46780b2de3e4 | 338 | # AWS IoT Config Parameters. The user needs to enter these (they should match the parameters in aws_iot_config.h) |
ampembeng | 17:46780b2de3e4 | 339 | # |
ampembeng | 17:46780b2de3e4 | 340 | ####################################################################################################################### |
ampembeng | 19:488dad1e168e | 341 | useWebsocket = False # The FRDM-K64F demo isn't designed to work with web socket |
ampembeng | 19:488dad1e168e | 342 | hardCodeMQTT = False # Set this to true if you want to hard code the MQTT params below |
ampembeng | 19:488dad1e168e | 343 | |
ampembeng | 19:488dad1e168e | 344 | # AWS parameters |
ampembeng | 19:488dad1e168e | 345 | AWS_IOT_MQTT_HOST = "TODO" |
ampembeng | 17:46780b2de3e4 | 346 | AWS_IOT_MQTT_PORT = 8883 |
ampembeng | 19:488dad1e168e | 347 | AWS_IOT_MQTT_CLIENT_ID = "TODO" |
ampembeng | 19:488dad1e168e | 348 | AWS_IOT_MY_THING_NAME = "TODO" |
ampembeng | 19:488dad1e168e | 349 | AWS_MQTT_CONFIG_FILENAME = "C:/Temp/certs/mqtt_config.txt" |
ampembeng | 17:46780b2de3e4 | 350 | AWS_IOT_ROOT_CA_FILENAME = "C:/Temp/certs/rootCA-certificate.crt" |
ampembeng | 17:46780b2de3e4 | 351 | AWS_IOT_PRIVATE_KEY_FILENAME = "C:/Temp/certs/private.pem.key" |
ampembeng | 17:46780b2de3e4 | 352 | AWS_IOT_CERTIFICATE_FILENAME = "C:/Temp/certs/certificate.pem.crt" |
ampembeng | 17:46780b2de3e4 | 353 | |
ampembeng | 17:46780b2de3e4 | 354 | ####################################################################################################################### |
ampembeng | 17:46780b2de3e4 | 355 | # |
ampembeng | 19:488dad1e168e | 356 | # Arg Parser (TODO) |
ampembeng | 17:46780b2de3e4 | 357 | # |
ampembeng | 17:46780b2de3e4 | 358 | ####################################################################################################################### |
ampembeng | 17:46780b2de3e4 | 359 | ''' |
ampembeng | 17:46780b2de3e4 | 360 | try: |
ampembeng | 17:46780b2de3e4 | 361 | opts, args = getopt.getopt(sys.argv[1:], "hwe:k:c:r:", ["help", "endpoint=", "key=","cert=","rootCA=", "websocket"]) |
ampembeng | 17:46780b2de3e4 | 362 | if len(opts) == 0: |
ampembeng | 17:46780b2de3e4 | 363 | raise getopt.GetoptError("No input parameters!") |
ampembeng | 17:46780b2de3e4 | 364 | for opt, arg in opts: |
ampembeng | 17:46780b2de3e4 | 365 | if opt in ("-h", "--help"): |
ampembeng | 17:46780b2de3e4 | 366 | print(helpInfo) |
ampembeng | 17:46780b2de3e4 | 367 | exit(0) |
ampembeng | 17:46780b2de3e4 | 368 | if opt in ("-e", "--endpoint"): |
ampembeng | 17:46780b2de3e4 | 369 | AWS_IOT_MQTT_HOST = arg |
ampembeng | 17:46780b2de3e4 | 370 | if opt in ("-r", "--rootCA"): |
ampembeng | 17:46780b2de3e4 | 371 | AWS_IOT_ROOT_CA_FILENAME = arg |
ampembeng | 17:46780b2de3e4 | 372 | if opt in ("-c", "--cert"): |
ampembeng | 17:46780b2de3e4 | 373 | AWS_IOT_CERTIFICATE_FILENAME = arg |
ampembeng | 17:46780b2de3e4 | 374 | if opt in ("-k", "--key"): |
ampembeng | 17:46780b2de3e4 | 375 | AWS_IOT_PRIVATE_KEY_FILENAME = arg |
ampembeng | 17:46780b2de3e4 | 376 | if opt in ("-w", "--websocket"): |
ampembeng | 17:46780b2de3e4 | 377 | useWebsocket = True |
ampembeng | 17:46780b2de3e4 | 378 | except getopt.GetoptError: |
ampembeng | 17:46780b2de3e4 | 379 | print(usageInfo) |
ampembeng | 17:46780b2de3e4 | 380 | #exit(1) |
ampembeng | 17:46780b2de3e4 | 381 | |
ampembeng | 17:46780b2de3e4 | 382 | # Missing configuration notification |
ampembeng | 17:46780b2de3e4 | 383 | missingConfiguration = False |
ampembeng | 17:46780b2de3e4 | 384 | if not AWS_IOT_MQTT_HOST: |
ampembeng | 17:46780b2de3e4 | 385 | print("Missing '-e' or '--endpoint'") |
ampembeng | 17:46780b2de3e4 | 386 | missingConfiguration = True |
ampembeng | 17:46780b2de3e4 | 387 | if not AWS_IOT_ROOT_CA_FILENAME: |
ampembeng | 17:46780b2de3e4 | 388 | print("Missing '-r' or '--rootCA'") |
ampembeng | 17:46780b2de3e4 | 389 | missingConfiguration = True |
ampembeng | 17:46780b2de3e4 | 390 | if not useWebsocket: |
ampembeng | 17:46780b2de3e4 | 391 | if not AWS_IOT_CERTIFICATE_FILENAME: |
ampembeng | 17:46780b2de3e4 | 392 | print("Missing '-c' or '--cert'") |
ampembeng | 17:46780b2de3e4 | 393 | missingConfiguration = True |
ampembeng | 17:46780b2de3e4 | 394 | if not AWS_IOT_PRIVATE_KEY_FILENAME: |
ampembeng | 17:46780b2de3e4 | 395 | print("Missing '-k' or '--key'") |
ampembeng | 17:46780b2de3e4 | 396 | missingConfiguration = True |
ampembeng | 17:46780b2de3e4 | 397 | if missingConfiguration: |
ampembeng | 17:46780b2de3e4 | 398 | exit(2) |
ampembeng | 17:46780b2de3e4 | 399 | ''' |
ampembeng | 17:46780b2de3e4 | 400 | |
ampembeng | 17:46780b2de3e4 | 401 | # Configure logging |
ampembeng | 17:46780b2de3e4 | 402 | logger = logging.getLogger("AWSIoTPythonSDK.core") |
ampembeng | 17:46780b2de3e4 | 403 | logger.setLevel(logging.DEBUG) |
ampembeng | 17:46780b2de3e4 | 404 | streamHandler = logging.StreamHandler() |
ampembeng | 17:46780b2de3e4 | 405 | formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') |
ampembeng | 17:46780b2de3e4 | 406 | streamHandler.setFormatter(formatter) |
ampembeng | 17:46780b2de3e4 | 407 | logger.addHandler(streamHandler) |
ampembeng | 17:46780b2de3e4 | 408 | |
ampembeng | 17:46780b2de3e4 | 409 | ####################################################################################################################### |
ampembeng | 17:46780b2de3e4 | 410 | # |
ampembeng | 17:46780b2de3e4 | 411 | # Main Code |
ampembeng | 17:46780b2de3e4 | 412 | # |
ampembeng | 17:46780b2de3e4 | 413 | ####################################################################################################################### |
ampembeng | 19:488dad1e168e | 414 | # This block parses the MQTT file. |
ampembeng | 19:488dad1e168e | 415 | ''' Example format for mqtt_config.txt: |
ampembeng | 19:488dad1e168e | 416 | AWS_IOT_MQTT_HOST=1234asdf.iot.us-west-2.amazonaws.com |
ampembeng | 19:488dad1e168e | 417 | AWS_IOT_MQTT_PORT=8883 |
ampembeng | 19:488dad1e168e | 418 | AWS_IOT_MQTT_CLIENT_ID=MyThingName |
ampembeng | 19:488dad1e168e | 419 | WS_IOT_MY_THING_NAME=MyThingName |
ampembeng | 19:488dad1e168e | 420 | ''' |
ampembeng | 19:488dad1e168e | 421 | |
ampembeng | 19:488dad1e168e | 422 | if (not hardCodeMQTT): |
ampembeng | 19:488dad1e168e | 423 | if not os.path.exists(AWS_MQTT_CONFIG_FILENAME): |
ampembeng | 19:488dad1e168e | 424 | print "ERROR: MQTT Config file not found, " + AWS_MQTT_CONFIG_FILENAME |
ampembeng | 19:488dad1e168e | 425 | exit(1) |
ampembeng | 19:488dad1e168e | 426 | |
ampembeng | 19:488dad1e168e | 427 | mqtt_file = open(AWS_MQTT_CONFIG_FILENAME) |
ampembeng | 19:488dad1e168e | 428 | mqtt_tokens = re.split('=|\n', mqtt_file.read()) |
ampembeng | 19:488dad1e168e | 429 | print mqtt_tokens |
ampembeng | 19:488dad1e168e | 430 | |
ampembeng | 19:488dad1e168e | 431 | if (len(mqtt_tokens) != 8): |
ampembeng | 19:488dad1e168e | 432 | print "ERROR: Detected incorrect MQTT file format" |
ampembeng | 19:488dad1e168e | 433 | exit(1) |
ampembeng | 19:488dad1e168e | 434 | |
ampembeng | 19:488dad1e168e | 435 | index = 0 |
ampembeng | 19:488dad1e168e | 436 | for token in mqtt_tokens: |
ampembeng | 19:488dad1e168e | 437 | if (token == "AWS_IOT_MQTT_HOST"): |
ampembeng | 19:488dad1e168e | 438 | AWS_IOT_MQTT_HOST = mqtt_tokens[index+1] |
ampembeng | 19:488dad1e168e | 439 | if (token == "AWS_IOT_MQTT_PORT"): |
ampembeng | 19:488dad1e168e | 440 | AWS_IOT_MQTT_PORT = int(mqtt_tokens[index + 1]) |
ampembeng | 19:488dad1e168e | 441 | if (token == "AWS_IOT_MQTT_CLIENT_ID"): |
ampembeng | 19:488dad1e168e | 442 | AWS_IOT_MQTT_CLIENT_ID = mqtt_tokens[index + 1] |
ampembeng | 19:488dad1e168e | 443 | if (token == "AWS_IOT_MY_THING_NAME"): |
ampembeng | 19:488dad1e168e | 444 | AWS_IOT_MY_THING_NAME = mqtt_tokens[index+1] |
ampembeng | 19:488dad1e168e | 445 | |
ampembeng | 19:488dad1e168e | 446 | index += 1 |
ampembeng | 19:488dad1e168e | 447 | |
ampembeng | 19:488dad1e168e | 448 | print "MQTT Params:" |
ampembeng | 19:488dad1e168e | 449 | print "AWS_IOT_MQTT_HOST = " + AWS_IOT_MQTT_HOST |
ampembeng | 19:488dad1e168e | 450 | print "AWS_IOT_MQTT_PORT = " + str(AWS_IOT_MQTT_PORT) |
ampembeng | 19:488dad1e168e | 451 | print "AWS_IOT_MQTT_CLIENT_ID = " + AWS_IOT_MQTT_CLIENT_ID |
ampembeng | 19:488dad1e168e | 452 | print "AWS_IOT_MY_THING_NAME = " + AWS_IOT_MY_THING_NAME |
ampembeng | 19:488dad1e168e | 453 | |
ampembeng | 19:488dad1e168e | 454 | # Makes sure cert/key files exist |
ampembeng | 17:46780b2de3e4 | 455 | if not os.path.exists(AWS_IOT_ROOT_CA_FILENAME): |
ampembeng | 17:46780b2de3e4 | 456 | print "ERROR: Root CA file not found, " + AWS_IOT_ROOT_CA_FILENAME |
ampembeng | 17:46780b2de3e4 | 457 | exit(1) |
ampembeng | 17:46780b2de3e4 | 458 | if not os.path.exists(AWS_IOT_PRIVATE_KEY_FILENAME): |
ampembeng | 17:46780b2de3e4 | 459 | print "ERROR: Private Key file not found, " + AWS_IOT_PRIVATE_KEY_FILENAME |
ampembeng | 17:46780b2de3e4 | 460 | exit(1) |
ampembeng | 17:46780b2de3e4 | 461 | if not os.path.exists(AWS_IOT_CERTIFICATE_FILENAME): |
ampembeng | 17:46780b2de3e4 | 462 | print "ERROR: AWS IOT cert file not found, " + AWS_IOT_CERTIFICATE_FILENAME |
ampembeng | 17:46780b2de3e4 | 463 | exit(1) |
ampembeng | 17:46780b2de3e4 | 464 | |
ampembeng | 17:46780b2de3e4 | 465 | # Init AWSIoTMQTTShadowClient |
ampembeng | 17:46780b2de3e4 | 466 | myAWSIoTMQTTShadowClient = None |
ampembeng | 17:46780b2de3e4 | 467 | if useWebsocket: |
ampembeng | 17:46780b2de3e4 | 468 | myAWSIoTMQTTShadowClient = AWSIoTMQTTShadowClient("basicShadowDeltaListener", useWebsocket=True) |
ampembeng | 17:46780b2de3e4 | 469 | myAWSIoTMQTTShadowClient.configureEndpoint(AWS_IOT_MQTT_HOST, 443) |
ampembeng | 17:46780b2de3e4 | 470 | myAWSIoTMQTTShadowClient.configureCredentials(AWS_IOT_ROOT_CA_FILENAME) |
ampembeng | 17:46780b2de3e4 | 471 | else: |
ampembeng | 17:46780b2de3e4 | 472 | myAWSIoTMQTTShadowClient = AWSIoTMQTTShadowClient("basicShadowDeltaListener") |
ampembeng | 17:46780b2de3e4 | 473 | myAWSIoTMQTTShadowClient.configureEndpoint(AWS_IOT_MQTT_HOST, AWS_IOT_MQTT_PORT) |
ampembeng | 17:46780b2de3e4 | 474 | myAWSIoTMQTTShadowClient.configureCredentials(AWS_IOT_ROOT_CA_FILENAME, AWS_IOT_PRIVATE_KEY_FILENAME, AWS_IOT_CERTIFICATE_FILENAME) |
ampembeng | 17:46780b2de3e4 | 475 | |
ampembeng | 17:46780b2de3e4 | 476 | # AWSIoTMQTTShadowClient configuration |
ampembeng | 17:46780b2de3e4 | 477 | myAWSIoTMQTTShadowClient.configureAutoReconnectBackoffTime(1, 32, 20) |
ampembeng | 17:46780b2de3e4 | 478 | myAWSIoTMQTTShadowClient.configureConnectDisconnectTimeout(10) # 10 sec |
ampembeng | 17:46780b2de3e4 | 479 | myAWSIoTMQTTShadowClient.configureMQTTOperationTimeout(5) # 5 sec |
ampembeng | 17:46780b2de3e4 | 480 | |
ampembeng | 17:46780b2de3e4 | 481 | # Connect to AWS IoT |
ampembeng | 17:46780b2de3e4 | 482 | myAWSIoTMQTTShadowClient.connect() |
ampembeng | 17:46780b2de3e4 | 483 | |
ampembeng | 17:46780b2de3e4 | 484 | # Create a deviceShadow with persistent subscription |
ampembeng | 20:ee34856ae510 | 485 | AIT = myAWSIoTMQTTShadowClient.createShadowHandlerWithName(AWS_IOT_MY_THING_NAME, True) |
ampembeng | 17:46780b2de3e4 | 486 | |
ampembeng | 17:46780b2de3e4 | 487 | # Listen on deltas |
ampembeng | 20:ee34856ae510 | 488 | AIT.shadowRegisterDeltaCallback(customShadowCallback_Delta) |
ampembeng | 17:46780b2de3e4 | 489 | |
ampembeng | 17:46780b2de3e4 | 490 | # Delete shadow JSON doc |
ampembeng | 20:ee34856ae510 | 491 | #AIT.shadowDelete(customShadowCallback_Delete, 5) |
ampembeng | 17:46780b2de3e4 | 492 | |
ampembeng | 25:91d771247ac8 | 493 | # NOTE: We make this slightly slower than the target loop (to prevent 'stale data') |
ampembeng | 25:91d771247ac8 | 494 | getTimer = Timer(3.5, customShadowTimer_Get, ()) |
ampembeng | 17:46780b2de3e4 | 495 | getTimer.start() |
ampembeng | 17:46780b2de3e4 | 496 | |
ampembeng | 17:46780b2de3e4 | 497 | # GUI loop (loops until closed) |
ampembeng | 17:46780b2de3e4 | 498 | window.mainloop() |
ampembeng | 17:46780b2de3e4 | 499 | |
ampembeng | 17:46780b2de3e4 | 500 | # Kill our timer after the GUI closes |
ampembeng | 17:46780b2de3e4 | 501 | getTimerAlive = FALSE |
ampembeng | 19:488dad1e168e | 502 | getTimer.cancel() |