Commit Graph
32 Commits
Author SHA1 Message Date
maziggy cd004df817 Show Home Assistant sensors on the printer card (#1148, #448)
Binds binary_sensor and reading-carrying sensor entities to a printer and
renders their state on its card, worded by Home Assistant's device_class.
Optional per-sensor alert condition drives a notification on the transition
into the alert state and an opt-in interlock that holds queued prints while
alerting -- a hold with a readable waiting_reason, never a failure, and only
ever on a sensor that was read successfully.

Sensors get their own table rather than a wider entity pattern on SmartPlug:
get_smart_plug_by_printer would otherwise hand the card's power button a door
contact to switch.

The hold is passed to the model matcher directly rather than merged into
busy_printers: _check_auto_drying reads that set as "is currently printing"
and would put an idle-but-held printer down the mid-print drying path.

The notification_providers migration spells its default FALSE, not 0 --
Postgres rejects an integer default for a boolean and _safe_execute swallows
the error.
2026-08-05 14:26:38 +02:00
maziggy 71a06f3638 Add batch orders with a quantity per plate (#342)
Printing a multi-plate file in different quantities per plate meant
queueing each plate separately and tracking the counts by hand: one
shared Quantity field cannot say "plate 1 once, plate 2 twice, plate 3
three times". Each selected plate now carries its own quantity, and the
submission becomes an order on a new Batches tab.

The point is the distinction the old flat batch could not express.
print_batch_plates stores how many runs of each plate were wanted,
separately from what was queued, so a run that fails, is cancelled or is
skipped does not satisfy a target -- the order goes on saying it owes a
print instead of quietly under-delivering. Queue remaining re-queues
exactly what is missing, for the whole order or one plate, by cloning
the most recent item for that plate: that inherits the printer or model
target, AMS mapping, filament overrides and print options along with the
validation they already passed, rather than re-serialising twenty fields
through a template that would drift from the model the first time
someone adds a column. Clones append to the end of the relevant
printer's queue and take the same advisory lock the add-to-queue route
does; positions are per-printer sequences, not global.

Cost is measured, not estimated. print_log_entries gains queue_item_id,
set where the queue item is already in scope, so each run's material and
energy are attributed through the item that produced them -- an
unrelated reprint of the same archive never lands in an order's total,
and a multi-plate order gets each plate's own cost rather than the whole
file's via the plate-scoped estimate from #2614. Before any run has
completed there is no honest figure, so cost reads as unknown instead of
a fabricated 0.00.

The Batches tab wires up GET /queue/batches, which has been unreferenced
since the batch MVP shipped, along with six locale keys that were
translated and never used. It is a separate tab because an order
outlives the queue that produced it: once its runs finish they leave the
active queue, so Queue and History each hold half the picture.

completed was not a reachable status before now, so every batch created
since April is still marked active however long ago its last print
finished -- 73 of them on the development install. A startup pass closes
out the finished ones: those whose runs all completed become completed,
and groupings whose items were all cancelled become cancelled, which is
what they are. Not applied to orders, which state their intent
independently of their runs and still owe the work. Only batches with
nothing queued or printing are considered, and repeating the pass also
catches an order whose last run landed while the process was down.
Batches with neither items nor targets are no longer listed at all --
empty shells left when a grouping's items went with their source
archive.

Dispatch applies the same source-file gates as POST /queue/. It creates
queue items, so without them it would be a weaker door to the same
outcome; the archive and library-file checks move into shared helpers
so a third route cannot drift from them.
2026-08-04 11:11:36 +02:00
maziggy da07c5884b Add variant-group data model for cross-model queue alternatives (#671)
Adds file_variant_groups plus variant_group_id / variant_position on
library_files, so a set of files that are the same job sliced for
different printers can be resolved to whichever printer frees up first.

Backfills groups from the sliced_from_library_file_id provenance that
slice_and_persist and the pipeline runner have been writing into
file_metadata since they shipped, and which nothing has ever read.
Only sources with two or more children carrying distinct
sliced_for_model values are grouped: a single candidate is not a
choice, and two slices for the same printer give the resolver no basis
to prefer one.
2026-08-03 10:17:28 +02:00
maziggy 4bbf0f031e feat(slicer): Pipelines — archive entry point + slicer progress toast (#1425 PR B follow-up)
Two real gaps from the PR B drop:

1. Run-with-pipeline only existed in the file manager. Operators who keep
   working files in archives had to copy them to the library to use a
   pipeline.

2. Triggering a slice via a pipeline produced a silent multi-second-to-
   minute wait. The manual SliceModal flow shows the sticky
   "Slicing X - Generating G-code 75%" persistent toast; the pipeline
   path went through asyncio.create_task directly and never registered
   with SliceJobTracker.

Archive entry point
- POST /slicer-pipelines/{id}/check-eligibility and /run accept
  source_archive_id as an alternative to source_library_file_id (XOR,
  enforced by Pydantic validator).
- PipelineRun.source_archive_id is a new nullable FK column with the
  ALTER TABLE migration in run_migrations (idempotent via _safe_execute,
  works on SQLite + Postgres).
- _resolve_source branches: archive path reads source_3mf_path with
  fallback to file_path, mirroring routes/archives.py.
- ArchiveCard's context menu picks up a "Run with pipeline" item next to
  Slice (only on source archives), gated on useSlicerApi + pipelines:run.
  Slice (only on source archives), gated on useSlicerApi + pipelines:run.
- Path-safety: SEC-PATH-OK markers added at both LibraryFile.file_path
  and archive.source_3mf_path join sites, citing the upload-time
  validators.

Progress toast
- Pipeline orchestration is now the `run` callable of a
  slice_dispatch.enqueue call — the same dispatcher SliceModal uses —
  instead of a bare asyncio.create_task. The SliceJob lifecycle drives
  the existing progress toast end to end with no separate notification
  surface for pipeline runs.
- PipelineRun.slice_job_id is set before the 202 returns.
- RunWithPipelineModal calls useSliceJobTracker().trackJob() from
  runMutation.onSuccess.
- RunWithPipelineModal source prop is now {kind, id, filename}
  mirroring SliceModal.SliceSource; api.checkPipelineEligibility +
  api.runPipeline take a discriminated-union source argument.
2026-06-27 15:01:14 +02:00
maziggy d6bdb7e200 feat(slicer): Slicer Pipelines — save & reuse a preset bundle in one click (#1425 PR A)
The SliceModal forces the user to pick four slots every time (printer /
process / filament(s) / bed type). For fleet production that's tedious
and error-prone. Pipelines let an operator save a named bundle and apply
it with one click on the next file.

PR A is bundle-and-management only. PR B adds single-target dispatch,
PR C adds multi-copy batch with capability-matched fanout. Future-PR
columns (target_kind / target_printer_id / target_model_class /
fanout_strategy) ship in this migration so PR B+ is code-only, not a
schema bump.

Backend
- New model SlicerPipeline + slicer_pipelines table; soft-delete via
  is_deleted so PR B+ run history can still resolve metadata.
- Pydantic schemas reuse the existing PresetRef shape from
  schemas/slicer.py.
- CRUD routes at /api/v1/slicer-pipelines/ — list (newest first by id
  DESC), create (201), get-by-id, partial PUT, soft-delete (204).
- Three new permissions: PIPELINES_READ / PIPELINES_WRITE / PIPELINES_RUN.
  Administrators + Operators get all three; Viewers get READ.
  Backfill in seed_default_groups() so existing installs upgrade
  cleanly. All three denied to API keys for now.

Frontend
- Settings → Workflow splits into two horizontal sub-tabs mirroring
  the Authentication tab pattern: "Queue & Dispatch" (existing
  Workflow content) and "Pipelines" (new). URL deep-link via
  ?tab=queue&sub=pipelines.
- SlicerPipelinesPanel — list, inline rename, delete, stale-preset
  warning when a referenced preset no longer resolves.
- SliceModal gets "Apply pipeline ▾" + "Save as pipeline". Apply
  fills all four slot states; the filament list right-pads from
  current state so a pipeline with fewer entries than the current
  source's slot count keeps the existing tail.
2026-06-27 13:56:18 +02:00
maziggy a5fe5cb3d4 feat(sponsor-prompt): in-app toast at earned milestones
ghcr.io pull baseline (~10k/day rising → ~8-12k active installs) puts
  sponsor conversion at 0.08% — roughly an order of magnitude under
  industry-benchmark for OSS with visible CTA. The Settings banner from
  0d4b9d4e gives passive every-visit visibility on one page; this adds
  opt-out-able active visibility at moments where the user has just
  earned something with Bambuddy.

  Five trigger families with a 14-day cross-family cooldown: prints
  (100/500/1000/2500/5000), cost (100/500/1000 tracked filament +
  energy), archives (50/250/1000), anniversary (1 year), version-update
  (re-armable on each major bump). New sponsor_toast_state table with
  nullable user_id so auth-disabled installs get the same trigger logic
  through one code path (NULL-keyed install-default row).
2026-06-20 15:50:58 +02:00
maziggy 090c180ebf feat(heater-history): track nozzle / bed / chamber readings + per-tile chart-icon overlay opens history modal
New PrinterSensorHistory table + 60s recorder + GET/DELETE /printer-sensor-history
  route gated behind a new PRINTER_SENSOR_HISTORY_READ scope (separate from AMS). UI
  adds a 10x10 LineChart icon on each heater tile - click body opens the existing
  target-temp popover unchanged, click icon opens a HeaterHistoryModal mirroring the
  AMSHistoryModal shape (kind toggle + 6h/24h/48h/7d range + current/avg/min/max +
  recharts line for value + dashed target). Read-only X1C/P2S chamber tile finally
  gets an interaction. Retention configurable via printer_sensor_history_retention_days
  (default 30, sibling of ams_history_retention_days). 8 new i18n keys translated in
  all 11 locales, parity green. 4 backend + 6 frontend tests added; full pytest -n 30
  6226/6226, vitest 2176/2176, ruff/eslint/build all clean.
2026-06-20 12:55:24 +02:00
Poltavtcev af5d24e289 feat(inventory): structured storage locations catalog (#1505) 2026-06-17 11:33:23 +02:00
maziggy fcda728af4 feat(#1108): long-lived camera-stream tokens + fix(#1089) audit-pass tweaks
#1108 — Long-lived camera-stream tokens for HA / Frigate / kiosks. Camera-only
  V1, hard 365-day cap (no infinite tokens), pbkdf2 hashed at rest, plaintext
  shown to user exactly once on creation. New "Camera API Tokens" panel under
  Settings → API Keys with self-service create/revoke, styled confirm modal,
  admin "All users" view for leak triage. Auth path: /camera/stream tries the
  existing 60-min ephemeral table first, falls through to the long-lived path.
  Indexed lookup_prefix keeps verify O(1) per token.

  Permission audit: gated the existing API-keys-CRUD + Webhook docs + API
  Browser content behind api_keys:read so non-admins with camera:view land on
  the API Keys tab and see only the Camera Tokens panel they actually have
  permission to use. Grid layout collapses to single column for non-admins.

  Tests: 29 new backend (15 service + 14 integration covering create/list/
  revoke ownership rules, the auth fall-through, scope enforcement, prefix
  collisions) + 6 new frontend tests for the section UI including the new
  modal flow. All 77 backend tests + 21 frontend camera tests pass. Ruff
  clean (lint + format).

  Docs: README updated with fan-out + long-lived-token bullets. Wiki gets a
  new "Long-Lived Camera Tokens" section under features/camera.md (HA YAML
  example, security model, permission requirements, revoke flow). Website
  features.html gets the bullet under Camera Streaming.

  Also includes #1089 follow-up tweaks already merged in this branch:
  _stream_start_times.setdefault for accurate stream_uptime, subscribe()
  RuntimeError retry to close the grace-vs-subscribe race, atomic
  unsubscribe count via the iter_subscriber on_unsubscribe callback.
2026-04-25 10:44:37 +02:00
Sn0rrii ba1c97c808 feat: Two-Factor Authentication (TOTP, Email OTP) and OIDC/SSO – full implementation with admin UI (#933)
feat: Two-Factor Authentication (TOTP, Email OTP) and OIDC/SSO – full implementation with admin UI (#933)
2026-04-13 13:24:28 +02:00
maziggy 8266d225d2 fix(energy): date-range energy in total mode + restart-resilient per-print tracking (#941)
The Statistics page reported "Gesamt" (All Time) kWh correctly but showed
  zero for Today/Week/Month in total-consumption mode. Two bugs drove it:

  1. The starting plug counter was kept in an in-memory dict
     `_print_energy_start` that was lost on any backend restart mid-print, so
     the per-print `energy_kwh` delta silently never got computed. The stats
     endpoint's fallback path `SUM(PrintArchive.energy_kwh)` therefore summed
     to zero for users running in total mode.
  2. Total-consumption mode has no per-print delta by design — it includes
     idle/preheat/standby — so the fallback to archive rows was the wrong
     strategy even when the data existed.

  Fix, in two parts:

  - Persist `energy_start_kwh` on the archive row and read it back from a
    fresh session at print end. Deletes `_print_energy_start` and its 5
    call sites, replacing them with a single `_record_energy_start()` helper.
    Per-print tracking is now restart-resilient regardless of tracking mode.
  - Add hourly `smart_plug_energy_snapshots` table + `_snapshot_loop()` in
    SmartPlugManager. Rewrote the `/archives/stats` energy branch as
    `_sum_snapshot_deltas()` which computes per-plug
    `max(0, last-in-range - baseline)` where baseline is the latest snapshot
    at or before the range start, falling back to the earliest-ever snapshot
    and signalling `energy_data_warming_up` when no pre-range baseline
    exists (fresh upgrade). MQTT plugs are skipped from snapshots since they
    only report "today" and have no lifetime counter.

  Frontend: QuickStatsWidget renders an AlertTriangle next to Energy Used /
  Energy Cost with a tooltip when `energy_data_warming_up` is true, so the
  "low values right after upgrading" situation is explained in-product.
  Fully localised across 7 UI languages.

  Tests: new backend unit tests cover the snapshot delta arithmetic
  (baseline/endpoint, counter reset clamp, multi-plug, warming-up fallback,
  endpoint windowing), per-print restart resilience via expunge_all, and the
  snapshot task lifecycle (start idempotent, stop cancels). Frontend tests
  assert the warning icon appears only when the flag is set and only on the
  energy tiles.

  Docs: updated `CHANGELOG.md`, `README.md`, wiki `features/energy.md`,
  wiki `features/statistics.md`, and website `features.html` with the new
  behaviour and warming-up explanation.
2026-04-11 11:14:54 +02:00
maziggy 3270179090 Add batch print quantity to print/schedule dialog (#342) 2026-04-01 11:55:30 +02:00
maziggy 0b2b81a34d [Fix] Virtual Printer proxy mode fails on isolated networks (#757)
When the slicer and printer are on different VLANs, Bambu Studio could
  not send prints through the proxy because the printer's real IP leaked
  through MQTT payloads, the bind protocol forwarded the real printer's
  identity, the port 6000 file transfer tunnel was not proxied, and FTP
  data connections raced the TLS handshake on zero-byte uploads.

  - Rewrite IP addresses in MQTT PUBLISH payloads (string + integer)
    with proper packet framing and cross-chunk buffering
  - Respond to bind/detect with VP identity via BindServer
  - Add TLS proxy for port 6000 (file transfer tunnel)
  - Buffer slicer FTP data during printer connection setup
  - Advertise configured VP name in SSDP proxy
  - Add cross-subnet SSDP wildcard listener for VPN setups
  - Register UserEmailPreference model in models/__init__.py
  - Add 11 unit tests for MQTT rewrite, IP conversion, SSDP name
2026-03-19 11:03:10 +01:00
Thomas RambachandMartinNYHC 7ba67012f6 Feature AMS Info Card (#570)
* AMS Labels addition to PrintersPage

* Added database reinitialization for schema migrations on database restore

* Bug fixes and AMS Label persistence updates

* Update database.py to resolve PR conflicts

* PR Comment Resolution

* Resolve conflicts in database.py for PR#570

* Updates to address PR#570 additional comments

* Optimize visibility handling for popup component

* Improve serial key handling in printers.py

Refactor serial key assignment to handle empty AMS serial gracefully.

* Implement error handling in serial number mapping

Add error handling for serial number mapping.

* Add migration to drop old ams_labels table

---------

Co-authored-by: MartinNYHC <mz@v8w.de>
2026-03-05 08:36:39 +01:00
maziggy aeca15b585 Add SpoolBuddy integration as optional filament management add-on
SpoolBuddy turns a Raspberry Pi 4B with a PN5180 NFC reader and
NAU7802 scale into a filament management station that integrates
with Bambuddy via REST API and WebSocket.

Backend: SpoolBuddyDevice model, 10 REST endpoints (/spoolbuddy/*),
6 WebSocket broadcast types, background offline-detection watchdog.

RPi daemon: asyncio service with concurrent NFC polling (300ms,
MIFARE Classic + Bambu HKDF key derivation), scale reading (10 SPS,
5-sample moving average, stability detection), and 10s heartbeat
with exponential backoff reconnect.

Frontend: kiosk-optimized 1024x600 UI at /spoolbuddy with Dashboard
(live weight + NFC spool detection), AMS Overview, Inventory,
Printers, and Settings pages. useSpoolBuddyState reducer hook
driven by WebSocket CustomEvents.

i18n: all 6 locales (en, de, fr, it, ja, pt-BR).
2026-02-22 15:27:54 +01:00
maziggy ec82092bc7 Sync 2026-02-12 07:07:50 +01:00
maziggy edf244c469 Add local profiles — import OrcaSlicer presets without Bambu Cloud (#310)
Users who use OrcaSlicer without Bambu Cloud can now import slicer
presets directly into Bambuddy. Supports .orca_filament, .bbscfg,
.bbsflmt, .zip, and .json exports with automatic inheritance resolution
via OrcaSlicer's GitHub base profiles (cached with 7-day TTL).
2026-02-09 17:00:02 +01:00
maziggy 89229a5ecc Add group-based permissions system with granular access control
Implement a full permissions system replacing simple admin/user roles:

Backend:
- Add Group model with many-to-many user relationship
- Add 50+ granular permissions (resource:action pattern)
- Create default groups: Administrators, Operators, Viewers
- Add permission-checking dependencies for route protection
- Add groups API endpoints (CRUD, user assignment)
- Add change password endpoint for users
- Update backup/restore to include groups
- Migrate existing users to groups on startup

Frontend:
- Add GroupsPage for managing groups and permissions
- Add permission helpers to AuthContext (hasPermission, hasAnyPermission)
- Add PermissionRoute component for protected routes
- Disable buttons/features based on permissions (with tooltips)
- Add change password modal in sidebar for all users
- Add forgot password info modal on login page
- Show user groups in UsersPage with group assignment

Testing:
- Add integration tests for groups API
- Add tests for user-group assignments
- Add tests for change password endpoint
- Seed default groups in test fixtures

Closes #28 #161
2026-01-31 12:50:15 +01:00
maziggy 4ea7f7c4aa Add GitHub profile backup feature
New feature to automatically backup K-profiles, cloud profiles, and app
settings to a GitHub repository with scheduled or on-demand execution.

Features:
- Configure GitHub repo URL and Personal Access Token
- Schedule backups hourly, daily, or weekly (background scheduler)
- Manual backup trigger with real-time progress tracking
- Skip unchanged commits (only creates commit when data changes)
- Backup history log with status and commit links
- Requires Bambu Cloud login for full profile access
- New Settings → Backup & Restore tab consolidating all backup options
- GitHub backup config included in local backup/restore (except PAT)

Backend:
- New models: GitHubBackupConfig, GitHubBackupLog
- New service: GitHubBackupService with scheduler and GitHub API client
- New routes: /github-backup/* for config, status, logs, and triggers
- Updated settings.py to include github_backup in backup/restore

Frontend:
- New GitHubBackupSettings.tsx component with auto-save
- Updated SettingsPage with Backup tab and status indicator
- Added API types and methods to client.ts

Tests:
- Backend integration tests for all GitHub backup API endpoints
- Frontend API type and endpoint tests
2026-01-27 15:01:06 +01:00
JesseFPV f8857ba666 Added optional authentication and user management 2026-01-21 14:10:06 +01:00
maziggy d48abe0f6a Add File Manager and enhanced Queue features
File Manager:
- Add full File Manager page for browsing local library files
- Implement folder hierarchy with create/rename/delete operations
- Add file uploads with drag-and-drop support
- Enable adding sliced files directly to print queue
- Add linked folders with external path mounting

Queue Enhancements:
- Add plate selection for multi-plate 3MF files
- Add print options (bed levelling, flow calibration, vibration
  calibration, layer inspection, timelapse, use AMS)
- Fix print_count increment to only occur on actual prints

Testing:
- Add backend integration tests for library API (15 tests)
- Add backend integration tests for queue print options (8 tests)
- Add frontend tests for EditQueueItemModal (10 tests)
- Suppress console noise in frontend test suite

Closes #94
2026-01-19 13:29:37 +01:00
maziggy 88e84ca45a - Add Virtual Printer feature - emulate Bambu printer on network
- Virtual printer appears in Bambu Studio/Orca Slicer via SSDP discovery
  - Secure TLS/MQTT communication with auto-generated certificates
  - Queue mode (pending uploads) or auto-start mode
  - Configurable access code for authentication
  - Docker support with network_mode: host and certificate persistence
  - Fix backup/restore for virtual printer settings (auto-save no longer overwrites)
2025-12-29 15:52:48 +01:00
maziggy ea6d84031a Removed PWA P´push notification 2025-12-11 11:37:13 +01:00
maziggy 2f1ac92598 Restored some files that were deleted by mistake 2025-12-11 11:15:25 +01:00
maziggy 0dc76746b4 Add AMS humidity/temperature history tracking with charts
- Add database model for storing AMS sensor history (humidity, temperature)
  - Record sensor data every 5 minutes via background task
  - Add API endpoint to retrieve history with min/max/avg statistics
  - Create AMSHistoryModal component with Recharts line charts
  - Make humidity/temperature indicators clickable to open history
  - Support time range selection (6h, 24h, 48h, 7d)
  - Show threshold reference lines on charts (Good/Fair levels)
  - Add configurable data retention (default 30 days) in Settings
  - Auto-cleanup old data daily
  - Fix dark theme styling in modal using inline styles
  - Add Escape key to close modal
2025-12-10 19:45:38 +01:00
maziggy d1518083fc ## New Features
### Projects / Print Grouping
  - Create projects to group related prints (e.g., "Voron Build" with 50 parts)
  - Track progress with target count and completion percentage
  - Assign archives to projects via edit modal or context menu
  - Project cards show archive thumbnails with clickable links
  - Color-coded project badges on archive cards
  - Filter and manage projects by status (active/completed/archived)

  ### Full-Text Search (FTS5)
  - SQLite FTS5 virtual table for efficient searching
  - Search across print_name, filename, tags, notes, designer, filament_type
  - Automatic index sync with triggers for INSERT/UPDATE/DELETE

  ### Webhooks & API Keys
  - API key authentication with granular permissions
  - Permissions: can_read_status, can_manage_queue, can_control_printer
  - Secure key generation with prefix display only after creation
  - Settings page API Keys tab for key management
  - Webhook endpoints for external integrations

  ### Failure Analysis
  - Dashboard widget showing failure rate with color coding
  - Correlate failures with conditions (filament type, printer, time)
  - Top failure reasons breakdown
  - Weekly trend visualization

  ### Archive Comparison
  - Select 2-5 archives to compare side-by-side
  - Highlight differences in print settings (yellow)
  - Success/failure correlation insights
  - Modal with close via button, X, Escape, or backdrop

  ### CSV/Excel Export
  - Export archives and statistics with current filters
  - Support for both CSV and Excel (.xlsx) formats
  - openpyxl dependency added

  ## Bug Fixes
  - Fixed context menu submenu not showing (removed overflow-hidden)
  - Fixed project card thumbnails using correct API endpoint
  - Fixed EditArchiveModal to invalidate projects query on save
  - Fixed clipboard API fallback for HTTP contexts
  - Fixed archive PATCH 500 error (FTS5 index rebuild)
  - Fixed FastAPI trailing slash routing for projects endpoint

  ## UI Improvements
  - Context menu submenu with hover/click support
  - Project badge on archive cards with project color
  - "Go to Project" context menu item for assigned archives
  - Clickable project card thumbnails linking to archives
  - Reset Layout button moved to Stats page header
2025-12-10 17:06:43 +00:00
maziggy c74f24353a - Added smart plug monitoring and scheduling
- Added daily digest to notification module
2025-12-08 16:30:53 +00:00
maziggy 29aadfb774 Added template system to notification module. 2025-12-08 15:23:17 +00:00
maziggy e47ef36630 sync 2025-12-08 09:56:21 +00:00
Martin Ziegler f126b0a075 Added auto app update; Added maintenance module with notifications 2025-12-01 08:39:07 +01:00
Martin Ziegler 5c27da41c5 Added support for Tasmota based smart power plugs and automation 2025-11-28 13:17:22 +01:00
Martin Ziegler 09677861ba Added screenshots 2025-11-28 10:23:59 +01:00