Adding PTZ control to Tuya cameras (with localtuya)

I recently got one of those cheap Tuya indoor cameras with PTZ functionality and was disappointed, that it didn’t work in Home Assistant (with official Tuya integration). This guide will show how to quickly add PTZ control from HA with the use of localtuya.

Prerequisites

  1. Working localtuya integration (will be needed as official integration doesn’t expose PTZ). Setup instructions can be found here.
  2. Working official Tuya integration (as localtuya doesn’t support camera streams)

localtuya setup

  1. open Tuya cloud dashboard, click on DevicesAll devicesDebug device (make sure to select the camera)
  2. in the opened page in the center part (Control Device with Standard section) find ptz_control and ptz_stop and take note of their values (In my case ptz_control has a value of 3 and ptz_stop is ON (so True).
  3. Click Send instruction.
  4. In localtuya add new device and look for DPs with values of ptz_control and ptz_stop. DPs should be listed in the same order as they were in the Debug device menu under Standard Instruction Set section (right side of the page). In my case I would be looking for the value of True followed by 3.
  5. Once you find those add them. Make sure the DP for ptz_control has the entity type select (with selection reflecting what was visible in Tuya dashboard, in my case 0,1,2,3,4,5,6,7)

You should now have something similar to this:

Making it more friendly to use

Right now you can control your camera by selecting a value from PTZ control entity and toggling PTZ stop, when you reach the desired position. It’s nice, but not useful. Moving directions are as follows (at least for me):
0 or 1 - up
4 or 5 - down
6 or 7 - left
2 or 3 - right

Next we’ll add a script, which we will be able to call to move the camera in a more human friendly fashion. Just copy the code from below and paste it into new script. (Home Assistant really needs a way to share scripts, just like blueprints)
YAML:

alias: Tuya PTZ
icon: mdi:camera-control
mode: single
sequence:
  - choose:
      - conditions:
          - condition: template
            value_template: "{{ direction == \"up\" }}"
        sequence:
          - service: select.select_option
            data:
              option: "0"
            target:
              entity_id: "{{ ptz_control_entity }}"
          - delay:
              hours: 0
              minutes: 0
              seconds: 0
              milliseconds: "{{ duration | default(200) }}"
          - service: switch.toggle
            data: {}
            target:
              entity_id: "{{ ptz_stop_entity }}"
      - conditions:
          - condition: template
            value_template: "{{ direction == \"down\" }}"
        sequence:
          - service: select.select_option
            data:
              option: "4"
            target:
              entity_id: "{{ ptz_control_entity }}"
          - delay:
              hours: 0
              minutes: 0
              seconds: 0
              milliseconds: "{{ duration | default(200) }}"
          - service: switch.toggle
            data: {}
            target:
              entity_id: "{{ ptz_stop_entity }}"
      - conditions:
          - condition: template
            value_template: "{{ direction == \"left\" }}"
        sequence:
          - service: select.select_option
            data:
              option: "6"
            target:
              entity_id: "{{ ptz_control_entity }}"
          - delay:
              hours: 0
              minutes: 0
              seconds: 0
              milliseconds: "{{ duration | default(200) }}"
          - service: switch.toggle
            data: {}
            target:
              entity_id: "{{ ptz_stop_entity }}"
      - conditions:
          - condition: template
            value_template: "{{ direction == \"right\" }}"
        sequence:
          - service: select.select_option
            data:
              option: "2"
            target:
              entity_id: "{{ ptz_control_entity }}"
          - delay:
              hours: 0
              minutes: 0
              seconds: 0
              milliseconds: "{{ duration | default(200) }}"
          - service: switch.toggle
            data: {}
            target:
              entity_id: "{{ ptz_stop_entity }}"

Now verify, that it’s working by calling this service (don’t forget to change the entities, duration is specified in ms):

service: script.tuya_ptz
data:
  ptz_control_entity: select.ptz_control
  ptz_stop_entity: switch.ptz_stop
  direction: up
  duration: 200

And that’s all!
Now you should be able to control the camera from HA without tuya app.

As a bonus here’s a quick card with controls included:

type: picture-glance
camera_view: auto
camera_image: camera.kamera_tuya
entities:
  - entity: select.ptz_control
    icon: mdi:arrow-up
    tap_action:
      action: call-service
      service: script.tuya_ptz
      service_data:
        ptz_control_entity: select.ptz_control
        ptz_stop_entity: switch.ptz_stop
        direction: up
  - entity: select.ptz_control
    icon: mdi:arrow-down
    tap_action:
      action: call-service
      service: script.tuya_ptz
      service_data:
        ptz_control_entity: select.ptz_control
        ptz_stop_entity: switch.ptz_stop
        direction: down
  - entity: select.ptz_control
    icon: mdi:arrow-left
    tap_action:
      action: call-service
      service: script.tuya_ptz
      service_data:
        ptz_control_entity: select.ptz_control
        ptz_stop_entity: switch.ptz_stop
        direction: left
  - entity: select.ptz_control
    icon: mdi:arrow-right
    tap_action:
      action: call-service
      service: script.tuya_ptz
      service_data:
        ptz_control_entity: select.ptz_control
        ptz_stop_entity: switch.ptz_stop
        direction: right
3 Likes

First of all, thanks for the great tutorial.

However, it wasn’t working 100% in my case, since the localtuya integration failed to read available DPs from my camera.
I had to pass the “Manual DPs” to the device, but couldn’t figure out the proper values until I have found the tinytuya python library.
This simple python script:

import tinytuya

c = tinytuya.Cloud(
    apiRegion='eu', #cn, us, us-e, eu, eu-w, or in
    apiKey='API_KEY',
    apiSecret='API_SECRET'
)

for e in c.getdps('DEVICE_ID')['result']['status']:
    print(e)

will list all the available DPs and their states + possible values.
In my case the ptz_stop was 116 and ptz_control 119.
After I have passed “116,119” as the manual DPs input, everything went further smoothly.

1 Like

Nice.

on the localtuya’s github there’s an open PR for automatic labeling of DPs: https://github.com/rospogrigio/localtuya/pull/1256. I was somewhat able to get it working with this, but it also implements a bit broken support for subnodes (here), which made it impossible for me to configure the devices.

Hello, I read your tutorial and I am extremely frustrated, because I have 4 PTZ cameras that work normally in the smart life app and in the home assistant the same cameras do not display the video stream.

I would be very grateful if someone can help me to solve this problem.
I’m using the official tuya integration with HA.

Take a look at this: Fix tuya camera stream by IceOnly · Pull Request #84094 · home-assistant/core · GitHub but keep in mind that this thread is probably not the one to report this problem.

It works like a charm! Need to pass real DPid find by tinytuya, which can’t be found at tuya cloud

Hi,

thanks for this tutorial, but I cannot get my camera working with it.
I have the official Tuya integration installed and get a picture/video from it in HA.
Now I tried to get PTZ working as well and tried your tutorial. I stuck at the point adding a new device in Local Tuya. When I try, I only get a “…” under “Discovered Devices” listed.
How do I continue from that point?

Best wishes.

Yeah, this is where I’m at at the moment. I think it’s because my HA is on a different subnet. I can get the details of the device by using tinytuya but the camera doesn’t respond with anything, so the localtuya integration won’t set it up yet. I’ll keep playing around over the weekend though.

So I figured out that the list, where it shows the three dots only is not pulled from the cloud, it detects Tuya devices on the local subnet. You have to continue anyway and key in the required information on the next screen.

You have to know the IP-Address of your device (you mostly can look that up in your network configuration utility i.e. Fritzbox website), the device ID and local key (find this in your Tuya developers account).

In my case (a LSC PTZ Camera Dualband) I also had to enter the DPs manually, since Local Tuya could not find them:

For this specific camera it is 116 for PTZ control and 119 for PTZ stop.
Now I will see how to get this a bit more user friendly following the above tutorial further :slight_smile:

But one thing is to mention: the values for the PTZ control must not entered comma-separated, you have to use semicolon as divider.

1 Like

Hello! I had some problems with the PTZ camera integration. I successfully completed the Tuya Cloud Project setup (synced the camera, etc.). When I want to add the camera to the Local Tuya in HA, I can list the device id but there’s no ip address of the camera (that’s why I can’t add it even manually, although other devices are listed with their ip-address). Is it because of the characteristics of the camera? Can it be even used “locally”?

I’ll link the specific model:

Thank you in advance!