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'
export const NUMBER_TYPES = ['float', 'int', 'uint8', 'uint16', 'uint32', 'uint64'] as const
const NumberTypesLiterals = NUMBER_TYPES.map(Literal)
const NumberTypesBoundary = Union(NumberTypesLiterals[0], ...NumberTypesLiterals)
export type NumberTypes = Static<typeof NumberTypesBoundary>
export const NUMBER_TYPES = [] as const
const NumberAlikeTypesTypesBoundary = Union(
Literal('bool'),
Literal('float'),
Literal('int'),
Literal('uint8'),
Literal('uint16'),
Literal('uint32'),
Literal('uint64'),
)
export type NumberAlikeTypes = Static<typeof NumberAlikeTypesTypesBoundary>
export const CharacteristicBoundary = Intersect(
Record({ type: String, description: String }),
Union(
Record({
format: NumberTypesBoundary,
format: NumberAlikeTypesTypesBoundary,
value: Optional(Number),
unit: Optional(String),
}),
Record({ format: Literal('string'), value: String }),
Record({ format: Literal('bool') }),
),
)
export type Characteristic = Static<typeof CharacteristicBoundary>

View file

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

View file

@ -9,3 +9,7 @@ interface TypeMap {
export function isType<T extends Types>(type: T): (v: unknown) => v is TypeMap[T] {
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([
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_total_consumption_kwh', 0.051, timestamp, expectedLabelsAccessory1),
new Metric('outlet_apparent_power_va', 53248.8, timestamp, expectedLabelsAccessory1),
new Metric('outlet_volts_v', 230.8, 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_total_consumption_kwh', 13.025, 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_volume_control_type', 3, timestamp, expectedLabels4),
new Metric('speaker_mute', 0, timestamp, expectedLabels4),
new Metric('speaker_volume_percentage', 50, timestamp, expectedLabels4),
])
})