Example #1
0
 def __init__(self, log_flag=False):
     ## Logger object for formatting and printing logs
     self.logger = Logger(log_flag, LOG_FILE)
     self._parser = DaemonConfigParser(MASTER_CONF_FILE)
     ## Username for the database, searched in configuration file
     self.db_username = self._parser.getValueFromSection(
         MASTER_CONF_MYSQL_SECTION, MASTER_CONF_MYSQL_USER_ENTRY)
     ## Password for the database, searched in configuration file
     self.db_passwd = self._parser.getValueFromSection(
         MASTER_CONF_MYSQL_SECTION, MASTER_CONF_MYSQL_PASSWORD_ENTRY)
     ## Database name for the database, searched in configuration file
     self.db_dbname = self._parser.getValueFromSection(
         MASTER_CONF_MYSQL_SECTION, MASTER_CONF_MYSQL_DB_NAME_ENTRY)
     if not self.db_username or not self.db_passwd or not self.db_dbname:
         frameinfo = getframeinfo(currentframe())
         self.logger.debug(
             "[ MASTER DAEMON " + frameinfo.filaname + ":" +
             str(frameinfo.lineno) +
             " ]: initialization error: wrong or missing SQL configuration."
         )
         sys.exit(1)
     ## Function array with option ID
     self.functions_transform = {
         0: utils.convert_none,
         1: utils.convert_temperature,
         2: utils.convert_hundred,
         3: utils.convert_float32
     }
Example #2
0
def master_conf_initdb():
    file = DaemonConfigParser(MASTER_CONF_FILE_TO)

    # mysql password
    password = "".join(
        random.choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for _ in range(128)
    )
    password = sha1(password.encode("utf-8"))
    file.writeValueFromSection("mysql", "password", password.hexdigest())
    os.system(
        "sed -i \"s/define('DB_PASSWORD', 'domoleaf')/define('DB_PASSWORD', '"
        + password.hexdigest()
        + "')/g\" /etc/domoleaf/www/config.php"
    )

    # mysql user
    query1 = 'DELETE FROM user WHERE User="******"'
    query2 = 'DELETE FROM db WHERE User="******"'
    query3 = (
        "INSERT INTO user (Host, User, Password) VALUES ('%', 'domoleaf', PASSWORD('" + password.hexdigest() + "'));"
    )
    query4 = 'INSERT INTO db (Host, Db, User, Select_priv, Insert_priv, Update_priv, Delete_priv, Create_priv, Drop_priv, Grant_priv, References_priv, Index_priv, Alter_priv, Create_tmp_table_priv, Lock_tables_priv, Create_view_priv, Show_view_priv, Create_routine_priv, Alter_routine_priv, Execute_priv, Event_priv, Trigger_priv) VALUES ("%","domoleaf","domoleaf","Y","Y","Y","Y","Y","Y","Y","Y","Y","Y","Y","Y","Y","Y","Y","Y","Y","Y","Y");'
    query5 = "FLUSH PRIVILEGES"

    Popen(
        ["mysql", "--defaults-file=/etc/mysql/debian.cnf", "mysql", "-e", query1],
        stdin=PIPE,
        stdout=PIPE,
        stderr=PIPE,
        bufsize=-1,
    )
    Popen(
        ["mysql", "--defaults-file=/etc/mysql/debian.cnf", "mysql", "-e", query2],
        stdin=PIPE,
        stdout=PIPE,
        stderr=PIPE,
        bufsize=-1,
    )
    Popen(
        ["mysql", "--defaults-file=/etc/mysql/debian.cnf", "mysql", "-e", query3],
        stdin=PIPE,
        stdout=PIPE,
        stderr=PIPE,
        bufsize=-1,
    )
    Popen(
        ["mysql", "--defaults-file=/etc/mysql/debian.cnf", "mysql", "-e", query4],
        stdin=PIPE,
        stdout=PIPE,
        stderr=PIPE,
        bufsize=-1,
    )
    Popen(
        ["mysql", "--defaults-file=/etc/mysql/debian.cnf", "mysql", "-e", query5],
        stdin=PIPE,
        stdout=PIPE,
        stderr=PIPE,
        bufsize=-1,
    )
Example #3
0
 def __init__(self, slave_keys):
     ## Logger object for formatting and printing logs
     self.logger = Logger(DEBUG_MODE, LOG_FILE);
     ## SQL object for managing database
     self.sql = MasterSql();
     self._parser = DaemonConfigParser('/etc/domoleaf/master.conf');
     ## Object containing AES keys for encrypted communications
     self.aes_slave_keys = slave_keys;
Example #4
0
 def __init__(self, log_flag):
     ## Logger object for formatting and printing logs
     self.logger = Logger(log_flag, LOG_FILE)
     self.logger.info('Started Domoleaf Slave daemon')
     ## Array of master daemon on local network
     self.connected_masters = {}
     ## Array of monitor KNX on local network
     self.connected_knx = []
     ## Array of monitor EnOcean on local network
     self.connected_enocean = []
     ## Array of cron running on the system
     self.connected_cron = []
     self._scanner = Scanner(log_flag)
     self._hostlist = []
     myhostname = socket.gethostname().upper()
     if SLAVE_NAME_PREFIX in myhostname:
         self._scanner.scan()
         self._hostlist = self._scanner._HostList
     else:
         self._hostlist.append(Host('', '127.0.0.1', myhostname))
     self._parser = DaemonConfigParser(SLAVE_CONF_FILE)
     ## Keys for encrypting communications
     self.encrypt_keys = {}
     ## Main socket for communication with KNX daemon
     self.knx_sock = None
     ## Main socket for communication with master daemon
     self.master_sock = None
     ## Main socket for communication with enocean daemon
     self.enocean_sock = None
     ## Main socket for communication with cron
     self.cron_sock = None
     ## Private AES key got from configuration file
     self.private_aes = self._parser.getValueFromSection(
         'personnal_key', 'aes')
     self.wifi_init(self._parser.getValueFromSection('wifi', 'ssid'),
                    self._parser.getValueFromSection('wifi', 'password'),
                    self._parser.getValueFromSection('wifi', 'encryption'),
                    self._parser.getValueFromSection('wifi', 'mode'), 0)
     ## Port on which connect got from configuration file
     self.connect_port = self._parser.getValueFromSection(
         SLAVE_CONF_CONNECT_SECTION, SLAVE_CONF_CONNECT_PORT_ENTRY)
     ## Callback array indexed on packet type
     self.functions = {
         KNX_READ_REQUEST: self.knx_read_request,
         KNX_WRITE_SHORT: self.knx_write_short,
         KNX_WRITE_LONG: self.knx_write_long,
         KNX_WRITE_TEMP: self.knx_write_temp,
         CHECK_SLAVE: self.check_slave,
         MONITOR_IP: self.monitor_ip,
         DATA_UPDATE: self.update,
         SEND_TECH: self.send_tech,
         SEND_ALIVE: self.send_alive,
         SEND_INTERFACES: self.send_interfaces,
         SHUTDOWN_D3: self.shutdown_d3,
         REBOOT_D3: self.reboot_d3,
         WIFI_UPDATE: self.wifi_update
     }
Example #5
0
 def send_cron(cron_name):
     try:
         parser = DaemonConfigParser(SLAVE_CONF_FILE);
         port = parser.getValueFromSection('cron', 'port').encode()
         sock = socket.create_connection(('127.0.0.1', port));
         sock.send(bytes(cron_name, 'utf-8'));
         sock.close();
     except Exception as e:
         if 'sock' in locals():
             sock.close()
Example #6
0
def master_conf_init():
    file = DaemonConfigParser(SLAVE_CONF_FILE)
    personnal_key = file.getValueFromSection("personnal_key", "aes")
    hostname = socket.gethostname()

    # KNX Interface
    if os.path.exists("/dev/ttyAMA0"):
        knx = "tpuarts"
        knx_interface = "ttyAMA0"
    elif os.path.exists("/dev/ttyS0"):
        knx = "tpuarts"
        knx_interface = "ttyS0"
    else:
        knx = "ipt"
        knx_interface = "127.0.0.1"

    fic = open("/etc/domoleaf/.domoslave.version", "r")
    domoslave = fic.readline()
    fic.close()

    personnal_key = md5(personnal_key.encode("utf-8"))

    query1 = (
        "INSERT INTO daemon (name, serial, secretkey, validation, version) VALUES ('"
        + hostname
        + "','"
        + hostname
        + "','"
        + personnal_key.hexdigest()
        + "',1,'"
        + domoslave.split("\n")[0]
        + "')"
    )
    query2 = (
        "INSERT INTO daemon_protocol (daemon_id, protocol_id, interface, interface_arg) VALUES (1,1,'"
        + knx
        + "','"
        + knx_interface
        + "')"
    )
    Popen(
        ["mysql", "--defaults-file=/etc/mysql/debian.cnf", "domoleaf", "-e", query1],
        stdin=PIPE,
        stdout=PIPE,
        stderr=PIPE,
        bufsize=-1,
    )
    Popen(
        ["mysql", "--defaults-file=/etc/mysql/debian.cnf", "domoleaf", "-e", query2],
        stdin=PIPE,
        stdout=PIPE,
        stderr=PIPE,
        bufsize=-1,
    )
Example #7
0
 def ipVPN():
     private = InfoSys.ipPrivate();
     parser = DaemonConfigParser(SLAVE_CONF_FILE);
     server = parser.getValueFromSection('openvpn', 'openvpnserver').encode()
     if server.decode() == 'none':
         return '';
     try:
         vpn = ([(s.connect((server, 80)), s.getsockname()[0], s.close()) for s in [socket.socket(socket.AF_INET, socket.SOCK_DGRAM)]][0][1])
     except Exception as e:
         return ''
     if private != vpn:
         return vpn;
     return '';
Example #8
0
 def __init__(self, log_flag):
     self.logger = Logger(log_flag, LOG_FILE);
     self.logger.info('Started Domoleaf Slave daemon');
     print('######## SLAVE DAEMON #######')
     self.connected_masters = {};
     self.connected_knx = [];
     self.connected_enocean = [];
     self.connected_cron = [];
     self.clients = [];
     self._scanner = Scanner(HOST_CONF_FILE);
     self._scanner.scan(False);
     self._hostlist = self._scanner._HostList;
     self._parser = DaemonConfigParser(SLAVE_CONF_FILE);
     self.encrypt_keys = {};
     self.knx_sock = None;
     self.master_sock = None;
     self.enocean_sock = None;
     self.cron_sock = None;
     self.private_aes = hashlib.md5(self._parser.getValueFromSection('personnal_key', 'aes').encode()).hexdigest();
     self.wifi_init(self._parser.getValueFromSection('wifi', 'ssid'), self._parser.getValueFromSection('wifi', 'password'), self._parser.getValueFromSection('wifi', 'encryption'), self._parser.getValueFromSection('wifi', 'mode'), 0);
     self.functions = {
         KNX_READ_REQUEST    : self.knx_read_request,
         KNX_WRITE_SHORT     : self.knx_write_short,
         KNX_WRITE_LONG      : self.knx_write_long,
         KNX_WRITE_TEMP      : self.knx_write_temp,
         CHECK_SLAVE         : self.check_slave,
         MONITOR_IP          : self.monitor_ip,
         DATA_UPDATE         : self.update,
         SEND_TECH           : self.send_tech,
         SEND_ALIVE          : self.send_alive,
         SEND_INTERFACES     : self.send_interfaces,
         SHUTDOWN_D3         : self.shutdown_d3,
         REBOOT_D3           : self.reboot_d3,
         WIFI_UPDATE         : self.wifi_update
     };
Example #9
0
 def __init__(self, log_flag):
     self.logger = Logger(log_flag, LOG_FILE);
     self.logger.info('Started Greenleaf Slave daemon');
     print('######## SLAVE DAEMON #######')
     self.connected_masters = {};
     self.connected_knx = [];
     self.connected_enocean = [];
     self.clients = [];
     self._scanner = Scanner(HOST_CONF_FILE);
     self._scanner.scan(False);
     self._hostlist = self._scanner._HostList;
     self._parser = DaemonConfigParser(SLAVE_CONF_FILE);
     self.encrypt_keys = {};
     self.knx_sock = None;
     self.master_sock = None;
     self.enocean_sock = None;
     self.private_aes = hashlib.md5(self._parser.getValueFromSection('personnal_key', 'aes').encode()).hexdigest();
     self.functions = {
         KNX_READ_REQUEST    : self.knx_read_request,
         KNX_WRITE_SHORT     : self.knx_write_short,
         KNX_WRITE_LONG      : self.knx_write_long,
         KNX_WRITE_TEMP      : self.knx_write_temp,
         CHECK_SLAVE         : self.check_slave,
         MONITOR_IP          : self.monitor_ip,
         DATA_UPDATE         : self.update
     };
Example #10
0
 def __init__(self, _id_elem = 0, _option_id = 0, _debug = False):
     self._id = _id_elem;
     self._debug = _debug;
     self._option_id = _option_id;
     self._parser = DaemonConfigParser(CONF_FILENAME);
     self._db_name = self._parser.getValueFromSection(MYSQL_CONF_SECTION, MYSQL_CONF_USER_ENTRY);
     self._db_passwd = self._parser.getValueFromSection(MYSQL_CONF_SECTION, MYSQL_CONF_PASSWORD_ENTRY);
     self._db_dbname = self._parser.getValueFromSection(MYSQL_CONF_SECTION, MYSQL_CONF_DATABASE_ENTRY);
Example #11
0
 def __init__(self, log_flag):
     ## Logger object for formatting and printing logs
     self.logger = Logger(log_flag, LOG_FILE)
     self.logger.info("Started Domoleaf Slave daemon")
     ## Array of master daemon on local network
     self.connected_masters = {}
     ## Array of monitor KNX on local network
     self.connected_knx = []
     ## Array of monitor EnOcean on local network
     self.connected_enocean = []
     ## Array of cron running on the system
     self.connected_cron = []
     self._scanner = Scanner(log_flag)
     self._hostlist = []
     myhostname = socket.gethostname().upper()
     if SLAVE_NAME_PREFIX in myhostname:
         self._scanner.scan()
         self._hostlist = self._scanner._HostList
     else:
         self._hostlist.append(Host("", "127.0.0.1", myhostname))
     self._parser = DaemonConfigParser(SLAVE_CONF_FILE)
     ## Keys for encrypting communications
     self.encrypt_keys = {}
     ## Main socket for communication with KNX daemon
     self.knx_sock = None
     ## Main socket for communication with master daemon
     self.master_sock = None
     ## Main socket for communication with enocean daemon
     self.enocean_sock = None
     ## Main socket for communication with cron
     self.cron_sock = None
     ## Private AES key got from configuration file
     self.private_aes = self._parser.getValueFromSection("personnal_key", "aes")
     self.wifi_init(
         self._parser.getValueFromSection("wifi", "ssid"),
         self._parser.getValueFromSection("wifi", "password"),
         self._parser.getValueFromSection("wifi", "encryption"),
         self._parser.getValueFromSection("wifi", "mode"),
         0,
     )
     ## Port on which connect got from configuration file
     self.connect_port = self._parser.getValueFromSection(SLAVE_CONF_CONNECT_SECTION, SLAVE_CONF_CONNECT_PORT_ENTRY)
     ## Callback array indexed on packet type
     self.functions = {
         KNX_READ_REQUEST: self.knx_read_request,
         KNX_WRITE_SHORT: self.knx_write_short,
         KNX_WRITE_LONG: self.knx_write_long,
         KNX_WRITE_TEMP: self.knx_write_temp,
         CHECK_SLAVE: self.check_slave,
         MONITOR_IP: self.monitor_ip,
         DATA_UPDATE: self.update,
         SEND_TECH: self.send_tech,
         SEND_ALIVE: self.send_alive,
         SEND_INTERFACES: self.send_interfaces,
         SHUTDOWN_D3: self.shutdown_d3,
         REBOOT_D3: self.reboot_d3,
         WIFI_UPDATE: self.wifi_update,
     }
Example #12
0
def slave_conf_init():
    file = DaemonConfigParser(SLAVE_CONF_FILE_TO);
    
    #KEY
    KEY = ''.join(random.choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for _ in range(128))
    KEY = md5(KEY.encode('utf-8')).hexdigest()
    file.writeValueFromSection('personnal_key', 'aes', KEY);
    
    #KNX Interface
    knx_edit = 'KNXD_OPTS="-e 1.0.254 -D -T -S -b '
    if os.path.exists('/dev/ttyAMA0'):
        knx_edit = knx_edit + 'tpuarts:/dev/ttyAMA0"';
    elif os.path.exists('/dev/ttyS0'):
        knx_edit = knx_edit + 'tpuarts:/dev/ttyS0"';
    else:
        knx_edit = knx_edit + 'ipt:127.0.0.1"';
    conf_knx = open('/etc/knxd.conf', 'w');
    conf_knx.write(knx_edit + '\n');
    conf_knx.close();
Example #13
0
 def __init__(self, slave_keys):
     self.knx_function = {
         OPTION_ON_OFF       : self.send_knx_write_short_to_slave,
         OPTION_VAR          : self.send_knx_write_long_to_slave,
         OPTION_UP_DOWN      : self.send_knx_write_short_to_slave,
         OPTION_OPEN_CLOSE   : self.send_knx_write_short_to_slave,
         OPTION_STOP_UP_DOWN : self.send_knx_write_short_to_slave,
         OPTION_TEMPERATURE_W: self.send_knx_write_temp
     };
     self.sql = MasterSql();
     self._parser = DaemonConfigParser('/etc/greenleaf/master.conf');
     self.aes_slave_keys = slave_keys;
def master_conf_init():
    file = DaemonConfigParser(SLAVE_CONF_FILE);
    personnal_key = file.getValueFromSection('personnal_key', 'aes');
    hostname = socket.gethostname();
    #KNX Interface
    if os.path.exists('/dev/ttyAMA0'):
        knx = "tpuarts"
        knx_interface = 'ttyAMA0';
    elif os.path.exists('/dev/ttyS0'):
        knx = "tpuarts"
        knx_interface = 'ttyS0';
    else:
        knx = "ipt"
        knx_interface = '127.0.0.1';
    domoslave = os.popen("dpkg-query -W -f='${Version}\n' domoslave").read().split('\n')[0];
    query1 = "INSERT INTO daemon (name, serial, secretkey, validation, version) VALUES ('"+hostname+"','"+hostname+"','"+personnal_key+"',1,'"+domoslave+"')"
    query2 = "INSERT INTO daemon_protocol (daemon_id, protocol_id, interface, interface_arg) VALUES (1,1,'"+knx+"','"+knx_interface+"')"
    call(['mysql', '--defaults-file=/etc/mysql/debian.cnf', 'domoleaf',
           '-e', query1]);
    call(['mysql', '--defaults-file=/etc/mysql/debian.cnf', 'domoleaf',
           '-e', query2]);
Example #15
0
 def __init__(self, log_flag):
     self.logger = Logger(log_flag, LOG_FILE);
     self.logger.info('Started Greenleaf Master Daemon');
     self.d3config = {};
     self.aes_slave_keys = {};
     self.aes_master_key = None;
     self.connected_clients = {};
     self.sql = MasterSql();
     self._parser = DaemonConfigParser(MASTER_CONF_FILE);
     self.get_aes_slave_keys();
     self.reload_camera(None, None);
     self.scanner = Scanner(HOSTS_CONF);
     self.scanner.scan(False);
     self.hostlist = self.scanner._HostList;
     self.sql.insert_hostlist_in_db(self.scanner._HostList);
     self.knx_manager = KNXManager(self.aes_slave_keys);
     self.reload_d3config(None, None);
     self.protocol_function = {
         PROTOCOL_KNX        : KNXManager.protocol_knx,
         PROTOCOL_ENOCEAN    : self.protocol_enocean,
         PROTOCOL_IP         : self.protocol_ip
     };
     self.upnp_function = {
         UPNP_PLAY           : self.upnp_set_play,
         UPNP_PAUSE          : self.upnp_set_pause,
         UPNP_NEXT           : self.upnp_set_next,
         UPNP_PREVIOUS       : self.upnp_set_prev,
         UPNP_STOP           : self.upnp_set_stop,
         UPNP_VOLUME_UP      : self.upnp_set_volume_up,
         UPNP_VOLUME_DOWN    : self.upnp_set_volume_down,
         UPNP_SET_VOLUME     : self.upnp_set_volume
     };
     self.enocean_function = {};
     self.data_function = {
         DATA_MONITOR_KNX            	  : self.monitor_knx,
         DATA_MONITOR_IP             	  : self.monitor_ip,
         DATA_MONITOR_ENOCEAN        	  : self.monitor_enocean,
         DATA_MONITOR_BLUETOOTH      	  : self.monitor_bluetooth,
         DATA_KNX_READ               	  : self.knx_read,
         DATA_KNX_WRITE_S            	  : self.knx_write_short,
         DATA_KNX_WRITE_L            	  : self.knx_write_long,
         DATA_SEND_TO_DEVICE         	  : self.send_to_device,
         DATA_CRON_UPNP             		  : self.cron_upnp,
         DATA_SEND_MAIL              	  : self.send_mail,
         DATA_CHECK_SLAVE            	  : self.check_slave,
         DATA_RELOAD_CAMERA          	  : self.reload_camera,
         DATA_RELOAD_D3CONFIG        	  : self.reload_d3config,
         DATA_BACKUP_DB_CREATE_LOCAL       : self.backup_db_create_local,
         DATA_BACKUP_DB_REMOVE_LOCAL       : self.backup_db_remove_local,
         DATA_BACKUP_DB_LIST_LOCAL         : self.backup_db_list_local,
         DATA_BACKUP_DB_RESTORE_LOCAL      : self.backup_db_restore_local,
         DATA_UPDATE                 	  : self.update
     };
