Ejemplo n.º 1
0
def CheckReporting(devKey, reporting, field, cluster, attrId, attrType, defVal):
    global pendingRptAttrId
    rpt = cluster+":"+attrId
    if pendingRptAttrId == None and rpt not in reporting:
        pendingRptAttrId = attrId
        devRpt = database.GetDeviceItem(devKey, field, defVal)
        rptList = devRpt.split(",") # Explode the CSV line to a list of min, max, delta
        if "-1" == rptList[0]: return  # Don't configure this attribute for reporting if min==-1 
        log.debug("Update device reporting for "+database.GetDeviceItem(devKey, "userName")+"'s "+field)
        log.debug("Reporting was "+reporting+" which didn't include " + rpt)
        if attrType == zcl.AttributeTypes.Uint8 or attrType == zcl.AttributeTypes.Sint8:
            deltaLen = 2   # Need to know number of digits to use in deltaHex
        elif attrType == zcl.AttributeTypes.Uint16 or attrType == zcl.AttributeTypes.Sint16:
            deltaLen = 4   # Need to know number of digits to use in deltaHex
        elif attrType == zcl.AttributeTypes.Uint24 or attrType == zcl.AttributeTypes.Sint24:
            deltaLen = 6   # Need to know number of digits to use in deltaHex
        elif attrType == zcl.AttributeTypes.Uint32 or attrType == zcl.AttributeTypes.Sint32:
            deltaLen = 8   # Need to know number of digits to use in deltaHex
        elif attrType == zcl.AttributeTypes.Uint48:
            deltaLen = 12   # Need to know number of digits to use in deltaHex
        else:
            deltaLen = 0    # Don't know format, so fail
        if deltaLen != 0:
            deltaLenFmt = "%0."+str(deltaLen)+"X"   # Work out how many digits to use given the attribute type
            minHex = "%0.4X" % int(rptList[0]) # Convert each decimal item to a hex string
            if int(rptList[1]) == -1:
                maxHex = "FFFF" # To disable this report
            else:
                maxHex = "%0.4X" % int(rptList[1])
            deltaHex = deltaLenFmt % int(rptList[2])    # Use the correct number of leading zeros for delta's hex representation
            nwkId = database.GetDeviceItem(devKey, "nwkId")
            ep = database.GetDeviceItem(devKey, "endPoints")
            return ("AT+CFGRPT:"+nwkId+","+ep+",0,"+cluster+",0,"+attrId+","+attrType+","+minHex+","+maxHex+","+deltaHex, "CFGRPTRSP")
    return None
Ejemplo n.º 2
0
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
Ejemplo n.º 3
0
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)
Ejemplo n.º 4
0
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)
Ejemplo n.º 5
0
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)
Ejemplo n.º 6
0
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)
Ejemplo n.º 7
0
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)
Ejemplo n.º 8
0
def CheckThermostat(devKey):
    nwkId = database.GetDeviceItem(devKey, "nwkId")
    if nwkId == None:
        return None  # Make sure it's a real device before continuing (it may have just been deleted)
    inClstr = database.GetDeviceItem(
        devKey,
        "inClusters")  # Assume we have a list of clusters if we get this far
    if zcl.Cluster.Thermostat not in inClstr:
        return None  # Not a thermostat
    return nwkId
Ejemplo n.º 9
0
def SetBinding(devKey, cluster, ourEp):
    global pendingBinding, pendingBindingTimeoutS
    nwkId = database.GetDeviceItem(devKey, "nwkId")
    ep = database.GetDeviceItem(devKey, "endPoints")
    eui = database.GetDeviceItem(devKey, "eui64")
    if None != ep and None != eui:
        pendingBinding[devKey] = cluster
        pollFreq = float(database.GetDeviceItem(devKey, "longPollInterval", 0))   # See if the device is a fast poller
        pendingBindingTimeoutS[devKey] = pollFreq+10 # Allow LongPollInterval and add 10 seconds to that.  Or just 10 seconds if an FFD
        return ("AT+BIND:"+nwkId+",3,"+eui+","+ep+","+cluster+","+database.GetDeviceItem(0, "eui64")+","+ourEp, "Bind")
