示例#1
0
def Sanitise(val):  # Assume val is a string containing a hour:minute time
    timeOfDay = MakeTime(val)
    if timeOfDay == None:
        synopsis.problem("TimedRule", "Bad time in Rule containing '" + val)
        return None  # Must return something
    return "\"" + timeOfDay.strftime(
        "%H:%M") + "\""  # Normalise timestamp (cope with leading zeros)
示例#2
0
文件: rules.py 项目: chava33/Vesta
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])
示例#3
0
文件: weather.py 项目: chava33/Vesta
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
示例#4
0
def NoteMsgDetails(devKey, arg):
    devIndex = GetIndexFromKey(devKey)
    if devIndex == None:
        synopsis.problem("Unknown device:",str(devKey))
        return
    if arg[0] == expRsp[devIndex]:
        expRsp[devIndex] = None   # Note that we've found the expected response now, so we're now clear to send
    presence.Set(devKey) # Note presence, and update radio quality
    if isnumeric(arg[-2]):
        if int(arg[-2]) < 0: # Assume penultimate item is RSSI, and thus that ultimate one is LQI
            rssi = arg[-2]
            lqi = arg[-1]
            try:
                signal = int((int(lqi, 16) * 100) / 255)    # Convert 0-255 to 0-100.  Ignore RSSI for now
            except ValueError:
                signal = -1   # Cannot get signal
            if signal != -1:
                entry = database.GetLatestLoggedItem(devKey, "SignalPercentage")
                if entry != None:
                    oldSignal = entry[0] # Just the value
                    fmt = "%Y-%m-%d %H:%M:%S"
                    oldTimestamp = datetime.strptime(entry[1], fmt)
                    if oldSignal == None:
                        oldSignal = signal + 100  # Force an update if no previous signal
                    deltaSignal = signal - oldSignal
                    deltaTime = datetime.now() - oldTimestamp
                    if abs(deltaSignal) > 5:
                        if deltaTime.seconds > 600:  # Only note signal level that's different enough and at least 10 minutes since last one
                            database.LogItem(devKey, "SignalPercentage", signal)
                    else:   # signal is sufficiently similar to last one, so update timestamp
                        database.RefreshLoggedItem(devKey, "SignalPercentage")  # Update timestamp to avoid too many >10 minutes!
            arg.remove(rssi)
            arg.remove(lqi)
示例#5
0
文件: rules.py 项目: chava33/Vesta
def ParseCondition(ruleConditionList, trigger):
    #log.debug("Parsing: "+" ".join(ruleConditionList))
    subAnswers = "True"
    for condition in ruleConditionList[1:]:
        if condition == "and":
            subAnswers = subAnswers + " and "  # Note surrounding spaces, for python eval()
        elif condition == "or":
            subAnswers = subAnswers + " or "
        elif "<time<" in condition:
            sep = condition.index("<time<")  # Handle time here
            nowTime = datetime.strptime(datetime.now().strftime("%H:%M"),
                                        "%H:%M")
            startTime = iottime.Get(condition[:sep])
            endTime = iottime.Get(condition[sep + 6:])
            if endTime < startTime:  # Handle midnight-crossing here...
                almostMidnight = datetime.strptime("23:59", "%H:%M")
                midnight = datetime.strptime("0:00", "%H:%M")
                if nowTime > datetime.strptime(
                        "12:00", "%H:%M"):  # After midday but before midnight
                    subAnswers = subAnswers + str(
                        iottime.IsTimeBetween(startTime, nowTime,
                                              almostMidnight))
                else:  # Assume after midnight
                    subAnswers = subAnswers + str(
                        iottime.IsTimeBetween(midnight, nowTime, endTime))
            else:  # Doesn't involve midnight
                subAnswers = subAnswers + str(
                    iottime.IsTimeBetween(startTime, nowTime, endTime))
        elif "<=" in condition:
            subAnswers = subAnswers + str(GetConditionResult("<=", condition))
        elif ">=" in condition:
            subAnswers = subAnswers + str(GetConditionResult(">=", condition))
        elif "<" in condition:
            subAnswers = subAnswers + str(GetConditionResult("<", condition))
        elif ">" in condition:
            subAnswers = subAnswers + str(GetConditionResult(">", condition))
        elif "==" in condition:
            subAnswers = subAnswers + str(GetConditionResult("==", condition))
    # End of loop
    if subAnswers != "":
        #log.debug("About to evaluate:'"+subAnswers+"'")
        try:
            finalAnswer = eval(subAnswers)
        except:  # Catch all errors that the rule might raise
            err = sys.exc_info()[0]
            synopsis.problem(
                "BadRule", "Bad rule : '" + join(ruleConditionList)
            )  # Make a note in the status page so the user can fix the rule
            finalAnswer = False  # Say rule failed
        return finalAnswer
    else:
        return False  # Empty string is always False
