@switchbot/homebridge-switchbot 5.0.0-beta.3 → 5.0.0-beta.31

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 (153) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/README.md +23 -3
  3. package/config.schema.json +70 -4
  4. package/dist/devices-hap/device.d.ts +1 -0
  5. package/dist/devices-hap/device.d.ts.map +1 -1
  6. package/dist/devices-hap/device.js +70 -30
  7. package/dist/devices-hap/device.js.map +1 -1
  8. package/dist/devices-matter/BaseMatterAccessory.d.ts +23 -0
  9. package/dist/devices-matter/BaseMatterAccessory.d.ts.map +1 -1
  10. package/dist/devices-matter/BaseMatterAccessory.js +167 -5
  11. package/dist/devices-matter/BaseMatterAccessory.js.map +1 -1
  12. package/dist/devices-matter/ColorLightAccessory.d.ts.map +1 -1
  13. package/dist/devices-matter/ColorLightAccessory.js +12 -12
  14. package/dist/devices-matter/ColorLightAccessory.js.map +1 -1
  15. package/dist/devices-matter/ColorTemperatureLightAccessory.d.ts.map +1 -1
  16. package/dist/devices-matter/ColorTemperatureLightAccessory.js +5 -7
  17. package/dist/devices-matter/ColorTemperatureLightAccessory.js.map +1 -1
  18. package/dist/devices-matter/DimmableLightAccessory.js +9 -9
  19. package/dist/devices-matter/DimmableLightAccessory.js.map +1 -1
  20. package/dist/devices-matter/ExtendedColorLightAccessory.d.ts.map +1 -1
  21. package/dist/devices-matter/ExtendedColorLightAccessory.js +14 -15
  22. package/dist/devices-matter/ExtendedColorLightAccessory.js.map +1 -1
  23. package/dist/devices-matter/OnOffLightAccessory.d.ts.map +1 -1
  24. package/dist/devices-matter/OnOffLightAccessory.js +8 -16
  25. package/dist/devices-matter/OnOffLightAccessory.js.map +1 -1
  26. package/dist/devices-matter/OnOffOutletAccessory.d.ts +2 -0
  27. package/dist/devices-matter/OnOffOutletAccessory.d.ts.map +1 -1
  28. package/dist/devices-matter/OnOffOutletAccessory.js +10 -7
  29. package/dist/devices-matter/OnOffOutletAccessory.js.map +1 -1
  30. package/dist/devices-matter/OnOffSwitchAccessory.js +2 -2
  31. package/dist/devices-matter/OnOffSwitchAccessory.js.map +1 -1
  32. package/dist/homebridge-ui/public/index.html +48 -1
  33. package/dist/homebridge-ui/server.js +35 -0
  34. package/dist/homebridge-ui/server.js.map +1 -1
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +4 -5
  37. package/dist/index.js.map +1 -1
  38. package/dist/index.test.js +7 -2
  39. package/dist/index.test.js.map +1 -1
  40. package/dist/irdevice/irdevice.d.ts +11 -10
  41. package/dist/irdevice/irdevice.d.ts.map +1 -1
  42. package/dist/irdevice/irdevice.js +76 -35
  43. package/dist/irdevice/irdevice.js.map +1 -1
  44. package/dist/platform-hap.d.ts +11 -14
  45. package/dist/platform-hap.d.ts.map +1 -1
  46. package/dist/platform-hap.js +64 -64
  47. package/dist/platform-hap.js.map +1 -1
  48. package/dist/platform-matter.d.ts +87 -6
  49. package/dist/platform-matter.d.ts.map +1 -1
  50. package/dist/platform-matter.js +1845 -84
  51. package/dist/platform-matter.js.map +1 -1
  52. package/dist/settings.d.ts +11 -0
  53. package/dist/settings.d.ts.map +1 -1
  54. package/dist/settings.js.map +1 -1
  55. package/dist/test/hap/platform-hap.logging.test.d.ts +2 -0
  56. package/dist/test/hap/platform-hap.logging.test.d.ts.map +1 -0
  57. package/dist/test/hap/platform-hap.logging.test.js +33 -0
  58. package/dist/test/hap/platform-hap.logging.test.js.map +1 -0
  59. package/dist/test/hap/platform-hap.test.d.ts +2 -0
  60. package/dist/test/hap/platform-hap.test.d.ts.map +1 -0
  61. package/dist/test/hap/platform-hap.test.js +62 -0
  62. package/dist/test/hap/platform-hap.test.js.map +1 -0
  63. package/dist/test/helpers/platform-fixtures.d.ts +9 -0
  64. package/dist/test/helpers/platform-fixtures.d.ts.map +1 -0
  65. package/dist/test/helpers/platform-fixtures.js +30 -0
  66. package/dist/test/helpers/platform-fixtures.js.map +1 -0
  67. package/dist/test/matter/devices-matter/baseMatterAccessory.test.d.ts +2 -0
  68. package/dist/test/matter/devices-matter/baseMatterAccessory.test.d.ts.map +1 -0
  69. package/dist/test/matter/devices-matter/baseMatterAccessory.test.js +71 -0
  70. package/dist/test/matter/devices-matter/baseMatterAccessory.test.js.map +1 -0
  71. package/dist/test/matter/platform-matter.additional.test.d.ts +2 -0
  72. package/dist/test/matter/platform-matter.additional.test.d.ts.map +1 -0
  73. package/dist/test/matter/platform-matter.additional.test.js +35 -0
  74. package/dist/test/matter/platform-matter.additional.test.js.map +1 -0
  75. package/dist/test/matter/platform-matter.bleparse.test.d.ts +2 -0
  76. package/dist/test/matter/platform-matter.bleparse.test.d.ts.map +1 -0
  77. package/dist/test/matter/platform-matter.bleparse.test.js +43 -0
  78. package/dist/test/matter/platform-matter.bleparse.test.js.map +1 -0
  79. package/dist/test/matter/platform-matter.cleanup.test.d.ts +2 -0
  80. package/dist/test/matter/platform-matter.cleanup.test.d.ts.map +1 -0
  81. package/dist/test/matter/platform-matter.cleanup.test.js +70 -0
  82. package/dist/test/matter/platform-matter.cleanup.test.js.map +1 -0
  83. package/dist/test/matter/platform-matter.keepstale.test.d.ts +2 -0
  84. package/dist/test/matter/platform-matter.keepstale.test.d.ts.map +1 -0
  85. package/dist/test/matter/platform-matter.keepstale.test.js +27 -0
  86. package/dist/test/matter/platform-matter.keepstale.test.js.map +1 -0
  87. package/dist/test/matter/platform-matter.logging.test.d.ts +2 -0
  88. package/dist/test/matter/platform-matter.logging.test.d.ts.map +1 -0
  89. package/dist/test/matter/platform-matter.logging.test.js +29 -0
  90. package/dist/test/matter/platform-matter.logging.test.js.map +1 -0
  91. package/dist/test/matter/platform-matter.mapping.test.d.ts +2 -0
  92. package/dist/test/matter/platform-matter.mapping.test.d.ts.map +1 -0
  93. package/dist/test/matter/platform-matter.mapping.test.js +43 -0
  94. package/dist/test/matter/platform-matter.mapping.test.js.map +1 -0
  95. package/dist/test/matter/platform-matter.openapi-mapping.test.d.ts +2 -0
  96. package/dist/test/matter/platform-matter.openapi-mapping.test.d.ts.map +1 -0
  97. package/dist/test/matter/platform-matter.openapi-mapping.test.js +84 -0
  98. package/dist/test/matter/platform-matter.openapi-mapping.test.js.map +1 -0
  99. package/dist/test/matter/platform-matter.test.d.ts +2 -0
  100. package/dist/test/matter/platform-matter.test.d.ts.map +1 -0
  101. package/dist/test/matter/platform-matter.test.js +117 -0
  102. package/dist/test/matter/platform-matter.test.js.map +1 -0
  103. package/dist/test/matter/platform-matter.unregister.test.d.ts +2 -0
  104. package/dist/test/matter/platform-matter.unregister.test.d.ts.map +1 -0
  105. package/dist/test/matter/platform-matter.unregister.test.js +30 -0
  106. package/dist/test/matter/platform-matter.unregister.test.js.map +1 -0
  107. package/dist/utils.d.ts +127 -0
  108. package/dist/utils.d.ts.map +1 -1
  109. package/dist/utils.js +405 -0
  110. package/dist/utils.js.map +1 -1
  111. package/dist/utils.test.d.ts +2 -0
  112. package/dist/utils.test.d.ts.map +1 -0
  113. package/dist/utils.test.js +95 -0
  114. package/dist/utils.test.js.map +1 -0
  115. package/dist/verifyconfig.test.js +2 -2
  116. package/dist/verifyconfig.test.js.map +1 -1
  117. package/docs/assets/main.js +2 -2
  118. package/docs/index.html +20 -2
  119. package/docs/variables/default.html +1 -1
  120. package/package.json +14 -14
  121. package/src/devices-hap/device.ts +68 -30
  122. package/src/devices-matter/BaseMatterAccessory.ts +168 -5
  123. package/src/devices-matter/ColorLightAccessory.ts +12 -12
  124. package/src/devices-matter/ColorTemperatureLightAccessory.ts +5 -7
  125. package/src/devices-matter/DimmableLightAccessory.ts +9 -9
  126. package/src/devices-matter/ExtendedColorLightAccessory.ts +14 -15
  127. package/src/devices-matter/OnOffLightAccessory.ts +8 -16
  128. package/src/devices-matter/OnOffOutletAccessory.ts +12 -7
  129. package/src/devices-matter/OnOffSwitchAccessory.ts +2 -2
  130. package/src/homebridge-ui/public/index.html +48 -1
  131. package/src/homebridge-ui/server.ts +37 -0
  132. package/src/index.test.ts +7 -2
  133. package/src/index.ts +4 -5
  134. package/src/irdevice/irdevice.ts +74 -35
  135. package/src/platform-hap.ts +68 -73
  136. package/src/platform-matter.ts +1879 -87
  137. package/src/settings.ts +15 -0
  138. package/src/test/hap/platform-hap.logging.test.ts +36 -0
  139. package/src/test/hap/platform-hap.test.ts +70 -0
  140. package/src/test/helpers/platform-fixtures.ts +33 -0
  141. package/src/test/matter/devices-matter/baseMatterAccessory.test.ts +88 -0
  142. package/src/test/matter/platform-matter.additional.test.ts +44 -0
  143. package/src/test/matter/platform-matter.bleparse.test.ts +47 -0
  144. package/src/test/matter/platform-matter.cleanup.test.ts +86 -0
  145. package/src/test/matter/platform-matter.keepstale.test.ts +37 -0
  146. package/src/test/matter/platform-matter.logging.test.ts +33 -0
  147. package/src/test/matter/platform-matter.mapping.test.ts +57 -0
  148. package/src/test/matter/platform-matter.openapi-mapping.test.ts +109 -0
  149. package/src/test/matter/platform-matter.test.ts +144 -0
  150. package/src/test/matter/platform-matter.unregister.test.ts +39 -0
  151. package/src/utils.test.ts +96 -0
  152. package/src/utils.ts +419 -3
  153. package/src/verifyconfig.test.ts +11 -10
