OzWrapper.OzRGB.OzRGB¶
RGB LED indicator wrapper for NeoPixel status display.
Drives a NeoPixel LED strip to indicate device status (network connectivity,
data sending, errors) through color and blink/fade patterns, and optionally
shows AQI-based beacon colors on remaining LEDs. Pixel 0 carries the
status color/pattern produced by the Magic animation engine; pixels
1..N-1 carry the beacon color resolved from the live AQI queue.
The module also exposes lightweight helpers hex_to_rgb() and
get_color_from_value() for converting hex strings and mapping AQI
values to beacon colors, plus a main() event loop that the parent
process spawns as a long-running indicator worker.
Example
Run the indicator loop with a network status queue and beacon queue:
>>> from queue import Queue
>>> from OzWrapper.OzRGB import OzRGB
>>> net_q, beacon_q = Queue(), Queue()
>>> net_q.put(1) # 1 -> Connected (cyan, fade)
>>> OzRGB.main(None, net_q, beacon_q)
Note
The NeoPixel data line is driven via the on-board PWM peripheral on
GPIO18 (board.D18); the NeoPixel WS281x protocol requires an
800 kHz signal with sub-microsecond timing, so the pin and frequency
must not be reconfigured at runtime. Ten LEDs are addressed by default
(num_pixels = 10); index 0 is reserved for status and indices
1..9 form the AQI beacon ring.
Reference config:
{
"beacon": {
"en": 1,
"sc": "aqi",
"range": [
0,
50,
100,
200,
300,
400
],
"colors": [
"50f0e6",
"29cc70",
"f0e641",
"d08000",
"ff0000",
"c700d0"
]
}
}
See also
SensorBase.SensorBase.GenericSensor: Common sensor wrapper
base class used across the Oizom hardware peripherals.
Attributes¶
Classes¶
LED animation engine providing fade and blink patterns for NeoPixel strips. |
Functions¶
|
Look up the beacon hex color for a given AQI value. |
|
Convert a 6-character hex color code to an |
|
Run the RGB LED main loop, updating status and beacon colors continuously. |
Module Contents¶
- OzWrapper.OzRGB.OzRGB.get_color_from_value(value: int) str | None[source]¶
Look up the beacon hex color for a given AQI value.
Walks the ordered
beacon_config["range"]thresholds and returns the color frombeacon_config["colors"]whose lower bound is the largest value<= value. Values at or above the final threshold are clamped to the last color, modelling the “hazardous” AQI bucket.- Parameters:
value (
int) – AQI (or other index) value to classify. Negative values are treated as out-of-range and yieldNone.- Returns:
Hex color string (without
#prefix) frombeacon_config["colors"], orNoneifvalueis below the first range boundary.- Return type:
str | None
- Raises:
KeyError – If
beacon_configis missing the"range"or"colors"keys.IndexError – If
rangeandcolorsare not the same length andvaluefalls in an undefined bucket.
Example
Resolve a “moderate” AQI bucket:
>>> from OzWrapper.OzRGB.OzRGB import get_color_from_value >>> get_color_from_value(75) # 50 <= 75 < 100 -> "f0e641" 'f0e641'
Note
The lookup is half-open
[lower, upper)for intermediate buckets and closed[lower, +inf)for the final bucket. Pair this helper withhex_to_rgb()before assigning to a NeoPixel pixel.
- OzWrapper.OzRGB.OzRGB.hex_to_rgb(hex_code: str) tuple[source]¶
Convert a 6-character hex color code to an
(R, G, B)tuple.Parses two characters at a time from
hex_code(red, green, blue) and returns 8-bit channel values suitable for assignment to a NeoPixel pixel. Invalid lengths log an error viacontext_loggerand fall back to black so that the LED strip stays in a defined state.- Parameters:
hex_code (
str) – Six-character hex string (for example"50f0e6"), without a leading#. Letters may be upper or lower case.- Returns:
(red, green, blue)integers, each in the inclusive range0..255.(0, 0, 0)is returned for malformed input.- Return type:
- Raises:
ValueError – If
hex_codeis the correct length but contains characters that are not valid hexadecimal digits.
Example
Convert the default “Connected” beacon color:
>>> from OzWrapper.OzRGB.OzRGB import hex_to_rgb >>> hex_to_rgb("50f0e6") (80, 240, 230)
Note
The function does not accept a leading
#or 3-digit shorthand ("fff"). Callers must supply the canonical 6-character form used bybeacon_config.
- OzWrapper.OzRGB.OzRGB.main(os_network_alert_file: None, network_status: queue.Queue | None = None, beacon_queue: queue.Queue | None = None) None[source]¶
Run the RGB LED main loop, updating status and beacon colors continuously.
Instantiates a
Magicengine over the module-levelpixelsstrip, then enters an infinite loop that:peeks the head of
network_statusto resolve the current status code, looks up(color, pattern, label)instatus, and writes the color to pixel0;when
beacon_flagisTrue, drainsbeacon_queuefor an AQI payload, resolves a beacon color viaget_color_from_value()andhex_to_rgb(), and fills pixels1..9with it;dispatches to
Magic.blinking()orMagic.fade()to animate pixel0according to the selected pattern.
Any exception inside the loop fills the strip with
unsupported_color(white), runs a fade tick to keep motion visible, and rate-limits the error log to one line perprint_intervalsecond.- Parameters:
os_network_alert_file (
None) – Unused. Retained for backward compatibility with the legacy entry-point signature that took a network alert file path.network_status (
queue.Queue | None) – Queue providing integer network status codes for LED color and pattern. Codes are looked up instatus; values from0(Disconnected) through8(QC Mode) are recognised.beacon_queue (
queue.Queue | None) – Queue providing AQI data for beacon LED coloring. Each element is a dict that either contains the"aqi"key directly or nests sensor channels under"d".
- Returns:
This function never returns under normal operation; the outer
while 1loop runs for the lifetime of the process.- Return type:
None
- Raises:
AttributeError – If
network_statusisNoneand the queue peek is attempted (the widerexceptcatches and logs this).KeyboardInterrupt – Propagated when the operator stops the process.
Example
Spawn the indicator from a parent supervisor:
>>> from multiprocessing import Process >>> from queue import Queue >>> from OzWrapper.OzRGB.OzRGB import main >>> net_q, beacon_q = Queue(), Queue() >>> p = Process(target=main, args=(None, net_q, beacon_q)) >>> p.start()
Note
The loop is intentionally tight (no
time.sleep) becauseMagic.fade()andMagic.blinking()self-throttle onMagic.millis(). Driving the WS281x line at 800 kHz from Python requires the loop to keep callingpixels.show()so the kernel-side DMA stays fed; never insertsleephere.See also
SensorBase.SensorBase.GenericSensor: Sibling sensor wrappers consume the same status queue idiom used here.
- OzWrapper.OzRGB.OzRGB.NETWORK_FILE = '/etc/ozone_network.txt'¶
- OzWrapper.OzRGB.OzRGB.all_colors¶
- OzWrapper.OzRGB.OzRGB.basic_logger¶
- OzWrapper.OzRGB.OzRGB.beacon_config¶
- OzWrapper.OzRGB.OzRGB.beacon_flag = False¶
- OzWrapper.OzRGB.OzRGB.blue = (0, 0, 255)¶
- OzWrapper.OzRGB.OzRGB.context_logger¶
- OzWrapper.OzRGB.OzRGB.cyan = (0, 255, 255)¶
- OzWrapper.OzRGB.OzRGB.green = (0, 255, 0)¶
- OzWrapper.OzRGB.OzRGB.magenta = (170, 10, 127)¶
- OzWrapper.OzRGB.OzRGB.neonOrange = (255, 95, 31)¶
- OzWrapper.OzRGB.OzRGB.num_pixels = 10¶
- OzWrapper.OzRGB.OzRGB.pattern_fade = 0¶
- OzWrapper.OzRGB.OzRGB.pattern_fast_blink = 3¶
- OzWrapper.OzRGB.OzRGB.pattern_normal_blink = 1¶
- OzWrapper.OzRGB.OzRGB.pattern_slow_blink = 2¶
- OzWrapper.OzRGB.OzRGB.pattern_ultra_fast_blink = 4¶
- OzWrapper.OzRGB.OzRGB.pixel_pin¶
- OzWrapper.OzRGB.OzRGB.pixels¶
- OzWrapper.OzRGB.OzRGB.purple = (68, 0, 68)¶
- OzWrapper.OzRGB.OzRGB.red = (255, 0, 0)¶
- OzWrapper.OzRGB.OzRGB.status¶
- OzWrapper.OzRGB.OzRGB.unsupported_color = (255, 255, 255)¶
- OzWrapper.OzRGB.OzRGB.white = (255, 255, 255)¶
- OzWrapper.OzRGB.OzRGB.yellow = (255, 255, 0)¶