Skip to content

Commit 1d1ec9c

Browse files
authored
Added support for advanced UI elements (#58)
* Added support for advanced UI elements: gauge, arc, polygon, table, text_box, datamatrix, and xlegend. * datamatrix: add color/bgcolor, default boxsize 2 Add support for customizing DataMatrix foreground/background colors and change the default boxsize from 4 to 2. The renderer now coerces data to string, uses DataMatrixEncoder.get_imagedata (in-memory via BytesIO) instead of writing a temp file, converts the DataMatrix to RGBA and remaps pixels using getIndexColor for color/bgcolor, then pastes the image onto the canvas. README updated to document color and bgcolor options.
1 parent d519622 commit 1d1ec9c

5 files changed

Lines changed: 1159 additions & 669 deletions

File tree

README.md

Lines changed: 140 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,10 @@ target:
196196
> [!TIP]
197197
> All elements support the `visible` field (`true`/`false`) to conditionally show or hide them.
198198

199+
> [!NOTE]
200+
> **Color values**: This is an **e-ink display** that only supports 4 colors: `white`, `black`, `red`, `yellow`.
201+
> HEX strings (`#RRGGBB`) are accepted as a convenience but will be **automatically mapped to the nearest supported color**.
202+
199203
#### text
200204

201205
```yaml
@@ -212,6 +216,9 @@ target:
212216
stroke_width: 1
213217
stroke_fill: white
214218
max_width: 200 # Auto-wrap text within this pixel width
219+
rotation: 45 # Rotate text by angle (degrees counter-clockwise)
220+
background: "#CCCCCC" # Optional background color behind text
221+
background_padding: 3 # Padding around background (default: 2)
215222
```
216223

217224
If `y` is omitted the element stacks below the previous element automatically (`y_padding` controls the gap, default `10`).
@@ -243,6 +250,7 @@ If `y` is omitted the element stacks below the previous element automatically (`
243250
y_end: 64
244251
fill: black
245252
width: 2
253+
dash: [10, 5] # Optional: [on_px, off_px] for dashed/dotted lines
246254
```
247255

248256
If `y_start` is omitted the line is drawn at the current auto-stack Y position (`y_padding` offset).
@@ -326,6 +334,12 @@ Uses [Material Design Icons](https://pictogrammers.com/library/mdi/). You can us
326334

327335
Supports **HTTP/HTTPS URLs**, **local file paths**, and **Base64 data URIs**.
328336

337+
| `mode` | Description |
338+
|--------|-------------|
339+
| `stretch` | Stretch to fill exactly (default) |
340+
| `fit` / `contain` | Scale preserving aspect ratio, pad with transparency |
341+
| `fill` | Scale and crop to fill exactly, no padding |
342+
329343
```yaml
330344
- type: dlimg
331345
url: "https://example.com/image.png"
@@ -334,6 +348,7 @@ Supports **HTTP/HTTPS URLs**, **local file paths**, and **Base64 data URIs**.
334348
xsize: 100
335349
ysize: 100
336350
rotate: 0
351+
mode: fit # stretch / fit / fill / contain
337352
```
338353

339354
```yaml
@@ -415,6 +430,7 @@ Reads entity history from **Home Assistant Recorder**.
415430
- entity: sensor.temperature
416431
color: black
417432
width: 2
433+
area_fill: "#CCCCCC" # Optional: fill area under the line
418434
duration: 86400 # Seconds (default: 86400 = 1 day)
419435
x_start: 30
420436
y_start: 10
@@ -435,6 +451,11 @@ Reads entity history from **Home Assistant Recorder**.
435451
tick_every: 5
436452
grid: 5
437453
grid_color: black
454+
xlegend: # Optional: time labels on X-axis
455+
ticks: 3 # Number of labels (default: 3)
456+
format: "%H:%M" # strftime format (default: "%H:%M")
457+
color: black
458+
size: 9
438459
debug: false
439460
```
440461

@@ -452,9 +473,110 @@ Reads entity history from **Home Assistant Recorder**.
452473
fill: red
453474
outline: black
454475
width: 1
476+
radius: 8 # Optional: rounded corners radius
455477
show_percentage: true
456478
```
457479

480+
#### arc
481+
482+
```yaml
483+
- type: arc
484+
x_start: 10
485+
y_start: 10
486+
x_end: 110
487+
y_end: 110
488+
start_angle: 0
489+
end_angle: 270
490+
outline: black
491+
width: 3
492+
pie: false # true = pieslice (filled wedge), false = arc only
493+
fill: "#CCCCCC" # Only used when pie: true
494+
```
495+
496+
#### gauge
497+
498+
```yaml
499+
- type: gauge
500+
x: 120 # Center X
501+
y: 80 # Center Y
502+
radius: 60
503+
progress: 72 # Current value (mapped between min_value and max_value)
504+
min_value: 0
505+
max_value: 100
506+
fill: black # Progress arc color
507+
background: white # Track (background arc) color
508+
outline: black
509+
width: 8 # Arc bar thickness
510+
show_value: true # Display numeric value at center
511+
font: "fonts/NotoSansKR-Regular.ttf"
512+
size: 18
513+
color: black
514+
```
515+
516+
#### polygon
517+
518+
```yaml
519+
- type: polygon
520+
points: "10,100;60,10;110,100" # Semicolon-separated x,y pairs
521+
fill: black
522+
outline: black
523+
width: 1
524+
```
525+
526+
#### table
527+
528+
```yaml
529+
- type: table
530+
x: 10
531+
y: 50
532+
columns: [80, 120, 80] # Column widths in pixels
533+
rows:
534+
- ["TIME", "EVENT", "PLACE"] # First row treated as header if header: true
535+
- ["09:00", "Meeting", "3F"]
536+
- ["14:00", "Lunch", "B1"]
537+
header: true # First row is a header (default: true)
538+
header_fill: black
539+
header_color: white
540+
cell_color: black
541+
cell_fill: null # Optional background for data rows
542+
border_color: black
543+
border_width: 1
544+
row_height: 22
545+
padding: 4
546+
font: "fonts/NotoSansKR-Regular.ttf"
547+
font_size: 14
548+
align: left # left / center / right
549+
```
550+
551+
#### text_box
552+
553+
```yaml
554+
- type: text_box
555+
value: "ON"
556+
x: 10
557+
y: 10
558+
size: 18
559+
font: "fonts/NotoSansKR-Regular.ttf"
560+
padding: 6 # Padding around text
561+
fill: black # Box background color
562+
color: white # Text color
563+
outline: black # Box border color
564+
width: 1 # Border width
565+
radius: 6 # Corner radius
566+
```
567+
568+
#### datamatrix
569+
570+
```yaml
571+
- type: datamatrix
572+
data: "https://home-assistant.io"
573+
x: 10
574+
y: 10
575+
boxsize: 2 # Pixel size of each cell (default: 2)
576+
color: black
577+
bgcolor: white
578+
```
579+
458580
---
459581

460582
### Combined Example
@@ -517,20 +639,26 @@ target:
517639

518640
| **Type** | **Required Fields** | **Optional Fields** | **Description** |
519641
| --------------------- | ----------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------- |
520-
| **text** | `x`, `value` | `y`, `size`(20), `font`, `color`(black), `anchor`(lt), `align`(left), `spacing`(5), `stroke_width`(0), `stroke_fill`(white), `max_width`, `y_padding`(10) | Draws text (auto-wrap if `max_width` provided). Auto-stacks if `y` omitted. |
642+
| **text** | `x`, `value` | `y`, `size`(20), `font`, `color`(black), `anchor`(lt), `align`(left), `spacing`(5), `stroke_width`(0), `stroke_fill`(white), `max_width`, `y_padding`(10), `rotation`(0), `background`, `background_padding`(2) | Draws text. Supports rotation and background fill. Auto-stacks if `y` omitted. |
521643
| **multiline** | `x`, `value`, `delimiter`, `offset_y` | `start_y`, `size`(20), `font`, `color`(black), `anchor`(lm), `stroke_width`(0), `stroke_fill`(white), `y_padding`(10) | Splits text by delimiter and draws each line with `offset_y` spacing. |
522-
| **line** | `x_start`, `x_end` | `y_start`, `y_end`, `fill`(black), `width`(1), `y_padding`(0) | Draws a straight line. Auto-stacks if `y_start` omitted. |
644+
| **line** | `x_start`, `x_end` | `y_start`, `y_end`, `fill`(black), `width`(1), `y_padding`(0), `dash`([on,off]) | Draws a straight line. `dash` for dashed/dotted style. Auto-stacks if `y_start` omitted. |
523645
| **rectangle** | `x_start`, `x_end`, `y_start`, `y_end` | `fill`, `outline`(black), `width`(1), `radius`(0), `corners`(all) | Draws a rectangle with optional rounded corners. |
524646
| **rectangle_pattern** | `x_start`, `y_start`, `x_size`, `y_size`, `x_repeat`, `y_repeat`, `x_offset`, `y_offset` | `fill`, `outline`(black), `width`(1), `radius`(0), `corners`(all) | Repeated grid of rectangles (pattern/tiling). |
525647
| **circle** | `x`, `y`, `radius` | `fill`, `outline`(black), `width`(1) | Draws a circle at center (`x`, `y`). |
526648
| **ellipse** | `x_start`, `x_end`, `y_start`, `y_end` | `fill`, `outline`(black), `width`(1) | Draws an ellipse inside a bounding box. |
527-
| **icon** | `x`, `y`, `value`, `size` | `color`/`fill`(black), `anchor`(la), `stroke_width`(0), `stroke_fill`(white) | Draws [Material Design Icons](https://pictogrammers.com/library/mdi/). Supports `mdi:` prefix. |
528-
| **dlimg** | `x`, `y`, `url`, `xsize`, `ysize` | `rotate`(0) | Loads image from URL, local path, or Base64 data URI. |
649+
| **arc** | `x_start`, `y_start`, `x_end`, `y_end`, `start_angle`, `end_angle` | `fill`, `outline`(black), `width`(1), `pie`(false) | Draws an arc or filled pieslice. `pie: true` for filled wedge. |
650+
| **gauge** | `x`, `y`, `radius`, `progress` | `min_value`(0), `max_value`(100), `fill`(black), `background`(white), `outline`(black), `width`(8), `show_value`(false), `font`, `size`(16), `color`(black) | Circular gauge (270° sweep). Shows progress as arc. |
651+
| **polygon** | `points` | `fill`, `outline`(black), `width`(1) | Draws a polygon. `points`: `"x1,y1;x2,y2;..."` format. |
652+
| **table** | `x`, `y`, `columns`, `rows` | `header`(true), `header_fill`(black), `header_color`(white), `cell_color`(black), `cell_fill`, `border_color`(black), `border_width`(1), `row_height`, `padding`(4), `font`, `font_size`(14), `align`(left) | Draws a bordered table with optional header row. |
653+
| **text_box** | `x`, `y`, `value` | `size`(20), `font`, `padding`(5), `fill`(black), `color`(white), `outline`, `width`(1), `radius`(5) | Draws text inside a rounded, filled background box. |
654+
| **icon** | `x`, `y`, `value`, `size` | `color`/`fill`(black), `anchor`(la), `stroke_width`(0), `stroke_fill`(white) | Draws [Material Design Icons](https://pictogrammers.com/library/mdi/). Supports `mdi:` prefix. MDI metadata is cached. |
655+
| **dlimg** | `x`, `y`, `url`, `xsize`, `ysize` | `rotate`(0), `mode`(stretch) | Loads image from URL, local path, or Base64. `mode`: `stretch`/`fit`/`fill`/`contain`. |
529656
| **qrcode** | `x`, `y`, `data` | `color`(black), `bgcolor`(white), `border`(1), `boxsize`(2) | Generates and embeds a QR code. |
530657
| **barcode** | `x`, `y`, `data` | `color`(black), `bgcolor`(white), `code`(code128), `module_width`(0.2), `module_height`(7), `quiet_zone`(6.5), `font_size`(5), `text_distance`(5.0), `write_text`(true) | Draws various barcode formats. |
658+
| **datamatrix** | `x`, `y`, `data` | `color`(black), `bgcolor`(white), `boxsize`(2) | Generates a DataMatrix 2D barcode. Requires `pyStrich`. |
531659
| **diagram** | `x`, `y`, `height` | `width`(canvas), `margin`(20), `font`, `bars` | Creates a bar chart. `bars` object: `values`(required, `"name,val;..."`) `color`(required), `margin`(10), `legend_size`(10), `legend_color`(black). |
532-
| **plot** | `data`([{`entity`}]) | `duration`(86400), `x_start`(0), `y_start`(0), `x_end`, `y_end`, `size`(10), `font`, `low`, `high`, `ylegend`, `yaxis`, `debug`(false) | Time-series graph from HA Recorder. Per-series: `entity`(required), `color`(black), `width`(1), `joint`. |
533-
| **progress_bar** | `x_start`, `x_end`, `y_start`, `y_end`, `progress` | `direction`(right), `background`(white), `fill`(red), `outline`(black), `width`(1), `show_percentage`(false) | Draws a progress bar. `direction`: right/left/up/down. |
660+
| **plot** | `data`([{`entity`}]) | `duration`(86400), `x_start`(0), `y_start`(0), `x_end`, `y_end`, `size`(10), `font`, `low`, `high`, `ylegend`, `yaxis`, `xlegend`, `debug`(false) | Time-series graph from HA Recorder. Per-series: `entity`(required), `color`(black), `width`(1), `joint`, `area_fill`. |
661+
| **progress_bar** | `x_start`, `x_end`, `y_start`, `y_end`, `progress` | `direction`(right), `background`(white), `fill`(red), `outline`(black), `width`(1), `radius`(0), `show_percentage`(false) | Draws a progress bar. `direction`: right/left/up/down. `radius` for rounded corners. |
534662

535663
### `plot` Sub-Objects
536664

@@ -547,6 +675,12 @@ target:
547675
| | `tick_every` | `1` | Tick interval (value unit) |
548676
| | `grid` | `5` | Grid dot spacing (pixels) |
549677
| | `grid_color` | `black` | Grid color |
678+
| **xlegend** | `ticks` | `3` | Number of time labels |
679+
| | `format` | `"%H:%M"` | strftime format string |
680+
| | `color` | `black` | Label text color |
681+
| | `size` | (inherits) | Label font size |
682+
| | `font` | (inherits) | Label font file |
683+
| **data item** | `area_fill` | `null` | Fill color for area under the line |
550684

551685
---
552686

custom_components/gicisky/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from asyncio import sleep, Lock
1010
from io import BytesIO
1111

12-
from .imagegen import *
12+
from .renderer import *
1313
from .gicisky_ble import GiciskyBluetoothDeviceData, SensorUpdate
1414
from .gicisky_ble.writer import update_image
1515
from homeassistant.components.bluetooth import (
@@ -183,7 +183,7 @@ async def writeservice(service: ServiceCall) -> None:
183183

184184
threshold = int(service.data.get("threshold", 128))
185185
red_threshold = int(service.data.get("red_threshold", 128))
186-
image = await hass.async_add_executor_job(customimage, entry_id, data.device, service, hass)
186+
image = await hass.async_add_executor_job(render_image, entry_id, data.device, service, hass)
187187
image_bytes = BytesIO()
188188
image.save(image_bytes, "PNG")
189189
preview_coordinator.async_set_updated_data(image_bytes.getvalue())

0 commit comments

Comments
 (0)