iobroker.tapo 0.4.0 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -32,7 +32,11 @@ tapo.0.id.remote auf true/false setzen steuert den jeweiligen Befehl. Der Befehl
32
32
  <https://forum.iobroker.net/topic/57336/test-adapter-tp-link-tapo/>
33
33
 
34
34
  ## Changelog
35
- ### 0.4.0 (2024-11-29)
35
+ ### 0.4.2 (2024-12-09)
36
+
37
+ - fix handshake for device with HW v1.20
38
+
39
+ ### 0.4.1 (2024-11-29)
36
40
 
37
41
  - fixed Get Device Info failed error
38
42
 
@@ -161,7 +161,15 @@ const _TAPOCamera = class extends import_onvifCamera.OnvifCamera {
161
161
  })
162
162
  };
163
163
  }
164
- const responseLogin = await this.fetch(`https://${this.config.ipAddress}`, fetchParams);
164
+ const responseLogin = await this.fetch(`https://${this.config.ipAddress}`, fetchParams).catch((e) => {
165
+ this.log.debug("refreshStok: Error during login", e);
166
+ return null;
167
+ });
168
+ if (!responseLogin) {
169
+ this.log.debug("refreshStok: empty response login, raising exception");
170
+ this.log.error("Empty response login");
171
+ return;
172
+ }
165
173
  const responseLoginData = await responseLogin.json();
166
174
  let response, responseData;
167
175
  if (!responseLoginData) {
@@ -226,8 +234,8 @@ const _TAPOCamera = class extends import_onvifCamera.OnvifCamera {
226
234
  loginRetryCount,
227
235
  responseLoginData
228
236
  );
229
- this.log.info("Invalid device confirm. Firmware Fix by TP-Link expected in Dezember 2024. Only motion detection is supported.");
230
- this.log.info("Or follow https://github.com/JurajNyiri/HomeAssistant-Tapo-Control/blob/main/add_camera_with_new_firmware.md");
237
+ this.log.error("Invalid device confirm. Firmware Fix by TP-Link expected in Dezember 2024. Only motion detection is supported.");
238
+ this.log.error("Or follow https://github.com/JurajNyiri/HomeAssistant-Tapo-Control/blob/main/add_camera_with_new_firmware.md");
231
239
  return;
232
240
  }
