Added luma.oled display support
This commit is contained in:
parent
d3e52ba944
commit
6b43a5e592
8 changed files with 334 additions and 9 deletions
|
@ -11,11 +11,6 @@ Backends
|
||||||
platypush/backend/assistant.rst
|
platypush/backend/assistant.rst
|
||||||
platypush/backend/assistant.google.rst
|
platypush/backend/assistant.google.rst
|
||||||
platypush/backend/assistant.snowboy.rst
|
platypush/backend/assistant.snowboy.rst
|
||||||
platypush/backend/bluetooth.rst
|
|
||||||
platypush/backend/bluetooth.fileserver.rst
|
|
||||||
platypush/backend/bluetooth.pushserver.rst
|
|
||||||
platypush/backend/bluetooth.scanner.rst
|
|
||||||
platypush/backend/bluetooth.scanner.ble.rst
|
|
||||||
platypush/backend/button.flic.rst
|
platypush/backend/button.flic.rst
|
||||||
platypush/backend/camera.pi.rst
|
platypush/backend/camera.pi.rst
|
||||||
platypush/backend/chat.telegram.rst
|
platypush/backend/chat.telegram.rst
|
||||||
|
@ -53,7 +48,6 @@ Backends
|
||||||
platypush/backend/sensor.distance.vl53l1x.rst
|
platypush/backend/sensor.distance.vl53l1x.rst
|
||||||
platypush/backend/sensor.envirophat.rst
|
platypush/backend/sensor.envirophat.rst
|
||||||
platypush/backend/sensor.ir.zeroborg.rst
|
platypush/backend/sensor.ir.zeroborg.rst
|
||||||
platypush/backend/sensor.leap.rst
|
|
||||||
platypush/backend/sensor.ltr559.rst
|
platypush/backend/sensor.ltr559.rst
|
||||||
platypush/backend/sensor.mcp3008.rst
|
platypush/backend/sensor.mcp3008.rst
|
||||||
platypush/backend/sensor.motion.pwm3901.rst
|
platypush/backend/sensor.motion.pwm3901.rst
|
||||||
|
|
|
@ -252,6 +252,7 @@ autodoc_mock_imports = ['googlesamples.assistant.grpc.audio_helpers',
|
||||||
'pandas',
|
'pandas',
|
||||||
'samsungtvws',
|
'samsungtvws',
|
||||||
'paramiko',
|
'paramiko',
|
||||||
|
'luma',
|
||||||
]
|
]
|
||||||
|
|
||||||
sys.path.insert(0, os.path.abspath('../..'))
|
sys.path.insert(0, os.path.abspath('../..'))
|
||||||
|
|
5
docs/source/platypush/plugins/luma.oled.rst
Normal file
5
docs/source/platypush/plugins/luma.oled.rst
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
``platypush.plugins.luma.oled``
|
||||||
|
===============================
|
||||||
|
|
||||||
|
.. automodule:: platypush.plugins.luma.oled
|
||||||
|
:members:
|
|
@ -8,7 +8,6 @@ Plugins
|
||||||
|
|
||||||
platypush/plugins/adafruit.io.rst
|
platypush/plugins/adafruit.io.rst
|
||||||
platypush/plugins/alarm.rst
|
platypush/plugins/alarm.rst
|
||||||
platypush/plugins/arduino.rst
|
|
||||||
platypush/plugins/assistant.rst
|
platypush/plugins/assistant.rst
|
||||||
platypush/plugins/assistant.echo.rst
|
platypush/plugins/assistant.echo.rst
|
||||||
platypush/plugins/assistant.google.rst
|
platypush/plugins/assistant.google.rst
|
||||||
|
@ -66,6 +65,7 @@ Plugins
|
||||||
platypush/plugins/light.hue.rst
|
platypush/plugins/light.hue.rst
|
||||||
platypush/plugins/linode.rst
|
platypush/plugins/linode.rst
|
||||||
platypush/plugins/logger.rst
|
platypush/plugins/logger.rst
|
||||||
|
platypush/plugins/luma.oled.rst
|
||||||
platypush/plugins/media.rst
|
platypush/plugins/media.rst
|
||||||
platypush/plugins/media.chromecast.rst
|
platypush/plugins/media.chromecast.rst
|
||||||
platypush/plugins/media.ctrl.rst
|
platypush/plugins/media.ctrl.rst
|
||||||
|
@ -85,7 +85,6 @@ Plugins
|
||||||
platypush/plugins/music.mpd.rst
|
platypush/plugins/music.mpd.rst
|
||||||
platypush/plugins/music.snapcast.rst
|
platypush/plugins/music.snapcast.rst
|
||||||
platypush/plugins/nmap.rst
|
platypush/plugins/nmap.rst
|
||||||
platypush/plugins/otp.rst
|
|
||||||
platypush/plugins/pihole.rst
|
platypush/plugins/pihole.rst
|
||||||
platypush/plugins/ping.rst
|
platypush/plugins/ping.rst
|
||||||
platypush/plugins/printer.cups.rst
|
platypush/plugins/printer.cups.rst
|
||||||
|
@ -98,7 +97,6 @@ Plugins
|
||||||
platypush/plugins/sound.rst
|
platypush/plugins/sound.rst
|
||||||
platypush/plugins/ssh.rst
|
platypush/plugins/ssh.rst
|
||||||
platypush/plugins/stt.rst
|
platypush/plugins/stt.rst
|
||||||
platypush/plugins/stt.deepspeech.rst
|
|
||||||
platypush/plugins/stt.picovoice.hotword.rst
|
platypush/plugins/stt.picovoice.hotword.rst
|
||||||
platypush/plugins/stt.picovoice.speech.rst
|
platypush/plugins/stt.picovoice.speech.rst
|
||||||
platypush/plugins/switch.rst
|
platypush/plugins/switch.rst
|
||||||
|
|
0
platypush/plugins/luma/__init__.py
Normal file
0
platypush/plugins/luma/__init__.py
Normal file
322
platypush/plugins/luma/oled.py
Normal file
322
platypush/plugins/luma/oled.py
Normal file
|
@ -0,0 +1,322 @@
|
||||||
|
import enum
|
||||||
|
import os
|
||||||
|
from typing import Optional, Union, Tuple, List
|
||||||
|
|
||||||
|
import luma.core.interface.serial
|
||||||
|
import luma.oled.device
|
||||||
|
from luma.core.render import canvas
|
||||||
|
from PIL import Image, ImageFont
|
||||||
|
|
||||||
|
from platypush.plugins import Plugin, action
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceInterface(enum.Enum):
|
||||||
|
I2C = 'i2c'
|
||||||
|
SPI = 'spi'
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceSlot(enum.IntEnum):
|
||||||
|
BACK = 0
|
||||||
|
FRONT = 1
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceRotation(enum.IntEnum):
|
||||||
|
ROTATE_0 = 0
|
||||||
|
ROTATE_90 = 1
|
||||||
|
ROTATE_180 = 2
|
||||||
|
ROTATE_270 = 3
|
||||||
|
|
||||||
|
|
||||||
|
class LumaOledPlugin(Plugin):
|
||||||
|
"""
|
||||||
|
Plugin to interact with small OLED-based RaspberryPi displays through the luma.oled driver.
|
||||||
|
|
||||||
|
Requires:
|
||||||
|
|
||||||
|
* **luma.oled** (``pip install git+https://github.com/rm-hull/luma.oled``)
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self,
|
||||||
|
interface: str,
|
||||||
|
device: str,
|
||||||
|
port: int = 0,
|
||||||
|
slot: int = DeviceSlot.BACK.value,
|
||||||
|
width: int = 128,
|
||||||
|
height: int = 64,
|
||||||
|
rotate: int = DeviceRotation.ROTATE_0.value,
|
||||||
|
gpio_DC: int = 24,
|
||||||
|
gpio_RST: int = 25,
|
||||||
|
bus_speed_hz: int = 8000000,
|
||||||
|
address: int = 0x3c,
|
||||||
|
cs_high: bool = False,
|
||||||
|
transfer_size: int = 4096,
|
||||||
|
spi_mode: Optional[int] = None,
|
||||||
|
font: Optional[str] = None,
|
||||||
|
font_size: int = 10,
|
||||||
|
**kwargs):
|
||||||
|
"""
|
||||||
|
:param interface: Serial interface the display is connected to (``spi`` or ``i2c``).
|
||||||
|
:param device: Display chipset type (supported: ssd1306 ssd1309, ssd1322, ssd1325, ssd1327, ssd1331, ssd1351, ssd1362, sh1106).
|
||||||
|
:param port: Device port (usually 0 or 1).
|
||||||
|
:param slot: Device slot (0 for back, 1 for front).
|
||||||
|
:param width: Display width.
|
||||||
|
:param height: Display height.
|
||||||
|
:param rotate: Display rotation (0 for no rotation, 1 for 90 degrees, 2 for 180 degrees, 3 for 270 degrees).
|
||||||
|
:param gpio_DC: [SPI only] GPIO PIN used for data (default: 24).
|
||||||
|
:param gpio_RST: [SPI only] GPIO PIN used for RST (default: 25).
|
||||||
|
:param bus_speed_hz: [SPI only] Bus speed in Hz (default: 8 MHz).
|
||||||
|
:param address: [I2C only] Device address (default: 0x3c).
|
||||||
|
:param cs_high: [SPI only] Set to True if the SPI chip select is high.
|
||||||
|
:param transfer_size: [SPI only] Maximum amount of bytes to transfer in one go (default: 4096).
|
||||||
|
:param spi_mode: [SPI only] SPI mode as two bit pattern of clock polarity and phase [CPOL|CPHA], 0-3 (default:None).
|
||||||
|
:param font: Path to a default TTF font used to display the text.
|
||||||
|
:param font_size: Font size - it only applies if ``font`` is set.
|
||||||
|
"""
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
|
iface_name = interface
|
||||||
|
interface = getattr(luma.core.interface.serial, DeviceInterface(interface).value)
|
||||||
|
|
||||||
|
if iface_name == DeviceInterface.SPI.value:
|
||||||
|
self.serial = interface(port=port, device=slot, cs_high=cs_high, gpio_DC=gpio_DC,
|
||||||
|
gpio_RST=gpio_RST, bus_speed_hz=bus_speed_hz,
|
||||||
|
transfer_size=transfer_size, spi_mode=spi_mode)
|
||||||
|
else:
|
||||||
|
self.serial = interface(port=port, address=address)
|
||||||
|
|
||||||
|
device = getattr(luma.oled.device, device)
|
||||||
|
self.device = device(self.serial, width=width, height=height, rotate=rotate)
|
||||||
|
self.canvas = canvas(self.device)
|
||||||
|
self.font = None
|
||||||
|
self.font_size = font_size
|
||||||
|
self.font = self._get_font(font, font_size)
|
||||||
|
|
||||||
|
def _get_font(self, font: Optional[str] = None, font_size: Optional[int] = None):
|
||||||
|
if font:
|
||||||
|
return ImageFont.truetype(os.path.abspath(os.path.expanduser(font)), font_size or self.font_size)
|
||||||
|
|
||||||
|
return self.font
|
||||||
|
|
||||||
|
@action
|
||||||
|
def clear(self):
|
||||||
|
"""
|
||||||
|
clear the display canvas.
|
||||||
|
"""
|
||||||
|
self.device.clear()
|
||||||
|
del self.canvas
|
||||||
|
self.canvas = canvas(self.device)
|
||||||
|
|
||||||
|
@action
|
||||||
|
def text(self, text: str, pos: Union[Tuple[int], List[int]] = (0, 0),
|
||||||
|
fill: str = 'white', font: Optional[str] = None, font_size: Optional[int] = None, clear: bool = False):
|
||||||
|
"""
|
||||||
|
Draw text on the canvas.
|
||||||
|
|
||||||
|
:param text: Text to be drawn.
|
||||||
|
:param pos: Position of the text.
|
||||||
|
:param fill: Text color (default: ``white``).
|
||||||
|
:param font: ``font`` type override.
|
||||||
|
:param font_size: ``font_size`` override.
|
||||||
|
:param clear: Set to True if you want to clear the canvas before writing the text (default: False).
|
||||||
|
"""
|
||||||
|
if clear:
|
||||||
|
self.clear()
|
||||||
|
|
||||||
|
font = self._get_font(font, font_size)
|
||||||
|
|
||||||
|
with self.canvas as draw:
|
||||||
|
draw.text(pos, text, fill=fill, font=font)
|
||||||
|
|
||||||
|
@action
|
||||||
|
def rectangle(self, xy: Optional[Union[Tuple[int], List[int]]] = None,
|
||||||
|
fill: Optional[str] = None, outline: Optional[str] = None,
|
||||||
|
width: int = 1, clear: bool = False):
|
||||||
|
"""
|
||||||
|
Draw a rectangle on the canvas.
|
||||||
|
|
||||||
|
:param xy: Two points defining the bounding box, either as [(x0, y0), (x1, y1)] or [x0, y0, x1, y1]. Default: bounding box of the device.
|
||||||
|
:param fill: Fill color - can be ``black`` or ``white``.
|
||||||
|
:param outline: Outline color - can be ``black`` or ``white``.
|
||||||
|
:param width: Figure width in pixels (default: 1).
|
||||||
|
:param clear: Set to True if you want to clear the canvas before writing the text (default: False).
|
||||||
|
"""
|
||||||
|
if clear:
|
||||||
|
self.clear()
|
||||||
|
|
||||||
|
if not xy:
|
||||||
|
xy = self.device.bounding_box
|
||||||
|
|
||||||
|
with self.canvas as draw:
|
||||||
|
draw.rectangle(xy, outline=outline, fill=fill, width=width)
|
||||||
|
|
||||||
|
@action
|
||||||
|
def arc(self, start: int, end: int, xy: Optional[Union[Tuple[int], List[int]]] = None,
|
||||||
|
fill: Optional[str] = None, outline: Optional[str] = None,
|
||||||
|
width: int = 1, clear: bool = False):
|
||||||
|
"""
|
||||||
|
Draw an arc on the canvas.
|
||||||
|
|
||||||
|
:param start: Starting angle, in degrees (measured from 3 o' clock and increasing clockwise).
|
||||||
|
:param end: Ending angle, in degrees (measured from 3 o' clock and increasing clockwise).
|
||||||
|
:param xy: Two points defining the bounding box, either as [(x0, y0), (x1, y1)] or [x0, y0, x1, y1]. Default: bounding box of the device.
|
||||||
|
:param fill: Fill color - can be ``black`` or ``white``.
|
||||||
|
:param outline: Outline color - can be ``black`` or ``white``.
|
||||||
|
:param width: Figure width in pixels (default: 1).
|
||||||
|
:param clear: Set to True if you want to clear the canvas before writing the text (default: False).
|
||||||
|
"""
|
||||||
|
if clear:
|
||||||
|
self.clear()
|
||||||
|
|
||||||
|
if not xy:
|
||||||
|
xy = self.device.bounding_box
|
||||||
|
|
||||||
|
with self.canvas as draw:
|
||||||
|
draw.arc(xy, start=start, end=end, outline=outline, fill=fill, width=width)
|
||||||
|
|
||||||
|
@action
|
||||||
|
def chord(self, start: int, end: int, xy: Optional[Union[Tuple[int], List[int]]] = None,
|
||||||
|
fill: Optional[str] = None, outline: Optional[str] = None,
|
||||||
|
width: int = 1, clear: bool = False):
|
||||||
|
"""
|
||||||
|
Same as ``arc``, but it connects the end points with a straight line.
|
||||||
|
|
||||||
|
:param start: Starting angle, in degrees (measured from 3 o' clock and increasing clockwise).
|
||||||
|
:param end: Ending angle, in degrees (measured from 3 o' clock and increasing clockwise).
|
||||||
|
:param xy: Two points defining the bounding box, either as [(x0, y0), (x1, y1)] or [x0, y0, x1, y1]. Default: bounding box of the device.
|
||||||
|
:param fill: Fill color - can be ``black`` or ``white``.
|
||||||
|
:param outline: Outline color - can be ``black`` or ``white``.
|
||||||
|
:param width: Figure width in pixels (default: 1).
|
||||||
|
:param clear: Set to True if you want to clear the canvas before writing the text (default: False).
|
||||||
|
"""
|
||||||
|
if clear:
|
||||||
|
self.clear()
|
||||||
|
|
||||||
|
if not xy:
|
||||||
|
xy = self.device.bounding_box
|
||||||
|
|
||||||
|
with self.canvas as draw:
|
||||||
|
draw.chord(xy, start=start, end=end, outline=outline, fill=fill, width=width)
|
||||||
|
|
||||||
|
@action
|
||||||
|
def pieslice(self, start: int, end: int, xy: Optional[Union[Tuple[int], List[int]]] = None,
|
||||||
|
fill: Optional[str] = None, outline: Optional[str] = None,
|
||||||
|
width: int = 1, clear: bool = False):
|
||||||
|
"""
|
||||||
|
Same as ``arc``, but it also draws straight lines between the end points and the center of the bounding box.
|
||||||
|
|
||||||
|
:param start: Starting angle, in degrees (measured from 3 o' clock and increasing clockwise).
|
||||||
|
:param end: Ending angle, in degrees (measured from 3 o' clock and increasing clockwise).
|
||||||
|
:param xy: Two points defining the bounding box, either as [(x0, y0), (x1, y1)] or [x0, y0, x1, y1]. Default: bounding box of the device.
|
||||||
|
:param fill: Fill color - can be ``black`` or ``white``.
|
||||||
|
:param outline: Outline color - can be ``black`` or ``white``.
|
||||||
|
:param width: Figure width in pixels (default: 1).
|
||||||
|
:param clear: Set to True if you want to clear the canvas before writing the text (default: False).
|
||||||
|
"""
|
||||||
|
if clear:
|
||||||
|
self.clear()
|
||||||
|
|
||||||
|
if not xy:
|
||||||
|
xy = self.device.bounding_box
|
||||||
|
|
||||||
|
with self.canvas as draw:
|
||||||
|
draw.pieslice(xy, start=start, end=end, outline=outline, fill=fill, width=width)
|
||||||
|
|
||||||
|
@action
|
||||||
|
def ellipse(self, xy: Optional[Union[Tuple[int], List[int]]] = None,
|
||||||
|
fill: Optional[str] = None, outline: Optional[str] = None,
|
||||||
|
width: int = 1, clear: bool = False):
|
||||||
|
"""
|
||||||
|
Draw an ellipse on the canvas.
|
||||||
|
|
||||||
|
:param xy: Two points defining the bounding box, either as [(x0, y0), (x1, y1)] or [x0, y0, x1, y1]. Default: bounding box of the device.
|
||||||
|
:param fill: Fill color - can be ``black`` or ``white``.
|
||||||
|
:param outline: Outline color - can be ``black`` or ``white``.
|
||||||
|
:param width: Figure width in pixels (default: 1).
|
||||||
|
:param clear: Set to True if you want to clear the canvas before writing the text (default: False).
|
||||||
|
"""
|
||||||
|
if clear:
|
||||||
|
self.clear()
|
||||||
|
|
||||||
|
if not xy:
|
||||||
|
xy = self.device.bounding_box
|
||||||
|
|
||||||
|
with self.canvas as draw:
|
||||||
|
draw.ellipse(xy, outline=outline, fill=fill, width=width)
|
||||||
|
|
||||||
|
@action
|
||||||
|
def line(self, xy: Optional[Union[Tuple[int], List[int]]] = None,
|
||||||
|
fill: Optional[str] = None, outline: Optional[str] = None,
|
||||||
|
width: int = 1, curve: bool = False, clear: bool = False):
|
||||||
|
"""
|
||||||
|
Draw a line on the canvas.
|
||||||
|
|
||||||
|
:param xy: Sequence of either 2-tuples like [(x, y), (x, y), ...] or numeric values like [x, y, x, y, ...].
|
||||||
|
:param fill: Fill color - can be ``black`` or ``white``.
|
||||||
|
:param outline: Outline color - can be ``black`` or ``white``.
|
||||||
|
:param width: Figure width in pixels (default: 1).
|
||||||
|
:param curve: Set to True for rounded edges (default: False).
|
||||||
|
:param clear: Set to True if you want to clear the canvas before writing the text (default: False).
|
||||||
|
"""
|
||||||
|
if clear:
|
||||||
|
self.clear()
|
||||||
|
|
||||||
|
if not xy:
|
||||||
|
xy = self.device.bounding_box
|
||||||
|
|
||||||
|
with self.canvas as draw:
|
||||||
|
draw.line(xy, outline=outline, fill=fill, width=width, joint='curve' if curve else None)
|
||||||
|
|
||||||
|
@action
|
||||||
|
def point(self, xy: Optional[Union[Tuple[int], List[int]]] = None,
|
||||||
|
fill: Optional[str] = None, clear: bool = False):
|
||||||
|
"""
|
||||||
|
Draw one or more points on the canvas.
|
||||||
|
|
||||||
|
:param xy: Sequence of either 2-tuples like [(x, y), (x, y), ...] or numeric values like [x, y, x, y, ...].
|
||||||
|
:param fill: Fill color - can be ``black`` or ``white``.
|
||||||
|
:param clear: Set to True if you want to clear the canvas before writing the text (default: False).
|
||||||
|
"""
|
||||||
|
if clear:
|
||||||
|
self.clear()
|
||||||
|
|
||||||
|
if not xy:
|
||||||
|
xy = self.device.bounding_box
|
||||||
|
|
||||||
|
with self.canvas as draw:
|
||||||
|
draw.point(xy, fill=fill)
|
||||||
|
|
||||||
|
@action
|
||||||
|
def polygon(self, xy: Optional[Union[Tuple[int], List[int]]] = None,
|
||||||
|
fill: Optional[str] = None, outline: Optional[str] = None,
|
||||||
|
clear: bool = False):
|
||||||
|
"""
|
||||||
|
Draw a polygon on the canvas.
|
||||||
|
|
||||||
|
:param xy: Sequence of either 2-tuples like [(x, y), (x, y), ...] or numeric values like [x, y, x, y, ...].
|
||||||
|
:param fill: Fill color - can be ``black`` or ``white``.
|
||||||
|
:param outline: Outline color - can be ``black`` or ``white``.
|
||||||
|
:param clear: Set to True if you want to clear the canvas before writing the text (default: False).
|
||||||
|
"""
|
||||||
|
if clear:
|
||||||
|
self.clear()
|
||||||
|
|
||||||
|
if not xy:
|
||||||
|
xy = self.device.bounding_box
|
||||||
|
|
||||||
|
with self.canvas as draw:
|
||||||
|
draw.polygon(xy, outline=outline, fill=fill)
|
||||||
|
|
||||||
|
@action
|
||||||
|
def image(self, image: str):
|
||||||
|
"""
|
||||||
|
Draws an image to the canvas (this will clear the existing canvas).
|
||||||
|
|
||||||
|
:param image: Image path.
|
||||||
|
"""
|
||||||
|
image = Image.open(os.path.abspath(os.path.expanduser(image)))
|
||||||
|
self.clear()
|
||||||
|
self.device.display(image)
|
||||||
|
|
||||||
|
|
||||||
|
# vim:sw=4:ts=4:et:
|
|
@ -270,3 +270,6 @@ croniter
|
||||||
|
|
||||||
# Support for Google Translate
|
# Support for Google Translate
|
||||||
# google-cloud-translate
|
# google-cloud-translate
|
||||||
|
|
||||||
|
# Support for luma.oled
|
||||||
|
# git+https://github.com/rm-hull/luma.oled
|
||||||
|
|
2
setup.py
2
setup.py
|
@ -316,5 +316,7 @@ setup(
|
||||||
'ssh': ['paramiko'],
|
'ssh': ['paramiko'],
|
||||||
# Support for clipboard integration
|
# Support for clipboard integration
|
||||||
'clipboard': ['pyperclip'],
|
'clipboard': ['pyperclip'],
|
||||||
|
# Support for luma.oled display drivers
|
||||||
|
'luma-oled': ['luma.oled @ git+https://github.com/rm-hull/luma.oled'],
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in a new issue