Changeset - 7b33cc2da5f0
[Not reviewed]
tip refactor
0 6 0
matthewreed - 6 years ago 2020-02-25 20:19:03

Added new gas sensing and other stuff from a long time ago
6 files changed with 197 insertions and 61 deletions:
0 comments (0 inline, 0 general)
alerts.py
Show inline comments
 
@@ -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!")
 
database.py
Show inline comments
 
@@ -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!")
 
    
 
        
 
        
 
        
hydrobot.py
Show inline comments
 
@@ -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)
hydrobot_def.json
Show inline comments
 
@@ -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",
module.py
Show inline comments
 
@@ -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):
 
    
network_interface.py
Show inline comments
 
@@ -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):
0 comments (0 inline, 0 general)