Ejemplo n.º 10
0
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
Ejemplo n.º 11
0
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
Ejemplo n.º 12
0
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
Ejemplo n.º 13
0
def IsListening(devKey):
    type = database.GetDeviceItem(devKey, "devType")
    if type == "SED":
        pollFreq = database.GetDeviceItem(
            devKey, "longPollInterval")  # See if the device is a fast poller
        if pollFreq != None:
            if float(pollFreq) < 7.6:
                return True
        pollTime = GetTempVal(devKey, "PollingUntil")
        if pollTime != None:
            if datetime.now() < pollTime:
                return True
        return False
    else:  # Assume not sleepy
        return True  # These devices are always awake and listening
Ejemplo n.º 14
0
def EnsureReporting(devKey, clstrId, attrId, attrVal): # Check when this attr last reported and update device's reporting if necessary
    if isnumeric(attrVal, 16):
        newVal = int(attrVal, 16) # Assume value arrives in hex
    else:
        return
    if clstrId == zcl.Cluster.SimpleMetering and attrId == zcl.Attribute.InstantaneousDemand:
        prevItem = database.GetLatestLoggedItem(devKey, "PowerReadingW")  # Check when we got last reading
        field = "powerReporting"
    #elif clstrId == zcl.Cluster.SimpleMetering and attrId == zcl.Attribute.CurrentSummationDelivered:  # These energy* fields don't work, so interpolate power over time
    #    prevItem = database.GetLatestLoggedItem(devKey, "EnergyConsumedWh")
    #    field = "energyConsumedReporting"
    #elif clstrId == zcl.Cluster.SimpleMetering and attrId == zcl.Attribute.CurrentSummationReceived:
    #    prevItem = database.GetLatestLoggedItem(devKey, "EnergyGeneratedWh")
    #    field = "energyGeneratedReporting"
    else: # Don't know how often it should report.  Could add temperature and battery
        return
    if prevItem != None:
        prevTime = prevItem[1]
        prevVal = prevItem[0]
        minMaxDelta = database.GetDeviceItem(devKey, field) # Look up min and max for this item
        if minMaxDelta != None:
            confList = minMaxDelta.split(",")
            min = int(confList[0])
            max = int(confList[1])
            delta = int(confList[2])
            change = abs(newVal - int(prevVal))
            secsSinceLastReport = (datetime.now() - datetime.strptime(prevTime, "%Y-%m-%d %H:%M:%S")).seconds  # Work out time between now and prevTime in seconds
            log.debug("Prev report "+str(secsSinceLastReport)+" s ago, min="+str(min)+" for devKey "+str(devKey)+" with old val="+str(prevVal)+" vs new val of "+str(newVal))
            if max == -1 or secsSinceLastReport < min or secsSinceLastReport > max: # Check whether min>secsSincelastReport>max or max==-1
                Config(devKey, field) # Re-configure device
            elif secsSinceLastReport < max-(max/10) and change<delta: # Check delta if not too close to max
                Config(devKey, field) # Re-configure device
Ejemplo n.º 15
0
def EnsureInBinding(devKey, clusterId):  # Put clusterId in binding if not there already
    entry = database.GetDeviceItem(devKey, "binding", "[]")
    binding = eval(entry)
    #log.debug("Binding is "+str(binding))
    if clusterId not in binding:
        binding.append(clusterId)
        database.SetDeviceItem(devKey, "binding", str(binding))
Ejemplo n.º 16
0
def Remove(devKey):
    if IsListening(devKey):
        nwkId = database.GetDeviceItem(devKey, "nwkId")
        if nwkId:
            telegesis.Leave(nwkId)    # Tell device to leave the network immediately (assuming it's listening)
    database.RemoveDevice(devKey)
    devDict[devKey] = -1    # Remove link between the key and its index (but don't remove the entry in the dict)
