Hi, I’m very new to the whole world of SmartThings but have been trying to integrate my existing cameras into the platform. Using @pstuart 's Generic Camera Device as a starting point I’ve been trying to modify the code in order to add the ability to turn on and off the motion sensing. My intention is to allow myself to turn on the motion sensing when everyone is away so that it will automatically email me snapshots of any detected motion.
I think I already know what URL based commands I need to pass to the camera in order for it to do what I want:
DCS-942L:
ON: http://username:password@IP:Port/config/motion.cgi?enable=1&pir=1
OFF: http://username:password@IP:Port/config/motion.cgi?enable=0&pir=0
DCS-5020L:
ON: http://username:password@IP:Port/setSystemMotion?ReplySuccessPage=motion.htm&ReplyErrorPage=motion.htm&MotionDetectionEnable=1&MotionDetectionSensitivity=85&ConfigSystemMotion=Save
OFF: http://username:password@IP:Port/setSystemMotion?ReplySuccessPage=motion.htm&ReplyErrorPage=motion.htm&MotionDetectionEnable=0&MotionDetectionSensitivity=85&ConfigSystemMotion=Save
I’ve started with the DCS-942L because it is the simplest of the two and believe that I have it pretty close. Below is a copy of the hacked together devicetype, It’s basically just a bastardized version of @pstuart 's code with a little bit from @ethomasii 's DCS-930 code but it’s a start.
/**
* Generic Camera Device v1.0.07102014
* Modified for the D-Link DCS-942L
*
* Copyright 2014 patrick@patrickstuart.com
* Modified 2015 blebson
*
* 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:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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: "DCS-942L", author: "patrick@patrickstuart.com") {
capability "Image Capture"
capability "Sensor"
capability "Switch"
attribute "hubactionMode", "string"
}
preferences {
input("CameraIP", "string", title:"Camera IP Address", description: "Please enter your camera's IP Address", required: true, displayDuringSetup: true)
input("CameraPort", "string", title:"Camera Port", description: "Please enter your camera's Port", defaultValue: 80 , required: true, displayDuringSetup: true)
input("CameraPath", "string", title:"Camera Path to Image", description: "Please enter the path to the image", defaultValue: "/SnapshotJPEG?Resolution=640x480&Quality=Clarity", required: true, 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, 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", 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)
}
simulator {
}
/*
tiles {
standardTile("camera", "device.image", width: 1, height: 1, canChangeIcon: false, inactiveLabel: true, canChangeBackground: true) {
state "default", label: "", action: "", icon: "st.camera.dropcam-centered", backgroundColor: "#FFFFFF"
}
carouselTile("cameraDetails", "device.image", width: 3, height: 2) { }
standardTile("take", "device.image", width: 1, height: 1, canChangeIcon: false, inactiveLabel: true, canChangeBackground: false) {
state "take", label: "Take", action: "Image Capture.take", icon: "st.camera.camera", backgroundColor: "#FFFFFF", nextState:"taking"
state "taking", label:'Taking', action: "", icon: "st.camera.take-photo", backgroundColor: "#53a7c0"
//state "image", label: "Take", action: "Image Capture.take", icon: "st.camera.camera", backgroundColor: "#FFFFFF", nextState:"taking"
}
standardTile("blank", "device.image", width: 1, height: 1, canChangeIcon: false, canChangeBackground: false, decoration: "flat") {
state "blank", label: "", action: "", icon: "", backgroundColor: "#FFFFFF"
}
main "camera"
details(["cameraDetails", "blank", "take"])
}
*/
tiles {
standardTile("take", "device.image", width: 1, height: 1, canChangeIcon: false, inactiveLabel: true, canChangeBackground: false) {
state "take", label: "Take", action: "Image Capture.take", icon: "st.camera.camera", backgroundColor: "#FFFFFF", nextState:"taking"
state "taking", label:'Taking', action: "", icon: "st.camera.take-photo", backgroundColor: "#53a7c0"
state "image", label: "Take", action: "Image Capture.take", icon: "st.camera.camera", backgroundColor: "#FFFFFF", nextState:"taking"
}
standardTile("refresh", "device.alarmStatus", inactiveLabel: false, decoration: "flat") {
state "refresh", action:"polling.poll", icon:"st.secondary.refresh"
}
standardTile("button", "device.switch", width: 1, height: 1, canChangeIcon: true) {
state "off", label: 'Off', action: "switch.on", icon: "st.custom.buttons.rec", backgroundColor: "#ccffcc"
state "on", label: 'On', action: "switch.off", icon: "st.custom.buttons.rec", backgroundColor: "#EE0000"
}
carouselTile("cameraDetails", "device.image", width: 3, height: 2) { }
main "button"
details([ "take", "button", "refresh", "cameraDetails"])
}
}
def parse(String description) {
log.debug "Parsing '${description}'"
def map = [:]
def retResult = []
def descMap = parseDescriptionAsMap(description)
//Image
if (descMap["bucket"] && descMap["key"]) {
putImageInS3(descMap)
}
}
// handle commands
def take() {
def userpassascii = "${CameraUser}:${CameraPassword}"
def userpass = "Basic " + userpassascii.encodeAsBase64().toString()
def host = CameraIP
def hosthex = convertIPtoHex(host)
def porthex = convertPortToHex(CameraPort)
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
hubAction
}
catch (Exception e) {
log.debug "Hit Exception $e on $hubAction"
}
}
def putImageInS3(map) {
log.debug "firing s3"
def s3ObjectContent
try {
def imageBytes = getS3Object(map.bucket, map.key + ".jpg")
if(imageBytes)
{
s3ObjectContent = imageBytes.getObjectContent()
def bytes = new ByteArrayInputStream(s3ObjectContent.bytes)
storeImage(getPictureName(), bytes)
}
}
catch(Exception e) {
log.error e
}
finally {
//Explicitly close the stream
if (s3ObjectContent) { s3ObjectContent.close() }
}
}
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) {
Integer.parseInt(hex,16)
}
private String convertHexToIP(hex) {
log.debug("Convert hex to ip: $hex")
[convertHexToInt(hex[0..1]),convertHexToInt(hex[2..3]),convertHexToInt(hex[4..5]),convertHexToInt(hex[6..7])].join(".")
}
private getHostAddress() {
def parts = device.deviceNetworkId.split(":")
log.debug device.deviceNetworkId
def ip = convertHexToIP(parts[0])
def port = convertHexToInt(parts[1])
return ip + ":" + port
}
def motionCmd(int motion)
{
def userpassascii = "${CameraUser}:${CameraPassword}"
def userpass = "Basic " + userpassascii.encodeAsBase64().toString()
def host = CameraIP
def hosthex = convertIPtoHex(host)
def porthex = convertPortToHex(CameraPort)
device.deviceNetworkId = "$hosthex:$porthex"
log.debug "The device id configured is: $device.deviceNetworkId"
def path = "/config/motion.cgi?enable=yes&pir=yes"
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"
sendEvent(name: 'motion', value: motion)
if (motion == 1){
sendEvent(name: 'motion', value: motion)
try {
def hubAction = new physicalgraph.device.HubAction(
method: "POST",
path: "/config/motion.cgi?enable=yes&pir=yes",
headers: headers
)
//hubAction.options = [outputMsgToS3:true]
log.debug hubAction
hubAction
}
catch (Exception e) {
log.debug "Hit Exception $e on $hubAction"
}
}
else
{
path = "/config/motion.cgi?enable=no&pir=no"
try {
def hubAction = new physicalgraph.device.HubAction(
method: "POST",
path: "/config/motion.cgi?enable=no&pir=no",
headers: headers
)
//hubAction.options = [outputMsgToS3:true]
log.debug hubAction
hubAction
}
catch (Exception e) {
log.debug "Hit Exception $e on $hubAction"
}
return hubAction
}
}
def on() {
log.debug "Executing motion detection"
motionCmd(1)
sendEvent(name: "switch", value: "on")
}
def off() {
log.debug "Disabling motion detection"
motionCmd(0)
sendEvent(name: "switch", value: "off")
}
def setMotion(status) {
log.debug "Status: $status"
motionCmd(status) {
sendEvent(name: 'motion', value: status)
}
}
When I press the ‘Record’ button it shows that it runs the hubAction command but then nothing happens;
5797fd8f-1745-4b7d-968c-d97bc91a73cc 10:04:36 PM: debug POST /config/motion.cgi?enable=yes&pir=yes HTTP/1.1
Accept: /
User-Agent: Linux UPnP/1.0 SmartThings
HOST: 192.168.1.65:80
Authorization: Basic XXXXXXXXXXXXXXXX
5797fd8f-1745-4b7d-968c-d97bc91a73cc 10:04:35 PM: debug The Header is [HOST:192.168.1.65:80, Authorization:Basic XXXXXXXXXXXXX]
5797fd8f-1745-4b7d-968c-d97bc91a73cc 10:04:35 PM: debug Uses which method: GET
5797fd8f-1745-4b7d-968c-d97bc91a73cc 10:04:35 PM: debug Requires Auth: true
5797fd8f-1745-4b7d-968c-d97bc91a73cc 10:04:35 PM: debug path is: /config/motion.cgi?enable=yes&pir=yes
5797fd8f-1745-4b7d-968c-d97bc91a73cc 10:04:35 PM: debug The device id configured is: c0a80141:0050
5797fd8f-1745-4b7d-968c-d97bc91a73cc 10:04:35 PM: debug 0050
5797fd8f-1745-4b7d-968c-d97bc91a73cc 10:04:35 PM: debug IP address entered is 192.168.1.65 and the converted hex code is c0a80141
5797fd8f-1745-4b7d-968c-d97bc91a73cc 10:04:35 PM: debug Executing motion detection
Any help getting this working would be greatly appreciated!