-
Notifications
You must be signed in to change notification settings - Fork 104
Expand file tree
/
Copy pathWindowCoveringServer.ts
More file actions
651 lines (592 loc) · 28.3 KB
/
WindowCoveringServer.ts
File metadata and controls
651 lines (592 loc) · 28.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
/**
* @license
* Copyright 2022-2026 Matter.js Authors
* SPDX-License-Identifier: Apache-2.0
*/
import { Diagnostic, ImplementationError, isDeepEqual, Logger, MaybePromise, Worker } from "@matter/general";
import { StatusCode, StatusResponseError } from "@matter/types";
import { WindowCovering } from "@matter/types/clusters/window-covering";
import { WindowCoveringBehavior } from "./WindowCoveringBehavior.js";
const logger = Logger.get("WindowCoveringServer");
const WindowCoveringBase = WindowCoveringBehavior.with(
WindowCovering.Feature.Lift,
WindowCovering.Feature.Tilt,
WindowCovering.Feature.PositionAwareLift,
WindowCovering.Feature.PositionAwareTilt,
);
/** What element should move? */
export enum MovementType {
Lift,
Tilt,
}
/** Status of the Calibration mode. */
export enum CalibrationMode {
Enabled,
Running,
Disabled,
}
/**
* Direction the Window covering should move.
* The special state "DefinedByPosition" is used to indicate that the direction should be determined by the target
* position and the implementation needs to determine it himself.
*/
export enum MovementDirection {
Open,
Close,
DefinedByPosition,
}
const WC_PERCENT100THS_MIN_OPEN = 0;
const WC_PERCENT100THS_MAX_CLOSED = 10000;
const WC_PERCENT100THS_COEFFICIENT = 100;
/**
* This is the default server implementation of {@link WindowCoveringBehavior}.
*
* This implementation includes all features of {@link WindowCovering.Cluster} and implements all mandatory commands.
* You should use {@link WindowCoveringServer.with} to specialize the class for the features your implementation
* supports.
*
* If you enable position awareness (positionAware* features), the default logic automatically syncs current positions
* and operational state when the currentPosition*Percent100ths attributes change. You should update
* currentPosition*Percent100ths with the actual position from your device. This updates other attributes
* automatically.
*
* When targetPosition*Percent100ths attributes change, operational state updates bases on the current and target
* values.
*
* If you do not override {@link handleMovement} the default implementation updates current position to the target
* position immediately.
*
* In addition to Matter attributes, {@link WindowCoveringBaseServer.State} includes the following configuration
* properties:
*
* * supportsMaintenanceMode (default true): Set to false if the device has no maintenance mode
*
* The internal state allows to configure implementation details when extending the class:
* * supportsCalibration (default false): Set to true if the device supports calibration. You must implement
{@link WindowCoveringBaseServer.executeCalibration} to perform actual calibration.
* * disableOperationalModeHandling (default false): Set to true if you want to handle the operational status yourself
*
* When developing for specific hardware you should extend {@link WindowCoveringServer} and implement the following
* methods to map movement to your device. The default implementation maps Matter commands to these methods. The benefit
* of this structure is that basic data validations and option checks are already done and you can focus on the actual
* hardware interaction:
*
* * {@link WindowCoveringBaseServer.handleMovement} Logic to actually move the device. Via Parameters the movement
* type (Lift/Tilt), direction, target percentage and information if motor is configured reversed are provided. When
* the device moves the current Positions (if supported by the device) are updated with the movement. The
* operational state is automatically updated by the default implementation based on current and target values of
* the cluster state.
*
* * {@link WindowCoveringBaseServer.handleStopMovement} Logic to stop any movement of the device. You can use the
* super.handleStopMovement() to set the target positions to the current positions or do this yourself.
*
* * {@link WindowCoveringBaseServer.executeCalibration} If supported, override this method to implement the
* calibration process. The default implementation returns an error to indicate calibration is unsupported. If
* unsupported you should also add a Changing event handler to the mode attribute to ensure calibration mode is not
* set (needs to throw an ConstraintError).
*
* IMPORTANT NOTE:
*
* This default implementation could have pitfalls when the calibration process and/or movement is handled via
* long-running promises. There could be edge cases not correctly handled by the current implementation when it comes
* to long-running movements or calibration processes - especially when these processes are long-running async JS
* operations.
*
* A movement coming in while another movement is still running is assumed to be handled by the device. It is not
* handled here. If this causes you trouble please provide feedback, and we can discuss how to improve the default
* implementation.
*/
export class WindowCoveringBaseServer extends WindowCoveringBase {
declare protected internal: WindowCoveringBaseServer.Internal;
declare readonly state: WindowCoveringBaseServer.State;
override initialize(): MaybePromise {
// Initialize Internal state from the Mode attribute and keep in sync
this.internal.inMaintenanceMode = !!this.state.mode.maintenanceMode;
this.internal.calibrationMode =
this.state.mode.calibrationMode && !this.state.mode.maintenanceMode
? CalibrationMode.Enabled
: CalibrationMode.Disabled;
this.reactTo(this.events.mode$Changing, this.#handleModeChanging);
// Sync ConfigStatus with features ans set mode to operational if not in maintenance or calibration
const configStatus = this.state.configStatus;
configStatus.operational = !this.internal.inMaintenanceMode && !this.state.mode.calibrationMode;
if (this.features.lift) {
if (this.features.positionAwareLift) {
configStatus.liftPositionAware = true;
}
}
if (this.features.tilt) {
if (this.features.positionAwareTilt) {
configStatus.tiltPositionAware = true;
}
}
if (!isDeepEqual(configStatus, this.state.configStatus)) {
this.state.configStatus = configStatus;
}
// Initially sync the target positions with the current positions, so we have no movement
if (this.features.tilt) {
this.state.targetPositionTiltPercent100ths = this.state.currentPositionTiltPercent100ths;
}
if (this.features.lift) {
this.state.targetPositionLiftPercent100ths = this.state.currentPositionLiftPercent100ths;
}
if (!this.internal.disableOperationalModeHandling) {
// Keep position attributes (percentage and also absolute position) and operational state in sync
this.reactTo(this.events.currentPositionLiftPercent100ths$Changing, this.#syncLiftCurrentPositions);
this.reactTo(this.events.currentPositionTiltPercent100ths$Changing, this.#syncTiltCurrentPositions);
// Update operational state when target position changes
this.reactTo(this.events.targetPositionLiftPercent100ths$Changing, this.#handleLiftTargetPositionChanging);
this.reactTo(this.events.targetPositionTiltPercent100ths$Changing, this.#handleTiltTargetPositionChanging);
// Update the global operational status when lift or tilt status changes
this.reactTo(this.events.operationalStatus$Changing, this.#handleOperationalStatusChanging);
}
}
/**
* Sync the mode attribute with the configStatus attribute and the internal state.
*/
#handleModeChanging(mode: WindowCovering.Mode) {
// According to chip implementation maintenance mode has priority over calibration mode
if (mode.maintenanceMode && mode.calibrationMode) {
mode.calibrationMode = false;
}
if (mode.maintenanceMode && !this.state.supportsMaintenanceMode) {
throw new StatusResponseError("Maintenance mode not supported", StatusCode.ConstraintError);
}
this.internal.inMaintenanceMode = !!mode.maintenanceMode;
if (mode.calibrationMode) {
if (!this.internal.supportsCalibration) {
throw new StatusResponseError("Calibration not supported", StatusCode.ConstraintError);
}
if (this.internal.calibrationMode === CalibrationMode.Running) {
// What to do here? For now lets leave unchanged
} else {
this.internal.calibrationMode = CalibrationMode.Enabled;
}
} else {
this.internal.calibrationMode = CalibrationMode.Disabled;
}
const configStatus = this.state.configStatus;
configStatus.operational =
!mode.maintenanceMode || (mode.calibrationMode && !this.internal.supportsCalibration);
configStatus.liftMovementReversed = !!mode.motorDirectionReversed;
if (isDeepEqual(configStatus, this.state.configStatus)) {
this.agent.asLocalActor(() => {
this.state.configStatus = configStatus;
});
}
logger.debug(
`Mode changed to ${Diagnostic.json(mode)} and config status to ${Diagnostic.json(configStatus)} and internal calibration mode to ${this.internal.calibrationMode}`,
);
}
/** Update the global operational status based on the lift or tilt status. */
#handleOperationalStatusChanging(operationalStatus: WindowCovering.OperationalStatus) {
// Global tracks lift if moving otherwise it follows tilt
const globalStatus =
operationalStatus.lift !== WindowCovering.MovementStatus.Stopped
? operationalStatus.lift
: operationalStatus.tilt;
operationalStatus.global = globalStatus;
logger.debug(
`Operational status changed to ${Diagnostic.json(operationalStatus)} with new global status ${globalStatus}`,
);
this.state.operationalStatus = operationalStatus;
}
/** Update the operational state when the target lift position changes. */
#handleLiftTargetPositionChanging(percent100ths: number | null) {
if (this.features.positionAwareLift) {
this.state.operationalStatus.lift = this.#computeOperationalState(
percent100ths,
this.state.currentPositionLiftPercent100ths,
);
}
}
/** Update the operational state when the target tilt position changes. */
#handleTiltTargetPositionChanging(percent100ths: number | null) {
if (this.features.positionAwareTilt) {
this.state.operationalStatus.tilt = this.#computeOperationalState(
percent100ths,
this.state.currentPositionTiltPercent100ths,
);
}
}
/** Sync the current lift position attributes and the operational state. */
#syncLiftCurrentPositions(percent100ths: number | null) {
if (this.features.positionAwareLift) {
this.state.currentPositionLiftPercentage =
percent100ths === null ? percent100ths : Math.floor(percent100ths / WC_PERCENT100THS_COEFFICIENT);
if (
this.state.operationalStatus.lift !== WindowCovering.MovementStatus.Stopped &&
percent100ths === this.state.targetPositionLiftPercent100ths
) {
this.state.operationalStatus.lift = WindowCovering.MovementStatus.Stopped;
logger.debug("Lift movement stopped, target value reached");
}
}
logger.debug(
`Syncing lift position ${this.state.currentPositionLiftPercent100ths === null ? null : (this.state.currentPositionLiftPercent100ths / 100).toFixed(2)} to ${this.state.currentPositionLiftPercentage}%`,
);
}
/** Sync the current tilt position attributes and the operational state. */
#syncTiltCurrentPositions(percent100ths: number | null) {
if (this.features.positionAwareTilt) {
this.state.currentPositionTiltPercentage =
percent100ths === null ? percent100ths : Math.floor(percent100ths / WC_PERCENT100THS_COEFFICIENT);
if (
this.state.operationalStatus.tilt !== WindowCovering.MovementStatus.Stopped &&
percent100ths === this.state.targetPositionTiltPercent100ths
) {
this.state.operationalStatus.tilt = WindowCovering.MovementStatus.Stopped;
logger.debug("Tilt movement stopped, target value reached");
}
}
logger.debug(
`Syncing tilt position ${this.state.currentPositionTiltPercent100ths === null ? null : (this.state.currentPositionTiltPercent100ths / 100).toFixed(2)} to ${this.state.currentPositionTiltPercentage}%`,
);
}
/** Compute the operational state based on the current and target position. */
#computeOperationalState(target: number | null, current: number | null) {
if (current === null || target === null) {
return WindowCovering.MovementStatus.Stopped;
} else if (current === target) {
return WindowCovering.MovementStatus.Stopped;
} else if (current < target) {
return WindowCovering.MovementStatus.Closing;
} else {
return WindowCovering.MovementStatus.Opening;
}
}
/**
* Asserts if the device can be controlled or not because of an active Maintenance mode or a calibration is
* required but not supported.
*/
#assertMotionLockStatus() {
if (this.internal.inMaintenanceMode) {
throw new StatusResponseError("Device is in maintenance mode", StatusCode.Busy);
}
switch (this.internal.calibrationMode) {
case CalibrationMode.Enabled:
if (!this.internal.supportsCalibration) {
// Should never happy normally because mode attribute should never be set
throw new StatusResponseError("Calibration not implemented", StatusCode.Failure);
}
break;
case CalibrationMode.Running:
// Calibration already in progress, not defined what to return here
break;
case CalibrationMode.Disabled:
break;
}
if (!this.state.configStatus.operational) {
throw new StatusResponseError("Device is not operational", StatusCode.Failure);
}
}
/**
* Calibrate the device. The default implementation takes no action. Override to implement calibration if
* necessary.
*
* @protected
*/
protected executeCalibration(): MaybePromise {}
/**
* Perform actual "movement". Override to initiate movement of your device.
* The logic tries to determine the direction to Open or Close also when a target percentage is given. The direction
* value `DefinedByPosition` only is set if we can not determine the direction based on the current data.
* When a `targetPercent100ths` is set (not undefined) then this is the target value to use.
*
* The default implementation logs and immediately updates current position to the target positions. This is
* probably not desirable for a real device so do not invoke `super.handleMovement()` from your implementation.
*
* @param type Which element should move, Lift or Tilt
* @param reversed If the motor is configured reversed
* @param direction The direction of the movement (Open, Close, DefinedByPosition)
* @param targetPercent100ths Optionally the target position in percent 100ths. It depends on the used feature set
* of the cluster if this is provided or not.
*
* @protected
*/
protected handleMovement(
type: MovementType,
reversed: boolean,
direction: MovementDirection,
targetPercent100ths?: number,
): MaybePromise {
if (this.internal.disableOperationalModeHandling) {
return;
}
switch (type) {
case MovementType.Lift:
if (this.features.positionAwareLift) {
if (targetPercent100ths === undefined) {
throw new ImplementationError("Target position must be defined for position aware lift");
}
this.state.currentPositionLiftPercent100ths = targetPercent100ths;
}
break;
case MovementType.Tilt:
if (this.features.positionAwareTilt) {
if (targetPercent100ths === undefined) {
throw new ImplementationError("Target position must be defined for position aware lift");
}
this.state.currentPositionTiltPercent100ths = targetPercent100ths;
}
break;
}
const directionInfo =
direction === MovementDirection.DefinedByPosition
? ` in direction by position`
: ` in direction ${direction === MovementDirection.Close ? "Close" : "Open"}`;
const targetInfo =
targetPercent100ths === undefined ? "" : ` to target position ${(targetPercent100ths / 100).toFixed(2)}`;
logger.debug(
`Moving the device ${type === MovementType.Lift ? "Lift" : "Tilt"}${directionInfo} (reversed=${reversed})${targetInfo}`,
);
}
/**
* Handle a movement. If calibration is supported and needed then {@link executeCalibration} runs before the actual
* movement. The method increases the numberOfActuations* attribute and updates the operational status.
*
* Actual movement occurs in {@link handleMovement} as a worker. Thus, this method returns before actual movement
* completes.
*/
#prepareMovement(type: MovementType, direction: MovementDirection, targetPercent100ths?: number): void {
if (this.internal.supportsCalibration && this.internal.calibrationMode === CalibrationMode.Enabled) {
return this.env.runtime.add(
Worker({
name: `calibrating ${this}`,
done: this.#executeCalibrationAndMove(type, direction, targetPercent100ths),
lifetime: this.lifetime,
}),
);
}
if (type === MovementType.Lift && this.state.configStatus.liftMovementReversed) {
logger.debug("Lift movement is reversed");
}
switch (type) {
case MovementType.Lift:
this.state.numberOfActuationsLift = (this.state.numberOfActuationsLift ?? 0) + 1;
if (
this.features.positionAwareLift &&
direction === MovementDirection.DefinedByPosition &&
targetPercent100ths !== undefined &&
this.state.currentPositionLiftPercent100ths !== null
) {
direction =
targetPercent100ths > this.state.currentPositionLiftPercent100ths
? MovementDirection.Close
: MovementDirection.Open;
}
if (
!this.internal.disableOperationalModeHandling &&
direction !== MovementDirection.DefinedByPosition
) {
this.state.operationalStatus.lift =
direction === MovementDirection.Close
? WindowCovering.MovementStatus.Closing
: WindowCovering.MovementStatus.Opening;
}
break;
case MovementType.Tilt:
this.state.numberOfActuationsTilt = (this.state.numberOfActuationsTilt ?? 0) + 1;
if (
this.features.positionAwareLift &&
direction === MovementDirection.DefinedByPosition &&
targetPercent100ths !== undefined &&
this.state.currentPositionTiltPercent100ths !== null
) {
direction =
targetPercent100ths > this.state.currentPositionTiltPercent100ths
? MovementDirection.Close
: MovementDirection.Open;
}
if (
!this.internal.disableOperationalModeHandling &&
direction !== MovementDirection.DefinedByPosition
) {
this.state.operationalStatus.tilt =
direction === MovementDirection.Close
? WindowCovering.MovementStatus.Closing
: WindowCovering.MovementStatus.Opening;
}
break;
}
const done = this.handleMovement(
type,
type === MovementType.Lift && !!this.state.configStatus.liftMovementReversed,
direction,
targetPercent100ths,
);
if (done) {
this.env.runtime.add(
Worker({
name: `moving ${this}`,
done,
lifetime: this.lifetime,
}),
);
}
}
#executeCalibrationAndMove(type: MovementType, direction: MovementDirection, targetPercent100ths?: number) {
let calibration;
if (this.internal.calibrationMode === CalibrationMode.Enabled && this.internal.supportsCalibration) {
this.internal.calibrationMode = CalibrationMode.Running;
calibration = this.executeCalibration();
}
return MaybePromise.then(calibration, () => {
this.internal.calibrationMode = CalibrationMode.Disabled;
return this.#prepareMovement(type, direction, targetPercent100ths);
});
}
/**
* Stop device movement. Sets the target position to the current position and updates operational state. Override
* to implement the actual stop movement logic.
*
* If you update the current positions you can include the default logic via "super.handleStopMovement()".
*
* @protected
*/
protected handleStopMovement(): MaybePromise {
if (this.internal.disableOperationalModeHandling) {
return;
}
if (this.features.positionAwareLift) {
this.state.targetPositionLiftPercent100ths = this.state.currentPositionLiftPercent100ths;
}
if (this.features.positionAwareTilt) {
this.state.targetPositionTiltPercent100ths = this.state.currentPositionTiltPercent100ths;
}
if (!this.features.positionAwareLift && !this.features.positionAwareTilt) {
this.state.operationalStatus = {
global: WindowCovering.MovementStatus.Stopped,
lift: WindowCovering.MovementStatus.Stopped,
tilt: WindowCovering.MovementStatus.Stopped,
};
}
}
#triggerLiftMotion(direction: MovementDirection, targetPercent100ths?: number) {
this.#prepareMovement(MovementType.Lift, direction, targetPercent100ths);
}
#triggerTiltMotion(direction: MovementDirection, targetPercent100ths?: number) {
this.#prepareMovement(MovementType.Tilt, direction, targetPercent100ths);
}
/**
* Move the WindowCovering up or open. For position aware devices the target position is set to 0%. The method calls
* the handleMovement method to actually move the device.
*/
override upOrOpen(): MaybePromise {
this.#assertMotionLockStatus();
let targetLiftPercent100ths;
let targetTiltPercent100ths;
if (this.features.positionAwareLift) {
this.state.targetPositionLiftPercent100ths = targetLiftPercent100ths = WC_PERCENT100THS_MIN_OPEN;
}
if (this.features.positionAwareTilt) {
this.state.targetPositionTiltPercent100ths = targetTiltPercent100ths = WC_PERCENT100THS_MIN_OPEN;
}
if (this.features.lift) {
this.#triggerLiftMotion(MovementDirection.Open, targetLiftPercent100ths);
}
if (this.features.tilt) {
this.#triggerTiltMotion(MovementDirection.Open, targetTiltPercent100ths);
}
}
/**
* Move the WindowCovering down or close. For position aware devices the target position is set to 100%. The method
* calls the handleMovement method to actually move the device.
*/
override downOrClose(): MaybePromise {
this.#assertMotionLockStatus();
let targetLiftPercent100ths;
let targetTiltPercent100ths;
if (this.features.positionAwareLift) {
this.state.targetPositionLiftPercent100ths = targetLiftPercent100ths = WC_PERCENT100THS_MAX_CLOSED;
}
if (this.features.positionAwareTilt) {
this.state.targetPositionTiltPercent100ths = targetTiltPercent100ths = WC_PERCENT100THS_MAX_CLOSED;
}
if (this.features.lift) {
this.#triggerLiftMotion(MovementDirection.Close, targetLiftPercent100ths);
}
if (this.features.tilt) {
this.#triggerTiltMotion(MovementDirection.Close, targetTiltPercent100ths);
}
}
/**
* Stop any movement of the WindowCovering. The method calls the handleStopMovement method to actually stop the
* movement of the device.
*/
override stopMotion(): MaybePromise {
this.#assertMotionLockStatus();
return this.handleStopMovement();
}
/**
* Move the WindowCovering to a specific tilt value. The method calls the handleMovement method to actually move the
* device to the defined position.
*/
override goToLiftPercentage({ liftPercent100thsValue }: WindowCovering.GoToLiftPercentageRequest): MaybePromise {
this.#assertMotionLockStatus();
if (this.features.positionAwareLift) {
this.state.targetPositionLiftPercent100ths = liftPercent100thsValue;
this.#triggerLiftMotion(
MovementDirection.DefinedByPosition,
this.state.targetPositionLiftPercent100ths ?? undefined,
);
} else {
if (liftPercent100thsValue === 0) {
this.upOrOpen();
} else {
this.downOrClose();
}
}
}
/**
* Move the WindowCovering to a specific tilt value. The method calls the handleMovement method to actually move the
* device to the defined position.
*/
override goToTiltPercentage({ tiltPercent100thsValue }: WindowCovering.GoToTiltPercentageRequest): MaybePromise {
this.#assertMotionLockStatus();
if (this.features.positionAwareTilt) {
this.state.targetPositionTiltPercent100ths = tiltPercent100thsValue ?? null;
this.#triggerTiltMotion(
MovementDirection.DefinedByPosition,
this.state.targetPositionTiltPercent100ths ?? undefined,
);
} else {
if (tiltPercent100thsValue === 0) {
this.upOrOpen();
} else {
this.downOrClose();
}
}
}
}
export namespace WindowCoveringBaseServer {
export class Internal {
/** Does the device supports calibration? */
supportsCalibration: boolean = false;
/** Status of the Device Calibration mode. */
calibrationMode: CalibrationMode = CalibrationMode.Disabled;
/** Status of the Device Maintenance mode. */
inMaintenanceMode: boolean = false;
/**
* Disable OperationalMode and position value management.
* This requires the device developer to set all these states (operationalMode, percentage and
* absolute values according to the feature set) according to the Matter specification!
*/
disableOperationalModeHandling: boolean = false;
}
export class State extends WindowCoveringBase.State {
/** Does the device supports maintenance mode? */
supportsMaintenanceMode: boolean = true;
}
export declare const ExtensionInterface: {
handleMovement(
type: MovementType,
reversed: boolean,
direction: MovementDirection,
targetPercent100ths?: number,
): MaybePromise;
handleStopMovement(): MaybePromise;
executeCalibration(): MaybePromise;
};
}
export class WindowCoveringServer extends WindowCoveringBaseServer.for(WindowCovering) {}