Skip to content

Commit

Permalink
Merge d56c9ed into 520f3db
Browse files Browse the repository at this point in the history
  • Loading branch information
rm-hull committed Sep 25, 2020
2 parents 520f3db + d56c9ed commit 3b6c60a
Show file tree
Hide file tree
Showing 3 changed files with 149 additions and 42 deletions.
2 changes: 2 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ ChangeLog
+------------+---------------------------------------------------------------------+------------+
| Version | Description | Date |
+============+=====================================================================+============+
| *TBC* | * Add cmdline opt: "pcf8574" and "bitbang_6800" interfaces | |
+------------+---------------------------------------------------------------------+------------+
| **1.17.0** | * Added --inverse option for ST7735 to cmdline opt | 2020/09/25 |
| | * Allow SPI interface to define reset duration | |
| | * Change bitmap_font to be compatible with Pillow < 7.0 | |
Expand Down
71 changes: 46 additions & 25 deletions luma/core/cmdline.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ def get_interface_types():
:rtype: list
"""
return get_choices('luma.core.interface.serial')
return get_choices('luma.core.interface.serial') + get_choices('luma.core.interface.parallel')


def get_display_types():
Expand Down Expand Up @@ -121,7 +121,7 @@ def load_config(path):
return args


class make_serial(object):
class make_interface(object):
"""
Serial factory.
"""
Expand All @@ -133,23 +133,17 @@ def i2c(self):
from luma.core.interface.serial import i2c
return i2c(port=self.opts.i2c_port, address=self.opts.i2c_address)

def bitbang(self):
from luma.core.interface.serial import bitbang
GPIO = self.__init_alternative_GPIO()
return bitbang(transfer_size=self.opts.spi_transfer_size,
reset_hold_time=self.opts.gpio_reset_hold_time,
reset_release_time=self.opts.gpio_reset_release_time,
gpio=self.gpio or GPIO)

def spi(self):
from luma.core.interface.serial import spi
if hasattr(self.opts, 'gpio') and self.opts.gpio is not None:
GPIO = importlib.import_module(self.opts.gpio)

if hasattr(self.opts, 'gpio_mode') and self.opts.gpio_mode is not None:
(packageName, _, attrName) = self.opts.gpio_mode.rpartition('.')
pkg = importlib.import_module(packageName)
mode = getattr(pkg, attrName)
GPIO.setmode(mode)
else:
GPIO.setmode(GPIO.BCM)

atexit.register(GPIO.cleanup)
else:
GPIO = None

GPIO = self.__init_alternative_GPIO()
return spi(port=self.opts.spi_port,
device=self.opts.spi_device,
bus_speed_hz=self.opts.spi_bus_speed,
Expand All @@ -172,6 +166,33 @@ def ftdi_i2c(self):
from luma.core.interface.serial import ftdi_i2c
return ftdi_i2c(address=self.opts.i2c_address)

def pcf8574(self):
from luma.core.interface.serial import pcf8574
return pcf8574(port=self.opts.i2c_port, address=self.opts.i2c_address)

def bitbang_6800(self):
from luma.core.interface.parallel import bitbang_6800
GPIO = self.__init_alternative_GPIO()
return bitbang_6800(gpio=self.gpio or GPIO)

def __init_alternative_GPIO(self):
if hasattr(self.opts, 'gpio') and self.opts.gpio is not None:
GPIO = importlib.import_module(self.opts.gpio)

if hasattr(self.opts, 'gpio_mode') and self.opts.gpio_mode is not None:
(packageName, _, attrName) = self.opts.gpio_mode.rpartition('.')
pkg = importlib.import_module(packageName)
mode = getattr(pkg, attrName)
GPIO.setmode(mode)
else:
GPIO.setmode(GPIO.BCM)

atexit.register(GPIO.cleanup)
else:
GPIO = None

return GPIO


def create_device(args, display_types=None):
"""
Expand All @@ -187,16 +208,16 @@ def create_device(args, display_types=None):
if args.display in display_types.get('oled'):
import luma.oled.device
Device = getattr(luma.oled.device, args.display)
Serial = getattr(make_serial(args), args.interface)
device = Device(serial_interface=Serial(), **vars(args))
interface = getattr(make_interface(args), args.interface)
device = Device(serial_interface=interface(), **vars(args))

