Commit Graph

720 Commits

Author SHA1 Message Date
Pouzor be705f0cb9 bump: version 2.1.0 2026-05-16 18:18:46 +02:00
Rémy 1790d24ee7 Merge pull request #154 from Pouzor/feat/docker-container-lxc-parent
feat(node-modal): sanitize parent_id on type change and exclude descendants
v2.1.0
2026-05-16 18:03:31 +02:00
Pouzor 8b97f578c6 feat(node-modal): sanitize parent_id on type change and exclude descendants
- App.tsx: filter descendants of edited node from parentCandidates to
  prevent picking a child as parent (would create a cycle).
- NodeModal.tsx: clear parent_id when switching to a type with no valid
  parents, and revalidate parent_id on submit against the type's allowlist.
- Tests: cover getValidParentTypes for lxc/vm/docker_container and types
  that disallow parents.

ha-relevant: yes
2026-05-16 17:41:58 +02:00
Rémy 48e7b3c690 Merge pull request #153 from Pouzor/feat/docker-container-lxc-parent
feat(docker): allow LXC as parent for docker_container
2026-05-16 17:11:50 +02:00
Pouzor 3a4df578ec feat(node-modal): restore Parent Container selector for child types
Adds back the Parent Container selector in NodeModal, shown when:
- child type is lxc or vm (parents: proxmox/vm/lxc/docker_host)
- child type is docker_container (parents: docker_host/lxc/vm/proxmox)

Selector hidden when no valid candidate node exists on the canvas.
Centralizes the parent-type allowlist in getValidParentTypes
(shared with the virtual-edge auto-parent helper).

ha-relevant: yes
2026-05-16 17:08:06 +02:00
Pouzor 43426e96ac feat(docker): allow vm and proxmox as parent for docker_container
Extend docker_container parent rule to accept vm and proxmox nodes
in addition to docker_host and lxc, covering nested virtualization
topologies (Docker on a VM, Docker directly on Proxmox host).

ha-relevant: yes
2026-05-16 16:47:14 +02:00
Pouzor 280d5a2ae1 feat(docker): allow LXC as parent for docker_container
Previously docker_container could only attach to a docker_host via
virtual edge. Extend the rule so an LXC node can also act as parent,
enabling docker_container nesting under LXC without requiring an
intermediate docker_host.

Extracts the virtual-edge parent rule into a pure helper
(resolveVirtualEdgeParent) with unit tests covering all type pairs.

ha-relevant: yes
2026-05-15 22:54:51 +02:00
Rémy cdc6091bd0 Merge pull request #152 from Pouzor/feat/group-edge-handles
feat(group): add 4 side handles for edge connections
2026-05-15 21:19:31 +02:00
Pouzor 14597ab86f feat(group): add 4 side handles for edge connections
Group nodes can now be source/target of edges via snap points on
each side (top/right/bottom/left). Each side exposes a source handle
plus an invisible target overlay, matching the pattern used by
BaseNode.
2026-05-15 21:12:10 +02:00
Rémy 14719a5c39 Merge pull request #136 from findthelorax/feat/multiple-ips
feat: multiple ips in detail panel
2026-05-15 14:40:47 +02:00
Pouzor ea451885af refactor(ip-parsing): centralize multi-delim splitting in splitIps
Extend splitIps to accept comma/semicolon/newline delimiters with dedupe,
and reuse it in DetailPanel for both the IP list and the service-URL host.
Fixes ServiceBadge links pointing at concatenated string when IPs were
entered with ';' or newline separators. Adds tests for new delimiters.
2026-05-15 13:36:15 +02:00
findthelorax 9cd93ef294 updated tests for multiple ips 2026-05-15 11:43:09 +02:00
findthelorax 8d937b2a0d removed comma and allow multiple in a row 2026-05-15 11:31:25 +02:00
findthelorax 60ea0fa11b made multiple ips all clickable separately 2026-05-15 11:31:25 +02:00
Rémy ff0d33128c Merge pull request #150 from Pouzor/feat/edge-endpoint-reconnect
feat(canvas): edge endpoint reconnect + proxmox container snap points
2026-05-15 11:10:47 +02:00
Pouzor 17d4ddd070 feat(canvas): edge endpoint reconnect + proxmox container snap points
- Selected edge shows interactive dots at both endpoints; drag onto any
  handle (different snap point or different node) to reconnect. Edge
  follows the cursor live during drag; drop on empty space leaves edge
  unchanged.
