diff --git a/README.md b/README.md index 13432c47..e76d1db3 100644 --- a/README.md +++ b/README.md @@ -112,7 +112,7 @@ curl -X POST http://your-pi-ip:5000/api/v3/plugins/install \ | Plugin | Description | Preview | |--------|-------------|---------| -| [Simple Clock](./plugins/clock-simple/) | Time and date display | | +| [Simple Clock](./plugins/clock-simple/) | Time and date display | clock-simple on an LED panel | | [7-Segment Clock](./plugins/7-segment-clock/) | Retro-style 7-segment clock with customizable colors | 7-segment-clock on an LED panel | | [Google Calendar](./plugins/calendar/) | Upcoming events from Google Calendar | | | [Geochron World Clock](./plugins/geochron/) | World map with the real-time day/night terminator | | diff --git a/docs/assets/clock-simple/colors.png b/docs/assets/clock-simple/colors.png new file mode 100644 index 00000000..0e8f5ac3 Binary files /dev/null and b/docs/assets/clock-simple/colors.png differ diff --git a/docs/assets/clock-simple/date-format.png b/docs/assets/clock-simple/date-format.png new file mode 100644 index 00000000..68d9ea01 Binary files /dev/null and b/docs/assets/clock-simple/date-format.png differ diff --git a/docs/assets/clock-simple/fonts.png b/docs/assets/clock-simple/fonts.png new file mode 100644 index 00000000..1ce8a5ff Binary files /dev/null and b/docs/assets/clock-simple/fonts.png differ diff --git a/docs/assets/clock-simple/hero.png b/docs/assets/clock-simple/hero.png new file mode 100644 index 00000000..2e726112 Binary files /dev/null and b/docs/assets/clock-simple/hero.png differ diff --git a/docs/assets/clock-simple/panel-sizes.png b/docs/assets/clock-simple/panel-sizes.png new file mode 100644 index 00000000..82686e03 Binary files /dev/null and b/docs/assets/clock-simple/panel-sizes.png differ diff --git a/docs/assets/clock-simple/shots.json b/docs/assets/clock-simple/shots.json new file mode 100644 index 00000000..81e2e8cd --- /dev/null +++ b/docs/assets/clock-simple/shots.json @@ -0,0 +1,448 @@ +{ + "plugin": "clock-simple", + "defaults": { + "width": 128, + "height": 32, + "scale": 6, + "freeze_time": "2026-09-02T19:07:09+00:00", + "config": { + "enabled": true, + "timezone": "America/New_York" + } + }, + "shots": [ + { + "name": "hero" + }, + { + "name": "fmt-12h", + "config": { + "time_format": "12h" + }, + "standalone": false + }, + { + "name": "fmt-24h", + "config": { + "time_format": "24h" + }, + "standalone": false + }, + { + "name": "sec-off", + "config": { + "show_seconds": false + }, + "standalone": false + }, + { + "name": "sec-on", + "config": { + "show_seconds": true + }, + "standalone": false + }, + { + "name": "date-old", + "config": { + "date_format": "OLD_CLOCK" + }, + "standalone": false + }, + { + "name": "date-mdy", + "config": { + "date_format": "MM/DD/YYYY" + }, + "standalone": false + }, + { + "name": "date-dmy", + "config": { + "date_format": "DD/MM/YYYY" + }, + "standalone": false + }, + { + "name": "date-iso", + "config": { + "date_format": "YYYY-MM-DD" + }, + "standalone": false + }, + { + "name": "date-none", + "config": { + "show_date": false + }, + "standalone": false + }, + { + "name": "ampm-off", + "config": { + "time_format": "12h", + "center_time_with_ampm": false + }, + "standalone": false + }, + { + "name": "ampm-on", + "config": { + "time_format": "12h", + "center_time_with_ampm": true + }, + "standalone": false + }, + { + "name": "size-64x32", + "width": 64, + "height": 32, + "scale": 8, + "standalone": false + }, + { + "name": "size-128x32", + "width": 128, + "height": 32, + "scale": 8, + "standalone": false + }, + { + "name": "size-128x64", + "width": 128, + "height": 64, + "scale": 8, + "standalone": false + }, + { + "name": "size-256x32", + "width": 256, + "height": 32, + "scale": 4, + "standalone": false + }, + { + "name": "colour-default", + "standalone": false + }, + { + "name": "colour-amber", + "config": { + "customization": { + "time_text": { + "text_color": [ + 255, + 176, + 0 + ] + }, + "date_text": { + "text_color": [ + 255, + 120, + 0 + ] + }, + "ampm_text": { + "text_color": [ + 255, + 200, + 80 + ] + } + } + }, + "standalone": false + }, + { + "name": "colour-cyan", + "config": { + "customization": { + "time_text": { + "text_color": [ + 0, + 229, + 255 + ] + }, + "date_text": { + "text_color": [ + 0, + 150, + 200 + ] + }, + "ampm_text": { + "text_color": [ + 140, + 240, + 255 + ] + } + } + }, + "standalone": false + }, + { + "name": "colour-mono", + "config": { + "customization": { + "time_text": { + "text_color": [ + 255, + 255, + 255 + ] + }, + "date_text": { + "text_color": [ + 255, + 255, + 255 + ] + }, + "ampm_text": { + "text_color": [ + 255, + 255, + 255 + ] + } + } + }, + "standalone": false + }, + { + "name": "font-PressStart2P_Regular", + "config": { + "customization": { + "time_text": { + "font": "PressStart2P-Regular.ttf" + }, + "date_text": { + "font": "PressStart2P-Regular.ttf" + }, + "ampm_text": { + "font": "PressStart2P-Regular.ttf" + } + } + }, + "standalone": false + }, + { + "name": "font-4x6_font", + "config": { + "customization": { + "time_text": { + "font": "4x6-font.ttf" + }, + "date_text": { + "font": "4x6-font.ttf" + }, + "ampm_text": { + "font": "4x6-font.ttf" + } + } + }, + "standalone": false + }, + { + "name": "font-5by7", + "config": { + "customization": { + "time_text": { + "font": "5by7.regular.ttf" + }, + "date_text": { + "font": "5by7.regular.ttf" + }, + "ampm_text": { + "font": "5by7.regular.ttf" + } + } + }, + "standalone": false + }, + { + "name": "font-5x7", + "config": { + "customization": { + "time_text": { + "font": "5x7.bdf" + }, + "date_text": { + "font": "5x7.bdf" + }, + "ampm_text": { + "font": "5x7.bdf" + } + } + }, + "standalone": false + }, + { + "name": "font-4x6", + "config": { + "customization": { + "time_text": { + "font": "4x6.bdf" + }, + "date_text": { + "font": "4x6.bdf" + }, + "ampm_text": { + "font": "4x6.bdf" + } + } + }, + "standalone": false + } + ], + "composites": [ + { + "name": "time-format", + "columns": 2, + "cells": [ + { + "shot": "fmt-12h", + "label": "time_format: 12h", + "sublabel": "the default" + }, + { + "shot": "fmt-24h", + "label": "time_format: 24h", + "sublabel": "no AM/PM line is drawn" + }, + { + "shot": "sec-off", + "label": "show_seconds: false", + "sublabel": "the default" + }, + { + "shot": "sec-on", + "label": "show_seconds: true", + "sublabel": "narrower digits to fit HH:MM:SS" + } + ] + }, + { + "name": "date-format", + "columns": 2, + "cells": [ + { + "shot": "date-old", + "label": "OLD_CLOCK", + "sublabel": "the default: weekday over 'September 2nd'" + }, + { + "shot": "date-mdy", + "label": "MM/DD/YYYY", + "sublabel": "US numeric" + }, + { + "shot": "date-dmy", + "label": "DD/MM/YYYY", + "sublabel": "day-first numeric" + }, + { + "shot": "date-iso", + "label": "YYYY-MM-DD", + "sublabel": "ISO 8601" + }, + { + "shot": "date-none", + "label": "show_date: false", + "sublabel": "time only, drawn larger" + }, + { + "shot": "ampm-on", + "label": "center_time_with_ampm: true", + "sublabel": "centres time and AM/PM as one block" + } + ] + }, + { + "name": "panel-sizes", + "columns": 1, + "cells": [ + { + "shot": "size-64x32", + "label": "64 x 32", + "sublabel": "text shrinks and the date abbreviates to fit" + }, + { + "shot": "size-128x32", + "label": "128 x 32", + "sublabel": "the common two-panel chain" + }, + { + "shot": "size-128x64", + "label": "128 x 64", + "sublabel": "room for full weekday and date" + }, + { + "shot": "size-256x32", + "label": "256 x 32", + "sublabel": "a long chain" + } + ] + }, + { + "name": "fonts", + "columns": 2, + "cells": [ + { + "shot": "font-PressStart2P_Regular", + "label": "PressStart2P-Regular.ttf", + "sublabel": "the default; chunky 8-bit" + }, + { + "shot": "font-4x6_font", + "label": "4x6-font.ttf", + "sublabel": "small, fits more on a line" + }, + { + "shot": "font-5by7", + "label": "5by7.regular.ttf", + "sublabel": "a rounder 5x7" + }, + { + "shot": "font-5x7", + "label": "5x7.bdf", + "sublabel": "bitmap; loaded at its native 7px size" + }, + { + "shot": "font-4x6", + "label": "4x6.bdf", + "sublabel": "the smallest bitmap; native 6px" + } + ] + }, + { + "name": "colors", + "columns": 2, + "cells": [ + { + "shot": "colour-default", + "label": "The defaults", + "sublabel": "white time, orange date, pale yellow AM/PM" + }, + { + "shot": "colour-amber", + "label": "Amber", + "sublabel": "the clock-radio look" + }, + { + "shot": "colour-cyan", + "label": "Cyan", + "sublabel": "high contrast on a dark wall" + }, + { + "shot": "colour-mono", + "label": "All white", + "sublabel": "set every element the same for a plain look" + } + ] + } + ] +} diff --git a/docs/assets/clock-simple/time-format.png b/docs/assets/clock-simple/time-format.png new file mode 100644 index 00000000..11f64fb6 Binary files /dev/null and b/docs/assets/clock-simple/time-format.png differ diff --git a/plugins.json b/plugins.json index 610c1736..8f51520d 100644 --- a/plugins.json +++ b/plugins.json @@ -164,10 +164,10 @@ "plugin_path": "plugins/clock-simple", "stars": 0, "downloads": 0, - "last_updated": "2026-07-31", + "last_updated": "2026-09-02", "verified": true, "screenshot": "", - "latest_version": "1.0.10" + "latest_version": "1.1.0" }, { "id": "countdown", diff --git a/plugins/clock-simple/README.md b/plugins/clock-simple/README.md index d2d68a5f..2222f460 100644 --- a/plugins/clock-simple/README.md +++ b/plugins/clock-simple/README.md @@ -1,184 +1,329 @@ ------------------------------------------------------------------------------------ -### Connect with ChuckBuilds - -- Show support on Youtube: https://www.youtube.com/@ChuckBuilds -- Stay in touch on Instagram: https://www.instagram.com/ChuckBuilds/ -- Want to chat or need support? Reach out on the ChuckBuilds Discord: https://discord.com/invite/uW36dVAtcT -- Feeling Generous? Support the project: - - Github Sponsorship: https://github.com/sponsors/ChuckBuilds - - Buy Me a Coffee: https://buymeacoffee.com/chuckbuilds - - Ko-fi: https://ko-fi.com/chuckbuilds/ - ------------------------------------------------------------------------------------ - -# Simple Clock Plugin - -A simple, customizable clock display plugin for LEDMatrix that shows the current time and date. +# Simple Clock + +A clean time-and-date clock for your LED matrix. It picks the largest text that +fits your panel, shrinks and abbreviates when it has to, and lets you restyle +the time, the date and the AM/PM marker independently. + +![The clock on a 128x32 panel showing 3:07 PM in white with PM in pale yellow, +and Wednesday over September 2nd in +orange](../../docs/assets/clock-simple/hero.png) + +*Every image in this README is real plugin output, rendered at the true panel +size against a frozen clock and then scaled up so the pixels stay pixels. +Nothing here is a mockup.* + +--- + +## Table of Contents + +1. [What's On Screen](#whats-on-screen) +2. [Installation](#installation) +3. [Quick Start](#quick-start) +4. [Configuration Reference](#configuration-reference) + - [enabled](#enabled) + - [display_duration](#display_duration) + - [update_interval](#update_interval) + - [timezone](#timezone) + - [time_format](#time_format) + - [show_seconds](#show_seconds) + - [center_time_with_ampm](#center_time_with_ampm) + - [show_date](#show_date) + - [date_format](#date_format) + - [position_x and position_y](#position_x-and-position_y) +5. [Fonts and Colours](#fonts-and-colours) +6. [Panel Sizes and How Text Shrinks](#panel-sizes-and-how-text-shrinks) +7. [Troubleshooting](#troubleshooting) +8. [Development](#development) +9. [Support](#support) + +--- + +## What's On Screen + +Up to three elements, each styled separately: + +```text + 3:07 PM <- the time, and the AM/PM marker (12-hour mode only) + Wednesday <- the weekday, in the OLD_CLOCK date format only +September 2nd <- the date +``` -## Features +The weekday line is specific to the default `OLD_CLOCK` date format. The +numeric formats draw a single date line and no weekday. -- **Time Display**: Shows current time in 12-hour or 24-hour format -- **Date Display**: Optional date display with multiple format options -- **Timezone Support**: Configurable timezone for accurate time display -- **Color Customization**: Customizable colors for time, date, and AM/PM indicator -- **Position Control**: Configurable display position +--- ## Installation -### From the Plugin Store (recommended) +**From the Plugin Store (recommended).** Open the LEDMatrix web interface at +`http://:5000`, go to **Plugin Manager**, find **Simple Clock** in +the **Plugin Store** section, and click **Install**. + +**Manually.** Copy this directory into your LEDMatrix `plugin-repos/` and +restart the display service. -1. Open the LEDMatrix web interface (`http://your-pi-ip:5000`) -2. Open the **Plugin Manager** tab -3. Find **Simple Clock** in the **Plugin Store** section (it's in the - `time` category) and click **Install** -4. Toggle the plugin on, then click **Restart Display Service** on the - **Overview** tab +Note that `enabled` defaults to **`false`**, so the clock does nothing until +you switch it on — unlike most plugins in this repo. -### Manual Installation +--- -1. Copy this plugin directory to your `plugins/` folder -2. Restart LEDMatrix -3. Enable the plugin in the web interface +## Quick Start -## Configuration +```json +{ + "clock-simple": { + "enabled": true + } +} +``` -Add the following to your `config/config.json`: +Everything else has a sensible default. A fully specified configuration: ```json { "clock-simple": { "enabled": true, - "timezone": "America/New_York", + "display_duration": 15, + "update_interval": 1, + "timezone": "America/Chicago", "time_format": "12h", "show_seconds": false, + "center_time_with_ampm": false, "show_date": true, - "date_format": "MM/DD/YYYY", - "display_duration": 15, + "date_format": "OLD_CLOCK", "position_x": 0, "position_y": 0, "customization": { - "time_text": { - "font": "PressStart2P-Regular.ttf", - "font_size": 8, - "text_color": [255, 255, 255] - }, - "date_text": { - "font": "PressStart2P-Regular.ttf", - "font_size": 8, - "text_color": [255, 128, 64] - }, - "ampm_text": { - "font": "PressStart2P-Regular.ttf", - "font_size": 8, - "text_color": [255, 255, 128] - } + "time_text": { "font": "PressStart2P-Regular.ttf", "font_size": 8, + "text_color": [255, 255, 255] }, + "date_text": { "font": "PressStart2P-Regular.ttf", "font_size": 8, + "text_color": [255, 128, 64] }, + "ampm_text": { "font": "PressStart2P-Regular.ttf", "font_size": 8, + "text_color": [255, 255, 128] } } } } ``` -### Configuration Options +--- + +## Configuration Reference + +| Option | Type | Default | What it does | +|--------|------|---------|--------------| +| [`enabled`](#enabled) | boolean | `false` | Whether the clock runs at all | +| [`display_duration`](#display_duration) | number | `15` | Seconds the clock holds the panel per turn | +| [`update_interval`](#update_interval) | integer | `1` | Seconds between refreshes | +| [`timezone`](#timezone) | string / null | `null` | IANA timezone, or inherit | +| [`time_format`](#time_format) | string | `12h` | `12h` or `24h` | +| [`show_seconds`](#show_seconds) | boolean | `false` | Append `:SS` | +| [`center_time_with_ampm`](#center_time_with_ampm) | boolean | `false` | Centre time and AM/PM as one block | +| [`show_date`](#show_date) | boolean | `true` | Draw the date at all | +| [`date_format`](#date_format) | string | `OLD_CLOCK` | Which date style | +| [`position_x`](#position_x-and-position_y) | integer | `0` | Horizontal nudge, in pixels | +| [`position_y`](#position_x-and-position_y) | integer | `0` | Vertical nudge, in pixels | + +### `enabled` + +Turns the clock on. **It defaults to `false`**, which is the single most common +reason a fresh install shows nothing. + +### `display_duration` + +How many seconds the clock holds the panel each time the rotation reaches it. +This does not affect accuracy — the time is re-read every frame, so it stays +correct for the whole turn however long you make it. + +### `update_interval` + +Seconds between refreshes, default `1`. With `show_seconds` on you want `1`; +with it off you could raise it, but there is little to gain — the plugin only +pushes pixels to the panel when the rendered image actually changes, so an +unchanged minute costs nothing either way. + +### `timezone` + +An IANA timezone name such as `America/Chicago`, `Europe/London` or +`Australia/Sydney`. Resolution order, first match wins: + +1. `timezone` in this plugin's config +2. The global LEDMatrix `timezone` setting +3. The host system's timezone + +Leave it unset on a normal install. Set it only when you want this clock to +show a *different* zone from the rest of your board — a second clock for a +remote office, for instance. + +### `time_format` + +`12h` (the default) draws `3:07` with a separate `PM` marker that you can +colour independently. `24h` draws `15:07` and no marker at all — the AM/PM +element simply is not used. + +### `show_seconds` + +Appends `:SS`, giving `3:07:09` or `15:07:09`. The string is wider, so on a +narrow panel the clock picks a smaller size to fit it. + +![Four panels comparing 12-hour against 24-hour, and seconds off against +on](../../docs/assets/clock-simple/time-format.png) + +### `center_time_with_ampm` -| Option | Type | Default | Description | -|--------|------|---------|-------------| -| `enabled` | boolean | `false` | Enable or disable the plugin | -| `display_duration` | number | `15` | Seconds to hold the screen each rotation (1–300) | -| `update_interval` | integer | `1` | How often to redraw the clock, in seconds (1–60) | -| `timezone` | string \| null | `null` | IANA timezone name (e.g. `"America/New_York"`). When null, inherits the global LEDMatrix timezone | -| `time_format` | string | `"12h"` | `"12h"` or `"24h"` | -| `center_time_with_ampm` | boolean | `false` | Center the time + AM/PM as one block (12h only) | -| `show_seconds` | boolean | `false` | Include seconds in the time | -| `show_date` | boolean | `true` | Show date below the time | -| `date_format` | string | `"OLD_CLOCK"` | One of `"MM/DD/YYYY"`, `"DD/MM/YYYY"`, `"YYYY-MM-DD"`, `"OLD_CLOCK"` (e.g. `Monday, January 1st`) | -| `position_x` | integer | `0` | X offset in pixels | -| `position_y` | integer | `0` | Y offset in pixels | -| `customization` | object | See below | Per-element font and color overrides | +By default the time is centred on the panel and the `PM` marker sits beside it, +which means the digits stay put as the hour changes between one and two +characters. Set this to `true` to centre the time *and* the marker together as +one block — tidier at a glance, at the cost of the digits shifting slightly +when the hour rolls from `9:59` to `10:00`. -### Customization Options +### `show_date` -The `customization` object allows you to customize fonts and colors for each display element: +Set to `false` for a time-only clock. The time is then drawn larger, since it +has the whole panel to itself. -- **`time_text`**: Font and color settings for the time display - - `font`: Font family (e.g., `"PressStart2P-Regular.ttf"`) - - `font_size`: Font size in pixels (4-16) - - `text_color`: RGB color array `[R, G, B]` (default: `[255, 255, 255]`) +### `date_format` -- **`date_text`**: Font and color settings for the date display - - `font`: Font family - - `font_size`: Font size in pixels (4-16) - - `text_color`: RGB color array (default: `[255, 128, 64]`) +| Value | Renders as | Notes | +|-------|------------|-------| +| `OLD_CLOCK` | `Wednesday` over `September 2nd` | The default. The only format with a weekday line | +| `MM/DD/YYYY` | `09/02/2026` | US numeric | +| `DD/MM/YYYY` | `02/09/2026` | Day-first numeric | +| `YYYY-MM-DD` | `2026-09-02` | ISO 8601 | -- **`ampm_text`**: Font and color settings for AM/PM indicator (12-hour format only) - - `font`: Font family - - `font_size`: Font size in pixels (4-16) - - `text_color`: RGB color array (default: `[255, 255, 128]`) +![Six panels showing the four date formats, a time-only clock, and the centred +AM/PM variant](../../docs/assets/clock-simple/date-format.png) -### Timezone Examples +`OLD_CLOCK` is the format the original LEDMatrix clock used, with a full month +name and an ordinal day. It is the widest of the four, which is what makes the +shrink-to-fit behaviour below most visible. -- `"America/New_York"` - Eastern Time -- `"America/Chicago"` - Central Time -- `"America/Denver"` - Mountain Time -- `"America/Los_Angeles"` - Pacific Time -- `"Europe/London"` - GMT/BST -- `"Asia/Tokyo"` - Japan Standard Time -- `"Australia/Sydney"` - Australian Eastern Time +### `position_x` and `position_y` -## Usage +Pixel offsets applied to the whole clock, both defaulting to `0`. Positive `x` +moves right, positive `y` moves down. These are for nudging the layout on a +panel where it sits slightly wrong — for centring, leave them alone, since the +clock already centres itself on the panel it is given. -Once installed and configured: +--- -1. The plugin will automatically update every second (based on `update_interval` in manifest) -2. The display will show during rotation according to your configured `display_duration` -3. The time updates in real-time based on your configured timezone +## Fonts and Colours + +Three elements are styled independently under `customization`: `time_text`, +`date_text` and `ampm_text`. Each takes `font`, `font_size` and `text_color`. + +`text_color` is an `[R, G, B]` array. The defaults are deliberately not all +white — white time, orange date, pale yellow AM/PM — so the three read as +distinct at a glance. + +![Four panels showing the default colours, an amber scheme, a cyan scheme and +all-white](../../docs/assets/clock-simple/colors.png) + +`font` takes one of five faces: + +![Five panels showing each available font rendering the same +time](../../docs/assets/clock-simple/fonts.png) + +| Font | Kind | Notes | +|------|------|-------| +| `PressStart2P-Regular.ttf` | Scalable | The default; chunky 8-bit, very legible across a room | +| `4x6-font.ttf` | Scalable | Small, fits noticeably more on a line | +| `5by7.regular.ttf` | Scalable | A rounder 5×7 face | +| `5x7.bdf` | Bitmap | Crisp; always drawn at its native 7px size | +| `4x6.bdf` | Bitmap | The smallest option; native 6px | + +**`font_size` only applies to the scalable `.ttf` faces.** A `.bdf` is a bitmap +font that exists at exactly one pixel size, so the plugin loads it at the size +the file declares and ignores `font_size`. That is why the two `.bdf` faces +look sharp while a `.ttf` scaled to an odd size looks soft. + +--- + +## Panel Sizes and How Text Shrinks + +The clock measures its text against the panel and steps down rather than +overflowing. + +![The same clock on 64x32, 128x32, 128x64 and 256x32 +panels](../../docs/assets/clock-simple/panel-sizes.png) + +Two behaviours worth knowing, both visible in the 64×32 panel above: + +- **The weekday abbreviates.** `Wednesday` becomes `Wed` when the full name + will not fit. +- **The date falls back through shorter forms.** `September 2nd` becomes + `Sep 2nd`, then `Sep 2`; the numeric formats drop to a two-digit year + (`09/02/26`) and then to `09-02`. + +The clock only shortens what it must, so a wider panel keeps the full text. On +a 128×64 panel the time and date sit in separate bands with the space between +them; on a 256×32 chain the clock centres and simply has more black around it. + +--- ## Troubleshooting -### Common Issues +**Nothing appears at all.** +`enabled` defaults to `false` in this plugin. Check it is `true`. -**Time shows wrong timezone:** -- Verify the `timezone` setting in your configuration (if specified) -- If not specified, check the global `timezone` setting in your main LEDMatrix config -- Check that the timezone string is valid (see timezone examples above) +**The time is wrong by a whole number of hours.** +That is the timezone. Check `timezone` here, then the global LEDMatrix +`timezone`, then the host clock. Remember an unset value inherits rather than +defaulting to UTC. -**Colors not displaying correctly:** -- Ensure RGB values are between 0-255 -- Check that your display supports the chosen colors +**The time is wrong by minutes.** +That is the Pi's system clock, not the plugin. Check `timedatectl` and that NTP +is reaching a time server. -**Plugin not appearing in rotation:** -- Verify `enabled` is set to `true` -- Check that the plugin loaded successfully in the web interface -- Ensure `display_duration` is greater than 0 +**I picked a font and nothing changed.** +On a current version all five faces work. On older versions the two `.bdf` +options silently fell back to the default, because they were requested at +`font_size` rather than at the pixel size the file declares. If you are on an +older build, use one of the `.ttf` faces. -### Debug Logging +**The date is abbreviated and I want it in full.** +That is the fit logic — the full text does not fit at the current font size. +Use a smaller font (`4x6.bdf` or `4x6-font.ttf`), a shorter `date_format`, or a +wider panel. -Enable debug logging to troubleshoot issues: +**Text is cut off at an edge.** +Check `position_x` / `position_y` are `0`. The clock centres itself, so a +non-zero offset is the usual cause. -```json -{ - "logging": { - "level": "DEBUG", - "file": "/path/to/ledmatrix.log" - } -} +### Debug logging + +The plugin logs the formatted time once a minute at INFO, and font-loading +problems at WARNING: + +```bash +journalctl -t ledmatrix -f ``` +A line like `Could not load font @` means the face was found but +could not be loaded at that size; `Font file not found` means the file is +missing from `assets/fonts/`. + +--- + ## Development -### Plugin Structure +### Plugin structure +```text +clock-simple/ +├── manifest.json # Plugin metadata and version history +├── manager.py # ClockSimplePlugin +├── config_schema.json # Settings schema; source of truth for defaults +├── README.md +└── LICENSE ``` -plugins/clock-simple/ -├── manifest.json # Plugin metadata and requirements -├── manager.py # Main plugin class -├── config_schema.json # Configuration validation schema -└── README.md # This file -``` -### Testing +Fonts are not shipped with the plugin — they come from the core's +`assets/fonts/` directory. + +### Previewing a change without a panel -The fastest way to verify a change without restarting the full display -service is the LEDMatrix dev preview server, which renders this plugin in -your browser without any hardware: +The fastest way to see a change is the core's dev preview server, which renders +the plugin in a browser with no hardware: ```bash cd /path/to/LEDMatrix @@ -186,15 +331,30 @@ python3 scripts/dev_server.py --extra-dir /path/to/ledmatrix-plugins/plugins/clo # then open http://localhost:5001 ``` -You can also render a single still frame to a PNG with -`scripts/render_plugin.py` — see -[`docs/DEV_PREVIEW.md`](https://github.com/ChuckBuilds/LEDMatrix/blob/main/docs/DEV_PREVIEW.md) -for details. +To render a single still frame to a PNG instead, use `scripts/render_plugin.py` +in the core — see +[docs/DEV_PREVIEW.md](https://github.com/ChuckBuilds/LEDMatrix/blob/main/docs/DEV_PREVIEW.md). + +### Regenerating the images in this README + +Every screenshot comes from a declarative shot list against a frozen clock, so +re-rendering is reproducible: + +```bash +python scripts/render_docs_assets.py --plugin clock-simple +``` + +`--check` verifies the committed images still match what the plugin renders. -## License +--- -GPL-3.0 License - feel free to modify and distribute. +## Support -## Contributing +- YouTube: +- Instagram: +- Discord: +- Sponsor: [GitHub Sponsors](https://github.com/sponsors/ChuckBuilds) · + [Buy Me a Coffee](https://buymeacoffee.com/chuckbuilds) · + [Ko-fi](https://ko-fi.com/chuckbuilds/) -Found a bug or want to add features? Please create an issue or submit a pull request on the [LEDMatrix GitHub repository](https://github.com/ChuckBuilds/LEDMatrix). +Released under the GNU General Public License v3.0 — see [LICENSE](LICENSE). diff --git a/plugins/clock-simple/config_schema.json b/plugins/clock-simple/config_schema.json index c3c11b1f..aad94dfc 100644 --- a/plugins/clock-simple/config_schema.json +++ b/plugins/clock-simple/config_schema.json @@ -25,13 +25,19 @@ "x-advanced": true }, "timezone": { - "type": ["string", "null"], + "type": [ + "string", + "null" + ], "default": null, "description": "Timezone for the clock display (e.g., 'America/New_York', 'Europe/London'). Use IANA timezone database names. If not specified or set to null, inherits from LEDMatrix global timezone setting." }, "time_format": { "type": "string", - "enum": ["12h", "24h"], + "enum": [ + "12h", + "24h" + ], "default": "12h", "description": "Time format: '12h' for 12-hour format with AM/PM, '24h' for 24-hour format" }, @@ -53,7 +59,12 @@ }, "date_format": { "type": "string", - "enum": ["MM/DD/YYYY", "DD/MM/YYYY", "YYYY-MM-DD", "OLD_CLOCK"], + "enum": [ + "MM/DD/YYYY", + "DD/MM/YYYY", + "YYYY-MM-DD", + "OLD_CLOCK" + ], "default": "OLD_CLOCK", "description": "Date format to display. OLD_CLOCK matches the original clock layout with weekday and month/day with ordinal suffix (e.g., 'Monday, January 1st')" }, @@ -88,8 +99,7 @@ "4x6-font.ttf", "5by7.regular.ttf", "5x7.bdf", - "4x6.bdf", - "cozette.bdf" + "4x6.bdf" ], "default": "PressStart2P-Regular.ttf", "x-advanced": true @@ -114,10 +124,18 @@ }, "minItems": 3, "maxItems": 3, - "default": [255, 255, 255] + "default": [ + 255, + 255, + 255 + ] } }, - "x-propertyOrder": ["font", "font_size", "text_color"], + "x-propertyOrder": [ + "font", + "font_size", + "text_color" + ], "additionalProperties": false }, "date_text": { @@ -134,8 +152,7 @@ "4x6-font.ttf", "5by7.regular.ttf", "5x7.bdf", - "4x6.bdf", - "cozette.bdf" + "4x6.bdf" ], "default": "PressStart2P-Regular.ttf", "x-advanced": true @@ -160,10 +177,18 @@ }, "minItems": 3, "maxItems": 3, - "default": [255, 128, 64] + "default": [ + 255, + 128, + 64 + ] } }, - "x-propertyOrder": ["font", "font_size", "text_color"], + "x-propertyOrder": [ + "font", + "font_size", + "text_color" + ], "additionalProperties": false }, "ampm_text": { @@ -180,8 +205,7 @@ "4x6-font.ttf", "5by7.regular.ttf", "5x7.bdf", - "4x6.bdf", - "cozette.bdf" + "4x6.bdf" ], "default": "PressStart2P-Regular.ttf", "x-advanced": true @@ -206,17 +230,31 @@ }, "minItems": 3, "maxItems": 3, - "default": [255, 255, 128] + "default": [ + 255, + 255, + 128 + ] } }, - "x-propertyOrder": ["font", "font_size", "text_color"], + "x-propertyOrder": [ + "font", + "font_size", + "text_color" + ], "additionalProperties": false } }, - "x-propertyOrder": ["time_text", "date_text", "ampm_text"], + "x-propertyOrder": [ + "time_text", + "date_text", + "ampm_text" + ], "additionalProperties": false } }, - "required": ["enabled"], + "required": [ + "enabled" + ], "additionalProperties": false } diff --git a/plugins/clock-simple/manager.py b/plugins/clock-simple/manager.py index 386bad39..0a7d9bc3 100644 --- a/plugins/clock-simple/manager.py +++ b/plugins/clock-simple/manager.py @@ -132,6 +132,24 @@ def _load_element_font(self, element_cfg: Dict[str, Any]): font = ImageFont.truetype(path, size) break except OSError as e: + # A .bdf is a bitmap face and only exists at the one pixel + # size it was drawn at; FreeType rejects any other with + # "invalid pixel size". Asking for font_size (8 by default) + # therefore failed for every .bdf in the picker, and the + # clock silently fell back to the default font -- the + # setting looked like it did nothing. Retry at the size the + # file declares. + native = self._bdf_pixel_size(path) + if native is not None and native != size: + try: + font = ImageFont.truetype(path, native) + self.logger.debug( + "Loaded bitmap font %s at its native size %d " + "(requested %d)", name, native, size + ) + break + except OSError: + pass # OSError only: that is what FreeType raises for a missing, # unreadable or malformed face. Catching everything here # would turn a programming error into a silent fallback to @@ -142,6 +160,20 @@ def _load_element_font(self, element_cfg: Dict[str, Any]): self._font_cache[key] = font return self._font_cache[key] + @staticmethod + def _bdf_pixel_size(path: str) -> Optional[int]: + """The pixel size a .bdf font declares, or None if it does not.""" + try: + with open(path, "r", encoding="latin-1") as handle: + for line in handle: + if line.startswith("PIXEL_SIZE"): + return int(line.split()[1]) + if line.startswith("CHARS"): + break # past the header; no point reading the glyphs + except (OSError, ValueError, IndexError): + return None + return None + def _get_global_timezone(self) -> str: """Get the global timezone from the main config.""" try: diff --git a/plugins/clock-simple/manifest.json b/plugins/clock-simple/manifest.json index 1aac999f..a5ecd7f7 100644 --- a/plugins/clock-simple/manifest.json +++ b/plugins/clock-simple/manifest.json @@ -1,7 +1,7 @@ { "id": "clock-simple", "name": "Simple Clock", - "version": "1.0.10", + "version": "1.1.0", "author": "ChuckBuilds", "description": "A simple clock display with current time and date", "category": "time", @@ -16,6 +16,11 @@ "clock-simple" ], "versions": [ + { + "version": "1.1.0", + "released": "2026-09-02", + "ledmatrix_min": "2.0.0" + }, { "version": "1.0.10", "released": "2026-07-31", @@ -25,13 +30,13 @@ "released": "2026-07-21", "version": "1.0.8", "ledmatrix_min": "2.0.0", - "notes": "Actually stop the per-second flash. The previous fix only avoided a full hardware clear on unchanged frames, but it still called display_manager.clear() whenever the visible content changed — and clear() writes black straight to the panel's front buffer, so the panel blanked between the clear and the redraw on the core's ~1 Hz loop. With seconds shown that content changes every second, so it flashed every second (and once a minute otherwise). The clock now rebuilds each frame on a fresh in-memory buffer and lets update_display() swap it in atomically (SwapOnVSync); dirty-tracking skips the push when pixels are unchanged. No hardware clear, so the panel never blanks — whether or not seconds are shown." + "notes": "Actually stop the per-second flash. The previous fix only avoided a full hardware clear on unchanged frames, but it still called display_manager.clear() whenever the visible content changed \u2014 and clear() writes black straight to the panel's front buffer, so the panel blanked between the clear and the redraw on the core's ~1 Hz loop. With seconds shown that content changes every second, so it flashed every second (and once a minute otherwise). The clock now rebuilds each frame on a fresh in-memory buffer and lets update_display() swap it in atomically (SwapOnVSync); dirty-tracking skips the push when pixels are unchanged. No hardware clear, so the panel never blanks \u2014 whether or not seconds are shown." }, { "released": "2026-07-19", "version": "1.0.7", "ledmatrix_min": "2.0.0", - "notes": "Fix the clock flashing/blinking periodically. The plugin now redraws its content on every render tick and only clears the buffer when the displayed time or date changes, instead of skipping frames — so the panel stays lit steadily regardless of update_interval. Also fit the time/AM-PM and date to the panel width so content no longer overflows the edge on narrow (64px-wide) matrices; the weekday and date are abbreviated only when the full text wouldn't fit, so wider panels are unchanged." + "notes": "Fix the clock flashing/blinking periodically. The plugin now redraws its content on every render tick and only clears the buffer when the displayed time or date changes, instead of skipping frames \u2014 so the panel stays lit steadily regardless of update_interval. Also fit the time/AM-PM and date to the panel width so content no longer overflows the edge on narrow (64px-wide) matrices; the weekday and date are abbreviated only when the full text wouldn't fit, so wider panels are unchanged." }, { "released": "2026-07-17", @@ -50,7 +55,7 @@ "ledmatrix_min_version": "2.0.0" } ], - "last_updated": "2026-07-31", + "last_updated": "2026-09-02", "stars": 0, "downloads": 0, "verified": true,