Skip to content
This repository has been archived by the owner on Sep 8, 2024. It is now read-only.

Commit

Permalink
Fixes issue #434. Developers working on both Cerberus and Home durin… (
Browse files Browse the repository at this point in the history
…#435)

* Fixes issue #434.  Developers working on both Cerberus and Home during the transition would have to re-pair.

Also bumping enclosure client version.

* Correcting error from when Tarturus code was merged.  At startup it was calling Enclosure.system_reset(), which rebooted the Arduino, instead of implementing Enclosure.reset(), which sets the UI to a "ready for input" state.

While in here, I also added docstrings for all Enclosure API methods.

* Increment Arduino code version

* Adding a call to reset the face UI when the enclosure service starts up.  This is needed because the enclosure.reset that is posted by the speech service on the messagebus sometimes occurs before the enclosure client is up and listening for it -- especially if there is a Arduino firmware upgrade.

In the future, we may want to consider a core service roll-call that gets triggered whenever any of the core services come up.

* Update dev_setup.sh
  • Loading branch information
Steve Penrod authored and aatchison committed Dec 28, 2016
1 parent e3e58d3 commit 0ecc736
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 6 deletions.
13 changes: 11 additions & 2 deletions mycroft/client/enclosure/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ def process(self, data):

if "unit.factory-reset" in data:
subprocess.call(
'rm ~/.mycroft/identity/identity.json',
'rm ~/.mycroft/identity/identity2.json',
shell=True)
self.ws.emit(
Message("enclosure.eyes.spin"))
Expand Down Expand Up @@ -207,14 +207,15 @@ def __init__(self):
self.writer.write("system.version")
self.ws.on("enclosure.start", self.start)
self.started = False
Timer(5, self.stop).start()
Timer(5, self.stop).start() # WHY? This at least needs an explaination, this is non-obvious behavior

def start(self, event=None):
self.eyes = EnclosureEyes(self.ws, self.writer)
self.mouth = EnclosureMouth(self.ws, self.writer)
self.system = EnclosureArduino(self.ws, self.writer)
self.weather = EnclosureWeather(self.ws, self.writer)
self.__register_events()
self.__reset()
self.started = True

def __init_serial(self):
Expand All @@ -235,6 +236,8 @@ def __register_events(self):
self.__register_mouth_events)
self.ws.on('enclosure.mouth.events.deactivate',
self.__remove_mouth_events)
self.ws.on('enclosure.reset',
self.__reset)
self.__register_mouth_events()

def __register_mouth_events(self, event=None):
Expand All @@ -251,6 +254,12 @@ def __remove_mouth_events(self, event=None):
self.ws.remove('recognizer_loop:audio_output_end',
self.mouth.reset)

def __reset(self, event=None):
# Reset both the mouth and the eye elements to indicate the unit is
# ready for input.
self.writer.write("eyes.reset")
self.writer.write("mouth.reset")

def speak(self, text):
self.ws.emit(Message("speak", {'utterance': text}))

