Exemplo n.º 1
0
def CommandDev(action, devKey, actList, ruleId):
    if devKey == None:
        log.fault("Device " + actList[1] + " from rules not found in devices")
        synopsis.problem("rules", "Unknown device " + actList[1] + " in rules")
    else:
        devices.DelTempVal(
            devKey, "SwitchOff@")  # Kill any extant timers for this device
        if action == "SwitchOn".lower():
            database.NewEvent(devKey, "SwitchOn", "Rule:" + str(ruleId))
            devcmds.SwitchOn(devKey)
            if len(actList) > 3:
                if actList[2] == "for":
                    SetOnDuration(devKey, int(actList[3], 10))
        elif action == "SwitchOff".lower():
            database.NewEvent(devKey, "SwitchOff", "Rule:" + str(ruleId))
            devcmds.SwitchOff(devKey)
        elif action == "Toggle".lower():
            #database.NewEvent(devKey, "Toggle", "Rule:"+str(ruleId)) # Removed, otherwise Toggle function can't work out old state!
            devcmds.Toggle(devKey)
        elif action == "Dim".lower() and actList[2] == "to":
            database.NewEvent(devKey, "Dim", "Rule:" + str(ruleId))
            devcmds.Dim(devKey, float(actList[3]))
            if len(actList) > 5:
                if actList[4] == "for":
                    SetDimDuration(devKey, int(actList[5], 10))
        elif action == "HueSat".lower(
        ):  # Syntax is "do HueSat <Hue in degrees>,<fractional saturation>
            database.NewEvent(devKey, "HueSat", "Rule:" + str(ruleId))
            devcmds.Colour(devKey, int(actList[3], 10), float(actList[4]))
        else:
            log.debug("Unknown action: " + action + " for device: " +
                      actList[1])
Exemplo n.º 2
0
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
Exemplo n.º 3
0
def EventHandler(eventId, eventArg):
    global varList
    if eventId == events.ids.NEWDAY:
        oldest = database.GetOldestVar()
        if oldest:
            log.debug("Oldest variable is " + oldest)
            log.debug(oldest + "'s date is " + GetTime(oldest))
            if (datetime.now() - datetime.strptime(
                    GetTime(oldest), "%Y-%m-%d %H:%M:%S")).days > 30:
                log.debug("Removing " + oldest +
                          " since it's more than 30 days old")
                Del(oldest)
    if eventId == events.ids.SECONDS:
        away = database.GetAppState("away")
        if away == None:
            away = "False"  # Assume at home if we don't know any better
            database.SetAppState("away",
                                 away)  # Ensure database has our same default
            Set(
                "away", away
            )  # Keep our variable in sync with the value from the database, so now user can update the db via the web and we'll know...
        oldAway = Get("away")
        if oldAway == None:
            oldAway = "Unknown"  # Force update
        if oldAway != away:
            log.debug("Away state has changed from " + oldAway + " to " + away)
            if away == "False":
                database.NewEvent(0, "Arrived Home")
            else:
                database.NewEvent(0, "Gone Away")
            Set(
                "away", away
            )  # Keep our variable in sync with the value from the database, so now user can update the db via the web and we'll know...
