mirror of
https://github.com/KevinMidboe/python-gpiozero.git
synced 2025-10-29 17:50:37 +00:00
Merge pull request #558 from RPi-Distro/statusboard2
Add StatusZero and StatusBoard
This commit is contained in:
@@ -127,6 +127,20 @@ Energenie
|
||||
:inherited-members:
|
||||
:members:
|
||||
|
||||
StatusZero
|
||||
==========
|
||||
|
||||
.. autoclass:: StatusZero
|
||||
:inherited-members:
|
||||
:members:
|
||||
|
||||
StatusBoard
|
||||
===========
|
||||
|
||||
.. autoclass:: StatusBoard
|
||||
:inherited-members:
|
||||
:members:
|
||||
|
||||
SnowPi
|
||||
======
|
||||
|
||||
@@ -168,4 +182,3 @@ CompositeDevice
|
||||
|
||||
.. autoclass:: CompositeDevice(\*args, _order=None, \*\*kwargs)
|
||||
:members:
|
||||
|
||||
|
||||
@@ -90,11 +90,13 @@ digraph classes {
|
||||
ButtonBoard->HoldMixin;
|
||||
PiLiter->LEDBoard;
|
||||
PiLiterBarGraph->LEDBarGraph;
|
||||
StatusZero->LEDBoard;
|
||||
TrafficLights->LEDBoard;
|
||||
SnowPi->LEDBoard;
|
||||
PiTraffic->TrafficLights;
|
||||
PiStop->TrafficLights;
|
||||
TrafficLightsBuzzer->CompositeOutputDevice;
|
||||
StatusBoard->CompositeOutputDevice;
|
||||
FishDish->TrafficLightsBuzzer;
|
||||
TrafficHat->TrafficLightsBuzzer;
|
||||
Robot->CompositeDevice;
|
||||
@@ -115,4 +117,3 @@ digraph classes {
|
||||
PingServer->InternalDevice;
|
||||
CPUTemperature->InternalDevice;
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 247 KiB After Width: | Height: | Size: 253 KiB |
File diff suppressed because it is too large
Load Diff
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 48 KiB |
@@ -80,6 +80,8 @@ from .boards import (
|
||||
TrafficLights,
|
||||
PiTraffic,
|
||||
PiStop,
|
||||
StatusZero,
|
||||
StatusBoard,
|
||||
SnowPi,
|
||||
TrafficLightsBuzzer,
|
||||
FishDish,
|
||||
|
||||
@@ -12,7 +12,7 @@ except ImportError:
|
||||
from time import sleep
|
||||
from itertools import repeat, cycle, chain
|
||||
from threading import Lock
|
||||
from collections import OrderedDict
|
||||
from collections import OrderedDict, Counter
|
||||
|
||||
from .exc import (
|
||||
DeviceClosed,
|
||||
@@ -795,6 +795,102 @@ class PiStop(TrafficLights):
|
||||
pwm=pwm, initial_value=initial_value)
|
||||
|
||||
|
||||
class StatusZero(LEDBoard):
|
||||
"""
|
||||
Extends :class:`LEDBoard` for The Pi Hut's `STATUS Zero`_: a Pi Zero sized
|
||||
add-on board with three sets of red/green LEDs to provide a status
|
||||
indicator.
|
||||
|
||||
The following example designates the first strip the label "wifi" and the
|
||||
second "raining", and turns them green and red respectfully::
|
||||
|
||||
from gpiozero import StatusZero
|
||||
|
||||
status = StatusZero('wifi', 'raining')
|
||||
status.wifi.green.on()
|
||||
status.raining.red.on()
|
||||
|
||||
:param str \*labels:
|
||||
Specify the names of the labels you wish to designate the strips to.
|
||||
You can list up to three labels. If no labels are given, three strips
|
||||
will be initialised with names 'one', 'two', and 'three'. If some, but
|
||||
not all strips are given labels, any remaining strips will not be
|
||||
initialised.
|
||||
|
||||
.. _STATUS Zero: https://thepihut.com/statuszero
|
||||
"""
|
||||
default_labels = ('one', 'two', 'three')
|
||||
|
||||
def __init__(self, *labels, **kwargs):
|
||||
pins = (
|
||||
(17, 4),
|
||||
(22, 27),
|
||||
(9, 10),
|
||||
)
|
||||
if len(labels) == 0:
|
||||
labels = self.default_labels
|
||||
elif len(labels) > len(pins):
|
||||
raise ValueError("StatusZero doesn't support more than three labels")
|
||||
dup, count = Counter(labels).most_common(1)[0]
|
||||
if count > 1:
|
||||
raise ValueError("Duplicate label %s" % dup)
|
||||
super(StatusZero, self).__init__(_order=labels, **{
|
||||
label: LEDBoard(red=red, green=green, _order=('red', 'green'), **kwargs)
|
||||
for (green, red), label in zip(pins, labels)
|
||||
})
|
||||
|
||||
|
||||
class StatusBoard(CompositeOutputDevice):
|
||||
"""
|
||||
Extends :class:`CompositeOutputDevice` for The Pi Hut's `STATUS`_ board: a
|
||||
HAT sized add-on board with five sets of red/green LEDs and buttons to
|
||||
provide a status indicator with additional input.
|
||||
|
||||
The following example designates the first strip the label "wifi" and the
|
||||
second "raining", turns the wifi green and then activates the button to
|
||||
toggle its lights when pressed::
|
||||
|
||||
from gpiozero import StatusBoard
|
||||
|
||||
status = StatusBoard('wifi', 'raining')
|
||||
status.wifi.lights.green.on()
|
||||
status.wifi.button.when_pressed = status.wifi.lights.toggle
|
||||
|
||||
:param str \*labels:
|
||||
Specify the names of the labels you wish to designate the strips to.
|
||||
You can list up to three labels. If no labels are given, three strips
|
||||
will be initialised with names 'one' to 'five'. If some, but not all
|
||||
strips are given labels, any remaining strips will not be initialised.
|
||||
|
||||
.. _STATUS: https://thepihut.com/status
|
||||
"""
|
||||
default_labels = ('one', 'two', 'three', 'four', 'five')
|
||||
|
||||
def __init__(self, *labels, **kwargs):
|
||||
pins = (
|
||||
(17, 4, 14),
|
||||
(22, 27, 19),
|
||||
(9, 10, 15),
|
||||
(5, 11, 26),
|
||||
(13, 6, 18),
|
||||
)
|
||||
if len(labels) == 0:
|
||||
labels = self.default_labels
|
||||
elif len(labels) > len(pins):
|
||||
raise ValueError("StatusBoard doesn't support more than five labels")
|
||||
dup, count = Counter(labels).most_common(1)[0]
|
||||
if count > 1:
|
||||
raise ValueError("Duplicate label %s" % dup)
|
||||
super(StatusBoard, self).__init__(_order=labels, **{
|
||||
label: CompositeOutputDevice(
|
||||
button=Button(button),
|
||||
lights=LEDBoard(
|
||||
red=red, green=green, _order=('red', 'green'), **kwargs
|
||||
), _order=('button', 'lights'))
|
||||
for (green, red, button), label in zip(pins, labels)
|
||||
})
|
||||
|
||||
|
||||
class SnowPi(LEDBoard):
|
||||
"""
|
||||
Extends :class:`LEDBoard` for the `Ryanteck SnowPi`_ board.
|
||||
@@ -1184,4 +1280,3 @@ class Energenie(SourceMixin, Device):
|
||||
|
||||
def off(self):
|
||||
self.value = False
|
||||
|
||||
|
||||
@@ -315,23 +315,28 @@ class CompositeDevice(Device):
|
||||
self._named = frozendict({})
|
||||
self._namedtuple = None
|
||||
self._order = kwargs.pop('_order', None)
|
||||
if self._order is None:
|
||||
self._order = sorted(kwargs.keys())
|
||||
else:
|
||||
for missing_name in set(kwargs.keys()) - set(self._order):
|
||||
raise CompositeDeviceBadOrder('%s missing from _order' % missing_name)
|
||||
self._order = tuple(self._order)
|
||||
super(CompositeDevice, self).__init__()
|
||||
for name in set(self._order) & set(dir(self)):
|
||||
raise CompositeDeviceBadName('%s is a reserved name' % name)
|
||||
try:
|
||||
if self._order is None:
|
||||
self._order = sorted(kwargs.keys())
|
||||
else:
|
||||
for missing_name in set(kwargs.keys()) - set(self._order):
|
||||
raise CompositeDeviceBadOrder('%s missing from _order' % missing_name)
|
||||
self._order = tuple(self._order)
|
||||
for name in set(self._order) & set(dir(self)):
|
||||
raise CompositeDeviceBadName('%s is a reserved name' % name)
|
||||
for dev in chain(args, kwargs.values()):
|
||||
if not isinstance(dev, Device):
|
||||
raise CompositeDeviceBadDevice("%s doesn't inherit from Device" % dev)
|
||||
self._named = frozendict(kwargs)
|
||||
self._namedtuple = namedtuple('%sValue' % self.__class__.__name__, chain(
|
||||
('device_%d' % i for i in range(len(args))), self._order))
|
||||
except:
|
||||
for dev in chain(args, kwargs.values()):
|
||||
if isinstance(dev, Device):
|
||||
dev.close()
|
||||
raise
|
||||
self._all = args + tuple(kwargs[v] for v in self._order)
|
||||
for dev in self._all:
|
||||
if not isinstance(dev, Device):
|
||||
raise CompositeDeviceBadDevice("%s doesn't inherit from Device" % dev)
|
||||
self._named = frozendict(kwargs)
|
||||
self._namedtuple = namedtuple('%sValue' % self.__class__.__name__, chain(
|
||||
(str(i) for i in range(len(args))), self._order),
|
||||
rename=True)
|
||||
super(CompositeDevice, self).__init__()
|
||||
|
||||
def __getattr__(self, name):
|
||||
# if _named doesn't exist yet, pretend it's an empty dict
|
||||
@@ -377,6 +382,7 @@ class CompositeDevice(Device):
|
||||
if self._all:
|
||||
for device in self._all:
|
||||
device.close()
|
||||
self._all = ()
|
||||
|
||||
@property
|
||||
def closed(self):
|
||||
|
||||
@@ -30,6 +30,8 @@ def setup_function(function):
|
||||
'test_led_board_fade_background',
|
||||
'test_led_bar_graph_pwm_value',
|
||||
'test_led_bar_graph_pwm_initial_value',
|
||||
'test_statusboard_kwargs',
|
||||
'test_statuszero_kwargs',
|
||||
) else MockPin
|
||||
|
||||
def teardown_function(function):
|
||||
@@ -778,3 +780,113 @@ def test_energenie():
|
||||
pins[5].assert_states_and_times([(0.0, False), (0.1, True), (0.25, False)])
|
||||
device1.close()
|
||||
assert repr(device1) == '<gpiozero.Energenie object closed>'
|
||||
|
||||
def test_statuszero_init():
|
||||
with StatusZero() as sz:
|
||||
assert sz.namedtuple._fields == ('one', 'two', 'three')
|
||||
with StatusZero('a') as sz:
|
||||
assert sz.namedtuple._fields == ('a',)
|
||||
with StatusZero('a', 'b') as sz:
|
||||
assert sz.namedtuple._fields == ('a', 'b')
|
||||
with StatusZero('a', 'b', 'c') as sz:
|
||||
assert sz.namedtuple._fields == ('a', 'b', 'c')
|
||||
with pytest.raises(ValueError):
|
||||
StatusZero('a', 'b', 'c', 'd')
|
||||
with pytest.raises(ValueError):
|
||||
StatusZero('0')
|
||||
with pytest.raises(ValueError):
|
||||
StatusZero('foo', 'hello world')
|
||||
with pytest.raises(ValueError):
|
||||
StatusZero('foo', 'foo')
|
||||
|
||||
def test_statuszero():
|
||||
with StatusZero() as sz:
|
||||
assert isinstance(sz.one, LEDBoard)
|
||||
assert isinstance(sz.two, LEDBoard)
|
||||
assert isinstance(sz.three, LEDBoard)
|
||||
assert isinstance(sz.one.red, LED)
|
||||
assert isinstance(sz.one.green, LED)
|
||||
assert sz.value == ((False, False), (False, False), (False, False))
|
||||
sz.on()
|
||||
assert sz.value == ((True, True), (True, True), (True, True))
|
||||
sz.one.green.off()
|
||||
assert sz.one.value == (True, False)
|
||||
|
||||
def test_statuszero_kwargs():
|
||||
with StatusZero(pwm=True, initial_value=True) as sz:
|
||||
assert isinstance(sz.one, LEDBoard)
|
||||
assert isinstance(sz.two, LEDBoard)
|
||||
assert isinstance(sz.three, LEDBoard)
|
||||
assert isinstance(sz.one.red, PWMLED)
|
||||
assert isinstance(sz.one.green, PWMLED)
|
||||
assert sz.value == ((1, 1), (1, 1), (1, 1))
|
||||
sz.off()
|
||||
assert sz.value == ((0, 0), (0, 0), (0, 0))
|
||||
|
||||
def test_statuszero_named():
|
||||
with StatusZero('a') as sz:
|
||||
assert isinstance(sz.a, LEDBoard)
|
||||
assert isinstance(sz.a.red, LED)
|
||||
with pytest.raises(AttributeError):
|
||||
sz.one
|
||||
|
||||
def test_statusboard_init():
|
||||
with StatusBoard() as sb:
|
||||
assert sb.namedtuple._fields == ('one', 'two', 'three', 'four', 'five')
|
||||
with StatusBoard('a') as sb:
|
||||
assert sb.namedtuple._fields == ('a',)
|
||||
with StatusBoard('a', 'b') as sb:
|
||||
assert sb.namedtuple._fields == ('a', 'b',)
|
||||
with StatusBoard('a', 'b', 'c', 'd', 'e') as sb:
|
||||
assert sb.namedtuple._fields == ('a', 'b', 'c', 'd', 'e')
|
||||
with pytest.raises(ValueError):
|
||||
StatusBoard('a', 'b', 'c', 'd', 'e', 'f')
|
||||
with pytest.raises(ValueError):
|
||||
StatusBoard('0')
|
||||
with pytest.raises(ValueError):
|
||||
StatusBoard('foo', 'hello world')
|
||||
with pytest.raises(ValueError):
|
||||
StatusBoard('foo', 'foo')
|
||||
|
||||
def test_statusboard():
|
||||
with StatusBoard() as sb:
|
||||
assert isinstance(sb.one, CompositeOutputDevice)
|
||||
assert isinstance(sb.two, CompositeOutputDevice)
|
||||
assert isinstance(sb.five, CompositeOutputDevice)
|
||||
assert isinstance(sb.one.button, Button)
|
||||
assert isinstance(sb.one.lights, LEDBoard)
|
||||
assert isinstance(sb.one.lights.red, LED)
|
||||
assert isinstance(sb.one.lights.green, LED)
|
||||
assert sb.value == ((False, (False, False)), (False, (False, False)),
|
||||
(False, (False, False)), (False, (False, False)),
|
||||
(False, (False, False)))
|
||||
sb.on()
|
||||
assert sb.value == ((False, (True, True)), (False, (True, True)),
|
||||
(False, (True, True)), (False, (True, True)),
|
||||
(False, (True, True)))
|
||||
sb.one.lights.green.off()
|
||||
assert sb.one.value == (False, (True, False))
|
||||
|
||||
def test_statusboard_kwargs():
|
||||
with StatusBoard(pwm=True, initial_value=True) as sb:
|
||||
assert isinstance(sb.one, CompositeOutputDevice)
|
||||
assert isinstance(sb.two, CompositeOutputDevice)
|
||||
assert isinstance(sb.five, CompositeOutputDevice)
|
||||
assert isinstance(sb.one.button, Button)
|
||||
assert isinstance(sb.one.lights, LEDBoard)
|
||||
assert isinstance(sb.one.lights.red, PWMLED)
|
||||
assert isinstance(sb.one.lights.green, PWMLED)
|
||||
assert sb.value == ((False, (1, 1)), (False, (1, 1)), (False, (1, 1)),
|
||||
(False, (1, 1)), (False, (1, 1)))
|
||||
sb.off()
|
||||
assert sb.value == ((False, (0, 0)), (False, (0, 0)), (False, (0, 0)),
|
||||
(False, (0, 0)), (False, (0, 0)))
|
||||
|
||||
def test_statusboard_named():
|
||||
with StatusBoard('a') as sb:
|
||||
assert isinstance(sb.a, CompositeOutputDevice)
|
||||
assert isinstance(sb.a.button, Button)
|
||||
assert isinstance(sb.a.lights, LEDBoard)
|
||||
assert isinstance(sb.a.lights.red, LED)
|
||||
with pytest.raises(AttributeError):
|
||||
sb.one
|
||||
|
||||
@@ -91,7 +91,7 @@ def test_composite_device_sequence():
|
||||
assert len(device) == 2
|
||||
assert device[0].pin.number == 4
|
||||
assert device[1].pin.number == 5
|
||||
assert device.namedtuple._fields == ('_0', '_1')
|
||||
assert device.namedtuple._fields == ('device_0', 'device_1')
|
||||
|
||||
def test_composite_device_values():
|
||||
with CompositeDevice(
|
||||
|
||||
Reference in New Issue
Block a user