-
Notifications
You must be signed in to change notification settings - Fork 1
feat(steami_screen): Add steami_screen widget library. #354
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 9 commits
efc5ef6
2ddb36c
7054fde
f202f4e
608d8b8
9e82098
ff9be7c
52f0e9a
257ba78
169684b
0d6fbce
7b86403
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -34,6 +34,7 @@ module.exports = { | |
| 'style', | ||
| 'tests', | ||
| 'tooling', | ||
| 'steami_screen' | ||
| ], | ||
| ], | ||
| 'type-enum': [ | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,218 @@ | ||||||
| # STeaMi Screen | ||||||
|
|
||||||
| High-level UI library for STeaMi displays. | ||||||
|
|
||||||
| Provides a device-agnostic abstraction layer on top of display drivers (SSD1327, GC9A01) with a simple API to draw UI elements: text layouts, widgets, menus, icons. | ||||||
|
|
||||||
| --- | ||||||
|
|
||||||
| ## Features | ||||||
|
|
||||||
| * Display abstraction (works with any FrameBuffer-based backend) | ||||||
| * Automatic layout for round screens (cardinal positioning, safe margins) | ||||||
| * Text rendering with alignment and scaling | ||||||
| * Drawing primitives (pixel, line, rect, circle) | ||||||
| * 10 widgets: title, subtitle, value, bar, gauge, graph, menu, compass, watch, face | ||||||
|
|
||||||
| --- | ||||||
|
|
||||||
| ## Basic Usage | ||||||
|
|
||||||
| ```python | ||||||
| import ssd1327 | ||||||
| from machine import SPI, Pin | ||||||
| from steami_screen import Screen | ||||||
|
|
||||||
| spi = SPI(1) | ||||||
| dc = Pin("DATA_COMMAND_DISPLAY") | ||||||
| res = Pin("RST_DISPLAY") | ||||||
| cs = Pin("CS_DISPLAY") | ||||||
|
|
||||||
| display = ssd1327.WS_OLED_128X128_SPI(spi, dc, res, cs) | ||||||
| screen = Screen(display) | ||||||
|
|
||||||
| screen.clear() | ||||||
| screen.title("STeaMi") | ||||||
| screen.value(42, label="Temp", unit="C") | ||||||
| screen.show() | ||||||
| ``` | ||||||
|
|
||||||
| --- | ||||||
|
|
||||||
| ## API Reference | ||||||
|
|
||||||
| ### Initialization | ||||||
|
|
||||||
| ```python | ||||||
| screen = Screen(display) | ||||||
| ``` | ||||||
|
|
||||||
| `display` must expose `fill()`, `pixel()`, `line()`, `rect()`, `fill_rect()`, `text()`, `show()`. Width and height are auto-detected from the display backend. | ||||||
|
|
||||||
| --- | ||||||
|
|
||||||
| ### Drawing Primitives | ||||||
|
|
||||||
| ```python | ||||||
| screen.pixel(x, y, color) | ||||||
| screen.line(x1, y1, x2, y2, color) | ||||||
| screen.rect(x, y, w, h, color, fill=False) | ||||||
| screen.circle(x, y, r, color, fill=False) | ||||||
| ``` | ||||||
|
|
||||||
| --- | ||||||
|
|
||||||
| ### Text | ||||||
|
|
||||||
| ```python | ||||||
| screen.text("Hello", at="CENTER") | ||||||
| screen.text("Top", at="N") | ||||||
| screen.text("Custom", at=(10, 20)) | ||||||
| screen.text("Big", at="CENTER", scale=2) | ||||||
| ``` | ||||||
|
|
||||||
| Cardinal positions: `"N"`, `"NE"`, `"E"`, `"SE"`, `"S"`, `"SW"`, `"W"`, `"NW"`, `"CENTER"`. | ||||||
|
|
||||||
| --- | ||||||
|
|
||||||
| ### Widgets | ||||||
|
|
||||||
| #### Title | ||||||
|
|
||||||
| ```python | ||||||
| screen.title("STeaMi") | ||||||
| ``` | ||||||
|
|
||||||
| Draws text centered at the top (N position). | ||||||
|
|
||||||
| --- | ||||||
|
|
||||||
| #### Subtitle | ||||||
|
|
||||||
| ```python | ||||||
| screen.subtitle("Line 1", "Line 2") | ||||||
| ``` | ||||||
|
|
||||||
| Draws text centered at the bottom (S position). Accepts multiple lines. | ||||||
|
|
||||||
| --- | ||||||
|
|
||||||
| #### Value | ||||||
|
|
||||||
| ```python | ||||||
| screen.value(23.5, label="Temp", unit="C") | ||||||
| ``` | ||||||
|
|
||||||
| Displays a large centered value with optional label above and unit below. | ||||||
|
|
||||||
| --- | ||||||
|
|
||||||
| #### Progress Bar | ||||||
|
|
||||||
| ```python | ||||||
| screen.bar(value=75, max_value=100) | ||||||
| ``` | ||||||
|
|
||||||
| --- | ||||||
|
|
||||||
| #### Gauge | ||||||
|
|
||||||
| ```python | ||||||
| screen.gauge(value=60, min_value=0, max_value=100, unit="C") | ||||||
|
||||||
| screen.gauge(value=60, min_value=0, max_value=100, unit="C") | |
| screen.gauge(val=60, min_val=0, max_val=100, unit="C") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in 7b86403: gauge(value=60, min_value=0, max_value=100) → gauge(60, min_val=0, max_val=100) to match the actual API.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| metadata( | ||
| description="Library for controlling the STeaMi round display.", | ||
| version="0.0.1", | ||
| ) | ||
|
|
||
| package("steami_screen") |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| from steami_screen.colors import rgb_to_gray4, rgb_to_rgb8, rgb_to_rgb565 | ||
| from steami_screen.device import ( | ||
| BLACK, | ||
| BLUE, | ||
| DARK, | ||
| GRAY, | ||
| GREEN, | ||
| LIGHT, | ||
| RED, | ||
| WHITE, | ||
| YELLOW, | ||
| Screen, | ||
| ) | ||
|
|
||
| __all__ = [ | ||
| "BLACK", | ||
| "BLUE", | ||
| "DARK", | ||
| "GRAY", | ||
| "GREEN", | ||
| "LIGHT", | ||
| "RED", | ||
| "WHITE", | ||
| "YELLOW", | ||
| "Screen", | ||
| "rgb_to_gray4", | ||
| "rgb_to_rgb8", | ||
| "rgb_to_rgb565", | ||
| ] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| """ | ||
| Color conversion utilities for STeaMi display backends. | ||
|
|
||
| Colors are represented as RGB tuples (r, g, b) with values 0-255. | ||
| Each backend converts to its native format: | ||
| - SSD1327 : grayscale 4-bit (0-15) | ||
| - GC9A01 : RGB565 (16-bit) | ||
| - Simulator: RGB tuple (pass-through) | ||
|
|
||
| All functions accept legacy int values for backward compatibility. | ||
| """ | ||
|
|
||
|
|
||
| def rgb_to_gray4(color): | ||
| """Convert an RGB tuple to a 4-bit grayscale value (0-15). | ||
|
|
||
| Uses BT.601 luminance: Y = 0.299*R + 0.587*G + 0.114*B | ||
| Accepts int for backward compatibility (returned as-is, clamped to 0-15). | ||
| """ | ||
| if isinstance(color, int): | ||
| return max(0, min(15, color)) | ||
| r, g, b = color | ||
| luminance = (r * 77 + g * 150 + b * 29) >> 8 # 0-255 | ||
| return luminance >> 4 # 0-15 | ||
|
|
||
|
|
||
| def rgb_to_rgb565(color): | ||
| """Convert an RGB tuple to a 16-bit RGB565 integer. | ||
|
|
||
| Accepts int for backward compatibility (treated as gray4, expanded). | ||
| """ | ||
| if isinstance(color, int): | ||
| g = max(0, min(15, color)) * 17 # 0-255 | ||
| r, b = g, g | ||
| else: | ||
| r, g, b = color | ||
| return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3) | ||
|
|
||
|
|
||
| def rgb_to_rgb8(color): | ||
| """Convert a color to an RGB tuple (r, g, b). | ||
|
|
||
| If already a tuple, returns it unchanged. | ||
| Accepts int for backward compatibility (treated as gray4, expanded). | ||
| """ | ||
| if isinstance(color, int): | ||
| v = max(0, min(15, color)) * 17 # 0-255 | ||
| return (v, v, v) | ||
| return color |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The README example uses keyword arguments
valueandmax_value, butScreen.bar()is defined asbar(self, val, max_val=100, ...). Using the snippet as-is will raise a TypeError. Update the example to match the actual parameter names (or rename the function parameters for a stable public API).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in 7b86403:
bar(value=75, max_value=100)→bar(75, max_val=100)to match the actual API.