- Endpoint dots render in EdgeLabelRenderer above the node layer and walk
  the elementsFromPoint stack so the underlying handle wins over the
  dragged dot itself.
- ProxmoxGroupNode in container mode now renders bottom_handles snap
  points (was always single 'bottom' handle), matching BaseNode.
- New canvasStore.reconnectEdge action: swaps source/target/handles via
  normalizeHandle, snapshots history, marks dirty.
2026-05-15 03:05:38 +02:00
Rémy 4795dc40b8 Merge pull request #149 from Pouzor/feat/homepage-widget-stats
feat(api): stats endpoint for gethomepage widget
2026-05-15 01:16:48 +02:00
Pouzor 994ed9d77a feat(api): stats endpoint for gethomepage widget (closes #131)
Adds GET /api/v1/stats/summary, gated by HOMEPAGE_API_KEY env var and
X-API-Key header (hmac.compare_digest, matches the liveview pattern).

Payload:
  nodes / online / offline / unknown
  pending_devices (status='pending')
  zigbee_devices  (Node.ieee_address IS NOT NULL)
  last_scan_at    (max ScanRun.finished_at)

Disabled by default — endpoint returns 403 unless HOMEPAGE_API_KEY is set.
README documents activation and ships a ready-to-paste gethomepage
`customapi` widget snippet.

Tests cover: disabled-by-default, missing header, wrong key, empty DB,
and full aggregation across nodes/pending/zigbee/scan-runs.
2026-05-14 18:11:43 +02:00
Rémy 4d4befa916 Merge pull request #106 from findthelorax/fix/zone-styling
fix: zone styling
2026-05-14 15:58:45 +02:00
Pouzor 8f741691d0 fix(zone-modal): center slider thumb on track, drop dead code, cover a11y
- Center webkit slider thumb on 4px track (margin-top: -4.5px) so the
  opacity bullet no longer sits below the bar.
- Drop redundant inline accentColor; CSS module owns track color.
- Remove dead ::-ms-* prefix rules.
- Tests: assert aria-labels on grid/select triggers, cover getFontLabel
  happy path + unknown-value fallback via the Font selector DOM.
2026-05-14 15:35:19 +02:00
Pouzor c21fbac599 Merge remote-tracking branch 'origin/main' into fix/zone-styling 2026-05-14 14:03:46 +02:00
Rémy e9c66fceda Merge pull request #148 from Pouzor/feat/zigbee-properties
feat(zigbee): IEEE/Vendor/Model/LQI properties + security + tests
2026-05-14 13:47:12 +02:00
Pouzor 373960f6ea test(frontend): cover api client, ProxmoxGroupNode, CustomStyleModal 2026-05-14 13:25:41 +02:00
Pouzor aa17edf1d0 chore(security): drop passlib for direct bcrypt + reject CLI-flag check targets
- Replace passlib.CryptContext with bcrypt 4.2.1 directly (passlib is
  unmaintained and only emitted warnings when paired with bcrypt 4+).
  hash_password / verify_password now call bcrypt.checkpw / .hashpw, and
  verify_password is hardened against empty inputs without raising.
  scripts/hash_password.py + tests/conftest.py updated to match.
- status_checker.check_node() now rejects targets starting with '-' before
  any subprocess invocation, defending ping/tcp paths against arg-injection
  if an admin sets a check_target like '-O'.

Tests added:
- test_auth: expired JWT, malformed JWT, wrong-secret JWT, empty password
  vs empty server hash, verify_password edge cases.
- test_scan: _background_scan failure path marks ScanRun failed, leaves
  non-running terminal status alone, success path invokes run_scan.
- test_status_checker: ping/tcp invocations are bypassed when target or
  ip starts with '-'.

Backend coverage 89% → 90%.
2026-05-14 12:11:40 +02:00
Pouzor 5f87c64dcf fix(zigbee): default IEEE/Vendor/Model/LQI props to hidden, preserve user visibility
- New zigbee props (approve + first-time re-import) ship with visible=false
  so the canvas card stays clean. User opts in from the right panel.
- On re-import of an already-approved node, merge instead of overwrite:
  keys already present keep their visible flag (and any user-edited value
  is replaced with the freshly imported one), brand-new keys are appended
  hidden. Non-zigbee custom properties are preserved untouched.
2026-05-14 01:10:45 +02:00
Pouzor 5822a1483a fix(zigbee): inject IEEE/Vendor/Model/LQI props into approved nodes client-side
Backend already writes the properties on approve, but the frontend's local
addNode() call rebuilt NodeData without them — so newly approved zigbee
nodes showed an empty right panel until a full canvas reload. Build the
property list on the client from the PendingDevice fields so the canvas
state matches the DB row immediately. Single + bulk approve flows updated.
2026-05-14 00:45:58 +02:00
Pouzor 3a303a1376 feat(zigbee): populate IEEE/Vendor/Model/LQI properties on approve and re-import
- Approval (single + bulk) of zigbee pending devices now writes IEEE, Vendor,
  Model, LQI into Node.properties so they show in the right panel.
- Zigbee re-import refreshes properties on existing canvas Nodes and skips
  creating a pending row when the device was already approved — keeps
  approved devices out of pending/hidden modals.
- Coordinator Node also receives the same properties on first creation and
  on re-import.
- Remove the Parent Container selector from the add/edit node modal.
2026-05-14 00:17:13 +02:00
Rémy f5e0e68806 Update README.md 2026-05-13 11:49:14 +02:00
Pouzor 8541922386 bump: version 2.0.3 v2.0.3 2026-05-13 11:37:06 +02:00
Rémy 3ed9cb0d4f Merge pull request #146 from Pouzor/fix/zigbee-no-check-method
fix(zigbee): hide check method in modal, force none/online for zigbee nodes
2026-05-13 11:35:10 +02:00
Pouzor fff11a4b6a fix(zigbee): hide check method in modal, force none/online for zigbee nodes
Zigbee nodes have no IP-based check — hide Check Method and Check Target
fields in NodeModal for all three zigbee types. Default and force
check_method='none' so the scheduler marks them always online.
Backend approve and zigbee import routes also set status='online' and
check_method='none' for zigbee node types.
2026-05-13 11:14:35 +02:00
Brett Ferrante 890463373a Merge branch 'Pouzor:main' into fix/zone-styling 2026-05-12 14:40:41 -04:00
Pouzor 0680566081 bump: 2.0.2 v2.0.2 2026-05-11 19:55:21 +02:00
Remy 2f5a90a00e Merge pull request #142 from Pouzor/fix/visual
Node modal polish + Smart Home and Brand icon pickers
2026-05-11 19:53:48 +02:00
Pouzor 928f63df0f fix(icons): narrow ICON_MAP lookup type for strict build
tsc -b (used in npm run build) flagged TS2774 because LucideIcon is a
function and therefore always truthy. Cast the lookup result to
LucideIcon | undefined so the falsy branch becomes meaningful.
2026-05-11 19:44:20 +02:00
Pouzor e84a4e0eb3 merge feat/brand-icons into fix/visual 2026-05-11 19:20:10 +02:00
Pouzor 3b0dbd7a8b feat(icons): add brand icon picker from dashboard-icons
Add a second tab in the node Icon picker to choose from the ~2250 brand
icons hosted by homarr-labs/dashboard-icons (Plex, Sonarr, Home
Assistant, etc.) served via jsDelivr CDN. The Generic tab keeps the
existing lucide picker unchanged.

Storage uses a 'brand:<slug>' prefix on custom_icon, so existing nodes
referencing lucide keys keep working with zero migration. A new
resolveCustomIcon helper returns a discriminated union (lucide | brand)
and a NodeIcon component centralizes rendering for both kinds.

Includes a manifest fetch script (scripts/fetch-dashboard-icons.mjs)
and a checked-in dashboardIcons.json snapshot.
2026-05-11 19:18:01 +02:00
Pouzor 3a9b3b2650 feat(icons): add Smart Home / Sensors icon category
Add 27 new icons covering common IoT/Zigbee endpoints: smart plug,
relay, energy meter, solar, door/window sensor, smart lock, smoke
detector, siren, motion radar, presence, vibration, water leak,
humidity, air quality, HVAC vent, fan, AC, smart light, blinds,
doorbell, speaker, remote, garage, valve, weather station, plus
voice assistant and webhook in the existing Automation category.
2026-05-11 17:08:11 +02:00
Pouzor ff02f3b5db fix(node-modal): cap height at 90vh with scroll
Modal grew taller than viewport when icon picker expanded, hiding
header and footer buttons. Constrain DialogContent to 90vh and add
overflow-y-auto so all controls stay reachable.
2026-05-11 16:54:33 +02:00
Pouzor a66e6aa906 bump: version 2.0.1 v2.0.1 2026-05-11 16:11:51 +02:00
Remy 8e59d9a0d1 Merge pull request #141 from Pouzor/fix/zigbee-node-types-in-modal
fix(node-modal): expose Zigbee device types in selector
2026-05-11 16:09:07 +02:00
Pouzor ee4136b506 fix(node-modal): expose Zigbee device types in selector
Add Zigbee section (coordinator, router, end device) to NodeModal
type group list, matching types used by zigbee2mqtt import.
2026-05-11 15:55:34 +02:00
Pouzor 8e8f01d6ca bump version 2.0.0 v2.0.0 2026-05-11 02:23:48 +02:00
Pouzor aba4f34168 feat(style-modal): expose Zigbee node types for customization
Add zigbee_coordinator, zigbee_router, zigbee_enddevice to the
CustomStyleModal editable list with Radio/Zap/Lightbulb icons so users
can tweak color, opacity, and size like any other node type.
2026-05-11 01:44:24 +02:00
Remy 6524efe126 Merge pull request #139 from Pouzor/feature/node-alignment-guides
feat(canvas): alignment guides + snap while dragging
2026-05-11 01:29:44 +02:00
Pouzor 062a08f116 fix(alignment): only snap top-level dragged nodes
Code review flagged silent corruption when dragging a mixed selection
of a parent + one of its children: the child's ids stayed in
pendingSnap even though nodeBox excluded it from the bbox. On drag
stop we shifted the child's parent-relative position by the same delta
the parent already moves by — double-snapping the child off-screen.

Restrict the pendingSnap id set to nodes that contributed a box
(top-level only). Children follow their parent's move automatically;
no extra shift is needed.
2026-05-11 01:23:37 +02:00
Pouzor 5e9963db4d refactor(alignment): defer snap to drag stop, stabilise callback deps
Code review flagged a drag race + deps issues:

- Race: setNodes during onNodeDrag fights React Flow's internal drag
  handler, which derives positions from the cursor offset captured at
  drag start. Snap delta could be overwritten on the next frame.
- Deps: useCallback closed over guides.length, causing recreation on
  every guide change and pulling stale references into onNodeDrag.

Fix: compute guides live during drag for visual feedback, store the
pending snap delta in a ref, and apply it via setNodes only on
onNodeDragStop. Replace the guides.length dep with a ref mirror updated
via useEffect (not during render).
2026-05-11 01:14:47 +02:00
Pouzor 82203f67d8 fix(alignment): use OnNodeDrag (NodeDragHandler doesn't exist in @xyflow/react)
CI build failed with TS2305: NodeDragHandler is not exported. Local
typecheck missed it because tsc-noEmit is more permissive than tsc -b.

Use the exported OnNodeDrag<NodeType> type and alias it with our
NodeData so the callback params are inferred (no implicit any).
2026-05-11 01:00:36 +02:00
Pouzor 004a8f19c1 feat(canvas): alignment guides + snap while dragging nodes
Draw.io / Figma style: while dragging a node, show dashed cyan guide
lines when its edges (left / center / right / top / middle / bottom)
align with another node's, and snap the position to the matched line
within a configurable threshold.

- utils/alignment.ts: pure snap math, returns delta + guide segments.
  Same-size boxes show all aligned guides simultaneously.
- canvas/AlignmentGuides.tsx: SVG overlay locked to the React Flow
  viewport (panned/zoomed correctly).
- hooks/useAlignmentGuides.ts: wires onNodeDrag/onNodeDragStop, applies
  snap via setNodes, listens for Alt to temporarily disable.
- utils/alignmentSettings.ts: localStorage-backed prefs (enabled,
  threshold 2-16px) with a tiny CustomEvent pub-sub so the SettingsPanel
  and the drag hook stay in sync without a global store.
- Sidebar settings panel: toggle + threshold slider.

Multi-selection drag uses the union bounding box. Children with
parentId are skipped for v1 to avoid mixing absolute and parent-relative
coordinates. Hold Alt to escape snap.
2026-05-11 00:35:50 +02:00
Pouzor 5ab0bdeb7f fix(text-node): persist text in label so it survives reload
text_content is not in the API serializer schema, so text node content
was dropped on save and the node came back empty after reload.

Store text in label instead — already persisted, and TextNode + the
edit modal already fall back to label, so existing data stays
compatible. Clear stale text_content on update.

Regression test added for the text-node save/load roundtrip.
2026-05-11 00:34:38 +02:00