feat: add Z-Wave node types to add/edit node modal
Z-Wave (controller/router/end device) types existed in the model, theme accents, default icons and the custom-style editor, but were missing from the node Add/Edit type selector, so they could not be placed manually like Zigbee nodes. Add a Z-Wave group and default mesh-radio nodes to no status check (not IP-reachable), matching Zigbee behaviour.
This commit is contained in:
@@ -18,6 +18,7 @@ const NODE_TYPE_GROUPS: { label: string; types: NodeType[] }[] = [
|
||||
{ label: 'Virtualization', types: ['proxmox', 'vm', 'lxc', 'docker_host', 'docker_container'] },
|
||||
{ label: 'IoT', types: ['iot', 'camera', 'cpl'] },
|
||||
{ label: 'Zigbee', types: ['zigbee_coordinator', 'zigbee_router', 'zigbee_enddevice'] },
|
||||
{ label: 'Z-Wave', types: ['zwave_coordinator', 'zwave_router', 'zwave_enddevice'] },
|
||||
{ label: 'Personal', types: ['computer', 'laptop', 'mobile'] },
|
||||
{ label: 'Electrical', types: ['grid', 'ups', 'battery', 'generator', 'solar_panel', 'inverter', 'circuit_breaker', 'contactor', 'electrical_switch', 'socket', 'light', 'meter', 'transformer', 'load'] },
|
||||
{ label: 'Generic', types: ['generic', 'groupRect'] },
|
||||
@@ -26,6 +27,9 @@ const NODE_TYPE_GROUPS: { label: string; types: NodeType[] }[] = [
|
||||
const CHECK_METHODS: CheckMethod[] = ['none', 'ping', 'http', 'https', 'tcp', 'ssh', 'prometheus', 'health']
|
||||
const CONTAINER_MODE_TYPES: NodeType[] = ['proxmox', 'vm', 'lxc', 'docker_host']
|
||||
const ZIGBEE_TYPES: NodeType[] = ['zigbee_coordinator', 'zigbee_router', 'zigbee_enddevice']
|
||||
const ZWAVE_TYPES: NodeType[] = ['zwave_coordinator', 'zwave_router', 'zwave_enddevice']
|
||||
// Mesh radio devices aren't IP-reachable, so they default to no status check.
|
||||
const MESH_TYPES: NodeType[] = [...ZIGBEE_TYPES, ...ZWAVE_TYPES]
|
||||
|
||||
const CHECK_METHOD_LABELS: Record<CheckMethod, string> = {
|
||||
none: 'None',
|
||||
@@ -73,7 +77,7 @@ interface NodeModalProps {
|
||||
// initial value is enough - no need for a reset effect.
|
||||
export function NodeModal({ open, onClose, onSubmit, initial, title = 'Add Node', parentCandidates = [], currentNodeId }: NodeModalProps) {
|
||||
const merged = { ...DEFAULT_DATA, ...initial }
|
||||
if (ZIGBEE_TYPES.includes((merged.type ?? '') as NodeType)) merged.check_method = 'none'
|
||||
if (MESH_TYPES.includes((merged.type ?? '') as NodeType)) merged.check_method = 'none'
|
||||
const [form, setForm] = useState<Partial<NodeData>>(merged)
|
||||
const [iconSearch, setIconSearch] = useState('')
|
||||
const [iconPickerOpen, setIconPickerOpen] = useState(false)
|
||||
@@ -133,7 +137,7 @@ export function NodeModal({ open, onClose, onSubmit, initial, title = 'Add Node'
|
||||
const t = v as NodeType
|
||||
setForm((f) => {
|
||||
const next: Partial<NodeData> = { ...f, type: t }
|
||||
if (ZIGBEE_TYPES.includes(t)) next.check_method = 'none' as CheckMethod
|
||||
if (MESH_TYPES.includes(t)) next.check_method = 'none' as CheckMethod
|
||||
// Drop the parent only if it's no longer a valid target for the
|
||||
// new type — keep container-mode parents (any node can nest).
|
||||
const parent = parentCandidates.find((n) => n.id === f.parent_id)
|
||||
|
||||
@@ -188,6 +188,20 @@ describe('NodeModal', () => {
|
||||
expect((onSubmit.mock.calls[0][0] as Partial<NodeData>).type).toBe('nas')
|
||||
})
|
||||
|
||||
it('offers Z-Wave node types and submits one', () => {
|
||||
const { onSubmit } = renderModal({ initial: BASE })
|
||||
fireEvent.change(selects()[0], { target: { value: 'zwave_enddevice' } })
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Add' }))
|
||||
expect((onSubmit.mock.calls[0][0] as Partial<NodeData>).type).toBe('zwave_enddevice')
|
||||
})
|
||||
|
||||
it('forces check_method to none when a Z-Wave type is selected', () => {
|
||||
const { onSubmit } = renderModal({ initial: { ...BASE, check_method: 'ping' } })
|
||||
fireEvent.change(selects()[0], { target: { value: 'zwave_coordinator' } })
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Add' }))
|
||||
expect((onSubmit.mock.calls[0][0] as Partial<NodeData>).check_method).toBe('none')
|
||||
})
|
||||
|
||||
// ── Check method ──────────────────────────────────────────────────────
|
||||
|
||||
it('pre-fills check_method from initial', () => {
|
||||
|
||||
Reference in New Issue
Block a user