def local_shadow_connect(device_name, config_file, root_ca, certificate, private_key, group_ca_dir): cfg = GroupConfigFile(config_file) ggd_name = cfg['devices'][device_name]['thing_name'] iot_endpoint = cfg['misc']['iot_endpoint'] dip = DiscoveryInfoProvider() dip.configureEndpoint(iot_endpoint) dip.configureCredentials( caPath=root_ca, certPath=certificate, keyPath=private_key ) dip.configureTimeout(10) # 10 sec logging.info( "[shadow_connect] Discovery using CA:{0} cert:{1} prv_key:{2}".format( root_ca, certificate, private_key )) gg_core, discovery_info = discover_configured_core( config_file=config_file, dip=dip, device_name=ggd_name, ) if not gg_core: raise EnvironmentError("[core_connect] Couldn't find the Core") ca_list = discovery_info.getAllCas() core_list = discovery_info.getAllCores() group_id, ca = ca_list[0] core_info = core_list[0] logging.info("Discovered Greengrass Core:{0} from Group:{1}".format( core_info.coreThingArn, group_id) ) group_ca_file = save_group_ca(ca, group_ca_dir, group_id) # local Greengrass Core discovered # get a shadow client to receive commands mqttsc = AWSIoTMQTTShadowClient(ggd_name) # now connect to Core from this Device logging.info("[core_connect] gca_file:{0} cert:{1}".format( group_ca_file, certificate)) mqttsc.configureCredentials(group_ca_file, private_key, certificate) mqttc = mqttsc.getMQTTConnection() mqttc.configureOfflinePublishQueueing(10, DROP_OLDEST) if not mqtt_connect(mqttsc, gg_core): raise EnvironmentError("connection to Master Shadow failed.") # create and register the shadow handler on delta topics for commands # with a persistent connection to the Master shadow master_shadow = mqttsc.createShadowHandlerWithName( cfg['misc']['master_shadow_name'], True) return mqttc, mqttsc, master_shadow, ggd_name
class ThermoApp(App): DEVICE = '/dev/ttyAMA0' BAUD = 9600 TIMEOUT = 5 ipaddr='' lastGPUTempRead=0.0 lastWeatherRead=0.0 lastTempPressHumidRead=0.0 lastShadowUpdate=0.0 lastSetAlerts=0.0 ui = ObjectProperty(None) zones = ObjectProperty(None) zonemap=['','17','27','22'] zoneData={ '1':ThermoZone(1,17), '2':ThermoZone(2,27), '3':ThermoZone(3,22) } furnace=Furnace() currentZone=1 dataFeed = deque() deviceData={ 'AA':ThermoDevice('AA',2,'master'), 'AB':ThermoDevice('AB',2,'tess'), 'AC':ThermoDevice('AC',2,'kate'), 'AD':ThermoDevice('AD',3,'girls'), 'AE':ThermoDevice('AE',1,'snug'), 'AF':ThermoDevice('AF',1,'living'), 'AG':ThermoDevice('AG',0,'porch'), 'AH':ThermoDevice('AH',1,'ground'), 'BM':ThermoDevice('BM',0,'thermo'), 'AW':ThermoDevice('AW',0,'weather'), 'PI':ThermoDevice('PI',0,'GPU')} ser = serial.Serial(DEVICE, BAUD) voltage = 0.0 tempvale = 0.0 pressure = 0.0 weather = [] sensor = BME280(mode=BME280_OSAMPLE_8) host='a2pveb84akyryv.iot.us-east-1.amazonaws.com' rootCAPath='rootca.key' privateKeyPath='bdca28f300.private.key' certificatePath='bdca28f300.cert.pem' # -e a2pveb84akyryv.iot.us-east-1.amazonaws.com -r rootca.key -c bdca28f300.cert.pem -k bdca28f300.private.key def show_config(self): App.open_settings(self) Window.request_keyboard(self.keyboard_close, self) def keyboard_close(self): #print "close" return def build_config(self, config): config.setdefaults('startup', { 'weatherText': 'foobar', 'picSource': 'weather/1.jpg' }) self.config=config def build_settings(self, settings): jsondata = """[ { "type": "title", "title": "Thermo application" }, { "type": "options", "title": "Initial Weather", "desc": "Weather Pic", "section": "startup", "key": "picSource", "options": ["weather/1.jpg", "weather/images.jpg", "weather/part_coudy.jpg"] }, { "type": "string", "title": "Weather Title", "desc": "Weather Text", "section": "startup", "key": "weatherText" }]""" settings.add_json_panel('Thermo application', self.config, data=jsondata) def build(self): self.ui=ThermoWidget() self.ui.weatherText='ThermoWidget' self.ui.picSource='weather/1.jpg' self.ui.tempDataText="temps" self.ui.setPointText="0.0" self.ui.ipAddressText="192.168.0.0" self.ui.averageTempText="0.0" self.ui.zoneAlertsText="Loading..." btn=self.ui.ids['increase'] btn.bind(on_release=self.increaseSetPoint) btn=self.ui.ids['decrease'] btn.bind(on_release=self.decreaseSetPoint) self.zones=self.ui.ids['zones'] for z in range(0,4): btnstate='down' if self.currentZone==z else 'normal' btn = ToggleButton( allow_no_selection=False, text=str(z), group='zonegroup', size_hint=(None, None), halign='center', state=btnstate, background_normal='normal.png', background_down='down.png') btn.bind(on_release=self.switch_zone) self.zones.add_widget(btn) self.ui.weatherText=self.config.get('startup', 'weatherText') temp = subprocess.check_output(["ifconfig","wlan0"],universal_newlines=True) pos1=temp.find("inet addr:") pos2=temp.find(" Bcast:") self.ui.ipAddressText=temp[pos1+10:pos2] self.connectMQTT() Clock.schedule_interval(self.mainLoop, 10.0) return self.ui def switch_zone(self,toggle): self.currentZone=int(toggle.text) self.updateDisplay() pass def increaseSetPoint(self,instance): self.zoneData[str(self.currentZone)].setPoint+=5.0/9.0 self.takeAction() self.updateDisplay() pass def decreaseSetPoint(self,instance): self.zoneData[str(self.currentZone)].setPoint-=5.0/9.0 self.takeAction() self.updateDisplay() pass def loadConfig(self): # read config file into memory vars return def avgZone(self,zonenum): tot=0.0 cnt=0 for i in self.deviceData: device=self.deviceData[i] if(device.zone==zonenum): tot+=float(device.temp) if(device.temp>0.0): cnt+=1 if cnt==0: cnt=1 return tot/cnt def connectMQTT(self): self.myShadowClient = AWSIoTMQTTShadowClient("thermo") #self.myAWSIoTMQTTClient = AWSIoTMQTTClient("thermo") self.myShadowClient.configureEndpoint(self.host, 8883) self.myShadowClient.configureCredentials(self.rootCAPath, self.privateKeyPath, self.certificatePath) # myShadowClient connection configuration self.myShadowClient.configureAutoReconnectBackoffTime(1, 32, 20) self.myShadowClient.connect() self.myAWSIoTMQTTClient = self.myShadowClient.getMQTTConnection() self.myAWSIoTMQTTClient.configureOfflinePublishQueueing(-1) # Infinite offline Publish queueing self.myAWSIoTMQTTClient.configureDrainingFrequency(2) # Draining: 2 Hz self.myAWSIoTMQTTClient.configureConnectDisconnectTimeout(10) # 10 sec self.myAWSIoTMQTTClient.configureMQTTOperationTimeout(5) # 5 sec # Connect and subscribe to AWS IoT #self.myAWSIoTMQTTClient.connect() # myAWSIoTMQTTClient.subscribe("thermo", 1, customCallback) # self.myAWSIoTMQTTClient.publish("thermo", "[[\'"+(strftime(DATE_FORMAT,localtime())+"\','TT','START','1']]", 1) # Create a device shadow instance using persistent subscription self.myDeviceShadow = self.myShadowClient.createShadowHandlerWithName("mythermo", True) return def updateDeviceShadow(self): if(time()-self.lastShadowUpdate > 300): thingState={ "state" : { "reported" : { "sensors" : { }, "zones" : { }, "furnace" : { } } } } for i in self.deviceData: device=self.deviceData[i] thingState["state"]["reported"]["sensors"][device.id]={"temp":tformat(device.temp),"location":device.location,"batt":device.batt,"alert":device.alert,"lastupdate":device.lastupdate,"press":device.press,"humid":device.humid} for i in self.zoneData: zone=self.zoneData[i] thingState["state"]["reported"]["zones"][zone.id]={"status":zone.status, "average":tformat(zone.average), "setPoint":tformat(zone.setPoint), "triggertemp":tformat(zone.triggertemp), "alert":zone.alert} thingState["state"]["reported"]["furnace"]={"onSeconds":self.furnace.onSeconds,"offSeconds":self.furnace.offSeconds,"maxBurnSeconds":self.furnace.maxBurnSeconds,"maxRestSeconds":self.furnace.maxRestSeconds,"status":self.furnace.status,"lastupdate":self.furnace.lastupdate} self.myDeviceShadow.shadowUpdate(json.dumps(thingState), None, 5) self.lastShadowUpdate=time() return def updateDisplay(self): # draw everything # if click then show subpanel or change config self.ui.setPointText="{:2.0f}".format(self.zoneData[str(self.currentZone)].setPoint*9/5+32.0) self.ui.averageTempText=tformat(self.avgZone(self.currentZone)) self.ui.tempDataText='' zonealerts='Alerts:' for i in self.deviceData: device=self.deviceData[i] thisDeviceText=tformat(device.temp) thisDeviceText+=" "+device.location+" "+device.alert self.ui.tempDataText+=thisDeviceText+'\n' for i in self.zoneData: zone=self.zoneData[i] if(len(zone.alert)>0): zonealerts+=" Zone"+str(zone.id)+" "+zone.alert self.ui.zoneAlertsText=zonealerts return def readSensors(self): # get data from serial RF sensors # get data from remote PI # all data in memory only in this function # get temperature # messages are 12chars aIDTYPEVALUE aIDAWAKE---- or aIDSLEEPING- # returns -100 on error, or the temperature as a float fim = time()+ self.TIMEOUT voltage = 0 tempvalue = -100 deviceid = '' while (time()<fim) and (tempvalue == -100): n = self.ser.inWaiting() if n != 0: data = self.ser.read(n) nb_msg = len(data) / 12 for i in range (0, nb_msg): msg = data[i*12:(i+1)*12] deviceid = msg[1:3] if self.deviceData.has_key(deviceid): device=self.deviceData[deviceid] device.lastupdate=strftime(DATE_FORMAT,localtime()) if msg[3:7] == "TEMP": tempvalue = msg[7:] device.temp=tempvalue self.dataFeed.append((strftime(DATE_FORMAT,localtime()), deviceid, "TEMP", tempvalue)) if msg[3:7] == "BATT": voltage = msg[7:11] if voltage == "LOW": voltage = 0.1 device.batt=voltage self.dataFeed.append((strftime(DATE_FORMAT,localtime()), deviceid+'B', "BATT", voltage)) else: sleep(5) return def getPiSensorData(self): if(time()-self.lastGPUTempRead > 60): temp = "" temp = subprocess.check_output(["/opt/vc/bin/vcgencmd","measure_temp"],universal_newlines=True) temp = temp[5 : -3] device=self.deviceData['PI'] device.lastupdate=strftime(DATE_FORMAT,localtime()) device.temp=temp self.dataFeed.append((strftime(DATE_FORMAT,localtime()), "PI", "TEMP", temp)) self.lastGPUTempRead = time() return def getConnectedSensorData(self): if(time()-self.lastTempPressHumidRead > 60): # get BME280 data temp=self.sensor.read_temperature()-1.0 press=self.sensor.read_pressure() humid=self.sensor.read_humidity() self.pressure=press device=self.deviceData['BM'] device.lastupdate=strftime(DATE_FORMAT,localtime()) device.temp=temp device.press=press device.humid=humid self.dataFeed.append((strftime(DATE_FORMAT,localtime()), "BM", "TEMP", temp)) self.dataFeed.append((strftime(DATE_FORMAT,localtime()), "BP", "PRESS", press)) self.dataFeed.append((strftime(DATE_FORMAT,localtime()), "BH", "HUMID", humid)) self.lastTempPressHumidRead = time() def getWeather(self): if(time()-self.lastWeatherRead > 1800): # get and parse AccuWeather data cur = re.compile('Currently: (.*)<') link = "http://rss.accuweather.com/rss/liveweather_rss.asp?metric=0&locCode=US|44022" f = urllib.urlopen(link) myfile = f.read() tempvalue = cur.search(myfile).group(1) temp=tempvalue[-4:-1] pos=tempvalue.find(":") description=tempvalue[0:-5] if pos<0 else tempvalue[0:pos] description=description.replace(" ","_").lower() # print("description = [" + description +"]") device=self.deviceData['AW'] device.lastupdate=strftime(DATE_FORMAT,localtime()) device.temp=(float(temp)-32)*5/9 if device.desc<>description : self.ui.picSource='weather/'+description+'.jpg' if 6 < localtime()[3] < 18 else 'weather/'+description+'_dark.jpg' device.desc=description self.ui.weatherText = tempvalue self.dataFeed.append((strftime(DATE_FORMAT,localtime()), "AW", "NEWS", tempvalue)) self.lastWeatherRead = time() return def setAlerts(self): # Reasons for alerts: # sensor battery level below 2.3 # sensor not reporting ( sensor data age > 5x reporting ) # temperature not under control = falling when attempting to raise # alert if temp not correct direction for 10 minutes # need control switch date time if(time()-self.lastSetAlerts > 1800): for i in self.deviceData: device=self.deviceData[i] device.alert="" if (not device.batt is None) & (device.batt<2.5): device.alert="LOW" self.dataFeed.append((strftime(DATE_FORMAT,localtime()), "ALERT", "LOW Battery in "+device.location, device.batt)) self.lastSetAlerts = time() if (len(device.lastupdate)>0) & (device.id!='AW'): age = datetime.datetime.strptime(strftime(DATE_FORMAT,localtime()),DATE_FORMAT) - datetime.datetime.strptime(device.lastupdate,DATE_FORMAT) #print "{} {}".format(device.location,age.seconds) if ( age.seconds > 600 ): device.alert="OLD" self.dataFeed.append((strftime(DATE_FORMAT,localtime()), "ALERT", "NO Response in "+device.location, age.seconds)) self.lastSetAlerts = time() for i in self.zoneData: zone=self.zoneData[i] zone.alert="" if (zone.status): age = datetime.datetime.strptime(strftime(DATE_FORMAT,localtime()),DATE_FORMAT) - datetime.datetime.strptime(zone.lastupdate,DATE_FORMAT) if (age.seconds>600): zone.alert="OOC" self.dataFeed.append((strftime(DATE_FORMAT,localtime()), "ALERT", "OOC in zone "+str(zone.id), tformat(zone.average))) self.lastSetAlerts = time() return def uploadData(self): # put the data in the cloud or cache in a file until sucess # add it to the memory deque # if the deque > 10 try to upload it and any pending updates # else throw a flag for pending updates and write to a file if len(self.dataFeed)>10: try: # write to a file #print " write to file" with open("Output.txt", "a") as text_file: for record in self.dataFeed: text_file.write("{},{},{},{}\r\n".format(record[0],record[1],record[2],record[3])) # write to cloud #print " write to cloud" self.myAWSIoTMQTTClient.publish("thermo", json.dumps(list(self.dataFeed)), 1) # clear the deque self.dataFeed.clear() except: print("Unexpected error in uploadData:", sys.exc_info()[0]) return def downloadRequests(self): # get cloud data or web requests return def controlZone(self,zone,on,avg): zoneentry=self.zoneData[str(zone)] subprocess.call(["gpio", "-g", "write", str(zoneentry.port), "1" if on else "0"]) furnaceWasOn=False for i in self.zoneData: furnaceWasOn|=self.zoneData[i].status if(zoneentry.status != on): zoneentry.status=on furnaceIsOn=False for i in self.zoneData: furnaceIsOn|=self.zoneData[i].status if(furnaceIsOn!=furnaceWasOn): self.furnace.status=furnaceIsOn if (len(self.furnace.lastupdate)>0): age = datetime.datetime.strptime(strftime(DATE_FORMAT,localtime()),DATE_FORMAT) - datetime.datetime.strptime(self.furnace.lastupdate,DATE_FORMAT) # if it is now on - age is how long it was off if(furnaceIsOn): self.furnace.offSeconds+=age.seconds if(age.seconds>self.furnace.maxRestSeconds): self.furnace.maxRestSeconds=age.seconds # if it is now off - age is how long it was on else: self.furnace.onSeconds+=age.seconds if(age.seconds>self.furnace.maxBurnSeconds): self.furnace.maxBurnSeconds=age.seconds self.furnace.lastupdate=strftime(DATE_FORMAT,localtime()) zoneentry.lastupdate=strftime(DATE_FORMAT,localtime()) zoneentry.triggertemp=avg return def takeAction(self): # contains all rules to make decisions based on data for i in self.zoneData: zone=self.zoneData[i] zone.average=self.avgZone(zone.id) if(zone.average<10.0): self.controlZone(zone.id,False,zone.average) return #print "average in zone {} is {}".format(zone.id,zone.average) if(zone.average<zone.setPoint-0.5): self.controlZone(zone.id,True,zone.average) #turn it on if(zone.average>zone.setPoint): self.controlZone(zone.id,False,zone.average) #turn it off return def mainLoop(self,args): try: #print 'config' self.loadConfig() #print 'getWeather' self.getWeather() #print 'getPI' self.getPiSensorData() #print 'getBME' self.getConnectedSensorData() #print 'read' self.readSensors() #print 'alerts' self.setAlerts() #print 'update' self.updateDisplay() #print 'update shadow' self.updateDeviceShadow() #print 'upload' self.uploadData() #print 'download' self.downloadRequests() #print 'action' self.takeAction() except: type_, value_, traceback_ = sys.exc_info() print "EXCEPTION {}\r\n{}\r\n{}".format(type_, value_, traceback.format_tb(traceback_)) self.dataFeed.append(value_) return
def initialize(device_name, config_file, root_ca, certificate, private_key, group_ca_path): global ggd_name cfg = GroupConfigFile(config_file) local = dict() remote = dict() # determine heartbeat device's thing name and endpoint for MQTT clients ggd_name = cfg['devices'][device_name]['thing_name'] iot_endpoint = cfg['misc']['iot_endpoint'] # Discover Greengrass Core dip = DiscoveryInfoProvider() dip.configureEndpoint(iot_endpoint) dip.configureCredentials( caPath=root_ca, certPath=certificate, keyPath=private_key ) dip.configureTimeout(10) # 10 sec log.info("Discovery using CA: {0} certificate: {1} prv_key: {2}".format( root_ca, certificate, private_key )) # Now discover the groups in which this device is a member. # The arm should only be in two groups. The local and master groups. discovered, discovery_info = utils.ggc_discovery( ggd_name, dip, retry_count=10, max_groups=2 ) # Each group returned has a groupId which can compare to the configured # groupId in the config file. If the IDs match, the 'local' Group has been # found and therefore local core. # If the groupId's do not match, the 'remote' or 'master' group has been # found. group_list = discovery_info.getAllGroups() for g in group_list: logging.info("[initialize] group_id:{0}".format(g.groupId)) if g.groupId == cfg['group']['id']: local_cores = g.coreConnectivityInfoList local['core'] = local_cores[0] # just grab first core as local local['ca'] = g.caList else: remote_cores = g.coreConnectivityInfoList remote['core'] = remote_cores[0] # just grab first core as remote remote['ca'] = g.caList if len(local) > 1 and len(remote) > 1: logging.info("[initialize] local_core:{0} remote_core:{1}".format( local, remote )) else: raise EnvironmentError("Couldn't find the arm's Cores.") # just save one of the group's CAs to use as a CA file later local_core_ca_file = utils.save_group_ca( local['ca'][0], group_ca_path, local['core'].groupId ) remote_core_ca_file = utils.save_group_ca( remote['ca'][0], group_ca_path, remote['core'].groupId ) # Greengrass Cores discovered, now connect to Cores from this Device # get a client to send telemetry local_mqttc = AWSIoTMQTTClient(ggd_name) log.info("[initialize] local gca_file:{0} cert:{1}".format( local_core_ca_file, certificate)) local_mqttc.configureCredentials( local_core_ca_file, private_key, certificate ) local_mqttc.configureOfflinePublishQueueing(10, DROP_OLDEST) if not utils.mqtt_connect(mqtt_client=local_mqttc, core_info=local['core']): raise EnvironmentError("Connection to GG Core MQTT failed.") # get a shadow client to receive commands master_shadow_client = AWSIoTMQTTShadowClient(ggd_name) log.info("[initialize] remote ca_file:{0} cert:{1}".format( local_core_ca_file, certificate)) remote_mqttc = master_shadow_client.getMQTTConnection() remote_mqttc.configureCredentials( remote_core_ca_file, private_key, certificate ) if not utils.mqtt_connect(mqtt_client=master_shadow_client, core_info=remote['core']): raise EnvironmentError("Connection to Master Shadow failed.") # create and register the shadow handler on delta topics for commands # with a persistent connection to the Master shadow master_shadow = master_shadow_client.createShadowHandlerWithName( cfg['misc']['master_shadow_name'], True) log.info("[initialize] created handler for shadow name: {0}".format( cfg['misc']['master_shadow_name'] )) token = master_shadow.shadowGet(shadow_mgr, 5) log.info("[initialize] shadowGet() tk:{0}".format(token)) return local_mqttc, remote_mqttc, master_shadow
class Robot: """ Class Robot. Used to interact with the robot, it needs a cloud instance """ command = None class Commands: """ The robot mqtt command class. Used for the mqtt commands """ start: None stop: None pause: None dock: None status: None find: None resume: None def __init__(self, robot): """ Declare the partial functools. Used to gate what command are sent through mqtt """ self.start = functools.partial(robot._cmd, 'start') self.stop = functools.partial(robot._cmd, 'stop') self.pause = functools.partial(robot._cmd, 'pause') self.dock = functools.partial(robot._cmd, 'dock') self.status = functools.partial(robot._cmd, 'status') self.find = functools.partial(robot._cmd, 'find') self.resume = functools.partial(robot._cmd, 'resume') def __init__(self, cloud=None, rid=None, output_raw=None): """ Initialize the robot instance. Check if a cloud is provided and set the current map id. """ self.command = Robot.Commands(self) # alias for hass self.send_command = self._cmd # if no cloud connexion is passed,create one using provided credentials if not cloud: raise Exception('You need to provide a cloud connection') else: self._cloud = cloud # use provided id or first robot available if rid: self._id = rid else: self._id = list(self._cloud.robots())[0] self._current_map_id = None self._current_user_pmapv_id = None self.maps() self.device = None self.output_raw = output_raw self.name = None self.shadow_client = None def connect(self): """ Instantiate mqtt clients and delete them when exiting. We use AWSIoTMQTTShadowClient to create our MQTT connection. This manager will close the connection on exit """ self.shadow_client = AWSIoTMQTTShadowClient(self._cloud.app_id + str(os.urandom(6)), useWebsocket=True) self.shadow_client.configureEndpoint(self._cloud.mqtt_endpoint, 443) setuppath = '/usr/local/etc/aws-root-ca1.cer' pippath = site.USER_SITE if path.exists( "%s/usr/local/etc/aws-root-ca1.cer" % site.USER_SITE) else get_python_lib() cerpath = setuppath if path.exists( setuppath) else "%s/usr/local/etc/aws-root-ca1.cer" % pippath self.shadow_client.configureCredentials(cerpath) self.shadow_client.configureIAMCredentials(self._cloud.access_key_id, self._cloud.secret_key, self._cloud.session_token) self.shadow_client.configureAutoReconnectBackoffTime(1, 128, 20) self.shadow_client.configureConnectDisconnectTimeout(10) self.shadow_client.configureMQTTOperationTimeout(5) # Set keepAlive interval to be 1 second and connect # Raise exception if there is an error in connecting to AWS IoT try: if not self.shadow_client.connect(5): raise Exception('AWSIoTMQTTShadowClientCouldNotConnect') except ValueError as e: logger.error( "shadow_client.connect returned '%s'" ', credentials are not authorized.', str(e)) return -1 self.device = self.shadow_client.createShadowHandlerWithName( self._id, True) self.connection = self.shadow_client.getMQTTConnection() logger.info('[+] mqtt connected') def disconnect(self): """Disconnect the mqtt stuff.""" logger.info('[+] mqtt disconnected') self.connection.disconnect() # return maps and set active one def maps(self): """ Return the map lists. Used to return a map lists and to set the active one in the instance only retrieve first map for now (fixme) """ maps = [] params = {'visible': 'true', 'activeDetails': '1'} maps = self._cloud.api.get(self._id, 'pmaps', params=params) if maps: path = ['active_pmapv_details', 'active_pmapv', 'pmap_id'] self._current_map_id = maps[0][path[0]][path[1]][path[2]] self._current_user_pmapv_id = maps[0]['user_pmapv_id'] return maps def rooms(self): """ Return the room lists. Retrieve a correctly formated room list for humans """ if self.maps(): return (self.maps()[0]['active_pmapv_details']['regions']) else: return [] def missions(self): """ Return achieved mission list. return a json with the list of the finished missions and their statuses """ params = {'filterType': 'omit_quickly_canceled_not_scheduled'} return self._cloud.api.get(self._id, 'missionhistory', params=params) def evac_history(self): """ Return logs of evacuations. return a json with logs of evacuations """ params = {'robotId': self._id, 'maxAge': 90} return self._cloud.api.get('evachistory', params=params) def timeline(self): """ Return event timeline. return a json with the event timeline """ params = {'event_type': 'HKC', 'details_type_filter': 'all'} return self._cloud.api.get('robots', self._id, 'timeline', params=params) def vector_map(self, map_id=None, user_pmapv_id=None): """ Return a map as json. Return a json with coordinates of the map and history of the mission if any. """ if not map_id: map_id = self._current_map_id if not user_pmapv_id: user_pmapv_id = self._current_user_pmapv_id return self._cloud.api.get(self._id, 'pmaps', map_id, 'versions', user_pmapv_id, 'umf') def _make_payload(self, room_ids, cmd): payload = { 'state': 'desired', 'command': cmd, 'initiator': 'rmtApp', 'ordered': 0, } if cmd == 'start' and room_ids: logger.info('start cleaning of :') for room_id in room_ids.split(','): logger.info(' - %s (%s)', self.get_room_name(room_id), room_id) regions = [{'type':'rid', 'region_id': room_id} for room_id in room_ids.split(',')] \ if ',' in room_ids else [{'region_id': room_ids, 'type': 'rid'}] payload.update({ 'pmap_id': self._current_map_id, 'regions': regions, 'user_pmapv_id': self._current_user_pmapv_id }) return payload def _cmd(self, cmd, room_ids=None, print_output=_output_status): topic = '%s/things/%s/cmd' % (self._cloud.mqtt_topic, self._id) qos = 1 payload = self._make_payload(room_ids, cmd) if cmd == 'status': try: self.device.shadowGet(print_output, 5) except Exception as e: logger.info('shadow get failed, exception: %s', e) logger.info('trying to refresh the connection (and the aws \ credentials)') self.disconnect() self.connect() self.device.shadowGet(print_output, 5) self.device.shadowRegisterDeltaCallback(print_output) return 0 # exit(0) logger.info('executing command %s on robot %s, payload : %s', cmd, self._id, payload) if self.connection.publish(topic, json.dumps(payload), qos): try: self.device.shadowGet(print_output, 5) except Exception as e: logger.info('shadow get failed, exception: %s', e) logger.info('trying to refresh the connection (and the aws \ credentials)') self.disconnect() self.connect() self.device.shadowGet(print_output, 5) self.device.shadowRegisterDeltaCallback(print_output) else: raise Exception('MqttPublish%sError' % cmd) def current_state(self, state): """Return state as expected by the hass module.""" # https://github.com/NickWaterton/Roomba980-Python/blob/master/roomba/roomba.py states = { 'charge': 'Charging', 'new': 'New Mission', 'run': 'Running', 'resume': 'Running', 'hmMidMsn': 'Recharging', 'recharge': 'Recharging', 'stuck': 'Stuck', 'hmUsrDock': 'User Docking', 'dock': 'Docking', 'dockend': 'Docking - End Mission', 'cancelled': 'Cancelled', 'stop': 'Stopped', 'pause': 'Paused', 'hmPostMsn': 'End Mission', '': None } return states[state] def set_preference(self, **kwargs): """Set preferences in robot (not implemented).""" logger.info('Set preference not implemented for %s', self._id) logger.info('-- Received keys --') if kwargs is not None: for key, value in kwargs.iteritems(): logger.info('%s == %s' % (key, value)) logger.info('-- End of Received keys --') def get_room_id(self, name): """Get room id from name.""" for room in self.rooms(): if name == room['name']: return room['id'] def get_room_name(self, room_id): """Get room name from id.""" for room in self.rooms(): if room_id == room['id']: return room['name']
myAWSIoTMQTTShadowClient = AWSIoTMQTTShadowClient(config.clientId, useWebsocket=True) myAWSIoTMQTTShadowClient.configureEndpoint(config.host, config.port) myAWSIoTMQTTShadowClient.configureCredentials(config.rootCAPath) else: myAWSIoTMQTTShadowClient = AWSIoTMQTTShadowClient(config.clientId) myAWSIoTMQTTShadowClient.configureEndpoint(config.host, config.port) myAWSIoTMQTTShadowClient.configureCredentials(config.rootCAPath, config.privateKeyPath, config.certificatePath) # AWSIoTMQTTShadowClient configuration myAWSIoTMQTTShadowClient.configureAutoReconnectBackoffTime(1, 32, 20) myAWSIoTMQTTShadowClient.configureConnectDisconnectTimeout(10) # 10 sec myAWSIoTMQTTShadowClient.configureMQTTOperationTimeout(5) # 5 sec MQTTClient = myAWSIoTMQTTShadowClient.getMQTTConnection() MQTTClient.configureOfflinePublishQueueing(5, DROP_OLDEST) # Connect to AWS IoT Shadow myAWSIoTMQTTShadowClient.connect() # Create a deviceShadow with persistent subscription deviceShadowHandler = myAWSIoTMQTTShadowClient.createShadowHandlerWithName( config.thingName, True) print('Connecting to MQTT server and setting up callbacks...') jobsClient.connect() jobExecutor = JobExecutor(config, deviceShadowHandler) jobsMsgProc = JobsMessageProcessor(jobsClient, config.clientId, jobExecutor) print('Starting to process jobs...') while True:
from AWSIoTPythonSDK.MQTTLib import AWSIoTMQTTShadowClient #for cert based connection myShadowClient = AWSIoTMQTTShadowClient("raspberry-pi") myShadowClient.configureEndpoint("a1xugslbalqdxo.iot.us-east-1.amazonaws.com", 8883) myShadowClient.configureCredentials("/home/pi/python_mqtt/aws-iot-certs/rootCA.pem.crt", "/home/pi/python_mqtt/aws-iot-certs/c6417d9f55-private.pem.key", "/home/pi/python_mqtt/aws-iot-certs/c6417d9f55-certificate.pem.crt") #myShadowClient.configureConnectionDisconnectTimeout(10) myShadowClient.configureMQTTOperationTimeout(5) myShadowClient.connect() myDeviceShadow = myShadowClient.createShadowHandlerWithName("Bot", True) payload = json.dumps({ "state":{ "reported": { "this_thing_is_alive": "I am Raspberry" } } }) #myDeviceShadow.shadowGet(customCallback, 5) #myDeviceShadow.shadowUpdate(payload, shadowUpdate, 5) myMQTTClient = myShadowClient.getMQTTConnection() myMQTTClient.publish("topic/raspberry-pi/messages", "Payload message", 1)
def iot_setup(): # imports variable to be configured and used in delta_handler f(x) global myDeviceShadow # Get device configuration details with open('config.json', 'r') as cfg: device = json.load(cfg) thing_uid = device['thing_uid'] # Get keyfile paths key_dir = f'{os.getcwd()}/keys/' if os.path.exists(key_dir): try: with open(f'{key_dir}{thing_uid}.pem.crt', 'r') as r: root_file = f'{key_dir}RootCA.pem' key_file = f'{key_dir}{thing_uid}.private.key' crt_file = f'{key_dir}{thing_uid}.pem.crt' except FileNotFoundError as err: print(f'Issue with filenames in <{key_dir}>') print(str(err)) else: print(f'Path <{key_dir} does not exist; verify working directory') # Certificate based connection myShadowClient = AWSIoTMQTTShadowClient(thing_uid) print(f'Shadow Client: {myShadowClient}') print(f'Shadow Client ID: {thing_uid}') # Configuration for TLS mutual authentication myShadowClient.configureEndpoint(device['endpt'], int(device['prt'])) myShadowClient.configureCredentials(root_file, key_file, crt_file) myShadowClient.configureAutoReconnectBackoffTime(1, 32, 20) myShadowClient.configureConnectDisconnectTimeout(10) # 10 sec myShadowClient.configureMQTTOperationTimeout(5) # 5 sec print('shadow client configured') myShadowClient.connect() print('shadow client connected') # Create a device shadow instance using persistent subscriptions myDeviceShadow = myShadowClient.createShadowHandlerWithName( thing_uid, True) with open('default_payloads.json', 'r') as defaults: tmp = json.load(defaults) shadow_doc = tmp['default_shadow'] payload = tmp['default_payload'] # Shadow operations #init_shadow = myDeviceShadow.shadowGet(customShadowCallback, 5) shadow_doc['state']['reported']['property'] = 0 shadow_doc['state']['reported']['state'] = 'initialized' shadow_doc['state']['reported']['time'] = f'{datetime.now()}' myDeviceShadow.shadowUpdate(json.dumps(shadow_doc), customShadowCallback_Update, 5) #myDeviceShadow.shadowDelete(customShadowCallback_Delete, 5) myDeviceShadow.shadowRegisterDeltaCallback(customShadowCallback_Delta) #myDeviceShadow.shadowUnregisterDeltaCallback() print('shadow handler configured') # MQTT Client operations myMQTTClient = myShadowClient.getMQTTConnection() payload['mssg'] = 'MQTT live' payload['time'] = f'{datetime.now()}' payload['uid'] = thing_uid myMQTTClient.subscribe('myTopic', 1, customMssgCallback) sleep(0.1) myMQTTClient.publish("myTopic", json.dumps(payload), 0) print('mqtt client connection active') return (myDeviceShadow, myMQTTClient, payload)
shadowClient = AWSIoTMQTTShadowClient(clientId) shadowClient.configureEndpoint(host, port) shadowClient.configureCredentials(rootCAPath, privateKeyPath, certificatePath) # AWSIoTMQTTShadowClient configuration # Configure the auto-reconnect backoff to start with 1 second and use 32 seconds as a maximum back off time. # Connection over 20 seconds is considered stable and will reset the back off time back to its base. shadowClient.configureAutoReconnectBackoffTime(1, 32, 20) # Used to configure the time in seconds to wait for a CONNACK or a disconnect to complete. shadowClient.configureConnectDisconnectTimeout(10) # 10 sec # Used to configure the timeout in seconds for MQTT QoS 1 publish, subscribe and unsubscribe. shadowClient.configureMQTTOperationTimeout(5) # 5 sec shadowClient.connect() client = shadowClient.getMQTTConnection() # Used to configure the queue size and drop behavior for the offline requests queueing. client.configureOfflinePublishQueueing(-1) # Infinite offline Publish queueing time.sleep(1) ################################### # Shadow of device # Used to remotely set cycle time ################################### class DeviceAnalyzerShadow: def shadowCallback_Update(self, payload, responseStatus, token): if responseStatus == "timeout": print("Update request " + token + " time out!") self.sendReportedState()
# For certificate based connection myShadowClient = AWSIoTMQTTShadowClient("myClientID") # Basic ops myShadowClient.connect() # Create a device shadow instance using persistent subscription myDeviceShadow = myShadowClient.createShadowHandlerWithName("Bot", True) # Shadow operations myDeviceShadow.shadowGet(customCallback, 5) myDeviceShadow.shadowUpdate(myJSONPayload, customCallback, 5) myDeviceShadow.shadowDelete(customCallback, 5) myDeviceShadow.shadowRegisterDeltaCallback(customCallback) myDeviceShadow.shadowUnregisterDeltaCallback() # To retrieve MQTTClient to perform plain MQTT ops along with shadow ops myMQTTClient = myShadowClient.getMQTTConnection() myMQTTClient.publish("plainMQTTTopic", "Payload", 1) # Progressive Reconect Backoff # When non-client-side disconnect occurs, SDK will auto-reconnect AWSIoTPythonSDK.MQTTLib.AWSIoTMQTTShadowClient.configureAutoReconnectBackoffTime( baseReconnectQuietTimeSecond, maxReconnectQuietTimeSecond, stableConnectionTimeSecond) # Defaults: # baseReconnectQuietTimeSecond = 1; # maxReconnectQuietTimeSecond = 32; # stableConnectionTimeSecond = 20;
class AWSIoTMQTTShadowClientGenerator: def __init__(self, host, rootCAPath, certificatePath, privateKeyPath, thingName, clientId, topic, useWebsocket=False): self.host = host self.rootCAPath = rootCAPath self.certificatePath = certificatePath self.privateKeyPath = privateKeyPath self.useWebsocket = useWebsocket self.thingName = thingName self.clientId = clientId self.topic = topic if useWebsocket and certificatePath and privateKeyPath: print( "X.509 cert authentication and WebSocket are mutual exclusive. Please pick one." ) exit(2) if not useWebsocket and (not certificatePath or not privateKeyPath): print("Missing credentials for authentication.") exit(2) # Configure logging logger = logging.getLogger("AWSIoTPythonSDK.core") logger.setLevel(logging.INFO) streamHandler = logging.StreamHandler() formatter = logging.Formatter( '%(asctime)s - %(name)s - %(levelname)s - %(message)s') streamHandler.setFormatter(formatter) logger.addHandler(streamHandler) # Init AWSIoTMQTTShadowClient self.myAWSIoTMQTTShadowClient = None self.myAWSIoTMQTTShadowClient = AWSIoTMQTTShadowClient(clientId) # AWSIoTMQTTShadowClient configuration self.myAWSIoTMQTTShadowClient.configureEndpoint(host, 8883) self.myAWSIoTMQTTShadowClient.configureCredentials( rootCAPath, privateKeyPath, certificatePath) self.myAWSIoTMQTTShadowClient.configureAutoReconnectBackoffTime( 1, 32, 20) self.myAWSIoTMQTTShadowClient.configureConnectDisconnectTimeout( 10) # 10 sec self.myAWSIoTMQTTShadowClient.configureMQTTOperationTimeout(5) # 5 sec # Connect to AWS IoT self.myAWSIoTMQTTShadowClient.connect() time.sleep(2) # Init and configure AWSIoTMQTTClient. This is so I can publish to non-shadow topics self.myAWSIoTMQTTClient = self.myAWSIoTMQTTShadowClient.getMQTTConnection( ) self.myAWSIoTMQTTClient.configureAutoReconnectBackoffTime(1, 32, 20) self.myAWSIoTMQTTClient.configureOfflinePublishQueueing( -1) # Infinite offline Publish queueing self.myAWSIoTMQTTClient.configureDrainingFrequency(2) # Draining: 2 Hz self.myAWSIoTMQTTClient.configureConnectDisconnectTimeout(10) # 10 sec self.myAWSIoTMQTTClient.configureMQTTOperationTimeout(5) # 5 sec # Subscribe to MQTT topic self.myAWSIoTMQTTClient.subscribe(self.topic, 1, self.customMqttCallback) # Create a deviceShadow with persistent subscription self.deviceShadowHandler = self.myAWSIoTMQTTShadowClient.createShadowHandlerWithName( thingName, True) self.shadowCallbackContainer_Bot = ShadowCallbackContainer(self) # Listen on deltas self.deviceShadowHandler.shadowRegisterDeltaCallback( self.shadowCallbackContainer_Bot.customShadowCallbackDelta) # Create the initial State self._desired_state = {} self._reported_state = {} self._devices = [] # This is how object will make calls to update its container object def setContainerCallback(self, callback): self.container_callback = callback def shadowUpdate(self, JSONPayload): self.deviceShadowHandler.shadowUpdate(JSONPayload, self.genericCallback, 5) def publish(self, JSONPayload): try: self.myAWSIoTMQTTClient.publish(self.topic, JSONPayload, 1) except: print("Publish error: ") def getState(self): _r = '"reported": {"ble_devices":' + json.dumps( self._reported_state.values()) + '}' _d = '"desired": {"ble_devices":' + json.dumps( self._desired_state.values()) + '}' return '{"state": {' + _r + ', ' + _d + '} }' def updateState(self, value): self._reported_state[value["MAC"]] = value for x in self._devices: self._desired_state[x]["color"] = value["color"] print( str(datetime.now()) + " Desired state values: " + json.dumps(self._desired_state.values())) print( str(datetime.now()) + " Reported state values: " + json.dumps(self._reported_state.values())) return self.getState() def registerDeviceAddress(self, address): print "AWSIoTMQTTShadowClientGenerator is registering device: " + address self._devices.append(address) # Initialize dictionary for this BLE device. Set color to off self._desired_state[address] = { "MAC": address, "color": "21430000009b" } def registerNotificationDelegate(self, notificationDelgate): self.shadowCallbackContainer_Bot.setNotificationDelegate( notificationDelgate) # Custom MQTT message callback def customMqttCallback(self, client, userdata, message): print("Received a new message from the lights topic: ") print(message.payload) print("from topic: ") print(message.topic) print("--------------\n\n") print("Setting PiHub global state with new value") #{"state": {"desired": {"property": "2142019b"}}} d = json.loads(message.payload) self.container_callback(d["state"]["desired"]["property"]) def genericCallback(self, payload, responseStatus, token): # payload is a JSON string ready to be parsed using json.loads(...) # in both Py2.x and Py3.x if responseStatus == "timeout": print("Update request " + token + " time out!") if responseStatus == "accepted": payloadDict = json.loads(payload) print("~~~~~~~~~~~~~~~~~~~~~~~") print("Update request with token: " + token + " accepted!") print("property: " + json.dumps(payloadDict)) print("~~~~~~~~~~~~~~~~~~~~~~~\n\n") if responseStatus == "rejected": print("Update request " + token + " rejected!")
shadow_client = AWSIoTMQTTShadowClient(clientId) shadow_client.configureEndpoint(ggcAddr, 8883) shadow_client.configureCredentials(rootCAPath, privateKeyPath, certificatePath) # Config AWSIoTMQTTShadowClient shadow_client.configureAutoReconnectBackoffTime(1, 32, 20) shadow_client.configureConnectDisconnectTimeout(10) # 10 sec shadow_client.configureMQTTOperationTimeout(5) # 5 sec # Connect to AWS IoT shadow_client.connect() # Create a deviceShadow with persistent subscription shadow_handler = shadow_client.createShadowHandlerWithName(thingName, True) shadow_mqttcli = shadow_client.getMQTTConnection() shadow_container = ShadowTelemetryContainer(shadow_handler, shadow_mqttcli, **deviceParams) # Create a deviceShadow doc JSONPayload = json.dumps({ 'state': { 'reported': { 'running': False } } }).encode() shadow_container.shadow_handler.shadowUpdate( JSONPayload, shadow_container.callback_update, 5) # Listen on deltas
cfg = GroupConfigFile(args.config_file) web_name = cfg['devices']['GGD_web']['thing_name'] # get a shadow client to receive commands mqttc_shadow_client = AWSIoTMQTTShadowClient(web_name) mqttc_shadow_client.configureEndpoint(ggd_config.master_core_ip, ggd_config.master_core_port) mqttc_shadow_client.configureCredentials( CAFilePath=dir_path + "/certs/master-server.crt", KeyPath=dir_path + "/certs/GGD_web.private.key", CertificatePath=dir_path + "/certs/GGD_web.certificate.pem.crt") if not mqtt_connect(mqttc_shadow_client): raise EnvironmentError("connection to Master Shadow failed.") mqttc = mqttc_shadow_client.getMQTTConnection() # create and register the shadow handler on delta topics for commands global master_shadow master_shadow = mqttc_shadow_client.createShadowHandlerWithName( "MasterBrain", True) # persistent connection with Master Core shadow token = master_shadow.shadowGet(shadow_mgr, 5) log.debug("[initialize] shadowGet() tk:{0}".format(token)) for t in ggd_config.convey_topics: mqttc.subscribe(t, 1, topic_update) log.info('[initialize] subscribed to topic:{0}'.format(t)) for t in ggd_config.sort_bridge_topics:
print("Discovery failed after %d retries. Exiting...\n" % (MAX_DISCOVERY_RETRIES)) sys.exit(-1) # Init AWSIoTMQTTShadowClient myAWSIoTMQTTShadowClient = AWSIoTMQTTShadowClient(clientId) myAWSIoTMQTTShadowClient.configureEndpoint(host, port) myAWSIoTMQTTShadowClient.configureCredentials(groupCA, privateKeyPath, certificatePath) # myAWSIoTMQTTShadowClient.getMQTTConnection().onMessage = customOnMessage # AWSIoTMQTTShadowClient configuration myAWSIoTMQTTShadowClient.configureAutoReconnectBackoffTime(1, 32, 20) myAWSIoTMQTTShadowClient.configureConnectDisconnectTimeout(10) # 10 sec myAWSIoTMQTTShadowClient.configureMQTTOperationTimeout(5) # 5 sec myAWSIoTMQTTShadowClient.getMQTTConnection().configureOfflinePublishQueueing( 10) # Infinite offline Publish queueing myAWSIoTMQTTShadowClient.getMQTTConnection().configureDrainingFrequency( 2) # Draining: 2 Hz # Create a deviceShadow with persistent subscription deviceShadowHandler = myAWSIoTMQTTShadowClient.createShadowHandlerWithName( topic, True) # Delete shadow JSON doc deviceShadowHandler.shadowDelete(customShadowCallback_Delete, 5) # Listen on deltas deviceShadowHandler.shadowRegisterDeltaCallback(customShadowCallback_Delta) # Iterate through all connection options for the core and use the first successful one connected = False