'use strict' const { deepGetValue } = require('./helpers') const findWanIp = d => { const pub = extractAllInterfaces(d).find(i => i.role === 'internet') return pub ? pub.ip : '' } const extractAllInterfaces = d => { const ipIfaces = d.Device?.IP?.Interface if (!ipIfaces) return [] const results = [] for (const ifKey of Object.keys(ipIfaces)) { if (ifKey.startsWith('_')) continue const iface = ipIfaces[ifKey] if (!iface?.IPv4Address) continue const name = iface.Name?._value || '' for (const addrKey of Object.keys(iface.IPv4Address)) { if (addrKey.startsWith('_')) continue const addr = iface.IPv4Address[addrKey] const ip = addr?.IPAddress?._value if (!ip || ip === '0.0.0.0') continue const status = addr.Status?._value if (status && status !== 'Enabled') continue const role = (ip.startsWith('192.168.') || (ip.startsWith('10.') && name === 'br0')) ? 'lan' : (!ip.startsWith('10.') && !ip.startsWith('172.') && !ip.startsWith('192.168.') && !ip.startsWith('169.254.')) ? 'internet' : (ip.startsWith('172.17.') || ip.startsWith('172.16.') || name.includes('_10')) ? 'management' : 'service' results.push({ iface: ifKey, name, ip, mask: addr.SubnetMask?._value || '', addrType: addr.AddressingType?._value || '', role }) } } return results } const countAllWifiClients = d => { const aps = d.Device?.WiFi?.AccessPoint if (!aps) return { direct: 0, mesh: 0, total: 0, perAp: [] } let direct = 0, mesh = 0 const perAp = [] for (const k of Object.keys(aps)) { if (k.startsWith('_') || !aps[k]) continue const n = Number(aps[k].AssociatedDeviceNumberOfEntries?._value) || 0 const ssidRef = aps[k].SSIDReference?._value || '' const ssidIdx = ssidRef.match(/SSID\.(\d+)/) const idx = ssidIdx ? parseInt(ssidIdx[1]) : parseInt(k) if (idx <= 8) direct += n; else mesh += n if (n > 0) perAp.push({ ap: k, ssid: ssidRef, clients: n }) } return { direct, mesh, total: direct + mesh, perAp } } const extractMeshTopology = d => { const apDevices = d.Device?.WiFi?.MultiAP?.APDevice if (!apDevices) return null const nodes = [] let totalClients = 0 for (const dk of Object.keys(apDevices)) { if (dk.startsWith('_')) continue const dev = apDevices[dk] if (!dev?._object) continue const name = dev.X_TP_HostName?._value || '' const mac = dev.MACAddress?._value || '' let nodeClients = 0 for (const rk of Object.keys(dev.Radio || {})) { if (rk.startsWith('_')) continue for (const ak of Object.keys(dev.Radio[rk]?.AP || {})) { if (ak.startsWith('_')) continue nodeClients += Number(dev.Radio[rk].AP[ak]?.AssociatedDeviceNumberOfEntries?._value) || 0 } } totalClients += nodeClients if (name || mac) nodes.push({ id: dk, name, active: dev.X_TP_Active?._value === true, mac, ip: dev.X_TP_IPAddress?._value || '', clients: nodeClients }) } return nodes.length ? { nodes, totalClients } : null } const summarizeDevice = d => { const g = path => deepGetValue(d, path) const s = path => { const v = g(path); return v != null ? String(v) : '' } const counts = countAllWifiClients(d) const mesh = extractMeshTopology(d) return { _id: d._id, serial: s('DeviceID.SerialNumber') || s('InternetGatewayDevice.DeviceInfo.SerialNumber') || s('Device.DeviceInfo.SerialNumber') || d.DeviceID?.SerialNumber?._value || (d._id ? decodeURIComponent(d._id.split('-').slice(2).join('-')) : '') || '', manufacturer: s('DeviceID.Manufacturer') || d.DeviceID?.Manufacturer?._value || '', model: s('DeviceID.ProductClass') || d.DeviceID?.ProductClass?._value || '', oui: s('DeviceID.OUI') || d.DeviceID?.OUI?._value || '', firmware: s('InternetGatewayDevice.DeviceInfo.SoftwareVersion') || s('Device.DeviceInfo.SoftwareVersion') || '', uptime: g('InternetGatewayDevice.DeviceInfo.UpTime') || g('Device.DeviceInfo.UpTime') || null, lastInform: d._lastInform || null, lastBoot: d._lastBootstrap || null, registered: d._registered || null, interfaces: extractAllInterfaces(d), ip: s('InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANIPConnection.1.ExternalIPAddress') || findWanIp(d) || '', rxPower: g('InternetGatewayDevice.WANDevice.1.X_GponInterafceConfig.RXPower') || g('InternetGatewayDevice.WANDevice.1.WANCommonInterfaceConfig.RXPower') || g('Device.Optical.Interface.1.Stats.SignalRxPower') || g('Device.Optical.Interface.1.OpticalSignalLevel') || null, txPower: g('InternetGatewayDevice.WANDevice.1.X_GponInterafceConfig.TXPower') || g('InternetGatewayDevice.WANDevice.1.WANCommonInterfaceConfig.TXPower') || g('Device.Optical.Interface.1.Stats.SignalTxPower') || g('Device.Optical.Interface.1.TransmitOpticalLevel') || null, pppoeUser: s('InternetGatewayDevice.WANDevice.1.WANConnectionDevice.1.WANPPPConnection.1.Username') || s('Device.PPP.Interface.1.Username') || '', ssid: s('InternetGatewayDevice.LANDevice.1.WLANConfiguration.1.SSID') || s('Device.WiFi.SSID.1.SSID') || '', macAddress: s('InternetGatewayDevice.WANDevice.1.WANEthernetInterfaceConfig.MACAddress') || s('Device.Ethernet.Interface.1.MACAddress') || '', tags: d._tags || [], opticalStatus: s('Device.Optical.Interface.1.Status') || null, opticalErrors: { sent: g('Device.Optical.Interface.1.Stats.ErrorsSent') || 0, received: g('Device.Optical.Interface.1.Stats.ErrorsReceived') || 0 }, wifi: { radio1: { status: s('Device.WiFi.Radio.1.Status') || null, channel: g('Device.WiFi.Radio.1.Channel') || null, bandwidth: s('Device.WiFi.Radio.1.CurrentOperatingChannelBandwidth') || null, noise: g('Device.WiFi.Radio.1.Stats.Noise') || null, clients: g('Device.WiFi.AccessPoint.1.AssociatedDeviceNumberOfEntries') || 0 }, radio2: { status: s('Device.WiFi.Radio.2.Status') || null, channel: g('Device.WiFi.Radio.2.Channel') || null, bandwidth: s('Device.WiFi.Radio.2.CurrentOperatingChannelBandwidth') || null, noise: g('Device.WiFi.Radio.2.Stats.Noise') || null, clients: g('Device.WiFi.AccessPoint.2.AssociatedDeviceNumberOfEntries') || 0 }, radio3: { status: s('Device.WiFi.Radio.3.Status') || null, channel: g('Device.WiFi.Radio.3.Channel') || null, clients: g('Device.WiFi.AccessPoint.3.AssociatedDeviceNumberOfEntries') || 0 }, totalClients: mesh ? mesh.totalClients : counts.total, directClients: counts.direct, meshClients: mesh ? (mesh.totalClients - counts.direct) : counts.mesh, }, mesh: mesh ? mesh.nodes : null, hostsCount: g('Device.Hosts.HostNumberOfEntries') || null, ethernet: { port1: { status: s('Device.Ethernet.Interface.1.Status') || null, speed: g('Device.Ethernet.Interface.1.MaxBitRate') || null }, port2: { status: s('Device.Ethernet.Interface.2.Status') || null, speed: g('Device.Ethernet.Interface.2.MaxBitRate') || null }, }, } } module.exports = { summarizeDevice }