iobroker.tapo 0.2.0 → 0.2.1

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
@@ -32,12 +32,6 @@ tapo.0.id.remote auf true/false setzen steuert den jeweiligen Befehl. Der Befehl
32
32
 
33
33
  <https://forum.iobroker.net/topic/57336/test-adapter-tp-link-tapo/>
34
34
 
35
- ## Changelog
36
-
37
- ### 0.0.2
38
-
39
- - (TA2k) initial release
40
-
41
35
  ## License
42
36
 
43
37
  MIT License
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/lib/utils/camera/onvifCamera.ts"],
4
- "sourcesContent": ["import { DeviceInformation, VideoSource, NotificationMessage, Cam as ICam } from \"./types/onvif\";\n// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-ignore\nimport { Cam } from \"onvif\";\nimport { EventEmitter } from \"stream\";\ntype CameraConfig = {\n name: string;\n ipAddress: string;\n password: string;\n streamUser: string;\n streamPassword: string;\n\n pullInterval?: number;\n disableStreaming?: boolean;\n disablePrivacyAccessory?: boolean;\n disableAlarmAccessory?: boolean;\n disableMotionAccessory?: boolean;\n lowQuality?: boolean;\n};\nexport class OnvifCamera {\n private events: EventEmitter | undefined;\n private device: Cam | undefined;\n\n private readonly kOnvifPort = 2020;\n\n constructor(protected readonly config: CameraConfig) {}\n\n private async getDevice(): Promise<ICam> {\n return new Promise((resolve, reject) => {\n if (this.device) {\n return resolve(this.device);\n }\n\n const device: ICam = new Cam(\n {\n hostname: this.config.ipAddress,\n username: this.config.streamUser,\n password: this.config.streamPassword,\n port: this.kOnvifPort,\n },\n (err: Error) => {\n if (err) {\n return reject(err);\n }\n this.device = device;\n return resolve(this.device);\n },\n );\n });\n }\n\n async getEventEmitter() {\n if (this.events) {\n return this.events;\n }\n\n const onvifDevice = await this.getDevice();\n\n let lastMotionValue = false;\n\n this.events = new EventEmitter();\n\n onvifDevice.on(\"event\", (event: NotificationMessage) => {\n if (event?.topic?._?.match(/RuleEngine\\/CellMotionDetector\\/Motion$/)) {\n const motion = event.message.message.data.simpleItem.$.Value;\n if (motion !== lastMotionValue) {\n lastMotionValue = Boolean(motion);\n this.events = this.events || new EventEmitter();\n this.events.emit(\"motion\", motion);\n }\n }\n });\n\n return this.events;\n }\n\n async getVideoSource(): Promise<VideoSource> {\n const onvifDevice = await this.getDevice();\n return onvifDevice.videoSources[0];\n }\n\n async getDeviceInfo(): Promise<DeviceInformation> {\n const onvifDevice = await this.getDevice();\n return new Promise((resolve, reject) => {\n onvifDevice.getDeviceInformation((err, deviceInformation) => {\n if (err) return reject(err);\n resolve(deviceInformation);\n });\n });\n }\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA,IAAAA,gBAAoB;AACpB,oBAA6B;AAetB,MAAM,YAAY;AAAA,EAMvB,YAA+B,QAAsB;AAAtB;AAF/B,SAAiB,aAAa;AAAA,EAEwB;AAAA,EAEtD,MAAc,YAA2B;AACvC,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAI,KAAK,QAAQ;AACf,eAAO,QAAQ,KAAK,MAAM;AAAA,MAC5B;AAEA,YAAM,SAAe,IAAI;AAAA,QACvB;AAAA,UACE,UAAU,KAAK,OAAO;AAAA,UACtB,UAAU,KAAK,OAAO;AAAA,UACtB,UAAU,KAAK,OAAO;AAAA,UACtB,MAAM,KAAK;AAAA,QACb;AAAA,QACA,CAAC,QAAe;AACd,cAAI,KAAK;AACP,mBAAO,OAAO,GAAG;AAAA,UACnB;AACA,eAAK,SAAS;AACd,iBAAO,QAAQ,KAAK,MAAM;AAAA,QAC5B;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,kBAAkB;AACtB,QAAI,KAAK,QAAQ;AACf,aAAO,KAAK;AAAA,IACd;AAEA,UAAM,cAAc,MAAM,KAAK,UAAU;AAEzC,QAAI,kBAAkB;AAEtB,SAAK,SAAS,IAAI,2BAAa;AAE/B,gBAAY,GAAG,SAAS,CAAC,UAA+B;AA9D5D;AA+DM,WAAI,0CAAO,UAAP,mBAAc,MAAd,mBAAiB,MAAM,4CAA4C;AACrE,cAAM,SAAS,MAAM,QAAQ,QAAQ,KAAK,WAAW,EAAE;AACvD,YAAI,WAAW,iBAAiB;AAC9B,4BAAkB,QAAQ,MAAM;AAChC,eAAK,SAAS,KAAK,UAAU,IAAI,2BAAa;AAC9C,eAAK,OAAO,KAAK,UAAU,MAAM;AAAA,QACnC;AAAA,MACF;AAAA,IACF,CAAC;AAED,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAM,iBAAuC;AAC3C,UAAM,cAAc,MAAM,KAAK,UAAU;AACzC,WAAO,YAAY,aAAa;AAAA,EAClC;AAAA,EAEA,MAAM,gBAA4C;AAChD,UAAM,cAAc,MAAM,KAAK,UAAU;AACzC,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,kBAAY,qBAAqB,CAAC,KAAK,sBAAsB;AAC3D,YAAI;AAAK,iBAAO,OAAO,GAAG;AAC1B,gBAAQ,iBAAiB;AAAA,MAC3B,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AACF;",
4
+ "sourcesContent": ["import { DeviceInformation, VideoSource, NotificationMessage, Cam as ICam } from \"./types/onvif\";\n// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-ignore\nimport { Cam } from \"onvif\";\nimport { EventEmitter } from \"stream\";\ntype CameraConfig = {\n name: string;\n ipAddress: string;\n username: string;\n password: string;\n streamUser: string;\n streamPassword: string;\n\n pullInterval?: number;\n disableStreaming?: boolean;\n disablePrivacyAccessory?: boolean;\n disableAlarmAccessory?: boolean;\n disableMotionAccessory?: boolean;\n lowQuality?: boolean;\n\n videoConfig?: VideoConfig;\n\n privacyAccessoryName?: string;\n alarmAccessoryName?: string;\n};\nexport class OnvifCamera {\n private events: EventEmitter | undefined;\n private device: Cam | undefined;\n\n private readonly kOnvifPort = 2020;\n\n constructor(protected readonly config: CameraConfig) {}\n\n private async getDevice(): Promise<ICam> {\n return new Promise((resolve, reject) => {\n if (this.device) {\n return resolve(this.device);\n }\n\n const device: ICam = new Cam(\n {\n hostname: this.config.ipAddress,\n username: this.config.streamUser,\n password: this.config.streamPassword,\n port: this.kOnvifPort,\n },\n (err: Error) => {\n if (err) {\n return reject(err);\n }\n this.device = device;\n return resolve(this.device);\n },\n );\n });\n }\n\n async getEventEmitter() {\n if (this.events) {\n return this.events;\n }\n\n const onvifDevice = await this.getDevice();\n\n let lastMotionValue = false;\n\n this.events = new EventEmitter();\n\n onvifDevice.on(\"event\", (event: NotificationMessage) => {\n if (event?.topic?._?.match(/RuleEngine\\/CellMotionDetector\\/Motion$/)) {\n const motion = event.message.message.data.simpleItem.$.Value;\n if (motion !== lastMotionValue) {\n lastMotionValue = Boolean(motion);\n this.events = this.events || new EventEmitter();\n this.events.emit(\"motion\", motion);\n }\n }\n });\n\n return this.events;\n }\n\n async getVideoSource(): Promise<VideoSource> {\n const onvifDevice = await this.getDevice();\n return onvifDevice.videoSources[0];\n }\n\n async getDeviceInfo(): Promise<DeviceInformation> {\n const onvifDevice = await this.getDevice();\n return new Promise((resolve, reject) => {\n onvifDevice.getDeviceInformation((err, deviceInformation) => {\n if (err) return reject(err);\n resolve(deviceInformation);\n });\n });\n }\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA,IAAAA,gBAAoB;AACpB,oBAA6B;AAqBtB,MAAM,YAAY;AAAA,EAMvB,YAA+B,QAAsB;AAAtB;AAF/B,SAAiB,aAAa;AAAA,EAEwB;AAAA,EAEtD,MAAc,YAA2B;AACvC,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAI,KAAK,QAAQ;AACf,eAAO,QAAQ,KAAK,MAAM;AAAA,MAC5B;AAEA,YAAM,SAAe,IAAI;AAAA,QACvB;AAAA,UACE,UAAU,KAAK,OAAO;AAAA,UACtB,UAAU,KAAK,OAAO;AAAA,UACtB,UAAU,KAAK,OAAO;AAAA,UACtB,MAAM,KAAK;AAAA,QACb;AAAA,QACA,CAAC,QAAe;AACd,cAAI,KAAK;AACP,mBAAO,OAAO,GAAG;AAAA,UACnB;AACA,eAAK,SAAS;AACd,iBAAO,QAAQ,KAAK,MAAM;AAAA,QAC5B;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,kBAAkB;AACtB,QAAI,KAAK,QAAQ;AACf,aAAO,KAAK;AAAA,IACd;AAEA,UAAM,cAAc,MAAM,KAAK,UAAU;AAEzC,QAAI,kBAAkB;AAEtB,SAAK,SAAS,IAAI,2BAAa;AAE/B,gBAAY,GAAG,SAAS,CAAC,UAA+B;AApE5D;AAqEM,WAAI,0CAAO,UAAP,mBAAc,MAAd,mBAAiB,MAAM,4CAA4C;AACrE,cAAM,SAAS,MAAM,QAAQ,QAAQ,KAAK,WAAW,EAAE;AACvD,YAAI,WAAW,iBAAiB;AAC9B,4BAAkB,QAAQ,MAAM;AAChC,eAAK,SAAS,KAAK,UAAU,IAAI,2BAAa;AAC9C,eAAK,OAAO,KAAK,UAAU,MAAM;AAAA,QACnC;AAAA,MACF;AAAA,IACF,CAAC;AAED,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAM,iBAAuC;AAC3C,UAAM,cAAc,MAAM,KAAK,UAAU;AACzC,WAAO,YAAY,aAAa;AAAA,EAClC;AAAA,EAEA,MAAM,gBAA4C;AAChD,UAAM,cAAc,MAAM,KAAK,UAAU;AACzC,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,kBAAY,qBAAqB,CAAC,KAAK,sBAAsB;AAC3D,YAAI;AAAK,iBAAO,OAAO,GAAG;AAC1B,gBAAQ,iBAAiB;AAAA,MAC3B,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AACF;",
6
6
  "names": ["import_onvif"]
7
7
  }
@@ -142,13 +142,14 @@ class TAPOCamera extends import_onvifCamera.OnvifCamera {
142
142
  }
143
143
  response = await this.fetch(`https://${this.config.ipAddress}`, fetchParams);
144
144
  responseData = await response.json();
145
- this.log.debug("StokRefresh: Login response :>> ", response.status, JSON.stringify(responseData));
145
+ this.log.debug("StokRefresh: Login response :>> " + response.status + JSON.stringify(responseData));
146
146
  if (response.status === 401) {
147
147
  if (((_b = (_a = responseData == null ? void 0 : responseData.result) == null ? void 0 : _a.data) == null ? void 0 : _b.code) === 40411) {
148
148
  throw new Error("Invalid credentials");
149
149
  }
150
150
  }
151
151
  if (isSecureConnection) {
152
+ this.log.debug("StokRefresh: Using secure connection");
152
153
  const nonce = (_d = (_c = responseData == null ? void 0 : responseData.result) == null ? void 0 : _c.data) == null ? void 0 : _d.nonce;
153
154
  const deviceConfirm = (_f = (_e = responseData == null ? void 0 : responseData.result) == null ? void 0 : _e.data) == null ? void 0 : _f.device_confirm;
154
155
  if (nonce && deviceConfirm && this.validateDeviceConfirm(nonce, deviceConfirm)) {
@@ -171,7 +172,7 @@ class TAPOCamera extends import_onvifCamera.OnvifCamera {
171
172
  })
172
173
  });
