Hikvision camera PTZ control workaround (without ONVIF)

I own a new Hikvision camera and couldn’t find a reported PTZ control solution for HA that worked. I created one using the rest_command integration and some ISAPI manual I found.

PTZ via ONVIF not working (situation March 2020)

There is the usual way to enable ONVIF in your camera settings, but that way wasn’t working for me in HA 0.106.x in the first place and - as I understood - is working for less people since HA 0.107.x. I’m sure there will be a solution for ONVIF sooner or later, but in the meantime I couldn’t control PTZ via HA. Luckily you can make it work using rest_commands.

Description of my situation

  • I have NO issue with streaming the video image in HA.
  • When performing a call to the (new) service onvif.ptz there is a “PTZ actions are not supported” in the logs of HA.
  • I’m running HA 0.107.4 on HassIO.
  • The camera is a Hikvision mini PTZ camera model DS-2DE2A404IW-DE3. (Please report if the solution also works for other Hikvision models.)

Requirements for the solution

  • You have to have knowledge about curl or be able to use Postman to tweak the solution in a way that it is working in your situation / for your camera model.
  • In your camera settings enable “digest/basic” verification for Web. In my cam settings the menu path is: Configuration > System > Security > Verification > Web verification. Otherwise authorization will fail.
  • In your camera settings create an extra user: Configuration > System > User management > User management > Add. Choose for the level of ‘operator’ and check if ‘Remote PTZ control’ is enabled for this new user. The username and password are referred to as PTZ_USER_NAME and PTZ_PASSWORD in the description below.
  • Have your IP address of the camera at hand. It’s referred to as IP_OF_CAMERA in the description below.

The solution

There are some rest_commands, scripts, some customized icons and of course a picture-glance card with the PTZ controls on it.

The rest_commands

I made two rest_commands. The first one is needed for the basic PTZ commands like pan left/right, tilt up/down and zoom in/out. The second command is to easily move back to a default ‘zero position’. See the star icon in the image.

In the comments # I wrote down all the minimal and maximal values. These values differ per camera model. I found mine by requesting it to the camera:

(GET) http://PTZ_USER_NAME:[email protected]_OF_CAMERA/ISAPI/PTZCtrl/channels/1/capabilities

The duration is in milliseconds. You can choose of course another value here.

  # pan: -100..100, tilt: -100..100, zoom: -100..100, duration: in msecs
    url: http://IP_OF_CAMERA/ISAPI/PTZCtrl/channels/1/Momentary
    method: PUT
    payload: '<PTZData>
              	<pan>{{ pan }}</pan>
              	<tilt>{{ tilt }}</tilt>
              	<zoom>{{ zoom }}</zoom>
    username: PTZ_USER_NAME
    password: PTZ_PASSWORD
    content_type: 'application/xml'
    verify_ssl: false

  # azimuth: 0..3300, elevation: 0..900, absoluteZoom: 10..40
    url: http://IP_OF_CAMERA/ISAPI/PTZCtrl/channels/1/Absolute
    method: PUT
    payload: '<PTZData>
              	    <azimuth>{{ azimuth }}</azimuth>
              	    <elevation>{{ elevation }}</elevation>
              	    <absoluteZoom>{{ absoluteZoom }}</absoluteZoom>
    username: PTZ_USER_NAME
    password: PTZ_PASSWORD
    content_type: 'application/xml'
    verify_ssl: false

The scripts

There is a script per PTZ action. The values used in the data element have to adhere the minimal and maximal values you found earlier.

The values for the last script (the zero positioning script) can be found by trial and error. Or find them by moving the PTZ to the desired position via the camera UI and use the response of this request:

(GET) http://PTZ_USER_NAME:[email protected]_OF_CAMERA/ISAPI/PTZCtrl/channels/1/status
    alias: 'Cam4 pan left'
      - service: rest_command.cam04_ptz_momentary
          pan: -50
          tilt: 0
          zoom: 0

    alias: 'Cam4 pan right'
      - service: rest_command.cam04_ptz_momentary
          pan: 50
          tilt: 0
          zoom: 0

    alias: 'Cam4 tilt up'
      - service: rest_command.cam04_ptz_momentary
          pan: 0
          tilt: 75
          zoom: 0

    alias: 'Cam4 tilt down'
      - service: rest_command.cam04_ptz_momentary
          pan: 0
          tilt: -75
          zoom: 0

    alias: 'Cam4 zoom in'
      - service: rest_command.cam04_ptz_momentary
          pan: 0
          tilt: 0
          zoom: 100

    alias: 'Cam4 zoom out'
      - service: rest_command.cam04_ptz_momentary
          pan: 0
          tilt: 0
          zoom: -100

    alias: 'Cam4 PTZ zero position'
      - service: rest_command.cam04_ptz_absolute
          azimuth: 1500
          elevation: 0
          absoluteZoom: 10

Icon customization