Expand Down
86 changes: 86 additions & 0 deletions mycroft/client/enclosure/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,82 +29,168 @@ class EnclosureAPI:
This API is intended to be used to interface with the hardware
that is running Mycroft. It exposes all possible commands which
can be sent to a Mycroft enclosure implementation.
Different enclosure implementations may implement this differently
and/or may ignore certain API calls completely. For example,
the eyes_color() API might be ignore on a Mycroft that uses simple
LEDs which only turn on/off, or not at all on an implementation
where there is no face at all.
"""

def __init__(self, ws):
self.ws = ws

def reset(self):
"""The enclosure should restore itself to a started state.
Typically this would be represented by the eyes being 'open'
and the mouth reset to its default (smile or blank).
"""
self.ws.emit(Message("enclosure.reset"))

def system_reset(self):
"""The enclosure hardware should reset any CPUs, etc."""
self.ws.emit(Message("enclosure.system.reset"))

def system_mute(self):
"""Turn off the system microphone (not listening for wakeword)."""
self.ws.emit(Message("enclosure.system.mute"))

def system_unmute(self):
"""Turn the system microphone on (listening for wakeword)."""
self.ws.emit(Message("enclosure.system.unmute"))

def system_blink(self, times):
"""The 'eyes' should blink the given number of times.
Args:
times (int): number of times to blink
"""
self.ws.emit(Message("enclosure.system.blink", {'times': times}))

def eyes_on(self):
"""Illuminate or show the eyes."""
self.ws.emit(Message("enclosure.eyes.on"))

def eyes_off(self):
"""Turn off or hide the eyes."""
self.ws.emit(Message("enclosure.eyes.off"))

def eyes_blink(self, side):
"""Make the eyes blink
Args:
side (str): 'r', 'l', or 'b' for 'right', 'left' or 'both'
"""
self.ws.emit(Message("enclosure.eyes.blink", {'side': side}))

def eyes_narrow(self):
"""Make the eyes look narrow, like a squint"""
self.ws.emit(Message("enclosure.eyes.narrow"))

def eyes_look(self, side):
"""Make the eyes look to the given side
Args:
side (str): 'r' for right
'l' for left
'u' for up
'd' for down
'c' for crossed
"""
self.ws.emit(Message("enclosure.eyes.look", {'side': side}))

def eyes_color(self, r=255, g=255, b=255):
"""Change the eye color to the given RGB color
Args:
r (int): 0-255, red value
g (int): 0-255, green value
b (int): 0-255, blue value
"""
self.ws.emit(Message("enclosure.eyes.color",
{'r': r, 'g': g, 'b': b}))

def eyes_brightness(self, level=30):
"""Set the brightness of the eyes in the display.
Args:
level (int): 1-30, bigger numbers being brighter
"""
self.ws.emit(Message("enclosure.eyes.level", {'level': level}))

def eyes_reset(self):
"""Restore the eyes to their default (ready) state."""
self.ws.emit(Message("enclosure.eyes.reset"))

def eyes_timed_spin(self, length):
"""Make the eyes 'roll' for the given time.
Args:
length (int): duration in milliseconds of roll, None = forever
"""
self.ws.emit(Message("enclosure.eyes.timedspin",
{'length': length}))

def eyes_volume(self, volume):
"""Indicate the volume using the eyes
Args:
volume (int): 0 to 11
"""
self.ws.emit(Message("enclosure.eyes.volume", {'volume': volume}))

def mouth_reset(self):
"""Restore the mouth display to normal (blank)"""
self.ws.emit(Message("enclosure.mouth.reset"))

def mouth_talk(self):
"""Show a generic 'talking' animation for non-synched speech"""
self.ws.emit(Message("enclosure.mouth.talk"))

def mouth_think(self):
"""Show a 'thinking' image or animation"""
self.ws.emit(Message("enclosure.mouth.think"))

def mouth_listen(self):
"""Show a 'thinking' image or animation"""
self.ws.emit(Message("enclosure.mouth.listen"))

def mouth_smile(self):
"""Show a 'smile' image or animation"""
self.ws.emit(Message("enclosure.mouth.smile"))

def mouth_viseme(self, code):
"""Display a viseme mouth shape for synched speech
Args:
code (int): 0 = shape for sounds like 'y' or 'aa'
1 = shape for sounds like 'aw'
2 = shape for sounds like 'uh' or 'r'
3 = shape for sounds like 'th' or 'sh'
4 = neutral shape for no sound
5 = shape for sounds like 'f' or 'v'
6 = shape for sounds like 'oy' or 'ao'
"""
self.ws.emit(Message("enclosure.mouth.viseme", {'code': code}))

def mouth_text(self, text=""):
"""Display text (scrolling as needed)
Args:
text (str): text string to display
"""
self.ws.emit(Message("enclosure.mouth.text", {'text': text}))

def weather_display(self, img_code, temp):
"""Show a weather icon (deprecated)"""
self.ws.emit(Message("enclosure.weather.display",
{'img_code': img_code, 'temp': temp}))

def activate_mouth_events(self):
"""Enable movement of the mouth with speech"""
self.ws.emit(Message('enclosure.mouth.events.activate'))

def deactivate_mouth_events(self):
"""Disable movement of the mouth with speech"""
self.ws.emit(Message('enclosure.mouth.events.deactivate'))
2 changes: 1 addition & 1 deletion mycroft/client/enclosure/version.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.1.14
0.1.20
3 changes: 2 additions & 1 deletion mycroft/client/speech/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,8 @@ def handle_paired(event):


def handle_open():
EnclosureAPI(ws).system_reset()
# Reset the UI to indicate ready for speech processing
EnclosureAPI(ws).reset()


def connect():
Expand Down
4 changes: 2 additions & 2 deletions mycroft/identity/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class IdentityManager(object):
@staticmethod
def load():
try:
with FileSystemAccess('identity').open('identity.json', 'r') as f:
with FileSystemAccess('identity').open('identity2.json', 'r') as f:
IdentityManager.__identity = DeviceIdentity(**json.load(f))
except:
IdentityManager.__identity = DeviceIdentity()
Expand All @@ -49,7 +49,7 @@ def load():
def save(login=None):
if login:
IdentityManager.update(login)
with FileSystemAccess('identity').open('identity.json', 'w') as f:
with FileSystemAccess('identity').open('identity2.json', 'w') as f:
json.dump(IdentityManager.__identity.__dict__, f)

@staticmethod
Expand Down

0 comments on commit 0ecc736

Please sign in to comment.