Use code generation for UUID service map (#14)
Instead of introspecting *hap-nodejs* at runtime for UUID to service mapping, generate the map during build time.
This commit is contained in:
parent
38bf24df90
commit
f6bc8ca90c
8 changed files with 207 additions and 25 deletions
|
@ -43,7 +43,7 @@ module.exports = {
|
|||
},
|
||||
},
|
||||
{
|
||||
files: ['.eslintrc.js', 'jest.config.js', 'prettier.config.js'],
|
||||
files: ['**/*.js'],
|
||||
env: {
|
||||
node: true,
|
||||
browser: false,
|
||||
|
|
|
@ -142,3 +142,4 @@ prettier.config.js
|
|||
.eslintrc.js
|
||||
jest.config.js
|
||||
nodemon.json
|
||||
/code-generation/
|
||||
|
|
31
code-generation/hap-gen.js
Executable file
31
code-generation/hap-gen.js
Executable file
|
@ -0,0 +1,31 @@
|
|||
#!/usr/bin/env node
|
||||
const hap = require('hap-nodejs')
|
||||
const { format } = require('prettier')
|
||||
const prettierConfig = require('../prettier.config')
|
||||
const { writeFileSync } = require('fs')
|
||||
const { join, basename } = require('path')
|
||||
|
||||
const uuidToServiceMap = {}
|
||||
const serviceToUuidMap = {}
|
||||
|
||||
for (const [name, service] of Object.entries(hap.Service)) {
|
||||
if (typeof service !== 'function' || typeof service.UUID !== 'string') {
|
||||
console.log(`Skipping ${typeof service} ${name}`)
|
||||
continue
|
||||
}
|
||||
|
||||
uuidToServiceMap[service.UUID] = name
|
||||
serviceToUuidMap[name] = service.UUID
|
||||
}
|
||||
|
||||
const code = format(
|
||||
`
|
||||
// Auto-generated by "${join(basename(__dirname), basename(__filename))}", don’t manually edit
|
||||
export const Uuids: Record<string,string> = ${JSON.stringify(uuidToServiceMap)} as const
|
||||
|
||||
export const Services: Record<string,string> = ${JSON.stringify(serviceToUuidMap)} as const
|
||||
`,
|
||||
{ filepath: 'codegen.ts', ...prettierConfig },
|
||||
)
|
||||
|
||||
writeFileSync(join(__dirname, '../src/generated/services.ts'), code)
|
1
package-lock.json
generated
1
package-lock.json
generated
|
@ -23,6 +23,7 @@
|
|||
"eslint-import-resolver-typescript": "^3.5.2",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"hap-nodejs": "^0.10.4",
|
||||
"homebridge": "^1.3.5",
|
||||
"homebridge-cmdswitch2": "^0.2.10",
|
||||
"jest": "^29.3.0",
|
||||
|
|
12
package.json
12
package.json
|
@ -16,13 +16,14 @@
|
|||
},
|
||||
"main": "dist/src/index.js",
|
||||
"scripts": {
|
||||
"lint": "ifNotCi() { test \"$CI\" && echo \"$2\" || echo \"$1\"; }; `npm bin`/prettier --ignore-path=.gitignore `ifNotCi --write --check` '**/**.{ts,js,json}' && `npm bin`/eslint `ifNotCi --fix` --ignore-path=.gitignore '**/**.{ts,js,json}'",
|
||||
"lint": "ifNotCi() { test \"$CI\" && echo \"$2\" || echo \"$1\"; }; `npm bin`/tsc --noEmit && `npm bin`/prettier --ignore-path=.gitignore `ifNotCi --write --check` '**/**.{ts,js,json}' && `npm bin`/eslint `ifNotCi --fix` --ignore-path=.gitignore '**/**.{ts,js,json}'",
|
||||
"start": "npm run build && npm run link && nodemon",
|
||||
"test": "ifNotCi() { test \"$CI\" && echo \"$2\" || echo \"$1\"; }; `npm bin`/jest `ifNotCi --watchAll`",
|
||||
"test": "ifNotCi() { test \"$CI\" && echo \"$2\" || echo \"$1\"; }; npm run code-generation && `npm bin`/jest `ifNotCi --watchAll`",
|
||||
"link": "npm install --no-save file:///$PWD/",
|
||||
"build": "rimraf ./dist && tsc",
|
||||
"prepublishOnly": "npm run lint && npm run build",
|
||||
"release": "release() { test \"$1\" && test `git rev-parse --abbrev-ref HEAD` == \"develop\" && git pull origin develop --rebase && npm version $1 && npm publish && git push origin develop && git push origin --tags; }; release"
|
||||
"build": "rimraf ./dist && npm run code-generation && tsc",
|
||||
"code-generation": "./code-generation/hap-gen.js",
|
||||
"prepublishOnly": "npm run code-generation && npm run lint && npm run build",
|
||||
"release": "release() { test \"$1\" && test `git rev-parse --abbrev-ref HEAD` == \"develop\" && npm run code-generation && git pull origin develop --rebase && npm version $1 && npm publish && git push origin develop && git push origin --tags; }; release"
|
||||
},
|
||||
"keywords": [
|
||||
"homebridge-plugin",
|
||||
|
@ -40,6 +41,7 @@
|
|||
"eslint-import-resolver-typescript": "^3.5.2",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"hap-nodejs": "^0.10.4",
|
||||
"homebridge": "^1.3.5",
|
||||
"homebridge-cmdswitch2": "^0.2.10",
|
||||
"jest": "^29.3.0",
|
||||
|
|
160
src/generated/services.ts
Normal file
160
src/generated/services.ts
Normal file
|
@ -0,0 +1,160 @@
|
|||
// Auto-generated by "code-generation/hap-gen.js", don’t manually edit
|
||||
export const Uuids: Record<string, string> = {
|
||||
'00000260-0000-1000-8000-0026BB765291': 'AccessCode',
|
||||
'000000DA-0000-1000-8000-0026BB765291': 'AccessControl',
|
||||
'0000003E-0000-1000-8000-0026BB765291': 'AccessoryInformation',
|
||||
'00000270-0000-1000-8000-0026BB765291': 'AccessoryMetrics',
|
||||
'00000239-0000-1000-8000-0026BB765291': 'AccessoryRuntimeInformation',
|
||||
'000000BB-0000-1000-8000-0026BB765291': 'AirPurifier',
|
||||
'0000008D-0000-1000-8000-0026BB765291': 'AirQualitySensor',
|
||||
'00000267-0000-1000-8000-0026BB765291': 'AssetUpdate',
|
||||
'0000026A-0000-1000-8000-0026BB765291': 'Assistant',
|
||||
'00000127-0000-1000-8000-0026BB765291': 'AudioStreamManagement',
|
||||
'00000096-0000-1000-8000-0026BB765291': 'Battery',
|
||||
'000000A1-0000-1000-8000-0026BB765291': 'BridgeConfiguration',
|
||||
'00000062-0000-1000-8000-0026BB765291': 'BridgingState',
|
||||
'00000111-0000-1000-8000-0026BB765291': 'CameraControl',
|
||||
'0000021A-0000-1000-8000-0026BB765291': 'CameraOperatingMode',
|
||||
'00000204-0000-1000-8000-0026BB765291': 'CameraRecordingManagement',
|
||||
'00000110-0000-1000-8000-0026BB765291': 'CameraRTPStreamManagement',
|
||||
'00000097-0000-1000-8000-0026BB765291': 'CarbonDioxideSensor',
|
||||
'0000007F-0000-1000-8000-0026BB765291': 'CarbonMonoxideSensor',
|
||||
'0000005A-0000-1000-8000-0026BB765291': 'CloudRelay',
|
||||
'00000080-0000-1000-8000-0026BB765291': 'ContactSensor',
|
||||
'00000129-0000-1000-8000-0026BB765291': 'DataStreamTransportManagement',
|
||||
'00000237-0000-1000-8000-0026BB765291': 'Diagnostics',
|
||||
'00000081-0000-1000-8000-0026BB765291': 'Door',
|
||||
'00000121-0000-1000-8000-0026BB765291': 'Doorbell',
|
||||
'00000040-0000-1000-8000-0026BB765291': 'Fan',
|
||||
'000000B7-0000-1000-8000-0026BB765291': 'Fanv2',
|
||||
'000000D7-0000-1000-8000-0026BB765291': 'Faucet',
|
||||
'000000BA-0000-1000-8000-0026BB765291': 'FilterMaintenance',
|
||||
'00000041-0000-1000-8000-0026BB765291': 'GarageDoorOpener',
|
||||
'000000BC-0000-1000-8000-0026BB765291': 'HeaterCooler',
|
||||
'000000BD-0000-1000-8000-0026BB765291': 'HumidifierDehumidifier',
|
||||
'00000082-0000-1000-8000-0026BB765291': 'HumiditySensor',
|
||||
'000000D9-0000-1000-8000-0026BB765291': 'InputSource',
|
||||
'000000CF-0000-1000-8000-0026BB765291': 'IrrigationSystem',
|
||||
'00000083-0000-1000-8000-0026BB765291': 'LeakSensor',
|
||||
'00000043-0000-1000-8000-0026BB765291': 'Lightbulb',
|
||||
'00000084-0000-1000-8000-0026BB765291': 'LightSensor',
|
||||
'00000044-0000-1000-8000-0026BB765291': 'LockManagement',
|
||||
'00000045-0000-1000-8000-0026BB765291': 'LockMechanism',
|
||||
'00000112-0000-1000-8000-0026BB765291': 'Microphone',
|
||||
'00000085-0000-1000-8000-0026BB765291': 'MotionSensor',
|
||||
'00000266-0000-1000-8000-0026BB765291': 'NFCAccess',
|
||||
'00000086-0000-1000-8000-0026BB765291': 'OccupancySensor',
|
||||
'00000047-0000-1000-8000-0026BB765291': 'Outlet',
|
||||
'00000055-0000-1000-8000-0026BB765291': 'Pairing',
|
||||
'00000221-0000-1000-8000-0026BB765291': 'PowerManagement',
|
||||
'000000A2-0000-1000-8000-0026BB765291': 'ProtocolInformation',
|
||||
'0000007E-0000-1000-8000-0026BB765291': 'SecuritySystem',
|
||||
'000000CC-0000-1000-8000-0026BB765291': 'ServiceLabel',
|
||||
'00000133-0000-1000-8000-0026BB765291': 'Siri',
|
||||
'00000253-0000-1000-8000-0026BB765291': 'SiriEndpoint',
|
||||
'000000B9-0000-1000-8000-0026BB765291': 'Slats',
|
||||
'00000228-0000-1000-8000-0026BB765291': 'SmartSpeaker',
|
||||
'00000087-0000-1000-8000-0026BB765291': 'SmokeSensor',
|
||||
'00000113-0000-1000-8000-0026BB765291': 'TelevisionSpeaker',
|
||||
'00000088-0000-1000-8000-0026BB765291': 'StatefulProgrammableSwitch',
|
||||
'00000089-0000-1000-8000-0026BB765291': 'StatelessProgrammableSwitch',
|
||||
'00000049-0000-1000-8000-0026BB765291': 'Switch',
|
||||
'00000125-0000-1000-8000-0026BB765291': 'TargetControl',
|
||||
'00000122-0000-1000-8000-0026BB765291': 'TargetControlManagement',
|
||||
'000000D8-0000-1000-8000-0026BB765291': 'Television',
|
||||
'0000008A-0000-1000-8000-0026BB765291': 'TemperatureSensor',
|
||||
'0000004A-0000-1000-8000-0026BB765291': 'Thermostat',
|
||||
'00000701-0000-1000-8000-0026BB765291': 'ThreadTransport',
|
||||
'00000099-0000-1000-8000-0026BB765291': 'TimeInformation',
|
||||
'00000203-0000-1000-8000-0026BB765291': 'TransferTransportManagement',
|
||||
'00000056-0000-1000-8000-0026BB765291': 'Tunnel',
|
||||
'000000D0-0000-1000-8000-0026BB765291': 'Valve',
|
||||
'0000020A-0000-1000-8000-0026BB765291': 'WiFiRouter',
|
||||
'0000020F-0000-1000-8000-0026BB765291': 'WiFiSatellite',
|
||||
'0000022A-0000-1000-8000-0026BB765291': 'WiFiTransport',
|
||||
'0000008B-0000-1000-8000-0026BB765291': 'Window',
|
||||
'0000008C-0000-1000-8000-0026BB765291': 'WindowCovering',
|
||||
} as const
|
||||
|
||||
export const Services: Record<string, string> = {
|
||||
AccessCode: '00000260-0000-1000-8000-0026BB765291',
|
||||
AccessControl: '000000DA-0000-1000-8000-0026BB765291',
|
||||
AccessoryInformation: '0000003E-0000-1000-8000-0026BB765291',
|
||||
AccessoryMetrics: '00000270-0000-1000-8000-0026BB765291',
|
||||
AccessoryRuntimeInformation: '00000239-0000-1000-8000-0026BB765291',
|
||||
AirPurifier: '000000BB-0000-1000-8000-0026BB765291',
|
||||
AirQualitySensor: '0000008D-0000-1000-8000-0026BB765291',
|
||||
AssetUpdate: '00000267-0000-1000-8000-0026BB765291',
|
||||
Assistant: '0000026A-0000-1000-8000-0026BB765291',
|
||||
AudioStreamManagement: '00000127-0000-1000-8000-0026BB765291',
|
||||
BatteryService: '00000096-0000-1000-8000-0026BB765291',
|
||||
Battery: '00000096-0000-1000-8000-0026BB765291',
|
||||
BridgeConfiguration: '000000A1-0000-1000-8000-0026BB765291',
|
||||
BridgingState: '00000062-0000-1000-8000-0026BB765291',
|
||||
CameraControl: '00000111-0000-1000-8000-0026BB765291',
|
||||
CameraOperatingMode: '0000021A-0000-1000-8000-0026BB765291',
|
||||
CameraEventRecordingManagement: '00000204-0000-1000-8000-0026BB765291',
|
||||
CameraRecordingManagement: '00000204-0000-1000-8000-0026BB765291',
|
||||
CameraRTPStreamManagement: '00000110-0000-1000-8000-0026BB765291',
|
||||
CarbonDioxideSensor: '00000097-0000-1000-8000-0026BB765291',
|
||||
CarbonMonoxideSensor: '0000007F-0000-1000-8000-0026BB765291',
|
||||
Relay: '0000005A-0000-1000-8000-0026BB765291',
|
||||
CloudRelay: '0000005A-0000-1000-8000-0026BB765291',
|
||||
ContactSensor: '00000080-0000-1000-8000-0026BB765291',
|
||||
DataStreamTransportManagement: '00000129-0000-1000-8000-0026BB765291',
|
||||
Diagnostics: '00000237-0000-1000-8000-0026BB765291',
|
||||
Door: '00000081-0000-1000-8000-0026BB765291',
|
||||
Doorbell: '00000121-0000-1000-8000-0026BB765291',
|
||||
Fan: '00000040-0000-1000-8000-0026BB765291',
|
||||
Fanv2: '000000B7-0000-1000-8000-0026BB765291',
|
||||
Faucet: '000000D7-0000-1000-8000-0026BB765291',
|
||||
FilterMaintenance: '000000BA-0000-1000-8000-0026BB765291',
|
||||
GarageDoorOpener: '00000041-0000-1000-8000-0026BB765291',
|
||||
HeaterCooler: '000000BC-0000-1000-8000-0026BB765291',
|
||||
HumidifierDehumidifier: '000000BD-0000-1000-8000-0026BB765291',
|
||||
HumiditySensor: '00000082-0000-1000-8000-0026BB765291',
|
||||
InputSource: '000000D9-0000-1000-8000-0026BB765291',
|
||||
IrrigationSystem: '000000CF-0000-1000-8000-0026BB765291',
|
||||
LeakSensor: '00000083-0000-1000-8000-0026BB765291',
|
||||
Lightbulb: '00000043-0000-1000-8000-0026BB765291',
|
||||
LightSensor: '00000084-0000-1000-8000-0026BB765291',
|
||||
LockManagement: '00000044-0000-1000-8000-0026BB765291',
|
||||
LockMechanism: '00000045-0000-1000-8000-0026BB765291',
|
||||
Microphone: '00000112-0000-1000-8000-0026BB765291',
|
||||
MotionSensor: '00000085-0000-1000-8000-0026BB765291',
|
||||
NFCAccess: '00000266-0000-1000-8000-0026BB765291',
|
||||
OccupancySensor: '00000086-0000-1000-8000-0026BB765291',
|
||||
Outlet: '00000047-0000-1000-8000-0026BB765291',
|
||||
Pairing: '00000055-0000-1000-8000-0026BB765291',
|
||||
PowerManagement: '00000221-0000-1000-8000-0026BB765291',
|
||||
ProtocolInformation: '000000A2-0000-1000-8000-0026BB765291',
|
||||
SecuritySystem: '0000007E-0000-1000-8000-0026BB765291',
|
||||
ServiceLabel: '000000CC-0000-1000-8000-0026BB765291',
|
||||
Siri: '00000133-0000-1000-8000-0026BB765291',
|
||||
SiriEndpoint: '00000253-0000-1000-8000-0026BB765291',
|
||||
Slat: '000000B9-0000-1000-8000-0026BB765291',
|
||||
Slats: '000000B9-0000-1000-8000-0026BB765291',
|
||||
SmartSpeaker: '00000228-0000-1000-8000-0026BB765291',
|
||||
SmokeSensor: '00000087-0000-1000-8000-0026BB765291',
|
||||
Speaker: '00000113-0000-1000-8000-0026BB765291',
|
||||
StatefulProgrammableSwitch: '00000088-0000-1000-8000-0026BB765291',
|
||||
StatelessProgrammableSwitch: '00000089-0000-1000-8000-0026BB765291',
|
||||
Switch: '00000049-0000-1000-8000-0026BB765291',
|
||||
TargetControl: '00000125-0000-1000-8000-0026BB765291',
|
||||
TargetControlManagement: '00000122-0000-1000-8000-0026BB765291',
|
||||
Television: '000000D8-0000-1000-8000-0026BB765291',
|
||||
TelevisionSpeaker: '00000113-0000-1000-8000-0026BB765291',
|
||||
TemperatureSensor: '0000008A-0000-1000-8000-0026BB765291',
|
||||
Thermostat: '0000004A-0000-1000-8000-0026BB765291',
|
||||
ThreadTransport: '00000701-0000-1000-8000-0026BB765291',
|
||||
TimeInformation: '00000099-0000-1000-8000-0026BB765291',
|
||||
TransferTransportManagement: '00000203-0000-1000-8000-0026BB765291',
|
||||
TunneledBTLEAccessoryService: '00000056-0000-1000-8000-0026BB765291',
|
||||
Tunnel: '00000056-0000-1000-8000-0026BB765291',
|
||||
Valve: '000000D0-0000-1000-8000-0026BB765291',
|
||||
WiFiRouter: '0000020A-0000-1000-8000-0026BB765291',
|
||||
WiFiSatellite: '0000020F-0000-1000-8000-0026BB765291',
|
||||
WiFiTransport: '0000022A-0000-1000-8000-0026BB765291',
|
||||
Window: '0000008B-0000-1000-8000-0026BB765291',
|
||||
WindowCovering: '0000008C-0000-1000-8000-0026BB765291',
|
||||
} as const
|
|
@ -1,7 +1,6 @@
|
|||
import type { Accessory, Device, Service } from './boundaries/hap'
|
||||
import { Uuids } from './generated/services'
|
||||
import { assertTypeExhausted, isType } from './std'
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
import { Service as HapService } from 'hap-nodejs'
|
||||
|
||||
export class Metric {
|
||||
constructor(
|
||||
|
@ -48,7 +47,7 @@ export function aggregate(devices: Device[], timestamp: Date): Metric[] {
|
|||
break
|
||||
}
|
||||
const name = formatName(
|
||||
uuidToServerName(service.type),
|
||||
Uuids[service.type] || 'custom',
|
||||
characteristic.description,
|
||||
characteristic.unit,
|
||||
)
|
||||
|
@ -127,15 +126,3 @@ function getServiceLabels(service: Service): Record<string, string> {
|
|||
|
||||
return labels
|
||||
}
|
||||
|
||||
function uuidToServerName(uuid: string): string {
|
||||
for (const name of Object.getOwnPropertyNames(HapService)) {
|
||||
const maybeService = (HapService as unknown as Record<string, unknown>)[name]
|
||||
if (typeof maybeService === 'function' && 'UUID' in maybeService) {
|
||||
if ((maybeService as Record<string, string>)['UUID'] === uuid) {
|
||||
return name
|
||||
}
|
||||
}
|
||||
}
|
||||
return 'custom'
|
||||
}
|
||||
|
|
|
@ -149,10 +149,10 @@ describe('Metrics aggregator', () => {
|
|||
new Metric('input_source_current_visibility_state', 0, timestamp, expectedLabels3),
|
||||
new Metric('input_source_target_visibility_state', 0, timestamp, expectedLabels3),
|
||||
|
||||
new Metric('speaker_active', 1, timestamp, expectedLabels4),
|
||||
new Metric('speaker_volume_control_type', 3, timestamp, expectedLabels4),
|
||||
new Metric('speaker_mute', 0, timestamp, expectedLabels4),
|
||||
new Metric('speaker_volume_percentage', 50, timestamp, expectedLabels4),
|
||||
new Metric('television_speaker_active', 1, timestamp, expectedLabels4),
|
||||
new Metric('television_speaker_volume_control_type', 3, timestamp, expectedLabels4),
|
||||
new Metric('television_speaker_mute', 0, timestamp, expectedLabels4),
|
||||
new Metric('television_speaker_volume_percentage', 50, timestamp, expectedLabels4),
|
||||
])
|
||||
})
|
||||
|
||||
|
|
Loading…
Reference in a new issue