Example #16
0
def master_conf_initdb():
    file = DaemonConfigParser(MASTER_CONF_FILE_TO);
    
    #mysql password
    password = ''.join(random.choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for _ in range(128))
    password = sha1(password.encode('utf-8'))
    file.writeValueFromSection('mysql', 'password', password.hexdigest());
    os.system('sed -i "s/define(\'DB_PASSWORD\', \'domoleaf\')/define(\'DB_PASSWORD\', \''+password.hexdigest()+'\')/g" /etc/domoleaf/www/config.php')
    
    #mysql user
    query1 = 'DELETE FROM user WHERE User="******"';
    query2 = 'DELETE FROM db WHERE User="******"';
    query3 = 'INSERT INTO user (Host, User, Password) VALUES (\'%\', \'domoleaf\', PASSWORD(\''+password.hexdigest()+'\'));';
    query4 = 'INSERT INTO db (Host, Db, User, Select_priv, Insert_priv, Update_priv, Delete_priv, Create_priv, Drop_priv, Grant_priv, References_priv, Index_priv, Alter_priv, Create_tmp_table_priv, Lock_tables_priv, Create_view_priv, Show_view_priv, Create_routine_priv, Alter_routine_priv, Execute_priv, Event_priv, Trigger_priv) VALUES ("%","domoleaf","domoleaf","Y","Y","Y","Y","Y","Y","Y","Y","Y","Y","Y","Y","Y","Y","Y","Y","Y","Y","Y");';
    query5 = 'FLUSH PRIVILEGES';
    
    Popen(['mysql', '--defaults-file=/etc/mysql/debian.cnf', 'mysql', '-e', query1], stdin=PIPE, stdout=PIPE, stderr=PIPE, bufsize=-1);
    Popen(['mysql', '--defaults-file=/etc/mysql/debian.cnf', 'mysql', '-e', query2], stdin=PIPE, stdout=PIPE, stderr=PIPE, bufsize=-1);
    Popen(['mysql', '--defaults-file=/etc/mysql/debian.cnf', 'mysql', '-e', query3], stdin=PIPE, stdout=PIPE, stderr=PIPE, bufsize=-1);
    Popen(['mysql', '--defaults-file=/etc/mysql/debian.cnf', 'mysql', '-e', query4], stdin=PIPE, stdout=PIPE, stderr=PIPE, bufsize=-1);
    Popen(['mysql', '--defaults-file=/etc/mysql/debian.cnf', 'mysql', '-e', query5], stdin=PIPE, stdout=PIPE, stderr=PIPE, bufsize=-1);
Example #17
0
def slave_conf_init():
    file = DaemonConfigParser(SLAVE_CONF_FILE_TO)

    #KEY
    KEY = ''.join(
        random.choice(string.ascii_uppercase + string.ascii_lowercase +
                      string.digits) for _ in range(128))
    KEY = md5(KEY.encode('utf-8')).hexdigest()
    file.writeValueFromSection('personnal_key', 'aes', KEY)

    #KNX Interface
    knx_edit = 'KNXD_OPTS="-e 1.0.254 -D -T -S -b '
    if os.path.exists('/dev/ttyAMA0'):
        knx_edit = knx_edit + 'tpuarts:/dev/ttyAMA0"'
    elif os.path.exists('/dev/ttyS0'):
        knx_edit = knx_edit + 'tpuarts:/dev/ttyS0"'
    else:
        knx_edit = knx_edit + 'ipt:127.0.0.1"'
    conf_knx = open('/etc/knxd.conf', 'w')
    conf_knx.write(knx_edit + '\n')
    conf_knx.close()
Example #18
0
 def __init__(self, slave_keys):
     ## Logger object for formatting and printing
     self.logger = Logger(DEBUG_MODE, LOG_FILE)
     ## SQL manager for the master daemon
     self.sql = MasterSql()
     self._parser = DaemonConfigParser('/etc/domoleaf/master.conf')
     ## Object containing the AES keys for encrypted communications
     self.aes_slave_keys = slave_keys
     ## Array containing functions associated with IDs
     self.functions_transform = {
         0: utils.convert_none,
         4: utils.eno_onoff
     }
Example #19
0
 def __init__(self, log_flag = True):
     self.logger = Logger(log_flag, '/var/log/domomaster.log');
     self._parser = DaemonConfigParser(MASTER_CONF_FILE);
     self.db_username = self._parser.getValueFromSection(MASTER_CONF_MYSQL_SECTION,
                                                         MASTER_CONF_MYSQL_USER_ENTRY);
     self.db_passwd = self._parser.getValueFromSection(MASTER_CONF_MYSQL_SECTION,
                                                       MASTER_CONF_MYSQL_PASSWORD_ENTRY);
     self.db_dbname = self._parser.getValueFromSection(MASTER_CONF_MYSQL_SECTION,
                                                       MASTER_CONF_MYSQL_DB_NAME_ENTRY);
     if not self.db_username or not self.db_passwd or not self.db_dbname:
         frameinfo = getframeinfo(currentframe());
         self.logger.info("[ MASTER DAEMON " + frameinfo.filaname + ":" + str(frameinfo.lineno) + " ]: initialization error: wrong or missing SQL configuration.");
         sys.exit(1);
Example #20
0
 def __init__(self, log_flag = True):
     self.logger = Logger(log_flag, '/var/log/domoleaf/domomaster.log');
     self._parser = DaemonConfigParser(MASTER_CONF_FILE);
     self.db_username = self._parser.getValueFromSection(MASTER_CONF_MYSQL_SECTION,
                                                         MASTER_CONF_MYSQL_USER_ENTRY);
     self.db_passwd = self._parser.getValueFromSection(MASTER_CONF_MYSQL_SECTION,
                                                       MASTER_CONF_MYSQL_PASSWORD_ENTRY);
     self.db_dbname = self._parser.getValueFromSection(MASTER_CONF_MYSQL_SECTION,
                                                       MASTER_CONF_MYSQL_DB_NAME_ENTRY);
     if not self.db_username or not self.db_passwd or not self.db_dbname:
         frameinfo = getframeinfo(currentframe());
         self.logger.debug("[ MASTER DAEMON "+frameinfo.filaname+":"+str(frameinfo.lineno)+" ]: initialization error: wrong or missing SQL configuration.");
         sys.exit(1);
     self.functions_transform = {
           0: utils.convert_none,
           1: utils.convert_temperature,
           2: utils.convert_hundred,
           3: utils.convert_float32
     };
Example #21
0
 def __init__(self, log_flag):
     self.logger = Logger(log_flag, LOG_FILE);
     self.logger.info('Started Domoleaf Slave daemon');
     print('######## SLAVE DAEMON #######')
     self.connected_masters = {};
     self.connected_knx = [];
     self.connected_enocean = [];
     self.connected_cron = [];
     self.clients = [];
     self._scanner = Scanner();
     self._hostlist = [];
     myhostname = socket.gethostname().upper()
     if SLAVE_NAME_PREFIX in myhostname:
         self._scanner.scan();
         self._hostlist = self._scanner._HostList;
     else:
         self._hostlist.append(Host('', '127.0.0.1', myhostname));
     self._parser = DaemonConfigParser(SLAVE_CONF_FILE);
     self.encrypt_keys = {};
     self.knx_sock = None;
     self.master_sock = None;
     self.enocean_sock = None;
     self.cron_sock = None;
     self.private_aes = self._parser.getValueFromSection('personnal_key', 'aes');
     self.wifi_init(self._parser.getValueFromSection('wifi', 'ssid'), self._parser.getValueFromSection('wifi', 'password'), self._parser.getValueFromSection('wifi', 'encryption'), self._parser.getValueFromSection('wifi', 'mode'), 0);
     self.connect_port = self._parser.getValueFromSection(SLAVE_CONF_CONNECT_SECTION, SLAVE_CONF_CONNECT_PORT_ENTRY);
     self.functions = {
         KNX_READ_REQUEST    : self.knx_read_request,
         KNX_WRITE_SHORT     : self.knx_write_short,
         KNX_WRITE_LONG      : self.knx_write_long,
         KNX_WRITE_TEMP      : self.knx_write_temp,
         CHECK_SLAVE         : self.check_slave,
         MONITOR_IP          : self.monitor_ip,
         DATA_UPDATE         : self.update,
         SEND_TECH           : self.send_tech,
         SEND_ALIVE          : self.send_alive,
         SEND_INTERFACES     : self.send_interfaces,
         SHUTDOWN_D3         : self.shutdown_d3,
         REBOOT_D3           : self.reboot_d3,
         WIFI_UPDATE         : self.wifi_update
     };
Example #22
0
 def __init__(self, slave_keys):
     self.knx_function = {
         OPTION_ON_OFF       : self.send_knx_write_short_to_slave,
         OPTION_VAR          : self.send_knx_write_long_to_slave,
         OPTION_UP_DOWN      : self.send_knx_write_short_to_slave,
         OPTION_OPEN_CLOSE   : self.send_knx_write_short_to_slave,
         OPTION_STOP_UP_DOWN : self.send_knx_write_short_to_slave,
         OPTION_SPEED_FAN_0  : self.send_knx_write_speed_fan,
         OPTION_SPEED_FAN_1  : self.send_knx_write_speed_fan,
         OPTION_SPEED_FAN_2  : self.send_knx_write_speed_fan,
         OPTION_SPEED_FAN_3  : self.send_knx_write_speed_fan,
         OPTION_SPEED_FAN_4  : self.send_knx_write_speed_fan,
         OPTION_SPEED_FAN_5  : self.send_knx_write_speed_fan,
         OPTION_SPEED_FAN_6  : self.send_knx_write_speed_fan,
         OPTION_TEMPERATURE_W: self.send_knx_write_temp,
         OPTION_COLOR_R      : self.send_knx_write_long_to_slave,
         OPTION_COLOR_G      : self.send_knx_write_long_to_slave,
         OPTION_COLOR_B      : self.send_knx_write_long_to_slave,
         OPTION_COLOR_W      : self.send_knx_write_long_to_slave
     };
     self.logger = Logger(True, LOG_FILE);
     self.sql = MasterSql();
     self._parser = DaemonConfigParser('/etc/domoleaf/master.conf');
     self.aes_slave_keys = slave_keys;
Example #23
0
 def __init__(self, log_flag = False):
     ## Logger object for formatting and printing logs
     self.logger = Logger(log_flag, LOG_FILE);
     self._parser = DaemonConfigParser(MASTER_CONF_FILE);
     ## Username for the database, searched in configuration file
     self.db_username = self._parser.getValueFromSection(MASTER_CONF_MYSQL_SECTION,
                                                         MASTER_CONF_MYSQL_USER_ENTRY);
     ## Password for the database, searched in configuration file
     self.db_passwd = self._parser.getValueFromSection(MASTER_CONF_MYSQL_SECTION,
                                                       MASTER_CONF_MYSQL_PASSWORD_ENTRY);
     ## Database name for the database, searched in configuration file
     self.db_dbname = self._parser.getValueFromSection(MASTER_CONF_MYSQL_SECTION,
                                                       MASTER_CONF_MYSQL_DB_NAME_ENTRY);
     if not self.db_username or not self.db_passwd or not self.db_dbname:
         frameinfo = getframeinfo(currentframe());
         self.logger.debug("[ MASTER DAEMON "+frameinfo.filaname+":"+str(frameinfo.lineno)+" ]: initialization error: wrong or missing SQL configuration.");
         sys.exit(1);
     ## Function array with option ID
     self.functions_transform = {
           0: utils.convert_none,
           1: utils.convert_temperature,
           2: utils.convert_hundred,
           3: utils.convert_float32
     };
Example #24
0
def slave_conf_copy():
    file_from = DaemonConfigParser(SLAVE_CONF_FILE_FROM)
    file_to = DaemonConfigParser(SLAVE_CONF_FILE_TO)

    #listen
    var = file_from.getValueFromSection('listen', 'port')
    file_to.writeValueFromSection('listen', 'port', var)

    #connect
    var = file_from.getValueFromSection('connect', 'port')
    file_to.writeValueFromSection('connect', 'port', var)

    #knx
    var = file_from.getValueFromSection('knx', 'port')
    file_to.writeValueFromSection('knx', 'port', var)
    var = file_from.getValueFromSection('knx', 'interface')
    file_to.writeValueFromSection('knx', 'interface', var)
    var = file_from.getValueFromSection('knx', 'activated')
    file_to.writeValueFromSection('knx', 'activated', var)

    #enocean
    var = file_from.getValueFromSection('enocean', 'port')
    file_to.writeValueFromSection('enocean', 'port', var)
    var = file_from.getValueFromSection('enocean', 'interface')
    file_to.writeValueFromSection('enocean', 'interface', var)

    #cron
    var = file_from.getValueFromSection('cron', 'port')
    file_to.writeValueFromSection('cron', 'port', var)
    var = file_from.getValueFromSection('cron', 'address')
    file_to.writeValueFromSection('cron', 'address', var)

    #personnal_key
    var = file_from.getValueFromSection('personnal_key', 'aes')
    file_to.writeValueFromSection('personnal_key', 'aes', var)

    #openvpn
    var = file_from.getValueFromSection('openvpn', 'openvpnserver')
    file_to.writeValueFromSection('openvpn', 'openvpnserver', var)
Example #25
0
class MasterDaemon:
    """
    Main class of the master daemon
    It provides communication between master and slave boxes and a part of the database management
    """
    def __init__(self, log_flag):
        self.logger = Logger(log_flag, LOG_FILE);
        self.logger.info('Started Greenleaf Master Daemon');
        self.d3config = {};
        self.aes_slave_keys = {};
        self.aes_master_key = None;
        self.connected_clients = {};
        self.sql = MasterSql();
        self._parser = DaemonConfigParser(MASTER_CONF_FILE);
        self.get_aes_slave_keys();
        self.reload_camera(None, None);
        self.scanner = Scanner(HOSTS_CONF);
        self.scanner.scan(False);
        self.hostlist = self.scanner._HostList;
        self.sql.insert_hostlist_in_db(self.scanner._HostList);
        self.knx_manager = KNXManager(self.aes_slave_keys);
        self.reload_d3config(None, None);
        self.protocol_function = {
            PROTOCOL_KNX        : KNXManager.protocol_knx,
            PROTOCOL_ENOCEAN    : self.protocol_enocean,
            PROTOCOL_IP         : self.protocol_ip
        };
        self.upnp_function = {
            UPNP_PLAY           : self.upnp_set_play,
            UPNP_PAUSE          : self.upnp_set_pause,
            UPNP_NEXT           : self.upnp_set_next,
            UPNP_PREVIOUS       : self.upnp_set_prev,
            UPNP_STOP           : self.upnp_set_stop,
            UPNP_VOLUME_UP      : self.upnp_set_volume_up,
            UPNP_VOLUME_DOWN    : self.upnp_set_volume_down,
            UPNP_SET_VOLUME     : self.upnp_set_volume
        };
        self.enocean_function = {};
        self.data_function = {
            DATA_MONITOR_KNX            	  : self.monitor_knx,
            DATA_MONITOR_IP             	  : self.monitor_ip,
            DATA_MONITOR_ENOCEAN        	  : self.monitor_enocean,
            DATA_MONITOR_BLUETOOTH      	  : self.monitor_bluetooth,
            DATA_KNX_READ               	  : self.knx_read,
            DATA_KNX_WRITE_S            	  : self.knx_write_short,
            DATA_KNX_WRITE_L            	  : self.knx_write_long,
            DATA_SEND_TO_DEVICE         	  : self.send_to_device,
            DATA_CRON_UPNP             		  : self.cron_upnp,
            DATA_SEND_MAIL              	  : self.send_mail,
            DATA_CHECK_SLAVE            	  : self.check_slave,
            DATA_RELOAD_CAMERA          	  : self.reload_camera,
            DATA_RELOAD_D3CONFIG        	  : self.reload_d3config,
            DATA_BACKUP_DB_CREATE_LOCAL       : self.backup_db_create_local,
            DATA_BACKUP_DB_REMOVE_LOCAL       : self.backup_db_remove_local,
            DATA_BACKUP_DB_LIST_LOCAL         : self.backup_db_list_local,
            DATA_BACKUP_DB_RESTORE_LOCAL      : self.backup_db_restore_local,
            DATA_UPDATE                 	  : self.update
        };

    def get_aes_slave_keys(self):
        """
        Get the secretkeys of each slave daemon stored in database
        """
        query = "SELECT serial, secretkey FROM daemon";
        res = self.sql.mysql_handler_personnal_query(query);
        self_hostname = socket.gethostname();
        for r in res:
            if SLAVE_NAME_PREFIX in r[0] or 'MD3' in r[0]:
                self.aes_slave_keys[r[0]] = r[1];
            elif self_hostname == r[0]:
                self.aes_slave_keys[r[0]] = r[1];
                self.aes_master_key = r[1];
        print(self.aes_slave_keys)

    def stop(self):
        """
        Stops the daemon and closes sockets
        """
        flag = False;
        while not flag:
            flag = True;
            for client in self.connected_clients.values():
                flag = False;
                client.close();
                break;
        self.slave_connection.close();
        sys.exit(0);

    def run(self):
        """
        Initialization of the connections and accepting incomming communications
        """
        self.slave_connection = socket.socket(socket.AF_INET, socket.SOCK_STREAM);
        self.cmd_connection = socket.socket(socket.AF_INET, socket.SOCK_STREAM);
        self.slave_connection.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1);
        self.cmd_connection.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1);
        s_port = self._parser.getValueFromSection(MASTER_CONF_LISTEN_SECTION, MASTER_CONF_LISTEN_PORT_SLAVE_ENTRY);
        c_port = self._parser.getValueFromSection(MASTER_CONF_LISTEN_SECTION, MASTER_CONF_LISTEN_PORT_CMD_ENTRY);
        if not s_port:
            frameinfo = getframeinfo(currentframe());
            self.logger.error('in run: No slave listening port defined in ' + MASTER_CONF_FILE);
            sys.exit(1);
        if not c_port:
            frameinfo = getframeinfo(currentframe());
            self.logger.error('in run: No command listening port defined in ' + MASTER_CONF_FILE);
            sys.exit(1);
        self.slave_connection.bind(('', int(s_port)));
        self.slave_connection.listen(MAX_SLAVES);
        self.cmd_connection.bind(('', int(c_port)));
        self.cmd_connection.listen(MAX_CMDS);
        self.loop();

    def loop(self):
        """
        Main loop. Waits for new connections.
        """
        self.run = True;
        while self.run:
            try:
                rlist, wlist, elist = select.select([self.slave_connection], [], [], SELECT_TIMEOUT);
                for connection in rlist:
                    self.accept_new_slave_connection(connection);
                rlist, wlist, elist = select.select([self.cmd_connection], [], [], SELECT_TIMEOUT);
                for connection in rlist:
                    self.accept_new_cmd_connection(connection);
            except KeyboardInterrupt as e:
                frameinfo = getframeinfo(currentframe());
                self.logger.info('in loop: Keyboard interrupt: leaving program');
                print("[ MASTER DAEMON " + frameinfo.filename + ":" + str(frameinfo.lineno) + " ]: Keyboard Interrupt");
                self.stop();
                sys.exit(0);
            except ValueError as e:
                frameinfo = getframeinfo(currentframe());
                self.logger.error('in loop: Value error: ' + str(e));
                print("[ MASTER DAEMON " + frameinfo.filename + ":" + str(frameinfo.lineno) + "]: Value Error");
                print(e);
                pass;

    def accept_new_cmd_connection(self, connection):
        """
        Gets new mastercommand connections and threads the treatment.
        """
        new_connection, addr = connection.accept();
        r = CommandReceiver(new_connection, self);
        r.start();

    def accept_new_slave_connection(self, connection):
        """
        Gets new slave connections and threads the treatment.
        """
        new_connection, addr = connection.accept();
        for host in self.hostlist:
            if addr[0] == host._IpAddr:
                hostname = host._Hostname.split('.')[0];
                r = SlaveReceiver(new_connection, hostname, self);
                r.start();

    def parse_data(self, data, connection):
        """
        Once data are received whether from mastercommand or slave, the function of the packet_type in data is called.
        """
        json_obj = json.JSONDecoder().decode(data);
        if json_obj['packet_type'] in self.data_function.keys():
            self.data_function[json_obj['packet_type']](json_obj, connection);
        else:
            frameinfo = getframeinfo(currentframe());

    def update(self, json_obj, connection):
        call(['apt-get', 'update']);
        call(['apt-get', 'install', 'glmaster', 'glslave', '-y']);
        hostname = socket.gethostname();
        if '.' in hostname:
            hostname = hostname.split('.')[0];
        version_file = open('/etc/greenleaf/.glmaster.version', 'r');
        if not version_file:
            self.logger.error("File not found: /etc/greenleaf/.glmaster.version");
            print("File not found: /etc/greenleaf/.glmaster.version");
            return;
        version = version_file.read();
        if '\n' in version:
            version = version.split('\n')[0];
        query = 'UPDATE daemon SET version="' + version + '" WHERE name="' + hostname + '"';
        self.sql.mysql_handler_personnal_query(query);
        query = 'UPDATE configuration SET configuration_value="' + version + '" WHERE configuration_id=4';
        self.sql.mysql_handler_personnal_query(query);
        json_obj['data'].append(hostname);
        port = self._parser.getValueFromSection('connect', 'port');
        for host in self.hostlist:
            sock = socket.create_connection((host._IpAddr, port));
            if host._Hostname.startswith('MD3') or host._Hostname.startswith('SD3') and host._Hostname not in json_obj['data']:
                json_str = json.JSONEncoder().encode(json_obj);
                sock.send(bytes(json_str, 'utf-8'));
                data = sock.recv(4096);
                decrypt_IV = data[:16].decode();
                decode_obj = AES.new(self.aes_master_key, AES.MODE_CBC, decrypt_IV);
                data2 = decode_obj.decrypt(data[16:]).decode();
                version = data2['new_version'];
                query = 'UPDATE daemon SET version="' + version + '" WHERE name="' + host._Hostname + '"';
                self.sql.mysql_handler_personnal_query(query);

    def backup_db_create_local(self, json_obj, connection):
        filename = '/etc/greenleaf/sql/backup/mastercommand_backup_';
        t = str(time.time());
        if '.' in t:
            t = t.split('.')[0];
        filename += t;
        filename += '.sql';
        os.popen("mysqldump --defaults-file=/etc/mysql/debian.cnf mastercommand > " + filename);

    def backup_db_remove_local(self, json_obj, connection):
        filename = '/etc/greenleaf/sql/backup/';
        filename += json_obj;
        filename += '.sql';
        if json_obj[0] == '.' or json_obj[0] == '/':
            print('The filename is corrupted. Aborting database file removing.')
            return;
        try:
            os.stat(filename);
        except Exception as e:
            print("The database file to remove does not exists.")
            print(e)
            return;
        os.remove(filename);

    def backup_db_list_local(self, json_obj, connection):
        json_obj = [];
        backup_list = os.listdir('/etc/greenleaf/sql/backup/')
        for f in backup_list:
            s = os.stat('/etc/greenleaf/sql/backup/' + f);
            if '.sql' in f:
                f = f.split('.sql')[0];
				json_obj.append({"name": f, "size": s.st_size});
		json_sorted = sorted(json_obj, key=lambda json_obj: json_obj['name']);
		json_str = json.JSONEncoder().encode(json_sorted);
		connection.send(bytes(json_str, 'utf-8'));
Example #26
0
## @package domomaster
# Master daemon for D3 boxes.
#
# Developed by GreenLeaf.

import sys
sys.path.append('/usr/lib/domoleaf')
import socket
import json
from DaemonConfigParser import *