Ejemplo n.º 17
0
def Config(devKey, field):
    # Read newly changed field from database for device and use this to update actual device ASAP
    log.debug("Updating reporting for " + field)
    reporting = database.GetDeviceItem(
        devKey, "reporting"
    )  # See if we're expecting this report, and note it in the reporting table
    if field == "batteryReporting":
        rptToUpdate = zcl.Cluster.PowerConfig + ":" + zcl.Attribute.Batt_Percentage
    elif field == "temperatureReporting":
        rptToUpdate = zcl.Cluster.Temperature + ":" + zcl.Attribute.Celsius
    elif field == "powerReporting":
        rptToUpdate = zcl.Cluster.SimpleMetering + ":" + zcl.Attribute.InstantaneousDemand
    elif field == "energyConsumedReporting":
        rptToUpdate = zcl.Cluster.SimpleMetering + ":" + zcl.Attribute.CurrentSummationDelivered
    elif field == "energyGeneratedReporting":
        rptToUpdate = zcl.Cluster.SimpleMetering + ":" + zcl.Attribute.CurrentSummationReceived
    else:
        rptToUpdate = None
    if reporting != None and rptToUpdate != None:
        reportList = eval(reporting)
        if rptToUpdate in reportList:
            reportList.remove(
                rptToUpdate
            )  # Remove newly changed item from list so that Check() routine will spot this and update the reporting accordingly
            updatedReporting = str(reportList)
            database.SetDeviceItem(
                devKey, "reporting",
                updatedReporting)  # Ready for Check() to send new item
Ejemplo n.º 18
0
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
Ejemplo n.º 19
0
def ParseCWShedule(eventArg):
    global thermoDevKey
    if eventArg[0] != "CWSCHEDULE" or len(eventArg) < 5:
        return
    devKey = devices.GetKey(eventArg[1])
    if devKey == None:
        return
    devices.NoteMsgDetails(devKey, eventArg)
    ep = eventArg[2]
    numSetpoints = int(eventArg[3], 16)
    dayOfWeekBitMap = int(eventArg[4], 16)
    dayOfWeekIndex = int(math.log(dayOfWeekBitMap, 2))
    dayOfWeek = iottime.GetDow(dayOfWeekIndex)
    # Assume eventArg[5] holds "01" for heating schedule
    if len(eventArg) < 5 + (
            2 * numSetpoints
    ):  # Sanity check that we have all the bits of schedule we expect
        return
    newSchedule = []
    for index in range(0, numSetpoints):
        minutesSinceMidnight = eventArg[6 + (
            index * 2)]  # Value in hex as number of minutes since midnight
        targetTemp = eventArg[6 + (
            index * 2) + 1]  # Value in hex as targetTemp in units of 1/100'C
        secs = int(minutesSinceMidnight, 16) * 60
        timeOfDay = time.strftime("%H:%M", time.gmtime(secs))
        scheduleTemp = int(targetTemp, 16) / 100
        newSchedule.append((timeOfDay, scheduleTemp))
    username = database.GetDeviceItem(devKey, "userName")
    log.debug("Schedule from " + username + " for " + dayOfWeek + " is " +
              str(newSchedule))
    scheduleType = username  # was config.Get("HeatingSchedule", "Heating")
    database.SetSchedule(scheduleType, dayOfWeek, str(
        newSchedule))  # Update the database from the Thermostat/boiler device
Ejemplo n.º 20
0
def DeviceRun(devKey, restOfRule):  # Run rule for specified device
    userName = database.GetDeviceItem(devKey, "userName")
    Run(userName + restOfRule)
    groupList = database.GetGroupsWithDev(
        devKey
    )  # Check if device is in any groups and run appropriate rules for each group
    for name in groupList:
        Run(name + restOfRule)
