generated from homebridge/homebridge-plugin-template
-
-
Notifications
You must be signed in to change notification settings - Fork 49
/
monitor.ts
150 lines (131 loc) · 5.23 KB
/
monitor.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
import { Characteristic, CharacteristicValue, Service, WithUUID } from 'homebridge';
export type MqttToHomeKitValueTransformer = (value: unknown) => CharacteristicValue | undefined;
export interface CharacteristicMonitor {
callback(state: Record<string, unknown>): void;
}
abstract class BaseCharacteristicMonitor implements CharacteristicMonitor {
constructor(
private readonly key: string,
protected readonly service: Service,
protected readonly characteristic: string | WithUUID<new () => Characteristic>
) {}
abstract transformValueFromMqtt(value: unknown): CharacteristicValue | undefined;
callback(state: Record<string, unknown>): void {
if (this.key in state) {
let value = state[this.key];
if (value !== undefined) {
value = this.transformValueFromMqtt(value);
if (value !== undefined) {
this.service.updateCharacteristic(this.characteristic, value as CharacteristicValue);
}
}
}
}
}
export class NestedCharacteristicMonitor implements CharacteristicMonitor {
constructor(
private readonly key: string,
private readonly monitors: CharacteristicMonitor[]
) {
if (monitors.length === 0) {
throw new RangeError(`No monitors passed to NestedCharacteristicMonitor for key ${key}.`);
}
}
callback(state: Record<string, unknown>): void {
if (this.key in state) {
const nested_state = state[this.key] as Record<string, unknown>;
this.monitors.forEach((m) => m.callback(nested_state));
}
}
}
export class PassthroughCharacteristicMonitor extends BaseCharacteristicMonitor {
transformValueFromMqtt(value: unknown): CharacteristicValue | undefined {
return value as CharacteristicValue;
}
}
export class MappingCharacteristicMonitor extends BaseCharacteristicMonitor {
constructor(
key: string,
service: Service,
characteristic: string | WithUUID<new () => Characteristic>,
private readonly mapping: Map<CharacteristicValue, CharacteristicValue>
) {
super(key, service, characteristic);
if (mapping.size === 0) {
throw new RangeError(`Empty mapping passed to MappingCharacteristicMonitor for key ${key} on service ${this.service.UUID}.`);
}
}
transformValueFromMqtt(value: unknown): CharacteristicValue | undefined {
return this.mapping.get(value as CharacteristicValue);
}
}
export type BinaryConditionBasedOnValue = (value: unknown) => boolean;
export class BinaryConditionCharacteristicMonitor extends BaseCharacteristicMonitor {
constructor(
key: string,
service: Service,
characteristic: string | WithUUID<new () => Characteristic>,
private readonly condition: BinaryConditionBasedOnValue,
private readonly value_true: CharacteristicValue,
private readonly value_false: CharacteristicValue
) {
super(key, service, characteristic);
}
transformValueFromMqtt(value: unknown): CharacteristicValue | undefined {
return this.condition(value) ? this.value_true : this.value_false;
}
}
export class NumericCharacteristicMonitor extends BaseCharacteristicMonitor {
constructor(
key: string,
service: Service,
characteristic: string | WithUUID<new () => Characteristic>,
private readonly input_min: number,
private readonly input_max: number,
private readonly output_min?: number | undefined,
private readonly output_max?: number | undefined,
private readonly ceilAlmostZeroValue = false
) {
super(key, service, characteristic);
if (input_min === input_max) {
throw new RangeError(`input min/max equal on NumericCharacteristicMonitor for key ${key} on service ${this.service.UUID}.`);
}
if (output_min !== undefined && output_min === output_max) {
throw new RangeError(`output min/max equal on NumericCharacteristicMonitor for key ${key} on service ${this.service.UUID}.`);
}
}
transformValueFromMqtt(value: unknown): CharacteristicValue | undefined {
const input = value as number;
let out_minimum: number;
let out_maximum: number;
const actualCharacteristic = this.service.getCharacteristic(this.characteristic);
if (this.output_min === undefined) {
if (actualCharacteristic === undefined || actualCharacteristic.props.minValue === undefined) {
throw new Error('NumericCharacteristicMonitor initialized without output_min, but it is not provided by characteristic either.');
}
out_minimum = actualCharacteristic.props.minValue;
} else {
out_minimum = this.output_min;
}
if (this.output_max === undefined) {
if (actualCharacteristic === undefined || actualCharacteristic.props.maxValue === undefined) {
throw new Error('NumericCharacteristicMonitor initialized without output_max, but it is not provided by characteristic either.');
}
out_maximum = actualCharacteristic.props.maxValue;
} else {
out_maximum = this.output_max;
}
if (input <= this.input_min) {
return out_minimum;
}
if (input >= this.input_max) {
return out_maximum;
}
const percentage = (input - this.input_min) / (this.input_max - this.input_min);
const result = out_minimum + percentage * (out_maximum - out_minimum);
if (this.ceilAlmostZeroValue && result > 0 && result < 1) {
return 1;
}
return result;
}
}