Files
python-gpiozero/gpiozero/boards.py
Dave Jones fa0a1b3cdd Fix #76, fix #79
This finishes off implementing values and source for all (current)
classes in gpiozero. I'm afraid things get rather complex in this
commit. For starters, we've now got quite a few "aggregate" classes
which necessarily don't descend from GPIODevice. To implement values and
source on these I could either repeat a helluva lot of code or ... turn
to mixin classes. Yeah, it's multiple inheritance time, baby!

Unfortunately multiple inheritance doesn't work with __slots__ but we
really ought to keep functionality that they provide us (raise
AttributeError when an unknown attribute is set). So I've implemented
this with ... erm ... metaclasses. Sorry!
2015-10-22 21:44:42 +01:00

376 lines
9.9 KiB
Python

from __future__ import (
unicode_literals,
print_function,
absolute_import,
division,
)
try:
from itertools import izip as zip
except ImportError:
pass
from time import sleep
from collections import namedtuple
from .input_devices import InputDeviceError, Button
from .output_devices import OutputDeviceError, LED, Buzzer, Motor
from .devices import CompositeDevice, SourceMixin
class LEDBoard(SourceMixin, CompositeDevice):
"""
A Generic LED Board or collection of LEDs.
"""
def __init__(self, *pins):
super(LEDBoard, self).__init__()
self._leds = tuple(LED(pin) for pin in pins)
@property
def value(self):
"""
A tuple containing a boolean value for each LED on the board. This
property can also be set to update the state of all LEDs on the board.
"""
return tuple(led.value for led in self._leds)
@value.setter
def value(self, value):
for l, v in zip(self._leds, value):
l.value = v
@property
def leds(self):
"""
A tuple of all the `LED` objects contained by the instance.
"""
return self._leds
def on(self):
"""
Turn all the LEDs on.
"""
for led in self._leds:
led.on()
def off(self):
"""
Turn all the LEDs off.
"""
for led in self._leds:
led.off()
def toggle(self):
"""
Toggle all the LEDs. For each LED, if it's on, turn it off; if it's
off, turn it on.
"""
for led in self._leds:
led.toggle()
def blink(self, on_time=1, off_time=1, n=None, background=True):
"""
Make all the LEDs turn on and off repeatedly.
on_time: `1`
Number of seconds to be on
off_time: `1`
Number of seconds to be off
n: `None`
Number of times to blink; None means forever
background: `True`
If `True`, start a background thread to continue blinking and
return immediately. If `False`, only return when the blink is
finished (warning: the default value of `n` will result in this
method never returning).
"""
for led in self._leds:
led.blink(on_time, off_time, n, background)
class PiLiter(LEDBoard):
"""
Ciseco Pi-LITEr: strip of 8 very bright LEDs.
"""
def __init__(self):
super(PiLiter, self).__init__(4, 17, 27, 18, 22, 23, 24, 25)
TrafficLightTuple = namedtuple('TrafficLightTuple', ('red', 'amber', 'green'))
class TrafficLights(LEDBoard):
"""
Generic Traffic Lights set.
red: `None`
Red LED pin
amber: `None`
Amber LED pin
green: `None`
Green LED pin
"""
def __init__(self, red=None, amber=None, green=None):
if not all([red, amber, green]):
raise OutputDeviceError('red, amber and green pins must be provided')
super(TrafficLights, self).__init__(red, amber, green)
@property
def value(self):
return TrafficLightTuple(*super(TrafficLights, self).value)
@value.setter
def value(self, value):
super(TrafficLights, self).value = value
@property
def red(self):
"""
The `LED` object representing the red LED.
"""
return self.leds[0]
@property
def amber(self):
"""
The `LED` object representing the red LED.
"""
return self.leds[1]
@property
def green(self):
"""
The `LED` object representing the green LED.
"""
return self.leds[2]
class PiTraffic(TrafficLights):
"""
Low Voltage Labs PI-TRAFFIC: vertical traffic lights board on pins 9, 10
and 11.
"""
def __init__(self):
super(PiTraffic, self).__init__(9, 10, 11)
FishDishTuple = namedtuple('FishDishTuple', ('red', 'amber', 'green', 'buzzer'))
class FishDish(TrafficLights):
"""
Pi Supply FishDish: traffic light LEDs, a button and a buzzer.
"""
def __init__(self):
super(FishDish, self).__init__(9, 22, 4)
self.buzzer = Buzzer(8)
self.button = Button(7, pull_up=False)
self._all = self.leds + (self.buzzer,)
@property
def all(self):
"""
A tuple containing objects for all the items on the board (several
`LED` objects, a `Buzzer`, and a `Button`).
"""
return self._all
@property
def value(self):
"""
Returns a named-tuple containing values representing the states of
the LEDs, and the buzzer. This property can also be set to a 4-tuple
to update the state of all the board's components.
"""
return FishDishTuple(
self.red.value,
self.amber.value,
self.green.value,
self.buzzer.value)
@value.setter
def value(self, value):
for i, v in zip(self._all, value):
i.value = v
def on(self):
"""
Turn all the board's components on.
"""
for thing in self._all:
thing.on()
def off(self):
"""
Turn all the board's components off.
"""
for thing in self._all:
thing.off()
def toggle(self):
"""
Toggle all the board's components. For each component, if it's on, turn
it off; if it's off, turn it on.
"""
for thing in self._all:
thing.toggle()
def blink(self, on_time=1, off_time=1, n=None, background=True):
"""
Make all the board's components turn on and off repeatedly.
on_time: `1`
Number of seconds to be on
off_time: `1`
Number of seconds to be off
n: `None`
Number of times to blink; None means forever
background: `True`
If `True`, start a background thread to continue blinking and
return immediately. If `False`, only return when the blink is
finished (warning: the default value of `n` will result in this
method never returning).
"""
for thing in self._all:
led.blink(on_time, off_time, n, background)
def lights_on(self):
"""
Turn all the board's LEDs on.
"""
super(FishDish, self).on()
def lights_off(self):
"""
Turn all the board's LEDs off.
"""
super(FishDish, self).off()
def toggle_lights(self):
"""
Toggle all the board's LEDs. For each LED, if it's on, turn
it off; if it's off, turn it on.
"""
super(FishDish, self).toggle()
def blink_lights(self, on_time=1, off_time=1, n=None, background=True):
"""
Make all the board's LEDs turn on and off repeatedly.
on_time: `1`
Number of seconds to be on
off_time: `1`
Number of seconds to be off
n: `None`
Number of times to blink; None means forever
background: `True`
If `True`, start a background thread to continue blinking and
return immediately. If `False`, only return when the blink is
finished (warning: the default value of `n` will result in this
method never returning).
"""
super(FishDish, self).blink(on_time, off_time, n, background)
class TrafficHat(FishDish):
"""
Ryanteck Traffic HAT: traffic light LEDs, a button and a buzzer.
"""
def __init__(self):
super(FishDish, self).__init__(22, 23, 24)
self.buzzer = Buzzer(5)
self.button = Button(25)
self._all = self._leds + (self.buzzer,)
RobotTuple = namedtuple('RobotTuple', ('left', 'right'))
class Robot(SourceMixin, CompositeDevice):
"""
Generic dual-motor Robot.
"""
def __init__(self, left=None, right=None):
if not all([left, right]):
raise OutputDeviceError('left and right motor pins must be provided')
super(Robot, self).__init__()
self._left = Motor(*left)
self._right = Motor(*right)
@property
def value(self):
"""
Returns a tuple of two floating point values (-1 to 1) representing the
speeds of the robot's two motors (left and right). This property can
also be set to alter the speed of both motors.
"""
return RobotTuple(self._left.value, self._right.value)
@value.setter
def value(self, value):
self._left.value, self._right.value = value
def forward(self, speed=1):
"""
Drive the robot forward by running both motors forward.
speed: `1`
Speed at which to drive the motors, 0 to 1.
"""
self._left.forward(speed)
self._right.forward(speed)
def backward(self, speed=1):
"""
Drive the robot backward by running both motors backward.
speed: `1`
Speed at which to drive the motors, 0 to 1.
"""
self._left.backward(speed)
self._right.backward(speed)
def left(self, speed=1):
"""
Make the robot turn left by running the right motor forward and left
motor backward.
speed: `1`
Speed at which to drive the motors, 0 to 1.
"""
self._right.forward(speed)
self._left.backward(speed)
def right(self, speed=1):
"""
Make the robot turn right by running the left motor forward and right
motor backward.
speed: `1`
Speed at which to drive the motors, 0 to 1.
"""
self._left.forward(speed)
self._right.backward(speed)
def stop(self):
"""
Stop the robot.
"""
self._left.stop()
self._right.stop()
class RyanteckRobot(Robot):
"""
RTK MCB Robot. Generic robot controller with pre-configured pin numbers.
"""
def __init__(self):
super(RyanteckRobot, self).__init__((17, 18), (22, 23))