示例#6
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
示例#7
0
文件: rules.py 项目: chava33/Vesta
def GetConditionResult(test, condition):
    sep = condition.index(
        test)  # Assume this has already been shown to return a valid answer
    tstVal = condition[sep + len(test):]  # Simple value must be on right
    varName = condition[:sep]  # Variable must be on the left of the expression
    if "-" in varName:
        variables.GetVal(varName, "-")
    elif "+" in varName:
        variables.GetVal(varName, "+")
    else:
        varVal = variables.Get(varName)
    if varVal != None:
        if isNumber(tstVal):
            varVal = str(varVal)
            tstVal = str(tstVal)
        elif ":" in tstVal:
            varVal = iottime.Sanitise(
                varVal
            )  # Ensure timestamps are consistently formatted before comparing (to avoid "0:15" != "00:15")
            tstVal = iottime.Sanitise(tstVal)
            log.debug("Time test checks " + varVal + " against " + tstVal)
        else:
            varVal = "'" + varVal.lower() + "'"
            tstVal = "'" + tstVal.lower(
            ) + "'"  # Surround strings with quotes to make string comparisons work (Tuesday==Tuesday fails, although 'Tuesday'=='Tuesday' works)
        condStr = varVal + test + tstVal
        try:
            answer = eval(condStr)
        except:
            synopsis.problem("BadRule",
                             "Failed to evaluate '" + condition + "'")
            log.debug("Failed to evaluate '" + condition + "'")
            answer = False  # Default answer to allow rest of rules to continue to run
        return answer
    else:
        return False  # If we couldn't find the item requested, assume the condition fails(?)
示例#8
0
文件: rules.py 项目: chava33/Vesta
def Action(actList, ruleId):
    log.debug("Action with: " + str(actList))
    action = actList[0].lower()
    if action == "Log".lower():
        log.debug("Rule says Log event for " + ' '.join(actList[1:]))
    elif action == "Play".lower():
        call(["omxplayer", "-o", actList[1], actList[2]])
    elif action == "Event".lower():
        if actList[1].lower() == "TimeOfDay".lower():
            events.IssueEvent(events.ids.TIMEOFDAY, actList[2])
        elif actList[1].lower() == "Alarm".lower():
            events.IssueEvent(events.ids.ALARM, actList[2])
        # Could have other events here...
    elif action == "synopsis":  # Was status
        emailAddress = config.Get("emailAddress")
        log.debug("About to send synopsis to " + emailAddress)
        if emailAddress != None:
            synopsis.BuildPage()  # Create synopsis page on demand
            with open("synopsis.txt", "r") as fh:  # Plain text of email
                emailText = fh.readlines()
            text = ''.join(emailText)
            with open("synopsis.html", "r") as fh:  # HTML of email
                emailHtml = fh.readlines()
            html = ''.join(emailHtml)
            sendmail.email("Vesta Status", text, html)  # See sendmail.py
        else:
            synopsis.problem(
                "NoEmail",
                "No emailAddress entry in config, needed to send synopsis")
    elif action == "email":  # All args are body of the text.  Fixed subject and email address
        emailAddress = config.Get("emailAddress")
        if emailAddress != None:
            emailBody = []
            for item in actList[1:]:
                emailBody.append(item)
            plainText = " ".join(emailBody)
            log.debug("Sending email with '" + plainText + "'")
            result = sendmail.email("Vesta Alert!", plainText, None)
            if result != 0:
                synopsis.problem(
                    "Email", "sendmail.email() failed with code " +
                    str(result) + " when trying to send:" + plainText)
        else:
            synopsis.problem("NoEmail", "No emailAddress entry in config")
    elif action == "override":  # Syntax is "Override <targetDevice> <targetDegC> <durationSecs>"
        devKey = devices.FindDev(actList[1])
        target = actList[2]
        timeSecs = actList[3]
        if devKey != None:
            schedule.Override(devKey, target, timeSecs)
    elif action == "set":  # Set a named variable to a value
        expression = "".join(
            actList[1:]
        )  # First recombine actList[1] onwards, with no spaces.  Now expression should be of the form "<var>=<val>"
        if "--" in expression:
            sep = expression.index("--")
            varName = expression[:sep]
            varVal = variables.Get(varName)
            if isNumber(varVal):
                newVal = str(eval(varVal + "-1"))
                variables.Set(varName, newVal)
                Run(
                    varName + "==" + newVal
                )  # Recurse! to see if any rules need running now that we've set a variable
            else:
                log.fault(varName + " not a number at " + expression)
        elif "++" in expression:
            sep = expression.index("++")
            varName = expression[:sep]
            varVal = variables.Get(varName)
            if isNumber(varVal):
                newVal = str(eval(varVal + "+1"))
                variables.Set(varName, newVal)
                Run(
                    varName + "==" + newVal
                )  # Recurse! to see if any rules need running now that we've set a variable
            else:
                log.fault(varName + " not a number at " + expression)
        elif "=" in expression:
            sep = expression.index("=")
            varName = expression[:sep]
            varVal = expression[sep + 1:]
            variables.Set(varName, varVal)
            Run(
                varName + "==" + varVal
            )  # Recurse! to see if any rules need running now that we've set a variable
        else:
            log.fault("Badly formatted rule at " + expression)
    elif action == "unset":  # Remove a named variable
        variables.Del(actList[1])
    else:  # Must be a command for a device, or group of devices
        if len(actList) >= 2:  # Check that we have a second arg...
            name = actList[1]  # Second arg is name
            if database.IsGroupName(name):  # Check if name is a groupName
                devKeyList = GetGroupDevs(name)
                for devKey in devKeyList:
                    CommandDev(action, devKey, actList,
                               ruleId)  # Command each device in list
            else:
                devKey = database.GetDevKey("userName", name)
                CommandDev(action, devKey, actList,
                           ruleId)  # Command one device
示例#9
0
文件: devices.py 项目: chava33/Vesta
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