173
174
  responseData = await response.json();
174
- this.log.debug("StokRefresh: Start_seq response :>>", response.status, JSON.stringify(responseData));
175
+ this.log.debug("StokRefresh: Start_seq response :>>" + response.status + JSON.stringify(responseData));
175
176
  if ((_g = responseData == null ? void 0 : responseData.result) == null ? void 0 : _g.start_seq) {
176
177
  if (((_h = responseData == null ? void 0 : responseData.result) == null ? void 0 : _h.user_group) !== "root") {
177
178
  throw new Error("Incorrect user_group detected");
@@ -192,14 +193,12 @@ class TAPOCamera extends import_onvifCamera.OnvifCamera {
192
193
  }
193
194
  if ((_m = responseData == null ? void 0 : responseData.result) == null ? void 0 : _m.stok) {
194
195
  this.stok = responseData.result.stok;
195
- this.log.debug("StokRefresh: Success :>>", this.stok);
196
+ this.log.debug("StokRefresh: Success :>>" + this.stok);
196
197
  return this.stok;
197
198
  }
198
199
  if ((responseData == null ? void 0 : responseData.error_code) === -40413 && loginRetryCount < MAX_LOGIN_RETRIES) {
199
200
  this.log.debug(
200
- `Unexpected response, retrying: ${loginRetryCount}/${MAX_LOGIN_RETRIES}.`,
201
- response.status,
202
- JSON.stringify(responseData)
201
+ `Unexpected response, retrying: ${loginRetryCount}/${MAX_LOGIN_RETRIES}.` + response.status + JSON.stringify(responseData)
203
202
  );
204
203
  return this.refreshStok(loginRetryCount + 1);
205
204
  }
@@ -218,8 +217,9 @@ class TAPOCamera extends import_onvifCamera.OnvifCamera {
218
217
  }
219
218
  })
220
219
  });
220
+ this.log.debug(JSON.stringify(response));
221
221
  const json = await response.json();
222
- this.log.debug("isSecureConnection response :>> ", response.status, json);
222
+ this.log.debug("isSecureConnection response :>> " + response.status + json);
223
223
  this.isSecureConnectionValue = json.error_code == -40413 && ((_c = (_b = (_a = json == null ? void 0 : json.result) == null ? void 0 : _a.data) == null ? void 0 : _b.encrypt_type) == null ? void 0 : _c.includes("3"));
224
224
  }
225
225
  return this.isSecureConnectionValue;
