V.06 11/3

Dependencies:   FT6206 SDFileSystem SPI_TFT_ILI9341 TFT_fonts

Fork of ATT_AWS_IoT_demo by attiot

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers ATT_AWS_IoT_Demo_GUI.py Source File

ATT_AWS_IoT_Demo_GUI.py

00001 '''
00002 /*
00003  * Copyright 2010-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
00004  *
00005  * Licensed under the Apache License, Version 2.0 (the "License").
00006  * You may not use this file except in compliance with the License.
00007  * A copy of the License is located at
00008  *
00009  *  http://aws.amazon.com/apache2.0
00010  *
00011  * or in the "license" file accompanying this file. This file is distributed
00012  * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
00013  * express or implied. See the License for the specific language governing
00014  * permissions and limitations under the License.
00015  */
00016  '''
00017 
00018 from AWSIoTPythonSDK.MQTTLib import AWSIoTMQTTShadowClient
00019 import sys
00020 import os.path
00021 import logging
00022 import time
00023 import json
00024 import re
00025 import getopt
00026 import Tkinter
00027 import tkFont
00028 import time
00029 from threading import Timer
00030 from Tkinter import *
00031 from PIL import Image, ImageTk
00032 
00033 exePath = os.path.dirname(os.path.realpath(__file__))
00034 assetPath = exePath + "\\assets\\"
00035 
00036 #######################################################################################################################
00037 #
00038 # Set up our Tkinter based GUI
00039 #
00040 #######################################################################################################################
00041 accepted = ["0", "1", "2", "4", "7"]
00042 colorDict = {0 : 'off', 1 : 'red', 2 : 'green', 4 : "blue", 7 : "white"}
00043 ledImgPaths = {0 : "LEDOff.png", 1 : 'LEDRed.png', 2 : 'LEDGreen.png', 4 : "LEDBlue.png", 7 : "LEDWhite.png"}
00044 
00045 window = Tkinter.Tk()
00046 window.wm_title("AT&T AWS IoT Starter Kit Demo")
00047 window.iconbitmap(assetPath + "ATT_Icon.ico")
00048 window.resizable(width=False, height=False)
00049 
00050 buttonColor = 'dark slate gray'
00051 buttonTxtColor = 'tv white'
00052 headerBgColor = 'snow'
00053 bgColor = 'steel blue'
00054 fgTextColor = 'white'
00055 
00056 # Setup frames
00057 def initFrames(_window, side, color=bgColor):
00058     frame = Frame(_window, background=color)
00059     frame.pack(fill=X, side=side)
00060     return frame
00061 
00062 headerframe = initFrames(window, TOP, headerBgColor)
00063 frameR1 = initFrames(window, TOP)
00064 frameR2 = initFrames(window, TOP)
00065 frameR3 = initFrames(window, TOP)
00066 frameR4 = initFrames(window, TOP)
00067 frameR5 = initFrames(window, TOP)
00068 frameR6 = initFrames(window, TOP)
00069 frameR7 = initFrames(window, TOP)
00070 frameR8 = initFrames(window, TOP)
00071 frameR9 = initFrames(window, TOP)
00072 buttonFrame = initFrames(window, BOTTOM)
00073 
00074 # So we can loop through our frames
00075 frameIndex = 0
00076 frames = [headerframe,
00077           frameR1, frameR2, frameR3, frameR4, frameR5,
00078           frameR6, frameR7, frameR8, frameR9,
00079           buttonFrame]
00080 
00081 def curFrame():
00082     return frames[frameIndex]
00083 
00084 def nextFrame():
00085     global frameIndex
00086     frameIndex += 1
00087     return frames[frameIndex]
00088 
00089 # Label vars (controls shown text)
00090 ledStaticLabelTxt = StringVar()
00091 ledStatusLabelTxt = StringVar()
00092 tempStaticLabelTxt = StringVar()
00093 tempStatusLabelTxt = StringVar()
00094 humidStaticLabelTxt = StringVar()
00095 humidStatusLabelTxt = StringVar()
00096 versionStaticLabelTxt = StringVar()
00097 versionStatusLabelTxt = StringVar()
00098 versionLastValue = -1
00099 
00100 # Control callbacks
00101 def setStatus(ledStatus, tempStatus, humidStatus, version):
00102     global versionLastValue
00103 
00104     ledStatusLabelTxt.set(colorDict[ledStatus])
00105     updateLEDImage(ledImgPaths[ledStatus])
00106     tempStatusLabelTxt.set(str(tempStatus) + " (F)")
00107     humidStatusLabelTxt.set(str(humidStatus) + " %")
00108 
00109     isStale = ""
00110     if (versionLastValue == version):
00111         isStale = " (stale data)"
00112     else:
00113         versionLastValue = version
00114     versionStatusLabelTxt.set(str(version) + isStale)
00115 
00116 def updateLEDImage(path):
00117     global ledPicture
00118     img2 = ImageTk.PhotoImage(Image.open(assetPath + path))
00119     ledPicture.configure(image=img2)
00120     ledPicture.image = img2
00121 
00122 def offButtonCallBack():
00123     sendLEDRequest("0")
00124 
00125 def redButtonCallBack():
00126     sendLEDRequest("1")
00127 
00128 def greenButtonCallBack():
00129     sendLEDRequest("2")
00130 
00131 def blueButtonCallBack():
00132     sendLEDRequest("4")
00133 
00134 def whiteButtonCallBack():
00135     sendLEDRequest("7")
00136 
00137 # Photos
00138 def initPhotoLabels(frame, photoName, color=bgColor, side=LEFT, pad=0):
00139     image = Image.open(assetPath + photoName)
00140     photo = ImageTk.PhotoImage(image)
00141 
00142     label = Label(frame, image=photo, background=color)
00143     label.image = photo  # keep a reference!
00144     label.pack(side=side, padx=pad)
00145     return label
00146 
00147 # Create our Labels
00148 headerLabel1 = initPhotoLabels(curFrame(), "attLogo.png", headerBgColor)
00149 headerLabel2 = initPhotoLabels(curFrame(), "awsLogo.png", headerBgColor, pad=10)
00150 
00151 starterKitBoxLabel = initPhotoLabels(nextFrame(), "starterKitBox.png")
00152 
00153 labelFont = tkFont.Font(family='Times', size=14, weight='bold')
00154 def initLargeTextLabels(frame, text):
00155     label = Label(frame, font=labelFont, text=text, anchor=W, relief=FLAT, fg=fgTextColor, background=bgColor)
00156     label.pack(side=LEFT)
00157 
00158 setLEDColorLabel = initLargeTextLabels(nextFrame(),"Reported Values")
00159 
00160 def initLabels(frame, lableVar, text):
00161     lableVar.set(text)
00162     label = Label(frame, textvariable=lableVar, anchor=W, relief=FLAT, fg=fgTextColor, background=bgColor)
00163     label.pack(side = LEFT)
00164     return label
00165 
00166 ledStaticLabel = initLabels(nextFrame(), ledStaticLabelTxt, "LED Color: ")
00167 ledStatusLabel = initLabels(curFrame(), ledStatusLabelTxt, "unknown (needs to sync)")
00168 
00169 tempStaticLabel = initLabels(nextFrame(), tempStaticLabelTxt, "Temperature: ")
00170 tempStatusLabel = initLabels(curFrame(), tempStatusLabelTxt, "unknown (needs to sync)")
00171 
00172 humidStaticLabel = initLabels(nextFrame(), humidStaticLabelTxt, "Humidity: ")
00173 humidStatusLabel = initLabels(curFrame(), humidStatusLabelTxt, "unknown (needs to sync)")
00174 
00175 versionStaticLabel = initLabels(nextFrame(), versionStaticLabelTxt, "Shadow Version: ")
00176 versionStatusLabel = initLabels(curFrame(), versionStatusLabelTxt, "unknown (needs to sync)")
00177 
00178 lineLabel = initLargeTextLabels(nextFrame(), "_______________________________________")
00179 setLEDColorLabel = initLargeTextLabels(nextFrame(),"Set LED Color")
00180 
00181 starterKitBoardLabel = initPhotoLabels(nextFrame(), "startKitBoard.png", side=TOP)
00182 
00183 # Creates an LED label (image) that we can update.
00184 ledPicture = Label(starterKitBoardLabel, background=bgColor, relief=FLAT, width=10, height=12)
00185 ledPicture.place(relx=1.0, rely=1.0, x=(40-300), y=(85-245), anchor="se")
00186 updateLEDImage("LEDOff.png")
00187 
00188 # Create our Buttons
00189 def buttonInit(text, bg, command):
00190     return Tkinter.Button(buttonFrame, text =text, fg="snow", bg=bg, height=1, width=6, command=command)
00191 
00192 O = buttonInit("OFF", buttonColor, offButtonCallBack)
00193 R = buttonInit("RED", buttonColor, redButtonCallBack)
00194 G = buttonInit("GREEN", buttonColor, greenButtonCallBack)
00195 B = buttonInit("BLUE", buttonColor, blueButtonCallBack)
00196 W = buttonInit("WHITE", buttonColor, whiteButtonCallBack)
00197 ledButtons = [O, R, G, B, W]
00198 
00199 # Set font
00200 buttonFont = tkFont.Font(family='Arial', size=12, weight='bold')
00201 for button in ledButtons:
00202     button['font'] = buttonFont
00203 
00204 buttonPadX = 5
00205 for button in ledButtons:
00206     button.pack(side=LEFT, padx=buttonPadX)
00207 
00208 # NOTE: If you want to test the GUI layout without running the AWS code comment these lines in
00209 #window.mainloop()
00210 #exit(0)
00211 
00212 #######################################################################################################################
00213 #
00214 # These functions are for AWS
00215 #
00216 #######################################################################################################################
00217 # Shadow JSON schema:
00218 '''
00219 Name: AIT (AWS IoT Thing)
00220 {
00221     "state": {
00222         "desired": {
00223             "ledColor": <UINT8>
00224         },
00225             "reported": {
00226             "ledColor": <UINT8>,
00227             "temperature": <FLOAT>,
00228             "humidity": <INT16>
00229         }
00230     }
00231 }
00232 '''
00233 
00234 def sendLEDRequest(color_input):
00235     if (color_input in accepted):
00236         JSONPayload = '{"state":{"desired":{"ledColor":' + str(color_input) + '}}}'
00237         AIT.shadowUpdate(JSONPayload, customShadowCallback_Update, 5)
00238     else:
00239         print("WARN: Color input invalid - " + color_input)
00240 
00241 # Custom Shadow callback
00242 def customShadowCallback_Delta(payload, responseStatus, token):
00243     # payload is a JSON string ready to be parsed using json.loads(...)
00244     # in both Py2.x and Py3.x
00245     print(responseStatus)
00246     payloadDict = json.loads(payload)
00247     print("++++++++DELTA++++++++++")
00248     print("ledColor: " + str(payloadDict["state"]["ledColor"]))
00249     print("version: " + str(payloadDict["version"]))
00250     print("+++++++++++++++++++++++\n\n")
00251 
00252 # Custom Shadow callback
00253 def customShadowCallback_Update(payload, responseStatus, token):
00254     # payload is a JSON string ready to be parsed using json.loads(...)
00255     # in both Py2.x and Py3.x
00256     if responseStatus == "timeout":
00257         print("Update request " + token + " time out!")
00258     if responseStatus == "accepted":
00259         payloadDict = json.loads(payload)
00260         print("~~~~~~~~~~~~~~~~~~~~~~~")
00261         print("Update request with token: " + token + " accepted!")
00262         print("ledColor: " + str(payloadDict["state"]["desired"]["ledColor"]))
00263         print("~~~~~~~~~~~~~~~~~~~~~~~\n\n")
00264     if responseStatus == "rejected":
00265         print("Update request " + token + " rejected!")
00266 
00267 def customShadowCallback_Delete(payload, responseStatus, token):
00268     if responseStatus == "timeout":
00269         print("Delete request " + token + " time out!")
00270     if responseStatus == "accepted":
00271         print("~~~~~~~~~~~~~~~~~~~~~~~")
00272         print("Delete request with token: " + token + " accepted!")
00273         print("~~~~~~~~~~~~~~~~~~~~~~~\n\n")
00274     if responseStatus == "rejected":
00275         print("Delete request " + token + " rejected!")
00276 
00277 # Custom Shadow callback
00278 def customShadowCallback_Get(payload, responseStatus, token):
00279     print(responseStatus)
00280     payloadDict = json.loads(payload)
00281     reportedColor = payloadDict["state"]["reported"]["ledColor"]
00282     reportedTemp = payloadDict["state"]["reported"]["temperature"]
00283     reportedHumid = payloadDict["state"]["reported"]["humidity"]
00284     reportedVersion = payloadDict["version"]
00285     print("++++++++GET++++++++++")
00286     print("ledColor   : " + str(reportedColor) + " (" + colorDict[reportedColor] + ")")
00287     print("temperature: " + str(reportedTemp))
00288     print("humidity   : " + str(reportedHumid))
00289     print("version: " + str(reportedVersion))
00290     print("+++++++++++++++++++++\n\n")
00291 
00292     # Send payload to our status
00293     setStatus(reportedColor, reportedTemp, reportedHumid, reportedVersion)
00294 
00295 getTimerAlive = TRUE
00296 def customShadowTimer_Get():
00297     while (getTimerAlive):
00298         AIT.shadowGet(customShadowCallback_Get, 5)
00299         time.sleep(3)
00300 
00301 #######################################################################################################################
00302 #
00303 # Vars
00304 #
00305 #######################################################################################################################
00306 # Usage
00307 usageInfo = """Usage:
00308 
00309 Use certificate based mutual authentication:
00310 python basicShadowDeltaListener.py -e <endpoint> -r <rootCAFilePath> -c <certFilePath> -k <privateKeyFilePath>
00311 
00312 Use MQTT over WebSocket:
00313 python basicShadowDeltaListener.py -e <endpoint> -r <rootCAFilePath> -w
00314 
00315 Type "python basicShadowDeltaListener.py -h" for available options.
00316 
00317 
00318 """
00319 # Help info
00320 helpInfo = """-e, --endpoint
00321     Your AWS IoT custom endpoint
00322 -r, --rootCA
00323     Root CA file path
00324 -c, --cert
00325     Certificate file path
00326 -k, --key
00327     Private key file path
00328 -w, --websocket
00329     Use MQTT over WebSocket
00330 -h, --help
00331     Help information
00332 
00333 
00334 """
00335 
00336 #######################################################################################################################
00337 #
00338 # AWS IoT Config Parameters.  The user needs to enter these (they should match the parameters in aws_iot_config.h)
00339 #
00340 #######################################################################################################################
00341 useWebsocket = False # The FRDM-K64F demo isn't designed to work with web socket
00342 hardCodeMQTT = False # Set this to true if you want to hard code the MQTT params below
00343 
00344 # AWS parameters
00345 AWS_IOT_MQTT_HOST      = "TODO"
00346 AWS_IOT_MQTT_PORT      = 8883
00347 AWS_IOT_MQTT_CLIENT_ID = "TODO"
00348 AWS_IOT_MY_THING_NAME  = "TODO"
00349 AWS_MQTT_CONFIG_FILENAME     = "C:/Temp/certs/mqtt_config.txt"
00350 AWS_IOT_ROOT_CA_FILENAME     = "C:/Temp/certs/rootCA-certificate.crt"
00351 AWS_IOT_PRIVATE_KEY_FILENAME = "C:/Temp/certs/private.pem.key"
00352 AWS_IOT_CERTIFICATE_FILENAME = "C:/Temp/certs/certificate.pem.crt"
00353 
00354 #######################################################################################################################
00355 #
00356 # Arg Parser (TODO)
00357 #
00358 #######################################################################################################################
00359 '''
00360 try:
00361     opts, args = getopt.getopt(sys.argv[1:], "hwe:k:c:r:", ["help", "endpoint=", "key=","cert=","rootCA=", "websocket"])
00362     if len(opts) == 0:
00363         raise getopt.GetoptError("No input parameters!")
00364     for opt, arg in opts:
00365         if opt in ("-h", "--help"):
00366             print(helpInfo)
00367             exit(0)
00368         if opt in ("-e", "--endpoint"):
00369             AWS_IOT_MQTT_HOST = arg
00370         if opt in ("-r", "--rootCA"):
00371             AWS_IOT_ROOT_CA_FILENAME = arg
00372         if opt in ("-c", "--cert"):
00373             AWS_IOT_CERTIFICATE_FILENAME = arg
00374         if opt in ("-k", "--key"):
00375             AWS_IOT_PRIVATE_KEY_FILENAME = arg
00376         if opt in ("-w", "--websocket"):
00377             useWebsocket = True
00378 except getopt.GetoptError:
00379     print(usageInfo)
00380     #exit(1)
00381 
00382 # Missing configuration notification
00383 missingConfiguration = False
00384 if not AWS_IOT_MQTT_HOST:
00385     print("Missing '-e' or '--endpoint'")
00386     missingConfiguration = True
00387 if not AWS_IOT_ROOT_CA_FILENAME:
00388     print("Missing '-r' or '--rootCA'")
00389     missingConfiguration = True
00390 if not useWebsocket:
00391     if not AWS_IOT_CERTIFICATE_FILENAME:
00392         print("Missing '-c' or '--cert'")
00393         missingConfiguration = True
00394     if not AWS_IOT_PRIVATE_KEY_FILENAME:
00395         print("Missing '-k' or '--key'")
00396         missingConfiguration = True
00397 if missingConfiguration:
00398     exit(2)
00399 '''
00400 
00401 # Configure logging
00402 logger = logging.getLogger("AWSIoTPythonSDK.core")
00403 logger.setLevel(logging.DEBUG)
00404 streamHandler = logging.StreamHandler()
00405 formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
00406 streamHandler.setFormatter(formatter)
00407 logger.addHandler(streamHandler)
00408 
00409 #######################################################################################################################
00410 #
00411 # Main Code
00412 #
00413 #######################################################################################################################
00414 # This block parses the MQTT file.
00415 ''' Example format for mqtt_config.txt:
00416 AWS_IOT_MQTT_HOST=1234asdf.iot.us-west-2.amazonaws.com
00417 AWS_IOT_MQTT_PORT=8883
00418 AWS_IOT_MQTT_CLIENT_ID=MyThingName
00419 WS_IOT_MY_THING_NAME=MyThingName
00420 '''
00421 
00422 if (not hardCodeMQTT):
00423     if not os.path.exists(AWS_MQTT_CONFIG_FILENAME):
00424         print "ERROR: MQTT Config file not found, " + AWS_MQTT_CONFIG_FILENAME
00425         exit(1)
00426 
00427     mqtt_file = open(AWS_MQTT_CONFIG_FILENAME)
00428     mqtt_tokens = re.split('=|\n', mqtt_file.read())
00429     print mqtt_tokens
00430 
00431     if (len(mqtt_tokens) != 8):
00432         print "ERROR: Detected incorrect MQTT file format"
00433         exit(1)
00434 
00435     index = 0
00436     for token in mqtt_tokens:
00437         if (token == "AWS_IOT_MQTT_HOST"):
00438             AWS_IOT_MQTT_HOST = mqtt_tokens[index+1]
00439         if (token == "AWS_IOT_MQTT_PORT"):
00440             AWS_IOT_MQTT_PORT = int(mqtt_tokens[index + 1])
00441         if (token == "AWS_IOT_MQTT_CLIENT_ID"):
00442             AWS_IOT_MQTT_CLIENT_ID = mqtt_tokens[index + 1]
00443         if (token == "AWS_IOT_MY_THING_NAME"):
00444             AWS_IOT_MY_THING_NAME = mqtt_tokens[index+1]
00445 
00446         index += 1
00447 
00448     print "MQTT Params:"
00449     print "AWS_IOT_MQTT_HOST = " + AWS_IOT_MQTT_HOST
00450     print "AWS_IOT_MQTT_PORT = " + str(AWS_IOT_MQTT_PORT)
00451     print "AWS_IOT_MQTT_CLIENT_ID = " + AWS_IOT_MQTT_CLIENT_ID
00452     print "AWS_IOT_MY_THING_NAME = " + AWS_IOT_MY_THING_NAME
00453 
00454 # Makes sure cert/key files exist
00455 if not os.path.exists(AWS_IOT_ROOT_CA_FILENAME):
00456     print "ERROR: Root CA file not found, " + AWS_IOT_ROOT_CA_FILENAME
00457     exit(1)
00458 if not os.path.exists(AWS_IOT_PRIVATE_KEY_FILENAME):
00459     print "ERROR: Private Key file not found, " + AWS_IOT_PRIVATE_KEY_FILENAME
00460     exit(1)
00461 if not os.path.exists(AWS_IOT_CERTIFICATE_FILENAME):
00462     print "ERROR: AWS IOT cert file not found, " + AWS_IOT_CERTIFICATE_FILENAME
00463     exit(1)
00464 
00465 # Init AWSIoTMQTTShadowClient
00466 myAWSIoTMQTTShadowClient = None
00467 if useWebsocket:
00468     myAWSIoTMQTTShadowClient = AWSIoTMQTTShadowClient("basicShadowDeltaListener", useWebsocket=True)
00469     myAWSIoTMQTTShadowClient.configureEndpoint(AWS_IOT_MQTT_HOST, 443)
00470     myAWSIoTMQTTShadowClient.configureCredentials(AWS_IOT_ROOT_CA_FILENAME)
00471 else:
00472     myAWSIoTMQTTShadowClient = AWSIoTMQTTShadowClient("basicShadowDeltaListener")
00473     myAWSIoTMQTTShadowClient.configureEndpoint(AWS_IOT_MQTT_HOST, AWS_IOT_MQTT_PORT)
00474     myAWSIoTMQTTShadowClient.configureCredentials(AWS_IOT_ROOT_CA_FILENAME, AWS_IOT_PRIVATE_KEY_FILENAME, AWS_IOT_CERTIFICATE_FILENAME)
00475 
00476 # AWSIoTMQTTShadowClient configuration
00477 myAWSIoTMQTTShadowClient.configureAutoReconnectBackoffTime(1, 32, 20)
00478 myAWSIoTMQTTShadowClient.configureConnectDisconnectTimeout(10)  # 10 sec
00479 myAWSIoTMQTTShadowClient.configureMQTTOperationTimeout(5)  # 5 sec
00480 
00481 # Connect to AWS IoT
00482 myAWSIoTMQTTShadowClient.connect()
00483 
00484 # Create a deviceShadow with persistent subscription
00485 AIT = myAWSIoTMQTTShadowClient.createShadowHandlerWithName(AWS_IOT_MY_THING_NAME, True)
00486 
00487 # Listen on deltas
00488 AIT.shadowRegisterDeltaCallback(customShadowCallback_Delta)
00489 
00490 # Delete shadow JSON doc
00491 #AIT.shadowDelete(customShadowCallback_Delete, 5)
00492 
00493 # NOTE: We make this slightly slower than the target loop (to prevent 'stale data')
00494 getTimer = Timer(3.5, customShadowTimer_Get, ())
00495 getTimer.start()
00496 
00497 # GUI loop (loops until closed)
00498 window.mainloop()
00499 
00500 # Kill our timer after the GUI closes
00501 getTimerAlive = FALSE
00502 getTimer.cancel()