Exemplo n.º 4
0
def EventHandler(eventId, eventArg):
    global overrideTimeoutMins, currentTargetTemp
    if eventId == events.ids.MINUTES:
        name = config.Get("HeatingDevice")
        if name != None:
            heatingDevKey = devices.FindDev(
                name
            )  # Get heating device every minute to allow for it changing
            if heatingDevKey != None:  # Check that we have a real heating device
                schedule = config.Get("HeatingSchedule")
                if schedule != None:
                    scheduledTargetTemp = GetTarget(schedule)
                    if scheduledTargetTemp != None:  # We now have a heating device and a schedule to follow or override
                        if overrideTimeoutMins > 0:
                            overrideTimeoutMins = overrideTimeoutMins - 1
                            if overrideTimeoutMins <= 0:  # Just finished override
                                database.NewEvent(
                                    heatingDevKey,
                                    "Resume " + str(scheduledTargetTemp) +
                                    "'C")  # For ActivityLog on web page
                                currentTargetTemp = heating.SetTargetTemp(
                                    heatingDevKey, scheduledTargetTemp
                                )  # Resume schedule here
                            else:  # Still overriding
                                if scheduledTargetTemp != currentTargetTemp:  # Check whether schedule in heating device is about to change target
                                    database.NewEvent(
                                        heatingDevKey,
                                        "Overriding scheduled " +
                                        str(scheduledTargetTemp) + "'C with " +
                                        str(currentTargetTemp) +
                                        "C")  # For ActivityLog on web page
                                    # Un-indent following line to force override temp once/min while overriding, rather than just at change
                                    heating.SetTargetTemp(
                                        heatingDevKey, currentTargetTemp
                                    )  # Re-Set target in heating device (since it's also running the schedule)
                        else:  # Not overriding
                            if scheduledTargetTemp != currentTargetTemp:
                                database.NewEvent(
                                    heatingDevKey,
                                    "Scheduled " + str(scheduledTargetTemp) +
                                    "'C")  # For ActivityLog on web page
                                #heating.SetTargetTemp(heatingDevKey, scheduledTargetTemp)   # Set target in heating device here.  (Not needed since it's running the schedule directly)
                                currentTargetTemp = scheduledTargetTemp
                    # else: No scheduled target
                # else: No HeatingSchedule
            # else: Despite having a name, there's no associated device
            variables.Set("TargetTemp", str(currentTargetTemp))
        else:  # Ignore schedules and overrides if no named heating device
            synopsis.problem(
                "NoHeatingDevice",
                "No HeatingDevice entry in config, needed to resume after override"
            )
Exemplo n.º 5
0
 def do_off(self, devId):
     """off name
     Sends off command to named device"""
     devKey = devices.FindDev(devId)
     if devKey != None:
         database.NewEvent(devKey, "SwitchOff", "UICmd")
         devcmds.SwitchOff(devKey)
Exemplo n.º 6
0
 def do_toggle(self, devId):
     """toggle name
     Sends toggle on/off command to named device"""
     devKey = devices.FindDev(devId)
     if devKey != None:
         database.NewEvent(devKey, "Toggle", "UICmd")
         devcmds.Toggle(devKey)
Exemplo n.º 7
0
def EventHandler(eventId, eventArg):
    global varList
    if eventId == events.ids.SECONDS:
        away = database.GetAppState("away")
        if away == None:
            away = "False"    # Assume at home if we don't know any better
            database.SetAppState("away", away)  # Ensure database has our same default
            Set("away", away)  # Keep our variable in sync with the value from the database, so now user can update the db via the web and we'll know...
        oldAway = Get("away")
        if oldAway == None:
            oldAway = "Unknown" # Force update
        if oldAway != away:
            log.debug("Away state has changed from "+oldAway+" to "+away)
            if away == "False":
                database.NewEvent(0, "Arrived Home")
            else:
                database.NewEvent(0, "Gone Away")
            Set("away", away)  # Keep our variable in sync with the value from the database, so now user can update the db via the web and we'll know...
Exemplo n.º 8
0
def Set(devKey, newState=states.present):
    oldTime, oldState = Get(devKey)
    if oldState != newState:
        database.NewEvent(devKey, newState)  # For ActivityLog on web page
    if newState == states.absent:
        database.LogItem(
            devKey, "SignalPercentage",
            0)  # Clear down signal strength when device goes missing
    database.LogItem(devKey, "Presence", newState)
Exemplo n.º 9
0
def Override(devKey, targetC, timeSecs):
    global overrideTimeoutMins, currentTargetTemp
    timeMins = int(int(timeSecs) / 60)
    if timeMins > 0:
        overrideTimeoutMins = timeMins
        database.NewEvent(
            devKey, "Override " + str(targetC) + "'C"
        )  # For web page.  Update event log so I can check my schedule follower works
        currentTargetTemp = heating.SetTargetTemp(
            devKey, targetC)  # Set target in heating device here