elif args.display in display_types.get('lcd'):
import luma.lcd.device
Device = getattr(luma.lcd.device, args.display)
serial = getattr(make_serial(args), args.interface)()
backlight_params = dict(gpio=serial._gpio, gpio_LIGHT=args.gpio_backlight, active_low=args.backlight_active == "low")
interface = getattr(make_interface(args), args.interface)()
backlight_params = dict(gpio=interface._gpio, gpio_LIGHT=args.gpio_backlight, active_low=args.backlight_active == "low")
params = dict(vars(args), **backlight_params)
device = Device(serial_interface=serial, **params)
device = Device(serial_interface=interface, **params)
try:
import luma.lcd.aux
luma.lcd.aux.backlight(**backlight_params).enable(True)
Expand All @@ -207,8 +228,8 @@ def create_device(args, display_types=None):
import luma.led_matrix.device
from luma.core.interface.serial import noop
Device = getattr(luma.led_matrix.device, args.display)
Serial = getattr(make_serial(args, gpio=noop()), args.interface)
device = Device(serial_interface=Serial(), **vars(args))
interface = getattr(make_interface(args, gpio=noop()), args.interface)
device = Device(serial_interface=interface(), **vars(args))

elif args.display in display_types.get('emulator'):
import luma.emulator.device
Expand Down Expand Up @@ -239,7 +260,7 @@ def create_parser(description):
general_group.add_argument('--width', type=int, default=128, help='Width of the device in pixels')
general_group.add_argument('--height', type=int, default=64, help='Height of the device in pixels')
general_group.add_argument('--rotate', '-r', type=int, default=0, help='Rotation factor. Allowed values are: {0}'.format(', '.join([str(x) for x in rotation_choices])), choices=rotation_choices, metavar='ROTATION')
general_group.add_argument('--interface', '-i', type=str, default=interface_types[0], help='Serial interface type. Allowed values are: {0}'.format(', '.join(interface_types)), choices=interface_types, metavar='INTERFACE')
general_group.add_argument('--interface', '-i', type=str, default=interface_types[0], help='Interface type. Allowed values are: {0}'.format(', '.join(interface_types)), choices=interface_types, metavar='INTERFACE')

i2c_group = parser.add_argument_group('I2C')
i2c_group.add_argument('--i2c-port', type=int, default=1, help='I2C bus number')
Expand Down
118 changes: 101 additions & 17 deletions tests/test_cmdline.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
from unittest.mock import patch, Mock

from luma.core import cmdline, error
from luma.core.interface.serial import __all__ as iface_types
from luma.core.interface.serial import __all__ as serial_iface_types
from luma.core.interface.parallel import __all__ as parallel_iface_types

from helpers import (get_reference_file, i2c_error,
rpi_gpio_missing, spidev_missing)
Expand Down Expand Up @@ -40,7 +41,19 @@ def test_get_interface_types():
"""
Enumerate interface types.
"""
assert cmdline.get_interface_types() == iface_types
assert cmdline.get_interface_types() == serial_iface_types + parallel_iface_types


def test_ensure_cmdline_opt_contains_all_interfaces():
"""
Checks that the cmdline make_interface factory contains initializers for all interface classes
"""
class opts:
pass

factory = cmdline.make_interface(opts)
for interface in cmdline.get_interface_types():
assert hasattr(factory, interface)


def test_get_display_types():
Expand Down Expand Up @@ -126,29 +139,29 @@ def test_create_parser():
assert args.config == test_config_file


def test_make_serial_i2c():
def test_make_interface_i2c():
"""
:py:func:`luma.core.cmdline.make_serial.i2c` returns an I2C instance.
:py:func:`luma.core.cmdline.make_interface.i2c` returns an I2C instance.
"""
class opts:
i2c_port = 200
i2c_address = 0x710

path_name = '/dev/i2c-{}'.format(opts.i2c_port)
fake_open = i2c_error(path_name, errno.ENOENT)
factory = cmdline.make_serial(opts)
factory = cmdline.make_interface(opts)

with patch('os.open', fake_open):
with pytest.raises(error.DeviceNotFoundError):
factory.i2c()


def test_make_serial_spi():
def test_make_interface_spi():
"""
:py:func:`luma.core.cmdline.make_serial.spi` returns an SPI instance.
:py:func:`luma.core.cmdline.make_interface.spi` returns an SPI instance.
"""
try:
factory = cmdline.make_serial(test_spi_opts)
factory = cmdline.make_interface(test_spi_opts)
assert 'luma.core.interface.serial.spi' in repr(factory.spi())
except ImportError:
# non-rpi platform, e.g. macos
Expand All @@ -158,9 +171,9 @@ def test_make_serial_spi():
pytest.skip('{0} ({1})'.format(type(e).__name__, str(e)))


