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:PTZ_PASSWORD@IP_OF_CAMERA/ISAPI/PTZCtrl/channels/1/capabilities

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

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

  # azimuth: 0..3300, elevation: 0..900, absoluteZoom: 10..40
  cam04_ptz_absolute:
    url: http://IP_OF_CAMERA/ISAPI/PTZCtrl/channels/1/Absolute
    method: PUT
    payload: '<PTZData>
              	<AbsoluteHigh>
              	    <azimuth>{{ azimuth }}</azimuth>
              	    <elevation>{{ elevation }}</elevation>
              	    <absoluteZoom>{{ absoluteZoom }}</absoluteZoom>
              	</AbsoluteHigh>
              </PTZData>'
    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:PTZ_PASSWORD@IP_OF_CAMERA/ISAPI/PTZCtrl/channels/1/status
script:
  cam04_pan_left:
    alias: 'Cam4 pan left'
    sequence:
      - service: rest_command.cam04_ptz_momentary
        data:
          pan: -50
          tilt: 0
          zoom: 0

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

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

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

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

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

  cam04_ptz_zero:
    alias: 'Cam4 PTZ zero position'
    sequence:
      - service: rest_command.cam04_ptz_absolute
        data:
          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.

homeassistant:
  customize:
    script.cam04_pan_left:
      icon: 'mdi:arrow-left-bold-circle-outline'
    script.cam04_pan_right:
      icon: 'mdi:arrow-right-bold-circle-outline'
    script.cam04_tilt_up:
      icon: 'mdi:arrow-up-bold-circle-outline'
    script.cam04_tilt_down:
      icon: 'mdi:arrow-down-bold-circle-outline'
    script.cam04_zoom_in:
      icon: 'mdi:magnify-plus'
    script.cam04_zoom_out:
      icon: 'mdi:magnify-minus'
    script.cam04_ptz_zero:
      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
        entities:
          - entity: script.cam04_pan_left
            tap_action:
              action: call-service
              service: script.cam04_pan_left
          - entity: script.cam04_tilt_up
            tap_action:
              action: call-service
              service: script.cam04_tilt_up
          - entity: script.cam04_tilt_down
            tap_action:
              action: call-service
              service: script.cam04_tilt_down
          - entity: script.cam04_pan_right
            tap_action:
              action: call-service
              service: script.cam04_pan_right
          - entity: script.cam04_zoom_in
            tap_action:
              action: call-service
              service: script.cam04_zoom_in
          - entity: script.cam04_zoom_out
            tap_action:
              action: call-service
              service: script.cam04_zoom_out
          - entity: script.cam04_ptz_zero
            tap_action:
              action: call-service
              service: script.cam04_ptz_zero
        tap_action:
          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.

7 Likes

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

Hello,
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).

camera:
  - 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:

Hi
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:yourpassword@yourip: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:yourpassword@yourip:andport/PTZCtrl/channels/1/presets/1/goto

Hope this helps.

/CT

@gurbyz How to use (GET) http://PTZ_USER_NAME:PTZ_PASSWORD@IP_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.

Hello,

Thank you so much for the sharing!
I have a question: How can I choose, for example, preset 1?

EDIT:
Solution:

cam_preset01:
    url: http://**IP_OF_CAMERA**/ISAPI/PTZCtrl/channels/1/presets/1/goto
    method: PUT
    username: **USERNAME**
    password: **PASSWORD**
    content_type: 'application/xml'
    verify_ssl: false`
cam_preset02:
    url: http://**IP_OF_CAMERA**/ISAPI/PTZCtrl/channels/1/presets/2/goto
    method: PUT
    username: **USERNAME**
    password: **PASSWORD**
    content_type: 'application/xml'
    verify_ssl: false`

Best regards
Gonçalo Almeida

Hello, does this solution with with a PTZ capable camera behind an NVR?

Do you have a web interfece on the cameras?

I actually worked this out. The NVR just acts as a proxy for all connected cameras, so you connect to the camera using the IP address of the NVR then the virtual port of the camera. Once I changed the web authentication to basic, setup an account (with permissions) directly on the camera I am now able to send curl commands to the camera!

1 Like

The only trouble I have now, is when I run one the positioning scripts, I get an error:

Failed to call service script/1707489398798. Cannot combine AUTH argument with credentials encoded in URL

1 Like

Ok, so I have everything setup verbatim as per @gurbyz guide. I am able to send curl commands to the camera which is exactly a DS-2DE2A404IW-DE3 the same as what is being used here for @gurbyz and others. When I use one of the scripts directly (lets say for example), cam04_ptz_zero I am getting the following error message in the logs:

Logger: homeassistant.components.rest_command
Source: components/rest_command/init.py:158
Integration: RESTful Command (documentation, issues)
First occurred: 13:40:42 (1 occurrences)
Last logged: 13:40:42

Error. Url: http://192.168.1.101:65004/iaspi/ptzctrl/channels/1/Absolute. Status code 405. Payload: b’ 1500 0 10 ’

The user I created does have remote ptz permissions and the 405 error suggests according to research that although the camera understands the function, it is not allowing it.

The only thing I can think of is that my camera is behind a Hikvision NVR but I’m not sure this matters since the NVR only proxies the camera and provides a port for access.

Any ideas anyone?

I followed the rest _commands and all proper formatting in configuration.yaml so I think the payload is reaching the camera but it is rejecting it.

1 Like

Hi, I have the Annke I81EM cam (which is basically HIK) and finally found out the correct format of url for PTZ presets:
curl.exe PUT

http://usr:[email protected]:80/ISAPI/PTZCtrl/channels/1/presets/1/goto