state manager implemented
parent
a6917c7712
commit
c0b603ab51
|
@ -37,11 +37,17 @@ from mycroft.util import play_wav, create_signal, connected, \
|
|||
from mycroft.util.audio_test import record
|
||||
from mycroft.util.log import getLogger
|
||||
from mycroft.api import is_paired
|
||||
from mycroft.client.enclosure.display_manager import run as \
|
||||
initiate_display_manager_ws
|
||||
|
||||
|
||||
__author__ = 'aatchison', 'jdorleans', 'iward'
|
||||
|
||||
LOG = getLogger("EnclosureClient")
|
||||
|
||||
# initiates the web sockets on display manager
|
||||
initiate_display_manager_ws()
|
||||
|
||||
|
||||
class EnclosureReader(Thread):
|
||||
"""
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with Mycroft Core. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
import mycroft.client.enclosure.display_manager as DisplayManager
|
||||
from mycroft.messagebus.message import Message
|
||||
from mycroft.util.log import getLogger
|
||||
from PIL import Image
|
||||
|
@ -25,6 +25,13 @@ __author__ = 'jdorleans'
|
|||
LOGGER = getLogger(__name__)
|
||||
|
||||
|
||||
'''
|
||||
API for the functions that affect the Mark I device.
|
||||
NOTE: current state management is poorly implemented,
|
||||
will be changed in the future.
|
||||
'''
|
||||
|
||||
|
||||
class EnclosureAPI:
|
||||
"""
|
||||
This API is intended to be used to interface with the hardware
|
||||
|
@ -38,8 +45,18 @@ class EnclosureAPI:
|
|||
where there is no face at all.
|
||||
"""
|
||||
|
||||
def __init__(self, ws):
|
||||
def __init__(self, ws, name=""):
|
||||
self.ws = ws
|
||||
self.name = name
|
||||
|
||||
def register(self, skill_name=""):
|
||||
"""Registers a skill as active. Used for speak() and speak_dialog()
|
||||
to 'patch' a previous implementation. Somewhat hacky.
|
||||
"""
|
||||
if self.name != "":
|
||||
DisplayManager.set_active(self.name)
|
||||
else:
|
||||
DisplayManager.set_active(skill_name)
|
||||
|
||||
def reset(self):
|
||||
"""The enclosure should restore itself to a started state.
|
||||
|
@ -136,22 +153,27 @@ class EnclosureAPI:
|
|||
def mouth_reset(self):
|
||||
"""Restore the mouth display to normal (blank)"""
|
||||
self.ws.emit(Message("enclosure.mouth.reset"))
|
||||
DisplayManager.set_active(self.name)
|
||||
|
||||
def mouth_talk(self):
|
||||
"""Show a generic 'talking' animation for non-synched speech"""
|
||||
self.ws.emit(Message("enclosure.mouth.talk"))
|
||||
DisplayManager.set_active(self.name)
|
||||
|
||||
def mouth_think(self):
|
||||
"""Show a 'thinking' image or animation"""
|
||||
self.ws.emit(Message("enclosure.mouth.think"))
|
||||
DisplayManager.set_active(self.name)
|
||||
|
||||
def mouth_listen(self):
|
||||
"""Show a 'thinking' image or animation"""
|
||||
self.ws.emit(Message("enclosure.mouth.listen"))
|
||||
DisplayManager.set_active(self.name)
|
||||
|
||||
def mouth_smile(self):
|
||||
"""Show a 'smile' image or animation"""
|
||||
self.ws.emit(Message("enclosure.mouth.smile"))
|
||||
DisplayManager.set_active(self.name)
|
||||
|
||||
def mouth_viseme(self, code):
|
||||
"""Display a viseme mouth shape for synched speech
|
||||
|
@ -171,6 +193,7 @@ class EnclosureAPI:
|
|||
Args:
|
||||
text (str): text string to display
|
||||
"""
|
||||
DisplayManager.set_active(self.name)
|
||||
self.ws.emit(Message("enclosure.mouth.text", {'text': text}))
|
||||
|
||||
def mouth_display(self, img_code="", x=0, y=0, refresh=True):
|
||||
|
@ -186,7 +209,8 @@ class EnclosureAPI:
|
|||
Useful if you'd like to display muliple images
|
||||
on the faceplate at once.
|
||||
"""
|
||||
self.ws.emit(Message("enclosure.mouth.display",
|
||||
DisplayManager.set_active(self.name)
|
||||
self.ws.emit(Message('enclosure.mouth.display',
|
||||
{'img_code': img_code,
|
||||
'xOffset': x,
|
||||
'yOffset': y,
|
||||
|
@ -212,9 +236,8 @@ class EnclosureAPI:
|
|||
displaying the new image or not.
|
||||
Useful if you'd like to display muliple images
|
||||
on the faceplate at once.
|
||||
|
||||
"
|
||||
"""
|
||||
DisplayManager.set_active(self.name)
|
||||
|
||||
# to understand how this funtion works you need to understand how the
|
||||
# Mark I arduino proprietary encoding works to display to the faceplate
|
||||
|
@ -332,6 +355,7 @@ class EnclosureAPI:
|
|||
7 = wind/mist
|
||||
temp (int): the temperature (either C or F, not indicated)
|
||||
"""
|
||||
DisplayManager.set_active(self.name)
|
||||
self.ws.emit(Message("enclosure.weather.display",
|
||||
{'img_code': img_code, 'temp': temp}))
|
||||
|
||||
|
|
|
@ -0,0 +1,159 @@
|
|||
|
||||
# Copyright 2017 Mycroft AI, Inc.
|
||||
#
|
||||
# This file is part of Mycroft Core.
|
||||
#
|
||||
# Mycroft Core is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Mycroft Core is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Mycroft Core. If not, see <http://www.gnu.org/licenses/>.
|
||||
from threading import Thread, Timer
|
||||
from mycroft.messagebus.client.ws import WebsocketClient
|
||||
from mycroft.configuration import ConfigurationManager
|
||||
from mycroft.util import get_ipc_directory
|
||||
import json
|
||||
import os
|
||||
from logging import getLogger
|
||||
|
||||
__author__ = 'connorpenrod', 'michaelnguyen'
|
||||
|
||||
|
||||
LOG = getLogger("Display Manager (mycroft.client.enclosure)")
|
||||
managerIPCDir = os.path.join(get_ipc_directory(), "managers")
|
||||
|
||||
|
||||
def write_data(dictionary):
|
||||
"""Writes the parama as JSON to the
|
||||
IPC dir (/tmp/mycroft/ipc/managers)
|
||||
args:
|
||||
dict: dictionary
|
||||
"""
|
||||
|
||||
# change read/write permissions based on if file exists or not
|
||||
path = os.path.join(managerIPCDir, "disp_info")
|
||||
permission = "r+" if os.path.isfile(path) else "w+"
|
||||
|
||||
if permission == "w+":
|
||||
os.makedirs(managerIPCDir)
|
||||
|
||||
try:
|
||||
with open(path, permission) as dispFile:
|
||||
|
||||
# check if file is empty
|
||||
if os.stat(str(dispFile.name)).st_size != 0:
|
||||
data = json.load(dispFile)
|
||||
|
||||
else:
|
||||
data = {}
|
||||
LOG.info("Display Manager is creating " + dispFile.name)
|
||||
|
||||
for key in dictionary:
|
||||
data[key] = dictionary[key]
|
||||
|
||||
dispFile.seek(0)
|
||||
dispFile.write(json.dumps(data))
|
||||
dispFile.truncate()
|
||||
|
||||
except Exception as e:
|
||||
LOG.error(e)
|
||||
|
||||
|
||||
def read_data():
|
||||
""" Reads the file in (/tmp/mycroft/ipc/managers/disp_info)
|
||||
and returns the the data as python dict
|
||||
"""
|
||||
|
||||
path = os.path.join(managerIPCDir, "disp_info")
|
||||
permission = "r" if os.path.isfile(path) else "w+"
|
||||
|
||||
if permission == "w+":
|
||||
os.makedirs(managerIPCDir)
|
||||
|
||||
data = {}
|
||||
try:
|
||||
with open(path, permission) as dispFile:
|
||||
|
||||
if os.stat(str(dispFile.name)).st_size != 0:
|
||||
data = json.load(dispFile)
|
||||
|
||||
except Exception as e:
|
||||
LOG.error(e)
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def set_active(skill_name):
|
||||
""" Sets skill name as active in the display Manager
|
||||
args:
|
||||
string: skill_name
|
||||
"""
|
||||
write_data({"active_skill": skill_name})
|
||||
LOG.info("Setting active skill to " + skill_name)
|
||||
|
||||
|
||||
def get_active():
|
||||
""" Get active skill in the display manager
|
||||
"""
|
||||
data = read_data()
|
||||
active_skill = ""
|
||||
|
||||
if "active_skill" in data:
|
||||
active_skill = data["active_skill"]
|
||||
|
||||
return active_skill
|
||||
|
||||
|
||||
def remove_active():
|
||||
""" Remove the active skill in the skill manager
|
||||
"""
|
||||
LOG.error("Removing active skill...")
|
||||
write_data({"active_skill": ""})
|
||||
|
||||
|
||||
def run():
|
||||
""" TODO: document
|
||||
"""
|
||||
|
||||
# Should remove needs to be an object so it can be referenced in functions
|
||||
# [https://stackoverflow.com/questions/986006/how-do-i-pass-a-variable-by-reference]
|
||||
should_remove = [True]
|
||||
|
||||
def check_flag(flag):
|
||||
if flag[0] is True:
|
||||
remove_active()
|
||||
|
||||
def set_delay(event=None):
|
||||
should_remove[0] = True
|
||||
Timer(2, check_flag, [should_remove]).start()
|
||||
|
||||
def set_remove_flag(event=None):
|
||||
should_remove[0] = False
|
||||
|
||||
def connect():
|
||||
ws.run_forever()
|
||||
|
||||
def remove_wake_word():
|
||||
data = read_data()
|
||||
if "active_skill" in data and data["active_skill"] == "wakeword":
|
||||
remove_active()
|
||||
|
||||
def set_wakeword_skill(event=None):
|
||||
set_active("wakeword")
|
||||
Timer(10, remove_wake_word).start()
|
||||
|
||||
ws = WebsocketClient()
|
||||
ws.on('recognizer_loop:audio_output_end', set_delay)
|
||||
ws.on('recognizer_loop:audio_output_start', set_remove_flag)
|
||||
ws.on('recognizer_loop:record_begin', set_wakeword_skill)
|
||||
|
||||
event_thread = Thread(target=connect)
|
||||
event_thread.setDaemon(True)
|
||||
event_thread.start()
|
|
@ -17,7 +17,6 @@
|
|||
|
||||
|
||||
import sys
|
||||
|
||||
from mycroft.client.enclosure import Enclosure
|
||||
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@ from mycroft.filesystem import FileSystemAccess
|
|||
from mycroft.messagebus.message import Message
|
||||
from mycroft.util.log import getLogger
|
||||
from mycroft.skills.settings import SkillSettings
|
||||
|
||||
__author__ = 'seanfitz'
|
||||
|
||||
BLACKLISTED_SKILLS = ["send_sms", "media"]
|
||||
|
@ -218,7 +219,7 @@ class MycroftSkill(object):
|
|||
def bind(self, emitter):
|
||||
if emitter:
|
||||
self.emitter = emitter
|
||||
self.enclosure = EnclosureAPI(emitter)
|
||||
self.enclosure = EnclosureAPI(emitter, self.name)
|
||||
self.__register_stop()
|
||||
|
||||
def __register_stop(self):
|
||||
|
@ -291,6 +292,8 @@ class MycroftSkill(object):
|
|||
self.emitter.emit(Message('register_vocab', {'regex': regex_str}))
|
||||
|
||||
def speak(self, utterance, expect_response=False):
|
||||
# registers the skill as being active
|
||||
self.enclosure.register(self.name)
|
||||
data = {'utterance': utterance,
|
||||
'expect_response': expect_response}
|
||||
self.emitter.emit(Message("speak", data))
|
||||
|
|
Loading…
Reference in New Issue