Skip to content

Two way talk for Chinese version of TP link cameras #1724

Closed

Description

@christaikobo

There are a bunch of cheap IP cameras (TP link IPCXXXX) by TP link that are sold in China:
https://www.tp-link.com.cn/list_2549.html

They use RTSP for video and audio streaming by default, and go2rtc picks up that without any issue.

But for two way talk, they have some proprietary protocols similar to that used in TAPO cameras, only difference is that it is wrapped inside RTSP instead of HTTP. Someone did a bit of packet capture and reverse engineering, and claimed that it was a RTSP MULTITRANS plugin. (https://iovxw.net/p/frigate/)

If anyone is interested in implementing it, I can provide a camera access for you, because I don't know how to do it.

Thank you!

Activity

  1. forrestsocool commented on Oct 11, 2025

    @forrestsocool
    Contributor

    Hi Alex, @AlexxIT these cameras support RTSP and the send_only voice socket can be opened by MULTITRANS auth like this: https://github.com/bingooo/hass-tplink-ipc/blob/main/custom_components/tplink_ipc/talkback.py。

        def _connect_and_auth(self):
            """Performs the full 3-step MULTITRANS handshake."""
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.settimeout(10)
            try:
                sock.connect((self._ip, 554))
                uri = f"rtsp://{self._ip}/multitrans"
    
                # Step 1: Get challenge
                req1 = (f"MULTITRANS {uri} RTSP/1.0\r\nCSeq: 0\r\n"
                        f"X-Client-UUID: {self._client_uuid}\r\n\r\n")
                sock.sendall(req1.encode())
                resp1 = sock.recv(2048).decode()
                if "401" not in resp1: raise ConnectionError("Failed to get auth challenge.")
                
                digest_line = [l for l in resp1.split('\r\n') if 'WWW-Authenticate: Digest' in l][0]
                realm = digest_line.split('realm="')[1].split('"')[0]
                nonce = digest_line.split('nonce="')[1].split('"')[0]
                
                # Step 2: Send auth
                response = self._calculate_digest(realm, nonce, "MULTITRANS", uri)
                auth_header = f'Digest username="{self._user}", realm="{realm}", nonce="{nonce}", uri="{uri}", response="{response}"'
                req2 = (f"MULTITRANS {uri} RTSP/1.0\r\nCSeq: 1\r\nAuthorization: {auth_header}\r\n"
                        f"X-Client-UUID: {self._client_uuid}\r\n\r\n")
                sock.sendall(req2.encode())
                resp2 = sock.recv(2048).decode()
                if "200 OK" not in resp2: raise ConnectionRefusedError("Authentication failed.")
                session_id = [l for l in resp2.split('\r\n') if 'Session:' in l][0].split(': ')[1].strip()
    
                # Step 3: Open talk channel
                payload = json.dumps({"type":"request","seq":0,"params":{"method":"get","talk":{"mode":"half_duplex"}}})
                req3 = (f"MULTITRANS {uri} RTSP/1.0\r\nCSeq: 2\r\nSession: {session_id}\r\n"
                        f"Content-Type: application/json\r\nContent-Length: {len(payload)}\r\n\r\n{payload}")
                sock.sendall(req3.encode())
                resp3 = sock.recv(2048).decode()
                if '"error_code":0' not in resp3: raise ConnectionError("Failed to open talkback channel.")
                
                _LOGGER.info("Successfully authenticated and talkback channel is open.")
                return sock
            except Exception as e:
                sock.close()
                _LOGGER.error(f"Handshake failed: {e}")
                return None

    by the way, these camera have a web ui lisening on port 80, which have two kind of auth method, encrypt_type "3" (MD5 method) like VIGI, and encrypt_type "2" (RSA method)。

    encrypt_type "3" MD5 method auth implement: https://github.com/bingooo/hass-tplink-ipc/blob/main/custom_components/tplink_ipc/api.py

    encrypt_type "2" (RSA method) auth implement: https://github.com/iapYang/tplink_ipc_implement/blob/main/custom_components/tplink_ipc_implement/core.py

    I have tested encrypt_type "2" loggin, and MULTITRANS method to open the socket, both works on my TP-LINK IPC485EP-AI, hope these infomation is useful!

  2. AlexxIT commented on Oct 11, 2025

    @AlexxIT
    Owner

    It's difficult to add support blindly without having such a camera.

  3. christaikobo commented on Oct 11, 2025

    @christaikobo
    Author
  4. AlexxIT commented on Oct 11, 2025

    @AlexxIT
    Owner

    It's still okay to add support for regular video remotely. But adding two-way audio support is almost impossible.
    There's no feedback. I don't understand whether the code is working correctly or not.

  5. christaikobo commented on Oct 11, 2025

    @christaikobo
    Author
  6. AlexxIT commented on Oct 12, 2025

    @AlexxIT
    Owner

    It's important to listen to what the camera is outputting. What's the latency? Are there any errors?
    I have an experience with adding remotely two way audio. It takes a lot of power, and the results are poor.
    It's better to spend this time and energy on more important tasks.

  7. christaikobo commented on Oct 12, 2025

    @christaikobo
    Author
  8. self-assigned this
    on Dec 18, 2025
  9. forrestsocool commented on Dec 22, 2025

    @forrestsocool
    Contributor

    Hi @christaikobo , This #1995 PR works on my TP-LINK IPC485EP-AI camera, you can have a test.

  10. added this to the master milestone on Jan 17, 2026
  11. AlexxIT commented on Jan 20, 2026

    @AlexxIT
    Owner
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions