From 5d1b92fba5774e8020768f1a58195b5c52bccbeb Mon Sep 17 00:00:00 2001 From: Juergen Kunz Date: Tue, 5 May 2026 20:05:48 +0000 Subject: [PATCH] Export handwritten integrations consistently --- readme.md | 15 +++--- scripts/generate-homeassistant-ports.mjs | 33 ++++++++++++ ts/integrations/index.ts | 64 ++++++++++++++++++++++++ 3 files changed, 104 insertions(+), 8 deletions(-) diff --git a/readme.md b/readme.md index 5a90e2b..347b536 100644 --- a/readme.md +++ b/readme.md @@ -38,9 +38,9 @@ console.log(candidates); | Core errors | `IntegrationError`, `DiscoveryError`, `AuthenticationError`, `DeviceCommunicationError`. | | Core types | Discovery, config-flow, runtime, service-call, entity, logging, and integration status contracts. | | Protocol namespaces | `mdns`, `ssdp`, `http`, `mqtt`, `bluetooth`, and `usb` descriptor helpers. | -| Registry helpers | `integrations` and `createDefaultIntegrationRegistry()`. | +| Registry helpers | `integrations` and `createDefaultIntegrationRegistry()`, currently registering 65 handwritten integrations before filling gaps with generated descriptors. | | Generated metadata | `generatedHomeAssistantPortIntegrations`, `generatedHomeAssistantPortCount`, and `handwrittenHomeAssistantPortDomains`. | -| Handwritten modules | Hue and Wolf Smartset exports from `ts/integrations/index.ts`. | +| Handwritten modules | All handwritten integration folders are re-exported from `ts/integrations/index.ts`. | ## Integration Lifecycle @@ -71,16 +71,15 @@ The hub uses the same primitives through `discoverIntegrationCandidates()` and ` The package includes generated native TypeScript port skeletons for upstream Home Assistant component domains under `ts/integrations/`. These are not Python wrappers and not a compatibility namespace. They are TypeScript classes that start as `descriptor-only` integrations and get replaced by handwritten clients, mappers, discovery, config-flow, and runtime code as each port matures. -Current generated metadata includes `generatedHomeAssistantPortCount = 1394`. Descriptor-only integrations intentionally throw from `setup()` until a real TypeScript runtime exists. +Current generated metadata includes `generatedHomeAssistantPortCount = 1394` and `handwrittenHomeAssistantPortDomains` for the 64 handwritten domains that replace upstream Home Assistant ports. Descriptor-only integrations intentionally throw from `setup()` until a real TypeScript runtime exists. ## Handwritten Integrations -| Integration | Current capability | -| --- | --- | -| Hue | Bridge discovery, validation, config flow, light/device/entity mapping, and `light.turn_on` / `light.turn_off` service calls. | -| Wolf Smartset | Discovery/config/runtime structure is present, currently marked `descriptor-only`. | +The default `integrations` array registers 65 handwritten integrations, including the 64 Home Assistant replacement domains tracked by `handwrittenHomeAssistantPortDomains` plus the custom Wolf Smartset integration. These handwritten folders are exported as named modules and registered before generated descriptors, so handwritten code wins whenever a generated Home Assistant descriptor has the same domain. -The `integrations` array also registers many handwritten domain folders and then fills gaps from the generated Home Assistant descriptors when `createDefaultIntegrationRegistry()` runs. +Examples include Hue, AdGuard, AirGradient, Amcrest, Android TV, APC UPSD, ASUSWRT, Axis, BleBox, Bosch SHC, Broadlink, deCONZ, Denon AVR, DSMR, ESPHome, Fritz, HomeKit Controller, Homematic, KNX, Kodi, Matter, MQTT, Nanoleaf, ONVIF, Pi-hole, Plex, Roku, Shelly, Sonos, Synology DSM, TP-Link, Tradfri, UniFi, Wolf Smartset, Xiaomi Miio, Yeelight, ZHA, and Z-Wave JS. + +The generator updates the barrel export file during `pnpm generate:ha`, so the named export surface stays aligned with preserved handwritten folders instead of drifting back to a short manual list. ## CLI diff --git a/scripts/generate-homeassistant-ports.mjs b/scripts/generate-homeassistant-ports.mjs index 2e194d2..bc9f621 100644 --- a/scripts/generate-homeassistant-ports.mjs +++ b/scripts/generate-homeassistant-ports.mjs @@ -29,6 +29,15 @@ const isGeneratedFolder = async (folderUrl) => { } }; +const fileExists = async (fileUrl) => { + try { + await stat(fileUrl); + return true; + } catch { + return false; + } +}; + const json = (value) => JSON.stringify(value, null, 2); await mkdir(integrationsRoot, { recursive: true }); @@ -116,4 +125,28 @@ await writeFile( `// Generated by scripts/generate-homeassistant-ports.mjs. Do not edit manually.\n\nimport type { BaseIntegration } from '../../core/classes.baseintegration.js';\n${imports}\n\nexport const generatedHomeAssistantPortIntegrations: BaseIntegration[] = [];\n${constructorPushes}\n\nexport const generatedHomeAssistantPortCount = ${ports.filter((port) => !port.handwritten).length};\nexport const handwrittenHomeAssistantPortDomains = ${json(ports.filter((port) => port.handwritten).map((port) => port.domain))};\n` ); +const handwrittenFolders = []; +for (const entry of await readdir(integrationsRoot, { withFileTypes: true })) { + if (!entry.isDirectory()) continue; + if (entry.name === 'generated') continue; + + const folderUrl = new URL(`./${entry.name}/`, integrationsRoot); + if (await isGeneratedFolder(folderUrl)) continue; + if (!(await fileExists(new URL('index.ts', folderUrl)))) continue; + + handwrittenFolders.push(entry.name); +} + +handwrittenFolders.sort((a, b) => a.localeCompare(b)); + +await writeFile( + new URL('index.ts', integrationsRoot), + [ + '// Generated by scripts/generate-homeassistant-ports.mjs. Do not edit manually.', + "export * from './generated/index.js';", + ...handwrittenFolders.map((folderName) => `export * from './${folderName}/index.js';`), + '', + ].join('\n') +); + console.log(`Generated ${ports.filter((port) => !port.handwritten).length} native TypeScript port skeletons. Preserved ${ports.filter((port) => port.handwritten).length} handwritten folders.`); diff --git a/ts/integrations/index.ts b/ts/integrations/index.ts index 00ca8ec..7d24bea 100644 --- a/ts/integrations/index.ts +++ b/ts/integrations/index.ts @@ -1,3 +1,67 @@ +// Generated by scripts/generate-homeassistant-ports.mjs. Do not edit manually. export * from './generated/index.js'; +export * from './adguard/index.js'; +export * from './airgradient/index.js'; +export * from './amcrest/index.js'; +export * from './android_ip_webcam/index.js'; +export * from './androidtv/index.js'; +export * from './androidtv_remote/index.js'; +export * from './apcupsd/index.js'; +export * from './arcam_fmj/index.js'; +export * from './asuswrt/index.js'; +export * from './axis/index.js'; +export * from './blebox/index.js'; +export * from './bluetooth_le_tracker/index.js'; +export * from './bosch_shc/index.js'; +export * from './braviatv/index.js'; +export * from './broadlink/index.js'; +export * from './cast/index.js'; +export * from './deconz/index.js'; +export * from './denonavr/index.js'; +export * from './devolo_home_network/index.js'; +export * from './dlna_dmr/index.js'; +export * from './dsmr/index.js'; +export * from './esphome/index.js'; +export * from './fritz/index.js'; +export * from './glances/index.js'; +export * from './heos/index.js'; +export * from './homekit_controller/index.js'; +export * from './homematic/index.js'; export * from './hue/index.js'; +export * from './ipp/index.js'; +export * from './jellyfin/index.js'; +export * from './knx/index.js'; +export * from './kodi/index.js'; +export * from './matter/index.js'; +export * from './mikrotik/index.js'; +export * from './modbus/index.js'; +export * from './motioneye/index.js'; +export * from './mpd/index.js'; +export * from './mqtt/index.js'; +export * from './nanoleaf/index.js'; +export * from './onvif/index.js'; +export * from './opentherm_gw/index.js'; +export * from './opnsense/index.js'; +export * from './pi_hole/index.js'; +export * from './plex/index.js'; +export * from './rainbird/index.js'; +export * from './rflink/index.js'; +export * from './roku/index.js'; +export * from './samsungtv/index.js'; +export * from './shelly/index.js'; +export * from './snapcast/index.js'; +export * from './sonos/index.js'; +export * from './squeezebox/index.js'; +export * from './synology_dsm/index.js'; +export * from './tplink/index.js'; +export * from './tradfri/index.js'; +export * from './unifi/index.js'; +export * from './velbus/index.js'; +export * from './volumio/index.js'; +export * from './wiz/index.js'; export * from './wolf_smartset/index.js'; +export * from './xiaomi_miio/index.js'; +export * from './yamaha_musiccast/index.js'; +export * from './yeelight/index.js'; +export * from './zha/index.js'; +export * from './zwave_js/index.js';