def TxReadAttrRsp(devKey, clstrId, attrId, attrType, attrVal): nwkId = database.GetDeviceItem(devKey, "nwkId") if nwkId == None: return # Make sure it's a real device before continuing (it may have just been deleted) ep = database.GetDeviceItem(devKey, "endPoints") cmdRsp = ReadAttrRsp(nwkId, ep, clstrId, attrId, attrType, attrVal) queue.EnqueueCmd(devKey, cmdRsp) # Queue up command for sending via devices.py
def SetDaySchedule(devKey, scheduleType="Heating", dayOfWeek="Sun"): nwkId = CheckThermostat(devKey) if nwkId == None: return # Make sure it's a real thermostat device before continuing ep = database.GetDeviceItem(devKey, "endPoints") frameCtl = "11" seqId = "00" dayOfWeekIndex = iottime.GetDowIndex(dayOfWeek) log.debug("From " + dayOfWeek + " get value of " + str(dayOfWeekIndex)) dayBit = 2**dayOfWeekIndex # ** is "raise to the power". Assumes dayOfWeek is a int where 0=Sunday, 1=Monday, etc. scheduleStr = database.GetSchedule(scheduleType, dayOfWeek) try: scheduleList = eval(scheduleStr) except: return # Bad list from database numSetpoints = len(scheduleList) scheduleStr = "" for index in range(0, numSetpoints): timeTemp = scheduleList[index] # Get each time & temp from schedule timeStr = timeTemp[0] tempStr = timeTemp[1] time = datetime.strptime(timeStr, "%H:%M") minsSinceMidnight = (time.hour * 60) + time.minute htonMins = telegesis.ByteSwap(minsSinceMidnight) htonTemp = telegesis.ByteSwap(int(float(tempStr) * 100)) scheduleStr = scheduleStr + "{:04X}".format(htonMins) scheduleStr = scheduleStr + "{:04X}".format(htonTemp) cmdRsp = ("AT+RAWZCL:" + nwkId + "," + ep + "," + zcl.Cluster.Thermostat + "," + frameCtl + seqId + zcl.Commands.SetSchedule + "{:02X}".format(numSetpoints) + "{:02X}".format(dayBit) + "01" + scheduleStr, "CWSCHEDULE") # Set heating(01) schedule queue.EnqueueCmd(devKey, cmdRsp) # Queue up command for sending via devices.py
def Sat(devKey, satPercentage): nwkId = database.GetDeviceItem(devKey, "nwkId") ep = database.GetDeviceItem(devKey, "endPoints") if nwkId and ep: satStr = format(int(float(satPercentage / 100) * 254), 'X').zfill(2) queue.EnqueueCmd(devKey, [ "AT+CCMVTOSAT:" + nwkId + "," + ep + ",0," + satStr + ",0001", "DFTREP" ]) # Fade over 100ms (in sec/10)
def do_devat(self, line): """devat name cmd Sends AT command to named device""" argList = line.split() if len(argList) >= 2: cmd = argList[1] devKey = devices.FindDev(argList[0]) if devKey != None: queue.EnqueueCmd(devKey, ["AT" + cmd, "OK"])
def Prod(devKey): # Ask device a question, just to provoke a response nwkId = database.GetDeviceItem(devKey, "nwkId") ep = database.GetDeviceItem(devKey, "endPoints") if nwkId != None and ep != None: log.debug("Prodding devKey " + str(devKey) + " (nwkId:" + nwkId + ")") cmdRsp = telegesis.ReadAttr( nwkId, ep, zcl.Cluster.Basic, zcl.Attribute.Model_Name ) # Get Basic's Device Name in order to prod it into life queue.EnqueueCmd(devKey, cmdRsp)
def Hue(devKey, hueDegree): nwkId = database.GetDeviceItem(devKey, "nwkId") ep = database.GetDeviceItem(devKey, "endPoints") if nwkId and ep: hueStr = format(int(float(hueDegree / 360) * 254), 'X').zfill(2) queue.EnqueueCmd(devKey, [ "AT+CCMVTOHUE:" + nwkId + "," + ep + ",0," + hueStr + ",00,0001", "DFTREP" ]) # Fade over 100ms (in sec/10)
def Identify( devKey, durationS ): # Duration in seconds to flash the device's LED. Use duration=0 to stop. nwkId = database.GetDeviceItem(devKey, "nwkId") ep = database.GetDeviceItem(devKey, "endPoints") if nwkId and ep: durationStr = format(int(durationS), 'X').zfill(4) queue.EnqueueCmd(devKey, [ "AT+IDENTIFY:" + nwkId + "," + ep + ",0," + durationStr, "DFTREP" ]) # Identify for selected time
def Sat(devKey, satPercentage): protocol = database.GetDeviceItem(devKey, "protocol") nwkId = database.GetDeviceItem(devKey, "nwkId") ep = database.GetDeviceItem(devKey, "endPoints") if protocol == "ZigbeeHA" and nwkId != None and ep != None: satStr = format(int(float(satPercentage / 100) * 254), 'X').zfill(2) queue.EnqueueCmd(devKey, [ "AT+CCMVTOSAT:" + nwkId + "," + ep + ",0," + satStr + ",0001", "DFTREP" ]) # Fade over 100ms (in sec/10)
def Hue(devKey, hueDegree): protocol = database.GetDeviceItem(devKey, "protocol") nwkId = database.GetDeviceItem(devKey, "nwkId") ep = database.GetDeviceItem(devKey, "endPoints") if protocol == "ZigbeeHA" and nwkId != None and ep != None: hueStr = format(int(float(hueDegree / 360) * 254), 'X').zfill(2) queue.EnqueueCmd(devKey, [ "AT+CCMVTOHUE:" + nwkId + "," + ep + ",0," + hueStr + ",00,0001", "DFTREP" ]) # Fade over 100ms (in sec/10)
def SwitchOn(devKey): nwkId = database.GetDeviceItem(devKey, "nwkId") ep = database.GetDeviceItem(devKey, "endPoints") dimLevel = database.GetDeviceItem(devKey, "dimLevel") inClstr = database.GetDeviceItem( devKey, "inClusters") # For checking whether we have LevelCtrl if nwkId and ep: #database.UpdateLoggedItem(devKey, "State", "SwitchOn") # So that we can access it from the rules later devices.SetTempVal(devKey, "JustSentOnOff", "True") devices.SetTempVal(devKey, "ExpectOnOff", "SwitchOn") queue.EnqueueCmd(devKey, ["AT+RONOFF:" + nwkId + "," + ep + ",0,1", "OK" ]) # Assume FFD if it supports OnOff cluster if zcl.Cluster.LevelCtrl in inClstr and dimLevel != 100: # Queue up a dimming command if available and we're at a different dimness queue.EnqueueCmd( devKey, ["AT+LCMVTOLEV:" + nwkId + "," + ep + ",0,1,FE,0001", "OK" ]) # Ensure fully bright ready to be turned on database.SetDeviceItem( devKey, "dimLevel", 100) # Assume the LevelCtrl command above works
def EventHandler(eventId, eventArg): global ser, txBuf, rxBuf if eventId == events.ids.INIT: serPort = config.Get("tty", '/dev/ttyUSB0') serSpeed = config.Get("baud", '19200') ser = serial.Serial(serPort, int(serSpeed), timeout=0) ser.flushInput() if database.GetDevicesCount()==0: # If we have no devices yet, then... devices.Add("0000", "N/A", "COO") # ...make sure we add this device as the first item before we try to use it! queue.EnqueueCmd(0, ["ATS63=0007", "OK"]) # Request RSSI & LQI on every received message, also disable automatic checkIn responses queue.EnqueueCmd(0, ["ATS0F=0400", "OK"]) # Use 0600 to set bit 9 (bit 10 already set) to get rawzcl responses so we can see schedule responses from thermostat if database.GetDeviceItem(0, "modelName") == None: queue.EnqueueCmd(0, ["ATI", "OK"]) # Request our EUI, as well as our Telegesis version queue.EnqueueCmd(0, ["AT+N", "OK"]) # Get network information, to see whether to start new network or use existing one elif eventId == events.ids.SECONDS: HandleSerial(ser) while len(rxBuf): Parse(rxBuf.popleft()) elif eventId == events.ids.RADIO_INFO: print(ourChannel+","+ourPowLvl+","+ourPan+","+ourExtPan) # Formatted to make it easy to extract in php elif eventId == events.ids.INFO: print("TxBuf: ", str(txBuf))
def SwitchOff(devKey): nwkId = database.GetDeviceItem(devKey, "nwkId") ep = database.GetDeviceItem(devKey, "endPoints") if nwkId and ep: #database.UpdateLoggedItem(devKey, "State", "SwitchOff") # So that we can access it from the rules later devices.DelTempVal( devKey, "SwitchOff@" ) # Remove any pending "Off" events if we're turning the device off directly devices.SetTempVal(devKey, "JustSentOnOff", "True") devices.SetTempVal(devKey, "ExpectOnOff", "SwitchOff") queue.EnqueueCmd(devKey, ["AT+RONOFF:" + nwkId + "," + ep + ",0,0", "OK" ]) # Assume FFD if it supports OnOff cluster database.SetDeviceItem(devKey, "dimLevel", 0) # Set dimness to 0
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 Dim(devKey, level): nwkId = database.GetDeviceItem(devKey, "nwkId") ep = database.GetDeviceItem(devKey, "endPoints") if nwkId and ep: if level > 1: # Assume it's a percentage level = level / 100 # Convert to a fraction levelStr = format(int(level * 254), 'X').zfill(2) queue.EnqueueCmd(devKey, [ "AT+LCMVTOLEV:" + nwkId + "," + ep + ",0,1," + levelStr + ",000A", "DFTREP" ]) # Fade over 1 sec (in 10ths) if level == 0: SwitchOff(devKey) database.SetDeviceItem(devKey, "dimLevel", level * 100) # Assume the LevelCtrl command above works
def GetDaySchedule( devKey, dayOfWeek="Sun"): # Ask Thermostat/Boiler device for its schedule global thermoDevKey nwkId = CheckThermostat(devKey) if nwkId == None: return # Make sure it's a real thermostat device before continuing thermoDevKey = devKey ep = database.GetDeviceItem(devKey, "endPoints") frameCtl = "11" seqId = "00" dayOfWeekIndex = iottime.GetDowIndex(dayOfWeek) dayBit = 2**dayOfWeekIndex # ** is "raise to the power". Assumes dayOfWeek is a int where 0=Sunday, 1=Monday, etc. cmdRsp = ("AT+RAWZCL:" + nwkId + "," + ep + "," + zcl.Cluster.Thermostat + "," + frameCtl + seqId + zcl.Commands.GetSchedule + "{:02X}".format(dayBit) + "01", "CWSCHEDULE" ) # Get heating(01) schedule queue.EnqueueCmd(devKey, cmdRsp) # Queue up command for sending via devices.py
def do_at(self, line): """at cmd Sends AT command to Telegesis stick""" queue.EnqueueCmd(0, ["AT" + line, "OK"])
def do_open(self, line): """open Opens network (for 60s) to allow new device to join""" queue.EnqueueCmd(0, ["AT+PJOIN", "OK"])
def SetTime(): # Set time up for HA devices to synchronise to timeVal = datetime.now() timeVal = time.mktime(timeVal.timetuple()) zigBeeTime = iottime.ToZigbee(timeVal) # Get local time in Unix epoch (1/Jan/1970) and convert it to Zigbee standard queue.EnqueueCmd(0, ["AT+SETTIME:{:08X}".format(int(zigBeeTime)), "OK"]) # Set time in CICIE ready for setting up time server
def do_open(self, line): """open Opens network (for 60s) to allow new device to join""" log.debug("----- Got open command - sending AT+PJOIN ------") queue.EnqueueCmd(0, ["AT+PJOIN", "OK"])
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 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()