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: {
|
env: {
|
||||||
node: true,
|
node: true,
|
||||||
browser: false,
|
browser: false,
|
||||||
|
|
|
@ -142,3 +142,4 @@ prettier.config.js
|
||||||
.eslintrc.js
|
.eslintrc.js
|
||||||
jest.config.js
|
jest.config.js
|
||||||
nodemon.json
|
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-import-resolver-typescript": "^3.5.2",
|
||||||
"eslint-plugin-import": "^2.26.0",
|
"eslint-plugin-import": "^2.26.0",
|
||||||
"eslint-plugin-prettier": "^4.2.1",
|
"eslint-plugin-prettier": "^4.2.1",
|
||||||
|
"hap-nodejs": "^0.10.4",
|
||||||
"homebridge": "^1.3.5",
|
"homebridge": "^1.3.5",
|
||||||
"homebridge-cmdswitch2": "^0.2.10",
|
"homebridge-cmdswitch2": "^0.2.10",
|
||||||
"jest": "^29.3.0",
|
"jest": "^29.3.0",
|
||||||
|
|
12
package.json
12
package.json
|
@ -16,13 +16,14 @@
|
||||||
},
|
},
|
||||||
"main": "dist/src/index.js",
|
"main": "dist/src/index.js",
|
||||||
"scripts": {
|
"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",
|
"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/",
|
"link": "npm install --no-save file:///$PWD/",
|
||||||
"build": "rimraf ./dist && tsc",
|
"build": "rimraf ./dist && npm run code-generation && tsc",
|
||||||
"prepublishOnly": "npm run lint && npm run build",
|
"code-generation": "./code-generation/hap-gen.js",
|
||||||
"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"
|
"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": [
|
"keywords": [
|
||||||
"homebridge-plugin",
|
"homebridge-plugin",
|
||||||
|
@ -40,6 +41,7 @@
|
||||||
"eslint-import-resolver-typescript": "^3.5.2",
|
"eslint-import-resolver-typescript": "^3.5.2",
|
||||||
"eslint-plugin-import": "^2.26.0",
|
"eslint-plugin-import": "^2.26.0",
|
||||||
"eslint-plugin-prettier": "^4.2.1",
|
"eslint-plugin-prettier": "^4.2.1",
|
||||||
|
"hap-nodejs": "^0.10.4",
|
||||||
"homebridge": "^1.3.5",
|
"homebridge": "^1.3.5",
|
||||||
"homebridge-cmdswitch2": "^0.2.10",
|
"homebridge-cmdswitch2": "^0.2.10",
|
||||||
"jest": "^29.3.0",
|
"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 type { Accessory, Device, Service } from './boundaries/hap'
|
||||||
|
import { Uuids } from './generated/services'
|
||||||
import { assertTypeExhausted, isType } from './std'
|
import { assertTypeExhausted, isType } from './std'
|
||||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
||||||
import { Service as HapService } from 'hap-nodejs'
|
|
||||||
|
|
||||||
export class Metric {
|
export class Metric {
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -48,7 +47,7 @@ export function aggregate(devices: Device[], timestamp: Date): Metric[] {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
const name = formatName(
|
const name = formatName(
|
||||||
uuidToServerName(service.type),
|
Uuids[service.type] || 'custom',
|
||||||
characteristic.description,
|
characteristic.description,
|
||||||
characteristic.unit,
|
characteristic.unit,
|
||||||
)
|
)
|
||||||
|
@ -127,15 +126,3 @@ function getServiceLabels(service: Service): Record<string, string> {
|
||||||
|
|
||||||
return labels
|
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_current_visibility_state', 0, timestamp, expectedLabels3),
|
||||||
new Metric('input_source_target_visibility_state', 0, timestamp, expectedLabels3),
|
new Metric('input_source_target_visibility_state', 0, timestamp, expectedLabels3),
|
||||||
|
|
||||||
new Metric('speaker_active', 1, timestamp, expectedLabels4),
|
new Metric('television_speaker_active', 1, timestamp, expectedLabels4),
|
||||||
new Metric('speaker_volume_control_type', 3, timestamp, expectedLabels4),
|
new Metric('television_speaker_volume_control_type', 3, timestamp, expectedLabels4),
|
||||||
new Metric('speaker_mute', 0, timestamp, expectedLabels4),
|
new Metric('television_speaker_mute', 0, timestamp, expectedLabels4),
|
||||||
new Metric('speaker_volume_percentage', 50, timestamp, expectedLabels4),
|
new Metric('television_speaker_volume_percentage', 50, timestamp, expectedLabels4),
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue