Skip to content

Commit 0d6fbce

Browse files
committed
fix(steami_screen): Add color conversion for SSD1327 and fix bar div by zero.
1 parent 169684b commit 0d6fbce

1 file changed

Lines changed: 49 additions & 33 deletions

File tree

lib/steami_screen/steami_screen/device.py

Lines changed: 49 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,14 @@ def __init__(self, display, width=None, height=None):
5252
self._d = display
5353
self.width = width or getattr(display, 'width', 128)
5454
self.height = height or getattr(display, 'height', 128)
55+
# Detect if the backend needs greyscale ints (SSD1327) or accepts RGB tuples
56+
self._needs_gray = not hasattr(display, 'fill_rect') or hasattr(display, 'framebuf')
57+
58+
def _c(self, color):
59+
"""Convert color for the backend. Returns gray4 int for SSD1327, pass-through otherwise."""
60+
if self._needs_gray:
61+
return rgb_to_gray4(color)
62+
return color
5563

5664
# --- Adaptive properties ---
5765

@@ -113,7 +121,7 @@ def _resolve(self, at, text_len=0, scale=1):
113121
def title(self, text, color=GRAY):
114122
"""Draw title text at the top (N)."""
115123
x, y = self._resolve("N", len(text))
116-
self._d.text(text, x, y, color)
124+
self._text(text, x, y, color)
117125

