from gpiozero import LED from gpiozero import Button import subprocess from threading import Thread import socket import yaml import time NO_CALL = 0 INCOMING_CALL = 1 OUTGOING_CALL = 2 OP_CALL = 'CALL'.encode() OP_HANG = 'HANG'.encode() OP_PING = 'PING'.encode() OP_OK = 'OK'.encode() OP_ERROR = 'ERROR'.encode() CHECK_STATUS_INTERVAL = 120 class CallManager: state = NO_CALL station = None call_process = None def call(self, station): try: self.call_process = subprocess.Popen(['parole', '-c', station['host'], '-d', config['audio device']]) s = socket.socket() s.connect((station['host'], station['port'])) s.send(OP_CALL) response = s.recv(1024) print('Call notice sent', 'response=', response.decode()) s.close() self.state = OUTGOING_CALL self.station = station station['red'].on() except Exception as e: print('Error al realizar la llamada: ', e) station['green'].off() self.call_process.terminate() self.call_process.communicate() def incoming_call(self, station): self.state = INCOMING_CALL self.station = station station['red'].on() def hang(self): print('Hang! self.station', self.station) if self.station is not None: self.station['red'].off() if self.call_process is not None: self.call_process.terminate() self.call_process.communicate() self.state = NO_CALL self.station = None def notify_hang(self): if self.station is not None: s = socket.socket() s.connect((self.station['host'], self.station['port'])) s.send(OP_HANG) response = s.recv(1024) print('Hang notify sent', 'response=', response.decode()) s.close() callManager = CallManager() def getstationbyip(ip): for s in stations: if s['ip'] == ip: return s return None # Escuchamos en port para peticiones de operaciones. def listen(call_manager): serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) serversocket.bind(('0.0.0.0', port)) print('Listening address', socket.gethostname(), 'on port', port) serversocket.listen(5) while True: # accept connections from outside (clientsocket, ip_address) = serversocket.accept() data = clientsocket.recv(1024) print('Connection from', ip_address, 'host=', clientsocket.getpeername(), 'data=', data.decode()) station = getstationbyip(ip_address[0]) if data == OP_HANG: call_manager.hang() clientsocket.send(OP_OK) elif data == OP_CALL: if callManager.state == OUTGOING_CALL or callManager.state == INCOMING_CALL: callManager.hang() call_manager.incoming_call(station) clientsocket.send(OP_OK) elif data == OP_PING: station['green'].on() clientsocket.send(OP_OK) else: clientsocket.send(OP_ERROR) clientsocket.close() # Comprueba que el otro intercom esta activo. def check_status(station): while True: s = socket.socket() try: s.connect((station['host'], station['port'])) s.send(OP_PING) if s.recv(1024) == OP_OK: station['green'].on() else: station['red'].off() station['green'].off() except socket.error: station['red'].off() station['green'].off() print('Check status failed! host=', station['host']) finally: s.close() time.sleep(CHECK_STATUS_INTERVAL) config = yaml.safe_load(open("intercom.yml")) port = config['port'] stations = config['stations'] # Start thread to wait for incoming hang petitions. thread_listen = Thread(target=listen, args=(callManager,)) thread_listen.daemon = True thread_listen.start() # Power off leds and start threads to check other intercoms status. for _station in stations: _station['ip'] = socket.gethostbyname(_station['host']) _station['red'] = LED(_station['red led']) _station['red'].off() _station['green'] = LED(_station['green led']) _station['green'].off() _station['btn'] = Button(_station['button']) _station['old button state'] = _station['btn'].is_pressed _station['button state changed time'] = time.clock_gettime(time.CLOCK_MONOTONIC) _station['button pressed'] = False thread_check = Thread(target=check_status, args=(_station,)) thread_check.daemon = True thread_check.start() # Listen for swith changes. try: while True: for _station in stations: # new_input_state = GPIO.input(_station['button']) new_input_state = _station['btn'].is_pressed # Al pulsar el interruptor cambia el estado. elapsed_time = time.clock_gettime(time.CLOCK_MONOTONIC) - _station['button state changed time'] if new_input_state != _station['old button state'] and elapsed_time > 0.2: if not _station['toogle button']: _station['button pressed'] = not _station['button pressed'] if _station['button pressed']: _station['old button state'] = new_input_state _station['button state changed time'] = time.clock_gettime(time.CLOCK_MONOTONIC) continue print('Interruptor pulsado callManager.state=', callManager.state, 'elapsed_time', elapsed_time) # Si no hay llamada en curso la hacemos. if callManager.state == NO_CALL: callManager.call(_station) elif callManager.state == OUTGOING_CALL: callManager.notify_hang() callManager.hang() elif callManager.state == INCOMING_CALL: callManager.notify_hang() callManager.hang() _station['old button state'] = new_input_state _station['button state changed time'] = time.clock_gettime(time.CLOCK_MONOTONIC) time.sleep(0.02) finally: for _station in stations: _station['red'].close() _station['green'].close() _station['btn'].close()