def test_make_serial_spi_alt_gpio():
def test_make_interface_spi_alt_gpio():
"""
:py:func:`luma.core.cmdline.make_serial.spi` returns an SPI instance
:py:func:`luma.core.cmdline.make_interface.spi` returns an SPI instance
when using an alternative GPIO implementation.
"""
class opts(test_spi_opts):
Expand All @@ -170,7 +183,7 @@ class opts(test_spi_opts):
'fake_gpio': Mock(unsafe=True)
}):
try:
factory = cmdline.make_serial(opts)
factory = cmdline.make_interface(opts)
assert 'luma.core.interface.serial.spi' in repr(factory.spi())
except ImportError:
pytest.skip(spidev_missing)
Expand All @@ -179,6 +192,77 @@ class opts(test_spi_opts):
pytest.skip('{0} ({1})'.format(type(e).__name__, str(e)))


def test_make_interface_bitbang():
"""
:py:func:`luma.core.cmdline.make_interface.bitbang` returns an BitBang instance.
"""
try:
factory = cmdline.make_interface(test_spi_opts)
assert 'luma.core.interface.serial.bitbang' in repr(factory.bitbang())
except ImportError:
# non-rpi platform, e.g. macos
pytest.skip(rpi_gpio_missing)
except error.UnsupportedPlatform as e:
# non-rpi platform, e.g. ubuntu 64-bit
pytest.skip('{0} ({1})'.format(type(e).__name__, str(e)))


def test_make_interface_pcf8574():
"""
:py:func:`luma.core.cmdline.make_interface.pcf8574` returns an pcf8574 instance.
"""
class opts:
i2c_port = 200
i2c_address = 0x710

path_name = '/dev/i2c-{}'.format(opts.i2c_port)
fake_open = i2c_error(path_name, errno.ENOENT)
factory = cmdline.make_interface(opts)

with patch('os.open', fake_open):
with pytest.raises(error.DeviceNotFoundError):
factory.pcf8574()


def test_make_interface_bitbang_6800():
"""
:py:func:`luma.core.cmdline.make_interface.bitbang_6800` returns a Bitbang-6800 instance.
"""
class opts:
pass

try:
factory = cmdline.make_interface(opts)
assert 'luma.core.interface.parallel.bitbang_6800' in repr(factory.bitbang_6800())
except ImportError:
# non-rpi platform, e.g. macos
pytest.skip(rpi_gpio_missing)
except error.UnsupportedPlatform as e:
# non-rpi platform, e.g. ubuntu 64-bit
pytest.skip('{0} ({1})'.format(type(e).__name__, str(e)))


def test_make_interface_bitbang_6800_alt_gpio():
"""
:py:func:`luma.core.cmdline.make_interface.bitbang_6800` returns an Bitbang-6800 instance
when using an alternative GPIO implementation.
"""
class opts():
gpio = 'fake_gpio'

with patch.dict('sys.modules', **{
'fake_gpio': Mock(unsafe=True)
}):
try:
factory = cmdline.make_interface(opts)
assert 'luma.core.interface.parallel.bitbang_6800' in repr(factory.bitbang_6800())
except ImportError:
pytest.skip(spidev_missing)
except error.DeviceNotFoundError as e:
# non-rpi platform, e.g. ubuntu 64-bit
pytest.skip('{0} ({1})'.format(type(e).__name__, str(e)))


def test_create_device():
"""
:py:func:`luma.core.cmdline.create_device` returns ``None`` for unknown
Expand Down Expand Up @@ -291,29 +375,29 @@ class args(test_spi_opts):


@patch('pyftdi.spi.SpiController')
def test_make_serial_ftdi_spi(mock_controller):
def test_make_interface_ftdi_spi(mock_controller):
"""
:py:func:`luma.core.cmdline.make_serial.ftdi_spi` returns an SPI instance.
:py:func:`luma.core.cmdline.make_interface.ftdi_spi` returns an SPI instance.
"""
class opts(test_spi_opts):
ftdi_device = 'ftdi://dummy'
gpio_data_command = 5
gpio_reset = 6
gpio_backlight = 7

factory = cmdline.make_serial(opts)
factory = cmdline.make_interface(opts)
assert 'luma.core.interface.serial.spi' in repr(factory.ftdi_spi())


@patch('pyftdi.i2c.I2cController')
def test_make_serial_ftdi_i2c(mock_controller):
def test_make_interface_ftdi_i2c(mock_controller):
"""
:py:func:`luma.core.cmdline.make_serial.ftdi_i2c` returns an I2C instance.
:py:func:`luma.core.cmdline.make_interface.ftdi_i2c` returns an I2C instance.
"""
class opts:
ftdi_device = 'ftdi://dummy'
i2c_port = 200
i2c_address = 0x710

factory = cmdline.make_serial(opts)
factory = cmdline.make_interface(opts)
assert 'luma.core.interface.serial.i2c' in repr(factory.ftdi_i2c())

0 comments on commit 3b6c60a

Please sign in to comment.