feat: add Import/Export for DWM rules in Homebridge UI

Export:
- '⬇ Export' button downloads all rules as dwm-rules-YYYY-MM-DD.json
- Compatible with desktop app rule format

Import:
- '⬆ Import' button opens file picker (accepts .json)
- Preview panel shows rule names + types before committing
- Merge mode: adds rules, skips any whose name already exists
- Replace mode: deletes all current rules then imports
- Server strips imported IDs/timestamps — fresh ones are assigned
- Reports imported/skipped count on completion

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
SRS IT
2026-03-28 20:25:12 -04:00
parent 951d4c4eaa
commit 3bcc427683
3 changed files with 143 additions and 0 deletions
@@ -84,6 +84,35 @@ class DibbyWemoUiServer extends HomebridgePluginUiServer {
return { ok: true };
});
this.onRequest('/rules/export', async () => {
return this._store.getDwmRules();
});
this.onRequest('/rules/import', async ({ rules, mode }) => {
if (!Array.isArray(rules) || rules.length === 0) throw new Error('No valid rules found in import data');
if (mode === 'replace') {
for (const r of this._store.getDwmRules()) this._store.deleteDwmRule(r.id);
}
const existing = this._store.getDwmRules();
const existingNames = new Set(existing.map((r) => (r.name ?? '').toLowerCase()));
let imported = 0, skipped = 0;
for (const rule of rules) {
// Strip old identity fields — store will assign fresh id + timestamps
const { id: _id, createdAt: _ca, updatedAt: _ua, ...ruleData } = rule;
if (mode === 'merge' && existingNames.has((ruleData.name ?? '').toLowerCase())) {
skipped++;
continue;
}
this._store.createDwmRule(ruleData);
imported++;
}
return { ok: true, imported, skipped };
});
// ── Scheduler heartbeat ───────────────────────────────────────────────────
this.onRequest('/scheduler/status', async () => {
const hb = this._store.getHeartbeat();