## Script sending the command to open or close the UPnP to the master.
if __name__ == "__main__":
    try:
        parser = DaemonConfigParser('/etc/domoleaf/master.conf')
        ip = '127.0.0.1'
        port = parser.getValueFromSection('listen', 'port_cmd')
        s = socket.create_connection((ip, port))
        obj = {
            "packet_type":
            "cron_upnp",
            "data": [{
                "action": "open",
                "configuration_id": 1,
                "protocol": "TCP"
            }, {
                "action": "close",
                "configuration_id": 2,
                "protocol": "TCP"
            }]
Example #27
0
#!/usr/bin/python3

import sys
sys.path.append('/usr/lib/domoleaf')
import socket
import json
from DaemonConfigParser import *;

if __name__ == "__main__":
    try:
        parser = DaemonConfigParser('/etc/domoleaf/master.conf')
        ip = '127.0.0.1'
        port = parser.getValueFromSection('listen', 'port_cmd')
        s = socket.create_connection((ip, port))
        obj = {
            "packet_type": "check_all_schedules",
            "data" : ""
        }
        obj_str = json.JSONEncoder().encode(obj)
        s.send(obj_str.encode())
        s.close()
    except Exception as e:
        print(str(e))
Example #28
0
class MasterSql:

    ## The constructor
    #
    # @param log_flag Flag saying if the logs are active or not.
    def __init__(self, log_flag=False):
        ## Logger object for formatting and printing logs
        self.logger = Logger(log_flag, LOG_FILE)
        self._parser = DaemonConfigParser(MASTER_CONF_FILE)
        ## Username for the database, searched in configuration file
        self.db_username = self._parser.getValueFromSection(
            MASTER_CONF_MYSQL_SECTION, MASTER_CONF_MYSQL_USER_ENTRY)
        ## Password for the database, searched in configuration file
        self.db_passwd = self._parser.getValueFromSection(
            MASTER_CONF_MYSQL_SECTION, MASTER_CONF_MYSQL_PASSWORD_ENTRY)
        ## Database name for the database, searched in configuration file
        self.db_dbname = self._parser.getValueFromSection(
            MASTER_CONF_MYSQL_SECTION, MASTER_CONF_MYSQL_DB_NAME_ENTRY)
        if not self.db_username or not self.db_passwd or not self.db_dbname:
            frameinfo = getframeinfo(currentframe())
            self.logger.debug(
                "[ MASTER DAEMON " + frameinfo.filaname + ":" +
                str(frameinfo.lineno) +
                " ]: initialization error: wrong or missing SQL configuration."
            )
            sys.exit(1)
        ## Function array with option ID
        self.functions_transform = {
            0: utils.convert_none,
            1: utils.convert_temperature,
            2: utils.convert_hundred,
            3: utils.convert_float32
        }

    ## Updates the table containing the hosts.
    # Inserts each host in "hostlist".
    #
    # @param hostlist The list of all the hosts connected on the local network.
    # @param db The database handler used for queries.
    # @return None
    def insert_hostlist_in_db(self, hostlist, db):
        for host in hostlist:
            db.update_datas_in_table(
                'ip_monitor', {"mac_addr": host._MacAddr}, {
                    "last_update": str(time.time()).split('.')[0],
                    "ip_addr": host._IpAddr,
                    "hostname": host._Hostname.split('.')[0]
                })
        db.personnal_query("DELETE FROM ip_monitor WHERE last_update<" +
                           str(time.time() - 7200).split('.')[0])
        db.updatedb()
        query = ''.join([
            "UPDATE room_device JOIN ip_monitor ON plus4=mac_addr SET addr=ip_addr WHERE protocol_id=6 AND plus4 != '' AND ip_addr != addr"
        ])
        self.mysql_handler_personnal_query(query, db)
        db.updatedb()

    ## Updates the enocean log table.
    #
    # @param json_obj JSON object containing the values to update.
    # @param db The database handler used for queries.
    #
    # @return The daemon_id.
    def update_enocean_log(self, json_obj, db):
        daemon_id = 0
        daemons = db.get_datas_from_table_with_names(
            'daemon', ['daemon_id', 'name', 'serial', 'secretkey'])
        for d in daemons:
            if json_obj['sender_name'] == d[2]:
                daemon_id = d[0]
                break
        db.insert_datas_in_table('enocean_log', [
            'type', 'addr_src', 'addr_dest', 'eo_value', 't_date', 'daemon_id'
        ], (json_obj['type'], json_obj['src_addr'], json_obj['dst_addr'],
            json_obj['value'], json_obj['date'], daemon_id))
        db.updatedb()
        return daemon_id

    ## Updates the KNX log table.
    #
    # @param json_obj JSON object containing the values to update.
    # @param db The database handler used for queries.
    #
    # @return The daemon_id.
    def update_knx_log(self, json_obj, db):
        daemon_id = 0
        daemons = db.get_datas_from_table_with_names(
            'daemon', ['daemon_id', 'name', 'serial', 'secretkey'])
        for d in daemons:
            if json_obj['sender_name'] == d[2]:
                daemon_id = d[0]
                break
        db.insert_datas_in_table('knx_log', [
            "type", "addr_src", "addr_dest", "knx_value", "t_date", "daemon_id"
        ], (json_obj['type'], json_obj['src_addr'], json_obj['dst_addr'],
            json_obj['value'], json_obj['date'], daemon_id))
        db.updatedb()
        return daemon_id

    ## Updates the table room_device_option with long KNX values.
    #
    # @param json_obj JSON object containing some data such as values, addr...
    # @param daemon_id The ID of the slave daemon to who send the packet.
    # @param db The database handler used for queries.
    #
    # @return The result of the query.
    def update_room_device_option_write_long(self, json_obj, daemon_id, db):
        query = ''.join([
            "SELECT room_device_option.option_id, room_device.room_device_id, function_answer, dpt_optiondef.dpt_id FROM room_device_option JOIN room_device ON room_device_option.room_device_id=room_device.room_device_id JOIN dpt_optiondef ON dpt_optiondef.option_id=room_device_option.option_id AND dpt_optiondef.protocol_id=room_device.protocol_id AND dpt_optiondef.dpt_id=room_device_option.dpt_id WHERE daemon_id=",
            str(daemon_id), " AND room_device_option.addr=\"",
            str(json_obj['dst_addr']), "\""
        ])
        res = self.mysql_handler_personnal_query(query, db)
        if not res:
            query = ''.join([
                "SELECT room_device_option.option_id, room_device.room_device_id, function_answer, dpt_optiondef.dpt_id FROM room_device_option JOIN room_device ON room_device_option.room_device_id=room_device.room_device_id JOIN dpt_optiondef ON dpt_optiondef.option_id=room_device_option.option_id AND dpt_optiondef.protocol_id=room_device.protocol_id AND dpt_optiondef.dpt_id=room_device_option.dpt_id WHERE ",
                str(daemon_id), " AND room_device_option.addr_plus=\"",
                str(json_obj['dst_addr']), "\""
            ])
            res = self.mysql_handler_personnal_query(query, db)
        for r in res:
            if int(r[0]) == 13:
                if not json_obj['value']:
                    up = 'UPDATE room_device_option SET opt_value=0 WHERE room_device_id=' + str(
                        r[1]) + ' AND option_id=12'
                else:
                    up = 'UPDATE room_device_option SET opt_value=1 WHERE room_device_id=' + str(
                        r[1]) + ' AND option_id=12'
                self.logger.debug(
                    'update_room_device_option write_long: up = ' + up)
                self.mysql_handler_personnal_query(up, db)
                query = ''.join([
                    "UPDATE room_device_option SET opt_value=\"",
                    str(json_obj['value']), "\" WHERE room_device_id=",
                    str(r[1]), " AND option_id=",
                    str(r[0])
                ])
                self.mysql_handler_personnal_query(query, db)
            elif int(r[0]) == 72 or int(r[0]) == 388:
                val = int(json_obj['value'])
                res = utils.convert_temperature(val)
                query = ''.join([
                    "UPDATE room_device_option JOIN room_device ON room_device_option.room_device_id=room_device.room_device_id SET opt_value=\"",
                    str(res), "\" WHERE daemon_id=",
                    str(daemon_id), " AND room_device_option.addr=\"",
                    str(json_obj['dst_addr']), "\"", " OR ",
                    "room_device_option.addr_plus=\"",
                    str(json_obj['dst_addr']), "\""
                ])
                self.logger.debug(
                    'update_room_device_option write_long: query = ' + query)
                self.mysql_handler_personnal_query(query, db)
            else:
                val = self.functions_transform[r[2]](int(json_obj['value']))
                up = ''.join([
                    "UPDATE room_device_option SET opt_value=\"",
                    str(val), "\" WHERE room_device_id=",
                    str(r[1]), " AND option_id=\"",
                    str(r[0]), "\""
                ])
                self.logger.debug(
                    'update_room_device_option write_long: up = ' + up)
                self.mysql_handler_personnal_query(up, db)
        return res

    ## Updates the table room_device_option with resp KNX values.
    #
    # @param json_obj JSON object containing data to update.
    # @param daemon_id The ID of the slave daemon to who send the packet.
    # @param db The database handler used for queries.
    #
    # @return The result of the query.
    def update_room_device_option_resp(self, json_obj, daemon_id, db):
        query = ''.join([
            "SELECT option_id, room_device.room_device_id, function_answer FROM ",
            "room_device_option JOIN room_device ON ",
            "room_device_option.room_device_id=room_device.room_device_id ",
            "JOIN dpt_optiondef ON dpt_optiondef.option_id=room_device_option.option_id AND ",
            "dpt_optiondef.protocol_id=room_device.protocol_id AND dpt_optiondef.dpt_id=room_device_option.dpt_id ",
            "WHERE daemon_id=",
            str(daemon_id), " AND room_device_option.addr=\"",
            str(json_obj['dst_addr']), "\""
        ])
        res = self.mysql_handler_personnal_query(query, db)
        for r in res:
            val = self.functions_transform[r[2]](int(json_obj['value']))
            query = ''.join([
                "UPDATE room_device_option JOIN room_device ON ",
                "room_device_option.room_device_id=room_device.room_device_id SET ",
                "opt_value=\"",
                str(val), "\" WHERE daemon_id=",
                str(daemon_id), " AND room_device_option.addr=\"",
                str(json_obj['dst_addr']), "\""
            ])
            self.logger.debug("update_room_device_option resp query = " +
                              query)
            self.mysql_handler_personnal_query(query, db)
        return res

    ## Updates the table room_device_option with short KNX values.
    #
    # @param json_obj JSON object containing data to update.
    # @param daemon_id The ID of the daemon to who send the packet.
    # @param db The database handler used for queries.
    #
    # @return The result of the query.
    def update_room_device_option_write_short(self, json_obj, daemon_id, db):
        query = ''.join([
            "SELECT option_id, room_device.room_device_id FROM ",
            "room_device_option JOIN room_device ON ",
            "room_device_option.room_device_id=room_device.room_device_id WHERE ",
            "daemon_id=",
            str(daemon_id), " AND room_device_option.addr=\"",
            str(json_obj['dst_addr']), "\""
        ])
        #self.logger.debug("update_room_device_option write_short query : " + query);
        res = self.mysql_handler_personnal_query(query, db)
        if not res:
            query = ''.join([
                "SELECT option_id, room_device.room_device_id FROM ",
                "room_device_option JOIN room_device ON ",
                "room_device_option.room_device_id=room_device.room_device_id WHERE ",
                "daemon_id=",
                str(daemon_id), " AND room_device_option.addr_plus=\"",
                str(json_obj['dst_addr']), "\""
            ])
            #self.logger.debug("update_room_device_option write_short query : " + query);
            res = self.mysql_handler_personnal_query(query, db)
        for r in res:
            if (int(r[0]) == MasterDaemon.OPTION_ON_OFF
                    or int(r[0]) == MasterDaemon.OPTION_UP_DOWN
                    or int(r[0]) == MasterDaemon.OPTION_OPEN_CLOSE):
                up = 'UPDATE room_device_option SET opt_value='
                if not json_obj['value']:
                    up += '0'
                else:
                    up += '255'
                up += ' WHERE room_device_id=' + str(
                    r[1]) + " AND option_id=13"
                self.logger.debug(
                    "update_room_device_option write_short up1: " + up)
                self.mysql_handler_personnal_query(up, db)
            up = ''.join([
                "UPDATE room_device_option SET opt_value=",
                str(json_obj['value']), " WHERE room_device_id=",
                str(r[1]), " AND option_id=",
                str(r[0]), ""
            ])
            self.logger.debug("update_room_device_option write_short up2: " +
                              up)
            self.mysql_handler_personnal_query(up, db)
        return res

    ## Sends a personnal query to the database.
    #
    # @param query The query to send to the database.
    # @param db The database handler used for queries.
    # @return Result of the query.
    def mysql_handler_personnal_query(self, query, db=0):
        tmp = db
        if not tmp:
            db = MysqlHandler(self.db_username, self.db_passwd, self.db_dbname)
        res = db.personnal_query(query)
        db.updatedb()
        if not tmp:
            db.close()
        return res

    ## Retrieves each daemon stored in the database.
    #
    # @param db The database handler used for queries.
    #
    # @return An array with all the daemons found.
    def get_daemons(self, db):
        daemons = db.get_datas_from_table_with_names(
            'daemon', ['daemon_id', 'name', 'serial', 'secretkey'])
        return daemons
Example #29
0
class DeviceManager:
    """
    Device management class. Manages the device described with the ID passed at construction.
    """
    def __init__(self, _id_elem = 0, _option_id = 0, _debug = False):
        self.logger = Logger(LOG_FLAG, LOG_FILE);
        self._id = _id_elem;
        self._debug = _debug;
        self._option_id = _option_id;
        self._parser = DaemonConfigParser(CONF_FILENAME);
        self._db_name = self._parser.getValueFromSection(MYSQL_CONF_SECTION, MYSQL_CONF_USER_ENTRY);
        self._db_passwd = self._parser.getValueFromSection(MYSQL_CONF_SECTION, MYSQL_CONF_PASSWORD_ENTRY);
        self._db_dbname = self._parser.getValueFromSection(MYSQL_CONF_SECTION, MYSQL_CONF_DATABASE_ENTRY);
        self.sql = MasterSql();
        
    ###############################################################
    # Va surement revoir cette fonction et remettre le mode debug #
    # suivant les options qu'il va falloir check ou pas           #
    ###############################################################
    def load_from_db(self):
        """
        Returns the device from the database.
        """
        if self._db_name is None:
            self.logger.error("[ DeviceManager ]: Mysql username not found in '" + CONF_FILENAME + "'");
            return None;
        if self._db_passwd is None:
            self.logger.error("[ DeviceManager ]: Mysql password not found in '" + CONF_FILENAME + "'");
            return None;
        if self._db_dbname is None:
            self.logger.error("[ DeviceManager ]: Mysql database name not found in '" + CONF_FILENAME + "'");
            return None;
        
        db = MysqlHandler(self._db_name, self._db_passwd, self._db_dbname);
        res = db.personnal_query(LOAD_DEVICE_QUERY + str(self._id));
        if len(res) == 0:
            res = db.personnal_query(LOAD_DEVICE_QUERY_IP + str(self._id));
        if len(res) == 0:
            self.logger.error('[ DeviceManager ]: Error: No device with id ' + str(self._id) + ' in database.');
            return None;
        elif len(res) > 1:
            self.logger.error('[ DeviceManager ]: Dunno wut to do if more than one item in DB.');
            return None;
        obj = res[0];
        device = {
            KEY_PROTOCOL_ID: obj[0],
            KEY_DEVICE_ID: obj[1],
            KEY_DAEMON_ID: obj[2],
            KEY_ADDR: obj[3],
            KEY_PLUS_1: obj[4],
            KEY_PLUS_2: obj[5],
            KEY_PLUS_3: obj[6],
            KEY_ROOM_DEVICE_ID: self._id
        };
        db.close();
        db = MysqlHandler(self._db_name, self._db_passwd, self._db_dbname);
        query = CHECK_ROOM_DEVICE_OPTIONS + str(device[KEY_PROTOCOL_ID]) + ' WHERE room_device_id = ' + str(self._id);
        res = db.personnal_query(query);
        db.close();
        if len(res) == 0:
            self.logger.error('[ DeviceManager ]: Error: No room_device_option for room_device_id \'' + str(self._id) + '\'');
            device['option_id'] = self._option_id;
            device['function_id'] = 0;
            device['dpt_id'] = 0;
            if device['protocol_id'] != IP_ID:
                db = MysqlHandler(self._db_name, self._db_passwd, self._db_dbname);
                res = db.personnal_query(GET_DAEMON_FROM_ID + str(device['daemon_id']));
                device['daemon_name'] = res[0][2];
                device['daemon_secretkey'] = res[0][3];
            db.close();
            return device;
        device['addr_dst'] = 0;

        for d in res:
            if d[0] == self._option_id:
                device['addr_dst'] = d[1];
                device['function_id'] = d[3];
                device['dpt_id'] = d[2];
                break;
        device['option_id'] = self._option_id;
        if device['protocol_id'] != IP_ID:
            db = MysqlHandler(self._db_name, self._db_passwd, self._db_dbname);
            res = db.personnal_query(GET_DAEMON_FROM_ID + str(device['daemon_id']));
            device['daemon_name'] = res[0][2];
            device['daemon_secretkey'] = res[0][3];
        db.close();
        return device;
Example #30
0
class KNXManager:

    ## The constructor.
    #
    # @param slave_keys Array containing the AES keys of all the slaves.
    # @return None
    def __init__(self, slave_keys):
        ## Logger object for formatting and printing logs
        self.logger = Logger(DEBUG_MODE, LOG_FILE);
        ## SQL object for managing database
        self.sql = MasterSql();
        self._parser = DaemonConfigParser('/etc/domoleaf/master.conf');
        ## Object containing AES keys for encrypted communications
        self.aes_slave_keys = slave_keys;

    ## Updates room_device_option table in database to set new values of the device described by json_obj.
    #
    # @param daemon_id The ID of a slave daemon.
    # @param json_obj JSON object containing the values to update.
    # @param db The database handler.
    #
    # @return The result of the query.
    def update_room_device_option(self, daemon_id, json_obj, db):
        if int(json_obj['type']) == KNX_RESPONSE:
            return self.sql.update_room_device_option_resp(json_obj, daemon_id, db);
        elif int(json_obj['type']) == KNX_WRITE_SHORT:
            return self.sql.update_room_device_option_write_short(json_obj, daemon_id, db);
        elif int(json_obj['type']) == KNX_WRITE_LONG:
            return self.sql.update_room_device_option_write_long(json_obj, daemon_id, db);

    ## Sends a JSON object to a slave daemon.
    #
    # @param json_str The data to send.
    # @param sock The socket used to send the data.
    # @param hostname The hostname of the slave daemon.
    # @param aes_key The AES key of the slave daemon to encrypt data.
    # @return None
    def send_json_obj_to_slave(self, json_str, sock, hostname, aes_key):
        hostname_key = '';
        if '.' in hostname:
            hostname_key = hostname.split('.')[0];
        else:
            hostname_key = hostname;
        AES.key_size = 32;
        aes_IV = AESManager.get_IV();
        encode_obj = AES.new(aes_key, AES.MODE_CBC, aes_IV);
        spaces = 16 - len(json_str) % 16;
        data2 = encode_obj.encrypt(json_str + (spaces * ' '));
        sock.send(bytes(aes_IV, 'utf-8') + data2);

    ## Changes the speed value of a fan.
    #
    # @param json_obj JSON object containing the values to write.
    # @param dev Object containing informations about the device.
    # @param hostname The hostname of the slave to who send the packet.
    # @return None
    def send_knx_write_speed_fan(self, json_obj, dev, hostname):
        port = self._parser.getValueFromSection('connect', 'port');
        sock = socket.create_connection((hostname, port));
        if not port:
            sys.exit(4);
        if json_obj['data']['value'] == '1':
            query = ''.join(['SELECT option_id, addr, dpt_id FROM room_device_option WHERE room_device_id=',
                           str(dev['room_device_id']),
                           ' AND option_id IN(400, 401, 402, 403, 404, 405, 406) AND status=1']);
            res = self.sql.mysql_handler_personnal_query(query);
            for line in res:
                if str(line[2]) == "51" and str(line[0]) == str(json_obj['data']['option_id']):
                    val = str(line[0]).split('40')[1];
                    json_str = json.JSONEncoder().encode(
                        {
                            "packet_type": "knx_write_long",
                            "addr_to_send": line[1],
                            "value": val
                        }
                    );
                    self.send_json_obj_to_slave(json_str, sock, hostname, self.aes_slave_keys[hostname]);
                    return;
                elif str(line[2]) == "2" and str(line[0]) != str(json_obj['data']['option_id']):
                    json_str = json.JSONEncoder().encode(
                        {
                            "packet_type": "knx_write_short",
                            "addr_to_send": line[1],
                            "value": "0"
                        }
                    );
                    self.send_json_obj_to_slave(json_str, sock, hostname, self.aes_slave_keys[hostname]);
        json_str = json.JSONEncoder().encode(
            {
                "packet_type": "knx_write_short",
                "addr_to_send": str(dev['addr_dst']),
                "value": json_obj['data']['value']
            }
        );
        self.send_json_obj_to_slave(json_str, sock, hostname, self.aes_slave_keys[hostname]);
        sock.close();

    ## Converts absolute value of temperature (Celsius) in 2 hexadecimal values for sending to KNX device.
    #
    # @param json_obj JSON object containing the values to write.
    # @param dev Object describing the KNX device to who send the packet.
    # @param hostname The hostname of the slave daemon to who send the packet.
    # @return None
    def send_knx_write_temp(self, json_obj, dev, hostname):
        port = self._parser.getValueFromSection('connect', 'port');
        if not port:
            sys.exit(4);
        sock = socket.create_connection((hostname, port));
        val_str = json_obj['data']['value'];
        if ',' in val_str:
            val_str = val_str.replace(',', '.')
        value = utils.convert_temperature_reverse(float(val_str));
        json_str = json.JSONEncoder().encode(
            {
                "packet_type": "knx_write_temp",
                "addr_to_send": str(dev['addr_dst']),
                "value": value[0] + ' ' + value[1]
            }
        );
        self.send_json_obj_to_slave(json_str, sock, hostname, self.aes_slave_keys[hostname]);
        sock.close();

    ## Builds a "long write" request and sends it to "hostname".
    #
    # @param json_obj JSON object containing the new values.
    # @param dev The device to who send the request.
    # @param hostname The slave daemon to who send the packet.
    # @return None
    def send_knx_write_long_to_slave(self, json_obj, dev, hostname):
        port = self._parser.getValueFromSection('connect', 'port');
        if not port:
            sys.exit(4);
        sock = socket.create_connection((hostname, port));
        json_str = json.JSONEncoder().encode(
            {
                "packet_type": "knx_write_long",
                "addr_to_send": str(dev['addr_dst']),
                "value": hex(int(json_obj['data']['value']))
            }
        );
        self.send_json_obj_to_slave(json_str, sock, hostname, self.aes_slave_keys[hostname]);
        sock.close();

    ## Builds a "short read" request and sends it to "hostname".
    #
    # @param json_obj JSON object containing the new values.
    # @param dev The device to who send the request.
    # @param hostname The hostname of the slave daemon to who send the packet.
    # @return None
    def send_knx_write_short_to_slave(self, json_obj, dev, hostname):
        port = self._parser.getValueFromSection('connect', 'port');
        if not port:
            sys.exit(4);
        sock = socket.create_connection((hostname, port));
        json_str = json.JSONEncoder().encode(
            {
                "packet_type": "knx_write_short",
                "addr_to_send": str(dev['addr_dst']),
                "value": json_obj['data']['value']
            }
        );
        self.send_json_obj_to_slave(json_str, sock, hostname, self.aes_slave_keys[hostname]);
        sock.close();

    ## Changes the value to send when it is supposed to be inverted and sends the packet to the slave.
    #
    # @param json_obj JSON object to change before sending.
    # @param dev The device to who send the request.
    # @param hostname The hostname of the slave daemon to who send the packet.
    # @return None.
    def send_knx_write_short_to_slave_reverse(self, json_obj, dev, hostname):
        json_obj['data']['value'] = (int(json_obj['data']['value']) + 1) % 2;
        self.send_knx_write_short_to_slave(json_obj, dev, hostname);
        
    ## Builds a "short read" request and sends it to the slave "hostname".
    #
    # @param hostname The slave daemon to who send the read request.
    # @param json_obj JSON object containing the data to send.
    # @return None
    def send_knx_read_request_to_slave(self, hostname, json_obj):
        port = self._parser.getValueFromSection('connect', 'port');
        if not port:
            sys.exit(4);
        sock = socket.create_connection((hostname, port));
        json_str = json.JSONEncoder().encode(
            {
                "packet_type": "knx_read_request",
                "addr_to_read": json_obj['data']['addr']
            }
        );
        self.send_json_obj_to_slave(json_str, sock, hostname, self.aes_slave_keys[hostname]);
        sock.close();

    ## Asks to send a "on" packet to a device.
    #
    # @param json_obj JSON object containing the new values.
    # @param dev The KNX device.
    # @param hostname The hostname of the slave daemon.
    # @return None
    def send_on(self, json_obj, dev, hostname):
        port = self._parser.getValueFromSection('connect', 'port');
        if not port:
            sys.exit(4);
        sock = socket.create_connection((hostname, port));
        json_str = json.JSONEncoder().encode(
            {
                "packet_type": "knx_write_short",
                "addr_to_send": str(dev['addr_dst']),
                "value": "1"
            }
        );
        self.send_json_obj_to_slave(json_str, sock, hostname, self.aes_slave_keys[hostname]);
        sock.close();
        return;

    ## Asks to send a "off" packet to a device.
    #
    # @param json_obj JSON object containing the new values.
    # @param dev The KNX device.
    # @param hostname The hostname of the slave daemon.
    # @return None
    def send_off(self, json_obj, dev, hostname):
        port = self._parser.getValueFromSection('connect', 'port');
        if not port:
            sys.exit(4);
        sock = socket.create_connection((hostname, port));
        json_str = json.JSONEncoder().encode(
            {
                "packet_type": "knx_write_short",
                "addr_to_send": str(dev['addr_dst']),
                "value": "0"
            }
        );
        self.send_json_obj_to_slave(json_str, sock, hostname, self.aes_slave_keys[hostname]);
        sock.close();
        return;

    ## Sends the new value of the temperature to thermostat.
    #
    # @param json_obj JSON object containing the new values.
    # @param dev The KNX device.
    # @param hostname The hostname of the slave daemon.
    # @return None
    def send_to_thermostat(self, json_obj, dev, hostname):
        port = self._parser.getValueFromSection('connect', 'port');
        if not port:
            sys.exit(4);
        if json_obj['data']['option_id'] == '412':
            val = 1;
        elif json_obj['data']['option_id'] == '413':
            val = 2;
        elif json_obj['data']['option_id'] == '414':
            val = 4;
        elif json_obj['data']['option_id'] == '415':
            val = 8;
        elif json_obj['data']['option_id'] == '416':
            val = 16;
        elif json_obj['data']['option_id'] == '417':
            val = 32;
        else:
            val = 0
        if val > 0:
            json_str = json.JSONEncoder().encode(
                {
                    "packet_type": "knx_write_long",
                    "addr_to_send": hex(int(dev['addr_dst'])),
                    "value": val
                }
            );
            sock = socket.create_connection((hostname, port));
            self.send_json_obj_to_slave(json_str, sock, hostname, self.aes_slave_keys[hostname]);
            sock.close();
            return;

    ## Sends the new mode of the air conditioner.
    #
    # @param json_obj JSON object containing the new values.
    # @param dev The KNX device.
    # @param hostname The hostname of the slave daemon.
    # @return None
    def send_clim_mode(self, json_obj, dev, hostname):
        if json_obj['data']['option_id'] == '425': #Auto
            val = 0
        elif json_obj['data']['option_id'] == '426': #Heat
            val = 1
        elif json_obj['data']['option_id'] == '427': #Morning Warmup
            val = 2
        elif json_obj['data']['option_id'] == '428': #Cool
            val = 3
        elif json_obj['data']['option_id'] == '429': #Night Purge
            val = 4
        elif json_obj['data']['option_id'] == '430': #Precool
            val = 5
        elif json_obj['data']['option_id'] == '431': #Off
            val = 6
        elif json_obj['data']['option_id'] == '432': #Test
            val = 7
        elif json_obj['data']['option_id'] == '433': #Emergency Heat
            val = 8
        elif json_obj['data']['option_id'] == '434': #Fan only
            val = 9
        elif json_obj['data']['option_id'] == '435': #Free Cool
            val = 10
        elif json_obj['data']['option_id'] == '436': #Ice
            val = 11
        else:
            val = -1
        if val >= 0:
            json_str = json.JSONEncoder().encode(
                {
                    "packet_type": "knx_write_long",
                    "addr_to_send": str(dev['addr_dst']),
                    "value":  hex(int(val))
                }
            );
            sock = socket.create_connection((hostname, port));
            self.send_json_obj_to_slave(json_str, sock, hostname, self.aes_slave_keys[hostname]);
            sock.close();
            return;

    ## Sends a value converted in percentages.
    #
    # @param json_obj Not used here.
    # @param dev Object describing the KNX device.
    # @param hostname Hostname of the slave daemon to who sent the packet.
    # @return None
    def send_knx_write_percent(self, json_obj, dev, hostname):
        port = self._parser.getValueFromSection('connect', 'port');
        if not port:
            sys.exit(4);
        sock = socket.create_connection((hostname, port));
        json_str = json.JSONEncoder().encode(
            {
                "packet_type": "knx_write_long",
                "addr_to_send": str(dev['addr_dst']),
                "value": hex(int(255*int(json_obj['data']['value'])/100))
            }
        );
        self.send_json_obj_to_slave(json_str, sock, hostname, self.aes_slave_keys[hostname]);
        sock.close();
Example #31
0
class MasterSql:
    """
    Master daemon SQL management class.
    """
    def __init__(self, log_flag = True):
        self.logger = Logger(log_flag, '/var/log/domoleaf/domomaster.log');
        self._parser = DaemonConfigParser(MASTER_CONF_FILE);
        self.db_username = self._parser.getValueFromSection(MASTER_CONF_MYSQL_SECTION,
                                                            MASTER_CONF_MYSQL_USER_ENTRY);
        self.db_passwd = self._parser.getValueFromSection(MASTER_CONF_MYSQL_SECTION,
                                                          MASTER_CONF_MYSQL_PASSWORD_ENTRY);
        self.db_dbname = self._parser.getValueFromSection(MASTER_CONF_MYSQL_SECTION,
                                                          MASTER_CONF_MYSQL_DB_NAME_ENTRY);
        if not self.db_username or not self.db_passwd or not self.db_dbname:
            frameinfo = getframeinfo(currentframe());
            self.logger.debug("[ MASTER DAEMON "+frameinfo.filaname+":"+str(frameinfo.lineno)+" ]: initialization error: wrong or missing SQL configuration.");
            sys.exit(1);
        self.functions_transform = {
              0: utils.convert_none,
              1: utils.convert_temperature,
              2: utils.convert_hundred,
              3: utils.convert_float32
        };

    def insert_hostlist_in_db(self, hostlist, db):
        """
        Update of the table containing the hosts. Inserts each host in 'hostlist'.
        """
        for host in hostlist:
            db.update_datas_in_table('ip_monitor',
                                     {"mac_addr": host._MacAddr},
                                     {"last_update": str(time.time()).split('.')[0],
                                      "ip_addr": host._IpAddr,
                                      "hostname": host._Hostname.split('.')[0]});
        db.personnal_query("DELETE FROM ip_monitor WHERE last_update<"+str(time.time()-7200).split('.')[0]);
        db.updatedb();
        query = ''.join(["UPDATE room_device JOIN ip_monitor ON plus4=mac_addr SET addr=ip_addr WHERE protocol_id=6 AND plus4 != '' AND ip_addr != addr"]);
        self.mysql_handler_personnal_query(query, db);
        db.updatedb();

    def update_enocean_log(self, json_obj, db):
        """
        Update of the enocean log table with values from 'json_obj'
        """
        daemon_id = 0;
        daemons = db.get_datas_from_table_with_names('daemon', ['daemon_id', 'name', 'serial', 'secretkey']);
        for d in daemons:
            if json_obj['sender_name'] == d[2]:
                daemon_id = d[0];
                break;
        db.insert_datas_in_table('enocean_log',
                                 ['type', 'addr_src', 'addr_dest', 'eo_value', 't_date', 'daemon_id'],
                                 (json_obj['type'], json_obj['src_addr'], json_obj['dst_addr'],
                                  json_obj['value'], json_obj['date'], daemon_id));
        db.updatedb();
        return daemon_id;

    def update_knx_log(self, json_obj, db):
        """
        Update of the knx log table with values from 'json_obj'
        """
        daemon_id = 0;
        daemons = db.get_datas_from_table_with_names('daemon', ['daemon_id', 'name', 'serial', 'secretkey']);
        for d in daemons:
            if json_obj['sender_name'] == d[2]:
                daemon_id = d[0];
                break;
        db.insert_datas_in_table('knx_log', ["type", "addr_src", "addr_dest", "knx_value", "t_date", "daemon_id"],
                                 (json_obj['type'], json_obj['src_addr'], json_obj['dst_addr'], json_obj['value'],
                                  json_obj['date'],
                                  daemon_id));
        db.updatedb();
        return daemon_id;

    def update_room_device_option_write_long(self, json_obj, daemon_id, db):
        """
        Update of the table room_device_option with long KNX value
        """
        query  = ''.join(["SELECT room_device_option.option_id, room_device.room_device_id, function_answer, dpt_optiondef.dpt_id FROM room_device_option JOIN room_device ON room_device_option.room_device_id=room_device.room_device_id JOIN dpt_optiondef ON dpt_optiondef.option_id=room_device_option.option_id AND dpt_optiondef.protocol_id=room_device.protocol_id AND dpt_optiondef.dpt_id=room_device_option.dpt_id WHERE daemon_id=",
                  str(daemon_id), " AND room_device_option.addr=\"", str(json_obj['dst_addr']), "\""]);
        res = self.mysql_handler_personnal_query(query, db);
        if not res:
            query = ''.join(["SELECT room_device_option.option_id, room_device.room_device_id, function_answer, dpt_optiondef.dpt_id FROM room_device_option JOIN room_device ON room_device_option.room_device_id=room_device.room_device_id JOIN dpt_optiondef ON dpt_optiondef.option_id=room_device_option.option_id AND dpt_optiondef.protocol_id=room_device.protocol_id AND dpt_optiondef.dpt_id=room_device_option.dpt_id WHERE ",
                     str(daemon_id), " AND room_device_option.addr_plus=\"", str(json_obj['dst_addr']), "\""]);
            res = self.mysql_handler_personnal_query(query, db);
        for r in res:
            if int(r[0]) == 13:
                if not json_obj['value']:
                    up = 'UPDATE room_device_option SET opt_value=0 WHERE room_device_id=' + str(r[1]) + ' AND option_id=12';
                else:
                    up = 'UPDATE room_device_option SET opt_value=1 WHERE room_device_id=' + str(r[1]) + ' AND option_id=12';
                self.logger.debug('update_room_device_option write_long: up = ' + up);
                self.mysql_handler_personnal_query(up, db);
                query = ''.join(["UPDATE room_device_option SET opt_value=\"", str(json_obj['value']),
                         "\" WHERE room_device_id=", str(r[1]), " AND option_id=", str(r[0])]);
                self.mysql_handler_personnal_query(query, db);
            elif int(r[0]) == 72 or int(r[0]) == 388:
                val = int(json_obj['value']);
                res = utils.convert_temperature(val);
                query = ''.join(["UPDATE room_device_option JOIN room_device ON room_device_option.room_device_id=room_device.room_device_id SET opt_value=\"",
                         str(res), "\" WHERE daemon_id=", str(daemon_id),
                         " AND room_device_option.addr=\"", str(json_obj['dst_addr']), "\"", 
                         " OR ", "room_device_option.addr_plus=\"", str(json_obj['dst_addr']), "\""]);
                self.logger.debug('update_room_device_option write_long: query = ' + query);
                self.mysql_handler_personnal_query(query, db);
            else:
                val = self.functions_transform[r[2]](int(json_obj['value']));
                up = ''.join(["UPDATE room_device_option SET opt_value=\"", str(val),
                     "\" WHERE room_device_id=", str(r[1]), " AND option_id=\"", str(r[0]), "\""]);
                self.logger.debug('update_room_device_option write_long: up = ' + up)
                self.mysql_handler_personnal_query(up, db);
        return res

    def update_room_device_option_resp(self, json_obj, daemon_id, db):
        """
        Update of the table room_device_option with resp KNX value
        """
        query = ''.join(["SELECT option_id, room_device.room_device_id, function_answer FROM ",
              "room_device_option JOIN room_device ON ",
              "room_device_option.room_device_id=room_device.room_device_id ",
              "JOIN dpt_optiondef ON dpt_optiondef.option_id=room_device_option.option_id AND ",
              "dpt_optiondef.protocol_id=room_device.protocol_id AND dpt_optiondef.dpt_id=room_device_option.dpt_id ",
              "WHERE daemon_id=", str(daemon_id), " AND room_device_option.addr=\"", str(json_obj['dst_addr']), "\""]);
        res = self.mysql_handler_personnal_query(query, db);
        for r in res:
            val = self.functions_transform[r[2]](int(json_obj['value']));
            query = ''.join(["UPDATE room_device_option JOIN room_device ON ",
                  "room_device_option.room_device_id=room_device.room_device_id SET ",
                  "opt_value=\"", str(val), "\" WHERE daemon_id=", str(daemon_id),
                  " AND room_device_option.addr=\"", str(json_obj['dst_addr']), "\""]);
            self.logger.debug("update_room_device_option resp query = "+query);
            self.mysql_handler_personnal_query(query, db);
        return res

    def update_room_device_option_write_short(self, json_obj, daemon_id, db):
        """
        Update of the table room_device_option with short KNX value
        """
        query = ''.join(["SELECT option_id, room_device.room_device_id FROM ",
              "room_device_option JOIN room_device ON ",
              "room_device_option.room_device_id=room_device.room_device_id WHERE ",
              "daemon_id=", str(daemon_id), " AND room_device_option.addr=\"",
              str(json_obj['dst_addr']), "\""]);
        #self.logger.debug("update_room_device_option write_short query : " + query);
        res = self.mysql_handler_personnal_query(query, db);
        if not res:
            query = ''.join(["SELECT option_id, room_device.room_device_id FROM ",
                  "room_device_option JOIN room_device ON ",
                  "room_device_option.room_device_id=room_device.room_device_id WHERE ",
                  "daemon_id=", str(daemon_id), " AND room_device_option.addr_plus=\"",
                  str(json_obj['dst_addr']), "\""]);
            #self.logger.debug("update_room_device_option write_short query : " + query);
            res = self.mysql_handler_personnal_query(query, db);
        for r in res:
            if (int(r[0]) == MasterDaemon.OPTION_ON_OFF or int(r[0]) == MasterDaemon.OPTION_UP_DOWN or int(r[0]) == MasterDaemon.OPTION_OPEN_CLOSE):
                up = 'UPDATE room_device_option SET opt_value=';
                if not json_obj['value']:
                    up += '0';
                else:
                    up += '255';
                up += ' WHERE room_device_id='+str(r[1])+" AND option_id=13";
                self.logger.debug("update_room_device_option write_short up1: "+up);
                self.mysql_handler_personnal_query(up, db);
            up = ''.join(["UPDATE room_device_option SET opt_value=", str(json_obj['value']),
                  " WHERE room_device_id=", str(r[1]), " AND option_id=", str(r[0]), ""]);
            self.logger.debug("update_room_device_option write_short up2: "+up);
            self.mysql_handler_personnal_query(up, db);
        return res

    def mysql_handler_personnal_query(self, query, db=0):
        """
        Sends personnal query to the database and returns the result
        """
        tmp = db;
        if not tmp:
            db = MysqlHandler(self.db_username, self.db_passwd, self.db_dbname);
        res = db.personnal_query(query);
        db.updatedb();
        if not tmp:
            db.close();
        return res;

    def get_daemons(self, db):
        """
        Retrieves each daemon stored in the database
        """
        daemons = db.get_datas_from_table_with_names('daemon', ['daemon_id', 'name', 'serial', 'secretkey']);
        return daemons;
Example #32
0
def slave_conf_copy():
    file_from = DaemonConfigParser(SLAVE_CONF_FILE_FROM);
    file_to   = DaemonConfigParser(SLAVE_CONF_FILE_TO);
    
    #listen
    var = file_from.getValueFromSection('listen', 'port');
    file_to.writeValueFromSection('listen', 'port', var);
    
    #connect
    var = file_from.getValueFromSection('connect', 'port');
    file_to.writeValueFromSection('connect', 'port', var);
    
    #knx
    var = file_from.getValueFromSection('knx', 'port');
    file_to.writeValueFromSection('knx', 'port', var);
    var = file_from.getValueFromSection('knx', 'interface');
    file_to.writeValueFromSection('knx', 'interface', var);
    var = file_from.getValueFromSection('knx', 'activated');
    file_to.writeValueFromSection('knx', 'activated', var);
    
    #enocean
    var = file_from.getValueFromSection('enocean', 'port');
    file_to.writeValueFromSection('enocean', 'port', var);
    var = file_from.getValueFromSection('enocean', 'interface');
    file_to.writeValueFromSection('enocean', 'interface', var);
    
    #cron
    var = file_from.getValueFromSection('cron', 'port');
    file_to.writeValueFromSection('cron', 'port', var);
    var = file_from.getValueFromSection('cron', 'address');
    file_to.writeValueFromSection('cron', 'address', var);
    
    #personnal_key
    var = file_from.getValueFromSection('personnal_key', 'aes');
    file_to.writeValueFromSection('personnal_key', 'aes', var);
    
    #openvpn
    var = file_from.getValueFromSection('openvpn', 'openvpnserver');
    file_to.writeValueFromSection('openvpn', 'openvpnserver', var);
Example #33
0
def master_conf_copy():
    file_from = DaemonConfigParser(MASTER_CONF_FILE_BKP)
    file_to = DaemonConfigParser(MASTER_CONF_FILE_TO)

    # listen
    var = file_from.getValueFromSection("listen", "port_slave")
    file_to.writeValueFromSection("listen", "port_slave", var)
    var = file_from.getValueFromSection("listen", "port_cmd")
    file_to.writeValueFromSection("listen", "port_cmd", var)

    # connect
    var = file_from.getValueFromSection("connect", "port")
    file_to.writeValueFromSection("connect", "port", var)

    # mysql
    var = file_from.getValueFromSection("mysql", "user")
    file_to.writeValueFromSection("mysql", "user", var)
    var = file_from.getValueFromSection("mysql", "database_name")
    file_to.writeValueFromSection("mysql", "database_name", var)

    # greenleaf
    var = file_from.getValueFromSection("greenleaf", "commercial")
    file_to.writeValueFromSection("greenleaf", "commercial", var)
    var = file_from.getValueFromSection("greenleaf", "admin_addr")
    file_to.writeValueFromSection("greenleaf", "admin_addr", var)
Example #34
0
class KNXManager:
    """
    KNX management class
    """
    def __init__(self, slave_keys):
        self.knx_function = {
            OPTION_ON_OFF       : self.send_knx_write_short_to_slave,
            OPTION_VAR          : self.send_knx_write_long_to_slave,
            OPTION_UP_DOWN      : self.send_knx_write_short_to_slave,
            OPTION_OPEN_CLOSE   : self.send_knx_write_short_to_slave,
            OPTION_STOP_UP_DOWN : self.send_knx_write_short_to_slave,
            OPTION_SPEED_FAN_0  : self.send_knx_write_speed_fan,
            OPTION_SPEED_FAN_1  : self.send_knx_write_speed_fan,
            OPTION_SPEED_FAN_2  : self.send_knx_write_speed_fan,
            OPTION_SPEED_FAN_3  : self.send_knx_write_speed_fan,
            OPTION_SPEED_FAN_4  : self.send_knx_write_speed_fan,
            OPTION_SPEED_FAN_5  : self.send_knx_write_speed_fan,
            OPTION_SPEED_FAN_6  : self.send_knx_write_speed_fan,
            OPTION_TEMPERATURE_W: self.send_knx_write_temp,
            OPTION_COLOR_R      : self.send_knx_write_long_to_slave,
            OPTION_COLOR_G      : self.send_knx_write_long_to_slave,
            OPTION_COLOR_B      : self.send_knx_write_long_to_slave,
            OPTION_COLOR_W      : self.send_knx_write_long_to_slave
        };
        self.logger = Logger(True, LOG_FILE);
        self.sql = MasterSql();
        self._parser = DaemonConfigParser('/etc/domoleaf/master.conf');
        self.aes_slave_keys = slave_keys;

    def update_room_device_option(self, daemon_id, json_obj):
        """
        Update room_device_option table in database to set new values of the device described by 'json_obj'
        """
        if int(json_obj['type']) == KNX_RESPONSE:
            self.sql.update_room_device_option_resp(json_obj, daemon_id);
        elif int(json_obj['type']) == KNX_WRITE_SHORT:
            self.sql.update_room_device_option_write_short(json_obj, daemon_id);
        elif int(json_obj['type']) == KNX_WRITE_LONG:
            self.sql.update_room_device_option_write_long(json_obj, daemon_id);

    def protocol_knx(self, json_obj, dev, hostname):
        """
        KNX protocol data treatment function
        """
        new_obj = {
            "data": {
                "addr": str(dev['addr_dst']),
                "value": str(json_obj['data']['value']),
                "option_id": str(json_obj['data']['option_id']),
                "room_device_id": str(dev['room_device_id']),
            }
        };
        self.knx_function[int(json_obj['data']['option_id'])](hostname, new_obj);

    def send_json_obj_to_slave(self, json_str, sock, hostname, aes_key, close_flag = True):
        """
        Send 'json_obj' to 'hostname' via 'sock'
        """
        hostname_key = '';
        if '.' in hostname:
            hostname_key = hostname.split('.')[0];
        else:
            hostname_key = hostname;
        AES.key_size = 32;
        aes_IV = AESManager.get_IV();
        encode_obj = AES.new(aes_key, AES.MODE_CBC, aes_IV);
        spaces = 16 - len(json_str) % 16;
        data2 = encode_obj.encrypt(json_str + (spaces * ' '));
        sock.send(bytes(aes_IV, 'utf-8') + data2);
        if close_flag == True:
            sock.close();

    def send_knx_write_speed_fan(self, hostname, json_obj):
        """
        Ask to close all the speed fan before open another
        """
        port = self._parser.getValueFromSection('connect', 'port');
        if not port:
            sys.exit(4);
        if json_obj['data']['value'] == '1':
            query =  'SELECT option_id, addr, dpt_id ';
            query += 'FROM room_device_option ';
            query += 'WHERE room_device_id=' + str(json_obj['data']['room_device_id']) + ' AND ';
            query += 'option_id IN(400, 401, 402, 403, 404, 405, 406) AND status=1';
            res = self.sql.mysql_handler_personnal_query(query);
            for line in res:
                if str(line[2]) == "51" and str(line[0]) == str(json_obj['data']['option_id']):
                    sock = socket.create_connection((hostname, port));
                    val = str(line[0]).split('40')[1];
                    json_str = json.JSONEncoder().encode(
                        {
                            "packet_type": "knx_write_long",
                            "addr_to_send": line[1],
                            "value": val
                        }
                    );
                    self.send_json_obj_to_slave(json_str, sock, hostname, self.aes_slave_keys[hostname]);
                    sock.close();
                    return;
                if str(line[2]) == "2" and str(line[0]) != str(json_obj['data']['option_id']):
                    sock = socket.create_connection((hostname, port));
                    json_str = json.JSONEncoder().encode(
                        {
                            "packet_type": "knx_write_short",
                            "addr_to_send": line[1],
                            "value": "0"
                        }
                    );
                    self.send_json_obj_to_slave(json_str, sock, hostname, self.aes_slave_keys[hostname]);
                    sock.close();

        sock = socket.create_connection((hostname, port));
        json_str = json.JSONEncoder().encode(
            {
                "packet_type": "knx_write_short",
                "addr_to_send": json_obj['data']['addr'],
                "value": json_obj['data']['value']
            }
        );
        self.send_json_obj_to_slave(json_str, sock, hostname, self.aes_slave_keys[hostname]);

    def send_knx_write_temp(self, hostname, json_obj):
        """
        Converts absolute value of temperature (Celsius) in 2 hexadecimal values for
        sending to KNX device
        """
        port = self._parser.getValueFromSection('connect', 'port');
        if not port:
            sys.exit(4);
        sock = socket.create_connection((hostname, port));
        val_str = json_obj['data']['value'];
        if '.' in val_str:
            val_str = val_str.split('.')[0];
        value = utils.convert_temperature_reverse(int(val_str));
        json_str = json.JSONEncoder().encode(
            {
                "packet_type": "knx_write_temp",
                "addr_to_send": json_obj['data']['addr'],
                "value": value[0] + ' ' + value[1]
            }
        );
        self.send_json_obj_to_slave(json_str, sock, hostname, self.aes_slave_keys[hostname]);

    def send_knx_write_long_to_slave(self, hostname, json_obj):
        """
        Constructs long write request and sends it to 'hostname'
        """
        port = self._parser.getValueFromSection('connect', 'port');
        if not port:
            sys.exit(4);
        sock = socket.create_connection((hostname, port));
        json_str = json.JSONEncoder().encode(
            {
                "packet_type": "knx_write_long",
                "addr_to_send": json_obj['data']['addr'],
                "value": hex(int(json_obj['data']['value']))
            }
        );
        self.send_json_obj_to_slave(json_str, sock, hostname, self.aes_slave_keys[hostname]);

    def send_knx_write_short_to_slave(self, hostname, json_obj):
        """
        Constructs short write request and sends it to 'hostname'
        """
        port = self._parser.getValueFromSection('connect', 'port');
        if not port:
            sys.exit(4);
        sock = socket.create_connection((hostname, port));
        json_str = json.JSONEncoder().encode(
            {
                "packet_type": "knx_write_short",
                "addr_to_send": json_obj['data']['addr'],
                "value": json_obj['data']['value']
            }
        );
        self.send_json_obj_to_slave(json_str, sock, hostname, self.aes_slave_keys[hostname]);

    def send_knx_read_request_to_slave(self, hostname, json_obj):
        """
        Constructs short read request and sends it to 'hostname'
        """
        port = self._parser.getValueFromSection('connect', 'port');
        if not port:
            sys.exit(4);
        sock = socket.create_connection((hostname, port));
        json_str = json.JSONEncoder().encode(
            {
                "packet_type": "knx_read_request",
                "addr_to_read": json_obj['data']['addr']
            }
        );
        self.send_json_obj_to_slave(json_str, sock, hostname, self.aes_slave_keys[hostname]);
Example #35
0
class MasterSql:
    """
    Master daemon SQL management class.
    """

    def __init__(self, log_flag=False):
        self.logger = Logger(log_flag, "/var/log/glmaser.log")
        self._parser = DaemonConfigParser(MASTER_CONF_FILE)
        self.db_username = self._parser.getValueFromSection(MASTER_CONF_MYSQL_SECTION, MASTER_CONF_MYSQL_USER_ENTRY)
        self.db_passwd = self._parser.getValueFromSection(MASTER_CONF_MYSQL_SECTION, MASTER_CONF_MYSQL_PASSWORD_ENTRY)
        self.db_dbname = self._parser.getValueFromSection(MASTER_CONF_MYSQL_SECTION, MASTER_CONF_MYSQL_DB_NAME_ENTRY)
        if not self.db_username or not self.db_passwd or not self.db_dbname:
            frameinfo = getframeinfo(currentframe())
            self.logger.info(
                "[ MASTER DAEMON "
                + frameinfo.filaname
                + ":"
                + str(frameinfo.lineno)
                + " ]: initialization error: wrong or missing SQL configuration."
            )
            sys.exit(1)

    def insert_hostlist_in_db(self, hostlist):
        """
        Update of the table containing the hosts. Inserts each host in 'hostlist'.
        """
        db = MysqlHandler(self.db_username, self.db_passwd, self.db_dbname)
        for host in hostlist:
            db.update_datas_in_table(
                "ip_monitor",
                {"mac_addr": host._MacAddr},
                {
                    "last_update": str(time.time()).split(".")[0],
                    "ip_addr": host._IpAddr,
                    "hostname": host._Hostname.split(".")[0],
                },
            )
        db.updatedb()
        db.close()

    def update_enocean_log(self, json_obj):
        """
        Update of the enocean log table with values from 'json_obj'
        """
        daemon_id = 0
        db = MysqlHandler(self.db_username, self.db_passwd, self.db_dbname)
        daemons = db.get_datas_from_table_with_names("daemon", ["daemon_id", "name", "serial", "secretkey"])
        for d in daemons:
            if json_obj["sender_name"] == d[2]:
                daemon_id = d[0]
                break
        db.insert_datas_in_table(
            "enocean_log",
            ["type", "addr_src", "addr_dest", "eo_value", "t_date", "daemon_id"],
            (
                json_obj["type"],
                json_obj["src_addr"],
                json_obj["dst_addr"],
                json_obj["value"],
                json_obj["date"],
                daemon_id,
            ),
        )
        db.updatedb()
        db.close()
        return daemon_id

    def update_knx_log(self, json_obj):
        """
        Update of the knx log table with values from 'json_obj'
        """
        daemon_id = 0
        db = MysqlHandler(self.db_username, self.db_passwd, self.db_dbname)
        daemons = db.get_datas_from_table_with_names("daemon", ["daemon_id", "name", "serial", "secretkey"])
        for d in daemons:
            if json_obj["sender_name"] == d[2]:
                daemon_id = d[0]
                break
        db.insert_datas_in_table(
            "knx_log",
            ["type", "addr_src", "addr_dest", "knx_value", "t_date", "daemon_id"],
            (
                json_obj["type"],
                json_obj["src_addr"],
                json_obj["dst_addr"],
                json_obj["value"],
                json_obj["date"],
                daemon_id,
            ),
        )
        db.updatedb()
        db.close()
        return daemon_id

    def update_room_device_option_write_long(self, json_obj, daemon_id):
        """
        Update of the table room_device_option with long KNX value
        """
        query = "SELECT option_id, room_device.room_device_id, addr_plus FROM room_device_option "
        query += "JOIN room_device ON room_device_option.room_device_id=room_device.room_device_id "
        query += "WHERE daemon_id=" + str(daemon_id) + ' AND room_device_option.addr="'
        query += str(json_obj["dst_addr"]) + '"'
        res = self.mysql_handler_personnal_query(query)
        query = "UPDATE room_device_option JOIN room_device ON "
        query += "room_device_option.room_device_id=room_device.room_device_id SET "
        for r in res:
            if int(r[0]) == MasterDaemon.OPTION_VAR:
                up = "UPDATE room_device_option SET valeur="
                if json_obj["value"] == 0:
                    up += "0"
                else:
                    up += "1"
                up += " WHERE room_device_id=" + str(r[1])
                up += " AND option_id=12"
                self.logger.info("update_room_device_option write_long: up = " + up)
                self.mysql_handler_personnal_query(up)
            elif int(r[0]) == MasterDaemon.OPTION_TEMPERATURE or int(r[0]) == MasterDaemon.OPTION_TEMPERATURE_W:
                val = int(json_obj["value"])
                res = utils.convert_temperature(val)
                query += 'valeur="' + str(res) + '" WHERE daemon_id=' + str(daemon_id)
                query += ' AND room_device_option.addr="' + str(json_obj["dst_addr"]) + '"'
                self.logger.info("update_room_device_option write_long: query = " + query)
                self.mysql_handler_personnal_query(query)
            else:
                up = 'UPDATE room_device_option SET valeur="' + str(json_obj["value"])
                up += '" WHERE room_device_id=' + str(r[1]) + ' AND option_id="' + str(r[0]) + '"'
                self.logger.info("update_room_device_option write_long: up = " + up)
                self.mysql_handler_personnal_query(up)

    def update_room_device_option_resp(self, json_obj, daemon_id):
        """
        Update of the table room_device_option with resp KNX value
        """
        query = "SELECT option_id, room_device.room_device_id FROM "
        query += "room_device_option JOIN room_device ON "
        query += "room_device_option.room_device_id=room_device.room_device_id WHERE "
        query += "daemon_id=" + str(daemon_id) + ' AND room_device_option.addr="'
        query += str(json_obj["dst_addr"]) + '"'
        res = self.mysql_handler_personnal_query(query)
        query = "UPDATE room_device_option JOIN room_device ON "
        query += "room_device_option.room_device_id=room_device.room_device_id SET "
        if type(res).__name__ == "list":
            for r in res:
                if int(r[0]) == OPTION_TEMPERATURE:
                    val = int(json_obj["value"])
                    res = utils.convert_temperature(val)
                    query += 'valeur="' + str(res) + '" WHERE daemon_id=' + str(daemon_id)
                    query += ' AND room_device_option.addr="' + str(json_obj["dst_addr"]) + '"'
                    self.logger.info("update_room_device_option resp query = " + query)
                    self.mysql_handler_personnal_query(query)
                else:
                    query += 'valeur="' + str(json_obj["value"]) + '" WHERE daemon_id=' + str(daemon_id)
                    query += ' AND addr_plus="' + str(json_obj["dst_addr"]) + '"'
                    self.logger.info("update_room_device_option resp query = " + query)
                    self.mysql_handler_personnal_query(query)
        else:
            if int(r[0]) == OPTION_TEMPERATURE:
                val = int(json_obj["value"])
                res = utils.convert_temperature(val)
                query += 'valeur="' + str(res) + '" WHERE daemon_id=' + str(daemon_id)
                query += ' AND room_device_option.addr="' + str(json_obj["dst_addr"]) + '"'
                self.logger.info("update_room_device_option resp query = " + query)
                self.mysql_handler_personnal_query(query)
            else:
                query += 'valeur="' + str(json_obj["value"]) + '" WHERE daemon_id=' + str(daemon_id)
                query += ' AND addr_plus="' + str(json_obj["dst_addr"]) + '"'
                self.logger.info("update_room_device_option resp query = " + query)
                self.mysql_handler_personnal_query(query)

    def update_room_device_option_write_short(self, json_obj, daemon_id):
        """
        Update of the table room_device_option with short KNX value
        """
        query = "SELECT option_id, room_device.room_device_id FROM "
        query += "room_device_option JOIN room_device ON "
        query += "room_device_option.room_device_id=room_device.room_device_id WHERE "
        query += "daemon_id=" + str(daemon_id) + ' AND room_device_option.addr="'
        query += str(json_obj["dst_addr"]) + '"'
        self.logger.info("update_room_device_option write_short query : " + query)
        res = self.mysql_handler_personnal_query(query)
        for r in res:
            if (
                int(r[0]) == MasterDaemon.OPTION_ON_OFF
                or int(r[0]) == MasterDaemon.OPTION_UP_DOWN
                or int(r[0]) == MasterDaemon.OPTION_OPEN_CLOSE
            ):
                up = "UPDATE room_device_option SET valeur="
                if json_obj["value"] == 0:
                    up += "0"
                else:
                    up += "255"
                up += " WHERE room_device_id=" + str(r[1]) + " AND option_id=13"
                self.logger.info("update_room_device_option write_short up1: " + up)
                self.mysql_handler_personnal_query(up)
            up = "UPDATE room_device_option SET valeur=" + str(json_obj["value"])
            up += " WHERE room_device_id=" + str(r[1]) + " AND option_id=" + str(r[0]) + ""
            self.logger.info("update_room_device_option write_short up2: " + up)
            self.mysql_handler_personnal_query(up)

    def mysql_handler_personnal_query(self, query):
        """
        Sends personnal query to the database and returns the result
        """
        db = MysqlHandler(self.db_username, self.db_passwd, self.db_dbname)
        res = db.personnal_query(query)
        db.updatedb()
        db.close()
        return res

    def get_daemons(self):
        """
        Retrieves each daemon stored in the database
        """
        db = MysqlHandler(self.db_username, self.db_passwd, self.db_dbname)
        daemons = db.get_datas_from_table_with_names("daemon", ["daemon_id", "name", "serial", "secretkey"])
        return daemons
Example #36
0
class SlaveDaemon:
    """
    Main slave class
    It does communication between different monitors (KNX, EnOcean... in C) and the masters (servers)
    """
    def __init__(self, log_flag):
        self.logger = Logger(log_flag, LOG_FILE);
        self.logger.info('Started Domoleaf Slave daemon');
        print('######## SLAVE DAEMON #######')
        self.connected_masters = {};
        self.connected_knx = [];
        self.connected_enocean = [];
        self.connected_cron = [];
        self.clients = [];
        self._scanner = Scanner();
        self._hostlist = [];
        myhostname = socket.gethostname().upper()
        if SLAVE_NAME_PREFIX in myhostname:
            self._scanner.scan();
            self._hostlist = self._scanner._HostList;
        else:
            self._hostlist.append(Host('', '127.0.0.1', myhostname));
        self._parser = DaemonConfigParser(SLAVE_CONF_FILE);
        self.encrypt_keys = {};
        self.knx_sock = None;
        self.master_sock = None;
        self.enocean_sock = None;
        self.cron_sock = None;
        self.private_aes = self._parser.getValueFromSection('personnal_key', 'aes');
        self.wifi_init(self._parser.getValueFromSection('wifi', 'ssid'), self._parser.getValueFromSection('wifi', 'password'), self._parser.getValueFromSection('wifi', 'encryption'), self._parser.getValueFromSection('wifi', 'mode'), 0);
        self.connect_port = self._parser.getValueFromSection(SLAVE_CONF_CONNECT_SECTION, SLAVE_CONF_CONNECT_PORT_ENTRY);
        self.functions = {
            KNX_READ_REQUEST    : self.knx_read_request,
            KNX_WRITE_SHORT     : self.knx_write_short,
            KNX_WRITE_LONG      : self.knx_write_long,
            KNX_WRITE_TEMP      : self.knx_write_temp,
            CHECK_SLAVE         : self.check_slave,
            MONITOR_IP          : self.monitor_ip,
            DATA_UPDATE         : self.update,
            SEND_TECH           : self.send_tech,
            SEND_ALIVE          : self.send_alive,
            SEND_INTERFACES     : self.send_interfaces,
            SHUTDOWN_D3         : self.shutdown_d3,
            REBOOT_D3           : self.reboot_d3,
            WIFI_UPDATE         : self.wifi_update
        };

    def update(self, json_obj, connection):
        p = call(['dpkg', '--configure', '-a'])
        call(['apt-get', 'update']);
        call(['DEBIAN_FRONTEND=noninteractive', 'apt-get', 'install', 'domoslave', '-y']);
        version = os.popen("dpkg-query -W -f='${Version}\n' domoslave").read().split('\n')[0];
        json_str = '{"packet_type": "update_finished", "aes_pass": "******", "new_version": ' + version + '}';
        encrypt_IV = AESManager.get_IV();
        spaces = 16 - len(json_str) % 16;
        json_str = json_str + (spaces * ' ');
        encode_obj = AES.new(self.private_aes, AES.MODE_CBC, encrypt_IV);
        data = encode_obj.encrypt(json_str);
        # faut ouvrir une nouvelle socket pour envoyer la nouvelle version
        # connection.send(bytes(encrypt_IV, 'utf-8') + data);

    def run(self):
        """
        Initialization of the sockets for listenning incomming connections.
        Calls the loop function.
        """
        self.run = True;
        self.knx_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM);
        self.master_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM);
        self.enocean_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM);
        self.cron_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM);
        self.knx_sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1);
        self.master_sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1);
        self.enocean_sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1);
        self.cron_sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1);
        port = self._parser.getValueFromSection(SLAVE_CONF_KNX_SECTION, SLAVE_CONF_KNX_PORT_ENTRY);
        if not port:
            sys.exit(2);
        port_master = self._parser.getValueFromSection(SLAVE_CONF_LISTEN_SECTION, SLAVE_CONF_LISTEN_PORT_ENTRY);
        if not port_master:
            sys.exit(2);
        port_enocean = self._parser.getValueFromSection(SLAVE_CONF_ENOCEAN_SECTION, SLAVE_CONF_ENOCEAN_PORT_ENTRY);
        if not port_enocean:
            sys.exit(2);
        port_cron = self._parser.getValueFromSection(SLAVE_CONF_CRON_SECTION, SLAVE_CONF_CRON_PORT_ENTRY);
        if not port_cron:
            sys.exit(2);
        self.knx_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1);
        self.master_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1);
        self.enocean_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1);
        self.cron_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1);
        self.knx_sock.bind(('', int(port)));
        self.master_sock.bind(('', int(port_master)));
        self.enocean_sock.bind(('', int(port_enocean)));
        self.cron_sock.bind(('127.0.0.1', int(port_cron)));
        self.knx_sock.listen(MAX_KNX);
        self.master_sock.listen(MAX_MASTERS);
        self.enocean_sock.listen(MAX_ENOCEAN);
        self.cron_sock.listen(MAX_CRON);
        self.send_monitor_ip()
        self.loop();

    def accept_knx(self):
        """
        Get available sockets for reading on the KNX socket.
        """
        rlist, wlist, elist = select.select([self.knx_sock], [], [], SELECT_TIMEOUT);
        append = self.connected_knx.append       
        for connection in rlist:
            new_knx, addr = connection.accept();
            append(new_knx);
        self.receive_from_knx(self.connected_knx);

    def accept_masters(self):
        """
        Get available sockets for reading on the master socket.
        """
        rlist, wlist, elist = select.select([self.master_sock], [], [], SELECT_TIMEOUT);
        masters_socks = [];
        append = masters_socks.append;
        for item in rlist:
            new_conn, addr = item.accept();
            append(new_conn);
        self.receive_from_masters(masters_socks);

    def accept_enocean(self):
        """
        Get available sockets for reading on the EnOcean socket.
        """
        rlist, wlist, elist = select.select([self.enocean_sock], [], [], SELECT_TIMEOUT);
        enocean_socks = [];
        append = enocean_socks.append;
        append_connected = self.connected_enocean.append;
        for item in rlist:
            new_conn, addr = item.accept();
            append(new_conn);
            append_connected(new_conn);
        self.receive_from_enocean(enocean_socks);

    def accept_cron(self):
        """
        Get available sockets for reading on the Cron socket.
        """
        rlist, wlist, elist = select.select([self.cron_sock], [], [], SELECT_TIMEOUT);
        cron_socks = [];
        append = cron_socks.append;
        append_connected = self.connected_cron.append;
        for item in rlist:
            new_conn, addr = item.accept();
            append(new_conn);
            append_connected(new_conn);
        self.receive_from_cron(cron_socks);

    def parse_data(self, data, connection):
        """
        Calls the wanted function with the packet_type described in 'data' (JSON syntax)
        """
        json_obj = json.JSONDecoder().decode(data);
        if json_obj['packet_type'] in self.functions.keys():
            self.functions[json_obj['packet_type']](json_obj, connection);
        else:
            raise Exception(str(json_obj['packet_type'])+": is not a valid packet type");

    def knx_read_request(self, json_obj, connection):
        """
        System call of "groupread" with parameters.
        """
        call(['knxtool', CALL_GROUPREAD, EIB_URL, json_obj['addr_to_read']]);

    def knx_write_temp(self, json_obj, connection):
        """
        System call of "groupwrite" with parameters.
        Almost the same as "knx_write_long" function, except that parameters are not the same
        """
        val = json_obj['value'].split(' ')
        call(['knxtool', CALL_GROUPWRITE, EIB_URL, json_obj['addr_to_send'], val[0], val[1]]);

    def knx_write_short(self, json_obj, connection):
        """
        System call of "groupswrite" with parameters.
        """
        call(['knxtool', CALL_GROUPSWRITE, EIB_URL, json_obj['addr_to_send'], str(json_obj['value'])]);

    def knx_write_long(self, json_obj, connection):
        """
        System call of "groupwrite" with parameters.
        """
        call(['knxtool', CALL_GROUPWRITE, EIB_URL, json_obj['addr_to_send'], str(json_obj['value'])]);

    def receive_from_masters(self, masters_to_read):
        """
        Read data comming from masters and call "parse_data" function.
        """
        for master in masters_to_read:
            data = master.recv(4096);
            decrypt_IV = data[:16].decode();
            decode_obj = AES.new(self.private_aes, AES.MODE_CBC, decrypt_IV);
            data2 = decode_obj.decrypt(data[16:]);
            self.parse_data(data2.decode(), master);
            master.close();

    def receive_from_knx(self, knx_to_read):
        """
        Read data from monitor KNX and transmits to master.
        """
        for knx in knx_to_read:
            data = knx.recv(TELEGRAM_LENGTH);
            if data:
                self.send_knx_data_to_masters(data);
            if knx in self.connected_knx:
                knx.close();
                self.connected_knx.remove(knx);

    def receive_from_enocean(self, enocean_to_read):
        """
        Read data from monitor EnOcean and transmits to master.
        """
        for enocean in enocean_to_read:
            data = enocean.recv(4096);
            if data:
                self.send_enocean_data_to_masters(data);
            if enocean in self.connected_enocean:
                enocean.close();
                self.connected_enocean.remove(enocean);

    def receive_from_cron(self, cron_to_read):
        """
        Receive data from Cron and execute it.
        """
        for cron in cron_to_read:
            data = cron.recv(4096);
            if data:
                json_str = json.JSONEncoder().encode(
                    {
                        "packet_type": data.decode()
                    }
                );
                self.parse_data(json_str, cron);
            if cron in self.connected_cron:
                cron.close();
                self.connected_cron.remove(cron);

    def check_slave(self, json_obj, connection):
        """
        Callback called each time a check_slave packet is received.
        Used to confirm the existence of this daemon.
        """
        interface_knx = self._parser.getValueFromSection(SLAVE_CONF_KNX_SECTION, SLAVE_CONF_KNX_INTERFACE);
        interface_enocean = self._parser.getValueFromSection(SLAVE_CONF_ENOCEAN_SECTION, SLAVE_CONF_ENOCEAN_INTERFACE);
        version = os.popen("dpkg-query -W -f='${Version}\n' domoslave").read().split('\n')[0];
        json_str = '{"packet_type": "check_slave", "aes_pass": "******", "version": "' + version + '", "interface_knx": "' + interface_knx + '", "interface_enocean": "' + interface_enocean + '"}';
        master_hostname = str(json_obj['sender_name']);
        encrypt_IV = AESManager.get_IV();
        spaces = 16 - len(json_str) % 16;
        json_str = json_str + (spaces * ' ');
        encode_obj = AES.new(self.private_aes, AES.MODE_CBC, encrypt_IV);
        data = encode_obj.encrypt(json_str);
        connection.send(bytes(encrypt_IV, 'utf-8') + data);

    def monitor_ip(self, json_obj, connection):
        """
        Re scan the local network to refresh hostlist.
        """
        self._scanner.scan();
        self._hostlist = self._scanner._HostList;

    def send_monitor_ip(self):
        json_str = json.JSONEncoder().encode(
            {
                "packet_type": "monitor_ip"
            }
        );
        self.send_data_to_all_masters(json_str);

    def loop(self):
        """
        Main daemon loop.
        """
        while self.run:
            try:
                self.accept_knx();
            except Exception as e:
                frameinfo = getframeinfo(currentframe());
                self.logger.error('in loop accept_knx: '+str(e));
                print('in loop accept_knx: ',str(e));
            except KeyboardInterrupt as e:
                frameinfo = getframeinfo(currentframe());
                self.logger.error('in loop: Keyboard interrupt');
            try:
                self.accept_masters();
            except Exception as e:
                frameinfo = getframeinfo(currentframe());
                self.logger.error('in loop accept_masters: '+str(e));
                print('in loop accept_masters: ',str(e));
            except KeyboardInterrupt as e:
                frameinfo = getframeinfo(currentframe());
                self.logger.error('in loop: Keyboard interrupt');
            try:
                self.accept_enocean();
            except Exception as e:
                frameinfo = getframeinfo(currentframe());
                self.logger.error('in loop accept_enocean: '+str(e));
                print('in loop accept_enocean: ',str(e));
            except KeyboardInterrupt as e:
                frameinfo = getframeinfo(currentframe());
                self.logger.error('in loop: Keyboard interrupt');
            try:
                self.accept_cron();
            except Exception as e:
                frameinfo = getframeinfo(currentframe());
                self.logger.error('in loop accept_cron: '+str(e));
                print('in loop accept_cron: ',str(e));
            except KeyboardInterrupt as e:
                frameinfo = getframeinfo(currentframe());
                self.logger.error('in loop: Keyboard interrupt');
        
    def stop(self):
        """
        Stop the daemon and closes all sockets.
        """
        for name, sock in self.connected_masters.items():
            sock.close();
        for knx in self.connected_knx:
            knx.close();
        self.knx_sock.close();

    def connect_to_masters(self):
        """
        Stored every device on network which have his hostname beginning by "MD3" and stores it
        in the self.connected_masters dict(), with hostnames as keys and sockets freshly open as values.
        """
        hostname = socket.gethostname()
        self.connected_masters = {};
        for host in self._hostlist:
            if MASTER_NAME_PREFIX in host._Hostname or str(host._IpAddr) == '127.0.0.1':
                if not self.connect_port:
                    self.logger.error('in connect_to_masters: No '+SLAVE_CONF_CONNECT_PORT_ENTRY+' in '+SLAVE_CONF_CONNECT_SECTION+' section or maybe no such '+SLAVE_CONF_CONNECT_SECTION+' defined');
                    sys.exit(1);
                try:
                    self.logger.debug('Connecting to '+str(host._IpAddr)+':'+str(self.connect_port));
                    sock = socket.create_connection((host._IpAddr, self.connect_port));
                    hostname = host._Hostname.split('.')[0];
                    self.connected_masters[host._Hostname] = sock;
                except Exception as e:
                    frameinfo = getframeinfo(currentframe());
                    self.logger.error('in connect_to_masters: '+str(e));
                    pass;

    def send_knx_data_to_masters(self, data):
        """
        Converts 'data' from bytes to a clear KNX datagran, and sends it to available slaves.
        """
        ctrl = int(data[0]);
        src_addr = int.from_bytes(data[1:3], byteorder='big');
        dst_addr = int.from_bytes(data[3:5], byteorder='big');
        data_len = int.from_bytes(data[5:6], byteorder='big');
        telegram_data = data[6:7 + data_len];
        typ = -1;
        value = 0;
        if telegram_data[1] & 0xC0 == 0x00:             # read
            typ = 0;
        elif telegram_data[1] & 0xC0 == 0x40:           # resp
            typ = 1;
            if data_len == 2:
                value = int(telegram_data[1] & 0x0f);
            elif data_len > 2:
                value = int.from_bytes(telegram_data[2:data_len], byteorder='big');
        elif telegram_data[1] & 0xC0 == 0x80:           # write
            typ = 2;
            if data_len == 2:
                value = int(telegram_data[1] & 0x0f);
            elif data_len > 2:
                typ = 3;
                value = int.from_bytes(telegram_data[2:data_len], byteorder='big');
        json_str = json.JSONEncoder().encode(
            {
                "packet_type": "monitor_knx",
                "type": typ,
                "src_addr": individual2string(src_addr),
                "dst_addr": group2string(dst_addr),
                "date": str(time.time()).split('.')[0],
                "value": value,
                "sender_name": socket.gethostname()
            }
        );
        self.send_data_to_all_masters(json_str);

    def send_enocean_data_to_masters(self, data):
        """
        Converts 'data' from bytes to a clear EnOcean datagran, and sends it to available slaves.
        """
        if (data[4] == PACKET_TYPE_RADIO_ERP1): # si le packet_type == radio_erp1
            data_len = int.from_bytes(data[1:2], byteorder='big');
            opt_data_len = int(data[3]);
            src_str = "%X" % int.from_bytes(data[1+data_len:5+data_len], byteorder='big');
            if len(src_str) < 8:
                src_str = "0"+src_str;
            json_dict = {
                "packet_type": "monitor_enocean",
                "src_addr": src_str,
                "dst_addr": "%X" % int.from_bytes(data[261:265 + opt_data_len], byteorder='big'),
                "date": str(time.time()).split('.')[0],
                "sender_name": socket.gethostname(),
                "type": int(data[6])
            };
            if data[6] == RORG_NORMAL:
                json_dict['value'] = int(data[7]);
            elif data[6] == RORG_TEMPERATURE:
                json_dict['value'] = float(40 - ((40 / 255) * int(data[9])));
            json_str = json.JSONEncoder().encode(json_dict);
            self.send_data_to_all_masters(json_str);

    def send_data_to_all_masters(self, json_str):
        """
        Sends a string 'json_str' to available slaves on network.
        """
        self.connect_to_masters();
        # ici envoyer a tous les masters
        for name in self.connected_masters.keys():
            try:
                master = self.connected_masters[name];
                AES.key_size = 32;
                aes_IV = AESManager.get_IV();
                encode_obj = AES.new(self.private_aes, AES.MODE_CBC, aes_IV);
                spaces = 16 - len(json_str) % 16;
                data2 = encode_obj.encrypt(json_str + (spaces * ' '));
                master.send(bytes(aes_IV, 'utf-8') + data2);
                master.close();
            except KeyError as e:
                self.logger.error('in send_data_to_all_masters: '+str(e));
                print(e);
                pass;

    def send_tech(self, json_obj, connection):
        json_str = json.JSONEncoder().encode(
            {
                "packet_type": "send_tech",
                "info": GLManager.TechInfo()
            }
        );
        self.send_data_to_all_masters(json_str);

    def send_alive(self, json_obj, connection):
        json_str = json.JSONEncoder().encode(
            {
                "packet_type": "send_alive",
                "info": GLManager.TechAlive()
            }
        );
        self.send_data_to_all_masters(json_str);

    def send_interfaces(self, json_obj, connection):
        try:
            if os.path.exists('/tmp/knxd'):
                call(['service', 'knxd', 'stop']);
            previous_val_knx = self._parser.getValueFromSection('knx', 'interface');
            previous_val_EnOcean = self._parser.getValueFromSection('enocean', 'interface');
            new_val = str(json_obj['interface_arg_knx'])
            self._parser.writeValueFromSection('knx', 'interface', new_val);
            self._parser.writeValueFromSection('knx', 'activated', str(json_obj['daemon_knx']));
            self._parser.writeValueFromSection('enocean', 'interface', str(json_obj['interface_arg_EnOcean']));
            if not previous_val_knx or previous_val_knx is None:
                call(['update-rc.d', 'knxd', 'defaults']);
                call(['update-rc.d', 'knxd', 'enable']);
            if not new_val or new_val is None:
                Popen(['systemctl', '-q', 'disable', 'knxd']);
            else:
                knx_edit = 'KNXD_OPTS="-e 1.0.254 -D -T -S -b ';
                if json_obj['interface_knx'] == 'tpuarts':
                    knx_edit += json_obj['interface_knx']+':/dev/'+new_val+'"';
                else:
                    knx_edit += json_obj['interface_knx']+':'+new_val+'"';
                conf_knx = open('/etc/knxd.conf', 'w');
                conf_knx.write(knx_edit+'\n');
                conf_knx.close();
                call(['service', 'knxd', 'start']);
                if json_obj['daemon_knx'] == 1:
                    if os.path.exists('/var/run/monitor_knx.pid'):
                        os.remove('/var/run/monitor_knx.pid');
                    Popen(['monitor_knx', 'ip:localhost', '--daemon']);
        except Exception as e:
            self.logger.error(e);
        json_str = '{"packet_type": "send_interfaces", "aes_pass": "******"}';
        master_hostname = str(json_obj['sender_name']);
        encrypt_IV = AESManager.get_IV();
        spaces = 16 - len(json_str) % 16;
        json_str = json_str + (spaces * ' ');
        encode_obj = AES.new(self.private_aes, AES.MODE_CBC, encrypt_IV);
        data = encode_obj.encrypt(json_str);
        connection.send(bytes(encrypt_IV, 'utf-8') + data);
        if previous_val_EnOcean != str(json_obj['interface_arg_EnOcean']):
            call(['service', 'domoslave', 'restart']);

    def shutdown_d3(self, json_obj, connection):
        call(['poweroff']);

    def reboot_d3(self, json_obj, connection):
        call(['reboot']);

    def wifi_init(self, ssid, password, security, mode, opt):
        try:
            ps_process = Popen(["ps", "-x"], stdout=PIPE);
            res = Popen(["grep", "hostapd"], stdin=ps_process.stdout, stdout=PIPE);
            res = res.stdout.read().decode().split("\n")[0].split(' ');
            ps_process.stdout.close();
            if res:
                while ('' in res):
                    res.remove('');
                call(['kill', '-9', res[0]]);
            ps_process = Popen(["ps", "-x"], stdout=PIPE);
            res = Popen(["grep", "wpa_supplicant"], stdin=ps_process.stdout, stdout=PIPE);
            res = res.stdout.read().decode().split("\n")[0].split(' ');
            ps_process.stdout.close();
            if res:
                while ('' in res):
                    res.remove('');
                call(['kill', '-9', res[0]]);
            call(['ifconfig', 'wlan0', 'down']);
            if mode == WIFI_MODE_DISABLED:
                if opt == 1:
                    call(['service', 'dnsmasq', 'stop']);
            elif mode == WIFI_MODE_CLIENT:
                call(['ifconfig', 'wlan0', 'up']);
                if opt == 1:
                    call(['service', 'dnsmasq', 'stop']);
                conf_file = open('/etc/network/interfaces', 'w');
                conf_str = ''.join(['auto lo\niface lo inet loopback\n\nallow-hotplug eth0\n',
                      'iface eth0 inet dhcp\n\nallow-hotplug usb0\niface usb0 inet dhcp\n\n',
                      'auto wlan0\niface wlan0 inet dhcp\n\twpa-conf ', WPA_SUPPLICANT_CONF_FILE, '\n']);
                conf_file.write(conf_str);
                conf_file.close();
                conf_file = open(WPA_SUPPLICANT_CONF_FILE, 'w');
                conf_str = ''.join(['ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev\n',
                      'update_config=1\nctrl_interface_group=0\neapol_version=1\n',
                      'ap_scan=1\n fast_reauth=1\n\n\nnetwork={\n\tdisabled=0\n',
                      '\tssid="', ssid, '"\n\tscan_ssid=0\n\tpriority=1\n']);
                if security == WIFI_SECURITY_WPA:
                    conf_str += ('\tproto=WPA\n\tkey_mgmt=WPA-PSK\n\tauth_alg=OPEN\n'+
                          '\tpairwise=TKIP CCMP\n\tgroup=TKIP CCMP\n\tpsk="'+password+'"\n');
                elif security == WIFI_SECURITY_WPA2:
                    conf_str += ('\tproto=RSN\n\tkey_mgmt=WPA-PSK\n\tauth_alg=OPEN\n\tpairwise=CCMP TKIP\n'+
                                   '\tgroup=CCMP TKIP\n\tpsk="'+password+'"\n');
                elif security == WIFI_SECURITY_WEP:
                    conf_str += '\tkey_mgmt=NONE\n\tauth_alg=SHARED\n';
                    if len(password) == 5 or len(password) == 10:
                        conf_str += '\tgroup=WEP40\n';
                    elif len(password) == 13 or len(password) == 26:
                        conf_str += '\tgroup=WEP104\n';
                    else:
                        conf_str += '\tgroup=WEP40 WEP104\n';
                    conf_str += '\twep_key0="'+password+'"\n\twep_tx_keyidx=0\n';
                conf_str += '\tpriority=1\n}\n';
                conf_file.write(conf_str);
                conf_file.close();
                call(['wpa_supplicant', '-Dnl80211', '-iwlan0', '-c' + WPA_SUPPLICANT_CONF_FILE , '-B']);
                call(['dhclient', 'wlan0']);
            elif mode == WIFI_MODE_ACCESS_POINT:
                call(['ifconfig', 'wlan0', '172.16.0.1', 'netmask', '255.255.255.0', 'up']);
                conf_file = open(HOSTAPD_CONF_FILE, 'w');
                conf_str = ''.join(['interface=wlan0\n\ndriver=nl80211\n\nssid=', ssid, '\n\n',
                      'hw_mode=g\n\nieee80211n=1\n\nchannel=6\n\nbeacon_int=100\n\n',
                      'dtim_period=2\n\nmax_num_sta=255\n\nrts_threshold=2347\n\n',
                      'fragm_threshold=2346\n\nmacaddr_acl=0\n\n']);
                if security == WIFI_SECURITY_WPA:
                    conf_str += ('auth_algs=1\n\nwpa=1\n\nwpa_passphrase='+password+'\n\n'+
                                 'wpa_key_mgmt=WPA-PSK\n\nwpa_pairwise=TKIP\n');
                elif security == WIFI_SECURITY_WPA2:
                    conf_str += ('auth_algs=1\n\nwpa=2\n\nwpa_passphrase='+password+'\n\n'+
                                 'wpa_key_mgmt=WPA-PSK\n\nwpa_pairwise=CCMP\n\nrsn_pairwise=CCMP\n');
                else:
                    self.logger.error('Wifi security = Unknown');
                conf_file.write(conf_str);
                conf_file.close();
                if opt == 1:
                    conf_file = open(DNSMASQ_CONF_FILE, 'w');
                    conf_str = 'domain-needed\ninterface=wlan0\ndhcp-range=172.16.0.2,172.16.0.254,12h\n';
                    conf_file.write(conf_str);
                    conf_file.close();
                    call(['service', 'dnsmasq', 'restart']);
                call(['iptables', '-t', 'nat', '-A', 'POSTROUTING', '-j', 'MASQUERADE']);
                call(['hostapd', HOSTAPD_CONF_FILE, '-B']);
            else:
                call(['ifconfig', 'wlan0', 'up']);
                self.logger.error('Wifi mode = Unknown');
        except Exception as e:
            self.logger.error(e);

    def wifi_update(self, json_obj, connection):
        try:
            self._parser.writeValueFromSection('wifi', 'ssid', json_obj['ssid']);
            self._parser.writeValueFromSection('wifi', 'password', json_obj['password']);
            self._parser.writeValueFromSection('wifi', 'encryption', json_obj['security']);
            self._parser.writeValueFromSection('wifi', 'mode', json_obj['mode']);
            self.wifi_init(json_obj['ssid'], json_obj['password'], json_obj['security'], json_obj['mode'], 1);
        except Exception as e:
            self.logger.error(e);
        json_str = '{"packet_type": "wifi_update", "aes_pass": "******"}';
        master_hostname = str(json_obj['sender_name']);
        encrypt_IV = AESManager.get_IV();
        spaces = 16 - len(json_str) % 16;
        json_str = json_str + (spaces * ' ');
        encode_obj = AES.new(self.private_aes, AES.MODE_CBC, encrypt_IV);
        data = encode_obj.encrypt(json_str);
        connection.send(bytes(encrypt_IV, 'utf-8') + data);
