Expose bools as metrics as well
This commit is contained in:
parent
f1b48575cb
commit
78a8972bb0
4 changed files with 51 additions and 22 deletions
|
@ -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>
|
||||||
|
|
|
@ -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,10 +129,10 @@ 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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new Error(`Could not resolve UUID ${uuid} to service`)
|
throw new Error(`Could not resolve UUID ${uuid} to service`)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)}`)
|
||||||
|
}
|
||||||
|
|
|
@ -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),
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in a new issue