Merge remote-tracking branch 'origin/main' into feat/electrical

This commit is contained in:
Pouzor
2026-06-02 16:10:46 +02:00
13 changed files with 553 additions and 101 deletions
+35 -2
View File
@@ -20,6 +20,34 @@ from app.services.zigbee_service import build_zigbee_properties
_ZIGBEE_TYPES = {"zigbee_coordinator", "zigbee_router", "zigbee_enddevice"}
def build_mac_property(mac: str | None) -> list[dict[str, Any]]:
"""Build a NodeProperty list carrying a device MAC address.
Shape matches the frontend ``NodeProperty`` type
(``{key, value, icon, visible}``). Hidden by default — the user opts in to
showing it on the canvas card from the right panel. Returns an empty list
when no MAC is known.
"""
if not mac:
return []
return [{"key": "MAC", "value": mac, "icon": None, "visible": False}]
def merge_mac_property(
props: list[dict[str, Any]] | None, mac: str | None
) -> list[dict[str, Any]]:
"""Append a MAC NodeProperty to ``props`` unless one is already present.
Preserves any user-supplied properties (and an existing MAC row's
visibility) untouched. Used on approve so the scanned MAC is not lost.
"""
out = [dict(p) for p in (props or [])]
if not mac or any(p.get("key") == "MAC" for p in out):
return out
out.append({"key": "MAC", "value": mac, "icon": None, "visible": False})
return out
class BulkActionRequest(BaseModel):
device_ids: list[str]
@@ -138,13 +166,14 @@ async def bulk_approve_devices(
label=device.hostname or device.friendly_name or device.ip or "device",
type=node_type,
ip=device.ip,
mac=device.mac,
hostname=device.hostname,
status="online" if is_zigbee else "unknown",
services=device.services or [],
ieee_address=device.ieee_address,
properties=build_zigbee_properties(
device.ieee_address, device.vendor, device.model, device.lqi
) if is_zigbee else [],
) if is_zigbee else build_mac_property(device.mac),
# Default to ping so the status checker actually polls the new node.
# Without this the scheduler skips it (check_method NULL → no check).
check_method="none" if is_zigbee else ("ping" if device.ip else None),
@@ -245,17 +274,21 @@ async def approve_device(
raise HTTPException(status_code=409, detail="Device already processed")
device.status = "approved"
_is_zigbee = node_data.type in _ZIGBEE_TYPES
# Prefer the MAC discovered during the scan (stored on the pending device);
# fall back to whatever the approve payload carried.
_mac = device.mac or node_data.mac
node = Node(
label=node_data.label,
type=node_data.type,
ip=node_data.ip,
mac=_mac,
hostname=node_data.hostname,
status="online" if _is_zigbee else node_data.status,
services=node_data.services or [],
ieee_address=device.ieee_address,
properties=build_zigbee_properties(
device.ieee_address, device.vendor, device.model, device.lqi
) if _is_zigbee else (node_data.properties or []),
) if _is_zigbee else merge_mac_property(node_data.properties, _mac),
check_method="none" if _is_zigbee else (node_data.check_method or ("ping" if node_data.ip else None)),
check_target=None if _is_zigbee else node_data.check_target,
design_id=node_design_id,