233
241
  } else {
@@ -289,7 +297,7 @@ const _TAPOCamera = class extends import_onvifCamera.OnvifCamera {
289
297
  }
290
298
  this.stokPromise().then(() => {
291
299
  if (!this.stok) {
292
- this.log.info("STOK not found");
300
+ this.log.error("STOK not found");
293
301
  }
294
302
  resolve(this.stok);
295
303
  }).finally(() => {
@@ -369,7 +377,14 @@ const _TAPOCamera = class extends import_onvifCamera.OnvifCamera {
369
377
  } else {
370
378
  fetchParams.body = JSON.stringify(req);
371
379
  }
372
- const response = await this.fetch(url, fetchParams);
380
+ const response = await this.fetch(url, fetchParams).catch((e) => {
381
+ this.log.debug("Error during camera fetch", e);
382
+ return;
383
+ });
384
+ if (!response) {
385
+ this.log.debug("API request failed, empty response");
386
+ return {};
387
+ }
373
388
  const responseDataTmp = await response.json();
374
389
  if (isSecureConnection && response.status === 500) {
375
390
  this.log.debug("Stok expired, reauthenticating on next request, setting STOK to undefined");
@@ -489,7 +504,7 @@ const _TAPOCamera = class extends import_onvifCamera.OnvifCamera {
489
504
  }
490
505
  });
491
506
  if (!responseData || !responseData.result || !responseData.result.responses) {
492
- this.log.info("No response data found");
507
+ this.log.error("No response data found");
493
508
  return {
494
509
  alarm: void 0,
495
510
  eyes: void 0,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/lib/utils/camera/tapoCamera.ts"],
4
- "sourcesContent": ["// import https, { Agent } from \"https\";\nimport crypto from \"crypto\";\nimport { OnvifCamera } from \"./onvifCamera\";\nimport type {\n TAPOBasicInfo,\n TAPOCameraEncryptedRequest,\n TAPOCameraEncryptedResponse,\n TAPOCameraLoginResponse,\n TAPOCameraRefreshStokResponse,\n TAPOCameraRequest,\n TAPOCameraResponse,\n TAPOCameraResponseDeviceInfo,\n TAPOCameraSetRequest,\n} from \"./types/tapo\";\n\nconst MAX_LOGIN_RETRIES = 3;\nconst AES_BLOCK_SIZE = 16;\n\nimport { Agent, setGlobalDispatcher } from \"undici\";\n\nconst ERROR_CODES_MAP = {\n \"-40401\": \"Invalid stok value\",\n \"-40210\": \"Function not supported\",\n \"-64303\": \"Action cannot be done while camera is in patrol mode.\",\n \"-64324\": \"Privacy mode is ON, not able to execute\",\n \"-64302\": \"Preset ID not found\",\n \"-64321\": \"Preset ID was deleted so no longer exists\",\n \"-40106\": \"Parameter to get/do does not exist\",\n \"-40105\": \"Method does not exist\",\n \"-40101\": \"Parameter to set does not exist\",\n \"-40209\": \"Invalid login credentials\",\n \"-64304\": \"Maximum Pan/Tilt range reached\",\n \"-71103\": \"User ID is not authorized\",\n};\n\nexport type Status = {\n eyes: boolean | undefined;\n alarm: boolean | undefined;\n notifications: boolean | undefined;\n motionDetection: boolean | undefined;\n led: boolean | undefined;\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 disableEyesToggleAccessory?: boolean;\n disableAlarmToggleAccessory?: boolean;\n disableNotificationsToggleAccessory?: boolean;\n disableMotionDetectionToggleAccessory?: boolean;\n disableLEDToggleAccessory?: boolean;\n\n disableMotionSensorAccessory?: boolean;\n lowQuality?: boolean;\n\n videoMaxWidth?: number;\n videoMaxHeight?: number;\n videoMaxFPS?: number;\n videoForceMax?: boolean;\n videoMaxBirate?: number;\n videoPacketSize?: number;\n videoCodec?: string;\n\n videoConfig?: VideoConfig;\n\n eyesToggleAccessoryName?: string;\n alarmToggleAccessoryName?: string;\n notificationsToggleAccessoryName?: string;\n motionDetectionToggleAccessoryName?: string;\n ledToggleAccessoryName?: string;\n};\nexport class TAPOCamera extends OnvifCamera {\n private readonly kStreamPort = 554;\n private readonly fetchAgent: Agent;\n\n private readonly hashedPassword: 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<void>) | 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 constructor(\n protected readonly log: any,\n protected readonly config: CameraConfig,\n ) {\n super(log, config);\n process.env.NODE_TLS_REJECT_UNAUTHORIZED = 0;\n this.fetchAgent = new Agent({\n connectTimeout: 5_000,\n connect: {\n // TAPO devices have self-signed certificates\n rejectUnauthorized: false,\n ciphers: \"AES256-SHA:AES128-GCM-SHA256\",\n },\n });\n setGlobalDispatcher(this.fetchAgent);\n\n this.cnonce = this.generateCnonce();\n\n this.hashedPassword = 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(): Record<string, string> {\n return {\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 }\n\n private getHashedPassword() {\n if (this.passwordEncryptionMethod === \"md5\") {\n return this.hashedPassword;\n } else if (this.passwordEncryptionMethod === \"sha256\") {\n return this.hashedSha256Password;\n } else {\n this.log.error(\"Unknown password encryption method\");\n }\n }\n\n private fetch(url: string, data: RequestInit) {\n return fetch(url, {\n headers: this.getHeaders(),\n // @ts-expect-error Dispatcher type not there\n dispatcher: this.fetchAgent,\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 this.passwordEncryptionMethod = null;\n\n const hashedNoncesWithSHA256 = crypto\n .createHash(\"sha256\")\n .update(this.cnonce + this.hashedSha256Password + nonce)\n .digest(\"hex\")\n .toUpperCase();\n if (deviceConfirm === hashedNoncesWithSHA256 + nonce + this.cnonce) {\n this.passwordEncryptionMethod = \"sha256\";\n return true;\n }\n\n const hashedNoncesWithMD5 = crypto\n .createHash(\"md5\")\n .update(this.cnonce + this.hashedPassword + nonce)\n .digest(\"hex\")\n .toUpperCase();\n if (deviceConfirm === hashedNoncesWithMD5 + nonce + this.cnonce) {\n this.passwordEncryptionMethod = \"md5\";\n return true;\n }\n\n this.log.debug('Invalid device confirm, expected \"sha256\" or \"md5\" to match, but none found', {\n hashedNoncesWithMD5,\n hashedNoncesWithSHA256,\n deviceConfirm,\n nonce,\n cnonce: this,\n });\n\n return this.passwordEncryptionMethod !== null;\n }\n\n async refreshStok(loginRetryCount = 0): Promise<void> {\n this.log.debug(\"refreshStok: Refreshing stok...\");\n\n const isSecureConnection = await this.isSecureConnection();\n\n let fetchParams = {};\n if (isSecureConnection) {\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 fetchParams = {\n method: \"post\",\n body: JSON.stringify({\n method: \"login\",\n params: {\n username: this.getUsername(),\n password: this.hashedPassword,\n hashed: true,\n },\n }),\n };\n }\n\n const responseLogin = await this.fetch(`https://${this.config.ipAddress}`, fetchParams);\n const responseLoginData = (await responseLogin.json()) as TAPOCameraRefreshStokResponse;\n\n let response, responseData;\n\n if (!responseLoginData) {\n this.log.debug(\"refreshStok: empty response login data, raising exception\", responseLogin.status);\n this.log.error(\"Empty response login data\");\n }\n\n this.log.debug(\"refreshStok: Login response\", responseLogin.status, responseLoginData);\n\n if (responseLogin.status === 401 && responseLoginData.result?.data?.code === -40411) {\n this.log.debug(\"refreshStok: invalid credentials, raising exception\", responseLogin.status);\n this.log.error(\"Invalid credentials\");\n }\n\n if (isSecureConnection) {\n const nonce = responseLoginData.result?.data?.nonce;\n const deviceConfirm = responseLoginData.result?.data?.device_confirm;\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 this.log.debug(\"refreshStok: sending start_seq request\");\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()) as TAPOCameraRefreshStokResponse;\n\n if (!responseData) {\n this.log.debug(\"refreshStock: empty response start_seq data, raising exception\", response.status);\n this.log.error(\"Empty response start_seq data\");\n return;\n }\n\n this.log.debug(\"refreshStok: start_seq response\", response.status, JSON.stringify(responseData));\n\n if (responseData.result?.start_seq) {\n if (responseData.result?.user_group !== \"root\") {\n this.log.debug(\"refreshStock: Incorrect user_group detected\");\n\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 this.log.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 } else {\n if (responseLoginData.error_code === -40413 && loginRetryCount < MAX_LOGIN_RETRIES) {\n this.log.debug(\n `refreshStock: Invalid device confirm, retrying: ${loginRetryCount}/${MAX_LOGIN_RETRIES}.`,\n responseLogin.status,\n responseLoginData,\n );\n return this.refreshStok(loginRetryCount + 1);\n }\n\n this.log.debug(\n \"refreshStock: Invalid device confirm and loginRetryCount exhausted, raising exception\",\n loginRetryCount,\n responseLoginData,\n );\n this.log.info(\"Invalid device confirm. Firmware Fix by TP-Link expected in Dezember 2024. Only motion detection is supported.\");\n this.log.info(\"Or follow https://github.com/JurajNyiri/HomeAssistant-Tapo-Control/blob/main/add_camera_with_new_firmware.md\");\n return;\n }\n } else {\n this.passwordEncryptionMethod = \"md5\";\n response = responseLogin;\n responseData = responseLoginData;\n }\n\n if (responseData.result?.data?.sec_left && responseData.result.data.sec_left > 0) {\n this.log.debug(\"refreshStok: temporary suspension\", responseData);\n\n this.log.error(`Temporary Suspension: Try again in ${responseData.result.data.sec_left} seconds`);\n }\n\n if (responseData?.data?.code === -40404 && responseData?.data?.sec_left && responseData.data.sec_left > 0) {\n this.log.debug(\"refreshStok: temporary suspension\", responseData);\n\n this.log.error(`refreshStok: 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(\"refreshStok: Success in obtaining STOK\", this.stok);\n return;\n }\n\n if (responseData?.error_code === -40413 && loginRetryCount < MAX_LOGIN_RETRIES) {\n this.log.debug(\n `refreshStock: Unexpected response, retrying: ${loginRetryCount}/${MAX_LOGIN_RETRIES}.`,\n response.status,\n responseData,\n );\n return this.refreshStok(loginRetryCount + 1);\n }\n\n this.log.debug(\"refreshStock: Unexpected end of flow, raising exception\");\n this.log.error(\"Invalid authentication data\");\n }\n\n async isSecureConnection() {\n if (this.isSecureConnectionValue === null) {\n this.log.debug(\"isSecureConnection: Checking secure connection...\");\n\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 responseData = (await response.json()) as TAPOCameraLoginResponse;\n\n this.log.debug(\"isSecureConnection response\", response.status, JSON.stringify(responseData));\n\n this.isSecureConnectionValue =\n responseData?.error_code == -40413 && String(responseData.result?.data?.encrypt_type || \"\")?.includes(\"3\");\n }\n\n return this.isSecureConnectionValue;\n }\n\n getStok(loginRetryCount = 0): Promise<string> {\n return new Promise((resolve) => {\n if (this.stok) {\n return resolve(this.stok);\n }\n\n if (!this.stokPromise) {\n this.stokPromise = () => this.refreshStok(loginRetryCount);\n }\n\n this.stokPromise()\n .then(() => {\n if (!this.stok) {\n this.log.info(\"STOK not found\");\n }\n resolve(this.stok!);\n })\n .finally(() => {\n this.stokPromise = undefined;\n });\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 this.log.error(\"Invalid padding\");\n }\n for (let i = text.length - paddingLength; i < text.length; i++) {\n if (text.charCodeAt(i) !== paddingLength) {\n this.log.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 this.log.debug(\"API request already pending\", reqJson);\n return this.pendingAPIRequests.get(reqJson) as Promise<TAPOCameraResponse>;\n } else {\n this.log.debug(\"New API request\", reqJson);\n }\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 const responseDataTmp = await response.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.log.debug(\"Stok expired, reauthenticating on next request, setting STOK to undefined\");\n this.stok = undefined;\n }\n\n let responseData: TAPOCameraResponse | null = null;\n\n if (isSecureConnection) {\n const encryptedResponse = responseDataTmp as TAPOCameraEncryptedResponse;\n if (encryptedResponse?.result?.response) {\n const decryptedResponse = this.decryptResponse(encryptedResponse.result.response);\n responseData = JSON.parse(decryptedResponse) as TAPOCameraResponse;\n }\n } else {\n responseData = responseDataTmp as TAPOCameraResponse;\n }\n\n this.log.debug(\"API response\", response.status, JSON.stringify(responseData));\n\n // Log error codes\n if (responseData && responseData.error_code !== 0) {\n const errorCode = String(responseData.error_code);\n const errorMessage =\n errorCode in ERROR_CODES_MAP ? ERROR_CODES_MAP[errorCode as keyof typeof ERROR_CODES_MAP] : \"Unknown error\";\n this.log.debug(`API request failed with specific error code ${errorCode}: ${errorMessage}`);\n }\n\n if (!responseData || responseData.error_code === -40401 || responseData.error_code === -1) {\n this.log.debug(\"API request failed\", responseData);\n this.stok = undefined;\n return {} as TAPOCameraResponse;\n // return this.apiRequest(req, loginRetryCount + 1);\n }\n\n // Success\n return responseData;\n } finally {\n this.pendingAPIRequests.delete(reqJson);\n }\n })(),\n );\n\n return this.pendingAPIRequests.get(reqJson) as Promise<TAPOCameraResponse>;\n }\n\n static SERVICE_MAP: Record<keyof Status, (value: boolean) => TAPOCameraSetRequest> = {\n eyes: (value) => ({\n method: \"setLensMaskConfig\",\n params: {\n lens_mask: {\n lens_mask_info: {\n // Watch out for the inversion\n enabled: value ? \"off\" : \"on\",\n },\n },\n },\n }),\n alarm: (value) => ({\n method: \"setAlertConfig\",\n params: {\n msg_alarm: {\n chn1_msg_alarm_info: {\n enabled: value ? \"on\" : \"off\",\n },\n },\n },\n }),\n notifications: (value) => ({\n method: \"setMsgPushConfig\",\n params: {\n msg_push: {\n chn1_msg_push_info: {\n notification_enabled: value ? \"on\" : \"off\",\n rich_notification_enabled: value ? \"on\" : \"off\",\n },\n },\n },\n }),\n motionDetection: (value) => ({\n method: \"setDetectionConfig\",\n params: {\n motion_detection: {\n motion_det: {\n enabled: value ? \"on\" : \"off\",\n },\n },\n },\n }),\n led: (value) => ({\n method: \"setLedStatus\",\n params: {\n led: {\n config: {\n enabled: value ? \"on\" : \"off\",\n },\n },\n },\n }),\n };\n\n async setStatus(service: keyof Status, value: boolean) {\n const responseData = await this.apiRequest({\n method: \"multipleRequest\",\n params: {\n requests: [TAPOCamera.SERVICE_MAP[service](value)],\n },\n });\n\n if (responseData.error_code !== 0) {\n this.log.error(`Failed to perform ${service} action`);\n }\n\n const method = TAPOCamera.SERVICE_MAP[service](value).method;\n const operation = responseData.result.responses.find((e) => e.method === method);\n if (operation?.error_code !== 0) {\n this.log.error(`Failed to perform ${service} action`);\n }\n\n return operation.result;\n }\n\n async getBasicInfo(): Promise<TAPOBasicInfo> {\n const responseData = 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 = responseData.result.responses[0] as TAPOCameraResponseDeviceInfo;\n return info.result.device_info.basic_info;\n }\n\n async getStatus(): Promise<Status> {\n const responseData = 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: \"getMsgPushConfig\",\n params: {\n msg_push: {\n name: \"chn1_msg_push_info\",\n },\n },\n },\n {\n method: \"getDetectionConfig\",\n params: {\n motion_detection: {\n name: \"motion_det\",\n },\n },\n },\n {\n method: \"getLedStatus\",\n params: {\n led: {\n name: \"config\",\n },\n },\n },\n ],\n },\n });\n\n if (!responseData || !responseData.result || !responseData.result.responses) {\n this.log.info(\"No response data found\");\n return {\n alarm: undefined,\n eyes: undefined,\n notifications: undefined,\n motionDetection: undefined,\n led: undefined,\n };\n }\n const operations = responseData.result.responses;\n\n const alert = operations.find((r) => r.method === \"getAlertConfig\");\n const lensMask = operations.find((r) => r.method === \"getLensMaskConfig\");\n const notifications = operations.find((r) => r.method === \"getMsgPushConfig\");\n const motionDetection = operations.find((r) => r.method === \"getDetectionConfig\");\n const led = operations.find((r) => r.method === \"getLedStatus\");\n\n if (!alert) this.log.debug(\"No alert config found\");\n if (!lensMask) this.log.debug(\"No lens mask config found\");\n if (!notifications) this.log.debug(\"No notifications config found\");\n if (!motionDetection) this.log.debug(\"No motion detection config found\");\n if (!led) this.log.debug(\"No led config found\");\n\n return {\n alarm: alert ? alert.result.msg_alarm.chn1_msg_alarm_info.enabled === \"on\" : undefined,\n // Watch out for the inversion\n eyes: lensMask ? lensMask.result.lens_mask.lens_mask_info.enabled === \"off\" : undefined,\n notifications: notifications ? notifications.result.msg_push.chn1_msg_push_info.notification_enabled === \"on\" : undefined,\n motionDetection: motionDetection ? motionDetection.result.motion_detection.motion_det.enabled === \"on\" : undefined,\n led: led ? led.result.led.config.enabled === \"on\" : undefined,\n };\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"],
5
- "mappings": "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",
4
+ "sourcesContent": ["// import https, { Agent } from \"https\";\nimport crypto from \"crypto\";\nimport { OnvifCamera } from \"./onvifCamera\";\nimport type {\n TAPOBasicInfo,\n TAPOCameraEncryptedRequest,\n TAPOCameraEncryptedResponse,\n TAPOCameraLoginResponse,\n TAPOCameraRefreshStokResponse,\n TAPOCameraRequest,\n TAPOCameraResponse,\n TAPOCameraResponseDeviceInfo,\n TAPOCameraSetRequest,\n} from \"./types/tapo\";\n\nconst MAX_LOGIN_RETRIES = 3;\nconst AES_BLOCK_SIZE = 16;\n\nimport { Agent, setGlobalDispatcher } from \"undici\";\n\nconst ERROR_CODES_MAP = {\n \"-40401\": \"Invalid stok value\",\n \"-40210\": \"Function not supported\",\n \"-64303\": \"Action cannot be done while camera is in patrol mode.\",\n \"-64324\": \"Privacy mode is ON, not able to execute\",\n \"-64302\": \"Preset ID not found\",\n \"-64321\": \"Preset ID was deleted so no longer exists\",\n \"-40106\": \"Parameter to get/do does not exist\",\n \"-40105\": \"Method does not exist\",\n \"-40101\": \"Parameter to set does not exist\",\n \"-40209\": \"Invalid login credentials\",\n \"-64304\": \"Maximum Pan/Tilt range reached\",\n \"-71103\": \"User ID is not authorized\",\n};\n\nexport type Status = {\n eyes: boolean | undefined;\n alarm: boolean | undefined;\n notifications: boolean | undefined;\n motionDetection: boolean | undefined;\n led: boolean | undefined;\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 disableEyesToggleAccessory?: boolean;\n disableAlarmToggleAccessory?: boolean;\n disableNotificationsToggleAccessory?: boolean;\n disableMotionDetectionToggleAccessory?: boolean;\n disableLEDToggleAccessory?: boolean;\n\n disableMotionSensorAccessory?: boolean;\n lowQuality?: boolean;\n\n videoMaxWidth?: number;\n videoMaxHeight?: number;\n videoMaxFPS?: number;\n videoForceMax?: boolean;\n videoMaxBirate?: number;\n videoPacketSize?: number;\n videoCodec?: string;\n\n videoConfig?: VideoConfig;\n\n eyesToggleAccessoryName?: string;\n alarmToggleAccessoryName?: string;\n notificationsToggleAccessoryName?: string;\n motionDetectionToggleAccessoryName?: string;\n ledToggleAccessoryName?: string;\n};\nexport class TAPOCamera extends OnvifCamera {\n private readonly kStreamPort = 554;\n private readonly fetchAgent: Agent;\n\n private readonly hashedPassword: 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<void>) | 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 constructor(\n protected readonly log: any,\n protected readonly config: CameraConfig,\n ) {\n super(log, config);\n process.env.NODE_TLS_REJECT_UNAUTHORIZED = 0;\n this.fetchAgent = new Agent({\n connectTimeout: 5_000,\n connect: {\n // TAPO devices have self-signed certificates\n rejectUnauthorized: false,\n ciphers: \"AES256-SHA:AES128-GCM-SHA256\",\n },\n });\n setGlobalDispatcher(this.fetchAgent);\n\n this.cnonce = this.generateCnonce();\n\n this.hashedPassword = 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(): Record<string, string> {\n return {\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 }\n\n private getHashedPassword() {\n if (this.passwordEncryptionMethod === \"md5\") {\n return this.hashedPassword;\n } else if (this.passwordEncryptionMethod === \"sha256\") {\n return this.hashedSha256Password;\n } else {\n this.log.error(\"Unknown password encryption method\");\n }\n }\n\n private fetch(url: string, data: RequestInit) {\n return fetch(url, {\n headers: this.getHeaders(),\n // @ts-expect-error Dispatcher type not there\n dispatcher: this.fetchAgent,\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 this.passwordEncryptionMethod = null;\n\n const hashedNoncesWithSHA256 = crypto\n .createHash(\"sha256\")\n .update(this.cnonce + this.hashedSha256Password + nonce)\n .digest(\"hex\")\n .toUpperCase();\n if (deviceConfirm === hashedNoncesWithSHA256 + nonce + this.cnonce) {\n this.passwordEncryptionMethod = \"sha256\";\n return true;\n }\n\n const hashedNoncesWithMD5 = crypto\n .createHash(\"md5\")\n .update(this.cnonce + this.hashedPassword + nonce)\n .digest(\"hex\")\n .toUpperCase();\n if (deviceConfirm === hashedNoncesWithMD5 + nonce + this.cnonce) {\n this.passwordEncryptionMethod = \"md5\";\n return true;\n }\n\n this.log.debug('Invalid device confirm, expected \"sha256\" or \"md5\" to match, but none found', {\n hashedNoncesWithMD5,\n hashedNoncesWithSHA256,\n deviceConfirm,\n nonce,\n cnonce: this,\n });\n\n return this.passwordEncryptionMethod !== null;\n }\n\n async refreshStok(loginRetryCount = 0): Promise<void> {\n this.log.debug(\"refreshStok: Refreshing stok...\");\n\n const isSecureConnection = await this.isSecureConnection();\n\n let fetchParams = {};\n if (isSecureConnection) {\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 fetchParams = {\n method: \"post\",\n body: JSON.stringify({\n method: \"login\",\n params: {\n username: this.getUsername(),\n password: this.hashedPassword,\n hashed: true,\n },\n }),\n };\n }\n\n const responseLogin = await this.fetch(`https://${this.config.ipAddress}`, fetchParams).catch((e) => {\n this.log.debug(\"refreshStok: Error during login\", e);\n return null;\n });\n if (!responseLogin) {\n this.log.debug(\"refreshStok: empty response login, raising exception\");\n this.log.error(\"Empty response login\");\n return;\n }\n const responseLoginData = (await responseLogin.json()) as TAPOCameraRefreshStokResponse;\n\n let response, responseData;\n\n if (!responseLoginData) {\n this.log.debug(\"refreshStok: empty response login data, raising exception\", responseLogin.status);\n this.log.error(\"Empty response login data\");\n }\n\n this.log.debug(\"refreshStok: Login response\", responseLogin.status, responseLoginData);\n\n if (responseLogin.status === 401 && responseLoginData.result?.data?.code === -40411) {\n this.log.debug(\"refreshStok: invalid credentials, raising exception\", responseLogin.status);\n this.log.error(\"Invalid credentials\");\n }\n\n if (isSecureConnection) {\n const nonce = responseLoginData.result?.data?.nonce;\n const deviceConfirm = responseLoginData.result?.data?.device_confirm;\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 this.log.debug(\"refreshStok: sending start_seq request\");\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()) as TAPOCameraRefreshStokResponse;\n\n if (!responseData) {\n this.log.debug(\"refreshStock: empty response start_seq data, raising exception\", response.status);\n this.log.error(\"Empty response start_seq data\");\n return;\n }\n\n this.log.debug(\"refreshStok: start_seq response\", response.status, JSON.stringify(responseData));\n\n if (responseData.result?.start_seq) {\n if (responseData.result?.user_group !== \"root\") {\n this.log.debug(\"refreshStock: Incorrect user_group detected\");\n\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 this.log.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 } else {\n if (responseLoginData.error_code === -40413 && loginRetryCount < MAX_LOGIN_RETRIES) {\n this.log.debug(\n `refreshStock: Invalid device confirm, retrying: ${loginRetryCount}/${MAX_LOGIN_RETRIES}.`,\n responseLogin.status,\n responseLoginData,\n );\n return this.refreshStok(loginRetryCount + 1);\n }\n\n this.log.debug(\n \"refreshStock: Invalid device confirm and loginRetryCount exhausted, raising exception\",\n loginRetryCount,\n responseLoginData,\n );\n this.log.error(\"Invalid device confirm. Firmware Fix by TP-Link expected in Dezember 2024. Only motion detection is supported.\");\n this.log.error(\"Or follow https://github.com/JurajNyiri/HomeAssistant-Tapo-Control/blob/main/add_camera_with_new_firmware.md\");\n return;\n }\n } else {\n this.passwordEncryptionMethod = \"md5\";\n response = responseLogin;\n responseData = responseLoginData;\n }\n\n if (responseData.result?.data?.sec_left && responseData.result.data.sec_left > 0) {\n this.log.debug(\"refreshStok: temporary suspension\", responseData);\n\n this.log.error(`Temporary Suspension: Try again in ${responseData.result.data.sec_left} seconds`);\n }\n\n if (responseData?.data?.code === -40404 && responseData?.data?.sec_left && responseData.data.sec_left > 0) {\n this.log.debug(\"refreshStok: temporary suspension\", responseData);\n\n this.log.error(`refreshStok: 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(\"refreshStok: Success in obtaining STOK\", this.stok);\n return;\n }\n\n if (responseData?.error_code === -40413 && loginRetryCount < MAX_LOGIN_RETRIES) {\n this.log.debug(\n `refreshStock: Unexpected response, retrying: ${loginRetryCount}/${MAX_LOGIN_RETRIES}.`,\n response.status,\n responseData,\n );\n return this.refreshStok(loginRetryCount + 1);\n }\n\n this.log.debug(\"refreshStock: Unexpected end of flow, raising exception\");\n this.log.error(\"Invalid authentication data\");\n }\n\n async isSecureConnection() {\n if (this.isSecureConnectionValue === null) {\n this.log.debug(\"isSecureConnection: Checking secure connection...\");\n\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 responseData = (await response.json()) as TAPOCameraLoginResponse;\n\n this.log.debug(\"isSecureConnection response\", response.status, JSON.stringify(responseData));\n\n this.isSecureConnectionValue =\n responseData?.error_code == -40413 && String(responseData.result?.data?.encrypt_type || \"\")?.includes(\"3\");\n }\n\n return this.isSecureConnectionValue;\n }\n\n getStok(loginRetryCount = 0): Promise<string> {\n return new Promise((resolve) => {\n if (this.stok) {\n return resolve(this.stok);\n }\n\n if (!this.stokPromise) {\n this.stokPromise = () => this.refreshStok(loginRetryCount);\n }\n\n this.stokPromise()\n .then(() => {\n if (!this.stok) {\n this.log.error(\"STOK not found\");\n }\n resolve(this.stok!);\n })\n .finally(() => {\n this.stokPromise = undefined;\n });\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 this.log.error(\"Invalid padding\");\n }\n for (let i = text.length - paddingLength; i < text.length; i++) {\n if (text.charCodeAt(i) !== paddingLength) {\n this.log.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 this.log.debug(\"API request already pending\", reqJson);\n return this.pendingAPIRequests.get(reqJson) as Promise<TAPOCameraResponse>;\n } else {\n this.log.debug(\"New API request\", reqJson);\n }\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).catch((e) => {\n this.log.debug(\"Error during camera fetch\", e);\n return;\n });\n if (!response) {\n this.log.debug(\"API request failed, empty response\");\n return {} as TAPOCameraResponse;\n }\n const responseDataTmp = await response.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.log.debug(\"Stok expired, reauthenticating on next request, setting STOK to undefined\");\n this.stok = undefined;\n }\n\n let responseData: TAPOCameraResponse | null = null;\n\n if (isSecureConnection) {\n const encryptedResponse = responseDataTmp as TAPOCameraEncryptedResponse;\n if (encryptedResponse?.result?.response) {\n const decryptedResponse = this.decryptResponse(encryptedResponse.result.response);\n responseData = JSON.parse(decryptedResponse) as TAPOCameraResponse;\n }\n } else {\n responseData = responseDataTmp as TAPOCameraResponse;\n }\n\n this.log.debug(\"API response\", response.status, JSON.stringify(responseData));\n\n // Log error codes\n if (responseData && responseData.error_code !== 0) {\n const errorCode = String(responseData.error_code);\n const errorMessage =\n errorCode in ERROR_CODES_MAP ? ERROR_CODES_MAP[errorCode as keyof typeof ERROR_CODES_MAP] : \"Unknown error\";\n this.log.debug(`API request failed with specific error code ${errorCode}: ${errorMessage}`);\n }\n\n if (!responseData || responseData.error_code === -40401 || responseData.error_code === -1) {\n this.log.debug(\"API request failed\", responseData);\n this.stok = undefined;\n return {} as TAPOCameraResponse;\n // return this.apiRequest(req, loginRetryCount + 1);\n }\n\n // Success\n return responseData;\n } finally {\n this.pendingAPIRequests.delete(reqJson);\n }\n })(),\n );\n\n return this.pendingAPIRequests.get(reqJson) as Promise<TAPOCameraResponse>;\n }\n\n static SERVICE_MAP: Record<keyof Status, (value: boolean) => TAPOCameraSetRequest> = {\n eyes: (value) => ({\n method: \"setLensMaskConfig\",\n params: {\n lens_mask: {\n lens_mask_info: {\n // Watch out for the inversion\n enabled: value ? \"off\" : \"on\",\n },\n },\n },\n }),\n alarm: (value) => ({\n method: \"setAlertConfig\",\n params: {\n msg_alarm: {\n chn1_msg_alarm_info: {\n enabled: value ? \"on\" : \"off\",\n },\n },\n },\n }),\n notifications: (value) => ({\n method: \"setMsgPushConfig\",\n params: {\n msg_push: {\n chn1_msg_push_info: {\n notification_enabled: value ? \"on\" : \"off\",\n rich_notification_enabled: value ? \"on\" : \"off\",\n },\n },\n },\n }),\n motionDetection: (value) => ({\n method: \"setDetectionConfig\",\n params: {\n motion_detection: {\n motion_det: {\n enabled: value ? \"on\" : \"off\",\n },\n },\n },\n }),\n led: (value) => ({\n method: \"setLedStatus\",\n params: {\n led: {\n config: {\n enabled: value ? \"on\" : \"off\",\n },\n },\n },\n }),\n };\n\n async setStatus(service: keyof Status, value: boolean) {\n const responseData = await this.apiRequest({\n method: \"multipleRequest\",\n params: {\n requests: [TAPOCamera.SERVICE_MAP[service](value)],\n },\n });\n\n if (responseData.error_code !== 0) {\n this.log.error(`Failed to perform ${service} action`);\n }\n\n const method = TAPOCamera.SERVICE_MAP[service](value).method;\n const operation = responseData.result.responses.find((e) => e.method === method);\n if (operation?.error_code !== 0) {\n this.log.error(`Failed to perform ${service} action`);\n }\n\n return operation.result;\n }\n\n async getBasicInfo(): Promise<TAPOBasicInfo> {\n const responseData = 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 = responseData.result.responses[0] as TAPOCameraResponseDeviceInfo;\n return info.result.device_info.basic_info;\n }\n\n async getStatus(): Promise<Status> {\n const responseData = 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: \"getMsgPushConfig\",\n params: {\n msg_push: {\n name: \"chn1_msg_push_info\",\n },\n },\n },\n {\n method: \"getDetectionConfig\",\n params: {\n motion_detection: {\n name: \"motion_det\",\n },\n },\n },\n {\n method: \"getLedStatus\",\n params: {\n led: {\n name: \"config\",\n },\n },\n },\n ],\n },\n });\n\n if (!responseData || !responseData.result || !responseData.result.responses) {\n this.log.error(\"No response data found\");\n return {\n alarm: undefined,\n eyes: undefined,\n notifications: undefined,\n motionDetection: undefined,\n led: undefined,\n };\n }\n const operations = responseData.result.responses;\n\n const alert = operations.find((r) => r.method === \"getAlertConfig\");\n const lensMask = operations.find((r) => r.method === \"getLensMaskConfig\");\n const notifications = operations.find((r) => r.method === \"getMsgPushConfig\");\n const motionDetection = operations.find((r) => r.method === \"getDetectionConfig\");\n const led = operations.find((r) => r.method === \"getLedStatus\");\n\n if (!alert) this.log.debug(\"No alert config found\");\n if (!lensMask) this.log.debug(\"No lens mask config found\");\n if (!notifications) this.log.debug(\"No notifications config found\");\n if (!motionDetection) this.log.debug(\"No motion detection config found\");\n if (!led) this.log.debug(\"No led config found\");\n\n return {\n alarm: alert ? alert.result.msg_alarm.chn1_msg_alarm_info.enabled === \"on\" : undefined,\n // Watch out for the inversion\n eyes: lensMask ? lensMask.result.lens_mask.lens_mask_info.enabled === \"off\" : undefined,\n notifications: notifications ? notifications.result.msg_push.chn1_msg_push_info.notification_enabled === \"on\" : undefined,\n motionDetection: motionDetection ? motionDetection.result.motion_detection.motion_det.enabled === \"on\" : undefined,\n led: led ? led.result.led.config.enabled === \"on\" : undefined,\n };\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"],
5
+ "mappings": "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",
6
6
  "names": ["crypto"]
7
7
  }
@@ -33,6 +33,7 @@ var import_newTpLinkCipher = __toESM(require("./newTpLinkCipher.js"));
33
33
  var import_axios2 = __toESM(require("axios"));
34
34
  var import_crypto = __toESM(require("crypto"));
35
35
  var import_utf8 = __toESM(require("utf8"));
36
+ var import_http = __toESM(require("http"));
36
37
  class P100 {
37
38
  constructor(log, ipAddress, email, password, timeout) {
38
39
  this.log = log;
@@ -142,7 +143,7 @@ class P100 {
142
143
  requestTimeMils: Math.round(Date.now() * 1e3)
143
144
  }
144
145
  };
145
- this.log.debug("Handshake P100 on host: " + this.ip);
146
+ this.log.debug("Old Handshake P100 on host: " + this.ip);
146
147
  const headers = {
147
148
  Connection: "Keep-Alive"
148
149
  };
@@ -151,7 +152,7 @@ class P100 {
151
152
  headers
152
153
  };
153
154
  await this._axios.post(URL, payload, config).then((res) => {
154
- this.log.debug("Received Handshake P100 on host response: " + this.ip);
155
+ this.log.debug("Received Old Handshake P100 on host response: " + this.ip);
155
156
  if (res.data.error_code || res.status !== 200) {
156
157
  return this.handleError(res.data.error_code ? res.data.error_code : res.status, "172");
157
158
  }
@@ -177,6 +178,9 @@ class P100 {
177
178
  Cookie: this.cookie,
178
179
  Connection: "Keep-Alive"
179
180
  };
181
+ this.log.debug("Old Login to P100 with url " + URL);
182
+ this.log.debug("Headers " + JSON.stringify(headers));
183
+ this.log.debug("Cipher: " + this.tpLinkCipher);
180
184
  if (this.tpLinkCipher) {
181
185
  const encryptedPayload = this.tpLinkCipher.encrypt(payload);
182
186
  const securePassthroughPayload = {
@@ -189,11 +193,13 @@ class P100 {
189
193
  headers,
190
194
  timeout: this._timeout * 1e3
191
195
  };
196
+ this.log.debug("Post request");
192
197
  await this._axios.post(URL, securePassthroughPayload, config).then((res) => {
193
198
  if (res.data.error_code || res.status !== 200) {
194
199
  return this.handleError(res.data.error_code ? res.data.error_code : res.status, "226");
195
200
  }
196
201
  const decryptedResponse = this.tpLinkCipher.decrypt(res.data.result.response);
202
+ this.log.debug("Decrypted Response: " + decryptedResponse);
197
203
  try {
198
204
  const response = JSON.parse(decryptedResponse);
199
205
  if (response.error_code !== 0) {
@@ -216,8 +222,7 @@ class P100 {
216
222
  Connection: "Keep-Alive",
217
223
  Host: this.ip,
218
224
  Accept: "*/*",
219
- "Content-Type": "application/octet-stream",
220
- "User-Agent": "ioBroker"
225
+ "Content-Type": "application/octet-stream"
221
226
  };
222
227
  if (this.cookie) {
223
228
  headers.Cookie = this.cookie;
@@ -228,6 +233,11 @@ class P100 {
228
233
  headers,
229
234
  params
230
235
  };
236
+ this.log.debug("Raw request to P100 with url " + URL);
237
+ this.log.debug("Data: " + data.toString("hex"));
238
+ this.log.debug("Headers: " + JSON.stringify(headers));
239
+ this.log.debug("Params: " + JSON.stringify(params));
240
+ this.log.debug("Cipher: " + this.tpLinkCipher);
231
241
  return this._axios.post(URL, data, config).then((res) => {
232
242
  this.log.debug("Received request on host response: " + this.ip);
233
243
  if (res.data.error_code || res.status !== 200) {
@@ -265,25 +275,90 @@ class P100 {
265
275
  this.tpLinkCipher = new import_tpLinkCipher.default(this.log, b_arr, b_arr2);
266
276
  }
267
277
  async handshake_new() {
268
- this.log.debug("Trying new habdshake");
278
+ this.log.debug("Trying new handshake");
269
279
  const local_seed = this._crypto.randomBytes(16);
270
- await this.raw_request("handshake1", local_seed, "arraybuffer").then((res) => {
271
- const remote_seed = res.subarray(0, 16);
272
- const server_hash = res.subarray(16);
273
- let auth_hash = void 0;
274
- const ah = this.calc_auth_hash(this.email, this.password);
275
- const local_seed_auth_hash = this._crypto.createHash("sha256").update(Buffer.concat([local_seed, remote_seed, ah])).digest();
276
- if (local_seed_auth_hash.toString("hex") === server_hash.toString("hex")) {
277
- this.log.debug("Handshake 1 successful");
278
- auth_hash = ah;
279
- }
280
- const req = this._crypto.createHash("sha256").update(Buffer.concat([remote_seed, local_seed, auth_hash])).digest();
281
- return this.raw_request("handshake2", req, "text").then((res2) => {
282
- this.log.debug("Handshake 2 successful: " + res2);
283
- this.newTpLinkCipher = new import_newTpLinkCipher.default(local_seed, remote_seed, auth_hash, this.log);
284
- this.log.debug("Init cipher successful");
285
- return;
280
+ const ah = this.calc_auth_hash(this.email, this.password);
281
+ const options = {
282
+ method: "POST",
283
+ hostname: this.ip,
284
+ path: "/app/handshake1",
285
+ headers: {
286
+ Connection: "Keep-Alive",
287
+ "Content-Type": "application/octet-stream",
288
+ "Content-Length": local_seed.length
289
+ },
290
+ agent: new import_http.default.Agent({
291
+ keepAlive: true
292
+ })
293
+ };
294
+ const response = await new Promise((resolve, reject) => {
295
+ const request = import_http.default.request(options, (res) => {
296
+ let chunks = [];
297
+ if (res.headers && res.headers["set-cookie"]) {
298
+ this.cookie = res.headers["set-cookie"][0].split(";")[0];
299
+ }
300
+ res.on("data", (chunk) => {
301
+ chunks.push(chunk);
302
+ });
303
+ res.on("end", (chunk) => {
304
+ var body = Buffer.concat(chunks);
305
+ this.log.debug(body.toString());
306
+ resolve(body);
307
+ });
308
+ res.on("error", (error) => {
309
+ this.log.error(error);
310
+ resolve(Buffer.from(""));
311
+ });
312
+ }).on("error", (error) => {
313
+ this.log.error(error);
314
+ resolve(Buffer.from(""));
286
315
  });
316
+ request.write(local_seed);
317
+ request.end();
318
+ });
319
+ if (!response || !response.subarray) {
320
+ this.log.debug("New Handshake 1 failed");
321
+ return;
322
+ }
323
+ this.log.debug("Handshake 1 response: " + response.toString("hex"));
324
+ const remote_seed = response.subarray(0, 16);
325
+ const server_hash = response.subarray(16);
326
+ this.log.debug("remote seed: " + remote_seed.toString("hex"));
327
+ this.log.debug("server hash: " + server_hash.toString("hex"));
328
+ this.log.debug("Extracted hashes");
329
+ let auth_hash = void 0;
330
+ this.log.debug("Calculated auth hash: " + ah.toString("hex"));
331
+ const calculateAuthHash = (email, password) => {
332
+ return this._crypto.createHash("sha256").update(Buffer.concat([local_seed, remote_seed, this.calc_auth_hash(email, password)])).digest();
333
+ };
334
+ const local_seed_auth_hash = calculateAuthHash(this.email, this.password);
335
+ this.log.debug("Calculated local seed auth hash: " + local_seed_auth_hash.toString("hex"));
336
+ this.log.debug("Server hash: " + server_hash.toString("hex"));
337
+ const validateAuthHash = (email, password) => {
338
+ const calculatedHash = calculateAuthHash(email, password);
339
+ this.log.debug(`Calculated auth hash for ${email}: ${calculatedHash.toString("hex")}`);
340
+ return calculatedHash.toString("hex") === server_hash.toString("hex");
341
+ };
342
+ if (validateAuthHash(this.email, this.password)) {
343
+ this.log.debug("New Handshake 1 successful");
344
+ auth_hash = ah;
345
+ } else if (validateAuthHash("", "")) {
346
+ this.log.debug("New Handshake 1 successful with empty auth hash");
347
+ auth_hash = this.calc_auth_hash("", "");
348
+ } else if (validateAuthHash("test@tp-link.net", "test")) {
349
+ this.log.debug("New Handshake 1 successful with test auth hash");
350
+ auth_hash = this.calc_auth_hash("test@tp-link.net", "test");
351
+ } else {
352
+ this.log.debug("New Handshake 1 failed");
353
+ this.log.debug("Local seed auth hash doesn't match server hash");
354
+ auth_hash = ah;
355
+ }
356
+ const req = this._crypto.createHash("sha256").update(Buffer.concat([remote_seed, local_seed, auth_hash])).digest();
357
+ return this.raw_request("handshake2", req, "text").then((res) => {
358
+ this.log.debug("New Handshake 2 successful: " + res);
359
+ this.newTpLinkCipher = new import_newTpLinkCipher.default(local_seed, remote_seed, auth_hash, this.log);
360
+ this.log.debug("New Init cipher successful");
361
+ return;
287
362
  });
288
363
  }
289
364
  async turnOff() {
@@ -566,11 +641,12 @@ class P100 {
566
641
  });
567
642
  }
568
643
  reAuthenticate() {
644
+ this.log.debug("Reauthenticating");
569
645
  if (this.is_klap) {
570
646
  this.handshake_new().then(() => {
571
647
  this.log.info("KLAP Authenticated successfully");
572
648
  }).catch(() => {
573
- this.log.error("KLAP Handshake failed");
649
+ this.log.error("KLAP Handshake New failed");
574
650
  this.is_klap = false;
575
651
  });
576
652
  } else {