Move Mark-1 specific image code from api to mark-1 enclosure (#1767)
* Fix removing of the active user * Move image drawing routines to enclosure clientpull/1773/head
parent
129b9456e9
commit
80a37ec46a
|
@ -13,6 +13,7 @@
|
|||
# limitations under the License.
|
||||
#
|
||||
import time
|
||||
from PIL import Image
|
||||
|
||||
|
||||
class EnclosureMouth:
|
||||
|
@ -37,6 +38,7 @@ class EnclosureMouth:
|
|||
self.bus.on('enclosure.mouth.viseme', self.viseme)
|
||||
self.bus.on('enclosure.mouth.text', self.text)
|
||||
self.bus.on('enclosure.mouth.display', self.display)
|
||||
self.bus.on('enclosure.mouth.display_image', self.display_image)
|
||||
self.bus.on('enclosure.weather.display', self.display_weather)
|
||||
|
||||
def reset(self, event=None):
|
||||
|
@ -70,23 +72,22 @@ class EnclosureMouth:
|
|||
text = event.data.get("text", text)
|
||||
self.writer.write("mouth.text=" + text)
|
||||
|
||||
def display(self, event=None):
|
||||
code = ""
|
||||
xOffset = ""
|
||||
yOffset = ""
|
||||
clearPrevious = ""
|
||||
if event and event.data:
|
||||
code = event.data.get("img_code", code)
|
||||
xOffset = int(event.data.get("xOffset", xOffset))
|
||||
yOffset = int(event.data.get("yOffset", yOffset))
|
||||
clearPrevious = event.data.get("clearPrev", clearPrevious)
|
||||
def __display(self, code, clear_previous, x_offset, y_offset):
|
||||
""" Write the encoded image to enclosure screen.
|
||||
|
||||
clearPrevious = int(str(clearPrevious) == "True")
|
||||
clearPrevious = "cP=" + str(clearPrevious) + ","
|
||||
x_offset = "x=" + str(xOffset) + ","
|
||||
y_offset = "y=" + str(yOffset) + ","
|
||||
Arguments:
|
||||
code (str): encoded image to display
|
||||
clean_previous (str): if "True" will clear the screen before
|
||||
drawing.
|
||||
x_offset (int): x direction offset
|
||||
y_offset (int): y direction offset
|
||||
"""
|
||||
clear_previous = int(str(clear_previous) == "True")
|
||||
clear_previous = "cP=" + str(clear_previous) + ","
|
||||
x_offset = "x=" + str(x_offset) + ","
|
||||
y_offset = "y=" + str(y_offset) + ","
|
||||
|
||||
message = "mouth.icon=" + x_offset + y_offset + clearPrevious + code
|
||||
message = "mouth.icon=" + x_offset + y_offset + clear_previous + code
|
||||
# Check if message exceeds Arduino's serial buffer input limit 64 bytes
|
||||
if len(message) > 60:
|
||||
message1 = message[:31]
|
||||
|
@ -101,6 +102,136 @@ class EnclosureMouth:
|
|||
time.sleep(0.1)
|
||||
self.writer.write(message)
|
||||
|
||||
def display(self, event=None):
|
||||
""" Display a Mark-1 specific code.
|
||||
Arguments:
|
||||
event (Message): messagebus message with data to display
|
||||
"""
|
||||
code = ""
|
||||
x_offset = ""
|
||||
y_offset = ""
|
||||
clear_previous = ""
|
||||
if event and event.data:
|
||||
code = event.data.get("img_code", code)
|
||||
x_offset = int(event.data.get("xOffset", xOffset))
|
||||
y_offset = int(event.data.get("yOffset", yOffset))
|
||||
clear_previous = event.data.get("clearPrev", clearPrevious)
|
||||
self.__display(code, clear_previous, x_offset, y_offset)
|
||||
|
||||
def display_image(self, event=None):
|
||||
""" Display an image on the enclosure.
|
||||
|
||||
The method uses PIL to convert the image supplied into a code
|
||||
suitable for the Mark-1 display.
|
||||
|
||||
Arguments:
|
||||
event (Message): messagebus message with data to display
|
||||
"""
|
||||
if not event:
|
||||
return
|
||||
|
||||
image_absolute_path = event.data['img_path']
|
||||
refresh = event.data['clearPrev']
|
||||
invert = event.data['invert']
|
||||
x_offset = event.data['xOffset']
|
||||
y_offset = event.data['yOffset']
|
||||
threshold = event.data.get('threshold', 70) # default threshold
|
||||
# to understand how this funtion works you need to understand how the
|
||||
# Mark I arduino proprietary encoding works to display to the faceplate
|
||||
img = Image.open(image_absolute_path).convert("RGBA")
|
||||
img2 = Image.new('RGBA', img.size, (255, 255, 255))
|
||||
width = img.size[0]
|
||||
height = img.size[1]
|
||||
|
||||
# strips out alpha value and blends it with the RGB values
|
||||
img = Image.alpha_composite(img2, img)
|
||||
img = img.convert("L")
|
||||
|
||||
# crop image to only allow a max width of 16
|
||||
if width > 32:
|
||||
img = img.crop((0, 0, 32, height))
|
||||
width = img.size[0]
|
||||
height = img.size[1]
|
||||
|
||||
# crop the image to limit the max height of 8
|
||||
if height > 8:
|
||||
img = img.crop((0, 0, width, 8))
|
||||
width = img.size[0]
|
||||
height = img.size[1]
|
||||
|
||||
encode = ""
|
||||
|
||||
# Each char value represents a width number starting with B=1
|
||||
# then increment 1 for the next. ie C=2
|
||||
width_codes = ['B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
|
||||
'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
|
||||
'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a']
|
||||
|
||||
height_codes = ['B', 'C', 'D', 'E', 'F', 'G', 'H', 'I']
|
||||
|
||||
encode += width_codes[width - 1]
|
||||
encode += height_codes[height - 1]
|
||||
# Turn the image pixels into binary values 1's and 0's
|
||||
# the Mark I face plate encoding uses binary values to
|
||||
# binary_values returns a list of 1's and 0s'. ie ['1', '1', '0', ...]
|
||||
binary_values = []
|
||||
for i in range(width):
|
||||
for j in range(height):
|
||||
if img.getpixel((i, j)) < threshold:
|
||||
if invert is False:
|
||||
binary_values.append('1')
|
||||
else:
|
||||
binary_values.append('0')
|
||||
else:
|
||||
if invert is False:
|
||||
binary_values.append('0')
|
||||
else:
|
||||
binary_values.append('1')
|
||||
|
||||
# these values are used to determine how binary values
|
||||
# needs to be grouped together
|
||||
number_of_top_pixel = 0
|
||||
number_of_bottom_pixel = 0
|
||||
|
||||
if height > 4:
|
||||
number_of_top_pixel = 4
|
||||
number_of_bottom_pixel = height - 4
|
||||
else:
|
||||
number_of_top_pixel = height
|
||||
|
||||
# this loop will group together the individual binary values
|
||||
# ie. binary_list = ['1111', '001', '0101', '100']
|
||||
binary_list = []
|
||||
binary_code = ''
|
||||
increment = 0
|
||||
alternate = False
|
||||
for val in binary_values:
|
||||
binary_code += val
|
||||
increment += 1
|
||||
if increment == number_of_top_pixel and alternate is False:
|
||||
# binary code is reversed for encoding
|
||||
binary_list.append(binary_code[::-1])
|
||||
increment = 0
|
||||
binary_code = ''
|
||||
alternate = True
|
||||
elif increment == number_of_bottom_pixel and alternate is True:
|
||||
binary_list.append(binary_code[::-1])
|
||||
increment = 0
|
||||
binary_code = ''
|
||||
alternate = False
|
||||
|
||||
# Code to let the Makrk I arduino know where to place the
|
||||
# pixels on the faceplate
|
||||
pixel_codes = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
|
||||
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P']
|
||||
|
||||
for binary_values in binary_list:
|
||||
number = int(binary_values, 2)
|
||||
pixel_code = pixel_codes[number]
|
||||
encode += pixel_code
|
||||
|
||||
self.__display(encode, refresh, x_offset, y_offset)
|
||||
|
||||
def display_weather(self, event=None):
|
||||
if event and event.data:
|
||||
# Convert img_code to icon
|
||||
|
|
|
@ -12,8 +12,6 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
from PIL import Image
|
||||
|
||||
from .display_manager import DisplayManager
|
||||
from mycroft.messagebus.message import Message
|
||||
|
||||
|
@ -245,128 +243,26 @@ class EnclosureAPI:
|
|||
'yOffset': y,
|
||||
'clearPrev': refresh}))
|
||||
|
||||
def mouth_display_png(self, image_absolute_path, threshold=70,
|
||||
def mouth_display_png(self, image_absolute_path,
|
||||
invert=False, x=0, y=0, refresh=True):
|
||||
"""Converts a png image into the appropriate encoding for the
|
||||
Arduino Mark I enclosure.
|
||||
""" Send an image to the enclosure.
|
||||
|
||||
NOTE: extract this out of api.py when re structuing the
|
||||
enclosure folder
|
||||
|
||||
Args:
|
||||
image_absolute_path (string): The absolute path of the image
|
||||
threshold (int): The value ranges from 0 to 255. The pixel will
|
||||
draw on the faceplate it the value is below a
|
||||
threshold
|
||||
invert (bool): inverts the image being drawn.
|
||||
x (int): x offset for image
|
||||
y (int): y offset for image
|
||||
refresh (bool): specify whether to clear the faceplate before
|
||||
displaying the new image or not.
|
||||
Useful if you'd like to display muliple images
|
||||
on the faceplate at once.
|
||||
Args:
|
||||
image_absolute_path (string): The absolute path of the image
|
||||
invert (bool): inverts the image being drawn.
|
||||
x (int): x offset for image
|
||||
y (int): y offset for image
|
||||
refresh (bool): specify whether to clear the faceplate before
|
||||
displaying the new image or not.
|
||||
Useful if you'd like to display muliple images
|
||||
on the faceplate at once.
|
||||
"""
|
||||
self.display_manager.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
|
||||
img = Image.open(image_absolute_path).convert("RGBA")
|
||||
img2 = Image.new('RGBA', img.size, (255, 255, 255))
|
||||
width = img.size[0]
|
||||
height = img.size[1]
|
||||
|
||||
# strips out alpha value and blends it with the RGB values
|
||||
img = Image.alpha_composite(img2, img)
|
||||
img = img.convert("L")
|
||||
|
||||
# crop image to only allow a max width of 16
|
||||
if width > 32:
|
||||
img = img.crop((0, 0, 32, height))
|
||||
width = img.size[0]
|
||||
height = img.size[1]
|
||||
|
||||
# crop the image to limit the max height of 8
|
||||
if height > 8:
|
||||
img = img.crop((0, 0, width, 8))
|
||||
width = img.size[0]
|
||||
height = img.size[1]
|
||||
|
||||
encode = ""
|
||||
|
||||
# Each char value represents a width number starting with B=1
|
||||
# then increment 1 for the next. ie C=2
|
||||
width_codes = ['B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
|
||||
'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
|
||||
'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a']
|
||||
|
||||
height_codes = ['B', 'C', 'D', 'E', 'F', 'G', 'H', 'I']
|
||||
|
||||
encode += width_codes[width - 1]
|
||||
encode += height_codes[height - 1]
|
||||
|
||||
# Turn the image pixels into binary values 1's and 0's
|
||||
# the Mark I face plate encoding uses binary values to
|
||||
# binary_values returns a list of 1's and 0s'. ie ['1', '1', '0', ...]
|
||||
binary_values = []
|
||||
for i in range(width):
|
||||
for j in range(height):
|
||||
if img.getpixel((i, j)) < threshold:
|
||||
if invert is False:
|
||||
binary_values.append('1')
|
||||
else:
|
||||
binary_values.append('0')
|
||||
else:
|
||||
if invert is False:
|
||||
binary_values.append('0')
|
||||
else:
|
||||
binary_values.append('1')
|
||||
|
||||
# these values are used to determine how binary values
|
||||
# needs to be grouped together
|
||||
number_of_top_pixel = 0
|
||||
number_of_bottom_pixel = 0
|
||||
|
||||
if height > 4:
|
||||
number_of_top_pixel = 4
|
||||
number_of_bottom_pixel = height - 4
|
||||
else:
|
||||
number_of_top_pixel = height
|
||||
|
||||
# this loop will group together the individual binary values
|
||||
# ie. binary_list = ['1111', '001', '0101', '100']
|
||||
binary_list = []
|
||||
binary_code = ''
|
||||
increment = 0
|
||||
alternate = False
|
||||
for val in binary_values:
|
||||
binary_code += val
|
||||
increment += 1
|
||||
if increment == number_of_top_pixel and alternate is False:
|
||||
# binary code is reversed for encoding
|
||||
binary_list.append(binary_code[::-1])
|
||||
increment = 0
|
||||
binary_code = ''
|
||||
alternate = True
|
||||
elif increment == number_of_bottom_pixel and alternate is True:
|
||||
binary_list.append(binary_code[::-1])
|
||||
increment = 0
|
||||
binary_code = ''
|
||||
alternate = False
|
||||
|
||||
# Code to let the Makrk I arduino know where to place the
|
||||
# pixels on the faceplate
|
||||
pixel_codes = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
|
||||
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P']
|
||||
|
||||
for binary_values in binary_list:
|
||||
number = int(binary_values, 2)
|
||||
pixel_code = pixel_codes[number]
|
||||
encode += pixel_code
|
||||
|
||||
self.bus.emit(Message("enclosure.mouth.display",
|
||||
{'img_code': encode,
|
||||
self.bus.emit(Message("enclosure.mouth.display_image",
|
||||
{'img_path': image_absolute_path,
|
||||
'xOffset': x,
|
||||
'yOffset': y,
|
||||
'invert': invert,
|
||||
'clearPrev': refresh}))
|
||||
|
||||
def weather_display(self, img_code, temp):
|
||||
|
|
|
@ -153,11 +153,12 @@ def init_display_manager_bus_connection():
|
|||
|
||||
# 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]
|
||||
display_manager = DisplayManager()
|
||||
should_remove = [True]
|
||||
|
||||
def check_flag(flag):
|
||||
if flag[0] is True:
|
||||
remove_active()
|
||||
display_manager.remove_active()
|
||||
|
||||
def set_delay(event=None):
|
||||
should_remove[0] = True
|
||||
|
@ -172,10 +173,10 @@ def init_display_manager_bus_connection():
|
|||
def remove_wake_word():
|
||||
data = _read_data()
|
||||
if "active_skill" in data and data["active_skill"] == "wakeword":
|
||||
remove_active()
|
||||
display_manager.remove_active()
|
||||
|
||||
def set_wakeword_skill(event=None):
|
||||
set_active("wakeword")
|
||||
display_manager.set_active("wakeword")
|
||||
Timer(10, remove_wake_word).start()
|
||||
|
||||
bus = WebsocketClient()
|
||||
|
|
Loading…
Reference in New Issue