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