From 365e23852f858f6f342627fd1e1fadf7bed0ebb1 Mon Sep 17 00:00:00 2001 From: Jonathan D'Orleans Date: Sat, 17 Sep 2016 15:14:54 -0400 Subject: [PATCH] Issues 356 - Moving enclosure logic from main to init --- mycroft/client/enclosure/__init__.py | 259 +++++++++++++++++++++++++- mycroft/client/enclosure/main.py | 261 +-------------------------- 2 files changed, 261 insertions(+), 259 deletions(-) diff --git a/mycroft/client/enclosure/__init__.py b/mycroft/client/enclosure/__init__.py index cb59143831..6e3464f773 100644 --- a/mycroft/client/enclosure/__init__.py +++ b/mycroft/client/enclosure/__init__.py @@ -16,4 +16,261 @@ # along with Mycroft Core. If not, see . -__author__ = 'seanfitz' +import subprocess +import time +from Queue import Queue +from alsaaudio import Mixer +from threading import Thread + +import serial + +from mycroft.client.enclosure.arduino import EnclosureArduino +from mycroft.client.enclosure.eyes import EnclosureEyes +from mycroft.client.enclosure.mouth import EnclosureMouth +from mycroft.client.enclosure.weather import EnclosureWeather +from mycroft.configuration import ConfigurationManager +from mycroft.messagebus.client.ws import WebsocketClient +from mycroft.messagebus.message import Message +from mycroft.util import play_wav, create_signal +from mycroft.util.audio_test import record +from mycroft.util.log import getLogger + +__author__ = 'aatchison', 'jdorleans', 'iward' + +LOGGER = getLogger("EnclosureClient") + + +class EnclosureReader(Thread): + """ + Reads data from Serial port. + + Listens to all commands sent by Arduino that must be be performed on + Mycroft Core. + + E.g. Mycroft Stop Feature + #. Arduino sends a Stop command after a button press on a Mycroft unit + #. ``EnclosureReader`` captures the Stop command + #. Notify all Mycroft Core processes (e.g. skills) to be stopped + + Note: A command is identified by a line break + """ + + def __init__(self, serial, client): + super(EnclosureReader, self).__init__(target=self.read) + self.alive = True + self.daemon = True + self.serial = serial + self.client = client + self.start() + + def read(self): + while self.alive: + try: + data = self.serial.readline()[:-2] + if data: + self.process(data) + LOGGER.info("Reading: " + data) + except Exception as e: + LOGGER.error("Reading error: {0}".format(e)) + + def process(self, data): + self.client.emit(Message(data)) + + if "mycroft.stop" in data: + create_signal('buttonPress') + self.client.emit(Message("mycroft.stop")) + + if "volume.up" in data: + self.client.emit( + Message("IncreaseVolumeIntent", {'play_sound': True})) + + if "volume.down" in data: + self.client.emit( + Message("DecreaseVolumeIntent", {'play_sound': True})) + + if "system.test.begin" in data: + self.client.emit(Message('recognizer_loop:sleep')) + + if "system.test.end" in data: + self.client.emit(Message('recognizer_loop:wake_up')) + + if "mic.test" in data: + mixer = Mixer() + prev_vol = mixer.getvolume()[0] + mixer.setvolume(35) + self.client.emit(Message("speak", { + 'utterance': "I am testing one two three"})) + + time.sleep(0.5) # Prevents recording the loud button press + record("/tmp/test.wav", 3.0) + mixer.setvolume(prev_vol) + play_wav("/tmp/test.wav").communicate() + + # Test audio muting on arduino + subprocess.call('speaker-test -P 10 -l 0 -s 1', shell=True) + + if "unit.shutdown" in data: + self.client.emit( + Message("enclosure.eyes.timedspin", + {'length': 12000})) + self.client.emit(Message("enclosure.mouth.reset")) + subprocess.call('systemctl poweroff -i', shell=True) + + if "unit.reboot" in data: + self.client.emit( + Message("enclosure.eyes.spin")) + self.client.emit(Message("enclosure.mouth.reset")) + subprocess.call('systemctl reboot -i', shell=True) + + if "unit.setwifi" in data: + self.client.emit(Message("wifisetup.start")) + + if "unit.factory-reset" in data: + subprocess.call( + 'rm ~/.mycroft/identity/identity.json', + shell=True) + self.client.emit( + Message("enclosure.eyes.spin")) + self.client.emit(Message("enclosure.mouth.reset")) + subprocess.call('systemctl reboot -i', shell=True) + + def stop(self): + self.alive = False + + +class EnclosureWriter(Thread): + """ + Writes data to Serial port. + #. Enqueues all commands received from Mycroft enclosures + implementation + #. Process them on the received order by writing on the Serial port + + E.g. Displaying a text on Mycroft's Mouth + #. ``EnclosureMouth`` sends a text command + #. ``EnclosureWriter`` captures and enqueue the command + #. ``EnclosureWriter`` removes the next command from the queue + #. ``EnclosureWriter`` writes the command to Serial port + + Note: A command has to end with a line break + """ + + def __init__(self, serial, client, size=16): + super(EnclosureWriter, self).__init__(target=self.flush) + self.alive = True + self.daemon = True + self.serial = serial + self.client = client + self.commands = Queue(size) + self.start() + + def flush(self): + while self.alive: + try: + cmd = self.commands.get() + self.serial.write(cmd + '\n') + LOGGER.info("Writing: " + cmd) + self.commands.task_done() + except Exception as e: + LOGGER.error("Writing error: {0}".format(e)) + + def write(self, command): + self.commands.put(str(command)) + + def stop(self): + self.alive = False + + +class Enclosure(object): + """ + Serves as a communication interface between Arduino and Mycroft Core. + + ``Enclosure`` initializes and aggregates all enclosures implementation. + + E.g. ``EnclosureEyes``, ``EnclosureMouth`` and ``EnclosureArduino`` + + It also listens to the basis events in order to perform those core actions + on the unit. + + E.g. Start and Stop talk animation + """ + + def __init__(self): + self.config = ConfigurationManager.get().get("enclosure") + self.__init_serial() + self.client = WebsocketClient() + self.reader = EnclosureReader(self.serial, self.client) + self.writer = EnclosureWriter(self.serial, self.client) + self.eyes = EnclosureEyes(self.client, self.writer) + self.mouth = EnclosureMouth(self.client, self.writer) + self.system = EnclosureArduino(self.client, self.writer) + self.weather = EnclosureWeather(self.client, self.writer) + self.__register_events() + self.update() + self.test() + + def update(self): + if self.config.get('update'): + try: + self.speak("I am upgrading my enclosure version") + subprocess.check_call('/opt/enclosure/upload.sh') + self.speak("Enclosure upgrade completed") + time.sleep(5) + except: + self.speak("I cannot upgrade right now, I'll try later") + + def test(self): + if self.config.get('test'): + self.speak("Beginning hardware self test") + self.writer.write("test.begin") + + def __init_serial(self): + try: + self.port = self.config.get("port") + self.rate = self.config.get("rate") + self.timeout = self.config.get("timeout") + self.serial = serial.serial_for_url( + url=self.port, baudrate=self.rate, timeout=self.timeout) + LOGGER.info( + "Connected to: " + self.port + " rate: " + str(self.rate) + + " timeout: " + str(self.timeout)) + except: + LOGGER.error( + "It is not possible to connect to serial port: " + self.port) + raise + + def __register_events(self): + self.client.on('enclosure.mouth.events.activate', + self.__register_mouth_events) + self.client.on('enclosure.mouth.events.deactivate', + self.__remove_mouth_events) + self.__register_mouth_events() + + def __register_mouth_events(self, event=None): + self.client.on('recognizer_loop:record_begin', self.mouth.listen) + self.client.on('recognizer_loop:record_end', self.mouth.reset) + self.client.on('recognizer_loop:audio_output_start', self.mouth.talk) + self.client.on('recognizer_loop:audio_output_end', self.mouth.reset) + + def __remove_mouth_events(self, event=None): + self.client.remove('recognizer_loop:record_begin', self.mouth.listen) + self.client.remove('recognizer_loop:record_end', self.mouth.reset) + self.client.remove('recognizer_loop:audio_output_start', + self.mouth.talk) + self.client.remove('recognizer_loop:audio_output_end', + self.mouth.reset) + + def speak(self, text): + self.client.emit(Message("speak", {'utterance': text})) + + def run(self): + try: + self.client.run_forever() + except Exception as e: + LOGGER.error("Client error: {0}".format(e)) + self.stop() + + def stop(self): + self.writer.stop() + self.reader.stop() + self.serial.close() + self.client.close() diff --git a/mycroft/client/enclosure/main.py b/mycroft/client/enclosure/main.py index b71e174906..3425b5b84d 100644 --- a/mycroft/client/enclosure/main.py +++ b/mycroft/client/enclosure/main.py @@ -14,266 +14,11 @@ # # You should have received a copy of the GNU General Public License # along with Mycroft Core. If not, see . -import subprocess + + import sys -import time -from Queue import Queue -from alsaaudio import Mixer -from threading import Thread -import serial - -from mycroft.client.enclosure.arduino import EnclosureArduino -from mycroft.client.enclosure.eyes import EnclosureEyes -from mycroft.client.enclosure.mouth import EnclosureMouth -from mycroft.client.enclosure.weather import EnclosureWeather -from mycroft.configuration import ConfigurationManager -from mycroft.messagebus.client.ws import WebsocketClient -from mycroft.messagebus.message import Message -from mycroft.util import play_wav -from mycroft.util import create_signal -from mycroft.util.audio_test import record -from mycroft.util.log import getLogger - -__author__ = 'aatchison + jdorleans + iward' - -LOGGER = getLogger("EnclosureClient") - - -class EnclosureReader(Thread): - """ - Reads data from Serial port. - - Listens to all commands sent by Arduino that must be be performed on - Mycroft Core. - - E.g. Mycroft Stop Feature - #. Arduino sends a Stop command after a button press on a Mycroft unit - #. ``EnclosureReader`` captures the Stop command - #. Notify all Mycroft Core processes (e.g. skills) to be stopped - - Note: A command is identified by a line break - """ - - def __init__(self, serial, client): - super(EnclosureReader, self).__init__(target=self.read) - self.alive = True - self.daemon = True - self.serial = serial - self.client = client - self.start() - - def read(self): - while self.alive: - try: - data = self.serial.readline()[:-2] - if data: - self.process(data) - LOGGER.info("Reading: " + data) - except Exception as e: - LOGGER.error("Reading error: {0}".format(e)) - - def process(self, data): - self.client.emit(Message(data)) - - if "mycroft.stop" in data: - create_signal('buttonPress') - self.client.emit(Message("mycroft.stop")) - - if "volume.up" in data: - self.client.emit( - Message("IncreaseVolumeIntent", {'play_sound': True})) - - if "volume.down" in data: - self.client.emit( - Message("DecreaseVolumeIntent", {'play_sound': True})) - - if "system.test.begin" in data: - self.client.emit(Message('recognizer_loop:sleep')) - - if "system.test.end" in data: - self.client.emit(Message('recognizer_loop:wake_up')) - - if "mic.test" in data: - mixer = Mixer() - prev_vol = mixer.getvolume()[0] - mixer.setvolume(35) - self.client.emit(Message("speak", { - 'utterance': "I am testing one two three"})) - - time.sleep(0.5) # Prevents recording the loud button press - record("/tmp/test.wav", 3.0) - mixer.setvolume(prev_vol) - play_wav("/tmp/test.wav").communicate() - - # Test audio muting on arduino - subprocess.call('speaker-test -P 10 -l 0 -s 1', shell=True) - - if "unit.shutdown" in data: - self.client.emit( - Message("enclosure.eyes.timedspin", - {'length': 12000})) - self.client.emit(Message("enclosure.mouth.reset")) - subprocess.call('systemctl poweroff -i', shell=True) - - if "unit.reboot" in data: - self.client.emit( - Message("enclosure.eyes.spin")) - self.client.emit(Message("enclosure.mouth.reset")) - subprocess.call('systemctl reboot -i', shell=True) - - if "unit.setwifi" in data: - self.client.emit(Message("mycroft.wifi.start")) - - if "unit.factory-reset" in data: - subprocess.call( - 'rm ~/.mycroft/identity/identity.json', - shell=True) - self.client.emit( - Message("enclosure.eyes.spin")) - self.client.emit(Message("enclosure.mouth.reset")) - subprocess.call('systemctl reboot -i', shell=True) - - def stop(self): - self.alive = False - - -class EnclosureWriter(Thread): - """ - Writes data to Serial port. - #. Enqueues all commands received from Mycroft enclosures - implementation - #. Process them on the received order by writing on the Serial port - - E.g. Displaying a text on Mycroft's Mouth - #. ``EnclosureMouth`` sends a text command - #. ``EnclosureWriter`` captures and enqueue the command - #. ``EnclosureWriter`` removes the next command from the queue - #. ``EnclosureWriter`` writes the command to Serial port - - Note: A command has to end with a line break - """ - - def __init__(self, serial, client, size=16): - super(EnclosureWriter, self).__init__(target=self.flush) - self.alive = True - self.daemon = True - self.serial = serial - self.client = client - self.commands = Queue(size) - self.start() - - def flush(self): - while self.alive: - try: - cmd = self.commands.get() - self.serial.write(cmd + '\n') - LOGGER.info("Writing: " + cmd) - self.commands.task_done() - except Exception as e: - LOGGER.error("Writing error: {0}".format(e)) - - def write(self, command): - self.commands.put(str(command)) - - def stop(self): - self.alive = False - - -class Enclosure(object): - """ - Serves as a communication interface between Arduino and Mycroft Core. - - ``Enclosure`` initializes and aggregates all enclosures implementation. - - E.g. ``EnclosureEyes``, ``EnclosureMouth`` and ``EnclosureArduino`` - - It also listens to the basis events in order to perform those core actions - on the unit. - - E.g. Start and Stop talk animation - """ - - def __init__(self): - self.config = ConfigurationManager.get().get("enclosure") - self.__init_serial() - self.client = WebsocketClient() - self.reader = EnclosureReader(self.serial, self.client) - self.writer = EnclosureWriter(self.serial, self.client) - self.eyes = EnclosureEyes(self.client, self.writer) - self.mouth = EnclosureMouth(self.client, self.writer) - self.system = EnclosureArduino(self.client, self.writer) - self.weather = EnclosureWeather(self.client, self.writer) - self.__register_events() - self.update() - self.test() - - def update(self): - if self.config.get('update'): - try: - self.speak("I am upgrading my enclosure version") - subprocess.check_call('/opt/enclosure/upload.sh') - self.speak("Enclosure upgrade completed") - time.sleep(5) - except: - self.speak("I cannot upgrade right now, I'll try later") - - def test(self): - if self.config.get('test'): - self.speak("Beginning hardware self test") - self.writer.write("test.begin") - - def __init_serial(self): - try: - self.port = self.config.get("port") - self.rate = self.config.get("rate") - self.timeout = self.config.get("timeout") - self.serial = serial.serial_for_url( - url=self.port, baudrate=self.rate, timeout=self.timeout) - LOGGER.info( - "Connected to: " + self.port + " rate: " + str(self.rate) + - " timeout: " + str(self.timeout)) - except: - LOGGER.error( - "It is not possible to connect to serial port: " + self.port) - raise - - def __register_events(self): - self.client.on('enclosure.mouth.events.activate', - self.__register_mouth_events) - self.client.on('enclosure.mouth.events.deactivate', - self.__remove_mouth_events) - self.__register_mouth_events() - - def __register_mouth_events(self, event=None): - self.client.on('recognizer_loop:record_begin', self.mouth.listen) - self.client.on('recognizer_loop:record_end', self.mouth.reset) - self.client.on('recognizer_loop:audio_output_start', self.mouth.talk) - self.client.on('recognizer_loop:audio_output_end', self.mouth.reset) - - def __remove_mouth_events(self, event=None): - self.client.remove('recognizer_loop:record_begin', self.mouth.listen) - self.client.remove('recognizer_loop:record_end', self.mouth.reset) - self.client.remove('recognizer_loop:audio_output_start', - self.mouth.talk) - self.client.remove('recognizer_loop:audio_output_end', - self.mouth.reset) - - def speak(self, text): - self.client.emit(Message("speak", {'utterance': text})) - - def run(self): - try: - self.client.run_forever() - except Exception as e: - LOGGER.error("Client error: {0}".format(e)) - self.stop() - - def stop(self): - self.writer.stop() - self.reader.stop() - self.serial.close() - self.client.close() +from mycroft.client.enclosure import Enclosure def main():