feat: add WT32-SC01 LCD support + dynamic display resolution
- Add WT32-SC01 V1 (ESP32-WROVER, 480x320 LCD) as second board option - Split ESP32 code into epaper/ and lcd/ subdirectories - LCD version: LovyanGFX, color display, touch-to-refresh, no deep sleep - Add display_type, width, height to displays table (migration 003) - Frontend: display type selector with predefined resolutions - Preview and layout editor now use dynamic resolution from display Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -5,6 +5,9 @@ export interface Display {
|
||||
id: number;
|
||||
name: string;
|
||||
description: string | null;
|
||||
display_type: string;
|
||||
width: number;
|
||||
height: number;
|
||||
layout_id: number | null;
|
||||
api_token: string | null;
|
||||
last_seen: string | null;
|
||||
@@ -12,8 +15,22 @@ export interface Display {
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
// Predefined display types with resolutions
|
||||
export const DISPLAY_TYPES: Record<string, { width: number; height: number; label: string }> = {
|
||||
'epaper-7.5': { width: 800, height: 480, label: 'Waveshare 7.5" E-Paper (800x480)' },
|
||||
'wt32-sc01': { width: 480, height: 320, label: 'WT32-SC01 LCD (480x320)' },
|
||||
'epaper-4.2': { width: 400, height: 300, label: 'Waveshare 4.2" E-Paper (400x300)' },
|
||||
'epaper-5.83': { width: 648, height: 480, label: 'Waveshare 5.83" E-Paper (648x480)' },
|
||||
'custom': { width: 800, height: 480, label: 'Benutzerdefiniert' },
|
||||
};
|
||||
|
||||
const router = Router();
|
||||
|
||||
// GET /api/displays/types — available display types
|
||||
router.get('/types', (_req, res): void => {
|
||||
res.json(DISPLAY_TYPES);
|
||||
});
|
||||
|
||||
// GET /api/displays
|
||||
router.get('/', (_req, res): void => {
|
||||
try {
|
||||
@@ -40,13 +57,18 @@ router.get('/:id', (req, res): void => {
|
||||
router.post('/', (req, res): void => {
|
||||
try {
|
||||
const db = getDb();
|
||||
const { name, description, layout_id, api_token, refresh_sec } = req.body;
|
||||
const { name, description, display_type, width, height, layout_id, api_token, refresh_sec } = req.body;
|
||||
if (!name) { res.status(400).json({ error: 'name is required' }); return; }
|
||||
|
||||
const dtype = display_type ?? 'epaper-7.5';
|
||||
const defaults = DISPLAY_TYPES[dtype] ?? DISPLAY_TYPES['epaper-7.5'];
|
||||
const w = width ?? defaults.width;
|
||||
const h = height ?? defaults.height;
|
||||
|
||||
const result = db.prepare(
|
||||
`INSERT INTO displays (name, description, layout_id, api_token, refresh_sec)
|
||||
VALUES (?, ?, ?, ?, ?)`
|
||||
).run(name, description ?? null, layout_id ?? null, api_token ?? null, refresh_sec ?? 300);
|
||||
`INSERT INTO displays (name, description, display_type, width, height, layout_id, api_token, refresh_sec)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`
|
||||
).run(name, description ?? null, dtype, w, h, layout_id ?? null, api_token ?? null, refresh_sec ?? 300);
|
||||
const id = result.lastInsertRowid as number;
|
||||
res.status(201).json(db.prepare('SELECT * FROM displays WHERE id = ?').get(id));
|
||||
} catch (err) {
|
||||
@@ -62,9 +84,12 @@ router.put('/:id', (req, res): void => {
|
||||
const fields: string[] = [];
|
||||
const values: unknown[] = [];
|
||||
|
||||
const { name, description, layout_id, api_token, refresh_sec } = req.body;
|
||||
const { name, description, display_type, width, height, layout_id, api_token, refresh_sec } = req.body;
|
||||
if (name !== undefined) { fields.push('name = ?'); values.push(name); }
|
||||
if (description !== undefined) { fields.push('description = ?'); values.push(description); }
|
||||
if (display_type !== undefined) { fields.push('display_type = ?'); values.push(display_type); }
|
||||
if (width !== undefined) { fields.push('width = ?'); values.push(width); }
|
||||
if (height !== undefined) { fields.push('height = ?'); values.push(height); }
|
||||
if (layout_id !== undefined) { fields.push('layout_id = ?'); values.push(layout_id); }
|
||||
if (api_token !== undefined) { fields.push('api_token = ?'); values.push(api_token); }
|
||||
if (refresh_sec !== undefined) { fields.push('refresh_sec = ?'); values.push(refresh_sec); }
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import Database from 'better-sqlite3';
|
||||
import { migrate001 } from './migrations/001-initial';
|
||||
import { migrate002 } from './migrations/002-iobroker';
|
||||
import { migrate003 } from './migrations/003-display-resolution';
|
||||
|
||||
let dbInstance: Database.Database | null = null;
|
||||
|
||||
@@ -13,6 +14,7 @@ export function initDb(path: string): Database.Database {
|
||||
// Run migrations
|
||||
migrate001(db);
|
||||
migrate002(db);
|
||||
migrate003(db);
|
||||
|
||||
dbInstance = db;
|
||||
return db;
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import Database from 'better-sqlite3';
|
||||
|
||||
export function migrate003(db: Database): void {
|
||||
// Check if column already exists
|
||||
const columns = db.pragma('table_info(displays)') as { name: string }[];
|
||||
if (columns.some((c) => c.name === 'display_type')) return;
|
||||
|
||||
db.exec(`
|
||||
ALTER TABLE displays ADD COLUMN display_type TEXT DEFAULT 'epaper-7.5' NOT NULL;
|
||||
ALTER TABLE displays ADD COLUMN width INTEGER DEFAULT 800 NOT NULL;
|
||||
ALTER TABLE displays ADD COLUMN height INTEGER DEFAULT 480 NOT NULL;
|
||||
`);
|
||||
}
|
||||
@@ -43,6 +43,9 @@ interface RawDisplay {
|
||||
id: number;
|
||||
name: string;
|
||||
description: string | null;
|
||||
display_type: string;
|
||||
width: number;
|
||||
height: number;
|
||||
layout_id: number | null;
|
||||
api_token: string | null;
|
||||
last_seen: string | null;
|
||||
@@ -98,8 +101,8 @@ export function buildDisplayJson(displayId: number): DisplayJson | null {
|
||||
display: {
|
||||
id: display.id,
|
||||
name: display.name,
|
||||
width: 800,
|
||||
height: 480,
|
||||
width: display.width,
|
||||
height: display.height,
|
||||
refresh_sec: display.refresh_sec,
|
||||
},
|
||||
layout: { type: 'free' },
|
||||
@@ -166,8 +169,8 @@ export function buildDisplayJson(displayId: number): DisplayJson | null {
|
||||
display: {
|
||||
id: display.id,
|
||||
name: display.name,
|
||||
width: layout.width,
|
||||
height: layout.height,
|
||||
width: display.width,
|
||||
height: display.height,
|
||||
refresh_sec: display.refresh_sec,
|
||||
},
|
||||
layout: layoutResult,
|
||||
|
||||
Reference in New Issue
Block a user