From db747d54bece564ff881cda3e21e38b45c91f35e Mon Sep 17 00:00:00 2001 From: Mark Powers Date: Sun, 15 Aug 2021 04:52:18 +0100 Subject: Refactor some things --- dial.py | 23 +++++++++++---------- events.py | 45 ++++++++++++++++++------------------------ hangup.py | 16 +++++++-------- main.py | 41 ++++++-------------------------------- phone.py | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 111 insertions(+), 82 deletions(-) create mode 100644 phone.py diff --git a/dial.py b/dial.py index d5117ff..fa5ca9e 100644 --- a/dial.py +++ b/dial.py @@ -10,12 +10,10 @@ REST_TIME = 0.3 # seconds DIAL_RESET_TIME = 5 # seconds class DialThread(threading.Thread): - def __init__(self, queue, phone_held, processing_event): + def __init__(self, phone): threading.Thread.__init__(self, args=(), kwargs=None) - self.queue = queue self.daemon = True - self.phone_held = phone_held - self.processing_event = processing_event + self.phone = phone GPIO.setmode(GPIO.BCM) GPIO.setup(BUTTON_GPIO, GPIO.IN, pull_up_down=GPIO.PUD_UP) @@ -40,8 +38,8 @@ class DialThread(threading.Thread): pressed = False if self.dial_over(rest_start) and not printed: # Only add this dial to queue if we should accept numbers at this time - if self.phone_held.is_set() and not self.processing_event.is_set(): - self.queue.put(count % 10) # wrap 10 to 0 + if self.phone.phone_held.is_set() and not self.phone.processing_event.is_set(): + self.phone.queue.put(count % 10) # wrap 10 to 0 count = 0 printed = True time.sleep(0.01) @@ -52,23 +50,24 @@ class DialThread(threading.Thread): class DialManager: - def __init__(self, phone_held, phone_hung_up): + def __init__(self, phone): self.sequence = [] + self.phone = phone self.update = datetime.now() - self._load_sequences(phone_held, phone_hung_up) + self._load_sequences() + print(self.sequences) - def _load_sequences(self, phone_held, phone_hung_up): + def _load_sequences(self): def recursive_add(classname, sequence_list, sequences, args): key = int(sequence_list[0]) if not sequence_list[1:]: - sequences[key] = eval(classname)(self, phone_held, phone_hung_up, args) + sequences[key] = eval(classname)(self.phone, args) else: - sequences[key] = {} + sequences[key] = sequences[key] if key in sequences and isinstance(sequences[key], dict) else {} recursive_add(classname, sequence_list[1:], sequences[key], args) self.sequences = {} for sequence in config["sequences"]: - print(sequence, config["sequences"][sequence], sep="\t") parts = config["sequences"][sequence].split(" ", 1) classname = parts[0] args = parts[1:] diff --git a/events.py b/events.py index fa702c0..36ca972 100644 --- a/events.py +++ b/events.py @@ -4,24 +4,19 @@ from config import config import requests from multiprocessing import Process -def run_until_hangup(command, phone_hung_up): - ''' - returns true if process was finished - ''' - process = subprocess.Popen(command) - while process.poll() is None: - if phone_hung_up.wait(timeout=0.1): - process.terminate() - return False - return True - +def tts_command(text): + if config["engine"]["tts"] == "espeak": + return ["espeak", text] + elif config["engine"]["tts"] == "festival": + filename = "/tmp/phone_input.txt" + with open(filename, "w") as f: + f.write(text) + return ["festival", "--tts", filename] class Event(): - def __init__(self, dial_manager, phone_held, phone_hung_up, args): - self.dial_manager = dial_manager - self.phone_held = phone_held - self.phone_hung_up = phone_hung_up + def __init__(self, phone, args): self.args = args + self.phone = phone def get_name(self): return "generic event" @@ -30,7 +25,7 @@ class Event(): return "event" def run(self): - run_until_hangup(["espeak", self.get_text()], self.phone_hung_up) + self.phone.run_until_hangup(tts_command(self.get_text())) class FortuneEvent(Event): @@ -56,7 +51,7 @@ class DirectoryEvent(Event): else: sequence_list.append((sequence, sequences[key])) return sequence_list - sequences = self.dial_manager.sequences + sequences = self.phone.dial_manager.sequences text = "" for sequence, event in recursive_build("", sequences): text += f"For {event.get_name()} dial {sequence}. " @@ -75,9 +70,8 @@ class OperatorEvent(Event): next_hour = now.hour % 12 if nearest_quarter != 0 else (now.hour + 1) % 12 next_hour = next_hour if next_hour != 0 else 12 text = f"In {nearest_quarter - now.minute} minutes it will be {next_hour} {nearest_quarter}" - loop = run_until_hangup(["espeak", text], self.phone_hung_up) - if loop: - loop = run_until_hangup(["aplay", "/etc/phone/sound/hold.wav"], self.phone_hung_up) + commands = [tts_command(text), ["aplay", "/etc/phone/sound/hold.wav"]] + self.phone.run_many_until_hangup(commands) class WeatherEvent(Event): @@ -106,9 +100,10 @@ class RecordEvent(Event): return "recorder" def run(self): - run_until_hangup(["espeak", "Please record a message."], self.phone_hung_up) + self.phone.run_until_hangup(tts_command("Please record a message.")) + process = subprocess.Popen(["arecord", "/tmp/out.wav"]) - self.phone_hung_up.wait() + self.phone.wait_until_hung_up() process.terminate() return "recording done" @@ -118,13 +113,11 @@ class PlayEvent(Event): return "play" def run(self): - run_until_hangup(["aplay", "/tmp/out.wav"], self.phone_hung_up) + self.phone.run_until_hangup(["aplay", "/tmp/out.wav"]) class WavEvent(Event): def get_name(self): return self.args[0] def run(self): - run_until_hangup(["aplay", self.args[0]], self.phone_hung_up) - - + self.phone.run_until_hangup(["aplay", self.args[0]]) diff --git a/hangup.py b/hangup.py index cfe6bdf..152118f 100644 --- a/hangup.py +++ b/hangup.py @@ -5,11 +5,9 @@ import time BUTTON_GPIO = 24 class HangUpThread(threading.Thread): - def __init__(self, phone_held, phone_hung_up, dial_manager): + def __init__(self, phone): threading.Thread.__init__(self, args=(), kwargs=None) - self.phone_held = phone_held - self.phone_hung_up = phone_hung_up - self.dial_manager = dial_manager + self.phone = phone self.daemon = True GPIO.setmode(GPIO.BCM) @@ -18,11 +16,11 @@ class HangUpThread(threading.Thread): def run(self): while True: if not GPIO.input(BUTTON_GPIO): - self.phone_held.clear() - self.phone_hung_up.set() - self.dial_manager.clear_sequence() + self.phone.phone_held.clear() + self.phone.phone_hung_up.set() + self.phone.dial_manager.clear_sequence() else: - self.phone_held.set() - self.phone_hung_up.clear() + self.phone.phone_held.set() + self.phone.phone_hung_up.clear() time.sleep(0.1) diff --git a/main.py b/main.py index c72147c..7c41d7b 100755 --- a/main.py +++ b/main.py @@ -1,47 +1,18 @@ #!/usr/bin/env python3 -from dial import DialManager, DialThread -from hangup import HangUpThread - -from time import sleep -from queue import Queue -from threading import Event +from phone import Phone if __name__ == "__main__": - queue = Queue() - phone_held = Event() - phone_hung_up = Event() - processing_event = Event() - - # start phone as on hook - phone_held.clear() - phone_hung_up.set() - processing_event.clear() - - dial_thread = DialThread(queue, phone_held, processing_event) - dial_thread.start() - dial_manager = DialManager(phone_held, phone_hung_up) - - hang_up_thread = HangUpThread(phone_held, phone_hung_up, dial_manager) - hang_up_thread.start() - + phone = Phone.get_instance() print("Ready") while True: try: # Wait for phone to be picked up - phone_held.wait() + phone.wait_until_answered() # Dial a number - try: - dialed = queue.get(block=True, timeout=0.1) - except: - continue - response = dial_manager.dial(dialed) - # If we matched a sequence, play out event - if response is not None: - processing_event.set() - response.run() + phone.process_dial() except Exception as e: print(e) + raise e finally: - processing_event.clear() - + phone.clear_event() diff --git a/phone.py b/phone.py new file mode 100644 index 0000000..b47ea41 --- /dev/null +++ b/phone.py @@ -0,0 +1,68 @@ +from dial import DialManager, DialThread +from hangup import HangUpThread +from threading import Event + +import subprocess +from queue import Queue + +class Phone: + __instance = None + @staticmethod + def get_instance(): + if Phone.__instance is None: + Phone() + return Phone.__instance + + def __init__(self): + Phone.__instance = self + self.queue = Queue() + self.phone_held = Event() + self.phone_hung_up = Event() + self.processing_event = Event() + + # start phone as on hook + self.phone_held.clear() + self.phone_hung_up.set() + self.processing_event.clear() + + self.dial_thread = DialThread(self) + self.dial_thread.start() + self.dial_manager = DialManager(self) + + self.hang_up_thread = HangUpThread(self) + self.hang_up_thread.start() + + def wait_until_answered(self, timeout=None): + return self.phone_held.wait(timeout) + + def wait_until_hung_up(self, timeout=None): + return self.phone_hung_up.wait(timeout) + + def process_dial(self): + try: + dialed = self.queue.get(block=True, timeout=0.1) + except: + return + response = self.dial_manager.dial(dialed) + # If we matched a sequence, play out event + if response is not None: + self.processing_event.set() + response.run() + + def clear_event(self): + self.processing_event.clear() + + def run_until_hangup(self, command): + process = subprocess.Popen(command) + while process.poll() is None: + if self.wait_until_hung_up(0.1): + print("terminating process") + process.terminate() + process.kill() + return False + return True + + def run_many_until_hangup(self, commands): + for command in commands: + if not self.run_until_hangup(command): + break -- cgit v1.2.3