def run_rule(self, rule_id, macro=None):
     # if macro is defined, run the rule for that macro only, otherwise run for all the macros
     macros = [macro] if macro is not None else self.rules[rule_id].keys()
     for macro in macros:
         rule = self.rules[rule_id][macro]
         # ensure this rule is not run too often to avoid loops
         if self.config[
                 "loop_safeguard"] > 0 and rule_id in self.last_run and macro in self.last_run[
                     rule_id] and time.time() - self.last_run[rule_id][
                         macro] < self.config["loop_safeguard"]:
             return
         # keep track of the last time this run has run
         if rule_id not in self.last_run: self.last_run[rule_id] = {}
         self.last_run[rule_id][macro] = time.time()
         self.log_debug("[" + rule_id + "][" + macro + "] running rule")
         # for each sensor we need the value which will be asked to the db module. Keep track of both values and requests
         if rule_id not in self.requests: self.requests[rule_id] = {}
         self.requests[rule_id][macro] = []
         if rule_id not in self.values: self.values[rule_id] = {}
         self.values[rule_id][macro] = {}
         # for every constant, store its value as is so will be ready for the evaluation
         if "constants" in rule:
             for constant_id, value in rule["constants"].iteritems():
                 self.values[rule_id][macro][constant_id] = value
         # for every variable, retrieve its latest value to the database
         if "variables" in rule:
             for variable_id, variable in rule["variables"].iteritems():
                 # process the variable string (0: request, 1: start, 2: end, 3: sensor_id)
                 match = re.match(self.variable_regexp, variable)
                 if match is None: continue
                 # query db for the data
                 command, start, end, sensor_id = match.groups()
                 message = Message(self)
                 message.recipient = "controller/db"
                 message.command = message.command = "GET_" + command if command != "" else "GET"
                 message.args = sensor_id
                 start = -1 if start is None else int(start)
                 end = -1 if end is None else int(end.replace(",", ""))
                 message.set("start", start)
                 message.set("end", end)
                 self.sessions.register(
                     message, {
                         "rule_id": rule_id,
                         "variable_id": variable_id,
                         "macro": macro,
                     })
                 self.log_debug("[" + rule_id + "][" + macro + "][" +
                                variable_id + "] requesting db for " +
                                message.command + " " + message.args +
                                ": " + str(message.get_data()))
                 self.send(message)
                 # keep track of the requests so that once all the data will be available the rule will be evaluated
                 self.requests[rule_id][macro].append(
                     message.get_request_id())
             # add a placeholder at the end to ensure the rule is not evaluated before all the definitions are retrieved
             self.requests[rule_id][macro].append("LAST")
         # if the rule requires no data to retrieve, just evaluate it
         if len(self.requests[rule_id][macro]) == 0:
             self.evaluate_rule(rule_id, macro)
Пример #2
0
 def __on_message(client, userdata, msg):
     try:
         # parse the incoming request into a message data structure
         message = Message()
         message.parse(msg.topic, msg.payload, msg.retain)
         if self.module.verbose: self.module.log_debug("Received message "+message.dump(), False)
     except Exception,e:
         self.module.log_error("Invalid message received on "+msg.topic+" - "+msg.payload+": "+exception.get(e))
         return
Пример #3
0
 def ping(self):
     # TODO: move into a dedicated monitoring module
     for module in self.modules:
         # raise a warning if a module becomes unreachable
         if module["ping"] > 10:
             self.log_warning("module " + module["fullname"] +
                              " is unreachable")
         # periodically ping all registered modules
         message = Message(self)
         message.recipient = module["fullname"]
         message.command = "PING"
         self.log_debug("Pinging " + module["fullname"] + "...")
         module["ping"] = time.time(
         )  # keep track of the timestamp of the request
         self.send(message)
         self.sleep(1)
Пример #4
0
 def on_stop(self):
     # remove the manifest
     message = Message(self)
     message.recipient = "*/*"
     message.command = "MANIFEST"
     message.args = self.manifest["package"]
     message.set_null()
     message.retain = True
     self.send(message)
Пример #5
0
 def __poll_sensor(self, sensor_id, configuration):
     # simulate a message from the hub to trigger the sensor
     message = Message(self)
     message.sender = "controller/hub"
     message.recipient = self.fullname
     message.command = "IN"
     message.args = sensor_id
     message.set_data(configuration)
     self.on_message(message)
Пример #6
0
 def clear_config(self, filename, version):
     # send a null so to cancel retention
     if filename != self.index_key: 
         self.log_debug("Unpublishing configuration "+filename+" (v"+str(version)+")")
     message = Message(self)
     message.recipient = "*/*"
     message.command = "CONF"
     message.args = filename
     message.config_schema = version
     # remove the retained message
     message.set_null()
     message.retain = True
     self.send(message)
