homebridge-easy-mqtt 1.0.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.
Files changed (62) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/LICENSE +176 -0
  3. package/README.md +176 -0
  4. package/config.schema.json +326 -0
  5. package/dist/accessory/base.d.ts +21 -0
  6. package/dist/accessory/base.js +47 -0
  7. package/dist/accessory/base.js.map +1 -0
  8. package/dist/accessory/lock.d.ts +25 -0
  9. package/dist/accessory/lock.js +159 -0
  10. package/dist/accessory/lock.js.map +1 -0
  11. package/dist/accessory/switch.d.ts +19 -0
  12. package/dist/accessory/switch.js +90 -0
  13. package/dist/accessory/switch.js.map +1 -0
  14. package/dist/homebridge/index.d.ts +3 -0
  15. package/dist/homebridge/index.js +6 -0
  16. package/dist/homebridge/index.js.map +1 -0
  17. package/dist/homebridge/platform.d.ts +13 -0
  18. package/dist/homebridge/platform.js +87 -0
  19. package/dist/homebridge/platform.js.map +1 -0
  20. package/dist/homebridge/settings.d.ts +2 -0
  21. package/dist/homebridge/settings.js +3 -0
  22. package/dist/homebridge/settings.js.map +1 -0
  23. package/dist/homebridge-ui/public/index.html +20 -0
  24. package/dist/homebridge-ui/public/ui.js +1 -0
  25. package/dist/homebridge-ui/server.d.ts +1 -0
  26. package/dist/homebridge-ui/server.js +15 -0
  27. package/dist/homebridge-ui/server.js.map +1 -0
  28. package/dist/i18n/en.d.ts +88 -0
  29. package/dist/i18n/en.js +94 -0
  30. package/dist/i18n/en.js.map +1 -0
  31. package/dist/i18n/i18n.d.ts +97 -0
  32. package/dist/i18n/i18n.js +38 -0
  33. package/dist/i18n/i18n.js.map +1 -0
  34. package/dist/i18n/template.d.ts +88 -0
  35. package/dist/i18n/template.js +8 -0
  36. package/dist/i18n/template.js.map +1 -0
  37. package/dist/i18n/zz.d.ts +88 -0
  38. package/dist/i18n/zz.js +6 -0
  39. package/dist/i18n/zz.js.map +1 -0
  40. package/dist/model/mqtt.d.ts +25 -0
  41. package/dist/model/mqtt.js +158 -0
  42. package/dist/model/mqtt.js.map +1 -0
  43. package/dist/model/types.d.ts +38 -0
  44. package/dist/model/types.js +18 -0
  45. package/dist/model/types.js.map +1 -0
  46. package/dist/tools/log.d.ts +16 -0
  47. package/dist/tools/log.js +45 -0
  48. package/dist/tools/log.js.map +1 -0
  49. package/dist/tools/storage.d.ts +2 -0
  50. package/dist/tools/storage.js +24 -0
  51. package/dist/tools/storage.js.map +1 -0
  52. package/dist/tools/time.d.ts +4 -0
  53. package/dist/tools/time.js +5 -0
  54. package/dist/tools/time.js.map +1 -0
  55. package/dist/tools/validation.d.ts +3 -0
  56. package/dist/tools/validation.js +13 -0
  57. package/dist/tools/validation.js.map +1 -0
  58. package/dist/tools/version.d.ts +1 -0
  59. package/dist/tools/version.js +18 -0
  60. package/dist/tools/version.js.map +1 -0
  61. package/eslint.config.ts +35 -0
  62. package/package.json +68 -0