Exemplo n.º 10
0
 def do_dim(self, line):
     """dim name percentage
     Sends level command to named device"""
     argList = line.split()
     if len(argList) >= 2:
         percentage = int(argList[1])
         devKey = devices.FindDev(argList[0])
         if devKey != None:
             database.NewEvent(devKey, "Dim", "UICmd")
             devcmds.Dim(devKey, percentage)
     else:
         log.fault("Insufficient Args")
Exemplo n.º 11
0
 def do_sat(self, line):
     """sat name sat
     Sends ColorCtrl command to named device, where 0<sat<100"""
     argList = line.split()
     if len(argList) >= 2:
         sat = int(argList[1])
         devKey = devices.FindDev(argList[0])
         if devKey != None:
             database.NewEvent(devKey, "Sat", "UICmd")
             devcmds.Sat(devKey, sat)
     else:
         log.fault("Insufficient Args")
Exemplo n.º 12
0
 def do_hue(self, line):
     """hue name hue
     Sends ColorCtrl command to named device, where 0<hue<360"""
     argList = line.split()
     if len(argList) >= 2:
         hue = int(argList[1])
         devKey = devices.FindDev(argList[0])
         if devKey != None:
             database.NewEvent(devKey, "Hue", "UICmd")
             devcmds.Hue(devKey, hue)
     else:
         log.fault("Insufficient Args")
Exemplo n.º 13
0
def SetStatus(devIdx, name, value):  # For web page
    global status, statusUpdate
    for item in status[devIdx]:
        if item[0] == name:
            if item[1] == value:
                return  # Bail if value hasn't changed
            status[devIdx].remove(
                item)  # Remove old tuple if value has changed
    status[devIdx].append((name, value))  # Add new one regardless
    if name == "Other" and value != "N/A":
        log.activity(devIdx, value)
    statusUpdate = True
    database.NewEvent(devIdx, name, value)
Exemplo n.º 14
0
def HandleSerial(ser):
    global txBuf, rxBuf
    while ser.inWaiting():
        try:
            telegesisInLine = str(ser.readline(),'utf-8').rstrip('\r\n')    # Rely on timeout=0 to return immediately, either with a line or with None
        except:
            database.NewEvent(0, "Serial port problem")
            vesta.Restart()
        rxBuf.append(telegesisInLine)  # Buffer this for subsequent processing in main thread
    while len(txBuf):
        atCmd = txBuf.popleft()
        wrAtCmd = atCmd + "\r\n"
        ser.write(wrAtCmd.encode())
        log.debug("Tx>"+atCmd)
Exemplo n.º 15
0
def EventHandler(eventId, eventArg):
    global sckLst, sck, cliSck
    if eventId == events.ids.INIT:
        sck = socket.socket(socket.AF_INET,
                            socket.SOCK_STREAM)  # Create socket
        sck.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        sck.setblocking(0)  # accept() is no longer blocking
        port = 12345
        try:
            sck.bind(('', port))  # Listen on all available interfaces
        except OSError as err:  # "OSError: [Errno 98] Address already in use"
            database.NewEvent(0, "Socket bind failed with " +
                              err.args[1])  # 0 is always hub
            vesta.Reboot()
        sck.listen(0)
        sckLst = [sck]
    if eventId == events.ids.SECONDS:
        if select.select(
            [sys.stdin], [], [],
                0)[0]:  # Read from stdin (for working with the console)
            cmd = sys.stdin.readline()
            if cmd:
                Commands().onecmd(cmd)
        rd, wr, er = select.select(
            sckLst, [], [],
            0)  # Read from remote socket (for working with web pages)
        for s in rd:
            if s is sck:
                cliSck, addr = sck.accept()
                sckLst.append(cliSck)
                #log.debug("New connection from web page!")
            else:
                try:
                    cmd = cliSck.recv(100)
                except OSError as err:  # OSError: [Errno 9] Bad file descriptor"
                    synopsis.problem("Web command failed with ", err.args[1])
                    cmd = ""  # No command if there was a failure
                if cmd:
                    cmd = cmd.decode()
                    log.debug("Got cmd \"" + cmd + "\" from web page")
                    sys.stdout = open("cmdoutput.txt",
                                      "w")  # Redirect stdout to file
                    Commands().onecmd(cmd)
                    sys.stdout = sys.__stdout__  # Put stdout back to normal (will hopefully also close the file)
                    f = open("cmdoutput.txt", "r")
                    cmdOut = f.read()
                    cliSck.send(str.encode(cmdOut))
                    call("rm cmdoutput.txt",
                         shell=True)  # Remove cmd output after we've used it