@@ -279,7 +279,7 @@ class TAPOCamera extends import_onvifCamera.OnvifCamera {
279
279
  if (this.pendingAPIRequests.has(reqJson)) {
280
280
  return this.pendingAPIRequests.get(reqJson);
281
281
  }
282
- this.log.debug("API new request", reqJson);
282
+ this.log.debug("API new request: " + reqJson);
283
283
  this.pendingAPIRequests.set(
284
284
  reqJson,
285
285
  (async () => {
@@ -317,7 +317,7 @@ class TAPOCamera extends import_onvifCamera.OnvifCamera {
317
317
  } else {
318
318
  json = json;
319
319
  }
320
- this.log.debug(`API response`, response.status, JSON.stringify(json));
320
+ this.log.debug(`API response: ` + response.status, JSON.stringify(json));
321
321
  if (isSecureConnection && response.status === 500) {
322
322
  this.stok = void 0;
323
323
  }
@@ -335,7 +335,7 @@ class TAPOCamera extends import_onvifCamera.OnvifCamera {
335
335
  return this.pendingAPIRequests.get(reqJson);
336
336
  }
337
337
  async setLensMaskConfig(value) {
338
- this.log.debug("Processing setLensMaskConfig", value);
338
+ this.adapter.log.debug("Processing setLensMaskConfig" + value);
339
339
  const json = await this.apiRequest({
340
340
  method: "multipleRequest",
341
341
  params: {
@@ -358,7 +358,7 @@ class TAPOCamera extends import_onvifCamera.OnvifCamera {
358
358
  }
359
359
  }
360
360
  async setAlertConfig(value) {
361
- this.log.debug("Processing setAlertConfig", value);
361
+ this.log.debug("Processing setAlertConfig" + value);
362
362
  const json = await this.apiRequest({
363
363
  method: "multipleRequest",
364
364
  params: {
@@ -398,6 +398,20 @@ class TAPOCamera extends import_onvifCamera.OnvifCamera {
398
398
  });
399
399
  return json.error_code !== 0;
400
400
  }
401
+ async moveMotorStep(angle) {
402
+ angle = angle.toString();
403
+ const json = await this.apiRequest({ method: "do", motor: { movestep: { direction: angle } } });
404
+ return json.error_code !== 0;
405
+ }
406
+ async moveMotor(x, y) {
407
+ const json = await this.apiRequest({
408
+ method: "multipleRequest",
409
+ params: {
410
+ requests: [{ method: "do", motor: { move: { x_coord: x, y_coord: y } } }]
411
+ }
412
+ });
413
+ return json.error_code !== 0;
414
+ }
401
415
  async getBasicInfo() {
402
416
  const json = await this.apiRequest({
403
417
  method: "multipleRequest",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/lib/utils/camera/tapoCamera.ts"],
4
- "sourcesContent": ["import fetch, { RequestInit } from \"node-fetch\";\nimport https, { Agent } from \"https\";\nimport crypto from \"crypto\";\nimport { OnvifCamera } from \"./onvifCamera\";\nimport type {\n TAPOCameraEncryptedRequest,\n TAPOCameraEncryptedResponse,\n TAPOCameraRequest,\n TAPOCameraResponse,\n TAPOCameraResponseDeviceInfo,\n TAPOCameraResponseGetAlert,\n TAPOCameraResponseGetLensMask,\n} from \"./types/tapo\";\n\nconst MAX_LOGIN_RETRIES = 3;\nconst AES_BLOCK_SIZE = 16;\n\ntype CameraConfig = {\n name: string;\n ipAddress: string;\n password: string;\n streamUser: string;\n streamPassword: string;\n\n pullInterval?: number;\n disableStreaming?: boolean;\n disablePrivacyAccessory?: boolean;\n disableAlarmAccessory?: boolean;\n disableMotionAccessory?: boolean;\n lowQuality?: boolean;\n};\nexport class TAPOCamera extends OnvifCamera {\n private readonly kStreamPort = 554;\n private readonly httpsAgent: Agent;\n\n private readonly hashedMD5Password: string;\n private readonly hashedSha256Password: string;\n private passwordEncryptionMethod: \"md5\" | \"sha256\" | null = null;\n\n private isSecureConnectionValue: boolean | null = null;\n\n private stokPromise: (() => Promise<string>) | undefined;\n\n private readonly cnonce: string;\n private lsk: Buffer | undefined;\n private ivb: Buffer | undefined;\n private seq: number | undefined;\n private stok: string | undefined;\n\n private loginRetryCount = 0;\n\n constructor(\n protected readonly log: any,\n protected readonly config: CameraConfig,\n ) {\n super(log, config);\n this.log.debug(\"Constructing Camera on host: \" + config.ipAddress);\n\n this.httpsAgent = new https.Agent({\n rejectUnauthorized: false,\n });\n\n this.cnonce = this.generateCnonce();\n\n this.hashedMD5Password = crypto.createHash(\"md5\").update(config.password).digest(\"hex\").toUpperCase();\n this.hashedSha256Password = crypto.createHash(\"sha256\").update(config.password).digest(\"hex\").toUpperCase();\n }\n\n private getUsername() {\n return this.config.username || \"admin\";\n }\n\n private getHeaders() {\n const headers: Record<string, string> = {\n Host: `https://${this.config.ipAddress}`,\n Referer: `https://${this.config.ipAddress}`,\n Accept: \"application/json\",\n \"Accept-Encoding\": \"gzip, deflate\",\n \"User-Agent\": \"Tapo CameraClient Android\",\n Connection: \"close\",\n requestByApp: \"true\",\n \"Content-Type\": \"application/json; charset=UTF-8\",\n };\n return headers;\n }\n\n private getHashedPassword() {\n if (this.passwordEncryptionMethod === \"md5\") {\n return this.hashedMD5Password;\n } else if (this.passwordEncryptionMethod === \"sha256\") {\n return this.hashedSha256Password;\n } else {\n throw new Error(\"Unknown password encryption method\");\n }\n }\n\n private fetch(url: string, data: RequestInit) {\n return fetch(url, {\n agent: this.httpsAgent,\n headers: this.getHeaders(),\n ...data,\n });\n }\n\n private generateEncryptionToken(tokenType: string, nonce: string): Buffer {\n const hashedKey = crypto\n .createHash(\"sha256\")\n .update(this.cnonce + this.getHashedPassword() + nonce)\n .digest(\"hex\")\n .toUpperCase();\n return crypto\n .createHash(\"sha256\")\n .update(tokenType + this.cnonce + nonce + hashedKey)\n .digest()\n .slice(0, 16);\n }\n\n getAuthenticatedStreamUrl(lowQuality = false) {\n const prefix = `rtsp://${this.config.streamUser}:${this.config.streamPassword}@${this.config.ipAddress}:${this.kStreamPort}`;\n return lowQuality ? `${prefix}/stream2` : `${prefix}/stream1`;\n }\n\n private generateCnonce() {\n return crypto.randomBytes(8).toString(\"hex\").toUpperCase();\n }\n\n private validateDeviceConfirm(nonce: string, deviceConfirm: string) {\n const hashedNoncesWithSHA256 = crypto\n .createHash(\"sha256\")\n .update(this.cnonce + this.hashedSha256Password + nonce)\n .digest(\"hex\")\n .toUpperCase();\n const hashedNoncesWithMD5 = crypto\n .createHash(\"md5\")\n .update(this.cnonce + this.hashedMD5Password + nonce)\n .digest(\"hex\")\n .toUpperCase();\n\n if (deviceConfirm === hashedNoncesWithSHA256 + nonce + this.cnonce) {\n this.passwordEncryptionMethod = \"sha256\";\n return true;\n }\n\n if (deviceConfirm === hashedNoncesWithMD5 + nonce + this.cnonce) {\n this.passwordEncryptionMethod = \"md5\";\n return true;\n }\n\n return false;\n }\n\n async refreshStok(loginRetryCount = 0): Promise<string> {\n const isSecureConnection = await this.isSecureConnection();\n\n let response = null;\n let responseData = null;\n\n let fetchParams = {};\n if (isSecureConnection) {\n this.log.debug(\"StokRefresh: Using secure connection\");\n fetchParams = {\n method: \"post\",\n body: JSON.stringify({\n method: \"login\",\n params: {\n cnonce: this.cnonce,\n encrypt_type: \"3\",\n username: this.getUsername(),\n },\n }),\n };\n } else {\n this.log.debug(\"StokRefresh: Using unsecure connection\");\n fetchParams = {\n method: \"post\",\n body: JSON.stringify({\n method: \"login\",\n params: {\n username: this.getUsername(),\n password: this.getHashedPassword(),\n hashed: true,\n },\n }),\n };\n }\n\n response = await this.fetch(`https://${this.config.ipAddress}`, fetchParams);\n responseData = await response.json();\n\n this.log.debug(\"StokRefresh: Login response :>> \", response.status, JSON.stringify(responseData));\n\n if (response.status === 401) {\n if (responseData?.result?.data?.code === 40411) {\n throw new Error(\"Invalid credentials\");\n }\n }\n\n if (isSecureConnection) {\n const nonce = responseData?.result?.data?.nonce;\n const deviceConfirm = responseData?.result?.data?.device_confirm;\n\n if (nonce && deviceConfirm && this.validateDeviceConfirm(nonce, deviceConfirm)) {\n const digestPasswd = crypto\n .createHash(\"sha256\")\n .update(this.getHashedPassword() + this.cnonce + nonce)\n .digest(\"hex\")\n .toUpperCase();\n\n const digestPasswdFull = Buffer.concat([\n Buffer.from(digestPasswd, \"utf8\"),\n Buffer.from(this.cnonce!, \"utf8\"),\n Buffer.from(nonce, \"utf8\"),\n ]).toString(\"utf8\");\n\n response = await this.fetch(`https://${this.config.ipAddress}`, {\n method: \"POST\",\n body: JSON.stringify({\n method: \"login\",\n params: {\n cnonce: this.cnonce,\n encrypt_type: \"3\",\n digest_passwd: digestPasswdFull,\n username: this.getUsername(),\n },\n }),\n });\n\n responseData = await response.json();\n\n this.log.debug(\"StokRefresh: Start_seq response :>>\", response.status, JSON.stringify(responseData));\n\n if (responseData?.result?.start_seq) {\n if (responseData?.result?.user_group !== \"root\") {\n // # encrypted control via 3rd party account does not seem to be supported\n // # see https://github.com/JurajNyiri/HomeAssistant-Tapo-Control/issues/456\n throw new Error(\"Incorrect user_group detected\");\n }\n\n this.lsk = this.generateEncryptionToken(\"lsk\", nonce);\n this.ivb = this.generateEncryptionToken(\"ivb\", nonce);\n this.seq = responseData.result.start_seq;\n }\n }\n } else {\n this.passwordEncryptionMethod = \"md5\";\n }\n\n if (responseData?.result?.data?.sec_left > 0) {\n throw new Error(`StokRefresh: Temporary Suspension: Try again in ${responseData.result.data.sec_left} seconds`);\n }\n\n if (responseData?.data?.code == -40404 && responseData?.data?.sec_left > 0) {\n throw new Error(`StokRefresh: Temporary Suspension: Try again in ${responseData.data.sec_left} seconds`);\n }\n\n if (responseData?.result?.stok) {\n this.stok = responseData.result.stok;\n this.log.debug(\"StokRefresh: Success :>>\", this.stok);\n\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n return this.stok!;\n }\n\n if (responseData?.error_code === -40413 && loginRetryCount < MAX_LOGIN_RETRIES) {\n this.log.debug(\n `Unexpected response, retrying: ${loginRetryCount}/${MAX_LOGIN_RETRIES}.`,\n response.status,\n JSON.stringify(responseData),\n );\n return this.refreshStok(loginRetryCount + 1);\n }\n\n throw new Error(\"Invalid authentication data\");\n }\n\n async isSecureConnection() {\n if (this.isSecureConnectionValue === null) {\n const response = await this.fetch(`https://${this.config.ipAddress}`, {\n method: \"post\",\n body: JSON.stringify({\n method: \"login\",\n params: {\n encrypt_type: \"3\",\n username: this.getUsername(),\n },\n }),\n });\n const json = await response.json();\n\n this.log.debug(\"isSecureConnection response :>> \", response.status, json);\n\n this.isSecureConnectionValue = json.error_code == -40413 && json?.result?.data?.encrypt_type?.includes(\"3\");\n }\n\n return this.isSecureConnectionValue;\n }\n\n getStok(loginRetryCount = 0): Promise<string> {\n if (this.stok) {\n return new Promise((resolve) => resolve(this.stok!));\n }\n\n if (!this.stokPromise) {\n this.stokPromise = () => this.refreshStok(loginRetryCount);\n }\n\n return this.stokPromise()\n .then(() => {\n return this.stok!;\n })\n .finally(() => {\n this.stokPromise = undefined;\n });\n }\n\n private async getAuthenticatedAPIURL(loginRetryCount = 0) {\n const token = await this.getStok(loginRetryCount);\n return `https://${this.config.ipAddress}/stok=${token}/ds`;\n }\n\n encryptRequest(request: string) {\n const cipher = crypto.createCipheriv(\"aes-128-cbc\", this.lsk!, this.ivb!);\n let ct_bytes = cipher.update(this.encryptPad(request, AES_BLOCK_SIZE), \"utf-8\", \"hex\");\n ct_bytes += cipher.final(\"hex\");\n return Buffer.from(ct_bytes, \"hex\");\n }\n\n private encryptPad(text: string, blocksize: number) {\n const padSize = blocksize - (text.length % blocksize);\n const padding = String.fromCharCode(padSize).repeat(padSize);\n return text + padding;\n }\n\n private decryptResponse(response: string): string {\n const decipher = crypto.createDecipheriv(\"aes-128-cbc\", this.lsk!, this.ivb!);\n let decrypted = decipher.update(response, \"base64\", \"utf-8\");\n decrypted += decipher.final(\"utf-8\");\n return this.encryptUnpad(decrypted, AES_BLOCK_SIZE);\n }\n\n private encryptUnpad(text: string, blockSize: number): string {\n const paddingLength = Number(text[text.length - 1]) || 0;\n if (paddingLength > blockSize || paddingLength > text.length) {\n throw new Error(\"Invalid padding\");\n }\n for (let i = text.length - paddingLength; i < text.length; i++) {\n if (text.charCodeAt(i) !== paddingLength) {\n throw new Error(\"Invalid padding\");\n }\n }\n return text.slice(0, text.length - paddingLength).toString();\n }\n\n private getTapoTag(request: TAPOCameraEncryptedRequest) {\n const tag = crypto\n .createHash(\"sha256\")\n .update(this.getHashedPassword() + this.cnonce)\n .digest(\"hex\")\n .toUpperCase();\n return crypto\n .createHash(\"sha256\")\n .update(tag + JSON.stringify(request) + this.seq!.toString())\n .digest(\"hex\")\n .toUpperCase();\n }\n\n private pendingAPIRequests: Map<string, Promise<TAPOCameraResponse>> = new Map();\n\n private async apiRequest(req: TAPOCameraRequest, loginRetryCount = 0): Promise<TAPOCameraResponse> {\n const reqJson = JSON.stringify(req);\n\n if (this.pendingAPIRequests.has(reqJson)) {\n return this.pendingAPIRequests.get(reqJson) as Promise<TAPOCameraResponse>;\n }\n\n this.log.debug(\"API new request\", reqJson);\n\n this.pendingAPIRequests.set(\n reqJson,\n (async () => {\n try {\n const isSecureConnection = await this.isSecureConnection();\n const url = await this.getAuthenticatedAPIURL(loginRetryCount);\n\n const fetchParams: RequestInit = {\n method: \"post\",\n };\n\n if (this.seq && isSecureConnection) {\n const encryptedRequest: TAPOCameraEncryptedRequest = {\n method: \"securePassthrough\",\n params: {\n request: Buffer.from(this.encryptRequest(JSON.stringify(req))).toString(\"base64\"),\n },\n };\n fetchParams.headers = {\n ...this.getHeaders(),\n Tapo_tag: this.getTapoTag(encryptedRequest),\n Seq: this.seq.toString(),\n };\n fetchParams.body = JSON.stringify(encryptedRequest);\n this.seq += 1;\n } else {\n fetchParams.body = JSON.stringify(req);\n }\n\n const response = await this.fetch(url, fetchParams);\n let json = await response.json();\n\n if (isSecureConnection) {\n const encryptedResponse = json as TAPOCameraEncryptedResponse;\n if (encryptedResponse.result.response) {\n const decryptedResponse = this.decryptResponse(encryptedResponse.result.response);\n json = JSON.parse(decryptedResponse) as TAPOCameraResponse;\n }\n } else {\n json = json as TAPOCameraResponse;\n }\n\n this.log.debug(`API response`, response.status, JSON.stringify(json));\n\n // Apparently the Tapo C200 returns 500 on successful requests,\n // but it's indicating an expiring token, therefore refresh the token next time\n if (isSecureConnection && response.status === 500) {\n this.stok = undefined;\n }\n\n // Check if we have to refresh the token\n if (json.error_code === -40401 || json.error_code === -1) {\n this.log.debug(\"API request failed, reauthenticating\");\n this.stok = undefined;\n return this.apiRequest(req, loginRetryCount + 1);\n }\n\n return json as TAPOCameraResponse;\n } finally {\n this.pendingAPIRequests.delete(reqJson);\n }\n })(),\n );\n\n return this.pendingAPIRequests.get(reqJson) as Promise<TAPOCameraResponse>;\n }\n\n async setLensMaskConfig(value: boolean) {\n this.log.debug(\"Processing setLensMaskConfig\", value);\n\n const json = await this.apiRequest({\n method: \"multipleRequest\",\n params: {\n requests: [\n {\n method: \"setLensMaskConfig\",\n params: {\n lens_mask: {\n lens_mask_info: {\n enabled: value ? \"on\" : \"off\",\n },\n },\n },\n },\n ],\n },\n });\n\n if (json.error_code !== 0) {\n throw new Error(\"Failed to perform action\");\n }\n }\n\n async setAlertConfig(value: boolean) {\n this.log.debug(\"Processing setAlertConfig\", value);\n\n const json = await this.apiRequest({\n method: \"multipleRequest\",\n params: {\n requests: [\n {\n method: \"setAlertConfig\",\n params: {\n msg_alarm: {\n chn1_msg_alarm_info: {\n enabled: value ? \"on\" : \"off\",\n },\n },\n },\n },\n ],\n },\n });\n\n return json.error_code !== 0;\n }\n async setForceWhitelampState(value: boolean) {\n const json = await this.apiRequest({\n method: \"multipleRequest\",\n params: {\n requests: [\n {\n method: \"setForceWhitelampState\",\n params: {\n image: {\n switch: {\n force_wtl_state: value ? \"on\" : \"off\",\n },\n },\n },\n },\n ],\n },\n });\n\n return json.error_code !== 0;\n }\n\n async getBasicInfo() {\n const json = await this.apiRequest({\n method: \"multipleRequest\",\n params: {\n requests: [\n {\n method: \"getDeviceInfo\",\n params: {\n device_info: {\n name: [\"basic_info\"],\n },\n },\n },\n ],\n },\n });\n\n const info = json.result.responses[0] as TAPOCameraResponseDeviceInfo;\n return info.result.device_info.basic_info;\n }\n\n async getStatus(): Promise<{ lensMask: boolean; alert: boolean }> {\n const json = await this.apiRequest({\n method: \"multipleRequest\",\n params: {\n requests: [\n {\n method: \"getAlertConfig\",\n params: {\n msg_alarm: {\n name: \"chn1_msg_alarm_info\",\n },\n },\n },\n {\n method: \"getLensMaskConfig\",\n params: {\n lens_mask: {\n name: \"lens_mask_info\",\n },\n },\n },\n {\n method: \"getForceWhitelampState\",\n params: {\n image: {\n name: \"switch\",\n },\n },\n },\n ],\n },\n });\n this.log.debug(`getStatus json: ${JSON.stringify(json)}`);\n if (json.error_code !== 0) {\n throw new Error(\"Camera replied with error\");\n }\n if (!json.result.responses) {\n throw new Error(\"Camera replied with invalid response\");\n }\n const alertConfig = json.result.responses.find((r) => r.method === \"getAlertConfig\") as TAPOCameraResponseGetAlert;\n\n const forceWhitelampState = json.result.responses.find((r) => r.method === \"getForceWhitelampState\") as TAPOCameraResponseGetForce;\n const lensMaskConfig = json.result.responses.find((r) => r.method === \"getLensMaskConfig\") as TAPOCameraResponseGetLensMask;\n\n return {\n alert: alertConfig.result.msg_alarm.chn1_msg_alarm_info.enabled === \"on\",\n lensMask: lensMaskConfig.result.lens_mask.lens_mask_info.enabled === \"on\",\n forceWhiteLamp: forceWhitelampState.result.image ? forceWhitelampState.result.image.switch.force_wtl_state === \"on\" : false,\n };\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import fetch, { RequestInit } from \"node-fetch\";\nimport https, { Agent } from \"https\";\nimport crypto from \"crypto\";\nimport { OnvifCamera } from \"./onvifCamera\";\nimport type {\n TAPOCameraEncryptedRequest,\n TAPOCameraEncryptedResponse,\n TAPOCameraRequest,\n TAPOCameraResponse,\n TAPOCameraResponseDeviceInfo,\n TAPOCameraResponseGetAlert,\n TAPOCameraResponseGetLensMask,\n} from \"./types/tapo\";\n\nconst MAX_LOGIN_RETRIES = 3;\nconst AES_BLOCK_SIZE = 16;\n\ntype CameraConfig = {\n name: string;\n ipAddress: string;\n username: string;\n password: string;\n streamUser: string;\n streamPassword: string;\n\n pullInterval?: number;\n disableStreaming?: boolean;\n disablePrivacyAccessory?: boolean;\n disableAlarmAccessory?: boolean;\n disableMotionAccessory?: boolean;\n lowQuality?: boolean;\n\n videoConfig?: VideoConfig;\n\n privacyAccessoryName?: string;\n alarmAccessoryName?: string;\n};\nexport class TAPOCamera extends OnvifCamera {\n private readonly kStreamPort = 554;\n private readonly httpsAgent: Agent;\n\n private readonly hashedMD5Password: string;\n private readonly hashedSha256Password: string;\n private passwordEncryptionMethod: \"md5\" | \"sha256\" | null = null;\n\n private isSecureConnectionValue: boolean | null = null;\n\n private stokPromise: (() => Promise<string>) | undefined;\n\n private readonly cnonce: string;\n private lsk: Buffer | undefined;\n private ivb: Buffer | undefined;\n private seq: number | undefined;\n private stok: string | undefined;\n\n private loginRetryCount = 0;\n\n constructor(\n protected readonly log: any,\n protected readonly config: CameraConfig,\n ) {\n super(log, config);\n this.log.debug(\"Constructing Camera on host: \" + config.ipAddress);\n\n this.httpsAgent = new https.Agent({\n rejectUnauthorized: false,\n });\n\n this.cnonce = this.generateCnonce();\n\n this.hashedMD5Password = crypto.createHash(\"md5\").update(config.password).digest(\"hex\").toUpperCase();\n this.hashedSha256Password = crypto.createHash(\"sha256\").update(config.password).digest(\"hex\").toUpperCase();\n }\n\n private getUsername() {\n return this.config.username || \"admin\";\n }\n\n private getHeaders() {\n const headers: Record<string, string> = {\n Host: `https://${this.config.ipAddress}`,\n Referer: `https://${this.config.ipAddress}`,\n Accept: \"application/json\",\n \"Accept-Encoding\": \"gzip, deflate\",\n \"User-Agent\": \"Tapo CameraClient Android\",\n Connection: \"close\",\n requestByApp: \"true\",\n \"Content-Type\": \"application/json; charset=UTF-8\",\n };\n return headers;\n }\n\n private getHashedPassword() {\n if (this.passwordEncryptionMethod === \"md5\") {\n return this.hashedMD5Password;\n } else if (this.passwordEncryptionMethod === \"sha256\") {\n return this.hashedSha256Password;\n } else {\n throw new Error(\"Unknown password encryption method\");\n }\n }\n\n private fetch(url: string, data: RequestInit) {\n return fetch(url, {\n agent: this.httpsAgent,\n headers: this.getHeaders(),\n ...data,\n });\n }\n\n private generateEncryptionToken(tokenType: string, nonce: string): Buffer {\n const hashedKey = crypto\n .createHash(\"sha256\")\n .update(this.cnonce + this.getHashedPassword() + nonce)\n .digest(\"hex\")\n .toUpperCase();\n return crypto\n .createHash(\"sha256\")\n .update(tokenType + this.cnonce + nonce + hashedKey)\n .digest()\n .slice(0, 16);\n }\n\n getAuthenticatedStreamUrl(lowQuality = false) {\n const prefix = `rtsp://${this.config.streamUser}:${this.config.streamPassword}@${this.config.ipAddress}:${this.kStreamPort}`;\n return lowQuality ? `${prefix}/stream2` : `${prefix}/stream1`;\n }\n\n private generateCnonce() {\n return crypto.randomBytes(8).toString(\"hex\").toUpperCase();\n }\n\n private validateDeviceConfirm(nonce: string, deviceConfirm: string) {\n const hashedNoncesWithSHA256 = crypto\n .createHash(\"sha256\")\n .update(this.cnonce + this.hashedSha256Password + nonce)\n .digest(\"hex\")\n .toUpperCase();\n const hashedNoncesWithMD5 = crypto\n .createHash(\"md5\")\n .update(this.cnonce + this.hashedMD5Password + nonce)\n .digest(\"hex\")\n .toUpperCase();\n\n if (deviceConfirm === hashedNoncesWithSHA256 + nonce + this.cnonce) {\n this.passwordEncryptionMethod = \"sha256\";\n return true;\n }\n\n if (deviceConfirm === hashedNoncesWithMD5 + nonce + this.cnonce) {\n this.passwordEncryptionMethod = \"md5\";\n return true;\n }\n\n return false;\n }\n\n async refreshStok(loginRetryCount = 0): Promise<string> {\n const isSecureConnection = await this.isSecureConnection();\n\n let response = null;\n let responseData = null;\n\n let fetchParams = {};\n if (isSecureConnection) {\n this.log.debug(\"StokRefresh: Using secure connection\");\n fetchParams = {\n method: \"post\",\n body: JSON.stringify({\n method: \"login\",\n params: {\n cnonce: this.cnonce,\n encrypt_type: \"3\",\n username: this.getUsername(),\n },\n }),\n };\n } else {\n this.log.debug(\"StokRefresh: Using unsecure connection\");\n fetchParams = {\n method: \"post\",\n body: JSON.stringify({\n method: \"login\",\n params: {\n username: this.getUsername(),\n password: this.getHashedPassword(),\n hashed: true,\n },\n }),\n };\n }\n\n response = await this.fetch(`https://${this.config.ipAddress}`, fetchParams);\n responseData = await response.json();\n\n this.log.debug(\"StokRefresh: Login response :>> \" + response.status + JSON.stringify(responseData));\n\n if (response.status === 401) {\n if (responseData?.result?.data?.code === 40411) {\n throw new Error(\"Invalid credentials\");\n }\n }\n\n if (isSecureConnection) {\n this.log.debug(\"StokRefresh: Using secure connection\");\n const nonce = responseData?.result?.data?.nonce;\n const deviceConfirm = responseData?.result?.data?.device_confirm;\n\n if (nonce && deviceConfirm && this.validateDeviceConfirm(nonce, deviceConfirm)) {\n const digestPasswd = crypto\n .createHash(\"sha256\")\n .update(this.getHashedPassword() + this.cnonce + nonce)\n .digest(\"hex\")\n .toUpperCase();\n\n const digestPasswdFull = Buffer.concat([\n Buffer.from(digestPasswd, \"utf8\"),\n Buffer.from(this.cnonce!, \"utf8\"),\n Buffer.from(nonce, \"utf8\"),\n ]).toString(\"utf8\");\n\n response = await this.fetch(`https://${this.config.ipAddress}`, {\n method: \"POST\",\n body: JSON.stringify({\n method: \"login\",\n params: {\n cnonce: this.cnonce,\n encrypt_type: \"3\",\n digest_passwd: digestPasswdFull,\n username: this.getUsername(),\n },\n }),\n });\n\n responseData = await response.json();\n\n this.log.debug(\"StokRefresh: Start_seq response :>>\" + response.status + JSON.stringify(responseData));\n\n if (responseData?.result?.start_seq) {\n if (responseData?.result?.user_group !== \"root\") {\n // # encrypted control via 3rd party account does not seem to be supported\n // # see https://github.com/JurajNyiri/HomeAssistant-Tapo-Control/issues/456\n throw new Error(\"Incorrect user_group detected\");\n }\n\n this.lsk = this.generateEncryptionToken(\"lsk\", nonce);\n this.ivb = this.generateEncryptionToken(\"ivb\", nonce);\n this.seq = responseData.result.start_seq;\n }\n }\n } else {\n this.passwordEncryptionMethod = \"md5\";\n }\n\n if (responseData?.result?.data?.sec_left > 0) {\n throw new Error(`StokRefresh: Temporary Suspension: Try again in ${responseData.result.data.sec_left} seconds`);\n }\n\n if (responseData?.data?.code == -40404 && responseData?.data?.sec_left > 0) {\n throw new Error(`StokRefresh: Temporary Suspension: Try again in ${responseData.data.sec_left} seconds`);\n }\n\n if (responseData?.result?.stok) {\n this.stok = responseData.result.stok;\n this.log.debug(\"StokRefresh: Success :>>\" + this.stok);\n\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n return this.stok!;\n }\n\n if (responseData?.error_code === -40413 && loginRetryCount < MAX_LOGIN_RETRIES) {\n this.log.debug(\n `Unexpected response, retrying: ${loginRetryCount}/${MAX_LOGIN_RETRIES}.` + response.status + JSON.stringify(responseData),\n );\n return this.refreshStok(loginRetryCount + 1);\n }\n\n throw new Error(\"Invalid authentication data\");\n }\n\n async isSecureConnection() {\n if (this.isSecureConnectionValue === null) {\n const response = await this.fetch(`https://${this.config.ipAddress}`, {\n method: \"post\",\n body: JSON.stringify({\n method: \"login\",\n params: {\n encrypt_type: \"3\",\n username: this.getUsername(),\n },\n }),\n });\n this.log.debug(JSON.stringify(response));\n const json = await response.json();\n\n this.log.debug(\"isSecureConnection response :>> \" + response.status + json);\n\n this.isSecureConnectionValue = json.error_code == -40413 && json?.result?.data?.encrypt_type?.includes(\"3\");\n }\n\n return this.isSecureConnectionValue;\n }\n\n getStok(loginRetryCount = 0): Promise<string> {\n if (this.stok) {\n return new Promise((resolve) => resolve(this.stok!));\n }\n\n if (!this.stokPromise) {\n this.stokPromise = () => this.refreshStok(loginRetryCount);\n }\n\n return this.stokPromise()\n .then(() => {\n return this.stok!;\n })\n .finally(() => {\n this.stokPromise = undefined;\n });\n }\n\n private async getAuthenticatedAPIURL(loginRetryCount = 0) {\n const token = await this.getStok(loginRetryCount);\n return `https://${this.config.ipAddress}/stok=${token}/ds`;\n }\n\n encryptRequest(request: string) {\n const cipher = crypto.createCipheriv(\"aes-128-cbc\", this.lsk!, this.ivb!);\n let ct_bytes = cipher.update(this.encryptPad(request, AES_BLOCK_SIZE), \"utf-8\", \"hex\");\n ct_bytes += cipher.final(\"hex\");\n return Buffer.from(ct_bytes, \"hex\");\n }\n\n private encryptPad(text: string, blocksize: number) {\n const padSize = blocksize - (text.length % blocksize);\n const padding = String.fromCharCode(padSize).repeat(padSize);\n return text + padding;\n }\n\n private decryptResponse(response: string): string {\n const decipher = crypto.createDecipheriv(\"aes-128-cbc\", this.lsk!, this.ivb!);\n let decrypted = decipher.update(response, \"base64\", \"utf-8\");\n decrypted += decipher.final(\"utf-8\");\n return this.encryptUnpad(decrypted, AES_BLOCK_SIZE);\n }\n\n private encryptUnpad(text: string, blockSize: number): string {\n const paddingLength = Number(text[text.length - 1]) || 0;\n if (paddingLength > blockSize || paddingLength > text.length) {\n throw new Error(\"Invalid padding\");\n }\n for (let i = text.length - paddingLength; i < text.length; i++) {\n if (text.charCodeAt(i) !== paddingLength) {\n throw new Error(\"Invalid padding\");\n }\n }\n return text.slice(0, text.length - paddingLength).toString();\n }\n\n private getTapoTag(request: TAPOCameraEncryptedRequest) {\n const tag = crypto\n .createHash(\"sha256\")\n .update(this.getHashedPassword() + this.cnonce)\n .digest(\"hex\")\n .toUpperCase();\n return crypto\n .createHash(\"sha256\")\n .update(tag + JSON.stringify(request) + this.seq!.toString())\n .digest(\"hex\")\n .toUpperCase();\n }\n\n private pendingAPIRequests: Map<string, Promise<TAPOCameraResponse>> = new Map();\n\n private async apiRequest(req: TAPOCameraRequest, loginRetryCount = 0): Promise<TAPOCameraResponse> {\n const reqJson = JSON.stringify(req);\n\n if (this.pendingAPIRequests.has(reqJson)) {\n return this.pendingAPIRequests.get(reqJson) as Promise<TAPOCameraResponse>;\n }\n\n this.log.debug(\"API new request: \" + reqJson);\n\n this.pendingAPIRequests.set(\n reqJson,\n (async () => {\n try {\n const isSecureConnection = await this.isSecureConnection();\n const url = await this.getAuthenticatedAPIURL(loginRetryCount);\n\n const fetchParams: RequestInit = {\n method: \"post\",\n };\n\n if (this.seq && isSecureConnection) {\n const encryptedRequest: TAPOCameraEncryptedRequest = {\n method: \"securePassthrough\",\n params: {\n request: Buffer.from(this.encryptRequest(JSON.stringify(req))).toString(\"base64\"),\n },\n };\n fetchParams.headers = {\n ...this.getHeaders(),\n Tapo_tag: this.getTapoTag(encryptedRequest),\n Seq: this.seq.toString(),\n };\n fetchParams.body = JSON.stringify(encryptedRequest);\n this.seq += 1;\n } else {\n fetchParams.body = JSON.stringify(req);\n }\n\n const response = await this.fetch(url, fetchParams);\n let json = await response.json();\n\n if (isSecureConnection) {\n const encryptedResponse = json as TAPOCameraEncryptedResponse;\n if (encryptedResponse.result.response) {\n const decryptedResponse = this.decryptResponse(encryptedResponse.result.response);\n json = JSON.parse(decryptedResponse) as TAPOCameraResponse;\n }\n } else {\n json = json as TAPOCameraResponse;\n }\n\n this.log.debug(`API response: ` + response.status, JSON.stringify(json));\n\n // Apparently the Tapo C200 returns 500 on successful requests,\n // but it's indicating an expiring token, therefore refresh the token next time\n if (isSecureConnection && response.status === 500) {\n this.stok = undefined;\n }\n\n // Check if we have to refresh the token\n if (json.error_code === -40401 || json.error_code === -1) {\n this.log.debug(\"API request failed, reauthenticating\");\n this.stok = undefined;\n return this.apiRequest(req, loginRetryCount + 1);\n }\n\n return json as TAPOCameraResponse;\n } finally {\n this.pendingAPIRequests.delete(reqJson);\n }\n })(),\n );\n\n return this.pendingAPIRequests.get(reqJson) as Promise<TAPOCameraResponse>;\n }\n\n async setLensMaskConfig(value: boolean) {\n this.adapter.log.debug(\"Processing setLensMaskConfig\" + value);\n\n const json = await this.apiRequest({\n method: \"multipleRequest\",\n params: {\n requests: [\n {\n method: \"setLensMaskConfig\",\n params: {\n lens_mask: {\n lens_mask_info: {\n enabled: value ? \"on\" : \"off\",\n },\n },\n },\n },\n ],\n },\n });\n\n if (json.error_code !== 0) {\n throw new Error(\"Failed to perform action\");\n }\n }\n\n async setAlertConfig(value: boolean) {\n this.log.debug(\"Processing setAlertConfig\" + value);\n\n const json = await this.apiRequest({\n method: \"multipleRequest\",\n params: {\n requests: [\n {\n method: \"setAlertConfig\",\n params: {\n msg_alarm: {\n chn1_msg_alarm_info: {\n enabled: value ? \"on\" : \"off\",\n },\n },\n },\n },\n ],\n },\n });\n\n return json.error_code !== 0;\n }\n async setForceWhitelampState(value: boolean) {\n const json = await this.apiRequest({\n method: \"multipleRequest\",\n params: {\n requests: [\n {\n method: \"setForceWhitelampState\",\n params: {\n image: {\n switch: {\n force_wtl_state: value ? \"on\" : \"off\",\n },\n },\n },\n },\n ],\n },\n });\n\n return json.error_code !== 0;\n }\n async moveMotorStep(angle: string) {\n angle = angle.toString();\n const json = await this.apiRequest({ method: \"do\", motor: { movestep: { direction: angle } } });\n\n return json.error_code !== 0;\n }\n\n async moveMotor(x: number, y: number) {\n const json = await this.apiRequest({\n method: \"multipleRequest\",\n params: {\n requests: [{ method: \"do\", motor: { move: { x_coord: x, y_coord: y } } }],\n },\n });\n\n return json.error_code !== 0;\n }\n\n async getBasicInfo() {\n const json = await this.apiRequest({\n method: \"multipleRequest\",\n params: {\n requests: [\n {\n method: \"getDeviceInfo\",\n params: {\n device_info: {\n name: [\"basic_info\"],\n },\n },\n },\n ],\n },\n });\n\n const info = json.result.responses[0] as TAPOCameraResponseDeviceInfo;\n return info.result.device_info.basic_info;\n }\n\n async getStatus(): Promise<{ lensMask: boolean; alert: boolean }> {\n const json = await this.apiRequest({\n method: \"multipleRequest\",\n params: {\n requests: [\n {\n method: \"getAlertConfig\",\n params: {\n msg_alarm: {\n name: \"chn1_msg_alarm_info\",\n },\n },\n },\n {\n method: \"getLensMaskConfig\",\n params: {\n lens_mask: {\n name: \"lens_mask_info\",\n },\n },\n },\n {\n method: \"getForceWhitelampState\",\n params: {\n image: {\n name: \"switch\",\n },\n },\n },\n ],\n },\n });\n this.log.debug(`getStatus json: ${JSON.stringify(json)}`);\n if (json.error_code !== 0) {\n throw new Error(\"Camera replied with error\");\n }\n if (!json.result.responses) {\n throw new Error(\"Camera replied with invalid response\");\n }\n const alertConfig = json.result.responses.find((r) => r.method === \"getAlertConfig\") as TAPOCameraResponseGetAlert;\n\n const forceWhitelampState = json.result.responses.find((r) => r.method === \"getForceWhitelampState\") as TAPOCameraResponseGetForce;\n const lensMaskConfig = json.result.responses.find((r) => r.method === \"getLensMaskConfig\") as TAPOCameraResponseGetLensMask;\n\n return {\n alert: alertConfig.result.msg_alarm.chn1_msg_alarm_info.enabled === \"on\",\n lensMask: lensMaskConfig.result.lens_mask.lens_mask_info.enabled === \"on\",\n forceWhiteLamp: forceWhitelampState.result.image ? forceWhitelampState.result.image.switch.force_wtl_state === \"on\" : false,\n };\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["https", "crypto", "fetch"]
7
7
  }
@@ -80,7 +80,7 @@ class L530 extends import_l520e.default {
80
80
  }
81
81
  return response.result;
82
82
  }).catch((error) => {
83
- if (error.message.indexOf("9999") > 0) {
83
+ if (error && error.message.indexOf("9999") > 0) {
84
84
  return this.reconnect().then(() => {
85
85
  return this.handleRequest(payload).then(() => {
86
86
  return true;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/lib/utils/l530.ts"],
4
- "sourcesContent": ["import L520E from \"./l520e\";\nimport { PowerUsage } from \"./powerUsage\";\nimport { ColorLightSysinfo, ConsumptionInfo } from \"./types\";\n\nexport default class L530 extends L520E {\n private _colorLightSysInfo!: ColorLightSysinfo;\n private _consumption!: ConsumptionInfo;\n\n constructor(\n public readonly log: any,\n public readonly ipAddress: string,\n public readonly email: string,\n public readonly password: string,\n public readonly timeout: number,\n ) {\n super(log, ipAddress, email, password, timeout);\n this.log.debug(\"Constructing L530 on host: \" + ipAddress);\n this._consumption = {\n total: 0,\n current: 0,\n };\n }\n\n async getDeviceInfo(): Promise<ColorLightSysinfo> {\n return super.getDeviceInfo().then(() => {\n return this.getSysInfo();\n });\n }\n\n async setColor(hue: number, saturation: number): Promise<boolean> {\n if (!hue) {\n hue = 0;\n }\n if (!saturation) {\n saturation = 0;\n }\n const payload =\n \"{\" +\n '\"method\": \"set_device_info\",' +\n '\"params\": {' +\n '\"hue\": ' +\n Math.round(hue) +\n \",\" +\n '\"color_temp\": 0,' +\n '\"saturation\": ' +\n Math.round(saturation) +\n \"},\" +\n '\"requestTimeMils\": ' +\n Math.round(Date.now() * 1000) +\n \"\" +\n \"};\";\n\n return this.sendRequest(payload);\n }\n\n protected setSysInfo(sysInfo: ColorLightSysinfo) {\n this._colorLightSysInfo = sysInfo;\n this._colorLightSysInfo.last_update = Date.now();\n }\n\n public getSysInfo(): ColorLightSysinfo {\n return this._colorLightSysInfo;\n }\n\n async getEnergyUsage(): Promise<PowerUsage> {\n const payload = \"{\" + '\"method\": \"get_device_usage\",' + '\"requestTimeMils\": ' + Math.round(Date.now() * 1000) + \"\" + \"};\";\n return this.handleRequest(payload)\n .then((response) => {\n if (response && response.result) {\n this._consumption = {\n total: response.result.power_usage.today / 1000,\n current: this._consumption ? response.result.power_usage.today - this._consumption.current : 0,\n };\n } else {\n this._consumption = {\n total: 0,\n current: 0,\n };\n }\n\n return response.result;\n })\n .catch((error) => {\n if (error.message.indexOf(\"9999\") > 0) {\n return this.reconnect().then(() => {\n return this.handleRequest(payload).then(() => {\n return true;\n });\n });\n }\n return false;\n });\n }\n\n public getPowerConsumption(): ConsumptionInfo {\n return this._consumption;\n }\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAkB;AAIlB,MAAO,aAA2B,aAAAA,QAAM;AAAA,EAItC,YACkB,KACA,WACA,OACA,UACA,SAChB;AACA,UAAM,KAAK,WAAW,OAAO,UAAU,OAAO;AAN9B;AACA;AACA;AACA;AACA;AAGhB,SAAK,IAAI,MAAM,gCAAgC,SAAS;AACxD,SAAK,eAAe;AAAA,MAClB,OAAO;AAAA,MACP,SAAS;AAAA,IACX;AAAA,EACF;AAAA,EAEA,MAAM,gBAA4C;AAChD,WAAO,MAAM,cAAc,EAAE,KAAK,MAAM;AACtC,aAAO,KAAK,WAAW;AAAA,IACzB,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,SAAS,KAAa,YAAsC;AAChE,QAAI,CAAC,KAAK;AACR,YAAM;AAAA,IACR;AACA,QAAI,CAAC,YAAY;AACf,mBAAa;AAAA,IACf;AACA,UAAM,UACJ,oDAIA,KAAK,MAAM,GAAG,IACd,oCAGA,KAAK,MAAM,UAAU,IACrB,0BAEA,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI,IAE5B;AAEF,WAAO,KAAK,YAAY,OAAO;AAAA,EACjC;AAAA,EAEU,WAAW,SAA4B;AAC/C,SAAK,qBAAqB;AAC1B,SAAK,mBAAmB,cAAc,KAAK,IAAI;AAAA,EACjD;AAAA,EAEO,aAAgC;AACrC,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAM,iBAAsC;AAC1C,UAAM,UAAU,sDAAgE,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI,IAAS;AACrH,WAAO,KAAK,cAAc,OAAO,EAC9B,KAAK,CAAC,aAAa;AAClB,UAAI,YAAY,SAAS,QAAQ;AAC/B,aAAK,eAAe;AAAA,UAClB,OAAO,SAAS,OAAO,YAAY,QAAQ;AAAA,UAC3C,SAAS,KAAK,eAAe,SAAS,OAAO,YAAY,QAAQ,KAAK,aAAa,UAAU;AAAA,QAC/F;AAAA,MACF,OAAO;AACL,aAAK,eAAe;AAAA,UAClB,OAAO;AAAA,UACP,SAAS;AAAA,QACX;AAAA,MACF;AAEA,aAAO,SAAS;AAAA,IAClB,CAAC,EACA,MAAM,CAAC,UAAU;AAChB,UAAI,MAAM,QAAQ,QAAQ,MAAM,IAAI,GAAG;AACrC,eAAO,KAAK,UAAU,EAAE,KAAK,MAAM;AACjC,iBAAO,KAAK,cAAc,OAAO,EAAE,KAAK,MAAM;AAC5C,mBAAO;AAAA,UACT,CAAC;AAAA,QACH,CAAC;AAAA,MACH;AACA,aAAO;AAAA,IACT,CAAC;AAAA,EACL;AAAA,EAEO,sBAAuC;AAC5C,WAAO,KAAK;AAAA,EACd;AACF;",
4
+ "sourcesContent": ["import L520E from \"./l520e\";\nimport { PowerUsage } from \"./powerUsage\";\nimport { ColorLightSysinfo, ConsumptionInfo } from \"./types\";\n\nexport default class L530 extends L520E {\n private _colorLightSysInfo!: ColorLightSysinfo;\n private _consumption!: ConsumptionInfo;\n\n constructor(\n public readonly log: any,\n public readonly ipAddress: string,\n public readonly email: string,\n public readonly password: string,\n public readonly timeout: number,\n ) {\n super(log, ipAddress, email, password, timeout);\n this.log.debug(\"Constructing L530 on host: \" + ipAddress);\n this._consumption = {\n total: 0,\n current: 0,\n };\n }\n\n async getDeviceInfo(): Promise<ColorLightSysinfo> {\n return super.getDeviceInfo().then(() => {\n return this.getSysInfo();\n });\n }\n\n async setColor(hue: number, saturation: number): Promise<boolean> {\n if (!hue) {\n hue = 0;\n }\n if (!saturation) {\n saturation = 0;\n }\n const payload =\n \"{\" +\n '\"method\": \"set_device_info\",' +\n '\"params\": {' +\n '\"hue\": ' +\n Math.round(hue) +\n \",\" +\n '\"color_temp\": 0,' +\n '\"saturation\": ' +\n Math.round(saturation) +\n \"},\" +\n '\"requestTimeMils\": ' +\n Math.round(Date.now() * 1000) +\n \"\" +\n \"};\";\n\n return this.sendRequest(payload);\n }\n\n protected setSysInfo(sysInfo: ColorLightSysinfo) {\n this._colorLightSysInfo = sysInfo;\n this._colorLightSysInfo.last_update = Date.now();\n }\n\n public getSysInfo(): ColorLightSysinfo {\n return this._colorLightSysInfo;\n }\n\n async getEnergyUsage(): Promise<PowerUsage> {\n const payload = \"{\" + '\"method\": \"get_device_usage\",' + '\"requestTimeMils\": ' + Math.round(Date.now() * 1000) + \"\" + \"};\";\n return this.handleRequest(payload)\n .then((response) => {\n if (response && response.result) {\n this._consumption = {\n total: response.result.power_usage.today / 1000,\n current: this._consumption ? response.result.power_usage.today - this._consumption.current : 0,\n };\n } else {\n this._consumption = {\n total: 0,\n current: 0,\n };\n }\n\n return response.result;\n })\n .catch((error) => {\n if (error && error.message.indexOf(\"9999\") > 0) {\n return this.reconnect().then(() => {\n return this.handleRequest(payload).then(() => {\n return true;\n });\n });\n }\n return false;\n });\n }\n\n public getPowerConsumption(): ConsumptionInfo {\n return this._consumption;\n }\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAkB;AAIlB,MAAO,aAA2B,aAAAA,QAAM;AAAA,EAItC,YACkB,KACA,WACA,OACA,UACA,SAChB;AACA,UAAM,KAAK,WAAW,OAAO,UAAU,OAAO;AAN9B;AACA;AACA;AACA;AACA;AAGhB,SAAK,IAAI,MAAM,gCAAgC,SAAS;AACxD,SAAK,eAAe;AAAA,MAClB,OAAO;AAAA,MACP,SAAS;AAAA,IACX;AAAA,EACF;AAAA,EAEA,MAAM,gBAA4C;AAChD,WAAO,MAAM,cAAc,EAAE,KAAK,MAAM;AACtC,aAAO,KAAK,WAAW;AAAA,IACzB,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,SAAS,KAAa,YAAsC;AAChE,QAAI,CAAC,KAAK;AACR,YAAM;AAAA,IACR;AACA,QAAI,CAAC,YAAY;AACf,mBAAa;AAAA,IACf;AACA,UAAM,UACJ,oDAIA,KAAK,MAAM,GAAG,IACd,oCAGA,KAAK,MAAM,UAAU,IACrB,0BAEA,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI,IAE5B;AAEF,WAAO,KAAK,YAAY,OAAO;AAAA,EACjC;AAAA,EAEU,WAAW,SAA4B;AAC/C,SAAK,qBAAqB;AAC1B,SAAK,mBAAmB,cAAc,KAAK,IAAI;AAAA,EACjD;AAAA,EAEO,aAAgC;AACrC,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAM,iBAAsC;AAC1C,UAAM,UAAU,sDAAgE,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI,IAAS;AACrH,WAAO,KAAK,cAAc,OAAO,EAC9B,KAAK,CAAC,aAAa;AAClB,UAAI,YAAY,SAAS,QAAQ;AAC/B,aAAK,eAAe;AAAA,UAClB,OAAO,SAAS,OAAO,YAAY,QAAQ;AAAA,UAC3C,SAAS,KAAK,eAAe,SAAS,OAAO,YAAY,QAAQ,KAAK,aAAa,UAAU;AAAA,QAC/F;AAAA,MACF,OAAO;AACL,aAAK,eAAe;AAAA,UAClB,OAAO;AAAA,UACP,SAAS;AAAA,QACX;AAAA,MACF;AAEA,aAAO,SAAS;AAAA,IAClB,CAAC,EACA,MAAM,CAAC,UAAU;AAChB,UAAI,SAAS,MAAM,QAAQ,QAAQ,MAAM,IAAI,GAAG;AAC9C,eAAO,KAAK,UAAU,EAAE,KAAK,MAAM;AACjC,iBAAO,KAAK,cAAc,OAAO,EAAE,KAAK,MAAM;AAC5C,mBAAO;AAAA,UACT,CAAC;AAAA,QACH,CAAC;AAAA,MACH;AACA,aAAO;AAAA,IACT,CAAC;AAAA,EACL;AAAA,EAEO,sBAAuC;AAC5C,WAAO,KAAK;AAAA,EACd;AACF;",
6
6
  "names": ["L520E"]
7
7
  }
@@ -161,7 +161,7 @@ class P100 {
161
161
  return this.handleError(res.data.error_code, "106");
162
162
  }
163
163
  }).catch((error) => {
164
- this.log.error("111 Error: " + error.message);
164
+ this.log.error("111 Error: " + error ? error.message : "");
165
165
  return error;
166
166
  });
167
167
  }
@@ -200,7 +200,7 @@ class P100 {
200
200
  return this.handleError(JSON.parse(decryptedResponse).error_code, "157");
201
201
  }
202
202
  }).catch((error) => {
203
- this.log.error("Error: " + error.message);
203
+ this.log.error("Error Login: " + error ? error.message : "");
204
204
  return error;
205
205
  });
206
206
  }
@@ -235,14 +235,21 @@ class P100 {
235
235
  } catch (error) {
236
236
  return this.handleError(res.data.error_code, "318");
237
237
  }
238
- }).catch((error) => {
239
- this.log.error("322 Error: " + error.message);
238
+ }).catch(async (error) => {
239
+ this.log.error("322 Error: " + error ? error.message : "");
240
+ if (this._reconnect_counter <= 3) {
241
+ this.log.info("Trying to reconnect...");
242
+ await this.newReconnect();
243
+ }
240
244
  return error;
241
245
  });
242
246
  }
243
247
  async handshake_new() {
244
248
  const local_seed = this.crypto.randomBytes(16);
245
249
  await this.raw_request("handshake1", local_seed, "arraybuffer").then((res) => {
250
+ if (!res || !res.subarray) {
251
+ return;
252
+ }
246
253
  const remote_seed = res.subarray(0, 16);
247
254
  const server_hash = res.subarray(16);
248
255
  let auth_hash = void 0;
@@ -301,6 +308,7 @@ class P100 {
301
308
  Cookie: this.cookie
302
309
  };
303
310
  if (this.tpLinkCipher) {
311
+ this.log.debug("using old cypher");
304
312
  const encryptedPayload = this.tpLinkCipher.encrypt(payload);
305
313
  const securePassthroughPayload = {
306
314
  method: "securePassthrough",
@@ -339,10 +347,11 @@ class P100 {
339
347
  return this.handleError(JSON.parse(decryptedResponse).error_code, "340");
340
348
  }
341
349
  }).catch((error) => {
342
- this.log.error("371 Error: " + error.message);
350
+ this.log.error("371 Error: " + error ? error.message : "");
343
351
  return error;
344
352
  });
345
353
  } else if (this.newTpLinkCipher) {
354
+ this.log.debug("using new cypher");
346
355
  const data = this.newTpLinkCipher.encrypt(payload);
347
356
  const URL2 = "http://" + this.ip + "/app/request";
348
357
  const headers2 = {
@@ -361,6 +370,7 @@ class P100 {
361
370
  params: { seq: data.seq.toString() }
362
371
  };
363
372
  return this.axios.post(URL2, data.encryptedPayload, config).then((res) => {
373
+ this.log.debug(JSON.stringify(res.data));
364
374
  if (res.data.error_code) {
365
375
  return this.handleError(res.data.error_code, "309");
366
376
  }
@@ -369,18 +379,21 @@ class P100 {
369
379
  this.cookie = res.headers["set-cookie"][0].split(";")[0];
370
380
  }
371
381
  const response = JSON.parse(this.newTpLinkCipher.decrypt(res.data));
382
+ this.log.debug("Device Info: " + JSON.stringify(response));
372
383
  if (response.error_code !== 0) {
373
384
  return this.handleError(response.error_code, "333");
374
385
  }
375
386
  this.setSysInfo(response.result);
376
- this.log.debug("Device Info: ", response.result);
377
387
  return this.getSysInfo();
378
388
  } catch (error) {
379
389
  return this.handleError(res.data.error_code, "480");
380
390
  }
381
- }).catch((error) => {
382
- this.log.error("322 Error: " + error.message);
383
- return error;
391
+ }).catch(async (error) => {
392
+ this.log.error("322 #2 Error: " + error ? error.message : "");
393
+ if (this._reconnect_counter <= 3) {
394
+ this.log.info("Trying to reconnect...");
395
+ await this.newReconnect();
396
+ }
384
397
  });
385
398
  } else {
386
399
  return new Promise((resolve, reject) => {
@@ -444,7 +457,7 @@ class P100 {
444
457
  return this.handleRequest(payload).then((result) => {
445
458
  return result ? true : false;
446
459
  }).catch((error) => {
447
- if (error.message.indexOf("9999") > 0 && this._reconnect_counter <= 3) {
460
+ if (error && error.message.indexOf("9999") > 0 && this._reconnect_counter <= 3) {
448
461
  return this.reconnect().then(() => {
449
462
  return this.handleRequest(payload).then((result) => {
450
463
  return result ? true : false;
@@ -458,7 +471,7 @@ class P100 {
458
471
  return this.newHandleRequest(payload).then((result) => {
459
472
  return result ? true : false;
460
473
  }).catch((error) => {
461
- if (error.message.indexOf("9999") > 0 && this._reconnect_counter <= 3) {
474
+ if (error && error.message.indexOf("9999") > 0 && this._reconnect_counter <= 3) {
462
475
  return this.newReconnect().then(() => {
463
476
  return this.newHandleRequest(payload).then((result) => {
464
477
  return result ? true : false;