Example #37
0
class SlaveDaemon:

    ## The constructor
    #
    # @param log_flag The flag saying whether the logs should be printing or not
    def __init__(self, log_flag):
        ## Logger object for formatting and printing logs
        self.logger = Logger(log_flag, LOG_FILE)
        self.logger.info('Started Domoleaf Slave daemon')
        ## Array of master daemon on local network
        self.connected_masters = {}
        ## Array of monitor KNX on local network
        self.connected_knx = []
        ## Array of monitor EnOcean on local network
        self.connected_enocean = []
        ## Array of cron running on the system
        self.connected_cron = []
        self._scanner = Scanner(log_flag)
        self._hostlist = []
        myhostname = socket.gethostname().upper()
        if SLAVE_NAME_PREFIX in myhostname:
            self._scanner.scan()
            self._hostlist = self._scanner._HostList
        else:
            self._hostlist.append(Host('', '127.0.0.1', myhostname))
        self._parser = DaemonConfigParser(SLAVE_CONF_FILE)
        ## Keys for encrypting communications
        self.encrypt_keys = {}
        ## Main socket for communication with KNX daemon
        self.knx_sock = None
        ## Main socket for communication with master daemon
        self.master_sock = None
        ## Main socket for communication with enocean daemon
        self.enocean_sock = None
        ## Main socket for communication with cron
        self.cron_sock = None
        ## Private AES key got from configuration file
        self.private_aes = self._parser.getValueFromSection(
            'personnal_key', 'aes')
        self.wifi_init(self._parser.getValueFromSection('wifi', 'ssid'),
                       self._parser.getValueFromSection('wifi', 'password'),
                       self._parser.getValueFromSection('wifi', 'encryption'),
                       self._parser.getValueFromSection('wifi', 'mode'), 0)
        ## Port on which connect got from configuration file
        self.connect_port = self._parser.getValueFromSection(
            SLAVE_CONF_CONNECT_SECTION, SLAVE_CONF_CONNECT_PORT_ENTRY)
        ## Callback array indexed on packet type
        self.functions = {
            KNX_READ_REQUEST: self.knx_read_request,
            KNX_WRITE_SHORT: self.knx_write_short,
            KNX_WRITE_LONG: self.knx_write_long,
            KNX_WRITE_TEMP: self.knx_write_temp,
            CHECK_SLAVE: self.check_slave,
            MONITOR_IP: self.monitor_ip,
            DATA_UPDATE: self.update,
            SEND_TECH: self.send_tech,
            SEND_ALIVE: self.send_alive,
            SEND_INTERFACES: self.send_interfaces,
            SHUTDOWN_D3: self.shutdown_d3,
            REBOOT_D3: self.reboot_d3,
            WIFI_UPDATE: self.wifi_update
        }

    ## Updates the base system of the slave daemon.
    #
    # @param json_obj Not used here.
    # @param connection Not used here.
    # @return None
    def update(self, json_obj, connection):
        p = call(['dpkg', '--configure', '-a'])
        call(['apt-get', 'update'])
        call([
            'DEBIAN_FRONTEND=noninteractive', 'apt-get', 'install',
            'domoslave', '-y'
        ])
        version = os.popen(
            "dpkg-query -W -f='${Version}\n' domoslave").read().split('\n')[0]
        json_str = '{"packet_type": "update_finished", "aes_pass": "******", "new_version": ' + version + '}'
        encrypt_IV = AESManager.get_IV()
        spaces = 16 - len(json_str) % 16
        json_str = json_str + (spaces * ' ')
        encode_obj = AES.new(self.private_aes, AES.MODE_CBC, encrypt_IV)
        data = encode_obj.encrypt(json_str)
        # faut ouvrir une nouvelle socket pour envoyer la nouvelle version
        # connection.send(bytes(encrypt_IV, 'utf-8') + data);

    ## Initializes the sockets for listenning incomming connections.
    #
    # @return None
    def run(self):
        ## Run flag at True for running, at False to stop the main loop
        self.run = True
        self.knx_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.master_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.enocean_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.cron_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.knx_sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
        self.master_sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
        self.enocean_sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
        self.cron_sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
        port = self._parser.getValueFromSection(SLAVE_CONF_KNX_SECTION,
                                                SLAVE_CONF_KNX_PORT_ENTRY)
        if not port:
            sys.exit(2)
        port_master = self._parser.getValueFromSection(
            SLAVE_CONF_LISTEN_SECTION, SLAVE_CONF_LISTEN_PORT_ENTRY)
        if not port_master:
            sys.exit(2)
        port_enocean = self._parser.getValueFromSection(
            SLAVE_CONF_ENOCEAN_SECTION, SLAVE_CONF_ENOCEAN_PORT_ENTRY)
        if not port_enocean:
            sys.exit(2)
        port_cron = self._parser.getValueFromSection(
            SLAVE_CONF_CRON_SECTION, SLAVE_CONF_CRON_PORT_ENTRY)
        if not port_cron:
            sys.exit(2)
        self.knx_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.master_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.enocean_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.cron_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.knx_sock.bind(('', int(port)))
        self.master_sock.bind(('', int(port_master)))
        self.enocean_sock.bind(('', int(port_enocean)))
        self.cron_sock.bind(('127.0.0.1', int(port_cron)))
        self.knx_sock.listen(MAX_KNX)
        self.master_sock.listen(MAX_MASTERS)
        self.enocean_sock.listen(MAX_ENOCEAN)
        self.cron_sock.listen(MAX_CRON)
        self.send_monitor_ip()
        self.loop()

    ## Gets available sockets for reading on the KNX socket.
    #
    # @return None
    def accept_knx(self):
        rlist, wlist, elist = select.select([self.knx_sock], [], [],
                                            SELECT_TIMEOUT)
        append = self.connected_knx.append
        for connection in rlist:
            new_knx, addr = connection.accept()
            append(new_knx)
        self.receive_from_knx(self.connected_knx)

    ## Gets available sockets for reading on the master socket.
    #
    # @return None
    def accept_masters(self):
        rlist, wlist, elist = select.select([self.master_sock], [], [],
                                            SELECT_TIMEOUT)
        masters_socks = []
        append = masters_socks.append
        for item in rlist:
            new_conn, addr = item.accept()
            append(new_conn)
        self.receive_from_masters(masters_socks)

    ## Gets available sockets for reading on the EnOcean socket.
    #
    # @return None
    def accept_enocean(self):
        rlist, wlist, elist = select.select([self.enocean_sock], [], [],
                                            SELECT_TIMEOUT)
        enocean_socks = []
        append = enocean_socks.append
        append_connected = self.connected_enocean.append
        for item in rlist:
            new_conn, addr = item.accept()
            append(new_conn)
            append_connected(new_conn)
        self.receive_from_enocean(enocean_socks)

    ## Gets available sockets for reading on the Cron socket.
    #
    # @return None
    def accept_cron(self):
        rlist, wlist, elist = select.select([self.cron_sock], [], [],
                                            SELECT_TIMEOUT)
        cron_socks = []
        append = cron_socks.append
        append_connected = self.connected_cron.append
        for item in rlist:
            new_conn, addr = item.accept()
            append(new_conn)
            append_connected(new_conn)
        self.receive_from_cron(cron_socks)

    ## Checks the packet_type of the data, and calls the appropriate function.
    #
    # @param data Packet data including the packet type.
    # @param connection Connection object sent to the function.
    # @return None
    def parse_data(self, data, connection):
        json_obj = json.JSONDecoder().decode(data)
        if json_obj['packet_type'] in self.functions.keys():
            self.functions[json_obj['packet_type']](json_obj, connection)
        else:
            raise Exception(
                str(json_obj['packet_type']) + ": is not a valid packet type")

    ## System call of 'groupread' with parameters.
    #
    # @param json_obj JSON Object containing the address to read.
    # @param connection Not used here.
    # @return None
    def knx_read_request(self, json_obj, connection):
        call(['knxtool', CALL_GROUPREAD, EIB_URL, json_obj['addr_to_read']])

    ## System call of "groupwrite" with parameters.
    #
    # @param json_obj JSON Object containing the address to which send, and the values to send.
    # @param connection Not used here.
    # @return None
    def knx_write_temp(self, json_obj, connection):
        val = json_obj['value'].split(' ')
        call([
            'knxtool', CALL_GROUPWRITE, EIB_URL, json_obj['addr_to_send'],
            val[0], val[1]
        ])

    ## System call of "groupswrite" with parameters.
    #
    # @param json_obj JSON Object containing the address to which send, and the value to send.
    # @param connection Not used here.
    # @return None
    def knx_write_short(self, json_obj, connection):
        call([
            'knxtool', CALL_GROUPSWRITE, EIB_URL, json_obj['addr_to_send'],
            str(json_obj['value'])
        ])

    ## System call of "groupwrite" with parameters.
    #
    # @param json_obj JSON Object containing the address to which send, and the value to send.
    # @param connection Not used here.
    # @return None
    def knx_write_long(self, json_obj, connection):
        call([
            'knxtool', CALL_GROUPWRITE, EIB_URL, json_obj['addr_to_send'],
            str(json_obj['value'])
        ])

    ## Reads data comming from amsters and calls parse_data().
    #
    # @param masters_to_read Array containing the sockets of all the masters found on local network.
    # @return None
    def receive_from_masters(self, masters_to_read):
        for master in masters_to_read:
            data = master.recv(4096)
            decrypt_IV = data[:16].decode()
            decode_obj = AES.new(self.private_aes, AES.MODE_CBC, decrypt_IV)
            data2 = decode_obj.decrypt(data[16:])
            self.parse_data(data2.decode(), master)
            master.close()

    ## Reads data from monitor KNX and transmits to master.
    #
    # @param knx_to_read Array containing the sockets of all the KNX daemons on local network.
    # @return None
    def receive_from_knx(self, knx_to_read):
        for knx in knx_to_read:
            data = knx.recv(TELEGRAM_LENGTH)
            if data:
                self.send_knx_data_to_masters(data)
            if knx in self.connected_knx:
                knx.close()
                self.connected_knx.remove(knx)

    ## Reads data from monitor EnOcean and transmits to master.
    #
    # @param enocean_to_read Array containing the sockets of all the EnOcean daemons on local network.
    # @return None
    def receive_from_enocean(self, enocean_to_read):
        for enocean in enocean_to_read:
            data = enocean.recv(4096)
            if data:
                self.send_enocean_data_to_masters(data)
            if enocean in self.connected_enocean:
                enocean.close()
                self.connected_enocean.remove(enocean)

    ## Receives data from a cron and executes it.
    #
    # @param cron_to_read Array containing the sockets of all crons on local network.
    # @return None
    def receive_from_cron(self, cron_to_read):
        for cron in cron_to_read:
            data = cron.recv(4096)
            if data:
                json_str = json.JSONEncoder().encode(
                    {"packet_type": data.decode()})
                self.parse_data(json_str, cron)
            if cron in self.connected_cron:
                cron.close()
                self.connected_cron.remove(cron)

    ## Checks the existence of this daemon.
    # This function is called when a check_slave packet is received.
    #
    # @param json_obj JSON Object containing the hostname of the sender of the packet.
    # @param connection Connection object used to send the response.
    # @return None
    def check_slave(self, json_obj, connection):
        interface_knx = self._parser.getValueFromSection(
            SLAVE_CONF_KNX_SECTION, SLAVE_CONF_KNX_INTERFACE)
        interface_enocean = self._parser.getValueFromSection(
            SLAVE_CONF_ENOCEAN_SECTION, SLAVE_CONF_ENOCEAN_INTERFACE)
        version = os.popen(
            "dpkg-query -W -f='${Version}\n' domoslave").read().split('\n')[0]
        json_str = '{"packet_type": "check_slave", "aes_pass": "******", "version": "' + version + '", "interface_knx": "' + interface_knx + '", "interface_enocean": "' + interface_enocean + '"}'
        master_hostname = str(json_obj['sender_name'])
        encrypt_IV = AESManager.get_IV()
        spaces = 16 - len(json_str) % 16
        json_str = json_str + (spaces * ' ')
        encode_obj = AES.new(self.private_aes, AES.MODE_CBC, encrypt_IV)
        data = encode_obj.encrypt(json_str)
        connection.send(bytes(encrypt_IV, 'utf-8') + data)

    ## Re scans the local network and refreshes hostlist.
    #
    # @param json_obj Not used here.
    # @param connection Not used here.
    # @return None
    def monitor_ip(self, json_obj, connection):
        self._scanner.scan()
        self._hostlist = self._scanner._HostList

    ## Sends a monitor_ip packet to all the masters available.
    #
    # @return None
    def send_monitor_ip(self):
        json_str = json.JSONEncoder().encode({"packet_type": "monitor_ip"})
        self.send_data_to_all_masters(json_str)

    ## Main daemon loop.
    #
    # @return None
    def loop(self):
        while self.run:
            try:
                self.accept_knx()
            except Exception as e:
                frameinfo = getframeinfo(currentframe())
                self.logger.error('in loop accept_knx: ' + str(e))
            except KeyboardInterrupt as e:
                frameinfo = getframeinfo(currentframe())
                self.logger.error('in loop: Keyboard interrupt')
            try:
                self.accept_masters()
            except Exception as e:
                frameinfo = getframeinfo(currentframe())
                self.logger.error('in loop accept_masters: ' + str(e))
            except KeyboardInterrupt as e:
                frameinfo = getframeinfo(currentframe())
                self.logger.error('in loop: Keyboard interrupt')
            try:
                self.accept_enocean()
            except Exception as e:
                frameinfo = getframeinfo(currentframe())
                self.logger.error('in loop accept_enocean: ' + str(e))
            except KeyboardInterrupt as e:
                frameinfo = getframeinfo(currentframe())
                self.logger.error('in loop: Keyboard interrupt')
            try:
                self.accept_cron()
            except Exception as e:
                frameinfo = getframeinfo(currentframe())
                self.logger.error('in loop accept_cron: ' + str(e))
            except KeyboardInterrupt as e:
                frameinfo = getframeinfo(currentframe())
                self.logger.error('in loop: Keyboard interrupt')

    ## Stops the daemon and closes all sockets.
    #
    # @return None
    def stop(self):
        for name, sock in self.connected_masters.items():
            sock.close()
        for knx in self.connected_knx:
            knx.close()
        self.knx_sock.close()

    ## Stores every host on local network if its hostname begins by 'MD3' in connected_masters dict().
    #
    # @return None
    def connect_to_masters(self):
        hostname = socket.gethostname()
        self.connected_masters = {}
        for host in self._hostlist:
            if MASTER_NAME_PREFIX in host._Hostname or str(
                    host._IpAddr) == '127.0.0.1':
                if not self.connect_port:
                    self.logger.error('in connect_to_masters: No ' +
                                      SLAVE_CONF_CONNECT_PORT_ENTRY + ' in ' +
                                      SLAVE_CONF_CONNECT_SECTION +
                                      ' section or maybe no such ' +
                                      SLAVE_CONF_CONNECT_SECTION + ' defined')
                    sys.exit(1)
                try:
                    self.logger.debug('Connecting to ' + str(host._IpAddr) +
                                      ':' + str(self.connect_port))
                    sock = socket.create_connection(
                        (host._IpAddr, self.connect_port))
                    hostname = host._Hostname.split('.')[0]
                    self.connected_masters[host._Hostname] = sock
                except Exception as e:
                    frameinfo = getframeinfo(currentframe())
                    self.logger.error('in connect_to_masters: ' + str(e))
                    pass

    ## Converts data from bytes to a clear KNX datagram, and sends it to all available masters.
    #
    # @param data The data having to be converted from bytes to clear KNX datagram.
    # @return None
    def send_knx_data_to_masters(self, data):
        ctrl = int(data[0])
        src_addr = int.from_bytes(data[1:3], byteorder='big')
        dst_addr = int.from_bytes(data[3:5], byteorder='big')
        data_len = int.from_bytes(data[5:6], byteorder='big')
        telegram_data = data[6:7 + data_len]
        typ = -1
        value = 0
        if telegram_data[1] & 0xC0 == 0x00:  # read
            typ = 0
        elif telegram_data[1] & 0xC0 == 0x40:  # resp
            typ = 1
            if data_len == 2:
                value = int(telegram_data[1] & 0x0f)
            elif data_len > 2:
                value = int.from_bytes(telegram_data[2:data_len],
                                       byteorder='big')
        elif telegram_data[1] & 0xC0 == 0x80:  # write
            typ = 2
            if data_len == 2:
                value = int(telegram_data[1] & 0x0f)
            elif data_len > 2:
                typ = 3
                value = int.from_bytes(telegram_data[2:data_len],
                                       byteorder='big')
        json_str = json.JSONEncoder().encode({
            "packet_type":
            "monitor_knx",
            "type":
            typ,
            "src_addr":
            individual2string(src_addr),
            "dst_addr":
            group2string(dst_addr),
            "date":
            str(time.time()).split('.')[0],
            "value":
            value,
            "sender_name":
            socket.gethostname()
        })
        self.send_data_to_all_masters(json_str)

    ## Convertes data from bytes to a clear EnOcean datagram, and sends it to available masters.
    #
    # @param data The data having to be converted from bytes to EnOcean datagram.
    # @return None
    def send_enocean_data_to_masters(self, data):
        if (data[4] == PACKET_TYPE_RADIO_ERP1
            ):  # si le packet_type == radio_erp1
            data_len = int.from_bytes(data[1:2], byteorder='big')
            opt_data_len = int(data[3])
            src_str = "%X" % int.from_bytes(data[1 + data_len:5 + data_len],
                                            byteorder='big')
            if len(src_str) < 8:
                src_str = "0" + src_str
            json_dict = {
                "packet_type":
                "monitor_enocean",
                "src_addr":
                src_str,
                "dst_addr":
                "%X" %
                int.from_bytes(data[261:265 + opt_data_len], byteorder='big'),
                "date":
                str(time.time()).split('.')[0],
                "sender_name":
                socket.gethostname(),
                "type":
                int(data[6])
            }
            if data[6] == RORG_NORMAL:
                json_dict['value'] = int(data[7])
            elif data[6] == RORG_TEMPERATURE:
                json_dict['value'] = float(40 - ((40 / 255) * int(data[9])))
            json_str = json.JSONEncoder().encode(json_dict)
            self.send_data_to_all_masters(json_str)

    ## Sends data to all masters available on local network.
    #
    # @param json_str The data to send under form of a JSON Object stringified.
    # @return None
    def send_data_to_all_masters(self, json_str):
        self.connect_to_masters()
        for name in self.connected_masters.keys():
            try:
                master = self.connected_masters[name]
                AES.key_size = 32
                aes_IV = AESManager.get_IV()
                encode_obj = AES.new(self.private_aes, AES.MODE_CBC, aes_IV)
                spaces = 16 - len(json_str) % 16
                data2 = encode_obj.encrypt(json_str + (spaces * ' '))
                master.send(bytes(aes_IV, 'utf-8') + data2)
                master.close()
            except KeyError as e:
                self.logger.error('in send_data_to_all_masters: ' + str(e))
                pass

    ## Sends the informations about the slave to all masters available.
    #
    # @param json_obj Not used here.
    # @param connection Not used here.
    # @return None
    def send_tech(self, json_obj, connection):
        json_str = json.JSONEncoder().encode({
            "packet_type": "send_tech",
            "info": GLManager.TechInfo()
        })
        self.send_data_to_all_masters(json_str)

    ## Sends that the salve daemon is alive to all masters available.
    #
    # @param json_obj Not used here.
    # @param connection Not used here.
    # @return None
    def send_alive(self, json_obj, connection):
        json_str = json.JSONEncoder().encode({
            "packet_type": "send_alive",
            "info": GLManager.TechAlive()
        })
        self.send_data_to_all_masters(json_str)

    ## Sends the protocol interface informations to all masters available.
    #
    # @param json_obj Protocol interface informations.
    # @param connection Connection object used to send the response.
    # @return None
    def send_interfaces(self, json_obj, connection):
        try:
            if os.path.exists('/tmp/knxd'):
                call(['service', 'knxd', 'stop'])
            previous_val_knx = self._parser.getValueFromSection(
                'knx', 'interface')
            previous_val_EnOcean = self._parser.getValueFromSection(
                'enocean', 'interface')
            new_val = str(json_obj['interface_arg_knx'])
            self._parser.writeValueFromSection('knx', 'interface', new_val)
            self._parser.writeValueFromSection('knx', 'activated',
                                               str(json_obj['daemon_knx']))
            self._parser.writeValueFromSection(
                'enocean', 'interface', str(json_obj['interface_arg_EnOcean']))
            if not previous_val_knx or previous_val_knx is None:
                call(['update-rc.d', 'knxd', 'defaults'])
                call(['update-rc.d', 'knxd', 'enable'])
            if not new_val or new_val is None:
                Popen(['systemctl', '-q', 'disable', 'knxd'])
            else:
                knx_edit = 'KNXD_OPTS="-e 1.0.254 -D -T -S -b '
                if json_obj['interface_knx'] == 'tpuarts':
                    knx_edit += json_obj[
                        'interface_knx'] + ':/dev/' + new_val + '"'
                else:
                    knx_edit += json_obj['interface_knx'] + ':' + new_val + '"'
                conf_knx = open('/etc/knxd.conf', 'w')
                conf_knx.write(knx_edit + '\n')
                conf_knx.close()
                call(['service', 'knxd', 'start'])
                if json_obj['daemon_knx'] == 1:
                    if os.path.exists('/var/run/monitor_knx.pid'):
                        os.remove('/var/run/monitor_knx.pid')
                    Popen(['monitor_knx', 'ip:localhost', '--daemon'])
        except Exception as e:
            self.logger.error(e)
        json_str = '{"packet_type": "send_interfaces", "aes_pass": "******"}'
        master_hostname = str(json_obj['sender_name'])
        encrypt_IV = AESManager.get_IV()
        spaces = 16 - len(json_str) % 16
        json_str = json_str + (spaces * ' ')
        encode_obj = AES.new(self.private_aes, AES.MODE_CBC, encrypt_IV)
        data = encode_obj.encrypt(json_str)
        connection.send(bytes(encrypt_IV, 'utf-8') + data)
        if previous_val_EnOcean != str(json_obj['interface_arg_EnOcean']):
            call(['service', 'domoslave', 'restart'])

    ## Shuts down the slave D3.
    #
    # @param json_obj Not used here.
    # @param connection Not used here.
    # @return None
    def shutdown_d3(self, json_obj, connection):
        call(['poweroff'])

    ## Reboots the slave D3.
    #
    # @param json_obj Not used here.
    # @param connection Not used here.
    # @return None
    def reboot_d3(self, json_obj, connection):
        call(['reboot'])

    ## Initializes the wifi protocol.
    #
    # @param ssid The SSID of the network.
    # @param password The password to connect to the network.
    # @param security The security type of the connection.
    # @param mode The mode of the initialization of the network interface.
    # @param opt Flag to do some more stuff if it is 1.
    # @return None
    def wifi_init(self, ssid, password, security, mode, opt):
        try:
            ps_process = Popen(["ps", "-x"], stdout=PIPE)
            res = Popen(["grep", "hostapd"],
                        stdin=ps_process.stdout,
                        stdout=PIPE)
            res = res.stdout.read().decode().split("\n")[0].split(' ')
            ps_process.stdout.close()
            if res:
                while ('' in res):
                    res.remove('')
                call(['kill', '-9', res[0]])
            ps_process = Popen(["ps", "-x"], stdout=PIPE)
            res = Popen(["grep", "wpa_supplicant"],
                        stdin=ps_process.stdout,
                        stdout=PIPE)
            res = res.stdout.read().decode().split("\n")[0].split(' ')
            ps_process.stdout.close()
            if res:
                while ('' in res):
                    res.remove('')
                call(['kill', '-9', res[0]])
            call(['ifconfig', 'wlan0', 'down'])
            if mode == WIFI_MODE_DISABLED:
                if opt == 1:
                    call(['service', 'dnsmasq', 'stop'])
            elif mode == WIFI_MODE_CLIENT:
                call(['ifconfig', 'wlan0', 'up'])
                if opt == 1:
                    call(['service', 'dnsmasq', 'stop'])
                conf_file = open('/etc/network/interfaces', 'w')
                conf_str = ''.join([
                    'auto lo\niface lo inet loopback\n\nallow-hotplug eth0\n',
                    'iface eth0 inet dhcp\n\nallow-hotplug usb0\niface usb0 inet dhcp\n\n',
                    'auto wlan0\niface wlan0 inet dhcp\n\twpa-conf ',
                    WPA_SUPPLICANT_CONF_FILE, '\n'
                ])
                conf_file.write(conf_str)
                conf_file.close()
                conf_file = open(WPA_SUPPLICANT_CONF_FILE, 'w')
                conf_str = ''.join([
                    'ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev\n',
                    'update_config=1\nctrl_interface_group=0\neapol_version=1\n',
                    'ap_scan=1\n fast_reauth=1\n\n\nnetwork={\n\tdisabled=0\n',
                    '\tssid="', ssid, '"\n\tscan_ssid=0\n\tpriority=1\n'
                ])
                if security == WIFI_SECURITY_WPA:
                    conf_str += (
                        '\tproto=WPA\n\tkey_mgmt=WPA-PSK\n\tauth_alg=OPEN\n' +
                        '\tpairwise=TKIP CCMP\n\tgroup=TKIP CCMP\n\tpsk="' +
                        password + '"\n')
                elif security == WIFI_SECURITY_WPA2:
                    conf_str += (
                        '\tproto=RSN\n\tkey_mgmt=WPA-PSK\n\tauth_alg=OPEN\n\tpairwise=CCMP TKIP\n'
                        + '\tgroup=CCMP TKIP\n\tpsk="' + password + '"\n')
                elif security == WIFI_SECURITY_WEP:
                    conf_str += '\tkey_mgmt=NONE\n\tauth_alg=SHARED\n'
                    if len(password) == 5 or len(password) == 10:
                        conf_str += '\tgroup=WEP40\n'
                    elif len(password) == 13 or len(password) == 26:
                        conf_str += '\tgroup=WEP104\n'
                    else:
                        conf_str += '\tgroup=WEP40 WEP104\n'
                    conf_str += '\twep_key0="' + password + '"\n\twep_tx_keyidx=0\n'
                conf_str += '\tpriority=1\n}\n'
                conf_file.write(conf_str)
                conf_file.close()
                call([
                    'wpa_supplicant', '-Dnl80211', '-iwlan0',
                    '-c' + WPA_SUPPLICANT_CONF_FILE, '-B'
                ])
                call(['dhclient', 'wlan0'])
            elif mode == WIFI_MODE_ACCESS_POINT:
                call([
                    'ifconfig', 'wlan0', '172.16.0.1', 'netmask',
                    '255.255.255.0', 'up'
                ])
                conf_file = open(HOSTAPD_CONF_FILE, 'w')
                conf_str = ''.join([
                    'interface=wlan0\n\ndriver=nl80211\n\nssid=', ssid, '\n\n',
                    'hw_mode=g\n\nieee80211n=1\n\nchannel=6\n\nbeacon_int=100\n\n',
                    'dtim_period=2\n\nmax_num_sta=255\n\nrts_threshold=2347\n\n',
                    'fragm_threshold=2346\n\nmacaddr_acl=0\n\n'
                ])
                if security == WIFI_SECURITY_WPA:
                    conf_str += ('auth_algs=1\n\nwpa=1\n\nwpa_passphrase=' +
                                 password + '\n\n' +
                                 'wpa_key_mgmt=WPA-PSK\n\nwpa_pairwise=TKIP\n')
                elif security == WIFI_SECURITY_WPA2:
                    conf_str += (
                        'auth_algs=1\n\nwpa=2\n\nwpa_passphrase=' + password +
                        '\n\n' +
                        'wpa_key_mgmt=WPA-PSK\n\nwpa_pairwise=CCMP\n\nrsn_pairwise=CCMP\n'
                    )
                else:
                    self.logger.error('Wifi security = Unknown')
                conf_file.write(conf_str)
                conf_file.close()
                if opt == 1:
                    conf_file = open(DNSMASQ_CONF_FILE, 'w')
                    conf_str = 'domain-needed\ninterface=wlan0\ndhcp-range=172.16.0.2,172.16.0.254,12h\n'
                    conf_file.write(conf_str)
                    conf_file.close()
                    call(['service', 'dnsmasq', 'restart'])
                call([
                    'iptables', '-t', 'nat', '-A', 'POSTROUTING', '-j',
                    'MASQUERADE'
                ])
                call(['hostapd', HOSTAPD_CONF_FILE, '-B'])
            else:
                call(['ifconfig', 'wlan0', 'up'])
                self.logger.error('Wifi mode = Unknown')
        except Exception as e:
            self.logger.error(e)

    ## Updates the wifi informations.
    #
    # @param json_obj JSON Object containing all the informations for the wifi.
    # @param connection Connection object used to send the response.
    # @return None
    def wifi_update(self, json_obj, connection):
        try:
            self._parser.writeValueFromSection('wifi', 'ssid',
                                               json_obj['ssid'])
            self._parser.writeValueFromSection('wifi', 'password',
                                               json_obj['password'])
            self._parser.writeValueFromSection('wifi', 'encryption',
                                               json_obj['security'])
            self._parser.writeValueFromSection('wifi', 'mode',
                                               json_obj['mode'])
            self.wifi_init(json_obj['ssid'], json_obj['password'],
                           json_obj['security'], json_obj['mode'], 1)
        except Exception as e:
            self.logger.error(e)
        json_str = '{"packet_type": "wifi_update", "aes_pass": "******"}'
        master_hostname = str(json_obj['sender_name'])
        encrypt_IV = AESManager.get_IV()
        spaces = 16 - len(json_str) % 16
        json_str = json_str + (spaces * ' ')
        encode_obj = AES.new(self.private_aes, AES.MODE_CBC, encrypt_IV)
        data = encode_obj.encrypt(json_str)
        connection.send(bytes(encrypt_IV, 'utf-8') + data)
