@ecobridge.xyz/devicemanager 3.0.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.
Files changed (137) hide show
  1. package/.gitea/workflows/default_nottags.yaml +66 -0
  2. package/.gitea/workflows/default_tags.yaml +124 -0
  3. package/.vscode/launch.json +11 -0
  4. package/.vscode/settings.json +26 -0
  5. package/changelog.md +91 -0
  6. package/dist_ts/00_commitinfo_data.d.ts +8 -0
  7. package/dist_ts/00_commitinfo_data.js +9 -0
  8. package/dist_ts/device/device.classes.device.d.ts +205 -0
  9. package/dist_ts/device/device.classes.device.js +344 -0
  10. package/dist_ts/devicemanager.classes.devicemanager.d.ts +210 -0
  11. package/dist_ts/devicemanager.classes.devicemanager.js +835 -0
  12. package/dist_ts/discovery/discovery.classes.homeassistant.d.ts +101 -0
  13. package/dist_ts/discovery/discovery.classes.homeassistant.js +313 -0
  14. package/dist_ts/discovery/discovery.classes.mdns.d.ts +90 -0
  15. package/dist_ts/discovery/discovery.classes.mdns.js +291 -0
  16. package/dist_ts/discovery/discovery.classes.networkscanner.d.ts +64 -0
  17. package/dist_ts/discovery/discovery.classes.networkscanner.js +535 -0
  18. package/dist_ts/discovery/discovery.classes.ssdp.d.ts +112 -0
  19. package/dist_ts/discovery/discovery.classes.ssdp.js +255 -0
  20. package/dist_ts/factories/index.d.ts +190 -0
  21. package/dist_ts/factories/index.js +350 -0
  22. package/dist_ts/features/feature.abstract.d.ts +118 -0
  23. package/dist_ts/features/feature.abstract.js +172 -0
  24. package/dist_ts/features/feature.camera.d.ts +95 -0
  25. package/dist_ts/features/feature.camera.js +162 -0
  26. package/dist_ts/features/feature.climate.d.ts +174 -0
  27. package/dist_ts/features/feature.climate.js +328 -0
  28. package/dist_ts/features/feature.cover.d.ts +121 -0
  29. package/dist_ts/features/feature.cover.js +217 -0
  30. package/dist_ts/features/feature.fan.d.ts +127 -0
  31. package/dist_ts/features/feature.fan.js +235 -0
  32. package/dist_ts/features/feature.light.d.ts +156 -0
  33. package/dist_ts/features/feature.light.js +296 -0
  34. package/dist_ts/features/feature.lock.d.ts +103 -0
  35. package/dist_ts/features/feature.lock.js +170 -0
  36. package/dist_ts/features/feature.playback.d.ts +98 -0
  37. package/dist_ts/features/feature.playback.js +191 -0
  38. package/dist_ts/features/feature.power.d.ts +79 -0
  39. package/dist_ts/features/feature.power.js +173 -0
  40. package/dist_ts/features/feature.print.d.ts +78 -0
  41. package/dist_ts/features/feature.print.js +226 -0
  42. package/dist_ts/features/feature.scan.d.ts +79 -0
  43. package/dist_ts/features/feature.scan.js +315 -0
  44. package/dist_ts/features/feature.sensor.d.ts +95 -0
  45. package/dist_ts/features/feature.sensor.js +147 -0
  46. package/dist_ts/features/feature.snmp.d.ts +88 -0
  47. package/dist_ts/features/feature.snmp.js +175 -0
  48. package/dist_ts/features/feature.switch.d.ts +75 -0
  49. package/dist_ts/features/feature.switch.js +126 -0
  50. package/dist_ts/features/feature.volume.d.ts +104 -0
  51. package/dist_ts/features/feature.volume.js +186 -0
  52. package/dist_ts/features/index.d.ts +19 -0
  53. package/dist_ts/features/index.js +23 -0
  54. package/dist_ts/helpers/helpers.iprange.d.ts +35 -0
  55. package/dist_ts/helpers/helpers.iprange.js +151 -0
  56. package/dist_ts/helpers/helpers.retry.d.ts +25 -0
  57. package/dist_ts/helpers/helpers.retry.js +69 -0
  58. package/dist_ts/index.d.ts +15 -0
  59. package/dist_ts/index.js +40 -0
  60. package/dist_ts/interfaces/feature.interfaces.d.ts +255 -0
  61. package/dist_ts/interfaces/feature.interfaces.js +6 -0
  62. package/dist_ts/interfaces/homeassistant.interfaces.d.ts +550 -0
  63. package/dist_ts/interfaces/homeassistant.interfaces.js +111 -0
  64. package/dist_ts/interfaces/index.d.ts +330 -0
  65. package/dist_ts/interfaces/index.js +16 -0
  66. package/dist_ts/interfaces/smarthome.interfaces.d.ts +282 -0
  67. package/dist_ts/interfaces/smarthome.interfaces.js +7 -0
  68. package/dist_ts/paths.d.ts +1 -0
  69. package/dist_ts/paths.js +3 -0
  70. package/dist_ts/plugins.d.ts +23 -0
  71. package/dist_ts/plugins.js +30 -0
  72. package/dist_ts/protocols/index.d.ts +12 -0
  73. package/dist_ts/protocols/index.js +21 -0
  74. package/dist_ts/protocols/protocol.escl.d.ts +58 -0
  75. package/dist_ts/protocols/protocol.escl.js +364 -0
  76. package/dist_ts/protocols/protocol.homeassistant.d.ts +248 -0
  77. package/dist_ts/protocols/protocol.homeassistant.js +611 -0
  78. package/dist_ts/protocols/protocol.ipp.d.ts +45 -0
  79. package/dist_ts/protocols/protocol.ipp.js +284 -0
  80. package/dist_ts/protocols/protocol.nut.d.ts +160 -0
  81. package/dist_ts/protocols/protocol.nut.js +364 -0
  82. package/dist_ts/protocols/protocol.sane.d.ts +104 -0
  83. package/dist_ts/protocols/protocol.sane.js +597 -0
  84. package/dist_ts/protocols/protocol.snmp.d.ts +133 -0
  85. package/dist_ts/protocols/protocol.snmp.js +333 -0
  86. package/dist_ts/protocols/protocol.upnp.d.ts +207 -0
  87. package/dist_ts/protocols/protocol.upnp.js +442 -0
  88. package/dist_ts/protocols/protocol.upssnmp.d.ts +160 -0
  89. package/dist_ts/protocols/protocol.upssnmp.js +263 -0
  90. package/npmextra.json +24 -0
  91. package/package.json +39 -0
  92. package/readme.hints.md +115 -0
  93. package/readme.md +624 -0
  94. package/test/test.ts +261 -0
  95. package/ts/00_commitinfo_data.ts +8 -0
  96. package/ts/device/device.classes.device.ts +454 -0
  97. package/ts/devicemanager.classes.devicemanager.ts +1219 -0
  98. package/ts/discovery/discovery.classes.homeassistant.ts +376 -0
  99. package/ts/discovery/discovery.classes.mdns.ts +347 -0
  100. package/ts/discovery/discovery.classes.networkscanner.ts +648 -0
  101. package/ts/discovery/discovery.classes.ssdp.ts +357 -0
  102. package/ts/factories/index.ts +696 -0
  103. package/ts/features/feature.abstract.ts +251 -0
  104. package/ts/features/feature.camera.ts +214 -0
  105. package/ts/features/feature.climate.ts +407 -0
  106. package/ts/features/feature.cover.ts +278 -0
  107. package/ts/features/feature.fan.ts +296 -0
  108. package/ts/features/feature.light.ts +369 -0
  109. package/ts/features/feature.lock.ts +223 -0
  110. package/ts/features/feature.playback.ts +246 -0
  111. package/ts/features/feature.power.ts +231 -0
  112. package/ts/features/feature.print.ts +297 -0
  113. package/ts/features/feature.scan.ts +370 -0
  114. package/ts/features/feature.sensor.ts +202 -0
  115. package/ts/features/feature.snmp.ts +235 -0
  116. package/ts/features/feature.switch.ts +170 -0
  117. package/ts/features/feature.volume.ts +256 -0
  118. package/ts/features/index.ts +25 -0
  119. package/ts/helpers/helpers.iprange.ts +172 -0
  120. package/ts/helpers/helpers.retry.ts +100 -0
  121. package/ts/index.ts +165 -0
  122. package/ts/interfaces/feature.interfaces.ts +359 -0
  123. package/ts/interfaces/homeassistant.interfaces.ts +666 -0
  124. package/ts/interfaces/index.ts +421 -0
  125. package/ts/interfaces/smarthome.interfaces.ts +421 -0
  126. package/ts/paths.ts +5 -0
  127. package/ts/plugins.ts +41 -0
  128. package/ts/protocols/index.ts +59 -0
  129. package/ts/protocols/protocol.escl.ts +439 -0
  130. package/ts/protocols/protocol.homeassistant.ts +737 -0
  131. package/ts/protocols/protocol.ipp.ts +329 -0
  132. package/ts/protocols/protocol.nut.ts +471 -0
  133. package/ts/protocols/protocol.sane.ts +694 -0
  134. package/ts/protocols/protocol.snmp.ts +439 -0
  135. package/ts/protocols/protocol.upnp.ts +627 -0
  136. package/ts/protocols/protocol.upssnmp.ts +377 -0
  137. package/tsconfig.json +12 -0
