TuyAPI

repository·master·Indexed 25 days ago

https://github.com/codetheweb/tuyapi

A library for communicating with Tuya cloud network devices, allowing developers to control and monitor smart devices that use the TuyaSmart app or have port 6668 open. It supports both asynchronous event-based and synchronous async/await flows, and provides tools for handling protocol 3.3 and manual device refreshes. The package includes a CLI tool (@tuyapi/cli) to help retrieve device IDs and keys via the Tuya IoT platform or network sniffing.

Tokens
5.5K
Snippets
11
Records
21
Agent score
80%

What's inside tuyapi

  1. Extract TuyAPI credentials using macOS and Charles Proxy

    master

    To construct a TuyAPI instance, you need three specific parameters: id (uuid), uid (productId), and key (localKey). You can intercept the HTTPS traffic from your device's official app using Charles Proxy on macOS to find these values.

    Steps:

    1. Setup Charles Proxy: Download Charles and turn off the local proxy for your computer and disable recording temporarily.
    2. SSL Certificate: Install the Charles SSL certificate on your phone. On iOS, you may need to navigate to Settings > General > About > Certificate Trust Settings to fully trust it.
    3. Proxy Traffic: Configure your phone to proxy its traffic through your computer's IP address.
    4. Capture Data:
      • Open the device's official app and remove the existing device if it's already added.
      • Add the device in the app.
      • Crucial: Pause and turn recording back on in Charles right after entering your Wi-Fi password, then turn it off once the device is successfully added.
    5. Identify Request: Look for the HTTPS request where the parameter a=s.m.dev.list is present.
    6. Extract Parameters: From the response body, extract the following mapping:
      • id: uuid
      • uid: productId
      • key: localKey
    {
      id: uuid,
      uid: productId,
      key: localKey
    }
  2. Extract TuyAPI credentials from Android config files (Rooted)

    master

    If you have a rooted Android device, you can bypass network sniffing by reading the app's shared preferences directly.

    Smart Life App

    Locate the settings at: /data/data/com.tuya.smartlife/shared_prefs/dev_data_storage.xml

    Inside this file, look for the tuya_data string. This string is HTML entity encoded; you must decode it to reveal a JSON string containing your device keys.

    Jinvoo Smart App

    Locate the settings at: /data/data/com.xenon.jinvoo/shared_prefs/gw_storage.xml

    You can use a Python script to parse this XML and extract the device metadata, including localKey, uuid, and the device schema.

    #!/usr/bin/env python
    # -*- coding: us-ascii -*-
    # vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
    #
    
    import codecs
    import os
    import json
    import xml.etree.ElementTree as ET
    
    try:
        # Python 2.6-2.7 
        from HTMLParser import HTMLParser
    except ImportError:
        # Python 3
        from html.parser import HTMLParser  ## FIXME use html.unescape()
    
    
    xml_in_filename = 'com.xenon.jinvoo/shared_prefs/gw_storage.xml'
    h = open(xml_in_filename, 'r')
    xml = h.read()
    h.close()
    
    builder = ET.XMLTreeBuilder()
    builder.feed(xml)
    tree = builder.close()
    
    
    h = HTMLParser()
    for entry in tree.findall('string'):
        if entry.get('name') == 'gw_dev':
            # found it, need content
            config = entry.text
            s = h.unescape(config)
            config_dict = json.loads(s)
            #print(config_dict)
            print(len(config_dict))
            for device in config_dict:
                #print(device)
                for key in ['name', 'localKey', 'uuid', 'gwType', 'verSw', 'iconUrl']:  # and/or 'gwId', 'devId'
                    print('%s = %r' % (key, device[key]))
                # there is a bunch of interesting meta data about the device
                print('schema =\')
                schema = device['devices'][0]['schema']  # NOTE I've only seen single entries
                schema = h.unescape(schema)
                schema_dict = json.loads(schema)
                print(json.dumps(schema_dict, indent=4))
                print('')
            print(json.dumps(config_dict, indent=4))
  3. Use TuyAPI synchronously

    master

    You can use TuyAPI with async/await for a synchronous flow. This is useful for one-off commands or scripts where you want to wait for specific actions to complete before proceeding.

    const TuyAPI = require('tuyapi');
    
    const device = new TuyAPI({
      id: 'xxxxxxxxxxxxxxxxxxxx',
      key: 'xxxxxxxxxxxxxxxx',
      issueGetOnConnect: false});
    
    (async () => {
      await device.find();
    
      await device.connect();
    
      let status = await device.get();
    
      console.log(`Current status: ${status}.`);
    
      await device.set({set: !status});
    
      status = await device.get();
    
      console.log(`New status: ${status}.`);
    
      device.disconnect();
    })();
  4. List Tuya devices using the Tuya Smart or Smart Life apps

    master

    This is the fastest method. It requires your devices to already be registered in the official Tuya Smart or Smart Life apps.

    1. Follow the initial setup steps for the Smart Link method (creating a developer account and project on iot.tuya.com).
    2. In the Tuya IoT platform, go to Cloud -> Development, select your project, and click the Devices tab.
    3. Click the Link Tuya App account tab. Select your data center (e.g., Western America) from the dropdown.
    4. Click Add App Account and scan the provided QR code using the 'Me' tab in your Tuya Smart/Smart Life mobile app.
    5. Once linked, run the following command to retrieve your device names, IDs, and keys:
    tuya-cli wizard

    Save the outputted information for use with TuyAPI.

  5. Use TuyAPI asynchronously (event-based)

    master

    The recommended way to use TuyAPI is through an asynchronous, event-based approach. This allows you to listen for connection changes, errors, and incoming data updates from the device.

    Key events:

    • connected: Emitted when the connection to the device is established.
    • disconnected: Emitted when the connection is lost.
    • error: Emitted when an error occurs.
    • data: Emitted when the device sends data updates. The data object contains a dps property with device parameters (e.g., data.dps['1']).
    • dp-refresh: Emitted when a manual refresh is triggered on certain devices.
    const TuyAPI = require('tuyapi');
    
    const device = new TuyAPI({
      id: 'xxxxxxxxxxxxxxxxxxxx',
      key: 'xxxxxxxxxxxxxxxx'});
    
    let stateHasChanged = false;
    
    // Find device on network
    device.find().then(() => {
      // Connect to device
      device.connect();
    });
    
    // Add event listeners
    device.on('connected', () => {
      console.log('Connected to device!');
    });
    
    device.on('disconnected', () => {
      console.log('Disconnected from device.');
    });
    
    device.on('error', error => {
      console.log('Error!', error);
    });
    
    device.on('data', data => {
      console.log('Data from device:', data);
    
      console.log(`Boolean status of default property: ${data.dps['1']}.`);
    
      // Set default property to opposite
      if (!stateHasChanged) {
        device.set({set: !(data.dps['1'])});
    
        // Otherwise we'll be stuck in an endless
        // loop of toggling the state.
        stateHasChanged = true;
      }
    });
    
    // Disconnect after 10 seconds
    setTimeout(() => { device.disconnect(); }, 10000);
  6. Extract TuyAPI credentials on Android using Packet Capture

    master

    If you are using an Android device, you can capture the traffic between the app and the Tuya/Jinvoo servers without rooting the device.

    Steps:

    1. Prepare App: Remove any existing registration for the device in the official app.
    2. Install Packet Capture: Install the "Packet Capture" app from the Play Store. Follow its instructions to install the necessary certificate and start capturing. You can use the specific capture mode (indicated by a green triangle/play button with a "1") to target only the Jinvoo app.
    3. Re-add Device: Run the Jinvoo Smart App (version 1.0.3 is known to work) to add the device.
    4. Stop and Review: Stop the capture in the "Packet Capture" app and review the captured packets (look for a large packet, e.g., ~9Kb out of 16Kb) to find the device details.
  7. Link a Tuya device using the Smart Link method

    master

    Use this method if you want to link devices regardless of whether they are currently in a Tuya app. This requires a developer account at iot.tuya.com.

    1. Create a Developer Project

    • Sign up at iot.tuya.com (Select United States as your country to skip verification).
    • Go to Cloud -> Development and create a project.
    • Important: Select Smart Home for both Industry and Development Method.
    • Note your Access ID/Client ID and Access Secret/Client Secret.

    2. Authorize API Services

    • Go to Cloud -> Development -> [Your Project] -> Service API -> Go to authorize.
    • Subscribe to the following services (use the basic edition which is free):
      • IoT Core
      • Authorization
      • Smart Home Scene Linkage
    • Verify they are listed under Cloud -> Projects -> [Your Project] -> API.

    3. Create an App SDK

    • Go to App -> App SDK -> Development.
    • Click Create. Enter a package name (Android must start with com.) and a Channel ID.
    • The Channel ID is your schema value for the CLI command.
    • Go to Cloud -> Development -> [Your Project] -> Link Device.
    • Click Link devices by Apps -> Add Apps and select the app you just created.
    • Put your physical device into pairing mode (refer to your device's manual).
    • Ensure any Tuya apps on your phone are completely closed.
    • Run the following command:
    tuya-cli link --api-key <your api key> --api-secret <your api secret> --schema <your schema/Channel ID> --ssid <your WiFi name> --password <your WiFi password> --region <us|eu|cn>

    Note: For --region, use the two-letter code (us, eu, or cn) geographically closest to you.

    tuya-cli link --api-key <your api key> --api-secret <your api secret> --schema <your schema/Channel ID> --ssid <your WiFi name> --password <your WiFi password> --region us
  8. Handle devices that do not send automatic updates

    master

    Some newer devices do not send data updates unless the official app is open. To receive updates from these devices, you must "force" them by calling refresh().

    When using this method, you should listen for the dp-refresh event. You can also use the issueRefreshOnConnect: true option in the constructor to attempt this on connection.

    const TuyAPI = require('tuyapi');
    
    const device = new TuyAPI({
        id: 'xxxxxxxxxxxxxxxxxxxx',
        key: 'xxxxxxxxxxxxxxxx',
        ip: 'xxx.xxx.xxx.xxx',
        version: '3.3',
        issueRefreshOnConnect: true});
    
    // Find device on network
    device.find().then(() => {
        device.connect();
    });
    
    // Add event listeners
    device.on('connected', () => {
        console.log('Connected to device!');
    });
    
    device.on('disconnected', () => {
        console.log('Disconnected from device.');
    });
    
    device.on('error', error => {
        console.log('Error!', error);
    });
    
    device.on('dp-refresh', data => {
        console.log('DP_REFRESH data from device: ', data);
    });
    
    device.on('data', data => {
        console.log('DATA from device: ', data);
    });
    
    // Disconnect after 10 seconds
    setTimeout(() => { device.disconnect(); }, 1000);
  9. Handle TuyaDevice events

    master

    The TuyaDevice class extends EventEmitter and emits several events during its lifecycle:

    • connected: Emitted when the socket connection is successfully established.
    • disconnected: Emitted when the socket is closed or the device goes offline.
    • data: Emitted when the device proactively sends data (e.g., a status update).
      • Arguments: (payload, commandByte, sequenceN)
    • dp-refresh: Emitted when the device proactively returns updated DP values.
      • Arguments: (payload, commandByte, sequenceN)
    • heartbeat: Emitted when a heartbeat pong is received from the device.
    • error: Emitted on socket errors, connection timeouts, or parsing errors.
    • error (string/Error): When nullPayloadOnJSONError is true, a string error might be emitted.
  10. Troubleshoot TuyAPI linking errors

    master

    Error: sign invalid

    One of your credentials (api-key, api-secret, or schema) is incorrect. Re-verify these values from the Tuya IoT platform.

    Device(s) failed to be registered! Error: Timed out waiting for devices to connect.

    The device failed to authenticate against the Tuya API. Try these steps:

    • Ensure your computer is connected via WiFi only (unplug Ethernet).
    • Ensure your network is 2.4 GHz (or that your 2.4 GHz and 5 GHz bands share the same SSID).
    • Try using a different Operating System.
    • Remove special characters from your WiFi SSID.