@@ -0,0 +1 @@
1
+ {"version":3,"file":"platform-matter.unregister.test.js","sourceRoot":"","sources":["../../../src/test/matter/platform-matter.unregister.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAA;AAEjD,OAAO,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAA;AAClE,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAA;AAE1E,QAAQ,CAAC,qDAAqD,EAAE,GAAG,EAAE;IACnE,EAAE,CAAC,yFAAyF,EAAE,KAAK,IAAI,EAAE;QACvG,mBAAmB;QACnB,MAAM,UAAU,GAAG,EAAE,CAAC,EAAE,EAAE,CAAA;QAC1B,MAAM,GAAG,GAAQ,WAAW,CAAC,EAAE,6BAA6B,EAAE,UAAU,EAAE,CAAC,CAAA;QAC3E,MAAM,GAAG,GAAQ,WAAW,EAAE,CAAA;QAC9B,MAAM,QAAQ,GAAG,IAAI,uBAAuB,CAAC,GAAU,EAAE,EAAE,gBAAgB,EAAE,KAAK,EAAS,EAAE,GAAG,CAAC,CAAA;QAEjG,gFAAgF;QAChF,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,oBAAoB,CAAC,CAAA;QAC3D,MAAM,SAAS,GAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,aAAa,EAAE,OAAO,EAAE,EAAE,QAAQ,EAAE,WAAW,EAAE,EAAE,CAAA;QAE/F,wEAAwE;QACxE,MAAM,GAAG,GAAI,QAAgB,CAAC,iBAAiB,CAAC,SAAS,CAAC,OAAO,CAAC,QAAQ,CAAC,CAC1E;QAAC,QAAgB,CAAC,aAAa,CAAC,GAAG,CAAC,GAAG,EAAE,IAAW,CAAC,CACrD;QAAC,QAAgB,CAAC,kBAAkB,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAG9D;QAAC,QAAgB,CAAC,iBAAiB,CAAC,GAAG,CAAC,IAAI,EAAE,SAAS,CAAC,CAAA;QAEzD,8BAA8B;QAC9B,MAAM,QAAQ,GAAG,EAAE,CAAC,KAAK,CAAC,QAAe,EAAE,sBAAsB,CAAC,CAAA;QAElE,0CAA0C;QAC1C,MAAO,QAAgB,CAAC,yBAAyB,EAAE,CAAA;QAEnD,6DAA6D;QAC7D,MAAM,CAAC,UAAU,CAAC,CAAC,gBAAgB,EAAE,CAAA;QACrC,MAAM,CAAE,QAAgB,CAAC,iBAAiB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,aAAa,EAAE,CAAA;QAErE,2EAA2E;QAC3E,MAAM,CAAC,QAAQ,CAAC,CAAC,oBAAoB,CAAC,SAAS,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAA;IACnE,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
package/dist/utils.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { API, Logging } from 'homebridge';
1
2
  import type { blindTilt, curtain, curtain3, device } from 'node-switchbot';
2
3
  import type { devicesConfig } from './settings.js';
3
4
  export declare enum BlindTiltMappingMode {
@@ -48,4 +49,130 @@ export declare function m2hs(m: any): number[];
48
49
  * Returns undefined if nothing meaningful remains.
49
50
  */
50
51
  export declare function cleanDeviceConfig(deviceConfig?: Record<string, Record<string, any>>): Record<string, Record<string, any>> | undefined;
52
+ /**
53
+ * Factory that returns a function to send OpenAPI commands using a retry wrapper.
54
+ *
55
+ * @param retryCommandFunc - bound function that calls platform.retryCommand(device, bodyChange, maxRetries, delay)
56
+ * @param deviceObj - the device object to operate on
57
+ * @param opts - optional overrides for maxRetries and delayBetweenRetries
58
+ * @param opts.maxRetries - override for maxRetries
59
+ * @param opts.delayBetweenRetries - override for delayBetweenRetries
60
+ */
61
+ export declare function makeOpenAPISender(retryCommandFunc: any, deviceObj: any, opts?: {
62
+ maxRetries?: number;
63
+ delayBetweenRetries?: number;
64
+ }): (command: string, parameter?: string) => Promise<any>;
65
+ /**
66
+ * Factory that returns a function to perform BLE actions using a SwitchBotBLE client.
67
+ * Handles discovery retries and method invocation on the discovered device instance.
68
+ *
69
+ * @param switchBotBLE - instance of SwitchBotBLE (may be undefined)
70
+ * @param deviceObj - the device object (used to obtain bleModel/deviceId)
71
+ * @param opts - optional retry settings
72
+ * @param opts.bleRetries - number of BLE discovery retries
73
+ * @param opts.bleRetryDelay - delay between BLE retries in ms
74
+ */
75
+ export declare function makeBLESender(switchBotBLE: any, deviceObj: any, opts?: {
76
+ bleRetries?: number;
77
+ bleRetryDelay?: number;
78
+ }): (methodName: string, ...args: any[]) => Promise<any>;
79
+ /**
80
+ * Decide effective connection type for a device given platform options.
81
+ * Mirrors the logic previously in platform-matter.
82
+ */
83
+ export declare function chooseConnectionType(platformOptions: any, deviceObj: any): 'BLE' | 'OpenAPI';
84
+ /**
85
+ * Detect whether Matter is enabled/available on the provided Homebridge API object.
86
+ * This encapsulates the multi-fallback detection used across the project.
87
+ */
88
+ /**
89
+ * Detect whether Matter is enabled on the provided Homebridge API object.
90
+ * Returns an object with an `enabled` boolean and an optional `reason` string
91
+ * describing which check matched (useful for diagnostics).
92
+ */
93
+ export declare function detectMatter(apiObj: API): {
94
+ enabled: boolean;
95
+ reason?: string;
96
+ };
97
+ /**
98
+ * Backwards-compatible boolean wrapper for detectMatter.
99
+ */
100
+ export declare function detectMatterEnabled(apiObj: API): boolean;
101
+ /**
102
+ * Create platform logging helpers used by both HAP and Matter platforms.
103
+ *
104
+ * getPlatformLogging may be either a synchronous string-returning function or an
105
+ * async function that resolves to the current platform logging setting. The
106
+ * returned helpers mirror the instance methods previously implemented on the
107
+ * HAP platform (infoLog, warnLog, errorLog, debugLog, etc.).
108
+ */
109
+ export declare function createPlatformLogger(getPlatformLogging: () => string | Promise<string | undefined>, log: Logging): {
110
+ infoLog: (...args: any[]) => Promise<void>;
111
+ successLog: (...args: any[]) => Promise<void>;
112
+ debugSuccessLog: (...args: any[]) => Promise<void>;
113
+ warnLog: (...args: any[]) => Promise<void>;
114
+ debugWarnLog: (...args: any[]) => Promise<void>;
115
+ errorLog: (...args: any[]) => Promise<void>;
116
+ debugErrorLog: (...args: any[]) => Promise<void>;
117
+ debugLog: (...args: any[]) => Promise<void>;
118
+ loggingIsDebug: () => Promise<boolean>;
119
+ enablingPlatformLogging: () => Promise<boolean>;
120
+ };
121
+ /**
122
+ * Create a Platform proxy class that selects between two platform constructors
123
+ * (HAP vs Matter) at runtime using `detectMatter`. Returns a class suitable
124
+ * for passing to `api.registerPlatform`.
125
+ */
126
+ export declare function createPlatformProxy(HAPCtor: any, MatterCtor: any): {
127
+ new (log: any, config: any, api: API): {
128
+ delegate: any;
129
+ readonly log: any;
130
+ readonly config: any;
131
+ readonly api: API;
132
+ configureAccessory(accessory: any): void;
133
+ configureMatterAccessory?(accessory: any): void;
134
+ get accessories(): any;
135
+ get matterAccessories(): any;
136
+ };
137
+ };
138
+ /**
139
+ * API Request Tracker - Persistent tracking of SwitchBot API calls
140
+ * Tracks requests per day with automatic midnight rollover
141
+ */
142
+ export declare class ApiRequestTracker {
143
+ private count;
144
+ private date;
145
+ private statsFile;
146
+ private hourlyTimer?;
147
+ private log;
148
+ constructor(api: API, log: Logging, pluginName?: string);
149
+ /**
150
+ * Load API request statistics from persistent storage
151
+ */
152
+ private load;
153
+ /**
154
+ * Save API request statistics to persistent storage
155
+ */
156
+ private save;
157
+ /**
158
+ * Increment API request counter and save
159
+ */
160
+ track(): void;
161
+ /**
162
+ * Start hourly logging of API request count
163
+ */
164
+ startHourlyLogging(): void;
165
+ /**
166
+ * Stop hourly logging
167
+ */
168
+ stopHourlyLogging(): void;
169
+ /**
170
+ * Get current count
171
+ */
172
+ getCount(): number;
173
+ /**
174
+ * Get current date
175
+ */
176
+ getDate(): string;
177
+ }
51
178
  //# sourceMappingURL=utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAE1E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AAElD,oBAAY,oBAAoB;IAC9B,MAAM,YAAY;IAClB,QAAQ,cAAc;IACtB,SAAS,gBAAgB;IACzB,SAAS,gBAAgB;IACzB,mBAAmB,2BAA2B;CAC/C;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,GAAG,MAAM,IAAI,CAAC,OAAO,GAAG,QAAQ,CAAC,GAAG,aAAa,CAE9G;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,GAAG,MAAM,IAAI,SAAS,GAAG,aAAa,CAErG;AAED,wBAAgB,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE/C;AACD;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAOlF;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAQlF;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,GAAG,UAWrC;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,OAAO,GAAG,MAAM,CA6BrF;AAED,wBAAgB,MAAM,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,YAiC5C;AAED,wBAAgB,MAAM,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,YAyCpC;AAED,wBAAgB,KAAK,CAAC,CAAC,EAAE,MAAM,OA8D9B;AAED,wBAAgB,IAAI,CAAC,CAAC,KAAA,YA2ZrB;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,GAAG,SAAS,CA2CrI"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"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"}
package/dist/utils.js CHANGED
@@ -1,3 +1,9 @@
1
+ /* Copyright(C) 2017-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved.
2
+ *
3
+ * util.ts: @switchbot/homebridge-switchbot platform class.
4
+ */
5
+ import { existsSync, readFileSync, writeFileSync } from 'node:fs';
6
+ import { join } from 'node:path';
1
7
  export var BlindTiltMappingMode;
2
8
  (function (BlindTiltMappingMode) {
3
9
  BlindTiltMappingMode["OnlyUp"] = "only_up";
@@ -690,4 +696,403 @@ export function cleanDeviceConfig(deviceConfig) {
690
696
  }
691
697
  return undefined;
692
698
  }
699
+ /**
700
+ * Factory that returns a function to send OpenAPI commands using a retry wrapper.
701
+ *
702
+ * @param retryCommandFunc - bound function that calls platform.retryCommand(device, bodyChange, maxRetries, delay)
703
+ * @param deviceObj - the device object to operate on
704
+ * @param opts - optional overrides for maxRetries and delayBetweenRetries
705
+ * @param opts.maxRetries - override for maxRetries
706
+ * @param opts.delayBetweenRetries - override for delayBetweenRetries
707
+ */
708
+ export function makeOpenAPISender(retryCommandFunc, deviceObj, opts) {
709
+ return async (command, parameter = 'default') => {
710
+ const bodyChange = { command, parameter, commandType: 'command' };
711
+ return retryCommandFunc(deviceObj, bodyChange, opts?.maxRetries, opts?.delayBetweenRetries);
712
+ };
713
+ }
714
+ /**
715
+ * Factory that returns a function to perform BLE actions using a SwitchBotBLE client.
716
+ * Handles discovery retries and method invocation on the discovered device instance.
717
+ *
718
+ * @param switchBotBLE - instance of SwitchBotBLE (may be undefined)
719
+ * @param deviceObj - the device object (used to obtain bleModel/deviceId)
720
+ * @param opts - optional retry settings
721
+ * @param opts.bleRetries - number of BLE discovery retries
722
+ * @param opts.bleRetryDelay - delay between BLE retries in ms
723
+ */
724
+ export function makeBLESender(switchBotBLE, deviceObj, opts) {
725
+ return async (methodName, ...args) => {
726
+ if (!switchBotBLE) {
727
+ throw new Error('Platform BLE not available');
728
+ }
729
+ const id = formatDeviceIdAsMac(deviceObj.deviceId);
730
+ const maxRetries = opts?.bleRetries ?? 2;
731
+ const retryDelay = opts?.bleRetryDelay ?? 500;
732
+ let attempt = 0;
733
+ while (attempt < maxRetries) {
734
+ try {
735
+ const list = await switchBotBLE.discover({ model: deviceObj.bleModel, id });
736
+ if (!Array.isArray(list) || list.length === 0) {
737
+ throw new Error('BLE device not found');
738
+ }
739
+ const deviceInst = list[0];
740
+ if (typeof deviceInst[methodName] !== 'function') {
741
+ throw new TypeError(`BLE method ${methodName} not available on device`);
742
+ }
743
+ return await deviceInst[methodName](...args);
744
+ }
745
+ catch (e) {
746
+ attempt++;
747
+ if (attempt >= maxRetries) {
748
+ throw e;
749
+ }
750
+ await sleep(retryDelay);
751
+ }
752
+ }
753
+ throw new Error('BLE operation failed');
754
+ };
755
+ }
756
+ /**
757
+ * Decide effective connection type for a device given platform options.
758
+ * Mirrors the logic previously in platform-matter.
759
+ */
760
+ export function chooseConnectionType(platformOptions, deviceObj) {
761
+ if (deviceObj?.connectionType) {
762
+ return deviceObj.connectionType === 'BLE' ? 'BLE' : 'OpenAPI';
763
+ }
764
+ if (platformOptions?.BLE && (deviceObj?.bleModel || (typeof deviceObj?.deviceId === 'string' && deviceObj.deviceId.length > 0))) {
765
+ return 'BLE';
766
+ }
767
+ return 'OpenAPI';
768
+ }
769
+ /**
770
+ * Detect whether Matter is enabled/available on the provided Homebridge API object.
771
+ * This encapsulates the multi-fallback detection used across the project.
772
+ */
773
+ /**
774
+ * Detect whether Matter is enabled on the provided Homebridge API object.
775
+ * Returns an object with an `enabled` boolean and an optional `reason` string
776
+ * describing which check matched (useful for diagnostics).
777
+ */
778
+ export function detectMatter(apiObj) {
779
+ try {
780
+ const maybe = apiObj.isMatterEnabled;
781
+ if (typeof maybe === 'function') {
782
+ return { enabled: Boolean(maybe.call(apiObj)), reason: 'api.isMatterEnabled() returned truthy' };
783
+ }
784
+ if (typeof maybe !== 'undefined') {
785
+ return { enabled: Boolean(maybe), reason: 'api.isMatterEnabled property present' };
786
+ }
787
+ const server = apiObj.server ?? apiObj.homebridgeServer ?? apiObj.homebridge_server;
788
+ const serverMaybe = server?.isMatterEnabled;
789
+ if (typeof serverMaybe === 'function') {
790
+ return { enabled: Boolean(serverMaybe.call(server)), reason: 'server.isMatterEnabled() returned truthy' };
791
+ }
792
+ if (typeof server?.isMatterEnabled !== 'undefined') {
793
+ return { enabled: Boolean(server.isMatterEnabled), reason: 'server.isMatterEnabled property present' };
794
+ }
795
+ }
796
+ catch (e) {
797
+ return { enabled: false, reason: `error during detection: ${String(e?.message ?? e)}` };
798
+ }
799
+ return { enabled: false, reason: 'no isMatterEnabled API or server fallback detected' };
800
+ }
801
+ /**
802
+ * Backwards-compatible boolean wrapper for detectMatter.
803
+ */
804
+ export function detectMatterEnabled(apiObj) {
805
+ return detectMatter(apiObj).enabled;
806
+ }
807
+ /**
808
+ * Create platform logging helpers used by both HAP and Matter platforms.
809
+ *
810
+ * getPlatformLogging may be either a synchronous string-returning function or an
811
+ * async function that resolves to the current platform logging setting. The
812
+ * returned helpers mirror the instance methods previously implemented on the
813
+ * HAP platform (infoLog, warnLog, errorLog, debugLog, etc.).
814
+ */
815
+ export function createPlatformLogger(getPlatformLogging, log) {
816
+ const getPL = async () => {
817
+ try {
818
+ return await getPlatformLogging();
819
+ }
820
+ catch {
821
+ return undefined;
822
+ }
823
+ };
824
+ const loggingIsDebug = async () => {
825
+ const pl = await getPL();
826
+ return pl === 'debugMode' || pl === 'debug';
827
+ };
828
+ const enablingPlatformLogging = async () => {
829
+ const pl = await getPL();
830
+ return pl === 'debugMode' || pl === 'debug' || pl === 'standard';
831
+ };
832
+ const formatArgs = (args) => {
833
+ return args
834
+ .map((a) => {
835
+ if (typeof a === 'string') {
836
+ return a;
837
+ }
838
+ try {
839
+ return JSON.stringify(a);
840
+ }
841
+ catch {
842
+ return String(a);
843
+ }
844
+ })
845
+ .join(' ');
846
+ };
847
+ return {
848
+ // Format arbitrary arguments into a single string to ensure values are not dropped
849
+ // when loggers only accept a single message parameter.
850
+ // Prefer readable JSON for objects, fall back to String() on errors.
851
+ // Example: infoLog('Loaded', accessory.displayName) => "Loaded My Light"
852
+ infoLog: async (...args) => {
853
+ if (await enablingPlatformLogging()) {
854
+ const msg = formatArgs(args);
855
+ log.info(msg);
856
+ }
857
+ },
858
+ successLog: async (...args) => {
859
+ if (await enablingPlatformLogging()) {
860
+ const msg = formatArgs(args);
861
+ log.success?.(msg) ?? log.info(msg);
862
+ }
863
+ },
864
+ debugSuccessLog: async (...args) => {
865
+ if (await enablingPlatformLogging()) {
866
+ if (await loggingIsDebug()) {
867
+ const msg = formatArgs(args);
868
+ log.success?.(`[DEBUG] ${msg}`) ?? log.info(`[DEBUG] ${msg}`);
869
+ }
870
+ }
871
+ },
872
+ warnLog: async (...args) => {
873
+ if (await enablingPlatformLogging()) {
874
+ const msg = formatArgs(args);
875
+ log.warn(msg);
876
+ }
877
+ },
878
+ debugWarnLog: async (...args) => {
879
+ if (await enablingPlatformLogging()) {
880
+ if (await loggingIsDebug()) {
881
+ const msg = formatArgs(args);
882
+ log.warn(`[DEBUG] ${msg}`);
883
+ }
884
+ }
885
+ },
886
+ errorLog: async (...args) => {
887
+ if (await enablingPlatformLogging()) {
888
+ const msg = formatArgs(args);
889
+ log.error(msg);
890
+ }
891
+ },
892
+ debugErrorLog: async (...args) => {
893
+ if (await enablingPlatformLogging()) {
894
+ if (await loggingIsDebug()) {
895
+ const msg = formatArgs(args);
896
+ log.error(`[DEBUG] ${msg}`);
897
+ }
898
+ }
899
+ },
900
+ debugLog: async (...args) => {
901
+ if (await enablingPlatformLogging()) {
902
+ const pl = await getPL();
903
+ if (pl === 'debug') {
904
+ const msg = formatArgs(args);
905
+ log.info(`[DEBUG] ${msg}`);
906
+ }
907
+ else if (pl === 'debugMode') {
908
+ const msg = formatArgs(args);
909
+ log.debug(msg);
910
+ }
911
+ }
912
+ },
913
+ loggingIsDebug,
914
+ enablingPlatformLogging,
915
+ };
916
+ }
917
+ /**
918
+ * Create a Platform proxy class that selects between two platform constructors
919
+ * (HAP vs Matter) at runtime using `detectMatter`. Returns a class suitable
920
+ * for passing to `api.registerPlatform`.
921
+ */
922
+ export function createPlatformProxy(HAPCtor, MatterCtor) {
923
+ return class PlatformProxy {
924
+ log;
925
+ config;
926
+ api;
927
+ delegate;
928
+ constructor(log, config, api) {
929
+ this.log = log;
930
+ this.config = config;
931
+ this.api = api;
932
+ const matterInfo = detectMatter(this.api);
933
+ const isMatter = matterInfo.enabled;
934
+ const reason = matterInfo.reason ? ` Reason: ${matterInfo.reason}` : '';
935
+ this.log.info?.(`Homebridge SwitchBot Plugin initializing in ${isMatter ? 'Matter' : 'HAP'} mode.`);
936
+ this.log.debug?.(`Homebridge SwitchBot Plugin initializing in ${isMatter ? 'Matter' : 'HAP'} mode.${reason}`);
937
+ const PlatformCtor = isMatter ? MatterCtor : HAPCtor;
938
+ this.delegate = new PlatformCtor(this.log, this.config, this.api);
939
+ }
940
+ configureAccessory(accessory) {
941
+ try {
942
+ if (this.delegate && typeof this.delegate.configureAccessory === 'function') {
943
+ return this.delegate.configureAccessory(accessory);
944
+ }
945
+ }
946
+ catch (e) {
947
+ // swallow — preserve previous behaviour where delegate errors don't bubble here
948
+ }
949
+ }
950
+ configureMatterAccessory(accessory) {
951
+ try {
952
+ if (this.delegate && typeof this.delegate.configureMatterAccessory === 'function') {
953
+ return this.delegate.configureMatterAccessory(accessory);
954
+ }
955
+ }
956
+ catch (e) {
957
+ // swallow — delegate may not implement this or may throw
958
+ }
959
+ }
960
+ get accessories() {
961
+ try {
962
+ return this.delegate?.accessories;
963
+ }
964
+ catch (e) {
965
+ return undefined;
966
+ }
967
+ }
968
+ get matterAccessories() {
969
+ try {
970
+ return this.delegate?.matterAccessories;
971
+ }
972
+ catch (e) {
973
+ return undefined;
974
+ }
975
+ }
976
+ };
977
+ }
978
+ /**
979
+ * API Request Tracker - Persistent tracking of SwitchBot API calls
980
+ * Tracks requests per day with automatic midnight rollover
981
+ */
982
+ export class ApiRequestTracker {
983
+ count = 0;
984
+ date = '';
985
+ statsFile = '';
986
+ hourlyTimer;
987
+ log;
988
+ constructor(api, log, pluginName = 'SwitchBot') {
989
+ this.log = log;
990
+ this.statsFile = join(api.user.storagePath(), `${pluginName.toLowerCase()}-api-stats.json`);
991
+ this.load();
992
+ }
993
+ /**
994
+ * Load API request statistics from persistent storage
995
+ */
996
+ load() {
997
+ try {
998
+ const today = new Date().toISOString().split('T')[0];
999
+ if (existsSync(this.statsFile)) {
1000
+ const data = JSON.parse(readFileSync(this.statsFile, 'utf8'));
1001
+ // If it's a new day, reset the counter
1002
+ if (data.date === today) {
1003
+ this.count = data.count || 0;
1004
+ this.date = data.date;
1005
+ this.log.warn?.(`[API Stats] Loaded: ${this.count} requests today (${today})`);
1006
+ }
1007
+ else {
1008
+ this.log.error?.(`[API Stats] New day detected. Previous: ${data.count || 0} requests on ${data.date}`);
1009
+ this.count = 0;
1010
+ this.date = today;
1011
+ this.save();
1012
+ }
1013
+ }
1014
+ else {
1015
+ this.log.debug?.('[API Stats] No existing stats file, starting fresh');
1016
+ this.count = 0;
1017
+ this.date = today;
1018
+ this.save();
1019
+ }
1020
+ }
1021
+ catch (e) {
1022
+ this.log.error?.(`[API Stats] Failed to load stats: ${e?.message ?? e}`);
1023
+ this.count = 0;
1024
+ this.date = new Date().toISOString().split('T')[0];
1025
+ }
1026
+ }
1027
+ /**
1028
+ * Save API request statistics to persistent storage
1029
+ */
1030
+ save() {
1031
+ try {
1032
+ const data = {
1033
+ date: this.date,
1034
+ count: this.count,
1035
+ lastUpdated: new Date().toISOString(),
1036
+ };
1037
+ writeFileSync(this.statsFile, JSON.stringify(data, null, 2), 'utf8');
1038
+ }
1039
+ catch (e) {
1040
+ this.log.debug?.(`[API Stats] Failed to save stats: ${e?.message ?? e}`);
1041
+ }
1042
+ }
1043
+ /**
1044
+ * Increment API request counter and save
1045
+ */
1046
+ track() {
1047
+ const today = new Date().toISOString().split('T')[0];
1048
+ // Reset counter if it's a new day
1049
+ if (this.date !== today) {
1050
+ this.log.debug?.(`[API Stats] Day rollover: ${this.count} requests on ${this.date}`);
1051
+ this.count = 0;
1052
+ this.date = today;
1053
+ }
1054
+ this.count++;
1055
+ this.save();
1056
+ }
1057
+ /**
1058
+ * Start hourly logging of API request count
1059
+ */
1060
+ startHourlyLogging() {
1061
+ // Log immediately on startup
1062
+ this.log.info?.(`[API Stats] Today (${this.date}): ${this.count} API requests`);
1063
+ // Then log every hour
1064
+ this.hourlyTimer = setInterval(() => {
1065
+ const today = new Date().toISOString().split('T')[0];
1066
+ if (this.date !== today) {
1067
+ // Day rollover
1068
+ this.log.info?.(`[API Stats] Day rollover - Previous day (${this.date}): ${this.count} API requests`);
1069
+ this.count = 0;
1070
+ this.date = today;
1071
+ this.save();
1072
+ }
1073
+ this.log.info?.(`[API Stats] Today (${this.date}): ${this.count} API requests`);
1074
+ }, 60 * 60 * 1000); // Every hour
1075
+ }
1076
+ /**
1077
+ * Stop hourly logging
1078
+ */
1079
+ stopHourlyLogging() {
1080
+ if (this.hourlyTimer) {
1081
+ clearInterval(this.hourlyTimer);
1082
+ this.hourlyTimer = undefined;
1083
+ }
1084
+ }
1085
+ /**
1086
+ * Get current count
1087
+ */
1088
+ getCount() {
1089
+ return this.count;
1090
+ }
1091
+ /**
1092
+ * Get current date
1093
+ */
1094
+ getDate() {
1095
+ return this.date;
1096
+ }
1097
+ }
693
1098
  //# sourceMappingURL=utils.js.map