Пример #7
0
 def publish_config(self, filename, version, content):
     if filename != self.index_key:
         self.log_debug("Publishing configuration " + filename + " (v" +
                        str(version) + ")")
     message = Message(self)
     message.recipient = "*/*"
     message.command = "CONF"
     message.args = filename
     message.config_schema = version
     message.set_data(content)
     # configuration is retained so when a module connects, immediately get the latest config
     message.retain = True
     self.send(message)
 def on_message(client, userdata, msg):
     try:
         self.log_debug("received mqtt message on " +
                        str(msg.topic))
         # find the sensor matching the topic
         for sensor_id in self.sensors:
             configuration = self.sensors[sensor_id]
             # exclude pull sensors
             if "topic" not in configuration:
                 continue
             # if the message is for this sensor
             if msg.topic == str(configuration["topic"]):
                 image = msg.payload
                 self.log_debug("received an image for " +
                                sensor_id)
                 # analyze the image
                 image = self.analyze_image(sensor_id,
                                            configuration, image)
                 if image is None:
                     return
                 image = base64.b64encode(image)
                 # prepare the message
                 message = Message(self)
                 message.recipient = "controller/hub"
                 message.command = "IN"
                 message.args = sensor_id
                 message.set("value", image)
                 # send the message to the controller
                 self.send(message)
                 break
     except Exception, e:
         self.log_error("Unable to process mqtt message: " +
                        exception.get(e))
 def on_start(self):
     # request all sensors' configuration so to filter sensors of interest
     self.add_configuration_listener("sensors/#", 1)
     # kill rtl_433 if running
     sdk.python.utils.command.run("killall rtl_433")
     # run rtl_433 and handle the output
     command = self.config['command']+" "+self.config['arguments']
     self.log_debug("running command "+command)
     process = subprocess.Popen(shlex.split(command), stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
     prev_output = ""
     while True:
         # read a line from the output
         output = process.stdout.readline()
         if output == '' and process.poll() is not None:
             # process ended, break
             self.log_info("rtl_433 has ended")
             break
         if output:
             # output available
             try:
                 # avoid handling the same exact output, skipping
                 if prev_output == output: continue
                 # parse the json output
                 json_output = json.loads(output)
             except Exception,e:
                 # not json format, ignoring
                 continue
             self.log_debug("Received: "+str(json_output))
             # for each registered sensor
             for sensor_id in self.sensors:
                 sensor = self.sensors[sensor_id]
                 # apply the filter if any
                 if "filter" in sensor:
                     search = {}
                     if "&" in sensor["filter"]: key_values = sensor["filter"].split("&")
                     else: key_values = [sensor["filter"]]
                     for key_value in key_values:
                         if "=" not in key_value: continue
                         key, value = key_value.split("=")
                         search[key] = value
                     # check if the output matches the search string
                     found = True
                     for key, value in search.iteritems():
                         # check every key/value pair
                         if key not in json_output: found = False
                         if str(value) != str(json_output[key]): found = False
                     if not found: continue
                 # prepare the message
                 message = Message(self)
                 message.recipient = "controller/hub"
                 message.command = "IN"
                 message.args = sensor_id
                 value = json_output[sensor["measure"]] if "measure" in sensor and sensor["measure"] in json_output else 1
                 message.set("value", value)
                 # send the measure to the controller
                 self.send(message)
                 self.log_debug("Matched sensor "+sensor_id+" with value "+str(value))
             # keep track of the last line of output
             prev_output = output
 def process_message(self, update, context):
     # clean up request
     request = update.message.text.replace(
         "/" + self.config["command_handler"] + " ", "").lower()
     # ask our chatbot what to respond
     message = Message(self)
     message.recipient = "controller/chatbot"
     message.command = "ASK"
     message.set("request", request)
     message.set("accept", ["text", "image"])
     self.sessions.register(message, {"update": update})
     self.send(message)
Пример #11
0
 def on_message(client, userdata, msg):
     try:
         # find the sensor matching the topic
         for sensor_id in self.sensors:
             sensor = self.sensors[sensor_id]
             if mqtt.topic_matches_sub(sensor["topic"], msg.topic):
                 self.log_debug("received " + str(msg.payload) +
                                " for " + sensor_id + " on topic " +
                                str(msg.topic))
                 # if JSON payload is expected
                 if "key" in sensor:
                     try:
                         data = json.loads(msg.payload)
                         if sensor["key"] not in data:
                             return
                         # apply the filter if any
                         if "filter" in sensor:
                             search = {}
                             if "&" in sensor["filter"]:
                                 key_values = sensor["filter"].split(
                                     "&")
                             else:
                                 key_values = [sensor["filter"]]
                             for key_value in key_values:
                                 if "=" not in key_value: continue
                                 key, value = key_value.split("=")
                                 search[key] = value
                             # check if the output matches the search string
                             found = True
                             for key, value in search.iteritems():
                                 # check every key/value pair
                                 if key not in data: found = False
                                 if key in data and str(value).lower(
                                 ) != str(data[key]).lower():
                                     found = False
                             # not matching, skip to the next sensor
                             if not found: continue
                         value = data[sensor["key"]]
                     except Exception, e:
                         self.log_warning(
                             "Unable to parse JSON payload " +
                             str(msg.payload) + ": " + exception.get(e))
                         return
                 # else consider the entire payload
                 else:
                     value = msg.payload
                 # prepare the message
                 message = Message(self)
                 message.recipient = "controller/hub"
                 message.command = "IN"
                 message.args = sensor_id
                 message.set("value", value)
                 # send the measure to the controller
                 self.send(message)
     except Exception, e:
         self.log_warning("runtime error during on_message(): " +
                          exception.get(e))
         return
 def on_start(self):
     self.log_debug("listening for UDP datagrams on port " +
                    str(self.config["port_listen"]))
     # bind to the network
     sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
     sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
     sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
     sock.bind(("", self.config["port_listen"]))
     while True:
         try:
             # new data arrives
             data, addr = sock.recvfrom(1024)
             self.log_debug("received " + data)
             # the message is expected to be in JSON format
             data = json.loads(data)
             if data["type"] != "WirelessMessage": continue
             # parse content
             content = data["data"][0]
             # sensor just started
             if content == "STARTED":
                 self.log_info(data["id"] + " has just started")
                 self.tx(data["id"], "ACK", True)
             elif content == "AWAKE":
                 # send a message if there is something in the queue
                 if data["id"] in self.queue and len(
                         self.queue[data["id"]]) > 0:
                     self.tx(data["id"], queue[data["id"]])
                     self.queue[data["id"]] = []
             else:
                 # look for the sensor_id associated to this measure
                 sensor = None
                 for sensor_id in self.sensors:
                     if data["id"] == self.sensors[sensor_id][
                             "node_id"] and content.startswith(
                                 self.sensors[sensor_id]["measure"]):
                         sensor = self.sensors[sensor_id]
                         break
                 # if not registered, skip it
                 if sensor is None: continue
                 # prepare the message
                 message = Message(self)
                 message.recipient = "controller/hub"
                 message.command = "IN"
                 message.args = sensor_id
                 # generate the timestamp
                 # date_in = datetime.datetime.strptime(data["timestamp"],"%d %b %Y %H:%M:%S +0000")
                 # message.set("timestamp", int(time.mktime(date_in.timetuple())))
                 # strip out the measure from the value
                 message.set(
                     "value",
                     content.replace(self.sensors[sensor_id]["measure"],
                                     ""))
                 # send the measure to the controller
                 self.send(message)
         except Exception, e:
             self.log_warning("unable to parse " + str(data) + ": " +
                              exception.get(e))
Пример #13
0
 def on_message(self, message):
     # handle response from the chatbot
     if message.sender == "controller/chatbot" and message.command == "ASK":
         content = message.get("content")
         message = Message(self)
         message.recipient = "notification/" + self.config["speaker"]
         message.command = "RUN"
         message.args = "info"
         message.set_data(content)
         self.send(message)
Пример #14
0
 def retention_policies(self):
     # ask the database module to purge the data
     message = Message(self)
     message.recipient = "controller/db"
     message.command = "PURGE_ALERTS"
     message.set_data(self.config["retention"])
     self.send(message)
 def event_callback(self, pin):
     if pin not in self.pins: return
     sensor_id = self.pins[pin]
     value = 1 if self.gpio_object.input(pin) else 0
     self.log_debug("GPIO input on pin " + str(pin) + " is now " +
                    str(value))
     message = Message(self)
     message.recipient = "controller/hub"
     message.command = "IN"
     message.args = sensor_id
     message.set("value", value)
     self.send(message)
Пример #16
0
 def on_start(self):
     while self.stopping == False:
         # init slack
         self.slack_init()
         if not self.slack_initialized:
             self.sleep(self.sleep_on_error)
             continue
         # connect to slack
         self.slack_connect()
         if not self.slack_connected:
             self.sleep(self.sleep_on_error)
             continue
         # read a rtm stream
         try:
             output_list = self.slack.rtm_read()
         except Exception, e:
             self.log_warning("unable to read from slack: " +
                              exception.get(e))
             self.slack_initialized = False
             self.slack_connected = False
             self.sleep(self.sleep_on_error)
             continue
         if output_list and len(output_list) > 0:
             for output in output_list:
                 if not output or 'text' not in output: continue
                 if output['user'] == self.bot_id: continue
                 # if the message is to the bot
                 if self.bot_id in output['text'] or self.config[
                         "bot_name"] in output['text'] or output[
                             'channel'].startswith("D"):
                     # clean up the request
                     request = output['text']
                     request = request.replace(self.config["bot_name"], '')
                     request = request.replace(self.bot_id, '')
                     request = request.lower()
                     channel = output['channel']
                     # ask our chatbot what to respond
                     message = Message(self)
                     message.recipient = "controller/chatbot"
                     message.command = "ASK"
                     message.set("request", request)
                     message.set("accept", ["text", "image"])
                     self.sessions.register(message, {"channel": channel})
                     self.send(message)
                     self.slack_typing(channel)
         self.sleep(1)
Пример #17
0
 def __do_log(self, message):
     # print the message
     self.logger.info(
         sdk.python.utils.strings.format_log_line(message.args,
                                                  message.sender,
                                                  message.get_data()))
     # ask db to save the log
     db_message = Message(self)
     db_message.recipient = "controller/db"
     db_message.command = "SAVE_LOG"
     db_message.args = message.args
     db_message.set_data("[" + message.sender + "] " +
                         str(message.get_data()))
     self.send(db_message)
Пример #18
0
 def on_message(client, userdata, msg):
     # find the sensor matching the topic
     for sensor_id in self.sensors:
         sensor = self.sensors[sensor_id]
         if mqtt.topic_matches_sub(sensor["topic"], msg.topic):
             self.log_debug("received " + str(msg.payload) + " for " +
                            sensor_id + " on topic " + str(msg.topic))
             # prepare the message
             message = Message(self)
             message.recipient = "controller/hub"
             message.command = "IN"
             message.args = sensor_id
             message.set("value", msg.payload)
             # send the measure to the controller
             self.send(message)
Пример #19
0
 def on_message(client, userdata, msg):
     self.log_debug("received on "+str(msg.topic)+": "+str(msg.payload))
     # find the sensor matching the topic
     for sensor_id in self.sensors:
         configuration = self.sensors[sensor_id]
         # if the message is for this sensor
         if msg.topic.endswith("/"+str(configuration["device_id"])):
             # parse the payload
             try:
                 data = json.loads(msg.payload)
             except Exception,e:
                 self.log_warning("Unable to parse payload "+str(msg.payload)+": "+exception.get(e))
                 return
             # skip if key is missing from the payload
             if configuration["key"] not in data: 
                 continue
             # apply the filter if any
             if "filter" in configuration:
                 search = {}
                 if "&" in configuration["filter"]: key_values = configuration["filter"].split("&")
                 else: key_values = [configuration["filter"]]
                 for key_value in key_values:
                     if "=" not in key_value: continue
                     key, value = key_value.split("=")
                     search[key] = value
                 # check if the output matches the search string
                 found = True
                 for key, value in search.iteritems():
                     # check every key/value pair
                     if key not in data: found = False
                     if key in data and str(value).lower() != str(data[key]).lower(): found = False
                 # not matching, skip to the next sensor
                 if not found: continue
             value = data[configuration["key"]]
             # ignore empty values (when homeassistant is true, zigbee2mqtt will report an empty value just after a value
             if value == "": continue
             self.log_debug("reporting "+sensor_id+" with value "+str(value))
             # prepare the message
             message = Message(self)
             message.recipient = "controller/hub"
             message.command = "IN"
             message.args = sensor_id
             message.set("value", value)
             # send the measure to the controller
             self.send(message)
Пример #20
0
 def on_start(self):
     # start the pulseaudio daemon
     self.log_info("Starting audio daemon...")
     self.log_debug(
         sdk.python.utils.command.run("setup/start_pulseaudio.sh"))
     # start the service
     input_file = "/tmp/audio_input.wav"
     listening_message = True
     while True:
         if listening_message:
             self.log_info("Listening for voice commands...")
         # run sox to record a voice sample trimming silence at the beginning and at the end
         device = "-t alsa " + str(
             self.config["device"]) if self.config["device"] != "" else ""
         command = "sox " + device + " " + input_file + " trim 0 " + str(
             self.recorder_max_duration) + " silence 1 " + str(
                 self.recorder_start_duration) + " " + str(
                     self.recorder_start_threshold) + "% 1 " + str(
                         self.recorder_end_duration) + " " + str(
                             self.recorder_end_threshold) + "%"
         sdk.python.utils.command.run(command)
         # ensure the sample contains any sound
         max_amplitude = sdk.python.utils.command.run(
             "killall sox 2>&1 2>/dev/null; sox " + input_file +
             " -n stat 2>&1|grep 'Maximum amplitude'|awk '{print $3}'")
         if not sdk.python.utils.numbers.is_number(max_amplitude) or float(
                 max_amplitude) == 0:
             listening_message = False
             continue
         self.log_info("Captured voice sample, processing...")
         listening_message = True
         # recognize the speech
         request = ""
         if self.config["engine"] == "google":
             # use the speech recognition engine to make google recognizing the file
             recognizer = speech_recognition.Recognizer()
             # open the input file
             with speech_recognition.AudioFile(input_file) as source:
                 audio = recognizer.record(source)
             try:
                 # perform the speech recognition
                 results = recognizer.recognize_google(
                     audio, show_all=True, language=self.house["language"])
                 # identify the best result
                 if len(results) != 0:
                     best_result = max(
                         results["alternative"],
                         key=lambda alternative: alternative["confidence"])
                     request = best_result["transcript"]
             except speech_recognition.UnknownValueError:
                 self.log_warning(
                     "Google Speech Recognition could not understand the audio"
                 )
             except speech_recognition.RequestError as e:
                 self.log_warning(
                     "Could not request results from Google Speech Recognition module; {0}"
                     .format(e))
         elif self.config["engine"] == "pocketsphinx":
             # run pocketsphinx to recognize the speech in the audio file
             language = self.house["language"].replace("-", "_")
             command = "pocketsphinx_continuous -infile " + input_file + " -hmm /usr/share/pocketsphinx/model/hmm/" + language + "/hub4wsj_sc_8k/ -dict /usr/share/pocketsphinx/model/lm/" + language + "/cmu07a.dic 2>/dev/null"
             output = sdk.python.utils.command.run(command)
             request = output.replace("000000000: ", "")
         if self.debug:
             # repeat the question
             message = Message(self)
             message.recipient = "notification/" + self.config["speaker"]
             message.command = "RUN"
             message.args = "info"
             message.set_data("I have understood: " + request)
             self.send(message)
         # ask the chatbot what to respond
         message = Message(self)
         message.recipient = "controller/chatbot"
         message.command = "ASK"
         message.set("request", request)
         message.set("accept", ["text"])
         self.send(message)
Пример #21
0
 def evaluate_rule(self, rule_id, macro):
     rule = self.rules[rule_id][macro]
     # 1) evaluate all the conditions of the rule
     or_evaluations = []
     for or_conditions in rule["conditions"]:
         and_evaluations = []
         for and_conditions in or_conditions:
             # remove spaces
             and_conditions = re.sub(' +', ' ', and_conditions)
             # look for sub expressions (grouped within parenthesis) and calculate them individually
             expressions = re.findall("\(([^\)]+)\)", and_conditions)
             for i in range(len(expressions)):
                 expression = expressions[i]
                 # subexpression will become internal variables
                 placeholder = "%exp_" + str(i) + "%"
                 # expression format is "exp1 operator exp2" (e.g. a == b)
                 exp1, operator, exp2 = expression.split(' ')
                 # calculate the sub expression
                 exp1_value = self.values[rule_id][macro][exp1]
                 exp2_value = self.values[rule_id][macro][exp2]
                 exp_value = self.evaluate_condition(
                     exp1_value, operator, exp2_value)
                 self.log_debug("[" + rule_id + "][" + macro +
                                "] resolving " + exp1 + " (" +
                                sdk.python.utils.strings.truncate(
                                    str(exp1_value), 50) + ") " + operator +
                                " " + exp2 + " (" +
                                sdk.python.utils.strings.truncate(
                                    str(exp2_value), 50) + "): " +
                                str(exp_value) + " (alias " + placeholder +
                                ")")
                 # store the sub expressions result in the values
                 self.values[rule_id][macro][placeholder] = exp_value
                 and_conditions = and_conditions.replace(
                     "(" + expression + ")", placeholder)
             # evaluate the main expression
             a, operator, b = and_conditions.split(' ')
             a_value = self.values[rule_id][macro][a]
             b_value = self.values[rule_id][macro][b]
             sub_evaluation = self.is_true(a_value, operator, b_value)
             self.log_debug(
                 "[" + rule_id + "][" + macro + "] evaluating condition " +
                 a + " (" +
                 sdk.python.utils.strings.truncate(str(a_value), 50) +
                 ") " + operator + " " + b + " (" +
                 sdk.python.utils.strings.truncate(str(b_value), 50) +
                 "): " + str(sub_evaluation))
             and_evaluations.append(sub_evaluation)
         # evaluation is true if all the conditions are met
         and_evaluation = True
         for evaluation in and_evaluations:
             if not evaluation: and_evaluation = False
         self.log_debug("[" + rule_id + "][" + macro +
                        "] AND block evaluates to " + str(and_evaluation))
         or_evaluations.append(and_evaluation)
     # evaluation is true if at least one condition is met
     or_evaluation = False
     for evaluation in or_evaluations:
         if evaluation: or_evaluation = True
     # if there were no conditions, the rule evaluates to true
     if len(or_evaluations) == 0: or_evaluation = True
     self.log_debug("[" + rule_id + "][" + macro + "] rule evaluates to " +
                    str(or_evaluation))
     # evaluate to false, just return
     if not or_evaluation:
         return
     # if suppress is in place for this rule, just return
     if self.filter_notification(rule_id, rule):
         return
     # 2) execute the requested actions
     if "actions" in rule:
         for action in rule["actions"]:
             action = re.sub(' +', ' ', action)
             # replace constants and variables placeholders in the action with their values
             action = self.format_placeholders(rule_id, macro, action)
             # execute the action
             action_split = action.split(" ")
             command = action_split[0]
             # set the sensor to a value or poll it
             if command == "SET" or command == "POLL":
                 sensor_id = action_split[1]
                 message = Message(self)
                 message.recipient = "controller/hub"
                 message.command = command
                 message.args = sensor_id
                 if command == "SET": message.set_data(action_split[2])
                 self.send(message)
             # run another rule
             elif command == "RUN":
                 rule_to_run = action_split[1]
                 message = Message(self)
                 message.recipient = "controller/alerter"
                 message.command = command
                 message.args = rule_to_run
                 self.send(message)
     # 3) format the alert text
     # replace constants and variables placeholders in the alert text with their values
     alert_text = self.format_placeholders(rule_id, macro, rule["text"])
     # 4) notify about the alert and save it
     if rule["severity"] != "none" and rule_id not in self.on_demand:
         self.log_info("[" + rule_id + "][" + rule["severity"] + "] " +
                       alert_text)
         if rule["severity"] != "debug":
             # ask db to save the alert
             message = Message(self)
             message.recipient = "controller/db"
             message.command = "SAVE_ALERT"
             message.args = rule["severity"]
             message.set_data(alert_text)
             self.send(message)
             # trigger output modules for notifications
             message = Message(self)
             message.recipient = "*/*"
             message.command = "NOTIFY"
             message.args = rule["severity"] + "/" + rule_id
             message.set_data(alert_text)
             self.send(message)
     # 5) if rule is manually requested to run, respond back
     if rule_id in self.on_demand:
         # retrieve original message
         message = self.on_demand[rule_id]
         message.reply()
         message.set_data(alert_text)
         self.send(message)
         del self.on_demand[rule_id]
     # 6) clean up, rule completed execution, remove the rule_id from the request queue and all the collected values
     del self.requests[rule_id][macro]
     if len(self.requests[rule_id]) == 0: del self.requests[rule_id]
     del self.values[rule_id][macro]
     if len(self.values[rule_id]) == 0: del self.values[rule_id]
Пример #22
0
 def on_start(self):
     # clear up previous manifest if any
     message = Message(self)
     message.recipient = "*/*"
     message.command = "MANIFEST"
     message.args = self.manifest["package"]
     message.set_null()
     message.retain = True
     self.send(message)
     # publish the new manifest
     message = Message(self)
     message.recipient = "*/*"
     message.command = "MANIFEST"
     message.args = self.manifest["package"]
     message.set_data(self.manifest)
     message.retain = True
     self.send(message)
     # start all the requested modules
     for entry in self.modules:
         self.start_module(entry)
         self.sleep(0.1)
     self.sleep(60)
     # loop forever
     while True:
         self.sleep(10)
Пример #23
0
 def on_message(self, message):
     # somebody ask this chatbot about something
     if message.command == "ASK":
         request = message.get("request")
         accept = message.get("accept")
         # remove weird characters from the request
         request = self.cleanup.sub(' ', request)
         action = None
         # build up the vocabularies to check based on what the sender asks
         vocabularies = []
         if "text" in accept:
             vocabularies.extend([
                 self.vocabulary["custom"], self.vocabulary_rules,
                 self.vocabulary_sensors_text
             ])
         if "image" in accept:
             vocabularies.extend([self.vocabulary_sensors_image])
         # evaluate each dictionary individually until we find a good answer
         for kb in vocabularies:
             evaluation = self.evaluate(request, kb)
             if evaluation is None: continue
             keywords = evaluation[0]
             score = evaluation[1]
             # if we are confident enough
             if score > self.not_understood_score:
                 actions = kb[keywords]
                 # pick up a random action and break
                 action = actions[sdk.python.utils.numbers.randint(
                     0,
                     len(actions) - 1)]
                 self.log_info("I've been asked by " + message.sender +
                               " '" + request + "'. I am " + str(score) +
                               "% sure to respond with '" + str(action) +
                               "'")
                 break
         # if we have no good answer, just tell the sender
         if action is None:
             action = self.vocabulary["not_understood"][
                 sdk.python.utils.numbers.randint(
                     0,
                     len(self.vocabulary["not_understood"]) - 1)]
             self.log_info(
                 "I've been asked by " + message.sender + " '" + request +
                 "' but I'm not sure enough so I'd respond with '" +
                 str(action) + "'")
         # reponse is a static text
         if keywords in self.vocabulary[
                 "custom"] or action in self.vocabulary["not_understood"]:
             # respond back
             message.reply()
             message.set("type", "text")
             message.set("content", action)
             self.send(message)
         # reponse is associated to a rule
         elif keywords in self.vocabulary_rules:
             # ask alerter to run the rule (requesting module has to intercept NOTIFY broadcast
             alerter_msg = Message(self)
             alerter_msg.recipient = "controller/alerter"
             alerter_msg.command = "RUN"
             alerter_msg.args = action
             self.sessions.register(alerter_msg, {"message": message})
             self.send(alerter_msg)
         # reponse is associated to a sensor
         elif keywords in self.vocabulary_sensors_text or keywords in self.vocabulary_sensors_image:
             # ask the db for the latest value of the sensor (continues in message.command == "GET")
             db_msg = Message(self)
             db_msg.recipient = "controller/db"
             db_msg.command = "GET"
             db_msg.args = action
             self.sessions.register(db_msg, {
                 "message": message,
                 "description": keywords.lower()
             })
             self.send(db_msg)
     # received latest value from a sensor
     elif message.sender == "controller/db" and message.command == "GET":
         session = self.sessions.restore(message)
         if session is None: return
         sensor_id = message.args
         sensor = self.sensors[sensor_id]
         value = str(message.get("data")[0]) if len(
             message.get("data")) > 0 else "N.A."
         if sensor_id in self.sensors and "unit" in self.sensors[sensor_id]:
             value = value + str(self.sensors[sensor_id]["unit"])
         message = session["message"]
         message.reply()
         if sensor["format"] == "image":
             type = "image"
             message.set(
                 "description",
                 sensor["description"] if "description" in sensor else "")
         else:
             value_is = self.vocabulary["value_is"][
                 sdk.python.utils.numbers.randint(
                     0,
                     len(self.vocabulary["value_is"]) - 1)]
             value = session["description"] + " " + value_is + " " + value
             type = "text"
         message.set("type", type)
         message.set("content", value)
         self.send(message)
     # received response from alerter after running the requested rule
     elif message.sender == "controller/alerter" and message.command == "RUN":
         session = self.sessions.restore(message)
         if session is None: return
         text = message.get_data()
         # retrieve requesting message
         message = session["message"]
         message.reply()
         message.set("type", "text")
         value_is = self.vocabulary["value_is"][
             sdk.python.utils.numbers.randint(
                 0,
                 len(self.vocabulary["value_is"]) - 1)]
         message.set("content", text)
         self.send(message)
Пример #24
0
 def on_message(self, message):
     # requested to update/save a configuration file
     if message.command == "SAVE":
         if self.parse_topic(message.args) is None: return
         version, filename = self.parse_topic(message.args)
         self.save_config_file(filename, version, message.get_data())
     # requested to delete a configuration file
     elif message.command == "DELETE":
         if self.parse_topic(message.args) is None: return
         version, filename = self.parse_topic(message.args)
         self.delete_config_file(filename, version)
     # requested to rename a configuration file
     elif message.command == "RENAME":
         if self.parse_topic(message.args) is None: return
         version, from_filename = self.parse_topic(message.args)
         to_filename = message.get_data()
         self.rename_config_file(from_filename, to_filename, version)
     # a module just subscribed to a configuration topic
     elif message.command == "SUBSCRIBE":
         # if config is not loaded yet, return, the sender will call back later
         if self.load_config_running:
             return
         # split pattern requested from configuration version
         match = re.match("^([^\/]+)\/(.+)$", message.get_data())
         if match is None: return
         version, pattern =  match.groups()
         # check if we have this configuration file by cycling through all the topics
         if self.index is None:
             return
         for topic in self.index:
             # if the pattern subscribed matches the configuration topic
             if mqtt.topic_matches_sub(pattern, topic):
                 # when a service subscribes to all the sensors, just send over those associated with those service to avoid sending out too many messages
                 if self.gateway_version >= 2 and message.sender.startswith("service/") and pattern == "sensors/#":
                     if "service" not in self.index[topic]["content"] or "service/"+self.index[topic]["content"]["service"]["name"] != message.sender:
                         continue
                 # respond to the module (directly) with the requested configuration file
                 self.publish_config(topic, self.index[topic]["version"], self.index[topic]["content"], message.sender)
         # ack the subscribe request so the sender module will not re-send the subscribe request message again
         ack_message = Message(self)
         ack_message.recipient = message.sender
         ack_message.command = "SUBSCRIBE_ACK"
         ack_message.set_data(message.get_data())
         self.send(ack_message)
     # receive manifest file (configuration is already loaded), it may contain default configurations
     elif message.command == "MANIFEST":
         if message.is_null: 
             return
         manifest = message.get_data()
         # ensure this is a manifest we can handle
         if manifest["manifest_schema"] != self.supported_manifest_schema: 
             return
         self.log_debug("Received manifest from "+message.sender)
         # if not accepting default configurations or if there are no default config, just return
         if not self.accept_default_config or self.force_reload or not "default_config" in manifest: 
             return
         # ensure we have not already received the same manifest before
         if message.sender in self.manifests and self.manifests[message.sender] == self.get_hash(str(manifest)):
             return
         self.manifests[message.sender] = self.get_hash(str(manifest))
         # if there is a default configuration in the manifest file, save it
         default_config = manifest["default_config"]
         for entry in default_config:
             for filename_with_version in entry:
                 if self.parse_filename(filename_with_version) is None: return
                 filename, version = self.parse_filename(filename_with_version)
                 file_content = entry[filename_with_version]
                 # do not overwrite existing files since the user may have changed default values
                 # for updated configurations, prevent saving the new version, letting the module managing the upgrade
                 if filename in self.index: 
                     continue
                 # ensure the file is in a valid YAML format
                 try:
                     content = yaml.safe_dump(file_content, default_flow_style=False)
                 except Exception,e:
                     self.log_warning("unable to save "+filename+", invalid YAML format: "+str(file_content)+" - "+exception.get(e))
                     return
                 # save the new/updated default configuration file
                 self.log_debug("Received new default configuration file "+filename)
                 self.save_config_file(filename, version, file_content, False)
                 self.reload_config()
 def process_inbound(self, node_id, child_id, command, ack, type, payload):
     # ensure command and type are valid
     if command >= len(self.commands):
         self.log_error("[" + str(node_id) + "][" + str(child_id) +
                        "] command not supported: " + str(command))
         return
     if type >= len(self.types[command]):
         self.log_error("[" + str(node_id) + "][" + str(child_id) +
                        "] type not supported: " + str(type))
         return
     # map the correspoding command and type string
     command_string = self.commands[command]
     type_string = self.types[command][type]
     ack_string = self.acks[ack]
     self.log_debug("[" + str(node_id) + "][" + str(child_id) + "][" +
                    command_string + "][" + type_string + "][" +
                    ack_string + "] received: " + str(payload))
     # handle protocol messages
     if command_string == "PRESENTATION":
         # handle presentation messages
         self.log_debug("[" + str(node_id) + "][" + str(child_id) +
                        "] presented as " + type_string)
     elif command_string == "SET":
         # handle set messages (messages from sensors handled below)
         self.log_debug("[" + str(node_id) + "][" + str(child_id) + "][" +
                        command_string + "][" + type_string + "]: " +
                        payload)
     elif command_string == "REQ":
         # handle req messages
         self.log_debug("[" + str(node_id) + "][" + str(child_id) + "][" +
                        command_string + "][" + type_string + "]: " +
                        payload)
     elif command_string == "INTERNAL":
         # handle internal messages
         if type_string == "I_TIME":
             # return the time as requested by the sensor
             self.log_debug("[" + str(node_id) + "] requesting timestamp")
             self.tx(node_id, child_id, command_string, type_string,
                     int(time.time()))
         elif type_string == "I_SKETCH_NAME":
             # log the sketch name
             self.log_debug("[" + str(node_id) +
                            "] reported sketch name: " + str(payload))
         elif type_string == "I_SKETCH_VERSION":
             # log the sketch version
             self.log_debug("[" + str(node_id) +
                            "] reported sketch version: " + str(payload))
         elif type_string == "I_ID_REQUEST":
             # return the next available id
             self.log_debug("[" + str(node_id) + "] requesting node_id")
             # we assume low node_id are assigned statically and we can manage the upper end
             for id in range(100, 254):
                 found = False
                 # cycle over registered sensors
                 for sensor_id in self.sensors:
                     sensor = self.sensors[sensor_id]
                     if id == sensor["node_id"]:
                         found = True
                         break
                 # if id is already registered, go to the next
                 if found:
                     continue
                     # assign the id which is not allocated yet to the node
                 else:
                     self.tx(node_id, child_id, command_string,
                             "I_ID_RESPONSE", str(id))
                     break
         elif type_string == "I_CONFIG":
             # return the controller's configuration
             self.log_debug("[" + str(node_id) +
                            "] requesting configuration")
             metric = "I" if self.units == "imperial" else "M"
             self.tx(node_id, child_id, command_string, type_string, metric)
         elif type_string == "I_BATTERY_LEVEL":
             # log the battery level
             self.log_debug("[" + str(node_id) +
                            "] reporting battery level: " + str(payload) +
                            "%")
         elif type_string == "I_LOG_MESSAGE":
             # log a custom message
             self.log_debug("[" + str(node_id) + "] logging: " +
                            str(payload))
         elif type_string == "I_GATEWAY_READY":
             # report gateway report
             self.log_debug("[" + str(node_id) +
                            "] reporting gateway ready")
         elif type_string == "I_POST_SLEEP_NOTIFICATION":
             # report awake
             self.log_debug("[" + str(node_id) + "] reporting awake")
         elif type_string == "I_HEARTBEAT_RESPONSE" or type_string == "I_PRE_SLEEP_NOTIFICATION":
             # handle smart sleep
             self.log_debug("[" + str(node_id) + "] going to sleep")
             if node_id in self.queue and not self.queue[node_id].empty():
                 # process the queue
                 while not self.queue[node_id].empty():
                     node_id, child_id, command_string, type_string, payload = self.queue[
                         node_id].get()
                     # send the message
                     self.tx(node_id, child_id, command_string, type_string,
                             payload)
         else:
             self.log_debug("[" + str(node_id) + "] received " +
                            type_string)
     elif command_string == "STREAM":
         # handle stream messages
         return
     else:
         self.log_error(" Invalid command " + command_string)
     # handle messages for registered sensors
     for sensor_id in self.sensors:
         sensor = self.sensors[sensor_id]
         if node_id == sensor["node_id"] and child_id == sensor[
                 "child_id"] and command_string == sensor[
                     "command"] and type_string == sensor["type"]:
             # prepare the message
             message = Message(self)
             message.recipient = "controller/hub"
             message.command = "IN"
             message.args = sensor_id
             message.set("value", payload)
             # send the measure to the controller
             self.send(message)