Ejemplo n.º 21
0
def SetTime(devKey):
    protocol = database.GetDeviceItem(devKey, "protocol")
    timeVal = datetime.now()
    timeVal = time.mktime(timeVal.timetuple())
    zbTime = iottime.ToZigbee(timeVal)
    timeStr = format(int(zbTime), 'X').zfill(8)
    if protocol == "ZigbeeHA":
        telegesis.TxWriteAttr(devKey, zcl.Cluster.Time, zcl.Attribute.Time,
                              zcl.AttributeTypes.UtcTime, timeStr)
Ejemplo n.º 22
0
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
Ejemplo n.º 23
0
def NoteReporting(devKey, clusterId, attrId):
    reporting = database.GetDeviceItem(devKey, "reporting") # See if we're expecting this report, and note it in the reporting table
    newRpt = clusterId+":"+attrId
    if reporting != None:
        reportList = eval(reporting)
        if newRpt not in reportList:
            reportList.append(newRpt)
            reporting = str(reportList)
    else:
        reporting = "['"+newRpt+"']"
    database.SetDeviceItem(devKey, "reporting", reporting) # Ready for next time
    log.debug("New reporting entry of "+reporting+" with cluster:"+clusterId+" and attrId:"+attrId+" for devKey:"+str(devKey))
    EnsureInBinding(devKey, clusterId)   # Assume reportable items must be in binding for us to receive them, so make sure this is up-to-date
Ejemplo n.º 24
0
def Check():  # Expected to be called infrequently - ie once/minute
    keyList = database.GetAllDevKeys(
    )  # Get a list of all the device identifiers from the database
    notHeardFromList = []
    for devKey in keyList:  # Element 0 is hub, rest are devices
        if database.GetDeviceItem(devKey, "nwkId") != "0000":  # Ignore hub
            lastSeen, presence = Get(devKey)
            if datetime.now() > lastSeen + timedelta(
                    seconds=900
            ) and "SED" != database.GetDeviceItem(
                    devKey, "devType"
            ):  # More than 15 minutes since we last heard from device, and it's listening
                notHeardFromList.append(
                    devKey)  # Make a list of devices to query
            if presence != states.absent and datetime.now(
            ) > lastSeen + timedelta(
                    seconds=1800
            ):  # More than 30 minutes since we last heard from device
                Set(devKey, states.absent)
            #if presence != states.present:
            #    notHeardFromList.append(devKey)    # Make a list of devices to query
    if notHeardFromList != []:
        log.debug("List of missing (or nearly missing) devices: " +
                  str(notHeardFromList))
        numDevs = len(notHeardFromList)
        if numDevs > 3:
            for i in range(3):  # Prod 3 random devices from list
                devKey = random.choice(notHeardFromList)
                devcmds.Prod(
                    devKey
                )  # Ask device a question, just to provoke a response
                notHeardFromList.remove(
                    devKey
                )  # Having prodded it, make sure we don't prod it again
        else:  # Prod each device in list
            for devKey in notHeardFromList:
                devcmds.Prod(
                    devKey
                )  # Ask device a question, just to provoke a response
Ejemplo n.º 25
0
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
Ejemplo n.º 26
0
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))
Ejemplo n.º 27
0
def GetAvailability(devKey):  # Over last 24 hours
    lastSeen, presence = Get(devKey)
    userName = database.GetDeviceItem(devKey, "userName")
    if userName == None:
        return "Unknown name for device"
    if lastSeen < datetime.now() - timedelta(
            hours=24):  # Check if any change in last 24 hours
        if presence == states.present:
            availability = ""  # Perfect availability for the whole time
        else:  # Assume absent
            availability = userName + " has been missing all day"
    else:  # There's been some changes in presence in the last 24 hours
        entries = database.GetLoggedItemsSinceTime(
            devKey, "Presence", "datetime('now', '-1 day')")
        #log.debug(userName+"'s presence for last 24 hours;"+str(entries))
        if entries == None:
            availability = "No presence for " + userName
        elif len(
                entries
        ) == 1:  # The device has changed only once in the last 24 hours
            lastTwoEntries = database.GetLastNLoggedItems(
                devKey, "Presence", 2)  # Get last 2 entries
            if len(lastTwoEntries
                   ) == 1:  # The device has changed only once ever
                availability = ""  # Perfect availability for the whole time
            else:  # Check when it changed, and what it changed to to work out
                availability = ""  # Hasn't changed enough to be a worry
        elif len(entries) > 1:
            availability = userName + "'s availability changed " + str(
                len(entries)
            ) + " times in last 24 hours and is now " + presence  # Changeable
        else:
            if presence == states.present:
                availability = ""  # Perfect availability for the whole time
            else:  # Assume absent or unknown
                availability = userName + " has not been heard from all day"
    return availability
