srveit/insteon-hub2

View on GitHub
tools/decodeDevicesXml.js

Summary

Maintainability
A
0 mins
Test Coverage
'use strict'
const { parseStringPromise } = require('xml2js')
const unzipper = require('unzipper')
const URL = 'https://www.insteon.com/s/houselinc-device-definitions-2987-bjb7.zip'

const getDevicesXml = async () => {
  const fetch = (await import('node-fetch')).default
  try {
    const response = await fetch(URL)
    const directory = await unzipper.Open.buffer(await response.buffer())
    const file = directory.files[0]
    const content = await file.buffer()

    return content
  } catch (error) {
    console.warn(error)
    return undefined
  }
}

function extractNode (node, alwaysArray) {
  let base = {}
  if (typeof node === 'string') {
    return node
  }

  if (Array.isArray(node)) {
    if (node.length === 1 && !alwaysArray) {
      return extractNode(node[0])
    }
    return node.map(extractNode)
  }

  for (const key in node) {
    if (key === '$') {
      base = Object.assign(base, node[key])
    } else {
      base[key] = extractNode(node[key], ['device'].includes(key))
    }
  }
  return base
}

const examplesOfDevices = {
  '00': 'ControLinc, RemoteLinc, SignaLinc, etc.',
  '01': 'Dimmable Light Switches, Dimmable Plug-In Modules',
  '02': 'Relay Switches, Relay Plug-In Modules',
  '03': 'PowerLinc Controllers, TRex, Lonworks, ZigBee, etc.',
  '04': 'Irrigation Management, Sprinkler Controllers',
  '05': 'Heating, Air conditioning, Exhausts Fans, Ceiling Fans, Indoor Air Quality',
  '06': 'Pumps, Heaters, Chemicals',
  '07': 'Sensors, Contact Closures',
  '08': 'Audio/Video Equipment',
  '09': 'Electricity, Water, Gas Consumption, Leak Monitors',
  '0A': 'White Goods, Brown Goods',
  '0B': 'Faucets, Showers, Toilets',
  '0C': 'Telephone System Controls, Intercoms',
  '0D': 'PC On/Off, UPS Control, App Activation, Remote Mouse, Keyboards',
  '0E': 'Drapes, Blinds, Awnings',
  '0F': 'Automatic Doors, Gates, Windows, Locks',
  10: 'Door and Window Sensors, Motion Sensors, Scales',
  11: 'Video Camera Control, Time-lapse Recorders, Security System Links',
  12: 'Remote Starters, Car Alarms, Car Door Locks',
  13: 'Pet Feeders, Trackers',
  14: 'Model Trains, Robots',
  15: 'Clocks, Alarms, Timers',
  16: 'Christmas Lights, Displays',
  FF: 'For devices that will be assigned a DevCat and SubCat by software',
}

// TODO: add missing devices
function devicesStruct (categories) {
  return categories.reduce(
    (devices, category) => {
      const categoryId = category.devCat.slice(2)
      const categoryName = category.description
      const subcategories = category.device
        ? category.device.reduce(
          (devices, xmlDevice) => {
            const subcategoryId = xmlDevice.subcategory.slice(-2)
            const device = devices[subcategoryId] || {}
            const [deviceDescription, modelNumber] =
              xmlDevice.description.split(/ *\[/)
            device.categoryId = categoryId
            device.subcategoryId = subcategoryId
            device.productKey = xmlDevice.productKey.slice(-6)
            device.deviceDescription = deviceDescription
            if (modelNumber) {
              device.modelNumber = modelNumber.replace(']', '')
            }
            devices[subcategoryId] = device
            return devices
          },
          {}
        )
        : {}
      devices[categoryId] = {
        categoryId,
        categoryName,
        examplesOfDevices: examplesOfDevices[categoryId],
        subcategories,
      }
      return devices
    },
    {}
  )
}

async function decodeDevicesXml () {
  const contents = await getDevicesXml()
  const xml = await parseStringPromise(contents.toString())
  const availableDevices = xml.deviceFactory.availableDevices

  return devicesStruct(extractNode(availableDevices[0].category))
}

module.exports = decodeDevicesXml