homebridge-easy-mqtt 1.2.0-beta.5 → 1.3.0-beta.0
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/CHANGELOG.md +13 -3
- package/README.md +74 -5
- package/config.schema.json +37 -0
- package/dist/accessory/abstract/base.d.ts +1 -5
- package/dist/accessory/abstract/base.js +24 -28
- package/dist/accessory/abstract/base.js.map +1 -1
- package/dist/accessory/abstract/customCharacteristic.d.ts +17 -0
- package/dist/accessory/abstract/customCharacteristic.js +65 -0
- package/dist/accessory/abstract/customCharacteristic.js.map +1 -0
- package/dist/accessory/abstract/mqtt.d.ts +7 -10
- package/dist/accessory/abstract/mqtt.js +55 -48
- package/dist/accessory/abstract/mqtt.js.map +1 -1
- package/dist/accessory/lock.d.ts +0 -3
- package/dist/accessory/lock.js +11 -26
- package/dist/accessory/lock.js.map +1 -1
- package/dist/accessory/onoff/lightbulb.d.ts +1 -13
- package/dist/accessory/onoff/lightbulb.js +9 -66
- package/dist/accessory/onoff/lightbulb.js.map +1 -1
- package/dist/accessory/onoff/onoff.d.ts +0 -2
- package/dist/accessory/onoff/onoff.js +1 -11
- package/dist/accessory/onoff/onoff.js.map +1 -1
- package/dist/accessory/onoff/outlet.d.ts +0 -3
- package/dist/accessory/onoff/outlet.js +1 -13
- package/dist/accessory/onoff/outlet.js.map +1 -1
- package/dist/accessory/security.d.ts +0 -7
- package/dist/accessory/security.js +4 -40
- package/dist/accessory/security.js.map +1 -1
- package/dist/accessory/temperatureSensor.d.ts +0 -3
- package/dist/accessory/temperatureSensor.js +5 -17
- package/dist/accessory/temperatureSensor.js.map +1 -1
- package/dist/homebridge-ui/public/index.html +4 -4
- package/dist/homebridge-ui/public/ui.js +1 -1
- package/dist/i18n/en.d.ts +5 -5
- package/dist/i18n/en.js +14 -14
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/i18n.d.ts +5 -5
- package/dist/i18n/template.d.ts +5 -5
- package/dist/model/enums.d.ts +1 -0
- package/dist/model/enums.js +1 -0
- package/dist/model/enums.js.map +1 -1
- package/dist/model/mqtt.d.ts +1 -1
- package/dist/model/mqtt.js +40 -16
- package/dist/model/mqtt.js.map +1 -1
- package/dist/model/types.d.ts +14 -0
- package/img/banner.png +0 -0
- package/img/banner.xcf +0 -0
- package/img/icon.png +0 -0
- package/img/icon.xcf +0 -0
- package/img/screenshot_1.png +0 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,24 +2,34 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to homebridge-dummy will be documented in this file.
|
|
4
4
|
|
|
5
|
-
## 1.
|
|
5
|
+
## 1.3.0-beta.0 (2025-09-XX)
|
|
6
6
|
|
|
7
7
|
### HELP NEEDED! (no coding experience required)
|
|
8
8
|
|
|
9
9
|
Would you like to see Homebridge Easy MQTT in your language? Please consider [getting involved](https://github.com/mpatfield/homebridge-easy-mqtt/issues/4).
|
|
10
10
|
|
|
11
|
+
### Added
|
|
12
|
+
- Support for arbitrary custom characteristics ([documentation](https://github.com/mpatfield/homebridge-easy-mqtt#custom-characteristics))
|
|
13
|
+
- JSONPath support in setter topics ([documentation](https://github.com/mpatfield/homebridge-easy-mqtt#jsonpaths))
|
|
14
|
+
- Banner image in config UI
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
- Significant under-the-hood DRY cleanup to speed future development
|
|
18
|
+
|
|
19
|
+
## 1.2.0 (2025-09-02)
|
|
20
|
+
|
|
11
21
|
### Added
|
|
12
22
|
- Security System accessory type
|
|
13
23
|
- Support for battery and status
|
|
14
24
|
|
|
15
25
|
### Fixed
|
|
16
|
-
- Allow raw strings in
|
|
26
|
+
- Allow raw strings in MQTT messages
|
|
17
27
|
- Changing accessory type cleans up old Homebridge accessories
|
|
18
28
|
|
|
19
29
|
### Changed
|
|
20
30
|
- Updated dev dependencies
|
|
21
31
|
- Significant under-the-hood refactoring to speed future development
|
|
22
|
-
- Removed in-use setter topic for outlet since it is not user
|
|
32
|
+
- Removed in-use setter topic for outlet since it is not user-modifiable
|
|
23
33
|
- Condensed and cleaned up config UI
|
|
24
34
|
- Removed optional accessory info fields as they were messy and provided little value
|
|
25
35
|
- Deprecated inconsistent LockMechanism topic names
|
package/README.md
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
<!--
|
|
2
1
|
<p align="center">
|
|
3
|
-
<img src="https://
|
|
2
|
+
<img src="https://raw.githubusercontent.com/mpatfield/homebridge-easy-mqtt/refs/heads/latest/img/banner.png" width="600">
|
|
4
3
|
</p>
|
|
5
|
-
-->
|
|
6
4
|
|
|
7
5
|
<span align="center">
|
|
8
6
|
|
|
@@ -10,6 +8,8 @@
|
|
|
10
8
|
|
|
11
9
|
Homebridge plugin to integrate simple MQTT devices into Apple HomeKit
|
|
12
10
|
|
|
11
|
+
[](https://github.com/homebridge/homebridge/wiki/Verified-Plugins)
|
|
12
|
+
[](https://discord.com/channels/432663330281226270/1412178951295467542) \
|
|
13
13
|
[](https://www.npmjs.com/package/homebridge-easy-mqtt)
|
|
14
14
|
[](https://www.npmjs.com/package/homebridge-easy-mqtt)
|
|
15
15
|
|
|
@@ -49,6 +49,15 @@ Using the Homebridge Config UI is the easiest way to set up this plugin. However
|
|
|
49
49
|
"password": "string",
|
|
50
50
|
"options": "string"
|
|
51
51
|
},
|
|
52
|
+
"customCharacteristics": [
|
|
53
|
+
{
|
|
54
|
+
"uuid": "string",
|
|
55
|
+
"name": "string",
|
|
56
|
+
"getTopic": "string",
|
|
57
|
+
"units": "string",
|
|
58
|
+
}
|
|
59
|
+
…
|
|
60
|
+
],
|
|
52
61
|
"topicGetStatusActive": "string",
|
|
53
62
|
"topicGetCurrentLockState": "string",
|
|
54
63
|
"topicGetTargetLockState": "string",
|
|
@@ -63,7 +72,7 @@ Using the Homebridge Config UI is the easiest way to set up this plugin. However
|
|
|
63
72
|
"valueOff": "string",
|
|
64
73
|
"disableLogging": false
|
|
65
74
|
}
|
|
66
|
-
|
|
75
|
+
…
|
|
67
76
|
],
|
|
68
77
|
"verbose": false,
|
|
69
78
|
"platform": "HomebridgeEasyMQTT"
|
|
@@ -187,7 +196,7 @@ You are able to pass in any arbitrary MQTT options via `mqtt.options` in the con
|
|
|
187
196
|
|
|
188
197
|
## JSONPaths
|
|
189
198
|
|
|
190
|
-
For some devices, the desired values in the MQTT messages
|
|
199
|
+
For some devices, the desired values in the MQTT messages are embedded within a JSON object. For example, here is the MQTT message for my door lock that is received when the door is locked:
|
|
191
200
|
|
|
192
201
|
```json
|
|
193
202
|
{ "time": 1750870005853, "state": 255 }
|
|
@@ -218,8 +227,68 @@ would use the topic
|
|
|
218
227
|
|
|
219
228
|
`zwave/1/door_lock/currentMode$.state.number.value`
|
|
220
229
|
|
|
230
|
+
You can do the same for the set topic if the device is expecting a JSON object rather than a raw value.
|
|
231
|
+
|
|
232
|
+
If, for example, your device is expecting this message:
|
|
233
|
+
|
|
234
|
+
```json
|
|
235
|
+
{
|
|
236
|
+
"target": "away"
|
|
237
|
+
}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
instead of just the raw value "away" you can use:
|
|
241
|
+
|
|
242
|
+
`zwave/4/security/set$.target`
|
|
243
|
+
|
|
244
|
+
Again, the `$.target` at the end tells the MQTT client to wrap the value a JSON object.
|
|
245
|
+
|
|
246
|
+
As with get topics, you can have an arbitrarily complex chain. So if, for example, you want this object:
|
|
247
|
+
|
|
248
|
+
```json
|
|
249
|
+
{
|
|
250
|
+
"target": {
|
|
251
|
+
"mode": {
|
|
252
|
+
"value": "away"
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
then you would use the topic
|
|
259
|
+
|
|
260
|
+
`zwave/4/security/set$.target.mode.value`
|
|
261
|
+
|
|
262
|
+
## Custom Characteristics
|
|
263
|
+
|
|
264
|
+
If you use a more advanced HomeKit app like [Eve](https://apps.apple.com/us/app/eve-for-matter-homekit/id917695792) or [Controller for Homekit](https://apps.apple.com/us/app/controller-for-homekit/id1198176727), you can add custom characteristics to display any arbitrary numeric information. Unfortunately, Apple currently doesn't offer a way to display this in the Home app.
|
|
265
|
+
|
|
266
|
+
<img src="https://raw.githubusercontent.com/mpatfield/homebridge-easy-mqtt/refs/heads/latest/img/screenshot_1.png">
|
|
267
|
+
|
|
268
|
+
Due to the complexity, this was intentionally left out of the plugin config UI, so this can only be configured manually.
|
|
269
|
+
|
|
270
|
+
```json
|
|
271
|
+
"customCharacteristics": [
|
|
272
|
+
{
|
|
273
|
+
"uuid": "string",
|
|
274
|
+
"name": "string",
|
|
275
|
+
"getTopic": "string",
|
|
276
|
+
"units": "string",
|
|
277
|
+
}
|
|
278
|
+
]
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
- `uuid` — A unique string (recommend using a (UUID generator)[https://www.uuidgenerator.net/])
|
|
282
|
+
- `name` — The display name for the characteristic
|
|
283
|
+
- `getTopic` — The topic which provides the numeric value
|
|
284
|
+
- `units` — (Optional) The units which will be displayed at the end of the numeric value
|
|
285
|
+
|
|
286
|
+
Since `customCharacteristics` is an array, you may define as many custom characteristics as you wish.
|
|
287
|
+
|
|
221
288
|
## Credits
|
|
222
289
|
|
|
223
290
|
[@arachnetech](https://github.com/arachnetech) for the fantastic [homebridge-mqttthing](https://github.com/arachnetech/homebridge-mqttthing) plugin which serves as the main inspiration for this project
|
|
224
291
|
|
|
292
|
+
[Keryan Belahcene](https://www.instagram.com/keryan.me) for creating the [Flume](https://github.com/homebridge-plugins/homebridge-flume) header logo which I adapted for this plugin
|
|
293
|
+
|
|
225
294
|
And to the amazing creators/contributors of [Homebridge](https://homebridge.io) who made this plugin possible!
|
package/config.schema.json
CHANGED
|
@@ -24,6 +24,22 @@
|
|
|
24
24
|
"enum": [ "Lightbulb", "LockMechanism", "Outlet", "SecuritySystem", "Switch", "TemperatureSensor"],
|
|
25
25
|
"enumNames": ["${config.enumNames.lightbulb}", "${config.enumNames.lockMechanism}", "${config.enumNames.outlet}", "${config.enumNames.securitySystem}", "${config.enumNames.switch}", "${config.enumNames.temperatureSensor}"],
|
|
26
26
|
"required": true
|
|
27
|
+
},
|
|
28
|
+
"manufacturer": {
|
|
29
|
+
"type": "string",
|
|
30
|
+
"title": "${config.title.manufacturer}"
|
|
31
|
+
},
|
|
32
|
+
"model": {
|
|
33
|
+
"type": "string",
|
|
34
|
+
"title": "${config.title.model}"
|
|
35
|
+
},
|
|
36
|
+
"serialNumber": {
|
|
37
|
+
"type": "string",
|
|
38
|
+
"title": "${config.title.serialNumber}"
|
|
39
|
+
},
|
|
40
|
+
"version": {
|
|
41
|
+
"type": "string",
|
|
42
|
+
"title": "${config.title.version}"
|
|
27
43
|
}
|
|
28
44
|
}
|
|
29
45
|
},
|
|
@@ -51,6 +67,23 @@
|
|
|
51
67
|
"placeholder": "{ \"protocolVersion\": \"4\", \"clientId\": \"my-client-id\", \"rejectUnauthorized\": true }"
|
|
52
68
|
}
|
|
53
69
|
}
|
|
70
|
+
},
|
|
71
|
+
"customCharacteristic": {
|
|
72
|
+
"type": "object",
|
|
73
|
+
"properties": {
|
|
74
|
+
"uuid": {
|
|
75
|
+
"type": "string"
|
|
76
|
+
},
|
|
77
|
+
"name": {
|
|
78
|
+
"type": "string"
|
|
79
|
+
},
|
|
80
|
+
"getTopic": {
|
|
81
|
+
"type": "string"
|
|
82
|
+
},
|
|
83
|
+
"units": {
|
|
84
|
+
"type": "string"
|
|
85
|
+
}
|
|
86
|
+
}
|
|
54
87
|
}
|
|
55
88
|
},
|
|
56
89
|
"properties": {
|
|
@@ -65,6 +98,10 @@
|
|
|
65
98
|
"properties": {
|
|
66
99
|
"info": { "$ref": "#/definitions/info" },
|
|
67
100
|
"mqtt": { "$ref": "#/definitions/mqtt" },
|
|
101
|
+
"customCharacteristics": {
|
|
102
|
+
"type": "array",
|
|
103
|
+
"items": { "$ref": "#/definitions/customCharacteristic" }
|
|
104
|
+
},
|
|
68
105
|
"temperatureUnits": {
|
|
69
106
|
"type": "string",
|
|
70
107
|
"enum": ["C", "F"],
|
|
@@ -4,11 +4,7 @@ import { CharacteristicType, BaseAccessoryConfig, ServiceType } from '../../mode
|
|
|
4
4
|
import { Log } from '../../tools/log.js';
|
|
5
5
|
export declare abstract class BaseAccessory<C extends BaseAccessoryConfig = BaseAccessoryConfig> extends MQTTAccessory<C> {
|
|
6
6
|
constructor(Service: ServiceType, Characteristic: CharacteristicType, accessory: PlatformAccessory, config: C, log: Log);
|
|
7
|
-
|
|
8
|
-
private getBatteryLevel;
|
|
9
|
-
private getBatteryLow;
|
|
10
|
-
private getStatusActive;
|
|
11
|
-
private onBatteryLevelUpdate;
|
|
7
|
+
private handleCustomCharacteristics;
|
|
12
8
|
private onBatteryLowUpdate;
|
|
13
9
|
private onStatusActiveUpdate;
|
|
14
10
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { MQTTAccessory } from './mqtt.js';
|
|
2
|
+
import { CustomCharacteristic } from './customCharacteristic.js';
|
|
2
3
|
import { PLATFORM_NAME } from '../../homebridge/settings.js';
|
|
3
4
|
import { strings } from '../../i18n/i18n.js';
|
|
4
5
|
import { CharacteristicKey } from '../../model/enums.js';
|
|
@@ -10,35 +11,30 @@ export class BaseAccessory extends MQTTAccessory {
|
|
|
10
11
|
accessory.getService(Service.AccessoryInformation)
|
|
11
12
|
.setCharacteristic(Characteristic.Name, config.info.name)
|
|
12
13
|
.setCharacteristic(Characteristic.ConfiguredName, config.info.name)
|
|
13
|
-
.setCharacteristic(Characteristic.Manufacturer, PLATFORM_NAME)
|
|
14
|
-
.setCharacteristic(Characteristic.Model, config.info.type)
|
|
15
|
-
.setCharacteristic(Characteristic.SerialNumber, config.info.id)
|
|
16
|
-
.setCharacteristic(Characteristic.FirmwareRevision, getVersion());
|
|
17
|
-
this.
|
|
18
|
-
this.
|
|
19
|
-
this.
|
|
20
|
-
this.
|
|
21
|
-
this.set(CharacteristicKey.StatusActive, true);
|
|
22
|
-
this.bind(Characteristic.StatusActive, 'topicGetStatusActive', this.getStatusActive.bind(this));
|
|
14
|
+
.setCharacteristic(Characteristic.Manufacturer, config.info.manufacturer ?? PLATFORM_NAME)
|
|
15
|
+
.setCharacteristic(Characteristic.Model, config.info.model ?? config.info.type)
|
|
16
|
+
.setCharacteristic(Characteristic.SerialNumber, config.info.serialNumber ?? config.info.id)
|
|
17
|
+
.setCharacteristic(Characteristic.FirmwareRevision, config.info.version ?? getVersion());
|
|
18
|
+
this.setupCharacteristic(CharacteristicKey.BatteryLevel, 100, 'topicGetBatteryLevel', this.bindOnUpdateNumeric(CharacteristicKey.BatteryLevel, strings.accessory.batteryLevel), false);
|
|
19
|
+
this.setupCharacteristic(CharacteristicKey.StatusLowBattery, false, 'topicGetBatteryLow', this.onBatteryLowUpdate.bind(this), false);
|
|
20
|
+
this.setupCharacteristic(CharacteristicKey.StatusActive, true, 'topicGetStatusActive', this.onStatusActiveUpdate.bind(this), false);
|
|
21
|
+
this.handleCustomCharacteristics();
|
|
23
22
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
if (this.assertNumber(value, strings.accessory.badBatteryLevel)) {
|
|
40
|
-
const logString = strings.accessory.batteryLevel.replace('%d', `${value.toString()}%`);
|
|
41
|
-
this.onUpdate(CharacteristicKey.BatteryLevel, value, logString); // TODO not working
|
|
23
|
+
handleCustomCharacteristics() {
|
|
24
|
+
const keepUUIDs = new Set(Object.values(CharacteristicKey).map((key) => this.Characteristic[key].UUID));
|
|
25
|
+
const toRemove = this.accessoryService.characteristics.filter((characteristic) => !keepUUIDs.has(characteristic.UUID));
|
|
26
|
+
for (const characteristic of toRemove) {
|
|
27
|
+
characteristic.updateValue(null);
|
|
28
|
+
this.accessoryService.removeCharacteristic(characteristic);
|
|
29
|
+
}
|
|
30
|
+
if (!this.config.customCharacteristics) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
for (const config of this.config.customCharacteristics) {
|
|
34
|
+
const customChar = CustomCharacteristic.create(this.accessoryService, this.Characteristic, config, this.name, this.log, this.config.disableLogging);
|
|
35
|
+
if (customChar !== undefined) {
|
|
36
|
+
this.addTopicHandler(customChar.topic, customChar.onUpdateHandler);
|
|
37
|
+
}
|
|
42
38
|
}
|
|
43
39
|
}
|
|
44
40
|
async onBatteryLowUpdate(topic, value) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base.js","sourceRoot":"","sources":["../../../src/accessory/abstract/base.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE1C,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAE7D,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAE7C,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAGzD,OAAO,EAAO,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,UAAU,MAAM,wBAAwB,CAAC;AAEhD,MAAM,OAAgB,aAAmE,SAAQ,aAAgB;IAE/G,YAAY,OAAoB,EAAE,cAAkC,EAAE,SAA4B,EAAE,MAAS,EAAE,GAAQ;QACrH,KAAK,CAAC,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC;QAEvD,SAAS,CAAC,UAAU,CAAC,OAAO,CAAC,oBAAoB,CAAE;aAChD,iBAAiB,CAAC,cAAc,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;aACxD,iBAAiB,CAAC,cAAc,CAAC,cAAc,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;aAClE,iBAAiB,CAAC,cAAc,CAAC,YAAY,EAAE,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"base.js","sourceRoot":"","sources":["../../../src/accessory/abstract/base.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { PrimitiveTypes, Service } from 'homebridge';
|
|
2
|
+
import { CharacteristicType, CustomCharacteristicConfig } from '../../model/types.js';
|
|
3
|
+
import { Log } from '../../tools/log.js';
|
|
4
|
+
export declare class CustomCharacteristic {
|
|
5
|
+
private readonly config;
|
|
6
|
+
private readonly caller;
|
|
7
|
+
private readonly log;
|
|
8
|
+
private readonly disableLogging;
|
|
9
|
+
private characteristic;
|
|
10
|
+
private value;
|
|
11
|
+
static create(Service: Service, Characteristic: CharacteristicType, config: CustomCharacteristicConfig, caller: string, log: Log, disableLogging: boolean): CustomCharacteristic | undefined;
|
|
12
|
+
private constructor();
|
|
13
|
+
get topic(): string;
|
|
14
|
+
get onUpdateHandler(): (topic: string, value: PrimitiveTypes) => Promise<void>;
|
|
15
|
+
private getValue;
|
|
16
|
+
private onValueUpdate;
|
|
17
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { strings } from '../../i18n/i18n.js';
|
|
2
|
+
import { assert } from '../../tools/validation.js';
|
|
3
|
+
export class CustomCharacteristic {
|
|
4
|
+
config;
|
|
5
|
+
caller;
|
|
6
|
+
log;
|
|
7
|
+
disableLogging;
|
|
8
|
+
characteristic;
|
|
9
|
+
value = null;
|
|
10
|
+
static create(Service, Characteristic, config, caller, log, disableLogging) {
|
|
11
|
+
if (!assert(log, config.name ?? CustomCharacteristic.name, config, 'name', 'uuid', 'getTopic')) {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
return new CustomCharacteristic(Service, Characteristic, config, caller, log, disableLogging);
|
|
15
|
+
}
|
|
16
|
+
constructor(Service, Characteristic, config, caller, log, disableLogging) {
|
|
17
|
+
this.config = config;
|
|
18
|
+
this.caller = caller;
|
|
19
|
+
this.log = log;
|
|
20
|
+
this.disableLogging = disableLogging;
|
|
21
|
+
if (Service.testCharacteristic(config.name)) {
|
|
22
|
+
this.characteristic = Service.getCharacteristic(config.name);
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
const customCharacteristic = class CustomCharacteristic extends Characteristic {
|
|
26
|
+
constructor() {
|
|
27
|
+
super(config.name, config.uuid, {
|
|
28
|
+
format: "uint32" /* Formats.UINT32 */,
|
|
29
|
+
perms: ["pr" /* Perms.PAIRED_READ */, "ev" /* Perms.NOTIFY */],
|
|
30
|
+
unit: config.units,
|
|
31
|
+
});
|
|
32
|
+
this.value = this.getDefaultValue();
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
this.characteristic = Service.addCharacteristic(customCharacteristic);
|
|
36
|
+
this.characteristic.UUID = config.uuid;
|
|
37
|
+
}
|
|
38
|
+
this.characteristic.onGet(this.getValue.bind(this));
|
|
39
|
+
}
|
|
40
|
+
get topic() {
|
|
41
|
+
return this.config.getTopic;
|
|
42
|
+
}
|
|
43
|
+
get onUpdateHandler() {
|
|
44
|
+
return this.onValueUpdate.bind(this);
|
|
45
|
+
}
|
|
46
|
+
async getValue() {
|
|
47
|
+
return this.value;
|
|
48
|
+
}
|
|
49
|
+
async onValueUpdate(topic, value) {
|
|
50
|
+
if (typeof value !== 'number') {
|
|
51
|
+
this.log.error(strings.characteristic.badValue, this.caller, this.characteristic.displayName, `'${value}'`);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (value === this.value) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
this.value = value;
|
|
58
|
+
this.characteristic.updateValue(this.value);
|
|
59
|
+
if (this.disableLogging) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
this.log.always(strings.characteristic.updated, this.caller, this.characteristic.displayName, `'${value}'`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=customCharacteristic.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"customCharacteristic.js","sourceRoot":"","sources":["../../../src/accessory/abstract/customCharacteristic.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { Characteristic,
|
|
1
|
+
import { Characteristic, CharacteristicSetHandler, CharacteristicValue, PlatformAccessory, PrimitiveTypes, Service } from 'homebridge';
|
|
2
2
|
import { CharacteristicKey } from '../../model/enums.js';
|
|
3
3
|
import { CharacteristicType, MQTTAccessoryConfig, ServiceType } from '../../model/types.js';
|
|
4
4
|
import { Log, LogType } from '../../tools/log.js';
|
|
5
|
+
type OnUpdateHandler = (topic: string, value: PrimitiveTypes) => (Promise<void>);
|
|
5
6
|
export declare abstract class MQTTAccessory<C extends MQTTAccessoryConfig> {
|
|
6
7
|
protected readonly Service: ServiceType;
|
|
7
8
|
protected readonly Characteristic: CharacteristicType;
|
|
@@ -13,24 +14,20 @@ export declare abstract class MQTTAccessory<C extends MQTTAccessoryConfig> {
|
|
|
13
14
|
private readonly topicHandlers;
|
|
14
15
|
protected readonly accessoryService: Service;
|
|
15
16
|
constructor(Service: ServiceType, Characteristic: CharacteristicType, accessory: PlatformAccessory, config: C, log: Log);
|
|
16
|
-
private onMQTTConnect;
|
|
17
17
|
protected abstract getAccessoryService(): Service;
|
|
18
|
-
protected bind(constructor: WithUUID<{
|
|
19
|
-
new (): Characteristic;
|
|
20
|
-
}>, getTopic: keyof C, getHandler: CharacteristicGetHandler, setTopic?: keyof C | undefined, setHandler?: CharacteristicSetHandler | undefined): void;
|
|
21
|
-
protected abstract addTopicHandlers(): void;
|
|
22
|
-
protected addTopicHandler(topicKey: keyof C, handler: (topic: string, value: PrimitiveTypes) => Promise<void>, assert?: boolean): void;
|
|
23
18
|
protected get name(): string;
|
|
19
|
+
protected setupCharacteristic(characteristicKey: CharacteristicKey, defaultValue: CharacteristicValue, getTopicKey: keyof C, onUpdateHandler: OnUpdateHandler, assertGetTopic: boolean, setTopicKey?: keyof C | undefined, onSetHandler?: CharacteristicSetHandler | undefined): Characteristic | undefined;
|
|
20
|
+
protected bindOnUpdateNumeric(charKey: CharacteristicKey, logTemplate: string): OnUpdateHandler;
|
|
21
|
+
protected bindOnUpdateNumericBoolean(charKey: CharacteristicKey, valueKey: keyof C, logTrue: string, logFalse: string): OnUpdateHandler;
|
|
22
|
+
protected addTopicHandler(topic: string, handler: (topic: string, value: PrimitiveTypes) => Promise<void>): void;
|
|
24
23
|
protected getRawValue(property: keyof C, assert?: boolean): string | undefined;
|
|
25
24
|
protected getPrimitiveValue(property: keyof C, assert?: boolean): PrimitiveTypes | undefined;
|
|
26
25
|
protected publish(topic: string, value: PrimitiveTypes): void;
|
|
27
26
|
teardown(): void;
|
|
28
|
-
protected get(key: CharacteristicKey): CharacteristicValue;
|
|
29
|
-
protected set(key: CharacteristicKey, value: CharacteristicValue): void;
|
|
30
27
|
protected assert(...keys: (keyof C)[]): boolean;
|
|
31
|
-
protected assertNumber(value: PrimitiveTypes, error: string): boolean;
|
|
32
28
|
protected onUpdate(key: CharacteristicKey, value: CharacteristicValue, logString?: string | undefined): boolean;
|
|
33
29
|
protected onSet(key: CharacteristicKey, value: CharacteristicValue, publish: PrimitiveTypes, topic: keyof C, logString: string | undefined): void;
|
|
34
30
|
protected logIfDesired(message: string, ...parameters: string[]): void;
|
|
35
31
|
protected logIfDesired(level: LogType, message: string, ...parameters: string[]): void;
|
|
36
32
|
}
|
|
33
|
+
export {};
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import { strings } from '../../i18n/i18n.js';
|
|
1
2
|
import { AccessoryType } from '../../model/enums.js';
|
|
2
3
|
import { MQTT } from '../../model/mqtt.js';
|
|
3
4
|
import { LogType } from '../../tools/log.js';
|
|
4
|
-
import { assert } from '../../tools/validation.js';
|
|
5
5
|
import { toPrimitive } from '../../tools/primitive.js';
|
|
6
|
+
import { assert } from '../../tools/validation.js';
|
|
6
7
|
export class MQTTAccessory {
|
|
7
8
|
Service;
|
|
8
9
|
Characteristic;
|
|
@@ -10,7 +11,7 @@ export class MQTTAccessory {
|
|
|
10
11
|
config;
|
|
11
12
|
log;
|
|
12
13
|
mqttClient;
|
|
13
|
-
properties =
|
|
14
|
+
properties = new Map();
|
|
14
15
|
topicHandlers = [];
|
|
15
16
|
accessoryService;
|
|
16
17
|
constructor(Service, Characteristic, accessory, config, log) {
|
|
@@ -21,7 +22,11 @@ export class MQTTAccessory {
|
|
|
21
22
|
this.log = log;
|
|
22
23
|
const name = config.info.name;
|
|
23
24
|
if (this.assert('mqtt')) {
|
|
24
|
-
this.mqttClient = new MQTT(log, config.mqtt,
|
|
25
|
+
this.mqttClient = new MQTT(log, config.mqtt, () => {
|
|
26
|
+
this.topicHandlers.forEach(topicHandler => {
|
|
27
|
+
this.mqttClient?.subscribe(topicHandler.topic, topicHandler.handler);
|
|
28
|
+
});
|
|
29
|
+
}, name);
|
|
25
30
|
this.mqttClient.connect();
|
|
26
31
|
}
|
|
27
32
|
this.accessoryService = this.getAccessoryService();
|
|
@@ -31,46 +36,61 @@ export class MQTTAccessory {
|
|
|
31
36
|
accessory.removeService(existingService);
|
|
32
37
|
}
|
|
33
38
|
}
|
|
34
|
-
this.addTopicHandlers();
|
|
35
39
|
}
|
|
36
|
-
|
|
37
|
-
this.
|
|
38
|
-
this.mqttClient?.subscribe(topicHandler.topic, topicHandler.handler);
|
|
39
|
-
});
|
|
40
|
+
get name() {
|
|
41
|
+
return this.config.info.name;
|
|
40
42
|
}
|
|
41
|
-
|
|
42
|
-
if (
|
|
43
|
+
setupCharacteristic(characteristicKey, defaultValue, getTopicKey, onUpdateHandler, assertGetTopic, setTopicKey = undefined, onSetHandler = undefined) {
|
|
44
|
+
if (!getTopicKey.toString().startsWith('topic')) {
|
|
45
|
+
throw new Error(`Trying to fetch topic with unexpected property name '${getTopicKey.toString()}'`);
|
|
46
|
+
}
|
|
47
|
+
if (assertGetTopic) {
|
|
48
|
+
this.assert(getTopicKey);
|
|
49
|
+
}
|
|
50
|
+
if (this.config[getTopicKey] === undefined) {
|
|
43
51
|
for (const characteristic of this.accessoryService.characteristics) {
|
|
44
|
-
if (characteristic.UUID ===
|
|
52
|
+
if (characteristic.UUID === this.Characteristic[characteristicKey].UUID) {
|
|
45
53
|
this.accessoryService.removeCharacteristic(characteristic);
|
|
46
54
|
break;
|
|
47
55
|
}
|
|
48
56
|
}
|
|
49
57
|
return;
|
|
50
58
|
}
|
|
51
|
-
const characteristic = this.accessoryService.getCharacteristic(
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
59
|
+
const characteristic = this.accessoryService.getCharacteristic(this.Characteristic[characteristicKey]);
|
|
60
|
+
this.properties.set(characteristicKey, defaultValue);
|
|
61
|
+
characteristic.onGet(async () => {
|
|
62
|
+
return this.properties.get(characteristicKey) ?? null;
|
|
63
|
+
});
|
|
64
|
+
this.addTopicHandler(this.config[getTopicKey], onUpdateHandler);
|
|
65
|
+
if (setTopicKey !== undefined) {
|
|
66
|
+
if (!setTopicKey.toString().startsWith('topic')) {
|
|
67
|
+
throw new Error(`Trying to fetch topic with unexpected property name '${setTopicKey.toString()}'`);
|
|
68
|
+
}
|
|
69
|
+
if (!onSetHandler) {
|
|
70
|
+
throw new Error(`Missing onSetHandler for topic '${setTopicKey.toString()}'`);
|
|
71
|
+
}
|
|
72
|
+
characteristic.onSet(onSetHandler);
|
|
57
73
|
}
|
|
74
|
+
return characteristic;
|
|
58
75
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
this.topicHandlers.push({ topic: topic, handler });
|
|
76
|
+
bindOnUpdateNumeric(charKey, logTemplate) {
|
|
77
|
+
return (async (_topic, value) => {
|
|
78
|
+
if (typeof value !== 'number') {
|
|
79
|
+
this.log.error(strings.accessory.badNumericValue, this.name, charKey, `'${value.toString()}'`);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
const logString = logTemplate.replace('%d', value.toString());
|
|
83
|
+
this.onUpdate(charKey, value, logString);
|
|
84
|
+
}).bind(this);
|
|
71
85
|
}
|
|
72
|
-
|
|
73
|
-
return
|
|
86
|
+
bindOnUpdateNumericBoolean(charKey, valueKey, logTrue, logFalse) {
|
|
87
|
+
return (async (_topic, value) => {
|
|
88
|
+
const numeric = value === this.getPrimitiveValue(valueKey) ? 1 : 0;
|
|
89
|
+
this.onUpdate(charKey, numeric, numeric ? logTrue : logFalse);
|
|
90
|
+
}).bind(this);
|
|
91
|
+
}
|
|
92
|
+
addTopicHandler(topic, handler) {
|
|
93
|
+
this.topicHandlers.push({ topic: topic, handler: handler });
|
|
74
94
|
}
|
|
75
95
|
getRawValue(property, assert = true) {
|
|
76
96
|
if (!property.toString().startsWith('value')) {
|
|
@@ -91,27 +111,14 @@ export class MQTTAccessory {
|
|
|
91
111
|
teardown() {
|
|
92
112
|
this.mqttClient?.teardown();
|
|
93
113
|
}
|
|
94
|
-
get(key) {
|
|
95
|
-
return this.properties[key];
|
|
96
|
-
}
|
|
97
|
-
set(key, value) {
|
|
98
|
-
this.properties[key] = value;
|
|
99
|
-
}
|
|
100
114
|
assert(...keys) {
|
|
101
115
|
return assert(this.log, this.name, this.config, ...keys);
|
|
102
116
|
}
|
|
103
|
-
assertNumber(value, error) {
|
|
104
|
-
if (typeof value === 'number') {
|
|
105
|
-
return true;
|
|
106
|
-
}
|
|
107
|
-
this.log.error(error, this.name, `'${value}'`);
|
|
108
|
-
return false;
|
|
109
|
-
}
|
|
110
117
|
onUpdate(key, value, logString = undefined) {
|
|
111
|
-
if (value === this.get(key)) {
|
|
118
|
+
if (value === this.properties.get(key)) {
|
|
112
119
|
return false;
|
|
113
120
|
}
|
|
114
|
-
this.set(key, value);
|
|
121
|
+
this.properties.set(key, value);
|
|
115
122
|
this.accessoryService.updateCharacteristic(this.Characteristic[key], value);
|
|
116
123
|
if (logString) {
|
|
117
124
|
this.logIfDesired(logString);
|
|
@@ -122,10 +129,10 @@ export class MQTTAccessory {
|
|
|
122
129
|
if (!this.assert(topic)) {
|
|
123
130
|
return;
|
|
124
131
|
}
|
|
125
|
-
if (logString && value !== this.get(key)) {
|
|
132
|
+
if (logString && value !== this.properties.get(key)) {
|
|
126
133
|
this.logIfDesired(logString);
|
|
127
134
|
}
|
|
128
|
-
this.set(key, value);
|
|
135
|
+
this.properties.set(key, value);
|
|
129
136
|
this.accessoryService.updateCharacteristic(this.Characteristic[key], value);
|
|
130
137
|
this.publish(this.config[topic], publish);
|
|
131
138
|
}
|