Ejemplo n.º 28
0
def EventHandler(eventId, eventArg):
    if eventId == events.ids.TRIGGER:
        devKey = devices.GetKey(
            eventArg[1])  # Lookup device from network address in eventArg[1]
        if devKey != None:
            devices.NoteMsgDetails(devKey, eventArg)  # Note presence
            now = datetime.now()
            nowStr = now.strftime("%H:%M")
            zoneType = database.GetDeviceItem(devKey,
                                              "iasZoneType")  # Device type
            if zoneType != None:
                oldState = database.GetLatestEvent(devKey)
                if zoneType == zcl.Zone_Type.Contact:
                    if int(eventArg[3], 16) & 1:  # Bottom bit indicates alarm1
                        newState = "opened"
                    else:
                        newState = "closed"
                    if oldState != newState:  # NB Might get same state if sensor re-sends, or due to battery report
                        database.UpdateLoggedItem(
                            devKey, "State", newState
                        )  # So that we can access it from the rules later
                        database.NewEvent(
                            devKey, newState
                        )  # For web page.  Only update event log when state changes
                        DeviceRun(devKey, "==" + newState
                                  )  # See if rule exists (when state changes)
                        #log.debug("Door "+ eventArg[1]+ " "+newState)
                elif zoneType == zcl.Zone_Type.PIR:
                    if int(eventArg[3], 16) & 1:  # Bottom bit indicates alarm1
                        newState = "active"
                        devices.SetTempVal(
                            devKey, "PirInactive@",
                            datetime.now() + timedelta(seconds=300))
                    else:
                        newState = "inactive"  # Might happen if we get an IAS battery report
                    if oldState != newState:
                        database.UpdateLoggedItem(
                            devKey, "State", newState
                        )  # So that we can access it from the rules later
                        database.NewEvent(
                            devKey, newState
                        )  # For web page.  Only update event log when state changes
                    DeviceRun(devKey, "==" + newState)  # See if rule exists
                else:
                    log.debug("DevId: " + eventArg[1] + " zonestatus " +
                              eventArg[3])
            else:
                log.fault("Unknown IAS device type for devId " + eventArg[1])
        else:  # devKey == None
            telegesis.Leave(
                eventArg[1]
            )  # Tell device to leave the network, since we don't know anything about it
    elif eventId == events.ids.BUTTON:
        devKey = devices.GetKey(
            eventArg[1])  # Lookup device from network address in eventArg[1]
        if devKey != None:
            #log.debug("Button "+ eventArg[1]+ " "+eventArg[0]) # Arg[0] holds "ON", "OFF" or "TOGGLE" (Case might be wrong)
            database.NewEvent(devKey, "pressed")  # For web page
            DeviceRun(devKey, "==" + eventArg[0])  # See if rule exists
        else:  # devKey == None
            telegesis.Leave(
                eventArg[1]
            )  # Tell device to leave the network, since we don't know anything about it
    elif eventId == events.ids.MULTISTATE:
        devKey = devices.GetKey(
            eventArg[1])  # Lookup device from network address in eventArg[1]
        if devKey != None:
            database.NewEvent(devKey,
                              "MultiState==" + eventArg[0])  # For web page
            DeviceRun(devKey, "==" + eventArg[0])  # See if rule exists
        else:  # devKey == None
            telegesis.Leave(
                eventArg[1]
            )  # Tell device to leave the network, since we don't know anything about it