@@ -0,0 +1,328 @@
1
+ /**
2
+ * Climate Feature
3
+ * Provides control for thermostats and HVAC systems
4
+ */
5
+ import { Feature } from './feature.abstract.js';
6
+ /**
7
+ * Climate Feature - thermostat and HVAC control
8
+ *
9
+ * Protocol-agnostic: works with Home Assistant, Nest, Ecobee, MQTT, etc.
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * const climate = device.getFeature<ClimateFeature>('climate');
14
+ * if (climate) {
15
+ * await climate.setHvacMode('heat');
16
+ * await climate.setTargetTemp(21);
17
+ * console.log(`Current: ${climate.currentTemp}°C, Target: ${climate.targetTemp}°C`);
18
+ * }
19
+ * ```
20
+ */
21
+ export class ClimateFeature extends Feature {
22
+ type = 'climate';
23
+ protocol;
24
+ /** Entity ID (e.g., "climate.living_room") */
25
+ entityId;
26
+ /** Capabilities */
27
+ capabilities;
28
+ /** Current state */
29
+ _currentTemp;
30
+ _targetTemp;
31
+ _targetTempHigh;
32
+ _targetTempLow;
33
+ _hvacMode = 'off';
34
+ _hvacAction;
35
+ _presetMode;
36
+ _fanMode;
37
+ _swingMode;
38
+ _humidity;
39
+ _targetHumidity;
40
+ _auxHeat;
41
+ /** Protocol client for controlling the climate device */
42
+ protocolClient;
43
+ constructor(device, port, options) {
44
+ super(device, port, options);
45
+ this.protocol = options.protocol;
46
+ this.entityId = options.entityId;
47
+ this.protocolClient = options.protocolClient;
48
+ this.capabilities = {
49
+ hvacModes: options.capabilities?.hvacModes ?? ['off', 'heat', 'cool', 'auto'],
50
+ presetModes: options.capabilities?.presetModes,
51
+ fanModes: options.capabilities?.fanModes,
52
+ swingModes: options.capabilities?.swingModes,
53
+ supportsTargetTemp: options.capabilities?.supportsTargetTemp ?? true,
54
+ supportsTargetTempRange: options.capabilities?.supportsTargetTempRange ?? false,
55
+ supportsHumidity: options.capabilities?.supportsHumidity ?? false,
56
+ supportsAuxHeat: options.capabilities?.supportsAuxHeat ?? false,
57
+ minTemp: options.capabilities?.minTemp ?? 7,
58
+ maxTemp: options.capabilities?.maxTemp ?? 35,
59
+ tempStep: options.capabilities?.tempStep ?? 0.5,
60
+ minHumidity: options.capabilities?.minHumidity,
61
+ maxHumidity: options.capabilities?.maxHumidity,
62
+ };
63
+ }
64
+ // ============================================================================
65
+ // Properties
66
+ // ============================================================================
67
+ /**
68
+ * Get current temperature (cached)
69
+ */
70
+ get currentTemp() {
71
+ return this._currentTemp;
72
+ }
73
+ /**
74
+ * Get target temperature (cached)
75
+ */
76
+ get targetTemp() {
77
+ return this._targetTemp;
78
+ }
79
+ /**
80
+ * Get target temperature high (for heat_cool mode, cached)
81
+ */
82
+ get targetTempHigh() {
83
+ return this._targetTempHigh;
84
+ }
85
+ /**
86
+ * Get target temperature low (for heat_cool mode, cached)
87
+ */
88
+ get targetTempLow() {
89
+ return this._targetTempLow;
90
+ }
91
+ /**
92
+ * Get current HVAC mode (cached)
93
+ */
94
+ get hvacMode() {
95
+ return this._hvacMode;
96
+ }
97
+ /**
98
+ * Get current HVAC action (cached)
99
+ */
100
+ get hvacAction() {
101
+ return this._hvacAction;
102
+ }
103
+ /**
104
+ * Get current preset mode (cached)
105
+ */
106
+ get presetMode() {
107
+ return this._presetMode;
108
+ }
109
+ /**
110
+ * Get current fan mode (cached)
111
+ */
112
+ get fanMode() {
113
+ return this._fanMode;
114
+ }
115
+ /**
116
+ * Get current swing mode (cached)
117
+ */
118
+ get swingMode() {
119
+ return this._swingMode;
120
+ }
121
+ /**
122
+ * Get current humidity (cached)
123
+ */
124
+ get humidity() {
125
+ return this._humidity;
126
+ }
127
+ /**
128
+ * Get target humidity (cached)
129
+ */
130
+ get targetHumidity() {
131
+ return this._targetHumidity;
132
+ }
133
+ /**
134
+ * Get aux heat state (cached)
135
+ */
136
+ get auxHeat() {
137
+ return this._auxHeat;
138
+ }
139
+ /**
140
+ * Get available HVAC modes
141
+ */
142
+ get hvacModes() {
143
+ return this.capabilities.hvacModes;
144
+ }
145
+ /**
146
+ * Get available preset modes
147
+ */
148
+ get presetModes() {
149
+ return this.capabilities.presetModes;
150
+ }
151
+ /**
152
+ * Get available fan modes
153
+ */
154
+ get fanModes() {
155
+ return this.capabilities.fanModes;
156
+ }
157
+ // ============================================================================
158
+ // Connection
159
+ // ============================================================================
160
+ async doConnect() {
161
+ try {
162
+ const state = await this.protocolClient.getState(this.entityId);
163
+ this.updateStateInternal(state);
164
+ }
165
+ catch {
166
+ // Ignore errors fetching initial state
167
+ }
168
+ }
169
+ async doDisconnect() {
170
+ // Nothing to disconnect
171
+ }
172
+ // ============================================================================
173
+ // Climate Control
174
+ // ============================================================================
175
+ /**
176
+ * Set HVAC mode
177
+ * @param mode HVAC mode (off, heat, cool, etc.)
178
+ */
179
+ async setHvacMode(mode) {
180
+ if (!this.capabilities.hvacModes.includes(mode)) {
181
+ throw new Error(`HVAC mode ${mode} not supported`);
182
+ }
183
+ await this.protocolClient.setHvacMode(this.entityId, mode);
184
+ this._hvacMode = mode;
185
+ this.emit('state:changed', this.getState());
186
+ }
187
+ /**
188
+ * Set target temperature
189
+ * @param temp Target temperature
190
+ */
191
+ async setTargetTemp(temp) {
192
+ if (!this.capabilities.supportsTargetTemp) {
193
+ throw new Error('Climate device does not support target temperature');
194
+ }
195
+ const clamped = Math.max(this.capabilities.minTemp, Math.min(this.capabilities.maxTemp, temp));
196
+ await this.protocolClient.setTargetTemp(this.entityId, clamped);
197
+ this._targetTemp = clamped;
198
+ this.emit('state:changed', this.getState());
199
+ }
200
+ /**
201
+ * Set target temperature range (for heat_cool mode)
202
+ * @param low Low temperature
203
+ * @param high High temperature
204
+ */
205
+ async setTargetTempRange(low, high) {
206
+ if (!this.capabilities.supportsTargetTempRange) {
207
+ throw new Error('Climate device does not support temperature range');
208
+ }
209
+ const clampedLow = Math.max(this.capabilities.minTemp, Math.min(this.capabilities.maxTemp, low));
210
+ const clampedHigh = Math.max(this.capabilities.minTemp, Math.min(this.capabilities.maxTemp, high));
211
+ await this.protocolClient.setTargetTempRange(this.entityId, clampedLow, clampedHigh);
212
+ this._targetTempLow = clampedLow;
213
+ this._targetTempHigh = clampedHigh;
214
+ this.emit('state:changed', this.getState());
215
+ }
216
+ /**
217
+ * Set preset mode
218
+ * @param preset Preset mode name
219
+ */
220
+ async setPresetMode(preset) {
221
+ if (!this.capabilities.presetModes?.includes(preset)) {
222
+ throw new Error(`Preset mode ${preset} not supported`);
223
+ }
224
+ await this.protocolClient.setPresetMode(this.entityId, preset);
225
+ this._presetMode = preset;
226
+ this.emit('state:changed', this.getState());
227
+ }
228
+ /**
229
+ * Set fan mode
230
+ * @param mode Fan mode name
231
+ */
232
+ async setFanMode(mode) {
233
+ if (!this.capabilities.fanModes?.includes(mode)) {
234
+ throw new Error(`Fan mode ${mode} not supported`);
235
+ }
236
+ await this.protocolClient.setFanMode(this.entityId, mode);
237
+ this._fanMode = mode;
238
+ this.emit('state:changed', this.getState());
239
+ }
240
+ /**
241
+ * Set swing mode
242
+ * @param mode Swing mode name
243
+ */
244
+ async setSwingMode(mode) {
245
+ if (!this.capabilities.swingModes?.includes(mode)) {
246
+ throw new Error(`Swing mode ${mode} not supported`);
247
+ }
248
+ await this.protocolClient.setSwingMode(this.entityId, mode);
249
+ this._swingMode = mode;
250
+ this.emit('state:changed', this.getState());
251
+ }
252
+ /**
253
+ * Set aux heat
254
+ * @param enabled Whether aux heat is enabled
255
+ */
256
+ async setAuxHeat(enabled) {
257
+ if (!this.capabilities.supportsAuxHeat) {
258
+ throw new Error('Climate device does not support aux heat');
259
+ }
260
+ await this.protocolClient.setAuxHeat(this.entityId, enabled);
261
+ this._auxHeat = enabled;
262
+ this.emit('state:changed', this.getState());
263
+ }
264
+ /**
265
+ * Get current state as object
266
+ */
267
+ getState() {
268
+ return {
269
+ currentTemp: this._currentTemp,
270
+ targetTemp: this._targetTemp,
271
+ targetTempHigh: this._targetTempHigh,
272
+ targetTempLow: this._targetTempLow,
273
+ hvacMode: this._hvacMode,
274
+ hvacAction: this._hvacAction,
275
+ presetMode: this._presetMode,
276
+ fanMode: this._fanMode,
277
+ swingMode: this._swingMode,
278
+ humidity: this._humidity,
279
+ targetHumidity: this._targetHumidity,
280
+ auxHeat: this._auxHeat,
281
+ };
282
+ }
283
+ /**
284
+ * Refresh state from the device
285
+ */
286
+ async refreshState() {
287
+ const state = await this.protocolClient.getState(this.entityId);
288
+ this.updateStateInternal(state);
289
+ return state;
290
+ }
291
+ /**
292
+ * Update state from external source
293
+ */
294
+ updateState(state) {
295
+ this.updateStateInternal(state);
296
+ this.emit('state:changed', state);
297
+ }
298
+ /**
299
+ * Internal state update
300
+ */
301
+ updateStateInternal(state) {
302
+ this._currentTemp = state.currentTemp;
303
+ this._targetTemp = state.targetTemp;
304
+ this._targetTempHigh = state.targetTempHigh;
305
+ this._targetTempLow = state.targetTempLow;
306
+ this._hvacMode = state.hvacMode;
307
+ this._hvacAction = state.hvacAction;
308
+ this._presetMode = state.presetMode;
309
+ this._fanMode = state.fanMode;
310
+ this._swingMode = state.swingMode;
311
+ this._humidity = state.humidity;
312
+ this._targetHumidity = state.targetHumidity;
313
+ this._auxHeat = state.auxHeat;
314
+ }
315
+ // ============================================================================
316
+ // Feature Info
317
+ // ============================================================================
318
+ getFeatureInfo() {
319
+ return {
320
+ ...this.getBaseFeatureInfo(),
321
+ type: 'climate',
322
+ protocol: this.protocol,
323
+ capabilities: this.capabilities,
324
+ currentState: this.getState(),
325
+ };
326
+ }
327
+ }
328
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmVhdHVyZS5jbGltYXRlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vdHMvZmVhdHVyZXMvZmVhdHVyZS5jbGltYXRlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7R0FHRztBQUVILE9BQU8sRUFBRSxPQUFPLEVBQXlCLE1BQU0sdUJBQXVCLENBQUM7QUEwQnZFOzs7Ozs7Ozs7Ozs7OztHQWNHO0FBQ0gsTUFBTSxPQUFPLGNBQWUsU0FBUSxPQUFPO0lBQ3pCLElBQUksR0FBRyxTQUFrQixDQUFDO0lBQzFCLFFBQVEsQ0FBbUI7SUFFM0MsOENBQThDO0lBQzlCLFFBQVEsQ0FBUztJQUVqQyxtQkFBbUI7SUFDSCxZQUFZLENBQXVCO0lBRW5ELG9CQUFvQjtJQUNWLFlBQVksQ0FBVTtJQUN0QixXQUFXLENBQVU7SUFDckIsZUFBZSxDQUFVO0lBQ3pCLGNBQWMsQ0FBVTtJQUN4QixTQUFTLEdBQWMsS0FBSyxDQUFDO0lBQzdCLFdBQVcsQ0FBZTtJQUMxQixXQUFXLENBQVU7SUFDckIsUUFBUSxDQUFVO0lBQ2xCLFVBQVUsQ0FBVTtJQUNwQixTQUFTLENBQVU7SUFDbkIsZUFBZSxDQUFVO0lBQ3pCLFFBQVEsQ0FBVztJQUU3Qix5REFBeUQ7SUFDakQsY0FBYyxDQUF5QjtJQUUvQyxZQUNFLE1BQXdCLEVBQ3hCLElBQVksRUFDWixPQUErQjtRQUUvQixLQUFLLENBQUMsTUFBTSxFQUFFLElBQUksRUFBRSxPQUFPLENBQUMsQ0FBQztRQUM3QixJQUFJLENBQUMsUUFBUSxHQUFHLE9BQU8sQ0FBQyxRQUFRLENBQUM7UUFDakMsSUFBSSxDQUFDLFFBQVEsR0FBRyxPQUFPLENBQUMsUUFBUSxDQUFDO1FBQ2pDLElBQUksQ0FBQyxjQUFjLEdBQUcsT0FBTyxDQUFDLGNBQWMsQ0FBQztRQUU3QyxJQUFJLENBQUMsWUFBWSxHQUFHO1lBQ2xCLFNBQVMsRUFBRSxPQUFPLENBQUMsWUFBWSxFQUFFLFNBQVMsSUFBSSxDQUFDLEtBQUssRUFBRSxNQUFNLEVBQUUsTUFBTSxFQUFFLE1BQU0sQ0FBQztZQUM3RSxXQUFXLEVBQUUsT0FBTyxDQUFDLFlBQVksRUFBRSxXQUFXO1lBQzlDLFFBQVEsRUFBRSxPQUFPLENBQUMsWUFBWSxFQUFFLFFBQVE7WUFDeEMsVUFBVSxFQUFFLE9BQU8sQ0FBQyxZQUFZLEVBQUUsVUFBVTtZQUM1QyxrQkFBa0IsRUFBRSxPQUFPLENBQUMsWUFBWSxFQUFFLGtCQUFrQixJQUFJLElBQUk7WUFDcEUsdUJBQXVCLEVBQUUsT0FBTyxDQUFDLFlBQVksRUFBRSx1QkFBdUIsSUFBSSxLQUFLO1lBQy9FLGdCQUFnQixFQUFFLE9BQU8sQ0FBQyxZQUFZLEVBQUUsZ0JBQWdCLElBQUksS0FBSztZQUNqRSxlQUFlLEVBQUUsT0FBTyxDQUFDLFlBQVksRUFBRSxlQUFlLElBQUksS0FBSztZQUMvRCxPQUFPLEVBQUUsT0FBTyxDQUFDLFlBQVksRUFBRSxPQUFPLElBQUksQ0FBQztZQUMzQyxPQUFPLEVBQUUsT0FBTyxDQUFDLFlBQVksRUFBRSxPQUFPLElBQUksRUFBRTtZQUM1QyxRQUFRLEVBQUUsT0FBTyxDQUFDLFlBQVksRUFBRSxRQUFRLElBQUksR0FBRztZQUMvQyxXQUFXLEVBQUUsT0FBTyxDQUFDLFlBQVksRUFBRSxXQUFXO1lBQzlDLFdBQVcsRUFBRSxPQUFPLENBQUMsWUFBWSxFQUFFLFdBQVc7U0FDL0MsQ0FBQztJQUNKLENBQUM7SUFFRCwrRUFBK0U7SUFDL0UsYUFBYTtJQUNiLCtFQUErRTtJQUUvRTs7T0FFRztJQUNILElBQVcsV0FBVztRQUNwQixPQUFPLElBQUksQ0FBQyxZQUFZLENBQUM7SUFDM0IsQ0FBQztJQUVEOztPQUVHO0lBQ0gsSUFBVyxVQUFVO1FBQ25CLE9BQU8sSUFBSSxDQUFDLFdBQVcsQ0FBQztJQUMxQixDQUFDO0lBRUQ7O09BRUc7SUFDSCxJQUFXLGNBQWM7UUFDdkIsT0FBTyxJQUFJLENBQUMsZUFBZSxDQUFDO0lBQzlCLENBQUM7SUFFRDs7T0FFRztJQUNILElBQVcsYUFBYTtRQUN0QixPQUFPLElBQUksQ0FBQyxjQUFjLENBQUM7SUFDN0IsQ0FBQztJQUVEOztPQUVHO0lBQ0gsSUFBVyxRQUFRO1FBQ2pCLE9BQU8sSUFBSSxDQUFDLFNBQVMsQ0FBQztJQUN4QixDQUFDO0lBRUQ7O09BRUc7SUFDSCxJQUFXLFVBQVU7UUFDbkIsT0FBTyxJQUFJLENBQUMsV0FBVyxDQUFDO0lBQzFCLENBQUM7SUFFRDs7T0FFRztJQUNILElBQVcsVUFBVTtRQUNuQixPQUFPLElBQUksQ0FBQyxXQUFXLENBQUM7SUFDMUIsQ0FBQztJQUVEOztPQUVHO0lBQ0gsSUFBVyxPQUFPO1FBQ2hCLE9BQU8sSUFBSSxDQUFDLFFBQVEsQ0FBQztJQUN2QixDQUFDO0lBRUQ7O09BRUc7SUFDSCxJQUFXLFNBQVM7UUFDbEIsT0FBTyxJQUFJLENBQUMsVUFBVSxDQUFDO0lBQ3pCLENBQUM7SUFFRDs7T0FFRztJQUNILElBQVcsUUFBUTtRQUNqQixPQUFPLElBQUksQ0FBQyxTQUFTLENBQUM7SUFDeEIsQ0FBQztJQUVEOztPQUVHO0lBQ0gsSUFBVyxjQUFjO1FBQ3ZCLE9BQU8sSUFBSSxDQUFDLGVBQWUsQ0FBQztJQUM5QixDQUFDO0lBRUQ7O09BRUc7SUFDSCxJQUFXLE9BQU87UUFDaEIsT0FBTyxJQUFJLENBQUMsUUFBUSxDQUFDO0lBQ3ZCLENBQUM7SUFFRDs7T0FFRztJQUNILElBQVcsU0FBUztRQUNsQixPQUFPLElBQUksQ0FBQyxZQUFZLENBQUMsU0FBUyxDQUFDO0lBQ3JDLENBQUM7SUFFRDs7T0FFRztJQUNILElBQVcsV0FBVztRQUNwQixPQUFPLElBQUksQ0FBQyxZQUFZLENBQUMsV0FBVyxDQUFDO0lBQ3ZDLENBQUM7SUFFRDs7T0FFRztJQUNILElBQVcsUUFBUTtRQUNqQixPQUFPLElBQUksQ0FBQyxZQUFZLENBQUMsUUFBUSxDQUFDO0lBQ3BDLENBQUM7SUFFRCwrRUFBK0U7SUFDL0UsYUFBYTtJQUNiLCtFQUErRTtJQUVyRSxLQUFLLENBQUMsU0FBUztRQUN2QixJQUFJLENBQUM7WUFDSCxNQUFNLEtBQUssR0FBRyxNQUFNLElBQUksQ0FBQyxjQUFjLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsQ0FBQztZQUNoRSxJQUFJLENBQUMsbUJBQW1CLENBQUMsS0FBSyxDQUFDLENBQUM7UUFDbEMsQ0FBQztRQUFDLE1BQU0sQ0FBQztZQUNQLHVDQUF1QztRQUN6QyxDQUFDO0lBQ0gsQ0FBQztJQUVTLEtBQUssQ0FBQyxZQUFZO1FBQzFCLHdCQUF3QjtJQUMxQixDQUFDO0lBRUQsK0VBQStFO0lBQy9FLGtCQUFrQjtJQUNsQiwrRUFBK0U7SUFFL0U7OztPQUdHO0lBQ0ksS0FBSyxDQUFDLFdBQVcsQ0FBQyxJQUFlO1FBQ3RDLElBQUksQ0FBQyxJQUFJLENBQUMsWUFBWSxDQUFDLFNBQVMsQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQztZQUNoRCxNQUFNLElBQUksS0FBSyxDQUFDLGFBQWEsSUFBSSxnQkFBZ0IsQ0FBQyxDQUFDO1FBQ3JELENBQUM7UUFFRCxNQUFNLElBQUksQ0FBQyxjQUFjLENBQUMsV0FBVyxDQUFDLElBQUksQ0FBQyxRQUFRLEVBQUUsSUFBSSxDQUFDLENBQUM7UUFDM0QsSUFBSSxDQUFDLFNBQVMsR0FBRyxJQUFJLENBQUM7UUFDdEIsSUFBSSxDQUFDLElBQUksQ0FBQyxlQUFlLEVBQUUsSUFBSSxDQUFDLFFBQVEsRUFBRSxDQUFDLENBQUM7SUFDOUMsQ0FBQztJQUVEOzs7T0FHRztJQUNJLEtBQUssQ0FBQyxhQUFhLENBQUMsSUFBWTtRQUNyQyxJQUFJLENBQUMsSUFBSSxDQUFDLFlBQVksQ0FBQyxrQkFBa0IsRUFBRSxDQUFDO1lBQzFDLE1BQU0sSUFBSSxLQUFLLENBQUMsb0RBQW9ELENBQUMsQ0FBQztRQUN4RSxDQUFDO1FBRUQsTUFBTSxPQUFPLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FDdEIsSUFBSSxDQUFDLFlBQVksQ0FBQyxPQUFPLEVBQ3pCLElBQUksQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLFlBQVksQ0FBQyxPQUFPLEVBQUUsSUFBSSxDQUFDLENBQzFDLENBQUM7UUFFRixNQUFNLElBQUksQ0FBQyxjQUFjLENBQUMsYUFBYSxDQUFDLElBQUksQ0FBQyxRQUFRLEVBQUUsT0FBTyxDQUFDLENBQUM7UUFDaEUsSUFBSSxDQUFDLFdBQVcsR0FBRyxPQUFPLENBQUM7UUFDM0IsSUFBSSxDQUFDLElBQUksQ0FBQyxlQUFlLEVBQUUsSUFBSSxDQUFDLFFBQVEsRUFBRSxDQUFDLENBQUM7SUFDOUMsQ0FBQztJQUVEOzs7O09BSUc7SUFDSSxLQUFLLENBQUMsa0JBQWtCLENBQUMsR0FBVyxFQUFFLElBQVk7UUFDdkQsSUFBSSxDQUFDLElBQUksQ0FBQyxZQUFZLENBQUMsdUJBQXVCLEVBQUUsQ0FBQztZQUMvQyxNQUFNLElBQUksS0FBSyxDQUFDLG1EQUFtRCxDQUFDLENBQUM7UUFDdkUsQ0FBQztRQUVELE1BQU0sVUFBVSxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLFlBQVksQ0FBQyxPQUFPLEVBQUUsSUFBSSxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsWUFBWSxDQUFDLE9BQU8sRUFBRSxHQUFHLENBQUMsQ0FBQyxDQUFDO1FBQ2pHLE1BQU0sV0FBVyxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLFlBQVksQ0FBQyxPQUFPLEVBQUUsSUFBSSxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsWUFBWSxDQUFDLE9BQU8sRUFBRSxJQUFJLENBQUMsQ0FBQyxDQUFDO1FBRW5HLE1BQU0sSUFBSSxDQUFDLGNBQWMsQ0FBQyxrQkFBa0IsQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLFVBQVUsRUFBRSxXQUFXLENBQUMsQ0FBQztRQUNyRixJQUFJLENBQUMsY0FBYyxHQUFHLFVBQVUsQ0FBQztRQUNqQyxJQUFJLENBQUMsZUFBZSxHQUFHLFdBQVcsQ0FBQztRQUNuQyxJQUFJLENBQUMsSUFBSSxDQUFDLGVBQWUsRUFBRSxJQUFJLENBQUMsUUFBUSxFQUFFLENBQUMsQ0FBQztJQUM5QyxDQUFDO0lBRUQ7OztPQUdHO0lBQ0ksS0FBSyxDQUFDLGFBQWEsQ0FBQyxNQUFjO1FBQ3ZDLElBQUksQ0FBQyxJQUFJLENBQUMsWUFBWSxDQUFDLFdBQVcsRUFBRSxRQUFRLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQztZQUNyRCxNQUFNLElBQUksS0FBSyxDQUFDLGVBQWUsTUFBTSxnQkFBZ0IsQ0FBQyxDQUFDO1FBQ3pELENBQUM7UUFFRCxNQUFNLElBQUksQ0FBQyxjQUFjLENBQUMsYUFBYSxDQUFDLElBQUksQ0FBQyxRQUFRLEVBQUUsTUFBTSxDQUFDLENBQUM7UUFDL0QsSUFBSSxDQUFDLFdBQVcsR0FBRyxNQUFNLENBQUM7UUFDMUIsSUFBSSxDQUFDLElBQUksQ0FBQyxlQUFlLEVBQUUsSUFBSSxDQUFDLFFBQVEsRUFBRSxDQUFDLENBQUM7SUFDOUMsQ0FBQztJQUVEOzs7T0FHRztJQUNJLEtBQUssQ0FBQyxVQUFVLENBQUMsSUFBWTtRQUNsQyxJQUFJLENBQUMsSUFBSSxDQUFDLFlBQVksQ0FBQyxRQUFRLEVBQUUsUUFBUSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUM7WUFDaEQsTUFBTSxJQUFJLEtBQUssQ0FBQyxZQUFZLElBQUksZ0JBQWdCLENBQUMsQ0FBQztRQUNwRCxDQUFDO1FBRUQsTUFBTSxJQUFJLENBQUMsY0FBYyxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLElBQUksQ0FBQyxDQUFDO1FBQzFELElBQUksQ0FBQyxRQUFRLEdBQUcsSUFBSSxDQUFDO1FBQ3JCLElBQUksQ0FBQyxJQUFJLENBQUMsZUFBZSxFQUFFLElBQUksQ0FBQyxRQUFRLEVBQUUsQ0FBQyxDQUFDO0lBQzlDLENBQUM7SUFFRDs7O09BR0c7SUFDSSxLQUFLLENBQUMsWUFBWSxDQUFDLElBQVk7UUFDcEMsSUFBSSxDQUFDLElBQUksQ0FBQyxZQUFZLENBQUMsVUFBVSxFQUFFLFFBQVEsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDO1lBQ2xELE1BQU0sSUFBSSxLQUFLLENBQUMsY0FBYyxJQUFJLGdCQUFnQixDQUFDLENBQUM7UUFDdEQsQ0FBQztRQUVELE1BQU0sSUFBSSxDQUFDLGNBQWMsQ0FBQyxZQUFZLENBQUMsSUFBSSxDQUFDLFFBQVEsRUFBRSxJQUFJLENBQUMsQ0FBQztRQUM1RCxJQUFJLENBQUMsVUFBVSxHQUFHLElBQUksQ0FBQztRQUN2QixJQUFJLENBQUMsSUFBSSxDQUFDLGVBQWUsRUFBRSxJQUFJLENBQUMsUUFBUSxFQUFFLENBQUMsQ0FBQztJQUM5QyxDQUFDO0lBRUQ7OztPQUdHO0lBQ0ksS0FBSyxDQUFDLFVBQVUsQ0FBQyxPQUFnQjtRQUN0QyxJQUFJLENBQUMsSUFBSSxDQUFDLFlBQVksQ0FBQyxlQUFlLEVBQUUsQ0FBQztZQUN2QyxNQUFNLElBQUksS0FBSyxDQUFDLDBDQUEwQyxDQUFDLENBQUM7UUFDOUQsQ0FBQztRQUVELE1BQU0sSUFBSSxDQUFDLGNBQWMsQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUFDLFFBQVEsRUFBRSxPQUFPLENBQUMsQ0FBQztRQUM3RCxJQUFJLENBQUMsUUFBUSxHQUFHLE9BQU8sQ0FBQztRQUN4QixJQUFJLENBQUMsSUFBSSxDQUFDLGVBQWUsRUFBRSxJQUFJLENBQUMsUUFBUSxFQUFFLENBQUMsQ0FBQztJQUM5QyxDQUFDO0lBRUQ7O09BRUc7SUFDSSxRQUFRO1FBQ2IsT0FBTztZQUNMLFdBQVcsRUFBRSxJQUFJLENBQUMsWUFBWTtZQUM5QixVQUFVLEVBQUUsSUFBSSxDQUFDLFdBQVc7WUFDNUIsY0FBYyxFQUFFLElBQUksQ0FBQyxlQUFlO1lBQ3BDLGFBQWEsRUFBRSxJQUFJLENBQUMsY0FBYztZQUNsQyxRQUFRLEVBQUUsSUFBSSxDQUFDLFNBQVM7WUFDeEIsVUFBVSxFQUFFLElBQUksQ0FBQyxXQUFXO1lBQzVCLFVBQVUsRUFBRSxJQUFJLENBQUMsV0FBVztZQUM1QixPQUFPLEVBQUUsSUFBSSxDQUFDLFFBQVE7WUFDdEIsU0FBUyxFQUFFLElBQUksQ0FBQyxVQUFVO1lBQzFCLFFBQVEsRUFBRSxJQUFJLENBQUMsU0FBUztZQUN4QixjQUFjLEVBQUUsSUFBSSxDQUFDLGVBQWU7WUFDcEMsT0FBTyxFQUFFLElBQUksQ0FBQyxRQUFRO1NBQ3ZCLENBQUM7SUFDSixDQUFDO0lBRUQ7O09BRUc7SUFDSSxLQUFLLENBQUMsWUFBWTtRQUN2QixNQUFNLEtBQUssR0FBRyxNQUFNLElBQUksQ0FBQyxjQUFjLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsQ0FBQztRQUNoRSxJQUFJLENBQUMsbUJBQW1CLENBQUMsS0FBSyxDQUFDLENBQUM7UUFDaEMsT0FBTyxLQUFLLENBQUM7SUFDZixDQUFDO0lBRUQ7O09BRUc7SUFDSSxXQUFXLENBQUMsS0FBb0I7UUFDckMsSUFBSSxDQUFDLG1CQUFtQixDQUFDLEtBQUssQ0FBQyxDQUFDO1FBQ2hDLElBQUksQ0FBQyxJQUFJLENBQUMsZUFBZSxFQUFFLEtBQUssQ0FBQyxDQUFDO0lBQ3BDLENBQUM7SUFFRDs7T0FFRztJQUNLLG1CQUFtQixDQUFDLEtBQW9CO1FBQzlDLElBQUksQ0FBQyxZQUFZLEdBQUcsS0FBSyxDQUFDLFdBQVcsQ0FBQztRQUN0QyxJQUFJLENBQUMsV0FBVyxHQUFHLEtBQUssQ0FBQyxVQUFVLENBQUM7UUFDcEMsSUFBSSxDQUFDLGVBQWUsR0FBRyxLQUFLLENBQUMsY0FBYyxDQUFDO1FBQzVDLElBQUksQ0FBQyxjQUFjLEdBQUcsS0FBSyxDQUFDLGFBQWEsQ0FBQztRQUMxQyxJQUFJLENBQUMsU0FBUyxHQUFHLEtBQUssQ0FBQyxRQUFRLENBQUM7UUFDaEMsSUFBSSxDQUFDLFdBQVcsR0FBRyxLQUFLLENBQUMsVUFBVSxDQUFDO1FBQ3BDLElBQUksQ0FBQyxXQUFXLEdBQUcsS0FBSyxDQUFDLFVBQVUsQ0FBQztRQUNwQyxJQUFJLENBQUMsUUFBUSxHQUFHLEtBQUssQ0FBQyxPQUFPLENBQUM7UUFDOUIsSUFBSSxDQUFDLFVBQVUsR0FBRyxLQUFLLENBQUMsU0FBUyxDQUFDO1FBQ2xDLElBQUksQ0FBQyxTQUFTLEdBQUcsS0FBSyxDQUFDLFFBQVEsQ0FBQztRQUNoQyxJQUFJLENBQUMsZUFBZSxHQUFHLEtBQUssQ0FBQyxjQUFjLENBQUM7UUFDNUMsSUFBSSxDQUFDLFFBQVEsR0FBRyxLQUFLLENBQUMsT0FBTyxDQUFDO0lBQ2hDLENBQUM7SUFFRCwrRUFBK0U7SUFDL0UsZUFBZTtJQUNmLCtFQUErRTtJQUV4RSxjQUFjO1FBQ25CLE9BQU87WUFDTCxHQUFHLElBQUksQ0FBQyxrQkFBa0IsRUFBRTtZQUM1QixJQUFJLEVBQUUsU0FBUztZQUNmLFFBQVEsRUFBRSxJQUFJLENBQUMsUUFBUTtZQUN2QixZQUFZLEVBQUUsSUFBSSxDQUFDLFlBQVk7WUFDL0IsWUFBWSxFQUFFLElBQUksQ0FBQyxRQUFRLEVBQUU7U0FDOUIsQ0FBQztJQUNKLENBQUM7Q0FDRiJ9
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Cover Feature
3
+ * Provides control for covers, blinds, garage doors, etc.
4
+ */
5
+ import { Feature, type TDeviceReference } from './feature.abstract.js';
6
+ import type { IFeatureOptions } from '../interfaces/feature.interfaces.js';
7
+ import type { TCoverProtocol, TCoverDeviceClass, TCoverState, ICoverCapabilities, ICoverStateInfo, ICoverFeatureInfo, ICoverProtocolClient } from '../interfaces/smarthome.interfaces.js';
8
+ /**
9
+ * Options for creating a CoverFeature
10
+ */
11
+ export interface ICoverFeatureOptions extends IFeatureOptions {
12
+ /** Protocol type */
13
+ protocol: TCoverProtocol;
14
+ /** Entity ID (for Home Assistant) */
15
+ entityId: string;
16
+ /** Protocol client for controlling the cover */
17
+ protocolClient: ICoverProtocolClient;
18
+ /** Device class */
19
+ deviceClass?: TCoverDeviceClass;
20
+ /** Cover capabilities */
21
+ capabilities?: Partial<ICoverCapabilities>;
22
+ }
23
+ /**
24
+ * Cover Feature - control for blinds, garage doors, etc.
25
+ *
26
+ * Protocol-agnostic: works with Home Assistant, MQTT, Somfy, etc.
27
+ *
28
+ * @example
29
+ * ```typescript
30
+ * const cover = device.getFeature<CoverFeature>('cover');
31
+ * if (cover) {
32
+ * await cover.open();
33
+ * await cover.setPosition(50); // 50% open
34
+ * }
35
+ * ```
36
+ */
37
+ export declare class CoverFeature extends Feature {
38
+ readonly type: "cover";
39
+ readonly protocol: TCoverProtocol;
40
+ /** Entity ID (e.g., "cover.garage_door") */
41
+ readonly entityId: string;
42
+ /** Capabilities */
43
+ readonly capabilities: ICoverCapabilities;
44
+ /** Current cover state (not connection state) */
45
+ protected _coverState: TCoverState;
46
+ protected _position?: number;
47
+ protected _tiltPosition?: number;
48
+ /** Protocol client for controlling the cover */
49
+ private protocolClient;
50
+ constructor(device: TDeviceReference, port: number, options: ICoverFeatureOptions);
51
+ /**
52
+ * Get current state (cached)
53
+ */
54
+ get coverState(): TCoverState;
55
+ /**
56
+ * Get current position 0-100 (cached)
57
+ * 0 = closed, 100 = fully open
58
+ */
59
+ get position(): number | undefined;
60
+ /**
61
+ * Get current tilt position 0-100 (cached)
62
+ */
63
+ get tiltPosition(): number | undefined;
64
+ /**
65
+ * Check if cover is open
66
+ */
67
+ get isOpen(): boolean;
68
+ /**
69
+ * Check if cover is closed
70
+ */
71
+ get isClosed(): boolean;
72
+ /**
73
+ * Check if cover is opening
74
+ */
75
+ get isOpening(): boolean;
76
+ /**
77
+ * Check if cover is closing
78
+ */
79
+ get isClosing(): boolean;
80
+ protected doConnect(): Promise<void>;
81
+ protected doDisconnect(): Promise<void>;
82
+ /**
83
+ * Open the cover
84
+ */
85
+ open(): Promise<void>;
86
+ /**
87
+ * Close the cover
88
+ */
89
+ close(): Promise<void>;
90
+ /**
91
+ * Stop the cover
92
+ */
93
+ stop(): Promise<void>;
94
+ /**
95
+ * Set cover position
96
+ * @param position Position 0-100 (0 = closed, 100 = open)
97
+ */
98
+ setPosition(position: number): Promise<void>;
99
+ /**
100
+ * Set tilt position
101
+ * @param position Tilt position 0-100
102
+ */
103
+ setTiltPosition(position: number): Promise<void>;
104
+ /**
105
+ * Get current state as object
106
+ */
107
+ getState(): ICoverStateInfo;
108
+ /**
109
+ * Refresh state from the device
110
+ */
111
+ refreshState(): Promise<ICoverStateInfo>;
112
+ /**
113
+ * Update state from external source
114
+ */
115
+ updateState(state: ICoverStateInfo): void;
116
+ /**
117
+ * Internal state update
118
+ */
119
+ private updateStateInternal;
120
+ getFeatureInfo(): ICoverFeatureInfo;
121
+ }
@@ -0,0 +1,217 @@
1
+ /**
2
+ * Cover Feature
3
+ * Provides control for covers, blinds, garage doors, etc.
4
+ */
5
+ import { Feature } from './feature.abstract.js';
6
+ /**
7
+ * Cover Feature - control for blinds, garage doors, etc.
8
+ *
9
+ * Protocol-agnostic: works with Home Assistant, MQTT, Somfy, etc.
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * const cover = device.getFeature<CoverFeature>('cover');
14
+ * if (cover) {
15
+ * await cover.open();
16
+ * await cover.setPosition(50); // 50% open
17
+ * }
18
+ * ```
19
+ */
20
+ export class CoverFeature extends Feature {
21
+ type = 'cover';
22
+ protocol;
23
+ /** Entity ID (e.g., "cover.garage_door") */
24
+ entityId;
25
+ /** Capabilities */
26
+ capabilities;
27
+ /** Current cover state (not connection state) */
28
+ _coverState = 'unknown';
29
+ _position;
30
+ _tiltPosition;
31
+ /** Protocol client for controlling the cover */
32
+ protocolClient;
33
+ constructor(device, port, options) {
34
+ super(device, port, options);
35
+ this.protocol = options.protocol;
36
+ this.entityId = options.entityId;
37
+ this.protocolClient = options.protocolClient;
38
+ this.capabilities = {
39
+ deviceClass: options.deviceClass,
40
+ supportsOpen: options.capabilities?.supportsOpen ?? true,
41
+ supportsClose: options.capabilities?.supportsClose ?? true,
42
+ supportsStop: options.capabilities?.supportsStop ?? true,
43
+ supportsPosition: options.capabilities?.supportsPosition ?? false,
44
+ supportsTilt: options.capabilities?.supportsTilt ?? false,
45
+ };
46
+ }
47
+ // ============================================================================
48
+ // Properties
49
+ // ============================================================================
50
+ /**
51
+ * Get current state (cached)
52
+ */
53
+ get coverState() {
54
+ return this._coverState;
55
+ }
56
+ /**
57
+ * Get current position 0-100 (cached)
58
+ * 0 = closed, 100 = fully open
59
+ */
60
+ get position() {
61
+ return this._position;
62
+ }
63
+ /**
64
+ * Get current tilt position 0-100 (cached)
65
+ */
66
+ get tiltPosition() {
67
+ return this._tiltPosition;
68
+ }
69
+ /**
70
+ * Check if cover is open
71
+ */
72
+ get isOpen() {
73
+ return this._coverState === 'open';
74
+ }
75
+ /**
76
+ * Check if cover is closed
77
+ */
78
+ get isClosed() {
79
+ return this._coverState === 'closed';
80
+ }
81
+ /**
82
+ * Check if cover is opening
83
+ */
84
+ get isOpening() {
85
+ return this._coverState === 'opening';
86
+ }
87
+ /**
88
+ * Check if cover is closing
89
+ */
90
+ get isClosing() {
91
+ return this._coverState === 'closing';
92
+ }
93
+ // ============================================================================
94
+ // Connection
95
+ // ============================================================================
96
+ async doConnect() {
97
+ try {
98
+ const state = await this.protocolClient.getState(this.entityId);
99
+ this.updateStateInternal(state);
100
+ }
101
+ catch {
102
+ // Ignore errors fetching initial state
103
+ }
104
+ }
105
+ async doDisconnect() {
106
+ // Nothing to disconnect
107
+ }
108
+ // ============================================================================
109
+ // Cover Control
110
+ // ============================================================================
111
+ /**
112
+ * Open the cover
113
+ */
114
+ async open() {
115
+ if (!this.capabilities.supportsOpen) {
116
+ throw new Error('Cover does not support open');
117
+ }
118
+ await this.protocolClient.open(this.entityId);
119
+ this._coverState = 'opening';
120
+ this.emit('state:changed', this.getState());
121
+ }
122
+ /**
123
+ * Close the cover
124
+ */
125
+ async close() {
126
+ if (!this.capabilities.supportsClose) {
127
+ throw new Error('Cover does not support close');
128
+ }
129
+ await this.protocolClient.close(this.entityId);
130
+ this._coverState = 'closing';
131
+ this.emit('state:changed', this.getState());
132
+ }
133
+ /**
134
+ * Stop the cover
135
+ */
136
+ async stop() {
137
+ if (!this.capabilities.supportsStop) {
138
+ throw new Error('Cover does not support stop');
139
+ }
140
+ await this.protocolClient.stop(this.entityId);
141
+ this._coverState = 'stopped';
142
+ this.emit('state:changed', this.getState());
143
+ }
144
+ /**
145
+ * Set cover position
146
+ * @param position Position 0-100 (0 = closed, 100 = open)
147
+ */
148
+ async setPosition(position) {
149
+ if (!this.capabilities.supportsPosition) {
150
+ throw new Error('Cover does not support position control');
151
+ }
152
+ const clamped = Math.max(0, Math.min(100, Math.round(position)));
153
+ await this.protocolClient.setPosition(this.entityId, clamped);
154
+ this._position = clamped;
155
+ this._coverState = clamped === 0 ? 'closed' : clamped === 100 ? 'open' : 'stopped';
156
+ this.emit('state:changed', this.getState());
157
+ }
158
+ /**
159
+ * Set tilt position
160
+ * @param position Tilt position 0-100
161
+ */
162
+ async setTiltPosition(position) {
163
+ if (!this.capabilities.supportsTilt) {
164
+ throw new Error('Cover does not support tilt control');
165
+ }
166
+ const clamped = Math.max(0, Math.min(100, Math.round(position)));
167
+ await this.protocolClient.setTiltPosition(this.entityId, clamped);
168
+ this._tiltPosition = clamped;
169
+ this.emit('state:changed', this.getState());
170
+ }
171
+ /**
172
+ * Get current state as object
173
+ */
174
+ getState() {
175
+ return {
176
+ state: this._coverState,
177
+ position: this._position,
178
+ tiltPosition: this._tiltPosition,
179
+ };
180
+ }
181
+ /**
182
+ * Refresh state from the device
183
+ */
184
+ async refreshState() {
185
+ const state = await this.protocolClient.getState(this.entityId);
186
+ this.updateStateInternal(state);
187
+ return state;
188
+ }
189
+ /**
190
+ * Update state from external source
191
+ */
192
+ updateState(state) {
193
+ this.updateStateInternal(state);
194
+ this.emit('state:changed', state);
195
+ }
196
+ /**
197
+ * Internal state update
198
+ */
199
+ updateStateInternal(state) {
200
+ this._coverState = state.state;
201
+ this._position = state.position;
202
+ this._tiltPosition = state.tiltPosition;
203
+ }
204
+ // ============================================================================
205
+ // Feature Info
206
+ // ============================================================================
207
+ getFeatureInfo() {
208
+ return {
209
+ ...this.getBaseFeatureInfo(),
210
+ type: 'cover',
211
+ protocol: this.protocol,
212
+ capabilities: this.capabilities,
213
+ currentState: this.getState(),
214
+ };
215
+ }
216
+ }
217
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmVhdHVyZS5jb3Zlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzL2ZlYXR1cmVzL2ZlYXR1cmUuY292ZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7OztHQUdHO0FBRUgsT0FBTyxFQUFFLE9BQU8sRUFBeUIsTUFBTSx1QkFBdUIsQ0FBQztBQTRCdkU7Ozs7Ozs7Ozs7Ozs7R0FhRztBQUNILE1BQU0sT0FBTyxZQUFhLFNBQVEsT0FBTztJQUN2QixJQUFJLEdBQUcsT0FBZ0IsQ0FBQztJQUN4QixRQUFRLENBQWlCO0lBRXpDLDRDQUE0QztJQUM1QixRQUFRLENBQVM7SUFFakMsbUJBQW1CO0lBQ0gsWUFBWSxDQUFxQjtJQUVqRCxpREFBaUQ7SUFDdkMsV0FBVyxHQUFnQixTQUFTLENBQUM7SUFDckMsU0FBUyxDQUFVO0lBQ25CLGFBQWEsQ0FBVTtJQUVqQyxnREFBZ0Q7SUFDeEMsY0FBYyxDQUF1QjtJQUU3QyxZQUNFLE1BQXdCLEVBQ3hCLElBQVksRUFDWixPQUE2QjtRQUU3QixLQUFLLENBQUMsTUFBTSxFQUFFLElBQUksRUFBRSxPQUFPLENBQUMsQ0FBQztRQUM3QixJQUFJLENBQUMsUUFBUSxHQUFHLE9BQU8sQ0FBQyxRQUFRLENBQUM7UUFDakMsSUFBSSxDQUFDLFFBQVEsR0FBRyxPQUFPLENBQUMsUUFBUSxDQUFDO1FBQ2pDLElBQUksQ0FBQyxjQUFjLEdBQUcsT0FBTyxDQUFDLGNBQWMsQ0FBQztRQUU3QyxJQUFJLENBQUMsWUFBWSxHQUFHO1lBQ2xCLFdBQVcsRUFBRSxPQUFPLENBQUMsV0FBVztZQUNoQyxZQUFZLEVBQUUsT0FBTyxDQUFDLFlBQVksRUFBRSxZQUFZLElBQUksSUFBSTtZQUN4RCxhQUFhLEVBQUUsT0FBTyxDQUFDLFlBQVksRUFBRSxhQUFhLElBQUksSUFBSTtZQUMxRCxZQUFZLEVBQUUsT0FBTyxDQUFDLFlBQVksRUFBRSxZQUFZLElBQUksSUFBSTtZQUN4RCxnQkFBZ0IsRUFBRSxPQUFP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