Expose bools as metrics as well

This commit is contained in:
Lars Strojny 2022-11-08 01:19:15 +01:00
parent f1b48575cb
commit 78a8972bb0
4 changed files with 51 additions and 22 deletions

View file

@ -1,20 +1,27 @@
import { Array, Intersect, Literal, Number, Optional, Record, Static, String, Union } from 'runtypes' import { Array, Intersect, Literal, Number, Optional, Record, Static, String, Union } from 'runtypes'
export const NUMBER_TYPES = ['float', 'int', 'uint8', 'uint16', 'uint32', 'uint64'] as const export const NUMBER_TYPES = [] as const
const NumberTypesLiterals = NUMBER_TYPES.map(Literal)
const NumberTypesBoundary = Union(NumberTypesLiterals[0], ...NumberTypesLiterals) const NumberAlikeTypesTypesBoundary = Union(
export type NumberTypes = Static<typeof NumberTypesBoundary> Literal('bool'),
Literal('float'),
Literal('int'),
Literal('uint8'),
Literal('uint16'),
Literal('uint32'),
Literal('uint64'),
)
export type NumberAlikeTypes = Static<typeof NumberAlikeTypesTypesBoundary>
export const CharacteristicBoundary = Intersect( export const CharacteristicBoundary = Intersect(
Record({ type: String, description: String }), Record({ type: String, description: String }),
Union( Union(
Record({ Record({
format: NumberTypesBoundary, format: NumberAlikeTypesTypesBoundary,
value: Optional(Number), value: Optional(Number),
unit: Optional(String), unit: Optional(String),
}), }),
Record({ format: Literal('string'), value: String }), Record({ format: Literal('string'), value: String }),
Record({ format: Literal('bool') }),
), ),
) )
export type Characteristic = Static<typeof CharacteristicBoundary> export type Characteristic = Static<typeof CharacteristicBoundary>

View file

@ -1,6 +1,5 @@
import type { Accessory, Device, Service } from './boundaries' import type { Accessory, Device, Service } from './boundaries'
import { isType } from './std' import { assertTypeExhausted, isType } from './std'
import { NUMBER_TYPES } from './boundaries'
import { Service as HapService } from 'hap-nodejs' import { Service as HapService } from 'hap-nodejs'
export class Metric { export class Metric {
@ -29,20 +28,33 @@ export function aggregate(devices: Device[], timestamp: Date): Metric[] {
...getServiceLabels(service), ...getServiceLabels(service),
} }
for (const characteristic of service.characteristics) { for (const characteristic of service.characteristics) {
for (const numberType of NUMBER_TYPES) { const format = characteristic.format
if (characteristic.format === numberType && typeof characteristic.value !== 'undefined') { switch (format) {
if (METRICS_FILTER.includes(characteristic.description)) { case 'string':
continue break
}
const name = formatName( case 'bool':
uuidToServerName(service.type), case 'float':
characteristic.description, case 'int':
characteristic.unit, case 'uint8':
) case 'uint16':
if (!METRICS_FILTER.includes(name)) { case 'uint32':
case 'uint64':
if (typeof characteristic.value !== 'undefined') {
if (METRICS_FILTER.includes(characteristic.description)) {
break
}
const name = formatName(
uuidToServerName(service.type),
characteristic.description,
characteristic.unit,
)
metrics.push(new Metric(name, characteristic.value, timestamp, labels)) metrics.push(new Metric(name, characteristic.value, timestamp, labels))
} }
} break
default:
assertTypeExhausted(format)
} }
} }
} }
@ -117,7 +129,7 @@ function uuidToServerName(uuid: string): string {
for (const name of Object.getOwnPropertyNames(HapService)) { for (const name of Object.getOwnPropertyNames(HapService)) {
const maybeService = (HapService as unknown as Record<string, unknown>)[name] const maybeService = (HapService as unknown as Record<string, unknown>)[name]
if (typeof maybeService === 'function' && 'UUID' in maybeService) { if (typeof maybeService === 'function' && 'UUID' in maybeService) {
if ((maybeService as Record<string,string>)['UUID'] === uuid) { if ((maybeService as Record<string, string>)['UUID'] === uuid) {
return name return name
} }
} }

View file

@ -9,3 +9,7 @@ interface TypeMap {
export function isType<T extends Types>(type: T): (v: unknown) => v is TypeMap[T] { export function isType<T extends Types>(type: T): (v: unknown) => v is TypeMap[T] {
return (v: unknown): v is TypeMap[T] => typeof v === type return (v: unknown): v is TypeMap[T] => typeof v === type
} }
export function assertTypeExhausted(v: never): never {
throw new Error(`Type should be exhausted but is not. Value "${JSON.stringify(v)}`)
}

View file

@ -69,11 +69,16 @@ describe('Metrics aggregator', () => {
} }
expect(aggregate([tpLink], timestamp)).toEqual([ expect(aggregate([tpLink], timestamp)).toEqual([
new Metric('outlet_on', 0, timestamp, expectedLabelsAccessory1),
new Metric('outlet_in_use', 0, timestamp, expectedLabelsAccessory1),
new Metric('outlet_amperes_a', 0.03, timestamp, expectedLabelsAccessory1), new Metric('outlet_amperes_a', 0.03, timestamp, expectedLabelsAccessory1),
new Metric('outlet_total_consumption_kwh', 0.051, timestamp, expectedLabelsAccessory1), new Metric('outlet_total_consumption_kwh', 0.051, timestamp, expectedLabelsAccessory1),
new Metric('outlet_apparent_power_va', 53248.8, timestamp, expectedLabelsAccessory1), new Metric('outlet_apparent_power_va', 53248.8, timestamp, expectedLabelsAccessory1),
new Metric('outlet_volts_v', 230.8, timestamp, expectedLabelsAccessory1), new Metric('outlet_volts_v', 230.8, timestamp, expectedLabelsAccessory1),
new Metric('outlet_consumption_w', 0, timestamp, expectedLabelsAccessory1), new Metric('outlet_consumption_w', 0, timestamp, expectedLabelsAccessory1),
new Metric('outlet_on', 0, timestamp, expectedLabelsAccessory2),
new Metric('outlet_in_use', 0, timestamp, expectedLabelsAccessory2),
new Metric('outlet_amperes_a', 0.03, timestamp, expectedLabelsAccessory2), new Metric('outlet_amperes_a', 0.03, timestamp, expectedLabelsAccessory2),
new Metric('outlet_total_consumption_kwh', 13.025, timestamp, expectedLabelsAccessory2), new Metric('outlet_total_consumption_kwh', 13.025, timestamp, expectedLabelsAccessory2),
new Metric('outlet_apparent_power_va', 53365.6, timestamp, expectedLabelsAccessory2), new Metric('outlet_apparent_power_va', 53365.6, timestamp, expectedLabelsAccessory2),
@ -145,6 +150,7 @@ describe('Metrics aggregator', () => {
new Metric('speaker_active', 1, timestamp, expectedLabels4), new Metric('speaker_active', 1, timestamp, expectedLabels4),
new Metric('speaker_volume_control_type', 3, 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('speaker_volume_percentage', 50, timestamp, expectedLabels4),
]) ])
}) })