diff --git a/alerts.py b/alerts.py --- a/alerts.py +++ b/alerts.py @@ -6,8 +6,9 @@ import logging class AlertManager: - def __init__(self, config): + def __init__(self, database, config): self.logger = logging.getLogger('hydrobot') + self.database = database self.from_addr = config.get("alerts", "from_address") self.to_addr = config.get("alerts", "to_address") self.mail_server = config.get("alerts", "mail_server") @@ -59,7 +60,7 @@ class AlertManager: server.login(self.username, self.password) server.send_message(msg) server.quit() - self.logger.debug("Sent email alert: " + message) + self.logger.info("Sent email alert: " + message) except: self.logger.error("Sending email alert failed!") @@ -96,19 +97,24 @@ class OutputFeedbackAlert(Alert): def evalutate(self): #get output status - output_status = 1 + output_status = self.manager.database.read_value(self.output_module, self.output_type, int(self.output_number)) + print("Output status: " + str(output_status)) #get input status - input_status = 0 - if output_status == 1: - if input_status < self.on_threshold: - #alert! - print("Alert! " + self.name + " did not turn on!") - self.manager.send_alert("Alert! " + self.name + " did not turn on!") - if output_status == 0: - if input_status > self.off_threshold: - #alert! - print("Alert! " + self.name + " did not turn off!") - self.manager.send_alert("Alert! " + self.name + " did not turn off!") + input_status = self.manager.database.read_value(self.input_module, self.input_type, int(self.input_number)) + print("Input status: " + str(input_status)) + + if input_status != None: + + if output_status == 1: + if input_status < self.on_threshold: + #alert! + #print("Alert! " + self.name + " did not turn on!") + self.manager.send_alert("Alert! " + self.name + " did not turn on!") + if output_status == 0: + if input_status > self.off_threshold: + #alert! + #print("Alert! " + self.name + " did not turn off!") + self.manager.send_alert("Alert! " + self.name + " did not turn off!") class MeasurementAlert(Alert): @@ -124,14 +130,17 @@ class MeasurementAlert(Alert): self.hysteresis = hysteresis def evalutate(self): - #get input status - input_status = 0 - if input_status > self.high_threshold: - #alert! - print("Alert! " + self.name + " is too high!") - self.manager.send_alert("Alert! " + self.name + " is too high!") - if input_status < self.low_threshold: - #alert! - print("Alert! " + self.name + " is too low!") - self.manager.send_alert("Alert! " + self.name + " is too low!") + #get measurement + measurement = self.manager.database.read_value(self.input_module, self.input_type, int(self.input_number)) + print("Measurement: " + str(measurement)) + + if measurement != None: + if measurement > self.high_threshold: + #alert! + #print("Alert! " + self.name + " is too high!") + self.manager.send_alert("Alert! " + self.name + " is too high!") + if measurement < self.low_threshold: + #alert! + #print("Alert! " + self.name + " is too low!") + self.manager.send_alert("Alert! " + self.name + " is too low!") diff --git a/database.py b/database.py --- a/database.py +++ b/database.py @@ -29,11 +29,11 @@ class Database: port = config.get("database", "port") username = config.get("database", "username") password = config.get("database", "password") - database = config.get("database", "database") + self.database = config.get("database", "database") self.name = config.get("system", "name") try: - self.client = InfluxDBClient(host, port, username, password, database) + self.client = InfluxDBClient(host, port, username, password, self.database) MySeriesHelper.Meta.client = self.client MySeriesHelper.Meta.series_name = self.name + '.{measurement}' self.logger.info("Connected to database") @@ -57,3 +57,24 @@ class Database: self.logger.error("Database issue!") else: self.logger.warning("Unknown data key: " + str(message)) + + def read_value(self, name, data_name, sensor_num): + #try: + + if sensor_num > 0: + num = "_" + str(sensor_num) + else: + num = "" + measurement = "\"" + self.name + "." + name + "." + data_name + num + "\"" + results = self.client.query(("SELECT last(\"value\") from %s") % (measurement)) + points = results.get_points() + for item in points: + return item["last"] + + + #except: + # self.logger.error("Database issue!") + + + + diff --git a/hydrobot.py b/hydrobot.py --- a/hydrobot.py +++ b/hydrobot.py @@ -13,6 +13,7 @@ import module import protocol from database import Database from scheduler import Scheduler +import alerts #TODO @@ -48,9 +49,9 @@ def main(argv): logger.info("Starting HydroBot!") - if len(argv) < 1: - logger.error("Please specify a can interface") - return 1 + #if len(argv) < 1: + # logger.error("Please specify a can interface") + # return 1 database = Database(config) @@ -59,6 +60,10 @@ def main(argv): scheduler = Scheduler(module_network, config) scheduler.start() + + alert_manager = alerts.AlertManager(database, config) + + #alert_manager.evaluate_alerts() while True: time.sleep(0.1) diff --git a/hydrobot_def.json b/hydrobot_def.json --- a/hydrobot_def.json +++ b/hydrobot_def.json @@ -40,11 +40,20 @@ }, { - "name" : "light", + "name" : "eco2", "type" : "input", "sensor_num" : "0", "functions" : [ - "ambient_light" + "air_eco2" + ] + + }, + { + "name" : "tvoc", + "type" : "input", + "sensor_num" : "0", + "functions" : [ + "air_tvoc" ] } @@ -232,7 +241,35 @@ "id" : "0x04", "name" : "protomodule", "display" : "ProtoModule", - "class" : "ProtoModule" + "class" : "ProtoModule", + "sensors" : [ + { + "name" : "ph", + "type" : "input", + "sensor_num" : "0", + "functions" : [ + "ph" + ] + + } + ] + }, + { + "id" : "0x05", + "name" : "lightsense", + "display" : "LightSense", + "class" : "LightSense", + "sensors" : [ + { + "name" : "light", + "type" : "input", + "sensor_num" : "0", + "functions" : [ + "ambient_light" + ] + + } + ] } ], "data_keys" : [ @@ -321,6 +358,18 @@ "units" : "pH" }, { + "key" : "0x000F", + "name" : "air_eco2", + "display" : "Air eCO2", + "units" : "ppm" + }, + { + "key" : "0x0010", + "name" : "air_tvoc", + "display" : "Air TVOC", + "units" : "ppb" + }, + { "key" : "0x0100", "name" : "can_id", "display" : "CAN ID", diff --git a/module.py b/module.py --- a/module.py +++ b/module.py @@ -181,6 +181,44 @@ class WaterSenseModule(Module): def config(self, data_key, sensor_num, value): message = HydroBotMessage(self.uuid, (0x80 | protocol.lookup_command_key_by_name("config")), protocol.lookup_data_key_by_name(data_key), sensor_num, value) self.interface.send_message(message) + +class ProtoModule(Module): + + def __init__(self, address, interface, name, database): + super(ProtoModule, self).__init__(address, interface, name, database) + + def send_message(self, message): + self.interface.network.send_message(message) + + def receive_message(self, message): + self.logger.debug("Receive message! From: " + self.name) + self.database.log_message(self.name, message) + + def update(self): + pass + + def config(self, data_key, sensor_num, value): + message = HydroBotMessage(self.uuid, (0x80 | protocol.lookup_command_key_by_name("config")), protocol.lookup_data_key_by_name(data_key), sensor_num, value) + self.interface.send_message(message) + +class LightSenseModule(Module): + + def __init__(self, address, interface, name, database): + super(LightSenseModule, self).__init__(address, interface, name, database) + + def send_message(self, message): + self.interface.network.send_message(message) + + def receive_message(self, message): + self.logger.debug("Receive message! From: " + self.name) + self.database.log_message(self.name, message) + + def update(self): + pass + + def config(self, data_key, sensor_num, value): + message = HydroBotMessage(self.uuid, (0x80 | protocol.lookup_command_key_by_name("config")), protocol.lookup_data_key_by_name(data_key), sensor_num, value) + self.interface.send_message(message) class UnknownModule(Module): diff --git a/network_interface.py b/network_interface.py --- a/network_interface.py +++ b/network_interface.py @@ -36,13 +36,17 @@ class CanBusNetworkInterface(NetworkInte def __init__(self, network, interface_name): super(CanBusNetworkInterface, self).__init__(network) - # Bring up CAN interface (maybe do this in a systemd service file) - # Passing random arguments to sudo is super dangerous - os.system("sudo ip link set " + interface_name + " up type can bitrate 500000") - - self.interface_name = interface_name - self.dev = socketcan.SocketCanDev(interface_name) - self.queue = queue.CanQueue(self.dev) + try: + # Bring up CAN interface (maybe do this in a systemd service file) + # Passing random arguments to sudo is super dangerous + os.system("sudo ip link set " + interface_name + " up type can bitrate 500000") + + self.interface_name = interface_name + self.queue = None + self.dev = socketcan.SocketCanDev(interface_name) + self.queue = queue.CanQueue(self.dev) + except: + self.logger.error("Can Bus failed to initilize") #bidirectional address <==> uuid lookup self.address_lookup = {} @@ -51,37 +55,47 @@ class CanBusNetworkInterface(NetworkInte def start(self): super(CanBusNetworkInterface, self).start() self.logger.info("CanBusNetworkInterface: start") - self.queue.start() - _thread.start_new_thread(self.process_message, ()) + if self.queue != None: + self.queue.start() + _thread.start_new_thread(self.process_message, ()) + else: + self.logger.error("Can Bus failed to start") def send_message(self, message): address = self.uuid_lookup[message.module_uuid] can_frame = can.Frame(address, 8, [message.message_type, (message.data_key>>8) & 0xff, (message.data_key>>0) & 0xff, message.sensor_num] + list(struct.pack("f", message.data))) self.logger.debug("Send CAN message! CAN ID: " + hex(can_frame.id) + " Data: " + str(can_frame.data)) - self.queue.send(can_frame) + if self.queue != None: + self.queue.send(can_frame) + else: + self.logger.error("Can Bus is not initilized") def process_message(self): while True: - frame = self.queue.recv() - if frame != None: - - base_id = frame.id & 0x7fe + if self.queue != None: + frame = self.queue.recv() - if not base_id in self.address_lookup: - self.logger.debug("Uknown module address: " + hex(base_id)) - device_id = frame.data[0] & 0x7f - new_module = self.network.module_list.new_module(device_id, base_id, self) - self.address_lookup[base_id] = new_module.uuid - self.uuid_lookup[new_module.uuid] = base_id - - self.logger.debug("Received CAN message! ID: " + hex(frame.id)) - - data = [frame.data[7], frame.data[6], frame.data[5], frame.data[4]] - b = struct.pack('4B', *data) - f_data = struct.unpack('>f', b)[0] - message = HydroBotMessage(self.address_lookup[base_id], frame.data[0], ((frame.data[1] << 8) + frame.data[2]), frame.data[3], f_data) - self.logger.debug("Process message: " + str(message)) - self.network.process_message(message) + if frame != None: + + base_id = frame.id & 0x7fe + + if not base_id in self.address_lookup: + self.logger.debug("Uknown module address: " + hex(base_id)) + device_id = frame.data[0] & 0x7f + new_module = self.network.module_list.new_module(device_id, base_id, self) + self.address_lookup[base_id] = new_module.uuid + self.uuid_lookup[new_module.uuid] = base_id + + self.logger.debug("Received CAN message! ID: " + hex(frame.id)) + + data = [frame.data[7], frame.data[6], frame.data[5], frame.data[4]] + b = struct.pack('4B', *data) + f_data = struct.unpack('>f', b)[0] + message = HydroBotMessage(self.address_lookup[base_id], frame.data[0], ((frame.data[1] << 8) + frame.data[2]), frame.data[3], f_data) + self.logger.debug("Process message: " + str(message)) + self.network.process_message(message) + else: + self.logger.error("Can Bus is not initilized") class WifiNetworkInterface(NetworkInterface):