Generic Camera Device using local connection (new version now available)

You going to do a Team Viewer session and help me setup a VPN? Tried to get OpenVNP working with my new Orbi, and it was like rocket science.I gave up! LOL.

Thank you for all your hard work BTW.

@tinlau : How are you determining whether or not to use the LocalURL? Would be nifty to be able to specify an IP block (CIDR math, I know) to match to assume local. You can’t use the IP of the client device, since some of us don’t run the same vlan/network for video & desktop devices. (I ask because it doesn’t work for me… assumes remote while I’m at home)

In theory you could match on RFC1918 space (,, and try the InHome first if the client IP starts with those.

I took the simple approach (from the note text at the end of the message where I posted the code):

Edit the preferences to add the external DNS and Port for the camera (it will ty to use the IP address as before if you don’t edit the preferences).



Has anyone managed to get Patricks Code working with Sercomm ICamera2?



I have been trying to use this DTH with a Dericam camera. I can get pstuart’s original DTH to work ok.

In order to get this DTH to work at all I had to change lines 270, 271, and 289 to what was in pstuart version. Then I can get live video.

270 OutHomeURL : parent.state.CameraStreamPath,
271 InHomeURL : parent.state.CameraStreamPath,
289 [InHomeURL: parent.state.CameraStreamPath]

What I would like to get is the snapshot, but the take doesn’t seem to work.

Any thoughts?

Hi, so I’ve got an Hikvion camera and got the screenshot and video working with altering this code a little bit. Now I want to use the motion, but I don’t get it working (I have no experience in programming). I want to base it on the output in the following url: [IPADRESS]/IO/outputs/1/status because I got that working on Domoticz:


<IOPortStatus xmlns="" version="1.0">

Device Handler

 *  Generic IP Camera
 *  Based on pstuart's Generic Video Camera and Generic Camera Device
 *  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 *  in compliance with the License. You may obtain a copy of the License at:
 *  Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
 *  on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
 *  for the specific language governing permissions and limitations under the License.

metadata {
	definition (name: "TEST Camera", namespace: "KiefDelicious", author: "tinlau") {
		capability "Image Capture"
		capability "Sensor"
		capability "Actuator"
		capability "Configuration"
		capability "Video Camera"
		capability "Video Capture"
		capability "Refresh"
		capability "Switch"
		capability "Motion Sensor"
        command "start"
		attribute "hubactionMode", "string"

    preferences {
    input("CameraIP", "string", title:"Camera IP Address", description: "Please enter your camera's IP Address", required: false, displayDuringSetup: true)
    input("CameraUser", "string", title:"Camera User", description: "Please enter your camera's username", required: false, displayDuringSetup: true)
    input("CameraPassword", "string", title:"Camera Password", description: "Please enter your camera's password", required: false, displayDuringSetup: true)
	input("CameraRtspPort", "string", title:"Camera RTSP Port", description: "Please enter your camera's RTSP Port", defaultValue: 554 , required: false, displayDuringSetup: true)
	input("CameraPort", "string", title:"Camera Port", description: "Please enter your camera's Port", defaultValue: 80 , required: false, displayDuringSetup: true)
    input("CameraPath", "string", title:"Camera Path to Image", description: "Please enter the path to the image", defaultValue: "/Streaming/Channels/1/picture", required: false, displayDuringSetup: true)
    input("CameraPathVideo", "string", title:"Camera Path to Video", description: "Please enter the path to the video", defaultValue: "/Streaming/Channels/1", required: false, displayDuringSetup: true)
    input("CameraAuth", "bool", title:"Does Camera require User Auth?", description: "Please choose if the camera requires authentication (only basic is supported)", defaultValue: true, required: false, displayDuringSetup: true)
    input("CameraPostGet", "string", title:"Does Camera use a Post or Get, normally Get?", description: "Please choose if the camera uses a POST or a GET command to retreive the image", defaultValue: "GET", required: false, displayDuringSetup: true)
	simulator {

    tiles {
		multiAttributeTile(name: "videoPlayer", type: "videoPlayer", width: 6, height: 4) {
			tileAttribute("device.switch", key: "CAMERA_STATUS") {
				attributeState("on", label: "Active", icon: "", action: "", backgroundColor: "#79b821", defaultState: true)
				attributeState("off", label: "Inactive", icon: "", action: "switch.on", backgroundColor: "#ffffff")
				attributeState("restarting", label: "Connecting", icon: "", backgroundColor: "#53a7c0")
				attributeState("unavailable", label: "Unavailable", icon: "", action: "refresh.refresh", backgroundColor: "#F22000")

			tileAttribute("device.errorMessage", key: "CAMERA_ERROR_MESSAGE") {
				attributeState("errorMessage", label: "", value: "", defaultState: true)

			tileAttribute("", key: "PRIMARY_CONTROL") {
				attributeState("on", label: "Active", icon: "", backgroundColor: "#79b821", defaultState: true)
				attributeState("off", label: "Inactive", icon: "", backgroundColor: "#ffffff")
				attributeState("restarting", label: "Connecting", icon: "", backgroundColor: "#53a7c0")
				attributeState("unavailable", label: "Unavailable", icon: "", backgroundColor: "#F22000")

			tileAttribute("device.startLive", key: "START_LIVE") {
				attributeState("live", action: "start", defaultState: true)

			tileAttribute("", key: "STREAM_URL") {
				attributeState("activeURL", defaultState: true)
        standardTile("take", "device.image", width: 2, height: 1, canChangeIcon: false, inactiveLabel: true, canChangeBackground: false) {
            state "take", label: "Take", action: "Image Capture.take", icon: "", backgroundColor: "#FFFFFF", nextState:"taking"
            state "taking", label:'Taking', action: "", icon: "", backgroundColor: "#53a7c0"
            state "image", label: "Take", action: "Image Capture.take", icon: "", backgroundColor: "#FFFFFF", nextState:"taking"

        standardTile("refresh", "device.alarmStatus", width: 2, height: 1, inactiveLabel: false, decoration: "flat") {
            state "refresh", action:"polling.poll", icon:"st.secondary.refresh"
        standardTile("blank", "device.image", width: 2, height: 1, canChangeIcon: false, canChangeBackground: false, decoration: "flat") {
            state "blank", label: "", action: "", icon: "", backgroundColor: "#FFFFFF"
        standardTile("motion", "device.motionStatus", inactiveLabel: false, decoration: "flat") {
    		state "off", label: "off", action: "toggleMotion", icon: "st.motion.motion.inactive", backgroundColor: "#FFFFFF"
			state "on", label: "on", action: "toggleMotion", icon: "",  backgroundColor: "#00ff00"
    	standardTile("refresh", "device.cameraStatus", inactiveLabel: false, decoration: "flat") {
        	state "refresh", action:"polling.poll", icon:"st.secondary.refresh"
        carouselTile("cameraDetails", "device.image", width: 6, height: 4) { }
        main "take"
        details(["videoPlayer", "take", "motion", "refresh", "cameraDetails"])

//for photo
def parse(String description) {
    log.debug "Parsing '${description}'"
    def map = [:]
	def retResult = []
	def descMap = parseDescriptionAsMap(description)
	def imageKey = descMap["tempImageKey"] ? descMap["tempImageKey"] : descMap["key"]
	if (imageKey) {
		storeTemporaryImage(imageKey, getPictureName())

// handle commands
def take() {
	def userpassascii = "${CameraUser}:${CameraPassword}"
	def userpass = "Basic " + userpassascii.encodeAsBase64().toString()
    def host = CameraIP 
    def hosthex = convertIPtoHex(host).toUpperCase() //thanks to @foxxyben for catching this
    def porthex = convertPortToHex(CameraPort).toUpperCase()
    device.deviceNetworkId = "$hosthex:$porthex" 
    log.debug "The device id configured is: $device.deviceNetworkId"
    def path = CameraPath 
    log.debug "path is: $path"
    log.debug "Requires Auth: $CameraAuth"
    log.debug "Uses which method: $CameraPostGet"
    def headers = [:] 
    headers.put("HOST", "$host:$CameraPort")
   	if (CameraAuth) {
        headers.put("Authorization", userpass)
    log.debug "The Header is $headers"
    def method = "GET"
    try {
    	if (CameraPostGet.toUpperCase() == "POST") {
        	method = "POST"
    catch (Exception e) { // HACK to get around default values not setting in devices
    	settings.CameraPostGet = "GET"
        log.debug e
        log.debug "You must not of set the perference for the CameraPOSTGET option"
    log.debug "The method is $method"
    try {
    def hubAction = new physicalgraph.device.HubAction(
    	method: method,
    	path: path,
    	headers: headers
    hubAction.options = [outputMsgToS3:true]
    log.debug hubAction
    catch (Exception e) {
    	log.debug "Hit Exception $e on $hubAction"

def parseDescriptionAsMap(description) {
description.split(",").inject([:]) { map, param ->
def nameAndValue = param.split(":")
map += [(nameAndValue[0].trim()):nameAndValue[1].trim()]

private getPictureName() {
	def pictureUuid = java.util.UUID.randomUUID().toString().replaceAll('-', '')
    log.debug pictureUuid
    def picName = device.deviceNetworkId.replaceAll(':', '') + "_$pictureUuid" + ".jpg"
	return picName

private String convertIPtoHex(ipAddress) { 
    String hex = ipAddress.tokenize( '.' ).collect {  String.format( '%02x', it.toInteger() ) }.join()
    log.debug "IP address entered is $ipAddress and the converted hex code is $hex"
    return hex


private String convertPortToHex(port) {
	String hexport = port.toString().format( '%04x', port.toInteger() )
    log.debug hexport
    return hexport

private Integer convertHexToInt(hex) {

private String convertHexToIP(hex) {
	log.debug("Convert hex to ip: $hex") 

private getHostAddress() {
	def parts = device.deviceNetworkId.split(":")
    log.debug device.deviceNetworkId
	def ip = convertHexToIP(parts[0])
	def port = convertHexToInt(parts[1])
	return ip + ":" + port

//for video
private getVideoURL() {
    def videoURL = ""
    if (CameraAuth) {
    	videoURL = "rtsp://${CameraUser}:${CameraPassword}@${CameraIP}:${CameraRtspPort}${CameraPathVideo}"
    } else {
    	videoURL = "rtsp://${CameraIP}:${CameraRtspPort}${CameraPathVideo}"
    return videoURL

mappings {
   path("/getInHomeURL") {
       [GET: "getInHomeURL"]

def installed() {

def updated() {

// parse events into attributes
/*def parse(String description) {
	log.debug "Parsing '${description}'"

// handle commands
def configure() {
	log.debug "Executing 'configure'"
    sendEvent(name:"switch", value: "on")

def start() {
	log.trace "start()"
    def videoURL = getVideoURL()
    log.debug videoURL
	def dataLiveVideo = [
		OutHomeURL  : videoURL,
		InHomeURL   : videoURL,
		ThumbnailURL: "",
		cookie      : [key: "key", value: "value"]

	def event = [
		name           : "stream",
		value          : groovy.json.JsonOutput.toJson(dataLiveVideo).toString(),
		data		   : groovy.json.JsonOutput.toJson(dataLiveVideo),
		descriptionText: "Starting the livestream",
		eventType      : "VIDEO",
		displayed      : false,
		isStateChange  : true

def getInHomeURL() {
	 [InHomeURL: getVideoURL()]
// for motion
def motion() {
	log.debug( "motion() state" );

def strUrl = "http://${CameraUser}:${CameraPassword}@${CameraIP}/IO/outputs/1/status{}";
	log.debug( "strUrl = ${strUrl}" );

	httpPost( strUrl ){


def motionDisable = new XmlSlurper().parseText('''
	<IOPortStatus xmlns="" version="1.0">
def motionEnable = new XmlSlurper().parseText('''
	<IOPortStatus xmlns="" version="1.0">

Since my limited experience I have no idea how to proceed (can’t find it on the forum either). Can someone, please help me? (the motion part(XML parser) is at the bottom of the file)

1 Like

guys please dont think i am mad but i have read and read.

I have tried maybe 4 different methods and nothing works (or to rephrase) I am doing it wrong.

I know the ip of my camera and the ports
I know the user name and password

When I follow methods they ask me for ip address but never user name and password.

I have a network Swann DVR with 4 cameras and an small tenvis indoor ip cam

any help would be great

I don’t know about your particular camera but you can find the database on ispycam website. A generic one usually username:password@ipaddress:port/videoFeed

Try it on a web browser first. If it didn’t work then it won’t work with this for sure.

The other thing to do, is add the device handler (I think you have done this), create the device (using the handler) and with an ID of the MAC address. Then after creation, go into the device on your phone and press on the cog at the top right, there it will definitely give you the option to add ip/port/user/pass plus

@Jon_Turnbull have you use the “Ispy Database”? I put an link to the swan camera as I didn’t know what camera you have? Try this link?

1 Like

I am guessing i might not be able to do it.
There are 4 cameras linked to a DRV which has the IP address ie each camera doens have an ip.

Device type VAL8-3425
Hardware Version H2MB02

Thanks fo the hints so far

In principle yes, ST could get very confused, but actually the camera is not passing any messages to ST, hence you might find that the mac address party isn’t actually needed… Worth a shot, you can make the 2/3/4 devices just have a text device id like camera2 .

Thank you but my device model is not on the list so struggling

If Im honest i guess i am just not at the level I need to be to do this because i dont know where to start.

I managed to add my Tenvis cam but cant get anywhere with these

@Jon_Turnbull That shouldnt matter, its generic so should work with most devices. Try these steps:

  1. First get a weblink (URL) into your DVR which gives you a video for Camera 1 (e.g. IP_OF_DVR/cameras/1/live or something like that, try looking in the webpage that you use connect to the DVR)
  2. Create a device from the devices page of the web dashboard (IDE)
  3. Fill in the details upon creation of the device, most things can be whatever you want, but device ID call it camera1 and device handler use the Generic Camera Device from pstuart (probably near the bottom of the list of device handlers).
  4. In the app on your phone, go into the new device, go to settings at the top right, and then add the IP address and web link from step 1 (plus username and password if needed - or try and turn off login passwords in the DVR webpage while testing).

That should get you something working, and I expect step 1 might be the hardest step… I am sure you know the IP address, and from a quick search I would try one of these links/URLs:

So is this just for images? Where are the images stored, on my phone or in the cloud?

1 Like

I just want to send a big thank you to PStuart!!! This was my first step into open source and you have created a fire inside of me. I am currently studying Groovy (in my spare time) and I really appreciate what you did here. I tried to set this up when I first got smart things and had some trouble, due to the fact that I had no idea how the logic worked. I thought I needed to put my IP in the code. After some reading and coming back to it a month later, I was able to set up four cameras in about 35 minutes. Thanks again man, very nice work!

1 Like

For anyone using @storageanarchy 's modified edition to get the ‘take’ image (I am), it doesn’t work any longer on ST. The reason is this line:

headers.put("HOST", "$host:$CameraPort")

This must now be:

headers.put("HOST", "$hosthex:$porthex")

If you make that change it will work again.

EDIT (update): this stopped working again. I had to do more changes, see HubAction parse no longer being called?

1 Like


I have been using this DTH on one HUB in the UK for months with 4 cameras and no issues. I set up another HUB in Spain recently and cannot get an image back from its local camera. The configurations between the two are identical and I have tested that sending the HTTP GET string form a local computer returns an image.

Looking at live logging I never see the line “Parsing…tempImageKey…”.

Any suggestions?

Hey all,

Not sure if anyone can point me in the right direction, I know how to create a device and install the DTH and SmartApps, but the camera I have; I’m not sure how to get to the video…I haven’t even been able to open the stream in VLC.

I can only open it in Internet Explorer at

It won’t open in Chrome (it wants to download a plugin)

I got the cam from Amazon

Does anyone know what I need to do to view the stream in VLC (then ultimately in Smartthings).

Thanks in advance.

Which plugin?

If the video is perhaps in Flash format (what exact camera is it? have you found the technical manual online with details as to its streams?)… Then I don’t think VLC will help.

But you might not need VLC if the camera has RTSP streaming.