def main(): events.Issue(events.ids.PREINIT) events.Issue(events.ids.INIT) sleepDelayS = 0.1 while True: time.sleep(sleepDelayS) events.Issue(events.ids.SECONDS, sleepDelayS)
def main(): log.Init("Starting Vesta") events.Issue(events.ids.PREINIT) events.Issue(events.ids.INIT) sleepDelayS = 0.1 while True: time.sleep(sleepDelayS) events.Issue(events.ids.SECONDS, sleepDelayS)
def EventHandler(eventId, eventArg): global owm, apiKey, location if eventId == events.ids.HOURS: # Get weather once/hour if owm == None: apiKey = config.Get("owmApiKey") location = config.Get("owmLocation") if (apiKey != None and location != None): owm = pyowm.OWM(apiKey) # My API key try: obs = owm.weather_at_place(location) # My location except: database.NewEvent(0, "Weather Feed failed!") synopsis.problem("Weather", "Feed failed @ " + str(datetime.now())) return w = obs.get_weather() cloudCover = w.get_clouds() # Percentage cloud cover variables.Set("cloudCover", str(cloudCover), True) outsideTemp = w.get_temperature("celsius")["temp"] # Outside temperature in celsius variables.Set("outsideTemperature", str(outsideTemp), True) windSpeed = w.get_wind()["speed"] variables.Set("windSpeed", str(windSpeed), True) rain = w.get_rain() if rain != {}: rain = 1 # was rain["3h"] # Rain volume in last 3 hours. Unknown units, may be ml(?) else: rain = 0 # No rain variables.Set("rain", str(rain), True) snow = w.get_snow() if snow != {}: snow = 1 # was snow["3h"] # Snow volume in last 3 hours. Unknown units, may be ml(?) else: snow = 0 # No snow variables.Set("snow", str(snow), True) database.NewEvent(0, "Weather now "+str(cloudCover)+"% cloudy") events.Issue(events.ids.WEATHER) # Tell system that we have a new weather report
def push_event(self, evt): if evt.rm_type not in ('opened', 'updated'): pass # Not handled yet. new = (evt.rm_type == 'opened') if new: update = 0 else: update = evt.raw.issue.lock_version issue = evt.raw.issue.id title = evt.raw.issue.subject if new: author = evt.raw.issue.author.login else: author = evt.raw.journal.author.login events.dispatcher.dispatch( 'redmine', events.Issue(new, update, issue, title, author))
def Parse(atLine): global expectOurEui global ourChannel, ourPowLvl, ourPan, ourExtPan if atLine == "": return # Ignore blank lines if atLine[0:2] == 'AT': return # Exit immediately if line starts with AT, indicating an echo log.debug("Parsing:"+ atLine) atLine = atLine.replace(':',',') # Replace colons with commas atList = atLine.split(',') # Split on commas if atList[0] == 'OK': events.Issue(events.ids.RXOK) elif expectOurEui == True and len(atList[0])==16: # If we're expecting an EUI and it's the correct length database.SetDeviceItem(0, "eui64", atList[0]) expectOurEui = False elif atList[0] == "ERROR": errNo = None if len(atList) > 1: try: errNo = int(atList[1],16) # Might be junk - have seen all sorts of nonsense from Telegesis stick here except ValueError: pass # Just use default value of errNo events.Issue(events.ids.RXERROR, errNo) # See if anyone cares about the error elif atList[0] == "SED" or atList[0] == "FFD" or atList[0] == "ZED": events.Issue(events.ids.DEVICE_ANNOUNCE, atList) # Tell system that device has (re-)joined elif atList[0] == 'CHECKIN': events.Issue(events.ids.CHECKIN, atList) # Tell devices that device is about to poll for data elif atList[0] == 'TOGGLE' or atList[0] == 'ON' or atList[0] == 'OFF': events.Issue(events.ids.BUTTON, atList) # Tell rules engine elif atList[0] == 'ZONESTATUS': events.Issue(events.ids.TRIGGER, atList) # Tell rules engine elif 0 == atList[0].find("Telegesis"): database.SetDeviceItem(0, "manufName", atList[0]) elif 0 == atList[0].find("CICIE"): database.SetDeviceItem(0, "modelName", atList[0]) expectOurEui = True # Following EUI has no prefix - we just have to know that it follows the CICIE line elif atList[0] == "+N=COO": ourChannel = atList[1] ourPowLvl = atList[2] ourPan = atList[3] ourExtPan = atList[4] elif atList[0] == "+N=NoPAN": # No PAN means we need to set up the network and add ourselves queue.EnqueueCmd(0, ["AT+EN", "OK"]) # Start new network elif atList[0] == "JPAN": # After Establish Network (AT+EN) has finished ourChannel = atList[1] ourPan = atList[2] ourExtPan = atList[3] else: events.Issue(events.ids.RXMSG, atList)
def EventHandler(eventId, eventArg): global oldMins if eventId == events.ids.INIT: SetSunTimes() rules.Run("trigger==hubstart") log.activity("hub", "started") #devices.SetSynopsis("IoT Hub started at", str(datetime.now())) elif eventId == events.ids.SECONDS: now = datetime.now() if now.minute != oldMins: events.Issue(events.ids.MINUTES) oldMins = now.minute # Ready for next time elif eventId == events.ids.MINUTES: now = datetime.now() rules.Run("time==" + now.strftime("%H:%M") ) # Run timed rules once per minute with time of date CheckTimedRule("dawn", now) CheckTimedRule("sunset", now) CheckTimedRule("sunrise", now) CheckTimedRule("dusk", now) if now.minute == 0 and now.hour == 1: # 1am, time to calculate sunrise and sunset for new day SetSunTimes() log.NewLog() # Roll the logs, to avoid running out of disc space
def EventHandler(eventId, eventArg): global oldMins, oldHours if eventId == events.ids.INIT: SetDayInfo() SetSunTimes() if variables.Get("sunrise") != None: variables.Set("morning", variables.Get("sunrise")) variables.Set("evening", variables.Get( "sunset")) # Set up defaults until we get a weather report variables.Set("dark", str(GetDark())) rules.Run("trigger==appstart") database.NewEvent(0, "App started") # 0 is always hub telegesis.SetTime() # Set time up for HA devices to synchronise to queue.EnqueueCmd(0, ["AT+SETATR:000A,0001,05", "OK" ]) # Set Master clock and timezone bits on CICIE queue.EnqueueCmd(0, ["AT+TIMERD", "OK"]) # Set CICIE as time server elif eventId == events.ids.SECONDS: now = datetime.now() if now.minute != oldMins: events.Issue(events.ids.MINUTES, now.minute) oldMins = now.minute # Ready for next time elif eventId == events.ids.MINUTES: now = datetime.now() if now.hour != oldHours: events.Issue(events.ids.HOURS, now.hour) oldHours = now.hour # Ready for next time variables.Set("time", str(now.strftime("%H:%M"))) rules.Run( "time==" + str(now.strftime("%H:%M"))) # Run timed rules once per minute rules.Run("trigger==time") # Run timed rules once per minute # Could alter above to rules.Run("trigger==minute") if variables.Get("sunrise") != None: CheckTimedRule("dawn", now) # Sky getting light before sunrise CheckTimedRule("sunrise", now) # Sun on horizon CheckTimedRule("morning", now) # Now proper daylight (depending on cloud) CheckTimedRule( "evening", now) # No longer proper daylight (depending on cloud) CheckTimedRule("sunset", now) # Sun on horizon CheckTimedRule("dusk", now) # Sky now getting dark after sunset dark = str(GetDark()) # Work out whether "dark" is True or False #log.debug("Old dark = " + variables.Get("dark") + " whereas new dark = " + dark) if dark != variables.Get("dark"): # Compare with previous variables.Set( "dark", dark ) # Update our idea of whether it's dark or light just now rules.Run("dark==" + variables.Get("dark") ) # This will also delete the variable afterwards variables.Set( "dark", dark ) # Re-instate the variable after the rule has deleted it if eventId == events.ids.HOURS: # Could add rules.Run("trigger==hour") if eventArg == 0: # Midnight, time to calculate sunrise and sunset for new day events.Issue(events.ids.NEWDAY) if eventArg == 4: # Re-synch Telegesis clock to local time at 4am every day to cope with DST telegesis.SetTime() # Set time up for HA devices to synchronise to if eventId == events.ids.NEWDAY: # Could add rules.Run("trigger==day") SetSunTimes() log.RollLogs() # Roll the logs, to avoid running out of disc space SetDayInfo() synopsis.BuildPage( ) # Create status page, once/day, based upon reported problems during the previous day synopsis.clearProblems()
def EventHandler(eventId, eventArg): global ephemera, globalDevKey, pendingBinding, pendingBindingTimeoutS, pendingRptAttrId, msp_ota if eventId == events.ids.PREINIT: keyList = database.GetAllDevKeys( ) # Get a list of all the device identifiers from the database for i in range( 100): # Fudge to ensure we have enough pendingBinding entries pendingBinding.append( "" ) # Problem is that the devKey indices aren't consecutive (removed devices) and so pendingBindingTimeoutS.append( 0 ) # using the keyList isn't the same as for x in range(maxDevKey) for devKey in keyList: # Hub and devices Init(devKey) # Initialise dictionary and associated ephemera if database.GetDeviceItem(devKey, "nwkId") != "0000": # Ignore hub SetTempVal( devKey, "GetNextBatteryAfter", datetime.now()) # Ask for battery shortly after startup if eventId == events.ids.INIT: msp_ota = config.Get("MSP_OTA") if eventId == events.ids.DEVICE_ANNOUNCE: if len(eventArg) >= 3: eui64 = eventArg[1] nwkId = eventArg[2] devKey = GetKey(nwkId) if devKey == None: # Which will only be the case if we've not seen this short Id before devKey = database.GetDevKey("eui64", eui64) if devKey == None: # Which will be the case if we've not seen the long Id either devKey = Add(nwkId, eui64, eventArg[0]) log.debug("New key for new device is " + str(devKey)) if eventArg[0] == "SED": SetTempVal(devKey, "PollingUntil", datetime.now() + timedelta(seconds=300)) events.Issue( events.ids.NEWDEVICE, devKey ) # Tell everyone that a new device has been seen, so it can be initialised else: # Same long Id, but short Id needs updating after it has changed database.SetDeviceItem(devKey, "nwkId", nwkId) else: NoteMsgDetails(devKey, eventArg) SetTempVal( devKey, "GetNextBatteryAfter", datetime.now() ) # Ask for battery shortly after Device Announce, either new or old one re-joining if eventId == events.ids.CHECKIN: # See if we have anything to ask the device... if len(eventArg) >= 3: endPoint = eventArg[2] seq = "00" # was seq = eventArg[3], but that's the RSSI devKey = GetKey(eventArg[1]) if devKey != None: EnsureInBinding( devKey, zcl.Cluster.PollCtrl ) # Assume CheckIn means PollCtrl must be in binding, so make sure this is up-to-date NoteMsgDetails(devKey, eventArg) if database.GetDeviceItem(devKey, "endPoints") == None: database.SetDeviceItem( devKey, "endPoints", endPoint ) # Note endpoint that CheckIn came from, unless we already know this nwkId = database.GetDeviceItem(devKey, "nwkId") cmdRsp = Check( devKey ) # Check to see if we want to know anything about the device if cmdRsp != None: log.debug("Keep awake for 10 secs so we can send " + cmdRsp[0]) queue.Jump(devKey, [ "AT+RAWZCL:" + nwkId + "," + endPoint + ",0020,11" + seq + "00012800", "DFTREP" ]) # Tell device to enter Fast Poll for 40qs (==10s) SetTempVal(devKey, "PollingUntil", datetime.now() + timedelta(seconds=10)) queue.EnqueueCmd( devKey, cmdRsp) # This will go out after the Fast Poll Set else: SetTempVal( devKey, "PollingUntil", datetime.now() + timedelta(seconds=2) ) # Say that it's polling for a short while, so that we can tell it to stop(!) queue.EnqueueCmd(devKey, [ "AT+RAWZCL:" + nwkId + "," + endPoint + ",0020,11" + seq + "00000100", "DFTREP" ]) # Tell device to stop Poll else: # Unknown device, so assume it's been deleted from our database telegesis.Leave( eventArg[1] ) # Tell device to leave the network, since we don't know anything about it if eventId == events.ids.TRIGGER or eventId == events.ids.BUTTON: if len(eventArg) >= 2: devKey = GetKey( eventArg[1] ) # Lookup device from network address in eventArg[1] if devKey != None: SetTempVal( devKey, "PollingUntil", datetime.now() + timedelta(seconds=1) ) # Say that it's polling for a very short while so that we can try to set up a PollCtrl cluster if eventId == events.ids.RXMSG: if eventArg[0] == "AddrResp" and eventArg[1] == "00" and len( eventArg) >= 3: devKey = GetKey(eventArg[2]) if devKey != None: database.SetDeviceItem(devKey, "eui64", eventArg[1]) elif eventArg[0] == "ActEpDesc" and len(eventArg) >= 3: if "00" == eventArg[2]: devKey = GetKey(eventArg[1]) if devKey != None: database.SetDeviceItem(devKey, "endPoints", eventArg[3]) # Note first endpoint elif eventArg[0] == "SimpleDesc" and len(eventArg) >= 3: if "00" == eventArg[2]: globalDevKey = GetKey( eventArg[1] ) # Is multi-line response, so expect rest of response and use this global index until it's all finished elif "82" == eventArg[2]: # 82 == Invalid endpoint devKey = GetKey(eventArg[1]) events.Issue(events.ids.RXERROR, int( eventArg[2], 16)) # Tell system that we're aborting this command elif eventArg[0] == "InCluster" and len(eventArg) >= 2: if globalDevKey != None: database.SetDeviceItem( globalDevKey, "inClusters", str(eventArg[1:]) ) # Store whole list from arg[1] to arg[n] elif eventArg[0] == "OutCluster" and len(eventArg) >= 2: if globalDevKey != None: NoteMsgDetails( globalDevKey, eventArg ) # Must do this so that we can remove RSSI and LQI if they're there, to avoid these values being interpreted as clusters database.SetDeviceItem( globalDevKey, "outClusters", str(eventArg[1:]) ) # Store whole list from arg[1] to arg[n] globalDevKey = None # We've finished with this global for now elif eventArg[0] == "RESPATTR" and len(eventArg) >= 7: devKey = GetKey(eventArg[1]) if devKey != None: NoteMsgDetails(devKey, eventArg) if len( eventArg ) >= 7: # Check for number of args after possibly removing RSSI and LQI ep = eventArg[2] clusterId = eventArg[3] attrId = eventArg[4] if "00" == eventArg[5]: attrVal = eventArg[6] SetAttrVal(devKey, clusterId, attrId, attrVal) else: SetAttrVal(devKey, clusterId, attrId, "Failed (error " + eventArg[5] + ")") # So that we don't keep asking elif eventArg[0] == "RESPMATTR" and len(eventArg) >= 8: devKey = GetKey(eventArg[1]) if devKey != None: NoteMsgDetails(devKey, eventArg) if len( eventArg ) >= 8: # Check for number of args after possibly removing RSSI and LQI ep = eventArg[2] mfgId = eventArg[3] clusterId = eventArg[4] attrId = eventArg[5] if "00" == eventArg[6]: attrVal = eventArg[7] SetAttrVal(devKey, clusterId, attrId, attrVal) elif eventArg[0] == "REPORTATTR" and len(eventArg) >= 7: devKey = GetKey(eventArg[1]) if devKey != None: ep = eventArg[2] clusterId = eventArg[3] attrId = eventArg[4] attrType = eventArg[5] attrVal = eventArg[6] if clusterId == zcl.Cluster.MultistateInput and attrId == zcl.Attribute.PresentValue: args = [attrVal, eventArg[1]] events.Issue(events.ids.MULTISTATE, args) return # Ignore reports on Basic cluster (eg lumi.sensor_cube when it joins will send this) #if clusterId == zcl.Cluster.Basic: # return # Ignore reports on Basic cluster (eg lumi.sensor_cube when it joins will send this) NoteMsgDetails(devKey, eventArg) EnsureReporting( devKey, clusterId, attrId, attrVal ) # Make sure reports are happening at the correct frequency and update device if not SetAttrVal(devKey, clusterId, attrId, attrVal) NoteReporting(devKey, clusterId, attrId) else: # Unknown device, so assume it's been deleted from our database telegesis.Leave( eventArg[1] ) # Tell device to leave the network, since we don't know anything about it elif eventArg[0] == "Bind" and len( eventArg) >= 2: # Binding Response from device devKey = GetKey(eventArg[1]) if devKey != None: if pendingBinding[devKey]: binding = eval( database.GetDeviceItem(devKey, "binding", "[]")) if pendingBinding[ devKey] not in binding: # Only put it in once, even if we get multiple responses binding.append(pendingBinding[devKey]) database.SetDeviceItem(devKey, "binding", str(binding)) pendingBinding[devKey] = None elif eventArg[0] == "CFGRPTRSP" and len( eventArg) >= 5: # Configure Report Response from device devKey = GetKey(eventArg[1]) status = eventArg[4] if devKey != None and status == "00": clusterId = eventArg[3] attrId = pendingRptAttrId # Need to remember this, since it doesn't appear in CFGRPTRSP NoteReporting(devKey, clusterId, attrId) pendingRptAttrId = None # Ready for the next report elif eventArg[0] == "CWSCHEDULE": heating.ParseCWShedule(eventArg) elif eventArg[0] == "DFTREP": devKey = GetKey(eventArg[1]) NoteMsgDetails(devKey, eventArg) #else: # Unrecognised message, but we still want to extract OOB info # if len(eventArg) >= 2: # devKey = GetKey(eventArg[1]) # Assume this is sensible # if devKey != None: # NoteMsgDetails(devKey, eventArg) #if eventId == events.ids.BUTTON: # devKey = GetKey(eventArg[1]) # Lookup device from network address in eventArg[1] # NoteMsgDetails(devKey, eventArg) if eventId == events.ids.RXERROR: globalDevKey = None # We've finished with this global if we get an error if eventId == events.ids.SECONDS: for devKey in devDict: # Go through devDict, pulling out each entry if devDict[devKey] >= 0: # Make sure device hasn't been deleted if IsListening(devKey): # True if FFD, ZED or Polling devIndex = GetIndexFromKey(devKey) if expRsp[ devIndex] == None: # We don't have a message in flight if queue.IsEmpty(devKey): cmdRsp = Check(devKey) if cmdRsp: queue.EnqueueCmd( devKey, cmdRsp ) # Queue up anything we ought to know cmdRsp = queue.DequeueCmd( devKey) # Pull first item from queue if cmdRsp != None: log.debug("Sending " + str(cmdRsp)) expRsp[devIndex] = cmdRsp[1] # Note response expRspTimeoutS[ devIndex] = 2 # If we've not heard back after 2 seconds, it's probably got lost, so try again telegesis.TxCmd(cmdRsp[0]) # Send command directly else: # We're expecting a response, so time it out expRspTimeoutS[ devIndex] = expRspTimeoutS[devIndex] - eventArg if expRspTimeoutS[devIndex] <= 0: expRsp[devIndex] = None if pendingBinding[ devKey]: # Make sure we timeout pendingBinding pendingBindingTimeoutS[ devKey] = pendingBindingTimeoutS[devKey] - eventArg if pendingBindingTimeoutS[devKey] <= 0: pendingBinding[devKey] = None offAt = GetTempVal(devKey, "SwitchOff@") if offAt: if datetime.now() >= offAt: DelTempVal(devKey, "SwitchOff@") devcmds.SwitchOff(devKey) fadeDownAt = GetTempVal(devKey, "FadeDown@") if fadeDownAt: if datetime.now() >= fadeDownAt: DelTempVal(devKey, "FadeDown@") devcmds.Dim(devKey, 0) devcmds.SwitchOff(devKey) # Switch off after dim command pirOffAt = GetTempVal(devKey, "PirInactive@") if pirOffAt: if datetime.now() >= pirOffAt: DelTempVal(devKey, "PirInactive@") newState = "inactive" database.NewEvent(devKey, newState) Rule(devKey, newState)
def do_info(self, line): """info Displays useful information""" events.Issue(events.ids.INFO)
def do_radio(self, item): """radio Shows info about the radio (channel, power, PAN id)""" events.Issue(events.ids.RADIO_INFO) # Used by web page for hub info
def Restart(): database.NewEvent(0, "Restarting...") # 0 is always hub events.Issue(events.ids.SHUTDOWN) # Tell system we're about to shutdown sys.exit(0) # Stop app, and rely on cron job to restart us
def Reboot(): database.NewEvent(0, "Rebooting...") # 0 is always hub events.Issue(events.ids.SHUTDOWN) # Tell system we're about to shutdown os.system("sudo reboot") # Unrecoverable, so reboot entire machine...
def EventHandler(eventId, eventArg): global info, dirty, globalDevIdx, pendingBinding, pendingRptAttrId, statusUpdate if eventId == events.ids.INIT: try: with open(devFilename, "r") as f: try: info = eval( f.read()) # Load previous cache of devices into info[] log.log("Loaded list from file") except: log.fault("Unusable device list from file - discarding!") info = [] except OSError: info = [] dirty = False # Info[] is initialised now devIdx = 0 database.ClearDevices() rowId = database.NewDevice("0000") database.UpdateDevice(rowId, "UserName", "Hub") for devices in info: ephemera.append([]) # Initialise parallel ephemeral device list InitDevStatus(devIdx) # Initialise parallel device status CopyDevToDB(devIdx) SetTempVal( devIdx, "LastSeen", datetime.now() ) # Mark it as "seen at start up" so we don't start looking for it immediately devIdx = devIdx + 1 CheckAllAttrs() # Set up any useful variables for the loaded devices if eventId == events.ids.DEVICE_ANNOUNCE: devId = eventArg[2] devIdx = GetIdx(devId) if devIdx == None: # Which will only be the case if this device is actually new, but it may have just reset and announced devIdx = InitDev(devId) SetUserNameFromDevIdx( devIdx, "(New) " + devId) # Default username of network ID, since that's unique SetVal(devIdx, "DevType", eventArg[0]) # SED, FFD or ZED SetVal(devIdx, "EUI", eventArg[1]) if eventArg[0] == "SED": SetTempVal(devIdx, "PollingUntil", datetime.now() + timedelta(seconds=300)) else: NoteEphemera(devIdx, eventArg) if eventId == events.ids.CHECKIN: # See if we have anything to ask the device... endPoint = eventArg[2] seq = "00" # was seq = eventArg[3], but that's the RSSI devIdx = GetIdx(eventArg[1]) if devIdx != None: NoteEphemera(devIdx, eventArg) if GetVal(devIdx, "EP") == None: SetVal( devIdx, "EP", endPoint ) # Note endpoint that CheckIn came from, unless we already know this devId = GetVal(devIdx, "devId") cmdRsp = Check( devIdx, False ) # Check to see if we want to know anything about the device if cmdRsp != None: log.log("Want to know " + str(cmdRsp)) telegesis.TxCmd([ "AT+RAWZCL:" + devId + "," + endPoint + ",0020,11" + seq + "00012800", "OK" ]) # Tell device to enter Fast Poll for 40qs (==10s) SetTempVal(devIdx, "PollingUntil", datetime.now() + timedelta(seconds=10)) telegesis.TxCmd( cmdRsp ) # This will go out after the Fast Poll Set - but possibly ought to go out as part of SECONDS handler..? else: #log.log("Don't want to know anything about "+GetUserNameFromDevIdx(devIdx)) telegesis.TxCmd([ "AT+RAWZCL:" + devId + "," + endPoint + ",0020,11" + seq + "00000100", "OK" ]) # Tell device to stop Poll if eventId == events.ids.RXMSG: if eventArg[0] == "AddrResp" and eventArg[1] == "00": devIdx = GetIdx(eventArg[2]) if devIdx != None: SetVal(devIdx, "EUI", eventArg[3]) elif eventArg[0] == "ActEpDesc": if "00" == eventArg[2]: devIdx = GetIdx(eventArg[1]) if devIdx != None: SetVal(devIdx, "EP", eventArg[3]) # Note first endpoint elif eventArg[0] == "SimpleDesc": if "00" == eventArg[2]: globalDevIdx = GetIdx( eventArg[1] ) # Is multi-line response, so expect rest of response and use this global index until it's all finished elif "82" == eventArg[2]: # 82 == Invalid endpoint devIdx = GetIdx(eventArg[1]) DelVal(devIdx, "EP") events.Issue(events.ids.RXERROR, int( eventArg[2], 16)) # Tell system that we're aborting this command elif eventArg[0] == "InCluster": if globalDevIdx != None: SetVal(globalDevIdx, "InCluster", eventArg[1:]) # Store whole list from arg[1] to arg[n] elif eventArg[0] == "OutCluster": if globalDevIdx != None: NoteEphemera(globalDevIdx, eventArg) SetVal(globalDevIdx, "OutCluster", eventArg[1:]) # Store whole list from arg[1] to arg[n] globalDevIdx = None # We've finished with this global for now if eventArg[0] == "RESPATTR": devIdx = GetIdx(eventArg[1]) if devIdx != None: NoteEphemera(devIdx, eventArg) ep = eventArg[2] clusterId = eventArg[3] attrId = eventArg[4] if "00" == eventArg[5]: attrVal = eventArg[6] else: attrVal = "Error:" + eventArg[ 5] # So that we don't ask for the same attribute again later SetAttrVal(devIdx, clusterId, attrId, attrVal) if eventArg[0] == "REPORTATTR": devIdx = GetIdx(eventArg[1]) if devIdx != None: ep = eventArg[2] clusterId = eventArg[3] attrId = eventArg[4] attrType = eventArg[5] attrVal = eventArg[6] NoteEphemera(devIdx, eventArg) SetAttrVal(devIdx, clusterId, attrId, attrVal) reporting = GetVal( devIdx, "Reporting" ) # See if we're expecting this report, and note it in the reporting table if reporting != None: newRpt = clusterId + ":" + attrId if newRpt not in reporting: reporting.append(newRpt) SetVal(devIdx, "Reporting", reporting) else: SetVal(devIdx, "Reporting", []) # Ready for next time if eventArg[0] == "Bind": # Binding Response from device devIdx = GetIdx(eventArg[1]) if devIdx != None: if pendingBinding != None: binding = GetVal(devIdx, "Binding") binding.append(pendingBinding) SetVal(devIdx, "Binding", binding) pendingBinding = None if eventArg[0] == "CFGRPTRSP": # Configure Report Response from device devIdx = GetIdx(eventArg[1]) status = eventArg[4] if devIdx != None and status == "00": clusterId = eventArg[3] attrId = pendingRptAttrId # Need to remember this, since it doesn't appear in CFGRPTRSP reporting = GetVal(devIdx, "Reporting") newRpt = clusterId + ":" + attrId if newRpt not in reporting: reporting.append(newRpt) SetVal(devIdx, "Reporting", reporting) if eventId == events.ids.BUTTON: devIdx = GetIdx( eventArg[1]) # Lookup device from network address in eventArg[1] NoteEphemera(devIdx, eventArg) if eventId == events.ids.RXERROR: globalDevIdx = None # We've finished with this global if we get an error if eventId == events.ids.SECONDS: SendPendingCommand() for devIdx, devInfo in enumerate( info ): # See if any devices are timing out, and turn them off if necessary offAt = GetVal(devIdx, "SwitchOff@") if offAt: if now >= offAt: SwitchOff(devIdx) if dirty: with open(devFilename, 'wt') as f: pprint(info, stream=f) # Was print(info, file=f) # Save devices list directly to file dirty = False # Don't save again until needed if statusUpdate: SaveStatus() statusUpdate = False if eventId == events.ids.MINUTES: SaveStatus() # For app UpTime CheckPresence() # For all devices