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 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)
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))
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)
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)
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)
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)
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)
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)
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)