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: |     :inherited-members: | ||||||
|     :members: |     :members: | ||||||
|  |  | ||||||
|  | StatusZero | ||||||
|  | ========== | ||||||
|  |  | ||||||
|  | .. autoclass:: StatusZero | ||||||
|  |     :inherited-members: | ||||||
|  |     :members: | ||||||
|  |  | ||||||
|  | StatusBoard | ||||||
|  | =========== | ||||||
|  |  | ||||||
|  | .. autoclass:: StatusBoard | ||||||
|  |     :inherited-members: | ||||||
|  |     :members: | ||||||
|  |  | ||||||
| SnowPi | SnowPi | ||||||
| ====== | ====== | ||||||
|  |  | ||||||
| @@ -168,4 +182,3 @@ CompositeDevice | |||||||
|  |  | ||||||
| .. autoclass:: CompositeDevice(\*args, _order=None, \*\*kwargs) | .. autoclass:: CompositeDevice(\*args, _order=None, \*\*kwargs) | ||||||
|     :members: |     :members: | ||||||
|  |  | ||||||
|   | |||||||
| @@ -90,11 +90,13 @@ digraph classes { | |||||||
|     ButtonBoard->HoldMixin; |     ButtonBoard->HoldMixin; | ||||||
|     PiLiter->LEDBoard; |     PiLiter->LEDBoard; | ||||||
|     PiLiterBarGraph->LEDBarGraph; |     PiLiterBarGraph->LEDBarGraph; | ||||||
|  |     StatusZero->LEDBoard; | ||||||
|     TrafficLights->LEDBoard; |     TrafficLights->LEDBoard; | ||||||
|     SnowPi->LEDBoard; |     SnowPi->LEDBoard; | ||||||
|     PiTraffic->TrafficLights; |     PiTraffic->TrafficLights; | ||||||
|     PiStop->TrafficLights; |     PiStop->TrafficLights; | ||||||
|     TrafficLightsBuzzer->CompositeOutputDevice; |     TrafficLightsBuzzer->CompositeOutputDevice; | ||||||
|  |     StatusBoard->CompositeOutputDevice; | ||||||
|     FishDish->TrafficLightsBuzzer; |     FishDish->TrafficLightsBuzzer; | ||||||
|     TrafficHat->TrafficLightsBuzzer; |     TrafficHat->TrafficLightsBuzzer; | ||||||
|     Robot->CompositeDevice; |     Robot->CompositeDevice; | ||||||
| @@ -115,4 +117,3 @@ digraph classes { | |||||||
|     PingServer->InternalDevice; |     PingServer->InternalDevice; | ||||||
|     CPUTemperature->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, |     TrafficLights, | ||||||
|     PiTraffic, |     PiTraffic, | ||||||
|     PiStop, |     PiStop, | ||||||
|  |     StatusZero, | ||||||
|  |     StatusBoard, | ||||||
|     SnowPi, |     SnowPi, | ||||||
|     TrafficLightsBuzzer, |     TrafficLightsBuzzer, | ||||||
|     FishDish, |     FishDish, | ||||||
|   | |||||||
| @@ -12,7 +12,7 @@ except ImportError: | |||||||
| from time import sleep | from time import sleep | ||||||
| from itertools import repeat, cycle, chain | from itertools import repeat, cycle, chain | ||||||
| from threading import Lock | from threading import Lock | ||||||
| from collections import OrderedDict | from collections import OrderedDict, Counter | ||||||
|  |  | ||||||
| from .exc import ( | from .exc import ( | ||||||
|     DeviceClosed, |     DeviceClosed, | ||||||
| @@ -795,6 +795,102 @@ class PiStop(TrafficLights): | |||||||
|                                         pwm=pwm, initial_value=initial_value) |                                         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): | class SnowPi(LEDBoard): | ||||||
|     """ |     """ | ||||||
|     Extends :class:`LEDBoard` for the `Ryanteck SnowPi`_ board. |     Extends :class:`LEDBoard` for the `Ryanteck SnowPi`_ board. | ||||||
| @@ -1184,4 +1280,3 @@ class Energenie(SourceMixin, Device): | |||||||
|  |  | ||||||
|     def off(self): |     def off(self): | ||||||
|         self.value = False |         self.value = False | ||||||
|  |  | ||||||
|   | |||||||
| @@ -315,23 +315,28 @@ class CompositeDevice(Device): | |||||||
|         self._named = frozendict({}) |         self._named = frozendict({}) | ||||||
|         self._namedtuple = None |         self._namedtuple = None | ||||||
|         self._order = kwargs.pop('_order', None) |         self._order = kwargs.pop('_order', None) | ||||||
|  |         try: | ||||||
|             if self._order is None: |             if self._order is None: | ||||||
|                 self._order = sorted(kwargs.keys()) |                 self._order = sorted(kwargs.keys()) | ||||||
|             else: |             else: | ||||||
|                 for missing_name in set(kwargs.keys()) - set(self._order): |                 for missing_name in set(kwargs.keys()) - set(self._order): | ||||||
|                     raise CompositeDeviceBadOrder('%s missing from _order' % missing_name) |                     raise CompositeDeviceBadOrder('%s missing from _order' % missing_name) | ||||||
|             self._order = tuple(self._order) |             self._order = tuple(self._order) | ||||||
|         super(CompositeDevice, self).__init__() |  | ||||||
|             for name in set(self._order) & set(dir(self)): |             for name in set(self._order) & set(dir(self)): | ||||||
|                 raise CompositeDeviceBadName('%s is a reserved name' % name) |                 raise CompositeDeviceBadName('%s is a reserved name' % name) | ||||||
|         self._all = args + tuple(kwargs[v] for v in self._order) |             for dev in chain(args, kwargs.values()): | ||||||
|         for dev in self._all: |  | ||||||
|                 if not isinstance(dev, Device): |                 if not isinstance(dev, Device): | ||||||
|                     raise CompositeDeviceBadDevice("%s doesn't inherit from Device" % dev) |                     raise CompositeDeviceBadDevice("%s doesn't inherit from Device" % dev) | ||||||
|             self._named = frozendict(kwargs) |             self._named = frozendict(kwargs) | ||||||
|             self._namedtuple = namedtuple('%sValue' % self.__class__.__name__, chain( |             self._namedtuple = namedtuple('%sValue' % self.__class__.__name__, chain( | ||||||
|             (str(i) for i in range(len(args))), self._order), |                 ('device_%d' % i for i in range(len(args))), self._order)) | ||||||
|             rename=True) |         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) | ||||||
|  |         super(CompositeDevice, self).__init__() | ||||||
|  |  | ||||||
|     def __getattr__(self, name): |     def __getattr__(self, name): | ||||||
|         # if _named doesn't exist yet, pretend it's an empty dict |         # if _named doesn't exist yet, pretend it's an empty dict | ||||||
| @@ -377,6 +382,7 @@ class CompositeDevice(Device): | |||||||
|         if self._all: |         if self._all: | ||||||
|             for device in self._all: |             for device in self._all: | ||||||
|                 device.close() |                 device.close() | ||||||
|  |             self._all = () | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def closed(self): |     def closed(self): | ||||||
|   | |||||||
| @@ -30,6 +30,8 @@ def setup_function(function): | |||||||
|         'test_led_board_fade_background', |         'test_led_board_fade_background', | ||||||
|         'test_led_bar_graph_pwm_value', |         'test_led_bar_graph_pwm_value', | ||||||
|         'test_led_bar_graph_pwm_initial_value', |         'test_led_bar_graph_pwm_initial_value', | ||||||
|  |         'test_statusboard_kwargs', | ||||||
|  |         'test_statuszero_kwargs', | ||||||
|         ) else MockPin |         ) else MockPin | ||||||
|  |  | ||||||
| def teardown_function(function): | 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)]) |         pins[5].assert_states_and_times([(0.0, False), (0.1, True), (0.25, False)]) | ||||||
|         device1.close() |         device1.close() | ||||||
|         assert repr(device1) == '<gpiozero.Energenie object closed>' |         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 len(device) == 2 | ||||||
|         assert device[0].pin.number == 4 |         assert device[0].pin.number == 4 | ||||||
|         assert device[1].pin.number == 5 |         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(): | def test_composite_device_values(): | ||||||
|     with CompositeDevice( |     with CompositeDevice( | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user