homebridge-tydom 0.22.1 → 0.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -230,6 +230,18 @@ You can also use the following environment variables (base64 encoded values)
230
230
  | HOMEBRIDGE_TYDOM_PASSWORD | Tydom password |
231
231
  | HOMEBRIDGE_TYDOM_PIN | Tyxal+ pin |
232
232
 
233
+ ### Help
234
+
235
+ If you open a new issue, please provide a dump of your tydom configuration using node-tydom-client:
236
+
237
+ ```sh
238
+ npx tydom-client request /configs/file /devices/data /devices/meta /devices/cmeta --file tydom_output.json --username 001A25XXXXXX --password XXXXXX
239
+ ```
240
+
241
+ Will create the file `tydom_output.json` to upload, you can use [https://gist.github.com](gist.github.com).
242
+
243
+ An homebridge log with [debug enabled](https://github.com/mgcrea/homebridge-tydom#debug) while using the tydom official app (to trace working requests) can also help a lot.
244
+
233
245
  ### Debug
234
246
 
235
247
  This library uses [debug](https://www.npmjs.com/package/debug) to provide high verbosity logs, just pass the following environment:
@@ -0,0 +1,3 @@
1
+ import type { PlatformAccessory } from 'homebridge';
2
+ import TydomController from '../controller';
3
+ export declare const setupBasicGarageDoorOpener: (accessory: PlatformAccessory, controller: TydomController) => void;
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.setupBasicGarageDoorOpener = void 0;
4
+ const hap_1 = require("../config/hap");
5
+ const accessory_1 = require("../helpers/accessory");
6
+ const debug_1 = require("../utils/debug");
7
+ const setupBasicGarageDoorOpener = (accessory, controller) => {
8
+ const { context } = accessory;
9
+ const { client } = controller;
10
+ const { deviceId, endpointId } = context;
11
+ (0, accessory_1.setupAccessoryInformationService)(accessory, controller);
12
+ (0, accessory_1.setupAccessoryIdentifyHandler)(accessory, controller);
13
+ // Add the actual accessory Service
14
+ const service = (0, accessory_1.addAccessoryService)(accessory, hap_1.Service.Switch, `${accessory.displayName}`, true);
15
+ // State
16
+ // const state = {
17
+ // isOn: false
18
+ // };
19
+ service
20
+ .getCharacteristic(hap_1.Characteristic.On)
21
+ // .on(CharacteristicEventTypes.GET, async (callback: NodeCallback<CharacteristicValue>) => {
22
+ // callback(null, state.isOn);
23
+ // })
24
+ .on("set" /* CharacteristicEventTypes.SET */, async (value, callback) => {
25
+ (0, debug_1.debugSet)(hap_1.Characteristic.On, service, value);
26
+ if (!value) {
27
+ callback();
28
+ return;
29
+ }
30
+ try {
31
+ await client.put(`/devices/${deviceId}/endpoints/${endpointId}/data`, [
32
+ {
33
+ name: 'levelCmd',
34
+ value: 'TOGGLE'
35
+ }
36
+ ]);
37
+ (0, debug_1.debugSetResult)(hap_1.Characteristic.On, service, value);
38
+ callback();
39
+ setTimeout(() => {
40
+ service.updateCharacteristic(hap_1.Characteristic.On, false);
41
+ }, 1000);
42
+ }
43
+ catch (err) {
44
+ callback(err);
45
+ }
46
+ })
47
+ .updateValue(false);
48
+ };
49
+ exports.setupBasicGarageDoorOpener = setupBasicGarageDoorOpener;
@@ -1,3 +1,5 @@
1
1
  import type { PlatformAccessory } from 'homebridge';
2
2
  import TydomController from '../controller';
3
+ import { TydomAccessoryUpdateType } from '../helpers/accessory';
3
4
  export declare const setupGarageDoorOpener: (accessory: PlatformAccessory, controller: TydomController) => void;
5
+ export declare const updateGarageDoorOpener: (accessory: PlatformAccessory, _controller: TydomController, updates: Record<string, unknown>[], type: TydomAccessoryUpdateType) => void;
@@ -1,49 +1,198 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.setupGarageDoorOpener = void 0;
3
+ exports.updateGarageDoorOpener = exports.setupGarageDoorOpener = void 0;
4
4
  const hap_1 = require("../config/hap");
5
5
  const accessory_1 = require("../helpers/accessory");
6
+ const basic_1 = require("../utils/basic");
7
+ const chalk_1 = require("../utils/chalk");
6
8
  const debug_1 = require("../utils/debug");
7
9
  const setupGarageDoorOpener = (accessory, controller) => {
8
10
  const { context } = accessory;
9
11
  const { client } = controller;
10
- const { deviceId, endpointId } = context;
12
+ const { CurrentDoorState, TargetDoorState } = hap_1.Characteristic;
13
+ const GARAGE_DOOR_DELAY = 15 * 1000;
14
+ const { deviceId, endpointId, state } = context;
15
+ const assignState = (update) => {
16
+ Object.assign(state, update);
17
+ };
18
+ const assignCurrentDoorState = (currentDoorState) => {
19
+ Object.assign(state, { currentDoorState });
20
+ service.updateCharacteristic(CurrentDoorState, currentDoorState);
21
+ };
22
+ const toggleGarageDoor = async () => await client.put(`/devices/${deviceId}/endpoints/${endpointId}/data`, [
23
+ {
24
+ name: 'levelCmd',
25
+ value: 'TOGGLE'
26
+ }
27
+ ]);
11
28
  (0, accessory_1.setupAccessoryInformationService)(accessory, controller);
12
29
  (0, accessory_1.setupAccessoryIdentifyHandler)(accessory, controller);
30
+ assignState({
31
+ currentDoorState: CurrentDoorState.CLOSED,
32
+ targetDoorState: TargetDoorState.CLOSED,
33
+ lastUpdatedAt: 0,
34
+ computedPosition: 0 // 0 <-> GARAGE_DOOR_DELAY
35
+ });
36
+ const getNextCurrentDoorState = (targetDoorState) => {
37
+ switch (state.currentDoorState) {
38
+ case CurrentDoorState.OPEN: {
39
+ switch (targetDoorState) {
40
+ case TargetDoorState.CLOSED:
41
+ return CurrentDoorState.OPENING;
42
+ case TargetDoorState.OPEN:
43
+ return CurrentDoorState.OPEN;
44
+ }
45
+ break;
46
+ }
47
+ case CurrentDoorState.CLOSED: {
48
+ switch (targetDoorState) {
49
+ case TargetDoorState.CLOSED:
50
+ return CurrentDoorState.CLOSED;
51
+ case TargetDoorState.OPEN:
52
+ return CurrentDoorState.CLOSING;
53
+ }
54
+ break;
55
+ }
56
+ case CurrentDoorState.CLOSING:
57
+ case CurrentDoorState.OPENING: {
58
+ return CurrentDoorState.STOPPED;
59
+ }
60
+ case CurrentDoorState.STOPPED: {
61
+ switch (targetDoorState) {
62
+ case TargetDoorState.CLOSED:
63
+ return CurrentDoorState.CLOSING;
64
+ case TargetDoorState.OPEN:
65
+ return CurrentDoorState.OPENING;
66
+ }
67
+ break;
68
+ }
69
+ }
70
+ return CurrentDoorState.CLOSED;
71
+ };
72
+ const computeCurrentPosition = () => {
73
+ const { lastUpdatedAt, computedPosition: lastComputedPosition, currentDoorState } = state;
74
+ const elapsed = lastUpdatedAt - Date.now();
75
+ switch (currentDoorState) {
76
+ case CurrentDoorState.STOPPED: {
77
+ return lastComputedPosition;
78
+ }
79
+ case CurrentDoorState.OPEN: {
80
+ return 100;
81
+ }
82
+ case CurrentDoorState.CLOSED: {
83
+ return 0;
84
+ }
85
+ case CurrentDoorState.OPENING: {
86
+ return Math.min(100, lastComputedPosition + elapsed / GARAGE_DOOR_DELAY);
87
+ }
88
+ case CurrentDoorState.CLOSING: {
89
+ return Math.max(0, lastComputedPosition - elapsed / GARAGE_DOOR_DELAY);
90
+ }
91
+ }
92
+ return 0;
93
+ };
13
94
  // Add the actual accessory Service
14
- const service = (0, accessory_1.addAccessoryService)(accessory, hap_1.Service.Switch, `${accessory.displayName}`, true);
15
- // State
16
- // const state = {
17
- // isOn: false
18
- // };
95
+ const service = (0, accessory_1.addAccessoryService)(accessory, hap_1.Service.GarageDoorOpener, `${accessory.displayName}`, true);
19
96
  service
20
- .getCharacteristic(hap_1.Characteristic.On)
21
- // .on(CharacteristicEventTypes.GET, async (callback: NodeCallback<CharacteristicValue>) => {
22
- // callback(null, state.isOn);
23
- // })
97
+ .getCharacteristic(CurrentDoorState)
98
+ .on("get" /* CharacteristicEventTypes.GET */, async (callback) => {
99
+ (0, debug_1.debugGet)(CurrentDoorState, service);
100
+ try {
101
+ const nextValue = state.currentDoorState;
102
+ (0, debug_1.debugGetResult)(CurrentDoorState, service, nextValue);
103
+ callback(null, nextValue);
104
+ }
105
+ catch (err) {
106
+ callback(err);
107
+ }
108
+ })
109
+ .getValue();
110
+ service
111
+ .getCharacteristic(TargetDoorState)
112
+ .on("get" /* CharacteristicEventTypes.GET */, async (callback) => {
113
+ (0, debug_1.debugGet)(TargetDoorState, service);
114
+ try {
115
+ const nextValue = state.targetDoorState;
116
+ (0, debug_1.debugGetResult)(TargetDoorState, service, nextValue);
117
+ callback(null, nextValue);
118
+ }
119
+ catch (err) {
120
+ callback(err);
121
+ }
122
+ })
24
123
  .on("set" /* CharacteristicEventTypes.SET */, async (value, callback) => {
25
- (0, debug_1.debugSet)(hap_1.Characteristic.On, service, value);
124
+ (0, debug_1.debugSet)(TargetDoorState, service, value);
26
125
  if (!value) {
27
126
  callback();
28
127
  return;
29
128
  }
30
129
  try {
31
- await client.put(`/devices/${deviceId}/endpoints/${endpointId}/data`, [
32
- {
33
- name: 'levelCmd',
34
- value: 'TOGGLE'
35
- }
36
- ]);
37
- (0, debug_1.debugSetResult)(hap_1.Characteristic.On, service, value);
130
+ (0, debug_1.debugSetResult)(TargetDoorState, service, value);
131
+ const targetDoorState = (0, basic_1.asNumber)(value);
132
+ assignState({
133
+ targetDoorState: targetDoorState,
134
+ lastUpdatedAt: Date.now(),
135
+ computedPosition: computeCurrentPosition()
136
+ });
137
+ let nextCurrentDoorState = getNextCurrentDoorState(targetDoorState);
138
+ if (nextCurrentDoorState === state.currentDoorState) {
139
+ (0, debug_1.debug)(`nextCurrentDoorState=${(0, chalk_1.chalkNumber)(nextCurrentDoorState)} === state.currentDoorState`);
140
+ callback();
141
+ return;
142
+ }
143
+ await toggleGarageDoor();
144
+ assignCurrentDoorState(nextCurrentDoorState);
38
145
  callback();
39
- setTimeout(() => {
40
- service.updateCharacteristic(hap_1.Characteristic.On, false);
41
- }, 1000);
146
+ // Handle Stopped state, if we are stopped, wait one second and trigger again to reverse course
147
+ // eg. Stopped -> Closing if target is Closed
148
+ if (nextCurrentDoorState === CurrentDoorState.STOPPED) {
149
+ await (0, basic_1.waitFor)(1000);
150
+ assignState({
151
+ targetDoorState: targetDoorState,
152
+ lastUpdatedAt: Date.now(),
153
+ computedPosition: computeCurrentPosition()
154
+ });
155
+ nextCurrentDoorState = getNextCurrentDoorState(targetDoorState);
156
+ await toggleGarageDoor();
157
+ assignCurrentDoorState(nextCurrentDoorState);
158
+ }
159
+ // Finally update pending states
160
+ switch (nextCurrentDoorState) {
161
+ case CurrentDoorState.OPENING:
162
+ await (0, basic_1.waitFor)((100 - state.computedPosition) * GARAGE_DOOR_DELAY);
163
+ assignCurrentDoorState(CurrentDoorState.OPEN);
164
+ break;
165
+ case CurrentDoorState.CLOSING:
166
+ await (0, basic_1.waitFor)(state.computedPosition * GARAGE_DOOR_DELAY);
167
+ assignCurrentDoorState(CurrentDoorState.CLOSED);
168
+ break;
169
+ }
42
170
  }
43
171
  catch (err) {
44
172
  callback(err);
45
173
  }
46
174
  })
47
- .updateValue(false);
175
+ .getValue();
48
176
  };
49
177
  exports.setupGarageDoorOpener = setupGarageDoorOpener;
178
+ const updateGarageDoorOpener = (accessory, _controller, updates, type) => {
179
+ const { context } = accessory;
180
+ // const {state} = context as TydomAccessoryContext<State>;
181
+ // Process command updates
182
+ if (type === 'cdata') {
183
+ updates.forEach((update) => {
184
+ const { values } = update;
185
+ const { event } = values;
186
+ (0, debug_1.debug)(`New ${(0, chalk_1.chalkKeyword)('GarageDoorOpener')} event=${(0, chalk_1.chalkJson)(event)}`);
187
+ });
188
+ return;
189
+ }
190
+ updates.forEach((update) => {
191
+ const { name, value } = update;
192
+ switch (name) {
193
+ default:
194
+ return;
195
+ }
196
+ });
197
+ };
198
+ exports.updateGarageDoorOpener = updateGarageDoorOpener;
@@ -1,3 +1,4 @@
1
1
  export declare const stringIncludes: (array: unknown[], value: string | number) => boolean;
2
2
  export declare const sameArrays: (source: unknown[], array: unknown[]) => boolean;
3
3
  export declare const asNumber: (maybeNumber: unknown) => number;
4
+ export declare const waitFor: (ms: number) => Promise<void>;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.asNumber = exports.sameArrays = exports.stringIncludes = void 0;
3
+ exports.waitFor = exports.asNumber = exports.sameArrays = exports.stringIncludes = void 0;
4
4
  const lodash_1 = require("lodash");
5
5
  const stringIncludes = (array, value) => array.includes(value) || array.includes(`${value}`);
6
6
  exports.stringIncludes = stringIncludes;
@@ -8,3 +8,5 @@ const sameArrays = (source, array) => source.length === array.length && (0, loda
8
8
  exports.sameArrays = sameArrays;
9
9
  const asNumber = (maybeNumber) => parseInt(`${maybeNumber}`, 10);
10
10
  exports.asNumber = asNumber;
11
+ const waitFor = async (ms) => new Promise((resolve) => setTimeout(resolve, ms));
12
+ exports.waitFor = waitFor;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "homebridge-tydom",
3
- "version": "0.22.1",
3
+ "version": "0.23.0",
4
4
  "description": "Homebridge plugin to manage Tydom devices by Delta Dore",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",