Exemplo n.º 1
0
def SwitchOff(devIdx):
    devId = GetVal(devIdx, "devId")
    ep = GetVal(devIdx, "EP")
    if devId and ep:
        DelTempVal(
            devIdx, "SwitchOff@"
        )  # Remove any pending "Off" events if we're turning the device off directly
        telegesis.TxCmd(
            ["AT+LCMVTOLEV:" + devId + "," + ep + ",0,0,FE,0001",
             "OK"])  # Ensure fully bright ready to be turned on later
        SetTempVal(devIdx, "JustSentOnOff", "True")
        telegesis.TxCmd(["AT+RONOFF:" + devId + "," + ep + ",0,0",
                         "OK"])  # Assume FFD if it supports OnOff cluster
Exemplo n.º 2
0
def SwitchOn(devIdx):
    devId = GetVal(devIdx, "devId")
    ep = GetVal(devIdx, "EP")
    if devId and ep:
        SetTempVal(devIdx, "JustSentOnOff", "True")
        telegesis.TxCmd(["AT+RONOFF:" + devId + "," + ep + ",0,1",
                         "OK"])  # Assume FFD if it supports OnOff cluster
Exemplo n.º 3
0
def Dim(devIdx, levelFraction):
    devId = GetVal(devIdx, "devId")
    ep = GetVal(devIdx, "EP")
    if devId and ep:
        levelStr = format(int(levelFraction * 254), 'X')
        telegesis.TxCmd([
            "AT+LCMVTOLEV:" + devId + "," + ep + ",0,1," + levelStr + ",000A",
            "OK"
        ])  # Fade over 1 sec (in 10ths)
Exemplo n.º 4
0
def Toggle(devIdx):
    devId = GetVal(devIdx, "devId")
    ep = GetVal(devIdx, "EP")
    if devId and ep:
        DelTempVal(
            devIdx, "SwitchOff@"
        )  # Remove any pending "Off" events if we're handling the device directly
        SetTempVal(devIdx, "JustSentOnOff", "True")
        telegesis.TxCmd(["AT+RONOFF:" + devId + "," + ep + ",0",
                         "OK"])  # Assume FFD if it supports OnOff cluster
Exemplo n.º 5
0
def SendPendingCommand():
    global info
    devIdx = 0
    for device in info:
        if IsListening(devIdx):  # True if FFD, ZED or Polling
            offTime = GetTempVal(devIdx, "SwitchOff@")
            if offTime != None:
                if datetime.now() > offTime:
                    SwitchOff(devIdx)
            cmdRsp = Check(devIdx,
                           True)  # Automatically consume any pending command
            if cmdRsp != None:
                log.log("Sending " + str(cmdRsp))
                telegesis.TxCmd(cmdRsp)  # Send command directly
        devIdx = devIdx + 1
Exemplo n.º 6
0
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)
Exemplo n.º 7
0
 def do_at(self, line):
     """at cmd
     "Sends AT command to Telegesis stick"""
     telegesis.TxCmd(["AT" + line, "OK"])
Exemplo n.º 8
0
 def do_open(self, line):
     """open
     Opens network (for 60s) to allow new device to join"""
     telegesis.TxCmd(["AT+PJOIN", "OK"])
Exemplo n.º 9
0
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