Implementing SUSI Linux App as a Finite State Machine

SUSI Linux app provides access to SUSI on Linux distributions on desktop as well as hardware devices like Raspberry Pi. It is a headless client that can be used to interact to SUSI via voice only. As more and more features like multiple hotword detection support and wake button support was added to SUSI Linux, the code became complex to understand and manage. A system was needed to model the app after. Finite State Machine is a perfect approach for such system. The Wikipedia definition of State Machine is “It is an abstract machine that can be in exactly one of a finite number of states at any given time. The FSM can change from one state to another in response to some external inputs; the change from one state to another is called a transition.” This means that if you can model your app into a finite number of states, you may consider using the State Machine implementation. State Machine implementation has following advantages: Better control over the working of the app. Improved Error handling by making an Error State to handle errors. States work independently which helps to modularize code in a better form. To begin with, we declare an abstract State class. This class declares all the common properties of a state and transition method. from abc import ABC, abstractclassmethod import logging class State(ABC): def __init__(self, components): self.components = components self.allowedStateTransitions = {} @abstractclassmethod def on_enter(self, payload=None): pass @abstractclassmethod def on_exit(self): pass def transition(self, state, payload=None): if not self.__can_transition(state): logging.warning("Invalid transition to State{0}".format(state)) return self.on_exit() state.on_enter(payload) def __can_transition(self, state): return state in self.allowedStateTransitions.values() We declared the on_enter() and on_exit() abstract method. These methods are executed on the entering and exiting a state respectively. The task designated for the state can be performed in the on_enter() method and it can free up resources or stop listening to callbacks in the on_exit() method. The transition method is to transition between one state to another. In a state machine, a state can transition to one of the allowed states only. Thus, we check if the transition is allowed or not before continuing it. The on_enter() and transition() methods additionally accepts a payload argument. This can be used to transfer some data to the state from the previous state. We also added the components property to the State. Components store the shared components that can be used across all the State and are needed to be initialized only once. We create a component class declaring all the components that are needed to be used by states. class Components: def __init__(self): recognizer = Recognizer() recognizer.dynamic_energy_threshold = False recognizer.energy_threshold = 1000 self.recognizer = recognizer self.microphone = Microphone() self.susi = susi self.config = json_config.connect('config.json') if self.config['hotword_engine'] == 'Snowboy': from main.hotword_engine import SnowboyDetector self.hotword_detector = SnowboyDetector() else: from main.hotword_engine import PocketSphinxDetector self.hotword_detector = PocketSphinxDetector() if self.config['wake_button'] == 'enabled': if self.config['device'] == 'RaspberryPi': from ..hardware_components import RaspberryPiWakeButton self.wake_button = RaspberryPiWakeButton() else: self.wake_button = None else: self.wake_button = None Now, we list out all the states that…

Continue ReadingImplementing SUSI Linux App as a Finite State Machine