Ejemplo n.º 29
0
def BuildPage():
    upTime = datetime.now() - iottime.appStartTime
    absUrl = config.Get("vestaURL", "")
    log.debug("Building status page")
    txt = open("synopsis.txt", "w")  # Create text file for txt
    html = open(
        "synopsis.html", "w"
    )  # Create local html file, so we can copy it for Apache to serve up, or mail directly
    html.write("\n<html><head>")  # Blank line at start
    html.write(
        "<META HTTP-EQUIV=\"CACHE-CONTROL\" CONTENT=\"NO-CACHE, no-store, must-revalidate\">"
    )  # Try to force browser to discard any previous version of this page
    html.write("</head><body>")
    html.write("<center><h1>Vesta Status</h1>")
    txt.write("Vesta Status\n\n")
    writeLine(
        "At " + datetime.now().strftime("%H:%M") + " on " +
        datetime.now().strftime("%Y/%m/%d"), html, txt)
    writeLine("Uptime: %d days, %.2d:%.2d" %
              (upTime.days, upTime.seconds // 3600,
               (upTime.seconds // 60) % 60), html,
              txt)  # Cribbed from "uptime" command
    writeLine("", html, txt)  # Just newline
    keyList = database.GetAllDevKeys(
    )  # Get a list of all the device identifiers from the database
    noProblems = True
    restartCount = database.CountEvents(0, "App started",
                                        "date('now', '-1 days')")
    if restartCount > 0:
        noProblems = False
        writeLine(
            "App restarted " + str(restartCount) +
            " times in the last 24 hours", html, txt)
    else:  # Don't check availablilty of devices if app has restarted
        for devKey in keyList:  # Element 0 is hub, rest are devices
            if database.GetDeviceItem(devKey, "nwkId") != "0000":  # Ignore hub
                availability = presence.GetAvailability(devKey)
                if availability != "":  # If device missing even only occasionally, tell user (Empty string means "fine")
                    noProblems = False
                    writeLine(availability, html, txt)
    dbSize = database.GetFileSize()
    if (dbSize / len(keyList)) > (30 *
                                  1024):  # Arbitrary limit of 30K per device
        noProblems = False
        problem(
            "dbSize", "Database file size is " +
            "{0:.2f}".format(dbSize / (1024 * 1024)) + "MB which is " +
            "{0:.0f}".format((dbSize / len(keyList)) / 1024) + "KB per device")
    errList = glob.glob(
        "/home/pi/Vesta/*_err.log")  # Get list of all error logs
    numLogs = len(errList)
    if numLogs:
        noProblems = False
        if numLogs == 1:
            problem("error_logs", "1 error log")
        else:
            problem("error_logs", str(numLogs) + " error logs")
    if len(issues) > 0:
        noProblems = False
        for items in issues.values():
            writeLine(items, html, txt)
    if noProblems:
        writeLine("Everything OK!", html, txt)
    writeLine("", html, txt)  # Just newline
    writeLine("(Vesta v" + vesta.GetVersion() + ")", html, txt)
    html.write("<br><center><a href=\"" + absUrl +
               "/vesta/index.php\"><img src=\"" + absUrl +
               "/vesta/vestaLogo.png\" width=32 height=32 title=\"Home\"></a>")
    html.write("</body></html>")
    html.close()
    txt.close()
    os.system(
        "sudo cp synopsis.html /var/www/html/vesta"
    )  # So vesta.php can refer to it.  Will overrwrite any previous status
Ejemplo n.º 30
0
def Check(devKey):
    global pendingBinding, msp_ota
    if devKey == 0: return  # We don't need anything from the hub
    nwkId = database.GetDeviceItem(devKey, "nwkId")
    if None == nwkId:
        return  # Make sure it's a real device before continuing (it may have just been deleted)
    ep = database.GetDeviceItem(devKey, "endPoints")
    if None == ep:
        return (["AT+ACTEPDESC:" + nwkId + "," + nwkId, "ActEpDesc"])
    eui = database.GetDeviceItem(devKey, "eui64")
    if None == eui: return (["AT+EUIREQ:" + nwkId + "," + nwkId, "AddrResp"])
    inClstr = database.GetDeviceItem(
        devKey, "inClusters",
        "[]")  # Assume we have a list of clusters if we get this far
    if "[]" == inClstr:
        return ([
            "AT+SIMPLEDESC:" + nwkId + "," + nwkId + "," + ep, "OutCluster"
        ])
    outClstr = database.GetDeviceItem(devKey, "outClusters", "[]")
    binding = database.GetDeviceItem(devKey, "binding" "[]")
    if str(pendingBinding[devKey]
           ) == "":  # Only try to add one binding per device at once
        if zcl.Cluster.PollCtrl in inClstr and zcl.Cluster.PollCtrl not in binding:
            return SetBinding(
                devKey, zcl.Cluster.PollCtrl,
                "01")  # 01 is our endpoint we want CHECKIN messages to come to
        if zcl.Cluster.OnOff in outClstr and zcl.Cluster.OnOff not in binding:  # If device sends OnOff commands (eg a Button)
            return SetBinding(
                devKey, zcl.Cluster.OnOff, "0A"
            )  # 0A is our endpoint we want messages to come to (so that we get TOGGLE, ON and OFF commands)
        if zcl.Cluster.Temperature in inClstr and zcl.Cluster.Temperature not in binding:
            return SetBinding(
                devKey, zcl.Cluster.Temperature, "01"
            )  # 01 is our endpoint we want Temperature reports to come to
        if zcl.Cluster.SimpleMetering in inClstr and zcl.Cluster.SimpleMetering not in binding:
            return SetBinding(
                devKey, zcl.Cluster.SimpleMetering, "01"
            )  # 01 is our endpoint we want SimpleMetering messages to come to
        if zcl.Cluster.Thermostat in inClstr and zcl.Cluster.Thermostat not in binding:
            return SetBinding(
                devKey, zcl.Cluster.Thermostat, "01"
            )  # 01 is our endpoint we want Thermostat messages to come to
    if zcl.Cluster.IAS_Zone in inClstr:
        if None == database.GetDeviceItem(devKey, "iasZoneType"):
            return telegesis.ReadAttr(
                nwkId, ep, zcl.Cluster.IAS_Zone, zcl.Attribute.Zone_Type
            )  # Get IAS device type (PIR or contact, etc.)
    if zcl.Cluster.Basic in inClstr:
        if None == database.GetDeviceItem(devKey, "modelName"):
            return telegesis.ReadAttr(
                nwkId, ep, zcl.Cluster.Basic,
                zcl.Attribute.Model_Name)  # Get Basic's Device Name
        if None == database.GetDeviceItem(devKey, "manufName"):
            return telegesis.ReadAttr(
                nwkId, ep, zcl.Cluster.Basic,
                zcl.Attribute.Manuf_Name)  # Get Basic's Manufacturer Name
    if zcl.Cluster.PowerConfig in inClstr and "SED" == database.GetDeviceItem(
            devKey, "devType"):
        checkBatt = GetTempVal(devKey, "GetNextBatteryAfter")
        if checkBatt != None:
            if datetime.now() > checkBatt:
                log.debug("Now = " + str(datetime.now()) +
                          " and checkBatt = " + str(checkBatt))
                return telegesis.ReadAttr(
                    nwkId, ep, zcl.Cluster.PowerConfig,
                    zcl.Attribute.Batt_Percentage)  # Get Battery percentage
    if zcl.Cluster.PollCtrl in inClstr:
        if None == database.GetDeviceItem(devKey, "longPollInterval"):
            return telegesis.ReadAttr(
                nwkId, ep, zcl.Cluster.PollCtrl, zcl.Attribute.
                LongPollIntervalQs)  # Get Poll Control's Long poll interval
    if zcl.Cluster.OTA in outClstr:
        if None == database.GetDeviceItem(devKey, "firmwareVersion"):
            return ("AT+READCATR:" + nwkId + "," + ep + ",0," +
                    zcl.Cluster.OTA + "," + zcl.Attribute.firmwareVersion,
                    "RESPATTR"
                    )  # Get OTA's Version number as a string of hex digits
    if msp_ota != None and msp_ota in outClstr:
        if None == database.GetDeviceItem(devKey, "firmwareVersion"):
            return ("AT+READMCATR:" + nwkId + "," + ep + ",0," +
                    config.Get(mfgId) + "," + msp_ota + "," +
                    zcl.Attribute.firmwareVersion, "RESPMATTR"
                    )  # Get OTA's Version number as a string of hex digits
    reporting = database.GetDeviceItem(devKey, "reporting", "[]")
    if zcl.Cluster.PowerConfig in binding and "SED" == database.GetDeviceItem(
            devKey, "devType"):
        atCmd = CheckReporting(
            devKey, reporting, "batteryReporting", zcl.Cluster.PowerConfig,
            zcl.Attribute.Batt_Percentage, zcl.AttributeTypes.Uint8,
            "43200,43200,2"
        )  # Default temperature reporting is "Every 12 hours"
        if atCmd != None: return atCmd
    if zcl.Cluster.Temperature in binding:
        atCmd = CheckReporting(
            devKey, reporting, "temperatureReporting", zcl.Cluster.Temperature,
            zcl.Attribute.Celsius, zcl.AttributeTypes.Uint16, "300,3600,100"
        )  # Default temperature reporting is "between 5 mins and 1 hr, or +/- 1.00'C"
        if atCmd != None: return atCmd
    if zcl.Cluster.SimpleMetering in binding:
        atCmd = CheckReporting(
            devKey, reporting, "powerReporting", zcl.Cluster.SimpleMetering,
            zcl.Attribute.InstantaneousDemand, zcl.AttributeTypes.Sint24,
            "-1,-1,10"
        )  # Default power reporting is "between 5 seconds and 15 minutes, or +/- 10W"
        if atCmd != None: return atCmd
        atCmd = CheckReporting(
            devKey, reporting, "energyConsumedReporting",
            zcl.Cluster.SimpleMetering,
            zcl.Attribute.CurrentSummationDelivered, zcl.AttributeTypes.Uint48,
            "-1,-1,100"
        )  # Default energy consumed reporting is "between 1 minute and 15 minutes, or +100Wh"
        if atCmd != None: return atCmd
        atCmd = CheckReporting(
            devKey, reporting, "energyGeneratedReporting",
            zcl.Cluster.SimpleMetering, zcl.Attribute.CurrentSummationReceived,
            zcl.AttributeTypes.Uint48, "-1,-1,0"
        )  # Default energy generated reporting is "never" (-1 as max)
        if atCmd != None: return atCmd
    if zcl.Cluster.Thermostat in binding:
        atCmd = CheckReporting(
            devKey, reporting, "targetTempReporting", zcl.Cluster.Thermostat,
            zcl.Attribute.OccupiedHeatingSetPoint, zcl.AttributeTypes.Sint16,
            "60,900,100"
        )  # Default target temperature reporting is "between 1 minute and 15 minutes, or +/-1.00'C"
        if atCmd != None: return atCmd
    if GetTempVal(devKey, "JustSentOnOff"):
        DelTempVal(devKey, "JustSentOnOff")
        return telegesis.ReadAttr(
            nwkId, ep, zcl.Cluster.OnOff,
            zcl.Attribute.OnOffState)  # Get OnOff state after sending toggle
    return None