Example #38
0
class KNXManager:
    """
    KNX management class
    """
    def __init__(self, slave_keys):
        self.knx_function = {
            OPTION_ON_OFF       : self.send_knx_write_short_to_slave,
            OPTION_VAR          : self.send_knx_write_long_to_slave,
            OPTION_UP_DOWN      : self.send_knx_write_short_to_slave,
            OPTION_OPEN_CLOSE   : self.send_knx_write_short_to_slave,
            OPTION_STOP_UP_DOWN : self.send_knx_write_short_to_slave,
            OPTION_TEMPERATURE_W: self.send_knx_write_temp
        };
        self.sql = MasterSql();
        self._parser = DaemonConfigParser('/etc/greenleaf/master.conf');
        self.aes_slave_keys = slave_keys;

    def update_room_device_option(self, daemon_id, json_obj):
        """
        Update room_device_option table in database to set new values of the device described by 'json_obj'
        """
        if int(json_obj['type']) == KNX_RESPONSE:
            self.sql.update_room_device_option_resp(json_obj, daemon_id);
        elif int(json_obj['type']) == KNX_WRITE_SHORT:
            self.sql.update_room_device_option_write_short(json_obj, daemon_id);
        elif int(json_obj['type']) == KNX_WRITE_LONG:
            self.sql.update_room_device_option_write_long(json_obj, daemon_id);

    def protocol_knx(self, json_obj, dev, hostname):
        """
        KNX protocol data treatment function
        """
        new_obj = {
            "data": {
                "addr": str(dev['addr_dst']),
                "value": str(json_obj['data']['value'])
            }
        };
        self.knx_function[int(json_obj['data']['option_id'])](hostname, new_obj);

    def send_json_obj_to_slave(self, json_str, sock, hostname, aes_key, close_flag = True):
        """
        Send 'json_obj' to 'hostname' via 'sock'
        """
        hostname_key = '';
        if '.' in hostname:
            hostname_key = hostname.split('.')[0];
        else:
            hostname_key = hostname;
        AES.key_size = 32;
        aes_IV = AESManager.get_IV();
        encode_obj = AES.new(aes_key, AES.MODE_CBC, aes_IV);
        data2 = encode_obj.encrypt(json_str + (176 - len(json_str)) * ' ');
        sock.send(bytes(aes_IV, 'utf-8') + data2);
        if close_flag == True:
            sock.close();

    def send_knx_write_temp(self, hostname, json_obj):
        """
        Converts absolute value of temperature (Celsius) in 2 hexadecimal values for
        sending to KNX device
        """
        port = self._parser.getValueFromSection('connect', 'port');
        if not port:
            sys.exit(4);
        sock = socket.create_connection((hostname, port));
        val_str = json_obj['data']['value'];
        if '.' in val_str:
            val_str = val_str.split('.')[0];
        value = utils.convert_temperature_reverse(int(val_str));
        json_str = json.JSONEncoder().encode(
            {
                "packet_type": "knx_write_temp",
                "addr_to_send": json_obj['data']['addr'],
                "value": value[0] + ' ' + value[1]
            }
        );
        self.send_json_obj_to_slave(json_str, sock, hostname, self.aes_slave_keys[hostname]);

    def send_knx_write_long_to_slave(self, hostname, json_obj):
        """
        Constructs long write request and sends it to 'hostname'
        """
        port = self._parser.getValueFromSection('connect', 'port');
        if not port:
            sys.exit(4);
        sock = socket.create_connection((hostname, port));
        json_str = json.JSONEncoder().encode(
            {
                "packet_type": "knx_write_long",
                "addr_to_send": json_obj['data']['addr'],
                "value": hex(int(json_obj['data']['value']))
            }
        );
        self.send_json_obj_to_slave(json_str, sock, hostname, self.aes_slave_keys[hostname]);

    def send_knx_write_short_to_slave(self, hostname, json_obj):
        """
        Constructs short write request and sends it to 'hostname'
        """
        port = self._parser.getValueFromSection('connect', 'port');
        if not port:
            sys.exit(4);
        sock = socket.create_connection((hostname, port));
        json_str = json.JSONEncoder().encode(
            {
                "packet_type": "knx_write_short",
                "addr_to_send": json_obj['data']['addr'],
                "value": json_obj['data']['value']
            }
        );
        self.send_json_obj_to_slave(json_str, sock, hostname, self.aes_slave_keys[hostname]);

    def send_knx_read_request_to_slave(self, hostname, json_obj):
        """
        Constructs short read request and sends it to 'hostname'
        """
        port = self._parser.getValueFromSection('connect', 'port');
        if not port:
            sys.exit(4);
        sock = socket.create_connection((hostname, port));
        json_str = json.JSONEncoder().encode(
            {
                "packet_type": "knx_read_request",
                "addr_to_read": json_obj['data']['addr']
            }
        );
        self.send_json_obj_to_slave(json_str, sock, hostname, self.aes_slave_keys[hostname]);