I gave each script an icon. That’s better for reuse.
But of course you can skip this an put an icon directly in the picture-glance card if you want.

      icon: 'mdi:arrow-left-bold-circle-outline'
      icon: 'mdi:arrow-right-bold-circle-outline'
      icon: 'mdi:arrow-up-bold-circle-outline'
      icon: 'mdi:arrow-down-bold-circle-outline'
      icon: 'mdi:magnify-plus'
      icon: 'mdi:magnify-minus'
      icon: 'mdi:star-circle-outline'

The card

Finally the picture-glance card to make it all visible in Lovelace.

      - type: picture-glance
        title: Cam4 Front garden
        camera_image: camera.cam04
          - entity: script.cam04_pan_left
              action: call-service
              service: script.cam04_pan_left
          - entity: script.cam04_tilt_up
              action: call-service
              service: script.cam04_tilt_up
          - entity: script.cam04_tilt_down
              action: call-service
              service: script.cam04_tilt_down
          - entity: script.cam04_pan_right
              action: call-service
              service: script.cam04_pan_right
          - entity: script.cam04_zoom_in
              action: call-service
              service: script.cam04_zoom_in
          - entity: script.cam04_zoom_out
              action: call-service
              service: script.cam04_zoom_out
          - entity: script.cam04_ptz_zero
              action: call-service
              service: script.cam04_ptz_zero
          action: more-info

Maybe my solution doesn’t work for you

I’ll write down briefly how I found the solution, because my solution above maybe is working for my camera model only.

There is a 700+ page manual at Hikvision that didn’t give me the solution right away, but did give enough hints on page 86 to search further. I found another manual dated 2013 on a Russian FTP server: IP Surveillance API PTZ Service Specification. In this second manual you’ll find the specification to send requests to your camera to control PTZ. You can test it e.g. using curl or an app like Postman.

Extending this solution

There are endless possibilities to extend. E.g. you can make use of the ‘presets’ of the camera instead of having to maintain the values for the ‘zero position’ in HA scripts. Or maybe you could make a template sensor to get the PTZ coordinates into HA and do something with it there.


I happen to have the exact same camera and this is working perfectly, thank you so much!

Okay. Great!

B.t.w. I added

camera_view: live

to the card configuration to have the image updated more frequently.

1 Like

have the same camera but I cannot setup Onvif at all. “Connect call failed”
Can you share your settings please?

Thank you.

Yep, same for me. That’s why I created this topic for a workaround without ONVIF. So, there are no ONVIF settings to share. Sorry.

In case you want to know how I connect to my camera in home assistant: I use a NAS solution (Surveillance Center from Synology).

  - platform: synology
    url: http://MY_IP
    username: MY_USERNAME
    password: MY_PASSWORD
    timeout: 30
    verify_ssl: False

Probably doesn’t answer your question, is it?

I see. I set it up as a generic camera.
Thanks anyway!

Update: with HA 0.110, ONVIF PTZ now works fine with my DS-2DE2A404IW-DE3 camera. :tada:

thank you for sharing,
first code (about speed) is working for me, but the second code (about zoom in a period of time) not working for me
how to solve this problem ?
thank you in advance

Hello, thanks for sharing.

I managed to create a couple of buttons on homebridge to control the patrol operation of the PTZ camera.

First step is to install the homebridge shell switch into your homebridge (https://github.com/cr3ative/homebridge-shell-switch#readme).

Next step is to create the shell files with the commands. Let’s do one for PTZ Patrol 1, call it /var/lib/homebridge/PTZ_Patrol_1.sh. You can use vi for this or simply run this command:

echo 'curl -X PUT -T /var/lib/homebridge/PTZ_Empty.xml http://youruser:[email protected]:andport/PTZCtrl/channels/1/patrols/1/start' >/var/lib/homebridge/PTZ_Patrol_1.sh

Also create a file called /var/lib/homebridge/PTZ_Empty.xml, the easiest way I suppose is to simply run this command in the terminal of the homebridge:
echo '<PTZData></PTZData>' >/var/lib/homebridge/PTZ_Empty.xml

Last step is to enter config into homebridge and add this command:

config into homebridge:
"accessories": [ { "accessory": "ShellSwitch", "name": "PTZ Patrol 1", "onCmd": "bash /var/lib/homebridge/PTZ_Patrol_1.sh", "offCmd": "bash /var/lib/homebridge/PTZ_Preset_1.sh" }

BTW, my PTZ_Preset_1.sh contains the command to route the camera back to preset 1:
curl -X PUT -T /var/lib/homebridge/PTZ_Empty.xml http://youruser:[email protected]:andport/PTZCtrl/channels/1/presets/1/goto

Hope this helps.


@gurbyz How to use (GET) http://PTZ_USER_NAME:[email protected]_OF_CAMERA/ISAPI/PTZCtrl/channels/1/status, please?

You have to replace PTZ_USER_NAME, PTZ_PASSWORD and IP_OF_CAMERA with their values and then you can use the complete url in a browser.