From b4d86c652f7beca99c47d57ae57190da3c3f723c Mon Sep 17 00:00:00 2001 From: michelle0927 Date: Tue, 29 Apr 2025 16:17:06 -0400 Subject: [PATCH 1/3] new components --- .../actions/list-devices/list-devices.mjs | 43 ++++++ .../tuya/actions/list-homes/list-homes.mjs | 29 ++++ .../send-instructions-to-device.mjs | 69 ++++++++++ components/tuya/package.json | 8 +- .../new-device-activated.mjs | 74 ++++++++++ components/tuya/tuya.app.mjs | 129 +++++++++++++++++- 6 files changed, 345 insertions(+), 7 deletions(-) create mode 100644 components/tuya/actions/list-devices/list-devices.mjs create mode 100644 components/tuya/actions/list-homes/list-homes.mjs create mode 100644 components/tuya/actions/send-instructions-to-device/send-instructions-to-device.mjs create mode 100644 components/tuya/sources/new-device-activated/new-device-activated.mjs diff --git a/components/tuya/actions/list-devices/list-devices.mjs b/components/tuya/actions/list-devices/list-devices.mjs new file mode 100644 index 0000000000000..308586dadfc24 --- /dev/null +++ b/components/tuya/actions/list-devices/list-devices.mjs @@ -0,0 +1,43 @@ +import tuya from "../../tuya.app.mjs"; + +export default { + key: "tuya-list-devices", + name: "List Devices", + description: "Get a list of devices associated with a home. [See the documentation](https://developer.tuya.com/en/docs/cloud/d7ee73aadb?id=Kawfjer0wkt2a)", + version: "0.0.1", + type: "action", + props: { + tuya, + userId: { + propDefinition: [ + tuya, + "userId", + ], + }, + homeId: { + propDefinition: [ + tuya, + "homeId", + (c) => ({ + userId: c.userId, + }), + ], + optional: true, + }, + }, + async run({ $ }) { + const response = this.homeId + ? await this.tuya.listHomeDevices({ + homeId: this.homeId, + }) + : await this.tuya.listUserDevices({ + userId: this.userId, + }); + if (response?.result?.length) { + $.export("$summary", `Found ${response.result.length} device${response.result.length === 1 + ? "" + : "s"}`); + } + return response; + }, +}; diff --git a/components/tuya/actions/list-homes/list-homes.mjs b/components/tuya/actions/list-homes/list-homes.mjs new file mode 100644 index 0000000000000..8d6c4eec44bad --- /dev/null +++ b/components/tuya/actions/list-homes/list-homes.mjs @@ -0,0 +1,29 @@ +import tuya from "../../tuya.app.mjs"; + +export default { + key: "tuya-list-homes", + name: "List Homes", + description: "Based on the user ID, query the list of homes where the specified user belongs. [See the documentation](https://developer.tuya.com/en/docs/cloud/f5dd40ed14?id=Kawfjh9hpov1n)", + version: "0.0.1", + type: "action", + props: { + tuya, + userId: { + propDefinition: [ + tuya, + "userId", + ], + }, + }, + async run({ $ }) { + const response = await this.tuya.listHomes({ + userId: this.userId, + }); + if (response?.result?.length) { + $.export("$summary", `Found ${response.result.length} home${response.result.length === 1 + ? "" + : "s"}`); + } + return response; + }, +}; diff --git a/components/tuya/actions/send-instructions-to-device/send-instructions-to-device.mjs b/components/tuya/actions/send-instructions-to-device/send-instructions-to-device.mjs new file mode 100644 index 0000000000000..0f34200843212 --- /dev/null +++ b/components/tuya/actions/send-instructions-to-device/send-instructions-to-device.mjs @@ -0,0 +1,69 @@ +import tuya from "../../tuya.app.mjs"; + +export default { + key: "tuya-send-instructions-to-device", + name: "Send Instructions to Device", + description: "Send an instruction to the specified device. [See the documentation](https://developer.tuya.com/en/docs/cloud/device-control?id=K95zu01ksols7#title-35-Send%20instructions%20to%20the%20device)", + version: "0.0.1", + type: "action", + props: { + tuya, + userId: { + propDefinition: [ + tuya, + "userId", + ], + }, + homeId: { + propDefinition: [ + tuya, + "homeId", + (c) => ({ + userId: c.userId, + }), + ], + optional: true, + }, + deviceId: { + propDefinition: [ + tuya, + "deviceId", + (c) => ({ + userId: c.userId, + homeId: c.homeId, + }), + ], + }, + instructionCode: { + propDefinition: [ + tuya, + "instructionCode", + (c) => ({ + deviceId: c.deviceId, + }), + ], + }, + value: { + type: "string", + label: "Value", + description: "The value to set", + }, + }, + async run({ $ }) { + const response = await this.tuya.sendInstructionsToDevice({ + deviceId: this.deviceId, + data: { + commands: [ + { + code: this.instructionCode, + value: this.value, + }, + ], + }, + }); + if (response.success) { + $.export("$summary", "Successfully sent instructions to device"); + } + return response; + }, +}; diff --git a/components/tuya/package.json b/components/tuya/package.json index 6de3afdd66cc7..8674e909b2b55 100644 --- a/components/tuya/package.json +++ b/components/tuya/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/tuya", - "version": "0.0.1", + "version": "0.1.0", "description": "Pipedream Tuya Components", "main": "tuya.app.mjs", "keywords": [ @@ -11,5 +11,9 @@ "author": "Pipedream (https://pipedream.com/)", "publishConfig": { "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^3.0.3", + "@tuya/tuya-connector-nodejs": "^2.1.2" } -} \ No newline at end of file +} diff --git a/components/tuya/sources/new-device-activated/new-device-activated.mjs b/components/tuya/sources/new-device-activated/new-device-activated.mjs new file mode 100644 index 0000000000000..01a81ad36c06a --- /dev/null +++ b/components/tuya/sources/new-device-activated/new-device-activated.mjs @@ -0,0 +1,74 @@ +import tuya from "../../tuya.app.mjs"; +import { DEFAULT_POLLING_SOURCE_TIMER_INTERVAL } from "@pipedream/platform"; + +export default { + key: "tuya-new-device-activated", + name: "New Device Activated", + description: "Emit new event when a device is activated. [See the documentation](https://developer.tuya.com/en/docs/cloud/device-management?id=K9g6rfntdz78a#title-10-Get%20a%20list%20of%20devices%20under%20a%20specified%20user)", + version: "0.0.1", + type: "source", + dedupe: "unique", + props: { + tuya, + db: "$.service.db", + timer: { + type: "$.interface.timer", + default: { + intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, + }, + }, + userId: { + propDefinition: [ + tuya, + "userId", + ], + }, + homeId: { + propDefinition: [ + tuya, + "homeId", + (c) => ({ + userId: c.userId, + }), + ], + optional: true, + }, + }, + methods: { + _getLastTs() { + return this.db.get("lastTs") || 0; + }, + _setLastTs(lastTs) { + this.db.set("lastTs", lastTs); + }, + generateMeta(item) { + return { + id: `${item.id}${item.active_time}`, + summary: `Device Activated with ID: ${item.id}`, + ts: item.active_time, + }; + }, + }, + async run() { + const lastTs = this._getLastTs(); + let maxTs = lastTs; + + const { result: devices } = this.homeId + ? await this.tuya.listHomeDevices({ + homeId: this.homeId, + }) + : await this.tuya.listUserDevices({ + userId: this.userId, + }); + + for (const device of devices) { + if (device.active_time >= lastTs) { + const meta = this.generateMeta(device); + this.$emit(device, meta); + maxTs = Math.max(device.active_time, maxTs); + } + } + + this._setLastTs(maxTs); + }, +}; diff --git a/components/tuya/tuya.app.mjs b/components/tuya/tuya.app.mjs index 7cf9ecd689e92..02798469d3432 100644 --- a/components/tuya/tuya.app.mjs +++ b/components/tuya/tuya.app.mjs @@ -1,11 +1,130 @@ +import { TuyaContext } from "@tuya/tuya-connector-nodejs"; +import { ConfigurationError } from "@pipedream/platform"; + export default { type: "app", app: "tuya", - propDefinitions: {}, + propDefinitions: { + userId: { + type: "string", + label: "User ID", + description: "The identifier of a user", + async options() { + const { result: { list } } = await this.listUsers(); + return list?.map(({ + user_id: value, user_name: label, + }) => ({ + label, + value, + })) || []; + }, + }, + homeId: { + type: "string", + label: "Home ID", + description: "The identifier of a home", + async options({ userId }) { + const { result } = await this.listHomes({ + userId, + }); + return result?.map(({ + home_id: value, name: label, + }) => ({ + label, + value, + })) || []; + }, + }, + deviceId: { + type: "string", + label: "Device ID", + description: "The identifier of a device", + async options({ + userId, homeId, + }) { + const { result } = homeId + ? await this.listHomeDevices({ + homeId, + }) + : await this.listUserDevices({ + userId, + }); + return result?.map(({ + id: value, name: label, + }) => ({ + label, + value, + })) || []; + }, + }, + instructionCode: { + type: "string", + label: "Instruction Code", + description: "The code of the command to use", + async options({ deviceId }) { + const { result } = await this.listDeviceFunctions({ + deviceId, + }); + return result?.functions?.map(({ + code: value, name: label, + }) => ({ + label, + value, + })) || []; + }, + }, + }, methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); + _getClient() { + return new TuyaContext({ + baseUrl: this.$auth.base_url, + accessKey: this.$auth.client_id, + secretKey: this.$auth.client_secret, + }); + }, + async _makeRequest({ + method = "GET", + path, + data = {}, + }) { + const response = await this._getClient().request({ + method, + path, + body: data, + }); + if (!response.success) { + console.log(response); + throw new ConfigurationError(`${response.msg}`); + } + }, + listUsers() { + return this._getClient().user.users({}); + }, + listHomes({ userId }) { + return this._makeRequest({ + path: `/v1.0/users/${userId}/homes`, + }); + }, + listUserDevices({ userId }) { + return this._makeRequest({ + path: `/v1.0/users/${userId}/devices`, + }); + }, + listHomeDevices({ homeId }) { + return this._makeRequest({ + path: `/v1.0/homes/${homeId}/devices`, + }); + }, + listDeviceFunctions({ deviceId }) { + return this._makeRequest({ + path: `/v1.0/devices/${deviceId}/functions`, + }); + }, + sendInstructionsToDevice({ deviceId }) { + return this._makeRequest({ + method: "POST", + path: `/v1.0/devices/${deviceId}/commands`, + }); }, }, -}; \ No newline at end of file +}; From 912d22aba83a03e9afc49df7d9f2fb7a52d507b2 Mon Sep 17 00:00:00 2001 From: michelle0927 Date: Tue, 29 Apr 2025 16:18:58 -0400 Subject: [PATCH 2/3] pnpm-lock.yaml --- pnpm-lock.yaml | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 795127a8cf399..5b11895ca85bc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -13491,7 +13491,14 @@ importers: components/tutor_lms: {} - components/tuya: {} + components/tuya: + dependencies: + '@pipedream/platform': + specifier: ^3.0.3 + version: 3.0.3 + '@tuya/tuya-connector-nodejs': + specifier: ^2.1.2 + version: 2.1.2 components/twelve_data: {} @@ -19800,6 +19807,9 @@ packages: '@tsconfig/node14@1.0.3': resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} + '@tuya/tuya-connector-nodejs@2.1.2': + resolution: {integrity: sha512-8tM7QlOF1QQrT3iQgcHp4JDNRUdOyi06h8F5ZL7antQZYP67TRQ2/puisoo2uhdXo+n+GT0B605pWaDqr9nPrA==} + '@types/acorn@4.0.6': resolution: {integrity: sha512-veQTnWP+1D/xbxVrPC3zHnCZRjSrKfhbMUlEA43iMZLu7EsnTtkJklIuwrCPbOi8YkvDQAiW05VQQFvvz9oieQ==} @@ -36373,6 +36383,13 @@ snapshots: '@tsconfig/node14@1.0.3': {} + '@tuya/tuya-connector-nodejs@2.1.2': + dependencies: + axios: 0.21.4 + qs: 6.13.1 + transitivePeerDependencies: + - debug + '@types/acorn@4.0.6': dependencies: '@types/estree': 1.0.6 From 3759c3b7c4005409469aa1281bcfcfc063d8d164 Mon Sep 17 00:00:00 2001 From: michelle0927 Date: Tue, 29 Apr 2025 16:37:02 -0400 Subject: [PATCH 3/3] update --- components/tuya/tuya.app.mjs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/components/tuya/tuya.app.mjs b/components/tuya/tuya.app.mjs index 02798469d3432..a487449bf59b7 100644 --- a/components/tuya/tuya.app.mjs +++ b/components/tuya/tuya.app.mjs @@ -120,10 +120,13 @@ export default { path: `/v1.0/devices/${deviceId}/functions`, }); }, - sendInstructionsToDevice({ deviceId }) { + sendInstructionsToDevice({ + deviceId, data, + }) { return this._makeRequest({ method: "POST", path: `/v1.0/devices/${deviceId}/commands`, + data, }); }, },