Example #39
0
class SlaveDaemon:
    """
    Main slave class
    It does communication between different monitors (KNX, EnOcean... in C) and the masters (servers)
    """
    def __init__(self, log_flag):
        self.logger = Logger(log_flag, LOG_FILE);
        self.logger.info('Started Greenleaf Slave daemon');
        print('######## SLAVE DAEMON #######')
        self.connected_masters = {};
        self.connected_knx = [];
        self.connected_enocean = [];
        self.clients = [];
        self._scanner = Scanner(HOST_CONF_FILE);
        self._scanner.scan(False);
        self._hostlist = self._scanner._HostList;
        self._parser = DaemonConfigParser(SLAVE_CONF_FILE);
        self.encrypt_keys = {};
        self.knx_sock = None;
        self.master_sock = None;
        self.enocean_sock = None;
        self.private_aes = hashlib.md5(self._parser.getValueFromSection('personnal_key', 'aes').encode()).hexdigest();
        self.functions = {
            KNX_READ_REQUEST    : self.knx_read_request,
            KNX_WRITE_SHORT     : self.knx_write_short,
            KNX_WRITE_LONG      : self.knx_write_long,
            KNX_WRITE_TEMP      : self.knx_write_temp,
            CHECK_SLAVE         : self.check_slave,
            MONITOR_IP          : self.monitor_ip,
            DATA_UPDATE         : self.update
        };

    def update(self, json_obj, connection):
        call(['apt-get', 'update']);
        call(['apt-get', 'install', 'glslave', '-y']);
        version_file = open('/etc/greenleaf/.glslave.version', 'r');
        if not version_file:
            self.logger.error('/etc/greenleaf/.glslave.version: no such file or directory');
            print('/etc/greenleaf/.glslave.version: no such file or directory');
            return;
        version = version_file.read();
        if '\n' in version:
            version = version.split('\n')[0];
        json_str = '{"packet_type": "update_finished", "aes_pass": "******", "new_version": ' + version + '}'
        encrypt_IV = AESManager.get_IV();
        json_str = json_str + (' ' * (320 - len(json_str)))
        encode_obj = AES.new(self.private_aes, AES.MODE_CBC, encrypt_IV);
        data = encode_obj.encrypt(json_str);
        # faut ouvrir une nouvelle socket pour envoyer la nouvelle version
        # connection.send(bytes(encrypt_IV, 'utf-8') + data);

    def run(self):
        """
        Initialization of the sockets for listenning incomming connections.
        Calls the loop function.
        """
        self.run = True;
        self.knx_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM);
        self.master_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM);
        self.enocean_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM);
        port = self._parser.getValueFromSection(SLAVE_CONF_KNX_SECTION, SLAVE_CONF_KNX_PORT_ENTRY);
        if not port:
            sys.exit(2);
        port_master = self._parser.getValueFromSection(SLAVE_CONF_LISTEN_SECTION, SLAVE_CONF_LISTEN_PORT_ENTRY);
        if not port_master:
            sys.exit(2);
        port_enocean = self._parser.getValueFromSection(SLAVE_CONF_ENOCEAN_SECTION, SLAVE_CONF_ENOCEAN_PORT_ENTRY);
        if not port_enocean:
            sys.exit(2);
        self.knx_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1);
        self.master_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1);
        self.enocean_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1);
        self.knx_sock.bind(('', int(port)));
        self.master_sock.bind(('', int(port_master)));
        self.enocean_sock.bind(('', int(port_enocean)));
        self.knx_sock.listen(MAX_KNX);
        self.master_sock.listen(MAX_MASTERS);
        self.enocean_sock.listen(MAX_ENOCEAN);
        self.loop();

    def accept_knx(self):
        """
        Get available sockets for reading on the KNX socket.
        """
        rlist, wlist, elist = select.select([self.knx_sock], [], [], SELECT_TIMEOUT);
        for connection in rlist:
            new_knx, addr = connection.accept();
            self.connected_knx.append(new_knx);
        self.receive_from_knx(self.connected_knx);

    def accept_masters(self):
        """
        Get available sockets for reading on the master socket.
        """
        rlist, wlist, elist = select.select([self.master_sock], [], [], SELECT_TIMEOUT);
        masters_socks = [];
        for item in rlist:
            new_conn, addr = item.accept();
            masters_socks.append(new_conn);
        self.receive_from_masters(masters_socks);

    def accept_enocean(self):
        """
        Get available sockets for reading on the EnOcean socket.
        """
        rlist, wlist, elist = select.select([self.enocean_sock], [], [], SELECT_TIMEOUT);
        enocean_socks = [];
        for item in rlist:
            new_conn, addr = item.accept();
            enocean_socks.append(new_conn);
            self.connected_enocean.append(new_conn);
        self.receive_from_enocean(enocean_socks);

    def parse_data(self, data, connection):
        """
        Calls the wanted function with the packet_type described in 'data' (JSON syntax)
        """
        json_obj = json.JSONDecoder().decode(data);
        print(json_obj);
        if json_obj['packet_type'] in self.functions.keys():
            self.functions[json_obj['packet_type']](json_obj, connection);
        else:
            raise Exception(str(json_obj['packet_type']) + ": is not a valid packet type");

    def knx_read_request(self, json_obj, connection):
        """
        System call of "groupread" with parameters.
        """
        call([CALL_GROUPREAD, EIB_URL, json_obj['addr_to_read']]);

    def knx_write_temp(self, json_obj, connection):
        """
        System call of "groupwrite" with parameters.
        Almost the same as "knx_write_long" function, except that parameters are not the same
        """
        val = json_obj['value'].split(' ')
        call([CALL_GROUPWRITE, EIB_URL, json_obj['addr_to_send'], val[0], val[1]]);

    def knx_write_short(self, json_obj, connection):
        """
        System call of "groupswrite" with parameters.
        """
        call([CALL_GROUPSWRITE, EIB_URL, json_obj['addr_to_send'], str(json_obj['value'])]);

    def knx_write_long(self, json_obj, connection):
        """
        System call of "groupwrite" with parameters.
        """
        call([CALL_GROUPWRITE, EIB_URL, json_obj['addr_to_send'], str(json_obj['value'])]);

    def receive_from_masters(self, masters_to_read):
        """
        Read data comming from masters and call "parse_data" function.
        """
        for master in masters_to_read:
            data = master.recv(4096);
            decrypt_IV = data[:16].decode();
            decode_obj = AES.new(self.private_aes, AES.MODE_CBC, decrypt_IV);
            data2 = decode_obj.decrypt(data[16:]);
            self.parse_data(data2.decode(), master);

    def receive_from_knx(self, knx_to_read):
        """
        Read data from monitor KNX and transmits to master.
        """
        for knx in knx_to_read:
            data = knx.recv(TELEGRAM_LENGTH);
            if data:
                self.send_knx_data_to_masters(data);
            else:
                if knx in self.connected_knx:
                    knx.close();
                    self.connected_knx.remove(knx);

    def receive_from_enocean(self, enocean_to_read):
        """
        Read data from monitor EnOcean and transmits to master.
        """
        for enocean in enocean_to_read:
            data = enocean.recv(4096);
            if data:
                self.send_enocean_data_to_masters(data);
            else:
                if enocean in self.connected_enocean:
                    enocean.close();
                    self.connected_enocean.remove(enocean);

    def check_slave(self, json_obj, connection):
        """
        Callback called each time a check_slave packet is received.
        Used to confirm the existence of this daemon.
        """
        print("===== CHECK SLAVE =====");
        print(json_obj);
        print("=======================");
        json_str = '{"packet_type": "check_slave", "aes_pass": "******"}'
        master_hostname = str(json_obj['sender_name']);
        encrypt_IV = AESManager.get_IV();
        json_str = json_str + (' ' * (320 - len(json_str)))
        encode_obj = AES.new(self.private_aes, AES.MODE_CBC, encrypt_IV);
        data = encode_obj.encrypt(json_str);
        connection.send(bytes(encrypt_IV, 'utf-8') + data);

    def monitor_ip(self, json_obj, connection):
        """
        Re scan the local network to refresh hostlist.
        """
        self._scanner.scan(True);
        self._hostlist = self._scanner._HostList;

    def loop(self):
        """
        Main daemon loop.
        """
        while self.run:
            try:
                self.accept_knx();
            except Exception as e:
                frameinfo = getframeinfo(currentframe());
                self.logger.error('in loop accept_knx: ' + str(e));
                print('in loop accept_knx: ' + str(e));
            except KeyboardInterrupt as e:
                frameinfo = getframeinfo(currentframe());
                self.logger.error('in loop: Keyboard interrupt');
            try:
                self.accept_masters();
            except Exception as e:
                frameinfo = getframeinfo(currentframe());
                self.logger.error('in loop accept_masters: ' + str(e));
                print('in loop accept_masters: ' + str(e));
            except KeyboardInterrupt as e:
                frameinfo = getframeinfo(currentframe());
                self.logger.error('in loop: Keyboard interrupt');
            try:
                self.accept_enocean();
            except Exception as e:
                frameinfo = getframeinfo(currentframe());
                self.logger.error('in loop accept_enocean: ' + str(e));
                print('in loop accept_enocean: ' + str(e));
            except KeyboardInterrupt as e:
                frameinfo = getframeinfo(currentframe());
                self.logger.error('in loop: Keyboard interrupt');

    def stop(self):
        """
        Stop the daemon and closes all sockets.
        """
        for name, sock in self.connected_masters.items():
            sock.close();
        for knx in self.connected_knx:
            knx.close();
        self.knx_sock.close();

    def connect_to_masters(self):
        """
        Stored every device on network which have his hostname beginning by "MD3" and stores it
        in the self.connected_masters dict(), with hostnames as keys and sockets freshly open as values.
        """
        self.connected_masters = {};
        for host in self._hostlist:
            if MASTER_NAME_PREFIX in host._Hostname:
                port = self._parser.getValueFromSection(SLAVE_CONF_CONNECT_SECTION, SLAVE_CONF_CONNECT_PORT_ENTRY);
                if not port:
                    self.logger.error('in connect_to_masters: No ' + SLAVE_CONF_CONNECT_PORT_ENTRY + ' in ' + SLAVE_CONF_CONNECT_SECTION + ' section or maybe no such ' + SLAVE_CONF_CONNECT_SECTION + ' defined');
                    sys.exit(1);
                try:
                    self.logger.info('Connecting to ' + str(host._IpAddr) + ":" + str(port));
                    sock = socket.create_connection((host._IpAddr, port));
                    hostname = host._Hostname.split('.')[0];
                    self.connected_masters[host._Hostname] = sock;
                except Exception as e:
                    frameinfo = getframeinfo(currentframe());
                    self.logger.error('in connect_to_masters: ' + str(e));
                    pass;

    def send_knx_data_to_masters(self, data):
        """
        Converts 'data' from bytes to a clear KNX datagran, and sends it to available slaves.
        """
        ctrl = int(data[0]);
        src_addr = int.from_bytes(data[1:3], byteorder='big');
        dst_addr = int.from_bytes(data[3:5], byteorder='big');
        data_len = int.from_bytes(data[5:6], byteorder='big');
        telegram_data = data[6:7 + data_len];
        typ = -1;
        value = 0;
        if telegram_data[1] & 0xC0 == 0x00:             # read
            typ = 0;
        elif telegram_data[1] & 0xC0 == 0x40:           # resp
            typ = 1;
            if data_len == 2:
                value = int(telegram_data[1] & 0x0f);
            elif data_len > 2:
                value = int.from_bytes(telegram_data[2:data_len], byteorder='big');
        elif telegram_data[1] & 0xC0 == 0x80:           # write
            typ = 2;
            if data_len == 2:
                value = int(telegram_data[1] & 0x0f);
            elif data_len > 2:
                typ = 3;
                value = int.from_bytes(telegram_data[2:data_len], byteorder='big');
        json_str = json.JSONEncoder().encode(
            {
                "packet_type": "monitor_knx",
                "type": typ,
                "src_addr": individual2string(src_addr),
                "dst_addr": group2string(dst_addr),
                "date": str(time.time()).split('.')[0],
                "value": value,
                "sender_name": socket.gethostname()
            }
        );
        print('===== SENDING KNX DATA =====')
        print(json_str)
        print('============================')
        print()
        self.send_data_to_all_masters(json_str);

    def send_enocean_data_to_masters(self, data):
        """
        Converts 'data' from bytes to a clear EnOcean datagran, and sends it to available slaves.
        """
        self.connect_to_masters();
        if (data[4] == PACKET_TYPE_RADIO_ERP1): # si le packet_type == radio_erp1
            data_len = int.from_bytes(data[1:2], byteorder='big');
            opt_data_len = int(data[3]);
            print(str(data_len) + ' - ' + str(opt_data_len));
            src_str = "%X" % int.from_bytes(data[1+data_len:5+data_len], byteorder='big');
            if len(src_str) < 8:
                src_str = "0" + src_str;
            json_dict = {
                "packet_type": "monitor_enocean",
                "src_addr": src_str,
                "dst_addr": "%X" % int.from_bytes(data[261:265 + opt_data_len], byteorder='big'),
                "date": str(time.time()).split('.')[0],
                "sender_name": socket.gethostname(),
                "type": int(data[6])
            };
            if data[6] == RORG_NORMAL:
                json_dict['value'] = int(data[7]);
            elif data[6] == RORG_TEMPERATURE:
                json_dict['value'] = float(40 - ((40 / 255) * int(data[9])));
            json_str = json.JSONEncoder().encode(json_dict);
            self.send_data_to_all_masters(json_str);

    def send_data_to_all_masters(self, json_str):
        """
        Sends a string 'json_str' to available slaves on network.
        """
        self.connect_to_masters();
        # ici envoyer a tous les masters
        for name in self.connected_masters.keys():
            try:
                master = self.connected_masters[name];
                AES.key_size = 32;
                aes_IV = AESManager.get_IV();
                encode_obj = AES.new(self.private_aes, AES.MODE_CBC, aes_IV);
                data2 = encode_obj.encrypt(json_str + ((320 - len(json_str)) * ' '));
                print("Sending data to " + name);
                master.send(bytes(aes_IV, 'utf-8') + data2);
                print('Done.');
                master.close();
            except KeyError as e:
                self.logger.error('in send_data_to_all_masters: ' + str(e));
                print(e);
                pass;