1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
|
import os
import os.path
class LEDError(IOError):
"""Base class for LED errors."""
pass
class LED(object):
def __init__(self, name, brightness=None):
"""Instantiate an LED object and open the sysfs LED corresponding to
the specified name.
`brightness` can be a boolean for on/off, integer value for a specific
brightness, or None to preserve existing brightness. Default is
preserve existing brightness.
Args:
name (str): Linux led name.
brightness (bool, int, None): Initial brightness.
Returns:
LED: LED object.
Raises:
LEDError: if an I/O or OS error occurs.
TypeError: if `name` or `brightness` types are invalid.
LookupError: if LED name does not exist.
ValueError: if `brightness` value is invalid.
"""
self._path = None
self._fd = None
self._name = None
self._max_brightness = None
self._open(name, brightness)
def __del__(self):
self.close()
def __enter__(self):
return self
def __exit__(self, t, value, traceback):
self.close()
def _open(self, name, brightness):
if not isinstance(name, str):
raise TypeError("Invalid name type, should be string.")
if not isinstance(brightness, (bool, int, type(None))):
raise TypeError("Invalid brightness type, should be bool, int, or None.")
led_path = "/sys/class/leds/{:s}".format(name)
if not os.path.isdir(led_path):
raise LookupError("Opening LED: LED \"{:s}\" not found.".format(name))
# Read max brightness
try:
with open(os.path.join(led_path, "max_brightness"), "r") as f_max_brightness:
self._max_brightness = int(f_max_brightness.read())
except IOError as e:
raise LEDError(e.errno, "Reading LED max brightness: " + e.strerror)
# Open brightness
try:
self._fd = os.open(os.path.join(led_path, "brightness"), os.O_RDWR)
except OSError as e:
raise LEDError(e.errno, "Opening LED brightness: " + e.strerror)
self._name = name
self._path = led_path
# Set initial brightness
if brightness:
self.write(brightness)
# Methods
def read(self):
"""Read the brightness of the LED.
Returns:
int: Current brightness.
Raises:
LEDError: if an I/O or OS error occurs.
"""
# Read value
try:
buf = os.read(self._fd, 8)
except OSError as e:
raise LEDError(e.errno, "Reading LED brightness: " + e.strerror)
# Rewind
try:
os.lseek(self._fd, 0, os.SEEK_SET)
except OSError as e:
raise LEDError(e.errno, "Rewinding LED brightness: " + e.strerror)
return int(buf)
def write(self, brightness):
"""Set the brightness of the LED to `brightness`.
`brightness` can be a boolean for on/off, or integer value for a
specific brightness.
Args:
brightness (bool, int): Brightness value to set.
Raises:
LEDError: if an I/O or OS error occurs.
TypeError: if `brightness` type is not bool or int.
"""
if not isinstance(brightness, (bool, int)):
raise TypeError("Invalid brightness type, should be bool or int.")
if isinstance(brightness, bool):
brightness = self._max_brightness if brightness else 0
else:
if not 0 <= brightness <= self._max_brightness:
raise ValueError("Invalid brightness value: should be between 0 and {:d}".format(self._max_brightness))
# Write value
try:
os.write(self._fd, "{:d}\n".format(brightness).encode())
except OSError as e:
raise LEDError(e.errno, "Writing LED brightness: " + e.strerror)
# Rewind
try:
os.lseek(self._fd, 0, os.SEEK_SET)
except OSError as e:
raise LEDError(e.errno, "Rewinding LED brightness: " + e.strerror)
def close(self):
"""Close the sysfs LED.
Raises:
LEDError: if an I/O or OS error occurs.
"""
if self._fd is None:
return
try:
os.close(self._fd)
except OSError as e:
raise LEDError(e.errno, "Closing LED: " + e.strerror)
self._fd = None
# Immutable properties
@property
def devpath(self):
"""Get the device path of the underlying sysfs LED device.
:type: str
"""
return self._path
@property
def fd(self):
"""Get the file descriptor for the underlying sysfs LED "brightness"
file of the LED object.
:type: int
"""
return self._fd
@property
def name(self):
"""Get the sysfs LED name.
:type: str
"""
return self._name
@property
def max_brightness(self):
"""Get the LED's max brightness.
:type: int
"""
return self._max_brightness
# Mutable properties
def _get_brightness(self):
# Read brightness
return self.read()
def _set_brightness(self, brightness):
return self.write(brightness)
brightness = property(_get_brightness, _set_brightness)
"""Get or set the LED's brightness.
Value can be a boolean for on/off, or integer value a for specific
brightness.
Raises:
LEDError: if an I/O or OS error occurs.
TypeError: if `brightness` type is not bool or int.
ValueError: if `brightness` value is invalid.
:type: int
"""
# String representation
def __str__(self):
return "LED {:s} (device={:s}, fd={:d}, max_brightness={:d})".format(self._name, self._path, self._fd, self._max_brightness)
|