Exemplo n.º 16
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
Exemplo n.º 17
0
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...
Exemplo n.º 18
0
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
Exemplo n.º 19
0
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()
Exemplo n.º 20
0
def SetAttrVal(devKey, clstrId, attrId, value):
    global msp_ota
    if clstrId == zcl.Cluster.PowerConfig and attrId == zcl.Attribute.Batt_Percentage:
        SetTempVal(devKey, "GetNextBatteryAfter",
                   datetime.now() +
                   timedelta(seconds=86400))  # Ask for battery every day
        if value != "FF":
            try:
                varVal = int(
                    int(value, 16) / 2
                )  # Arrives in 0.5% increments, but drop fractional component
            except ValueError:
                varVal = None
            if varVal != None:
                log.debug("Battery is " + str(varVal) +
                          "%.  Get next reading at " +
                          str(GetTempVal(devKey, "GetNextBatteryAfter")))
                database.LogItem(devKey, "BatteryPercentage",
                                 varVal)  # For web page
                lowBatt = int(config.Get("lowBattery", "5"))
                if varVal < lowBatt:
                    devName = database.GetDeviceItem(devKey, "userName")
                    synopsis.problem(
                        devName + "_batt",
                        devName + " low battery (" + str(varVal) + "%)")
    if clstrId == zcl.Cluster.Temperature and attrId == zcl.Attribute.Celsius:
        if value != "FF9C" and value != "8000":  # Don't know where this value (of -100) comes from, but seems to mean "Illegal temp", although it should be -1'C
            try:
                varVal = int(value, 16) / 100  # Arrives in 0.01'C increments
                database.LogItem(devKey, "TemperatureCelsius",
                                 varVal)  # For web page
            except ValueError:
                log.debug("Bad temperature of " + value)
    if clstrId == zcl.Cluster.OnOff and attrId == zcl.Attribute.OnOffState:
        if isnumeric(value, 16):
            oldState = database.GetLatestLoggedValue(devKey, "State")
            if int(value, 16) == 0:
                newState = "SwitchOff"
            else:
                newState = "SwitchOn"
            if oldState != newState:
                database.UpdateLoggedItem(
                    devKey, "State",
                    newState)  # So that we can access it from the rules later
                database.NewEvent(devKey, newState)
                Rule(devKey, newState)
            expectedState = GetTempVal(devKey, "ExpectOnOff")
            if expectedState != None:
                if newState != expectedState:
                    if expectedState == "SwitchOn":
                        devcmds.SwitchOn(devKey)  # Re-issue command
                    else:  # Assume SwitchOff
                        devcmds.SwitchOff(devKey)  # Re-issue command
                else:  # We got the expected result
                    DelTempVal(devKey, "ExpectOnOff")
    if clstrId == zcl.Cluster.Time and attrId == zcl.Attribute.LocalTime:
        if isnumeric(value, 16):
            varVal = int(value,
                         16)  # Arrives in Watts, so store it in the same way
            log.debug("Raw time:" + str(varVal))
            timeStr = iottime.FromZigbee(varVal)
            log.debug("Human time:" + timeStr)
            database.UpdateLoggedItem(devKey, "Time",
                                      timeStr)  # Just store latest time string
    if clstrId == zcl.Cluster.SimpleMetering and attrId == zcl.Attribute.InstantaneousDemand:
        if isnumeric(value, 16):
            varVal = int(value,
                         16)  # Arrives in Watts, so store it in the same way
            inClstr = database.GetDeviceItem(
                devKey, "inClusters"
            )  # Assume we have a list of clusters if we get this far
            if zcl.Cluster.OnOff not in inClstr:  # Thus device is powerclamp (has simplemetering but no OnOff)
                database.UpdateLoggedItem(
                    devKey, "State",
                    str(varVal) + "W"
                )  # So that we can access it from the rules later, or show it on the web
            database.UpdateLoggedItem(devKey, "PowerReadingW",
                                      varVal)  # Just store latest reading
    if clstrId == zcl.Cluster.SimpleMetering and attrId == zcl.Attribute.CurrentSummationDelivered:
        if isnumeric(value, 16):
            varVal = int(
                value, 16
            )  # Arrives in accumulated WattHours, so store it in the same way
            database.LogItem(devKey, "EnergyConsumedWh", varVal)
    if clstrId == zcl.Cluster.IAS_Zone and attrId == zcl.Attribute.Zone_Type:
        database.SetDeviceItem(devKey, "iasZoneType", value)
    if clstrId == zcl.Cluster.Basic:
        if attrId == zcl.Attribute.Model_Name:
            database.SetDeviceItem(devKey, "modelName", value)
        if attrId == zcl.Attribute.Manuf_Name:
            database.SetDeviceItem(devKey, "manufName", value)
    if clstrId == zcl.Cluster.OTA or clstrId == msp_ota:
        if attrId == zcl.Attribute.firmwareVersion:
            database.SetDeviceItem(devKey, "firmwareVersion", value)
    if clstrId == zcl.Cluster.PollCtrl:
        if attrId == zcl.Attribute.LongPollIntervalQs:
            varVal = str(float(int(value, 16) /
                               4))  # Value arrives in units of quarter seconds
            database.SetDeviceItem(
                devKey, "longPollInterval", varVal
            )  # For web page and also to see whether to wait for CheckIn or just send messages (if <6 secs)
    if clstrId == zcl.Cluster.Thermostat:
        if attrId == zcl.Attribute.LocalTemp:
            if isnumeric(value, 16):
                varVal = int(value, 16) / 100  # Arrives in 0.01'C increments
                database.LogItem(devKey, "SourceCelsius",
                                 varVal)  # For web page
                src = varVal
                tgt = database.GetLatestLoggedValue(devKey, "TargetCelsius")
                database.UpdateLoggedItem(
                    devKey, "State", "source " + str(src) + "'C/target " +
                    str(tgt) + "'C")  # So that we can show it on the web
        if attrId == zcl.Attribute.OccupiedHeatingSetPoint:
            if isnumeric(value, 16):
                varVal = int(value, 16) / 100  # Arrives in 0.01'C increments
                database.LogItem(devKey, "TargetCelsius",
                                 varVal)  # For web page
                tgt = varVal
                src = database.GetLatestLoggedValue(devKey, "SourceCelsius")
                database.UpdateLoggedItem(
                    devKey, "State", "source " + str(src) + "'C/target " +
                    str(tgt) + "'C")  # So that we can show it on the web
    if clstrId == zcl.Cluster.Time:
        if attrId == zcl.Attribute.Time:
            if isnumeric(value, 16):
                varVal = int(value,
                             16)  # Arrives in seconds since 1st Jan 2000
                timeStamp = iottime.FromZigbee(varVal)
                database.LogItem(devKey, "time",
                                 str(timeStamp))  # For web page
Exemplo n.º 21
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)