118126
def value(self, val, unit=None, at="CENTER", label=None,
119127
color=WHITE, scale=2, y_offset=0):
@@ -148,7 +156,7 @@ def value(self, val, unit=None, at="CENTER", label=None,
148156
# Optional label above
149157
if label:
150158
lx = x + tw // 2 - len(label) * self.CHAR_W // 2
151-
self._d.text(label, lx, y - self.CHAR_H - 4, GRAY)
159+
self._text(label, lx, y - self.CHAR_H - 4, GRAY)
152160

153161
# Value (large)
154162
self._draw_scaled_text(text, x, y, color, scale)
@@ -160,7 +168,7 @@ def value(self, val, unit=None, at="CENTER", label=None,
160168
if hasattr(self._d, 'draw_medium_text'):
161169
self._d.draw_medium_text(unit, ux, unit_y, LIGHT)
162170
else:
163-
self._d.text(unit, ux, unit_y, LIGHT)
171+
self._text(unit, ux, unit_y, LIGHT)
164172

165173
def subtitle(self, *lines, color=DARK):
166174
"""Draw subtitle text at the bottom (S). Accepts multiple lines."""
@@ -177,7 +185,7 @@ def subtitle(self, *lines, color=DARK):
177185
block_h = (n - 1) * line_h
178186
start_y = base_y - block_h // 2
179187

180-
draw = getattr(self._d, 'draw_small_text', self._d.text)
188+
draw = getattr(self._d, 'draw_small_text', self._text)
181189
for i, line in enumerate(lines):
182190
x, _ = self._resolve("S", len(line))
183191
y = start_y + i * line_h
@@ -190,7 +198,7 @@ def bar(self, val, max_val=100, y_offset=0, color=LIGHT):
190198
bar_h = 8
191199
bx = cx - bar_w // 2
192200
by = cy + 20 + y_offset
193-
fill_w = int(bar_w * min(val, max_val) / max_val)
201+
fill_w = int(bar_w * min(val, max_val) / max_val) if max_val else 0
194202

195203
# Background
196204
self._fill_rect(bx, by, bar_w, bar_h, DARK)
@@ -238,7 +246,7 @@ def gauge(self, val, min_val=0, max_val=100, unit=None, color=LIGHT):
238246
if hasattr(self._d, 'draw_medium_text'):
239247
self._d.draw_medium_text(unit, ux, uy, LIGHT)
240248
else:
241-
self._d.text(unit, ux, uy, LIGHT)
249+
self._text(unit, ux, uy, LIGHT)
242250

243251
# Min/max labels at arc endpoints (slightly inward to stay visible)
244252
min_t = str(int(min_val))
@@ -251,7 +259,7 @@ def gauge(self, val, min_val=0, max_val=100, unit=None, color=LIGHT):
251259
ly = int(cy + r_label * math.sin(angle_s))
252260
rx = int(cx + r_label * math.cos(angle_e)) - len(max_t) * self.CHAR_W // 2
253261
ry = int(cy + r_label * math.sin(angle_e))
254-
draw_sm = getattr(self._d, 'draw_small_text', self._d.text)
262+
draw_sm = getattr(self._d, 'draw_small_text', self._text)
255263
draw_sm(min_t, lx, ly, GRAY)
256264
draw_sm(max_t, rx, ry, GRAY)
257265

@@ -272,7 +280,7 @@ def graph(self, data, min_val=0, max_val=100, color=LIGHT):
272280
if data:
273281
text = str(int(data[-1]))
274282
draw_fn = getattr(self._d, 'draw_medium_text',
275-
self._d.text)
283+
self._text)
276284
tw = len(text) * self.CHAR_W
277285
vx = cx - tw // 2
278286
vy = 31
@@ -284,7 +292,7 @@ def _fmt(v):
284292
return str(int(v // 1000)) + "k"
285293
return str(int(v))
286294

287-
draw_sm = getattr(self._d, 'draw_small_text', self._d.text)
295+
draw_sm = getattr(self._d, 'draw_small_text', self._text)
288296
mid_val = (min_val + max_val) / 2
289297
for val, yp in [(max_val, gy),
290298
(mid_val, gy + gh // 2),
@@ -301,7 +309,7 @@ def _fmt(v):
301309
x = gx + 1
302310
while x < gx + gw:
303311
x2 = min(x + dash - 1, gx + gw - 1)
304-
self._d.line(x, mid_y, x2, mid_y, (51, 51, 51))
312+
self._line(x, mid_y, x2, mid_y, (51, 51, 51))
305313
x += dash + gap
306314

307315
# Y axis (extend +1 to meet X axis corner)
@@ -341,9 +349,9 @@ def menu(self, items, selected=0, color=WHITE):
341349
iy = y + (i - start) * item_h
342350
if i == selected:
343351
self._fill_rect(15, iy - 2, self.width - 30, item_h, DARK)
344-
self._d.text("> " + items[i], 18, iy, color)
352+
self._text("> " + items[i], 18, iy, color)
345353
else:
346-
self._d.text(" " + items[i], 18, iy, GRAY)
354+
self._text(" " + items[i], 18, iy, GRAY)
347355

348356
def compass(self, heading, color=LIGHT):
349357
"""Draw a compass with a rotating needle."""
@@ -359,7 +367,7 @@ def compass(self, heading, color=LIGHT):
359367
lx = cx + int((r + 5) * math.sin(math.radians(angle)))
360368
ly = cy - int((r + 5) * math.cos(math.radians(angle)))
361369
c = WHITE if label == "N" else GRAY
362-
self._d.text(label, lx - self.CHAR_W // 2, ly - self.CHAR_H // 2, c)
370+
self._text(label, lx - self.CHAR_W // 2, ly - self.CHAR_H // 2, c)
363371

364372
# Tick marks (8 directions)
365373
for angle in range(0, 360, 45):
@@ -427,7 +435,7 @@ def watch(self, hours, minutes, seconds=0, color=LIGHT):
427435
lx = cx + int((r - 15) * math.sin(rad))
428436
ly = cy - int((r - 15) * math.cos(rad))
429437
tw = len(text) * self.CHAR_W
430-
self._d.text(text, lx - tw // 2, ly - self.CHAR_H // 2, WHITE)
438+
self._text(text, lx - tw // 2, ly - self.CHAR_H // 2, WHITE)
431439

432440
# Hour hand (short, thick)
433441
h_angle = (hours % 12 + minutes / 60) * 30
@@ -506,7 +514,7 @@ def text(self, text, at="CENTER", color=WHITE, scale=1):
506514
if scale > 1:
507515
self._draw_scaled_text(text, x, y, color, scale)
508516
else:
509-
self._d.text(text, x, y, color)
517+
self._text(text, x, y, color)
510518

511519
def line(self, x1, y1, x2, y2, color=WHITE):
512520
self._line(x1, y1, x2, y2, color)
@@ -524,41 +532,49 @@ def rect(self, x, y, w, h, color=WHITE, fill=False):
524532
self._rect(x, y, w, h, color)
525533

526534
def pixel(self, x, y, color=WHITE):
527-
self._d.pixel(x, y, color)
535+
self._pixel(x, y, self._c(color))
528536

529537
# --- Control ---
530538

531539
def clear(self, color=BLACK):
532-
self._d.fill(color)
540+
self._d.fill(self._c(color))
533541

534542
def show(self):
535543
self._d.show()
536544

537545
# --- Internal drawing helpers ---
538546

547+
def _text(self, text, x, y, c):
548+
self._d.text(text, x, y, self._c(c))
549+
550+
def _pixel(self, x, y, c):
551+
self._d.pixel(x, y, self._c(c))
552+
539553
def _line(self, x1, y1, x2, y2, c):
540-
self._d.line(x1, y1, x2, y2, c)
554+
self._d.line(x1, y1, x2, y2, self._c(c))
541555

542556
def _hline(self, x, y, w, c):
543-
self._d.line(x, y, x + w - 1, y, c)
557+
self._d.line(x, y, x + w - 1, y, self._c(c))
544558

545559
def _vline(self, x, y, h, c):
546-
self._d.line(x, y, x, y + h - 1, c)
560+
self._d.line(x, y, x, y + h - 1, self._c(c))
547561

548562
def _fill_rect(self, x, y, w, h, c):
563+
cc = self._c(c)
549564
if hasattr(self._d, 'fill_rect'):
550-
self._d.fill_rect(x, y, w, h, c)
565+
self._d.fill_rect(x, y, w, h, cc)
551566
elif hasattr(self._d, 'framebuf'):
552-
self._d.framebuf.fill_rect(x, y, w, h, rgb_to_gray4(c))
567+
self._d.framebuf.fill_rect(x, y, w, h, cc)
553568
else:
554569
for row in range(h):
555-
self._d.line(x, y + row, x + w - 1, y + row, c)
570+
self._d.line(x, y + row, x + w - 1, y + row, cc)
556571

557572
def _rect(self, x, y, w, h, c):
573+
cc = self._c(c)
558574
if hasattr(self._d, 'rect'):
559-
self._d.rect(x, y, w, h, c)
575+
self._d.rect(x, y, w, h, cc)
560576
elif hasattr(self._d, 'framebuf'):
561-
self._d.framebuf.rect(x, y, w, h, rgb_to_gray4(c))
577+
self._d.framebuf.rect(x, y, w, h, cc)
562578
else:
563579
self._hline(x, y, w, c)
564580
self._hline(x, y + h - 1, w, c)
@@ -578,21 +594,21 @@ def _draw_scaled_text(self, text, x, y, color, scale):
578594
# On real hardware without scaled text support, draw at scale=1
579595
# centered at the same position (best effort)
580596
if not hasattr(self._d, 'pixel'):
581-
self._d.text(text, x, y, color)
597+
self._text(text, x, y, color)
582598
return
583599
# Render at 1x to a temporary buffer, then scale up
584600
# For MicroPython: draw each character using the display's text method
585601
# but multiple times offset for a bold effect at scale 2
586602
if scale == 2:
587603
for dx in range(2):
588604
for dy in range(2):
589-
self._d.text(text, x + dx, y + dy, color)
605+
self._text(text, x + dx, y + dy, color)
590606
elif scale == 3:
591607
for dx in range(3):
592608
for dy in range(3):
593-
self._d.text(text, x + dx, y + dy, color)
609+
self._text(text, x + dx, y + dy, color)
594610
else:
595-
self._d.text(text, x, y, color)
611+
self._text(text, x, y, color)
596612

597613
def _draw_arc(self, cx, cy, r, start_deg, sweep_deg, color, width=3):
598614
"""Draw a thick arc using individual pixels."""
@@ -607,7 +623,7 @@ def _draw_arc(self, cx, cy, r, start_deg, sweep_deg, color, width=3):
607623
x = int(cx + (r + dr) * math.cos(angle))
608624
y = int(cy + (r + dr) * math.sin(angle))
609625
if 0 <= x < self.width and 0 <= y < self.height:
610-
self._d.pixel(x, y, color)
626+
self._pixel(x, y, color)
611627

612628
def _draw_circle(self, cx, cy, r, color):
613629
"""Bresenham circle."""
@@ -617,7 +633,7 @@ def _draw_circle(self, cx, cy, r, color):
617633
(x, -y), (y, -x), (-x, -y), (-y, -x)):
618634
px, py = cx + sx, cy + sy
619635
if 0 <= px < self.width and 0 <= py < self.height:
620-
self._d.pixel(px, py, color)
636+
self._pixel(px, py, color)
621637
y += 1
622638
if d < 0:
623639
d += 2 * y + 1
@@ -633,7 +649,7 @@ def _fill_circle(self, cx, cy, r, color):
633649
if 0 <= y < self.height:
634650
x1 = max(0, cx - dx)
635651
x2 = min(self.width - 1, cx + dx)
636-
self._d.line(x1, y, x2, y, color)
652+
self._line(x1, y, x2, y, color)
637653

638654
def _fill_triangle(self, x0, y0, x1, y1, x2, y2, color):
639655
"""Filled triangle using scanline."""
@@ -659,4 +675,4 @@ def interp(ya, xa, yb, xb, y):
659675
x_start = max(0, xl)
660676
x_end = min(self.width - 1, xr)
661677
if x_start <= x_end:
662-
self._d.line(x_start, y, x_end, y, color)
678+
self._line(x_start, y, x_end, y, color)

0 commit comments

Comments
 (0)