@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.
- package/.gitea/workflows/default_nottags.yaml +66 -0
- package/.gitea/workflows/default_tags.yaml +124 -0
- package/.vscode/launch.json +11 -0
- package/.vscode/settings.json +26 -0
- package/changelog.md +91 -0
- package/dist_ts/00_commitinfo_data.d.ts +8 -0
- package/dist_ts/00_commitinfo_data.js +9 -0
- package/dist_ts/device/device.classes.device.d.ts +205 -0
- package/dist_ts/device/device.classes.device.js +344 -0
- package/dist_ts/devicemanager.classes.devicemanager.d.ts +210 -0
- package/dist_ts/devicemanager.classes.devicemanager.js +835 -0
- package/dist_ts/discovery/discovery.classes.homeassistant.d.ts +101 -0
- package/dist_ts/discovery/discovery.classes.homeassistant.js +313 -0
- package/dist_ts/discovery/discovery.classes.mdns.d.ts +90 -0
- package/dist_ts/discovery/discovery.classes.mdns.js +291 -0
- package/dist_ts/discovery/discovery.classes.networkscanner.d.ts +64 -0
- package/dist_ts/discovery/discovery.classes.networkscanner.js +535 -0
- package/dist_ts/discovery/discovery.classes.ssdp.d.ts +112 -0
- package/dist_ts/discovery/discovery.classes.ssdp.js +255 -0
- package/dist_ts/factories/index.d.ts +190 -0
- package/dist_ts/factories/index.js +350 -0
- package/dist_ts/features/feature.abstract.d.ts +118 -0
- package/dist_ts/features/feature.abstract.js +172 -0
- package/dist_ts/features/feature.camera.d.ts +95 -0
- package/dist_ts/features/feature.camera.js +162 -0
- package/dist_ts/features/feature.climate.d.ts +174 -0
- package/dist_ts/features/feature.climate.js +328 -0
- package/dist_ts/features/feature.cover.d.ts +121 -0
- package/dist_ts/features/feature.cover.js +217 -0
- package/dist_ts/features/feature.fan.d.ts +127 -0
- package/dist_ts/features/feature.fan.js +235 -0
- package/dist_ts/features/feature.light.d.ts +156 -0
- package/dist_ts/features/feature.light.js +296 -0
- package/dist_ts/features/feature.lock.d.ts +103 -0
- package/dist_ts/features/feature.lock.js +170 -0
- package/dist_ts/features/feature.playback.d.ts +98 -0
- package/dist_ts/features/feature.playback.js +191 -0
- package/dist_ts/features/feature.power.d.ts +79 -0
- package/dist_ts/features/feature.power.js +173 -0
- package/dist_ts/features/feature.print.d.ts +78 -0
- package/dist_ts/features/feature.print.js +226 -0
- package/dist_ts/features/feature.scan.d.ts +79 -0
- package/dist_ts/features/feature.scan.js +315 -0
- package/dist_ts/features/feature.sensor.d.ts +95 -0
- package/dist_ts/features/feature.sensor.js +147 -0
- package/dist_ts/features/feature.snmp.d.ts +88 -0
- package/dist_ts/features/feature.snmp.js +175 -0
- package/dist_ts/features/feature.switch.d.ts +75 -0
- package/dist_ts/features/feature.switch.js +126 -0
- package/dist_ts/features/feature.volume.d.ts +104 -0
- package/dist_ts/features/feature.volume.js +186 -0
- package/dist_ts/features/index.d.ts +19 -0
- package/dist_ts/features/index.js +23 -0
- package/dist_ts/helpers/helpers.iprange.d.ts +35 -0
- package/dist_ts/helpers/helpers.iprange.js +151 -0
- package/dist_ts/helpers/helpers.retry.d.ts +25 -0
- package/dist_ts/helpers/helpers.retry.js +69 -0
- package/dist_ts/index.d.ts +15 -0
- package/dist_ts/index.js +40 -0
- package/dist_ts/interfaces/feature.interfaces.d.ts +255 -0
- package/dist_ts/interfaces/feature.interfaces.js +6 -0
- package/dist_ts/interfaces/homeassistant.interfaces.d.ts +550 -0
- package/dist_ts/interfaces/homeassistant.interfaces.js +111 -0
- package/dist_ts/interfaces/index.d.ts +330 -0
- package/dist_ts/interfaces/index.js +16 -0
- package/dist_ts/interfaces/smarthome.interfaces.d.ts +282 -0
- package/dist_ts/interfaces/smarthome.interfaces.js +7 -0
- package/dist_ts/paths.d.ts +1 -0
- package/dist_ts/paths.js +3 -0
- package/dist_ts/plugins.d.ts +23 -0
- package/dist_ts/plugins.js +30 -0
- package/dist_ts/protocols/index.d.ts +12 -0
- package/dist_ts/protocols/index.js +21 -0
- package/dist_ts/protocols/protocol.escl.d.ts +58 -0
- package/dist_ts/protocols/protocol.escl.js +364 -0
- package/dist_ts/protocols/protocol.homeassistant.d.ts +248 -0
- package/dist_ts/protocols/protocol.homeassistant.js +611 -0
- package/dist_ts/protocols/protocol.ipp.d.ts +45 -0
- package/dist_ts/protocols/protocol.ipp.js +284 -0
- package/dist_ts/protocols/protocol.nut.d.ts +160 -0
- package/dist_ts/protocols/protocol.nut.js +364 -0
- package/dist_ts/protocols/protocol.sane.d.ts +104 -0
- package/dist_ts/protocols/protocol.sane.js +597 -0
- package/dist_ts/protocols/protocol.snmp.d.ts +133 -0
- package/dist_ts/protocols/protocol.snmp.js +333 -0
- package/dist_ts/protocols/protocol.upnp.d.ts +207 -0
- package/dist_ts/protocols/protocol.upnp.js +442 -0
- package/dist_ts/protocols/protocol.upssnmp.d.ts +160 -0
- package/dist_ts/protocols/protocol.upssnmp.js +263 -0
- package/npmextra.json +24 -0
- package/package.json +39 -0
- package/readme.hints.md +115 -0
- package/readme.md +624 -0
- package/test/test.ts +261 -0
- package/ts/00_commitinfo_data.ts +8 -0
- package/ts/device/device.classes.device.ts +454 -0
- package/ts/devicemanager.classes.devicemanager.ts +1219 -0
- package/ts/discovery/discovery.classes.homeassistant.ts +376 -0
- package/ts/discovery/discovery.classes.mdns.ts +347 -0
- package/ts/discovery/discovery.classes.networkscanner.ts +648 -0
- package/ts/discovery/discovery.classes.ssdp.ts +357 -0
- package/ts/factories/index.ts +696 -0
- package/ts/features/feature.abstract.ts +251 -0
- package/ts/features/feature.camera.ts +214 -0
- package/ts/features/feature.climate.ts +407 -0
- package/ts/features/feature.cover.ts +278 -0
- package/ts/features/feature.fan.ts +296 -0
- package/ts/features/feature.light.ts +369 -0
- package/ts/features/feature.lock.ts +223 -0
- package/ts/features/feature.playback.ts +246 -0
- package/ts/features/feature.power.ts +231 -0
- package/ts/features/feature.print.ts +297 -0
- package/ts/features/feature.scan.ts +370 -0
- package/ts/features/feature.sensor.ts +202 -0
- package/ts/features/feature.snmp.ts +235 -0
- package/ts/features/feature.switch.ts +170 -0
- package/ts/features/feature.volume.ts +256 -0
- package/ts/features/index.ts +25 -0
- package/ts/helpers/helpers.iprange.ts +172 -0
- package/ts/helpers/helpers.retry.ts +100 -0
- package/ts/index.ts +165 -0
- package/ts/interfaces/feature.interfaces.ts +359 -0
- package/ts/interfaces/homeassistant.interfaces.ts +666 -0
- package/ts/interfaces/index.ts +421 -0
- package/ts/interfaces/smarthome.interfaces.ts +421 -0
- package/ts/paths.ts +5 -0
- package/ts/plugins.ts +41 -0
- package/ts/protocols/index.ts +59 -0
- package/ts/protocols/protocol.escl.ts +439 -0
- package/ts/protocols/protocol.homeassistant.ts +737 -0
- package/ts/protocols/protocol.ipp.ts +329 -0
- package/ts/protocols/protocol.nut.ts +471 -0
- package/ts/protocols/protocol.sane.ts +694 -0
- package/ts/protocols/protocol.snmp.ts +439 -0
- package/ts/protocols/protocol.upnp.ts +627 -0
- package/ts/protocols/protocol.upssnmp.ts +377 -0
- 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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmVhdHVyZS5jbGltYXRlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vdHMvZmVhdHVyZXMvZmVhdHVyZS5jbGltYXRlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7R0FHRztBQUVILE9BQU8sRUFBRSxPQUFPLEVBQXlCLE1BQU0sdUJBQXVCLENBQUM7QUEwQnZFOzs7Ozs7Ozs7Ozs7OztHQWNHO0FBQ0gsTUFBTSxPQUFPLGNBQWUsU0FBUSxPQUFPO0lBQ3pCLElBQUksR0FBRyxTQUFrQixDQUFDO0lBQzFCLFFBQVEsQ0FBbUI7SUFFM0MsOENBQThDO0lBQzlCLFFBQVEsQ0FBUztJQUVqQyxtQkFBbUI7SUFDSCxZQUFZLENBQXVCO0lBRW5ELG9CQUFvQjtJQUNWLFlBQVksQ0FBVTtJQUN0QixXQUFXLENBQVU7SUFDckIsZUFBZSxDQUFVO0lBQ3pCLGNBQWMsQ0FBVTtJQUN4QixTQUFTLEdBQWMsS0FBSyxDQUFDO0lBQzdCLFdBQVcsQ0FBZTtJQUMxQixXQUFXLENBQVU7SUFDckIsUUFBUSxDQUFVO0lBQ2xCLFVBQVUsQ0FBVTtJQUNwQixTQUFTLENBQVU7SUFDbkIsZUFBZSxDQUFVO0lBQ3pCLFFBQVEsQ0FBVztJQUU3Qix5REFBeUQ7SUFDakQsY0FBYyxDQUF5QjtJQUUvQyxZQUNFLE1BQXdCLEVBQ3hCLElBQVksRUFDWixPQUErQjtRQUUvQixLQUFLLENBQUMsTUFBTSxFQUFFLElBQUksRUFBRSxPQUFPLENBQUMsQ0FBQztRQUM3QixJQUFJLENBQUMsUUFBUSxHQUFHLE9BQU8sQ0FBQyxRQUFRLENBQUM7UUFDakMsSUFBSSxDQUFDLFFBQVEsR0FBRyxPQUFPLENBQUMsUUFBUSxDQUFDO1FBQ2pDLElBQUksQ0FBQyxjQUFjLEdBQUcsT0FBTyxDQUFDLGNBQWMsQ0FBQztRQUU3QyxJQUFJLENBQUMsWUFBWSxHQUFHO1lBQ2xCLFNBQVMsRUFBRSxPQUFPLENBQUMsWUFBWSxFQUFFLFNBQVMsSUFBSSxDQUFDLEtBQUssRUFB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
|
|
@@ -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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
|