@@ -0,0 +1,158 @@
1
+ import mqtt from 'mqtt';
2
+ import { toPrimitive } from './types.js';
3
+ import { strings } from '../i18n/i18n.js';
4
+ import { LogType } from '../tools/log.js';
5
+ import { SECOND, MINUTE } from '../tools/time.js';
6
+ import { assert } from '../tools/validation.js';
7
+ const DELAYS = [5 * SECOND, 10 * SECOND, 15 * SECOND, 30 * SECOND, MINUTE, 2 * MINUTE];
8
+ const IDLE_CONNECTION_TIMER_INTERVAL = 5 * MINUTE;
9
+ class MQTTListener {
10
+ handler;
11
+ topic;
12
+ jsonPath;
13
+ constructor(topic, handler) {
14
+ this.handler = handler;
15
+ const index = topic.indexOf('$');
16
+ if (index === -1) {
17
+ this.topic = topic;
18
+ this.jsonPath = [];
19
+ }
20
+ else {
21
+ this.topic = topic.slice(0, index);
22
+ this.jsonPath = topic.slice(index).replace(/^\$?\.?/, '').split('.');
23
+ }
24
+ }
25
+ }
26
+ export class MQTT {
27
+ log;
28
+ config;
29
+ onConnect;
30
+ caller;
31
+ client = null;
32
+ shouldReconnect = false;
33
+ isReconnecting = false;
34
+ reconnectCount = 0;
35
+ idleMQTTTimer = null;
36
+ listeners = new Map();
37
+ constructor(log, config, onConnect, caller) {
38
+ this.log = log;
39
+ this.config = config;
40
+ this.onConnect = onConnect;
41
+ this.caller = caller;
42
+ }
43
+ teardown() {
44
+ this.shouldReconnect = false;
45
+ if (this.client) {
46
+ this.client.end(true);
47
+ this.client = null;
48
+ }
49
+ }
50
+ connect() {
51
+ if (!assert(this.log, this.caller, this.config, 'broker')) {
52
+ return;
53
+ }
54
+ this.shouldReconnect = true;
55
+ let additionalOptions = {};
56
+ try {
57
+ if (this.config.options) {
58
+ additionalOptions = JSON.parse(this.config.options);
59
+ }
60
+ }
61
+ catch (err) {
62
+ this.log.error(`${strings.mqtt.badOptions}:\n"${this.config.options}"`, this.caller);
63
+ }
64
+ const options = {
65
+ ...additionalOptions,
66
+ reconnectPeriod: 0,
67
+ };
68
+ this.client = mqtt.connect(this.config.broker, options);
69
+ this.client.on('connect', () => {
70
+ this.log.ifVerbose(strings.mqtt.connected, this.caller);
71
+ this.onConnect();
72
+ });
73
+ this.client.on('message', (topic, message) => this.messageReceived(topic, message.toString()));
74
+ this.client.on('close', () => this.connectionClosed());
75
+ this.client.on('error', (error) => {
76
+ this.log.ifVerbose(LogType.WARNING, strings.mqtt.clientError, this.caller, error);
77
+ });
78
+ }
79
+ subscribe(topic, handler) {
80
+ if (!this.client) {
81
+ this.log.error(strings.mqtt.notConnected, this.caller);
82
+ return;
83
+ }
84
+ const mqttListener = new MQTTListener(topic, handler);
85
+ this.client.subscribe(mqttListener.topic);
86
+ this.listeners.set(mqttListener.topic, mqttListener);
87
+ }
88
+ publish(topic, value) {
89
+ if (!this.client || !this.client.connected) {
90
+ this.log.error(strings.mqtt.notConnected, this.caller);
91
+ return;
92
+ }
93
+ this.client.publish(topic, value.toString());
94
+ this.log.ifVerbose(strings.mqtt.publish, this.caller, value, topic);
95
+ }
96
+ messageReceived(topic, message) {
97
+ this.reconnectCount = 0;
98
+ this.resetIdleMQTTTimer();
99
+ try {
100
+ this.log.ifVerbose(strings.mqtt.receivedMessage, this.caller, topic, message);
101
+ const listener = this.listeners.get(topic);
102
+ if (!listener) {
103
+ this.log.ifVerbose(strings.mqtt.noListeners, this.caller, topic);
104
+ return;
105
+ }
106
+ let value = JSON.parse(message);
107
+ for (const pathPart of listener.jsonPath) {
108
+ if (value && typeof value === 'object' && pathPart in value) {
109
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
110
+ value = value[pathPart];
111
+ }
112
+ else {
113
+ value = undefined;
114
+ break;
115
+ }
116
+ }
117
+ listener.handler(topic, toPrimitive(value));
118
+ }
119
+ catch (e) {
120
+ this.log.error(strings.mqtt.parseFailed, this.caller, message);
121
+ }
122
+ }
123
+ connectionClosed() {
124
+ this.log.ifVerbose(strings.mqtt.connectionClosed, this.caller);
125
+ this.reconnect();
126
+ }
127
+ resetIdleMQTTTimer() {
128
+ if (this.idleMQTTTimer) {
129
+ clearTimeout(this.idleMQTTTimer);
130
+ }
131
+ this.idleMQTTTimer = setTimeout(() => {
132
+ this.log.ifVerbose(strings.mqtt.idleConnection, this.caller);
133
+ this.reconnect();
134
+ }, IDLE_CONNECTION_TIMER_INTERVAL);
135
+ }
136
+ async reconnect() {
137
+ if (!this.shouldReconnect || this.isReconnecting) {
138
+ return;
139
+ }
140
+ this.isReconnecting = true;
141
+ if (this.client) {
142
+ this.client.end(true);
143
+ this.client = null;
144
+ }
145
+ const reconnectDelay = DELAYS[Math.min(this.reconnectCount, DELAYS.length - 1)];
146
+ if (reconnectDelay < MINUTE) {
147
+ this.log.ifVerbose(strings.mqtt.reconnectInSeconds, this.caller, reconnectDelay / SECOND);
148
+ }
149
+ else {
150
+ this.log.ifVerbose(strings.mqtt.reconnectInMinutes, this.caller, reconnectDelay / MINUTE);
151
+ }
152
+ setTimeout(() => {
153
+ this.isReconnecting = false;
154
+ this.connect();
155
+ }, reconnectDelay);
156
+ }
157
+ }
158
+ //# sourceMappingURL=mqtt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mqtt.js","sourceRoot":"","sources":["../../src/model/mqtt.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ export type Primitive = string | number | boolean;
2
+ export declare function toPrimitive(value: any): Primitive;
3
+ export type Assertable = {};
4
+ export type InfoConfig = Assertable & {
5
+ name: string;
6
+ type: string;
7
+ manufacturer?: string;
8
+ model?: string;
9
+ serialNumber?: string;
10
+ version?: string;
11
+ };
12
+ export type MQTTConfig = Assertable & {
13
+ broker: string;
14
+ options?: string;
15
+ };
16
+ export type AccessoryConfig = Assertable & {
17
+ mqtt: MQTTConfig;
18
+ info: InfoConfig;
19
+ disableLogging: boolean;
20
+ };
21
+ export type LockConfig = Assertable & AccessoryConfig & {
22
+ topicGetCurrent: string;
23
+ topicGetTarget: string;
24
+ topicSetTarget: string;
25
+ topicGetActive?: string;
26
+ valueSecured: string;
27
+ valueUnsecured: string;
28
+ valueJammed?: string;
29
+ valueActive?: string;
30
+ };
31
+ export type SwitchConfig = Assertable & AccessoryConfig & {
32
+ topicGetOn: string;
33
+ topicSetOn: string;
34
+ topicGetActive?: string;
35
+ valueOn: string;
36
+ valueOff: string;
37
+ valueActive?: string;
38
+ };
@@ -0,0 +1,18 @@
1
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
2
+ export function toPrimitive(value) {
3
+ if (typeof value === 'boolean' || typeof value === 'number') {
4
+ return value;
5
+ }
6
+ if (value === 'true') {
7
+ return true;
8
+ }
9
+ if (value === 'false') {
10
+ return false;
11
+ }
12
+ const num = Number(value);
13
+ if (!isNaN(num) && value.trim() !== '') {
14
+ return num;
15
+ }
16
+ return value;
17
+ }
18
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/model/types.ts"],"names":[],"mappings":"AAEA,8DAA8D;AAC9D,MAAM,UAAU,WAAW,CAAC,KAAU;IAEpC,IAAI,OAAO,KAAK,KAAK,SAAS,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC5D,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,KAAK,KAAK,MAAM,EAAE,CAAC;QACrB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,KAAK,KAAK,OAAO,EAAE,CAAC;QACtB,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAC1B,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QACvC,OAAO,GAAG,CAAC;IACb,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,16 @@
1
+ import { Logger } from 'homebridge';
2
+ export declare enum LogType {
3
+ ALWAYS = 0,
4
+ WARNING = 1,
5
+ ERROR = 2
6
+ }
7
+ export declare class Log {
8
+ private readonly logger;
9
+ readonly verbose: boolean;
10
+ constructor(logger: Logger, verbose: boolean);
11
+ always(message: string, ...parameters: any[]): void;
12
+ warning(message: string, ...parameters: any[]): void;
13
+ error(message: string, ...parameters: any[]): void;
14
+ ifVerbose(message: string, ...parameters: any[]): void;
15
+ ifVerbose(level: LogType, message: string, ...parameters: any[]): void;
16
+ }
@@ -0,0 +1,45 @@
1
+ export var LogType;
2
+ (function (LogType) {
3
+ LogType[LogType["ALWAYS"] = 0] = "ALWAYS";
4
+ LogType[LogType["WARNING"] = 1] = "WARNING";
5
+ LogType[LogType["ERROR"] = 2] = "ERROR";
6
+ })(LogType || (LogType = {}));
7
+ export class Log {
8
+ logger;
9
+ verbose;
10
+ constructor(logger, verbose) {
11
+ this.logger = logger;
12
+ this.verbose = verbose;
13
+ }
14
+ always(message, ...parameters) {
15
+ this.logger.info(message, ...parameters);
16
+ }
17
+ warning(message, ...parameters) {
18
+ this.logger.warn(message, ...parameters);
19
+ }
20
+ error(message, ...parameters) {
21
+ this.logger.error(message, ...parameters);
22
+ }
23
+ ifVerbose(levelOrMessage, ...rest) {
24
+ if (!this.verbose) {
25
+ return;
26
+ }
27
+ if (typeof levelOrMessage === 'string') {
28
+ this.always(levelOrMessage, ...rest);
29
+ return;
30
+ }
31
+ const [message, ...parameters] = rest;
32
+ switch (levelOrMessage) {
33
+ case LogType.ALWAYS:
34
+ this.always(message, ...parameters);
35
+ break;
36
+ case LogType.WARNING:
37
+ this.warning(message, ...parameters);
38
+ break;
39
+ case LogType.ERROR:
40
+ this.error(message, ...parameters);
41
+ break;
42
+ }
43
+ }
44
+ }
45
+ //# sourceMappingURL=log.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log.js","sourceRoot":"","sources":["../../src/tools/log.ts"],"names":[],"mappings":"AAGA,MAAM,CAAN,IAAY,OAIX;AAJD,WAAY,OAAO;IACjB,yCAAM,CAAA;IACN,2CAAO,CAAA;IACP,uCAAK,CAAA;AACP,CAAC,EAJW,OAAO,KAAP,OAAO,QAIlB;AAED,MAAM,OAAO,GAAG;IAGK;IACD;IAFlB,YACmB,MAAc,EACf,OAAgB;QADf,WAAM,GAAN,MAAM,CAAQ;QACf,YAAO,GAAP,OAAO,CAAS;IAC/B,CAAC;IAEG,MAAM,CAAC,OAAe,EAAE,GAAG,UAAiB;QACjD,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,UAAU,CAAC,CAAC;IAC3C,CAAC;IAEM,OAAO,CAAC,OAAe,EAAE,GAAG,UAAiB;QAClD,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,UAAU,CAAC,CAAC;IAC3C,CAAC;IAEM,KAAK,CAAC,OAAe,EAAE,GAAG,UAAiB;QAChD,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,GAAG,UAAU,CAAC,CAAC;IAC5C,CAAC;IAIM,SAAS,CAAC,cAAgC,EAAE,GAAG,IAAW;QAC/D,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;YAClB,OAAO;QACT,CAAC;QAED,IAAI,OAAO,cAAc,KAAK,QAAQ,EAAE,CAAC;YACvC,IAAI,CAAC,MAAM,CAAC,cAAc,EAAE,GAAG,IAAI,CAAC,CAAC;YACrC,OAAO;QACT,CAAC;QAED,MAAM,CAAC,OAAO,EAAE,GAAG,UAAU,CAAC,GAAG,IAAI,CAAC;QACtC,QAAO,cAAc,EAAE,CAAC;YACxB,KAAK,OAAO,CAAC,MAAM;gBACjB,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,GAAG,UAAU,CAAC,CAAC;gBACpC,MAAM;YACR,KAAK,OAAO,CAAC,OAAO;gBAClB,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,UAAU,CAAC,CAAC;gBACrC,MAAM;YACR,KAAK,OAAO,CAAC,KAAK;gBAChB,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,GAAG,UAAU,CAAC,CAAC;gBACnC,MAAM;QACR,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,2 @@
1
+ export declare function storageGet(dir: string, key: string): Promise<string | null>;
2
+ export declare function storageSet(dir: string, key: string, value: string): Promise<void>;
@@ -0,0 +1,24 @@
1
+ import storage from 'node-persist';
2
+ import { PLUGIN_NAME } from '../homebridge/settings.js';
3
+ async function init(dir) {
4
+ await storage.init({ dir: dir, forgiveParseErrors: true });
5
+ }
6
+ export async function storageGet(dir, key) {
7
+ try {
8
+ await init(dir);
9
+ return await storage.get(`${PLUGIN_NAME}:${key}`);
10
+ }
11
+ catch (err) {
12
+ return null;
13
+ }
14
+ }
15
+ export async function storageSet(dir, key, value) {
16
+ try {
17
+ await init(dir);
18
+ storage.set(`${PLUGIN_NAME}:${key}`, value);
19
+ }
20
+ catch {
21
+ // Nothing
22
+ }
23
+ }
24
+ //# sourceMappingURL=storage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage.js","sourceRoot":"","sources":["../../src/tools/storage.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,cAAc,CAAC;AAEnC,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAExD,KAAK,UAAU,IAAI,CAAC,GAAW;IAC7B,MAAM,OAAO,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,kBAAkB,EAAE,IAAI,EAAE,CAAC,CAAC;AAC7D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,GAAW,EAAE,GAAW;IACvD,IAAI,CAAC;QACH,MAAM,IAAI,CAAC,GAAG,CAAC,CAAC;QAChB,OAAO,MAAM,OAAO,CAAC,GAAG,CAAC,GAAG,WAAW,IAAI,GAAG,EAAE,CAAC,CAAC;IACpD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,GAAW,EAAE,GAAW,EAAE,KAAa;IACtE,IAAI,CAAC;QACH,MAAM,IAAI,CAAC,GAAG,CAAC,CAAC;QAChB,OAAO,CAAC,GAAG,CAAC,GAAG,WAAW,IAAI,GAAG,EAAE,EAAE,KAAK,CAAC,CAAC;IAC9C,CAAC;IAAC,MAAM,CAAC;QACP,UAAU;IACZ,CAAC;AACH,CAAC"}
@@ -0,0 +1,4 @@
1
+ export declare const SECOND = 1000;
2
+ export declare const MINUTE: number;
3
+ export declare const HOUR: number;
4
+ export declare const DAY: number;
@@ -0,0 +1,5 @@
1
+ export const SECOND = 1000;
2
+ export const MINUTE = 60 * SECOND;
3
+ export const HOUR = 60 * MINUTE;
4
+ export const DAY = 24 * HOUR;
5
+ //# sourceMappingURL=time.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"time.js","sourceRoot":"","sources":["../../src/tools/time.ts"],"names":[],"mappings":"AACA,MAAM,CAAC,MAAM,MAAM,GAAG,IAAI,CAAC;AAC3B,MAAM,CAAC,MAAM,MAAM,GAAG,EAAE,GAAG,MAAM,CAAC;AAClC,MAAM,CAAC,MAAM,IAAI,GAAG,EAAE,GAAG,MAAM,CAAC;AAChC,MAAM,CAAC,MAAM,GAAG,GAAG,EAAE,GAAG,IAAI,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { Log } from './log.js';
2
+ import { Assertable } from '../model/types.js';
3
+ export declare function assert(log: Log, caller: string, assertable: Assertable, ...keys: (keyof any)[]): boolean;
@@ -0,0 +1,13 @@
1
+ import { strings } from '../i18n/i18n.js';
2
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
3
+ export function assert(log, caller, assertable, ...keys) {
4
+ let valid = true;
5
+ for (const key of keys) {
6
+ if (assertable[key] === undefined) {
7
+ log.error(strings.accessory.missingRequired, caller, key);
8
+ valid = false;
9
+ }
10
+ }
11
+ return valid;
12
+ }
13
+ //# sourceMappingURL=validation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation.js","sourceRoot":"","sources":["../../src/tools/validation.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAI1C,8DAA8D;AAC9D,MAAM,UAAU,MAAM,CAAC,GAAQ,EAAE,MAAc,EAAE,UAAsB,EAAE,GAAG,IAAmB;IAC7F,IAAI,KAAK,GAAG,IAAI,CAAC;IACjB,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,IAAK,UAAsC,CAAC,GAAa,CAAC,KAAK,SAAS,EAAE,CAAC;YACzE,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,eAAe,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC;YAC1D,KAAK,GAAG,KAAK,CAAC;QAChB,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1 @@
1
+ export default function getVersion(): string;
@@ -0,0 +1,18 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { fileURLToPath } from 'url';
4
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
5
+ function loadPackageJson() {
6
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
7
+ const packageJSONPath = path.join(__dirname, '../../package.json');
8
+ return JSON.parse(fs.readFileSync(packageJSONPath, { encoding: 'utf8' }));
9
+ }
10
+ export default function getVersion() {
11
+ try {
12
+ return loadPackageJson().version;
13
+ }
14
+ catch (error) {
15
+ return '0.0.0';
16
+ }
17
+ }
18
+ //# sourceMappingURL=version.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/tools/version.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AAEpC,8DAA8D;AAC9D,SAAS,eAAe;IACtB,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IAC/D,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,oBAAoB,CAAC,CAAC;IACnE,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,eAAe,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;AAC5E,CAAC;AAED,MAAM,CAAC,OAAO,UAAU,UAAU;IAChC,IAAI,CAAC;QACH,OAAO,eAAe,EAAE,CAAC,OAAO,CAAC;IACnC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,OAAO,CAAC;IACjB,CAAC;AACH,CAAC"}
@@ -0,0 +1,35 @@
1
+ import eslint from '@eslint/js';
2
+ import tseslint from 'typescript-eslint';
3
+
4
+ export default tseslint.config(
5
+ {
6
+ ignores: ['dist/**'],
7
+ },
8
+ {
9
+ rules: {
10
+ 'quotes': ['error', 'single'],
11
+ 'indent': ['error', 2, { 'SwitchCase': 0 }],
12
+ 'linebreak-style': ['error', 'unix'],
13
+ 'semi': ['error', 'always'],
14
+ 'comma-dangle': ['error', 'always-multiline'],
15
+ 'dot-notation': 'error',
16
+ 'eqeqeq': ['error', 'smart'],
17
+ 'curly': ['error', 'all'],
18
+ 'brace-style': ['error'],
19
+ 'prefer-arrow-callback': 'warn',
20
+ 'max-len': ['warn', 160],
21
+ 'object-curly-spacing': ['error', 'always'],
22
+ 'no-use-before-define': 'off',
23
+ '@typescript-eslint/no-use-before-define': ['error', { 'classes': false, 'enums': false }],
24
+ '@typescript-eslint/no-unused-vars': ['error', { 'caughtErrors': 'none' }],
25
+ },
26
+ },
27
+ {
28
+ languageOptions: {
29
+ ecmaVersion: 2022,
30
+ sourceType: 'module',
31
+ },
32
+ },
33
+ eslint.configs.recommended,
34
+ ...tseslint.configs.recommended,
35
+ );
package/package.json ADDED
@@ -0,0 +1,68 @@
1
+ {
2
+ "name": "homebridge-easy-mqtt",
3
+ "platform": "HomebridgeEasyMQTT",
4
+ "displayName": "Homebridge Easy MQTT",
5
+ "description": "Homebridge plugin for easy control of MQTT devices",
6
+ "type": "module",
7
+ "version": "1.0.0-beta.0",
8
+ "homepage": "https://github.com/mpatfield/homebridge-easy-mqtt#readme",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "https://github.com/mpatfield/homebridge-easy-mqtt.git"
12
+ },
13
+ "funding": {
14
+ "type": "github",
15
+ "url": "https://github.com/sponsors/mpatfield"
16
+ },
17
+ "bugs": {
18
+ "url": "https://github.com/mpatfield/homebridge-easy-mqtt/issues"
19
+ },
20
+ "main": "dist/homebridge/index.js",
21
+ "scripts": {
22
+ "build": "rimraf ./dist && tsc && npm run build:html && npm run build:ui",
23
+ "build:html": "mkdir ./dist/homebridge-ui/public && cp -r ./src/homebridge-ui/public/* ./dist/homebridge-ui/public/",
24
+ "build:ui": "esbuild src/homebridge-ui/ui.ts --bundle --outfile=dist/homebridge-ui/public/ui.js --minify",
25
+ "lint": "eslint . --max-warnings=0",
26
+ "prepublishOnly": "npm run lint && npm run build"
27
+ },
28
+ "keywords": [
29
+ "homebridge",
30
+ "homebridge-plugin",
31
+ "homebridge-easy-mqtt",
32
+ "hoobs",
33
+ "hoobs-plugin",
34
+ "homekit",
35
+ "mqtt"
36
+ ],
37
+ "engines": {
38
+ "node": "^18.20.4 || ^20.18.0 || ^22.10.0",
39
+ "homebridge": "^1.8.0 || ^2.0.0-beta.0"
40
+ },
41
+ "dependencies": {
42
+ "@homebridge/plugin-ui-utils": "^2.0.2",
43
+ "homebridge-lib": "^7.1.6",
44
+ "lodash.merge": "^4.6.2",
45
+ "mqtt": "^5.7.0",
46
+ "node-persist": "^4.0.4"
47
+ },
48
+ "devDependencies": {
49
+ "@types/lodash.merge": "^4.6.9",
50
+ "@types/node-persist": "^3.1.8",
51
+ "@types/ws": "^8.18.1",
52
+ "esbuild": "^0.25.5",
53
+ "eslint": "^9.27.0",
54
+ "homebridge": "^2.0.0-beta.29",
55
+ "jiti": "^2.4.2",
56
+ "rimraf": "^6.0.1",
57
+ "ts-node": "^10.9.2",
58
+ "typescript-eslint": "^8.35.0"
59
+ },
60
+ "homebridge": {
61
+ "platform": "HomebridgeEasyMQTT",
62
+ "name": "homebridge-easy-mqtt"
63
+ },
64
+ "author": {
65
+ "name": "mpatfield"
66
+ },
67
+ "license": "Apache-2.0"
68
+ }