homebridge-sensus 1.1.1 → 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/config.schema.json +25 -1
- package/dist/accessory.d.ts +9 -0
- package/dist/accessory.js +51 -11
- package/package.json +1 -1
- package/src/accessory.ts +57 -11
package/config.schema.json
CHANGED
|
@@ -49,6 +49,26 @@
|
|
|
49
49
|
"type": "number",
|
|
50
50
|
"default": 30,
|
|
51
51
|
"description": "How often to fetch new data from Sensus Analytics, in minutes. Default: 30."
|
|
52
|
+
},
|
|
53
|
+
"displayUnit": {
|
|
54
|
+
"title": "HomeKit Display Unit",
|
|
55
|
+
"type": "string",
|
|
56
|
+
"default": "gal",
|
|
57
|
+
"oneOf": [
|
|
58
|
+
{
|
|
59
|
+
"title": "Gallons (gal)",
|
|
60
|
+
"enum": [
|
|
61
|
+
"gal"
|
|
62
|
+
]
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"title": "Liters (l)",
|
|
66
|
+
"enum": [
|
|
67
|
+
"l"
|
|
68
|
+
]
|
|
69
|
+
}
|
|
70
|
+
],
|
|
71
|
+
"description": "The unit of measurement to display in the HomeKit/Eve app. Default: Gallons."
|
|
52
72
|
}
|
|
53
73
|
}
|
|
54
74
|
},
|
|
@@ -63,7 +83,11 @@
|
|
|
63
83
|
"type": "fieldset",
|
|
64
84
|
"title": "Advanced Options",
|
|
65
85
|
"expandable": true,
|
|
66
|
-
"items": [
|
|
86
|
+
"items": [
|
|
87
|
+
"leakThreshold",
|
|
88
|
+
"pollInterval",
|
|
89
|
+
"displayUnit"
|
|
90
|
+
]
|
|
67
91
|
}
|
|
68
92
|
]
|
|
69
93
|
}
|
package/dist/accessory.d.ts
CHANGED
|
@@ -8,10 +8,19 @@ export declare class SensusWaterMeterAccessory {
|
|
|
8
8
|
private readonly leakService;
|
|
9
9
|
private readonly leakThreshold;
|
|
10
10
|
private readonly pollIntervalMs;
|
|
11
|
+
private readonly displayUnit;
|
|
11
12
|
private readonly eveConsumptionChar;
|
|
12
13
|
private readonly eveTotalChar;
|
|
13
14
|
private lastData;
|
|
14
15
|
constructor(platform: SensusAnalyticsPlatform, accessory: PlatformAccessory, apiClient: SensusAnalyticsApi);
|
|
16
|
+
/**
|
|
17
|
+
* Helper to get the conversion factor from the Sensus API unit to Gallons.
|
|
18
|
+
*/
|
|
19
|
+
private getSensusUnitMultiplier;
|
|
20
|
+
/**
|
|
21
|
+
* Converts a value in Gallons to the user's configured HomeKit display unit (gal or l).
|
|
22
|
+
*/
|
|
23
|
+
private convertGallonsToDisplayUnit;
|
|
15
24
|
/**
|
|
16
25
|
* Creates and registers a custom Characteristic on the LeakSensor service.
|
|
17
26
|
* If the characteristic already exists (restored from cache), it is returned as-is.
|
package/dist/accessory.js
CHANGED
|
@@ -18,6 +18,7 @@ class SensusWaterMeterAccessory {
|
|
|
18
18
|
(platform.config.pollInterval ?? DEFAULT_POLL_INTERVAL_MINUTES) *
|
|
19
19
|
60 *
|
|
20
20
|
1000;
|
|
21
|
+
this.displayUnit = platform.config.displayUnit ?? 'gal';
|
|
21
22
|
// ── Accessory Information ─────────────────────────────────────────────
|
|
22
23
|
this.accessory
|
|
23
24
|
.getService(Svc.AccessoryInformation)
|
|
@@ -44,6 +45,31 @@ class SensusWaterMeterAccessory {
|
|
|
44
45
|
this.poll();
|
|
45
46
|
setInterval(() => this.poll(), this.pollIntervalMs);
|
|
46
47
|
}
|
|
48
|
+
/**
|
|
49
|
+
* Helper to get the conversion factor from the Sensus API unit to Gallons.
|
|
50
|
+
*/
|
|
51
|
+
getSensusUnitMultiplier(sensusUnit) {
|
|
52
|
+
const unit = sensusUnit.toUpperCase().trim();
|
|
53
|
+
if (unit === 'CCF' || unit === 'HCF') {
|
|
54
|
+
return 748.052; // 1 CCF = 748.052 Gallons
|
|
55
|
+
}
|
|
56
|
+
if (unit === 'CF' || unit === 'CUBIC_FOOT' || unit === 'CUBIC_FEET' || unit === 'CUBIC FEET') {
|
|
57
|
+
return 7.48052; // 1 Cubic Foot = 7.48052 Gallons
|
|
58
|
+
}
|
|
59
|
+
if (unit === 'L' || unit === 'LITER' || unit === 'LITERS' || unit === 'LITRES') {
|
|
60
|
+
return 0.264172; // 1 Liter = 0.264172 Gallons
|
|
61
|
+
}
|
|
62
|
+
return 1.0; // Default to 1 (already gallons or unknown)
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Converts a value in Gallons to the user's configured HomeKit display unit (gal or l).
|
|
66
|
+
*/
|
|
67
|
+
convertGallonsToDisplayUnit(gallons) {
|
|
68
|
+
if (this.displayUnit === 'l') {
|
|
69
|
+
return gallons * 3.78541; // 1 Gallon = 3.78541 Liters
|
|
70
|
+
}
|
|
71
|
+
return gallons; // Default to Gallons
|
|
72
|
+
}
|
|
47
73
|
/**
|
|
48
74
|
* Creates and registers a custom Characteristic on the LeakSensor service.
|
|
49
75
|
* If the characteristic already exists (restored from cache), it is returned as-is.
|
|
@@ -55,7 +81,7 @@ class SensusWaterMeterAccessory {
|
|
|
55
81
|
return existing;
|
|
56
82
|
}
|
|
57
83
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
58
|
-
const props = { format, unit:
|
|
84
|
+
const props = { format, unit: this.displayUnit, minValue, maxValue, minStep, perms };
|
|
59
85
|
const char = new hap.Characteristic(displayName, uuid, props);
|
|
60
86
|
char.setValue(0);
|
|
61
87
|
this.leakService.addCharacteristic(char);
|
|
@@ -67,7 +93,9 @@ class SensusWaterMeterAccessory {
|
|
|
67
93
|
if (!this.lastData) {
|
|
68
94
|
return LEAK_NOT_DETECTED;
|
|
69
95
|
}
|
|
70
|
-
|
|
96
|
+
const multiplier = this.getSensusUnitMultiplier(this.lastData.daily.usageUnit);
|
|
97
|
+
const dailyUsageGallons = this.lastData.daily.dailyUsage * multiplier;
|
|
98
|
+
return dailyUsageGallons > this.leakThreshold
|
|
71
99
|
? LEAK_DETECTED
|
|
72
100
|
: LEAK_NOT_DETECTED;
|
|
73
101
|
}
|
|
@@ -82,18 +110,30 @@ class SensusWaterMeterAccessory {
|
|
|
82
110
|
this.lastData = data;
|
|
83
111
|
const { daily, hourly } = data;
|
|
84
112
|
const { LEAK_DETECTED, LEAK_NOT_DETECTED } = this.platform.Characteristic.LeakDetected;
|
|
85
|
-
|
|
86
|
-
|
|
113
|
+
// Convert raw API values (which can be in CCF, CF, liters, etc.) to Gallons
|
|
114
|
+
const multiplier = this.getSensusUnitMultiplier(daily.usageUnit);
|
|
115
|
+
const dailyUsageGallons = daily.dailyUsage * multiplier;
|
|
116
|
+
const odometerGallons = daily.odometer * multiplier;
|
|
117
|
+
const billingUsageGallons = daily.billingUsage * multiplier;
|
|
118
|
+
// Perform leak check in Gallons
|
|
119
|
+
const isLeaking = dailyUsageGallons > this.leakThreshold;
|
|
120
|
+
// Push leak status to HomeKit
|
|
87
121
|
this.leakService.updateCharacteristic(this.platform.Characteristic.LeakDetected, isLeaking ? LEAK_DETECTED : LEAK_NOT_DETECTED);
|
|
88
|
-
//
|
|
89
|
-
this.
|
|
90
|
-
this.
|
|
91
|
-
//
|
|
122
|
+
// Convert from Gallons to configured display unit (gal or l)
|
|
123
|
+
const dailyUsageDisplay = this.convertGallonsToDisplayUnit(dailyUsageGallons);
|
|
124
|
+
const odometerDisplay = this.convertGallonsToDisplayUnit(odometerGallons);
|
|
125
|
+
// Update custom Eve characteristics
|
|
126
|
+
// - eveConsumptionChar (E863F10D, Current Power / Consumption in Watts) displays daily value
|
|
127
|
+
this.eveConsumptionChar.updateValue(dailyUsageDisplay);
|
|
128
|
+
// - eveTotalChar (E863F10C, Total Consumption in kWh) displays cumulative odometer
|
|
129
|
+
// Multiply by 1000 because Eve app divides E863F10C by 1000 to show kWh
|
|
130
|
+
this.eveTotalChar.updateValue(odometerDisplay * 1000);
|
|
131
|
+
// Log a summary showing both raw units and HomeKit display units
|
|
92
132
|
const lastHour = hourly.at(-1);
|
|
93
133
|
this.platform.log.info(`[${this.accessory.displayName}] ` +
|
|
94
|
-
`daily=${daily.dailyUsage} ${daily.usageUnit} | ` +
|
|
95
|
-
`odometer=${daily.odometer} ${daily.usageUnit} | ` +
|
|
96
|
-
`billing=${daily.billingUsage} ${daily.usageUnit} | ` +
|
|
134
|
+
`daily=${dailyUsageDisplay.toFixed(2)} ${this.displayUnit} (raw=${daily.dailyUsage} ${daily.usageUnit}) | ` +
|
|
135
|
+
`odometer=${odometerDisplay.toFixed(2)} ${this.displayUnit} (raw=${daily.odometer} ${daily.usageUnit}) | ` +
|
|
136
|
+
`billing=${billingUsageGallons.toFixed(2)} gal (raw=${daily.billingUsage} ${daily.usageUnit}) | ` +
|
|
97
137
|
`leak=${isLeaking}` +
|
|
98
138
|
(lastHour ? ` | lastHourUsage=${lastHour.usage} | temp=${lastHour.temp}°F` : ''));
|
|
99
139
|
}
|
package/package.json
CHANGED
package/src/accessory.ts
CHANGED
|
@@ -12,6 +12,7 @@ export class SensusWaterMeterAccessory {
|
|
|
12
12
|
private readonly leakService: Service;
|
|
13
13
|
private readonly leakThreshold: number;
|
|
14
14
|
private readonly pollIntervalMs: number;
|
|
15
|
+
private readonly displayUnit: string;
|
|
15
16
|
|
|
16
17
|
// Direct references to Eve custom characteristics to avoid UUID lookups on update
|
|
17
18
|
private readonly eveConsumptionChar: Characteristic;
|
|
@@ -32,6 +33,7 @@ export class SensusWaterMeterAccessory {
|
|
|
32
33
|
((platform.config.pollInterval as number | undefined) ?? DEFAULT_POLL_INTERVAL_MINUTES) *
|
|
33
34
|
60 *
|
|
34
35
|
1000;
|
|
36
|
+
this.displayUnit = (platform.config.displayUnit as string | undefined) ?? 'gal';
|
|
35
37
|
|
|
36
38
|
// ── Accessory Information ─────────────────────────────────────────────
|
|
37
39
|
this.accessory
|
|
@@ -83,6 +85,33 @@ export class SensusWaterMeterAccessory {
|
|
|
83
85
|
setInterval(() => this.poll(), this.pollIntervalMs);
|
|
84
86
|
}
|
|
85
87
|
|
|
88
|
+
/**
|
|
89
|
+
* Helper to get the conversion factor from the Sensus API unit to Gallons.
|
|
90
|
+
*/
|
|
91
|
+
private getSensusUnitMultiplier(sensusUnit: string): number {
|
|
92
|
+
const unit = sensusUnit.toUpperCase().trim();
|
|
93
|
+
if (unit === 'CCF' || unit === 'HCF') {
|
|
94
|
+
return 748.052; // 1 CCF = 748.052 Gallons
|
|
95
|
+
}
|
|
96
|
+
if (unit === 'CF' || unit === 'CUBIC_FOOT' || unit === 'CUBIC_FEET' || unit === 'CUBIC FEET') {
|
|
97
|
+
return 7.48052; // 1 Cubic Foot = 7.48052 Gallons
|
|
98
|
+
}
|
|
99
|
+
if (unit === 'L' || unit === 'LITER' || unit === 'LITERS' || unit === 'LITRES') {
|
|
100
|
+
return 0.264172; // 1 Liter = 0.264172 Gallons
|
|
101
|
+
}
|
|
102
|
+
return 1.0; // Default to 1 (already gallons or unknown)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Converts a value in Gallons to the user's configured HomeKit display unit (gal or l).
|
|
107
|
+
*/
|
|
108
|
+
private convertGallonsToDisplayUnit(gallons: number): number {
|
|
109
|
+
if (this.displayUnit === 'l') {
|
|
110
|
+
return gallons * 3.78541; // 1 Gallon = 3.78541 Liters
|
|
111
|
+
}
|
|
112
|
+
return gallons; // Default to Gallons
|
|
113
|
+
}
|
|
114
|
+
|
|
86
115
|
/**
|
|
87
116
|
* Creates and registers a custom Characteristic on the LeakSensor service.
|
|
88
117
|
* If the characteristic already exists (restored from cache), it is returned as-is.
|
|
@@ -104,7 +133,7 @@ export class SensusWaterMeterAccessory {
|
|
|
104
133
|
}
|
|
105
134
|
|
|
106
135
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
107
|
-
const props: any = { format, unit:
|
|
136
|
+
const props: any = { format, unit: this.displayUnit, minValue, maxValue, minStep, perms };
|
|
108
137
|
const char = new hap.Characteristic(displayName, uuid, props);
|
|
109
138
|
char.setValue(0);
|
|
110
139
|
this.leakService.addCharacteristic(char);
|
|
@@ -119,7 +148,10 @@ export class SensusWaterMeterAccessory {
|
|
|
119
148
|
return LEAK_NOT_DETECTED;
|
|
120
149
|
}
|
|
121
150
|
|
|
122
|
-
|
|
151
|
+
const multiplier = this.getSensusUnitMultiplier(this.lastData.daily.usageUnit);
|
|
152
|
+
const dailyUsageGallons = this.lastData.daily.dailyUsage * multiplier;
|
|
153
|
+
|
|
154
|
+
return dailyUsageGallons > this.leakThreshold
|
|
123
155
|
? LEAK_DETECTED
|
|
124
156
|
: LEAK_NOT_DETECTED;
|
|
125
157
|
}
|
|
@@ -138,25 +170,39 @@ export class SensusWaterMeterAccessory {
|
|
|
138
170
|
const { daily, hourly } = data;
|
|
139
171
|
const { LEAK_DETECTED, LEAK_NOT_DETECTED } = this.platform.Characteristic.LeakDetected;
|
|
140
172
|
|
|
141
|
-
|
|
173
|
+
// Convert raw API values (which can be in CCF, CF, liters, etc.) to Gallons
|
|
174
|
+
const multiplier = this.getSensusUnitMultiplier(daily.usageUnit);
|
|
175
|
+
const dailyUsageGallons = daily.dailyUsage * multiplier;
|
|
176
|
+
const odometerGallons = daily.odometer * multiplier;
|
|
177
|
+
const billingUsageGallons = daily.billingUsage * multiplier;
|
|
178
|
+
|
|
179
|
+
// Perform leak check in Gallons
|
|
180
|
+
const isLeaking = dailyUsageGallons > this.leakThreshold;
|
|
142
181
|
|
|
143
|
-
// Push
|
|
182
|
+
// Push leak status to HomeKit
|
|
144
183
|
this.leakService.updateCharacteristic(
|
|
145
184
|
this.platform.Characteristic.LeakDetected,
|
|
146
185
|
isLeaking ? LEAK_DETECTED : LEAK_NOT_DETECTED,
|
|
147
186
|
);
|
|
148
187
|
|
|
149
|
-
//
|
|
150
|
-
this.
|
|
151
|
-
this.
|
|
188
|
+
// Convert from Gallons to configured display unit (gal or l)
|
|
189
|
+
const dailyUsageDisplay = this.convertGallonsToDisplayUnit(dailyUsageGallons);
|
|
190
|
+
const odometerDisplay = this.convertGallonsToDisplayUnit(odometerGallons);
|
|
191
|
+
|
|
192
|
+
// Update custom Eve characteristics
|
|
193
|
+
// - eveConsumptionChar (E863F10D, Current Power / Consumption in Watts) displays daily value
|
|
194
|
+
this.eveConsumptionChar.updateValue(dailyUsageDisplay);
|
|
195
|
+
// - eveTotalChar (E863F10C, Total Consumption in kWh) displays cumulative odometer
|
|
196
|
+
// Multiply by 1000 because Eve app divides E863F10C by 1000 to show kWh
|
|
197
|
+
this.eveTotalChar.updateValue(odometerDisplay * 1000);
|
|
152
198
|
|
|
153
|
-
// Log a summary
|
|
199
|
+
// Log a summary showing both raw units and HomeKit display units
|
|
154
200
|
const lastHour = hourly.at(-1);
|
|
155
201
|
this.platform.log.info(
|
|
156
202
|
`[${this.accessory.displayName}] ` +
|
|
157
|
-
`daily=${daily.dailyUsage} ${daily.usageUnit} | ` +
|
|
158
|
-
`odometer=${daily.odometer} ${daily.usageUnit} | ` +
|
|
159
|
-
`billing=${daily.billingUsage} ${daily.usageUnit} | ` +
|
|
203
|
+
`daily=${dailyUsageDisplay.toFixed(2)} ${this.displayUnit} (raw=${daily.dailyUsage} ${daily.usageUnit}) | ` +
|
|
204
|
+
`odometer=${odometerDisplay.toFixed(2)} ${this.displayUnit} (raw=${daily.odometer} ${daily.usageUnit}) | ` +
|
|
205
|
+
`billing=${billingUsageGallons.toFixed(2)} gal (raw=${daily.billingUsage} ${daily.usageUnit}) | ` +
|
|
160
206
|
`leak=${isLeaking}` +
|
|
161
207
|
(lastHour ? ` | lastHourUsage=${lastHour.usage} | temp=${lastHour.temp}°F` : ''),
|
|
162
208
|
);
|