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

Still not working for me :frowning:

This line of code is failing for me.

private String convertHexToIP(hex) {
	[convertHexToInt(hex[0..1]),convertHexToInt(hex[2..3]),convertHexToInt(hex[4..5]),convertHexToInt(hex[6..7])].join(".")
} 

Getting this from the logs

java.lang.StringIndexOutOfBoundsException: String index out of range: 8 @ line 204

Appreciate all the work man!!!

What is the IP @tslagle13 you are entering in preferences?

same as above:

10.0.0.22

EDIT.

My camera is located at 10.0.0.22:8006

that way you have the full address if needed.

But how do you access them from within the app?

Using Android? If so, viewing images is way broke. Supposedly it was fixed in the latest release of the app. Fat chance, still broken.

If you click the take button, they should show up above the take button in the 3x2 tile.

There is no way to persist the images, they seem to disappear into the etherā€¦

On iOS they should show the last 10. Android is fubared so that never works.

Android works for the current image plus however many you take during that session. At least for me it doesā€¦

https://github.com/pstuart/smartthings/blob/master/generic_camera.groovy

Update to hopefully fix the ip issue.

So now that we have this workingā€¦ whatā€™s the point if we have no access to images that may be taken from a motion trigger or such?
http://www.golfwrx.com/forums/public/style_emoticons/default/cheesy.gif

LOL, so you can pull up a snapshot of a camera remotely? Havenā€™t gotten that far. As to usage, we have to figure that one out nextā€¦

I figure, get the cameraā€™s working first :slight_smile:

Confirmed, Android doesnā€™t have access to stored images, iOS does. This is sadā€¦ Open a support ticket, if enough maybe we can get a developer to get android on par with iOS

Iā€™m trying this with a Logitech Alert camera. Have no luck getting an image as of right now. iOS app.

@Jsalicru want to post any debug output from the console? Hard to help you without details of what isnā€™t working.

Not familiar with that camera, so we will need to learn the path, method, authentication (if any) and format of the image to make sure its compatible.

I was able to find this info about it: http://IPADDRESS/image[CHANNEL].jpg

So, your path should be ā€œ/image1.jpgā€, port should be 80, ip should be the camera ip address.

Doesnā€™t look like it needs authentication, and uses a GET command.

Make sure you are using the latest code from my github too.

Hello Patrick - Thanks for the quick replies. Finally got back to test the updated code, It is still failing somewhere:

My port is 8090

baf77216-9f5a-42e5-9cfc-5875ab79c23b2:11:57 PM EDT: debug Parse returned Jonathanā€™s iPhone has left
b1dcbb1c-023c-4e0f-a0d6-6f23528b255d2:11:51 PM EDT: debug Parsing 'index:01, mac:007F281561AC, ip:6C32F971, port:1F9Aā€™
b1dcbb1c-023c-4e0f-a0d6-6f23528b255d2:11:48 PM EDT: debug The method is GET
b1dcbb1c-023c-4e0f-a0d6-6f23528b255d2:11:48 PM EDT: debug Uses which method: GET
b1dcbb1c-023c-4e0f-a0d6-6f23528b255d2:11:48 PM EDT: debug Requires Auth: true
b1dcbb1c-023c-4e0f-a0d6-6f23528b255d2:11:48 PM EDT: debug path is: /control/userimage.html

Also just in-case it matters, my Camera type is a Mobotix Q25

The image type is M-JPEG (Not image will be approx 2048x1536)

No picture for me eitherā€¦ can you take a look at my current foscam code and work your magic? :smile: These device types are gibberish to me. Iā€™m just not a code writer at all :frowning:

I really appreciate all the help man!!!

If you can just tell me what to replace and where in your code so the image can be pulled correctly that would be great. I have just tried to hardcode in the username and password to the URLā€¦ I can use this URL to get a snapshot in a browser so i can confirm the URL is correct. Just need to know what to change to get the device type to know how to handle the image. Also, i know foscams use POST for the image command.

Here is my output from the IDE log

21c953b2-0291-4118-a4d4-f649f3634d35 1:18:12 PM: debug The method is POST
21c953b2-0291-4118-a4d4-f649f3634d35 1:18:12 PM: debug The Header is [HOST:10.0.0.24:8005]
21c953b2-0291-4118-a4d4-f649f3634d35 1:18:12 PM: debug Uses which method: Post
21c953b2-0291-4118-a4d4-f649f3634d35 1:18:12 PM: debug Requires Auth: false
21c953b2-0291-4118-a4d4-f649f3634d35 1:18:12 PM: debug path is: /snapshot.cgi?user=[USERNAME]&pwd=[PASSWORD]&count=0  

(I omitted my username and password in the log)

Here is the current foscam code i am using.

/**
 *  Foscam
 *
 *  Author: danny@smartthings.com
 *  Author: brian@bevey.org
 *  Date: 5/2/14
 *
 *  Modified example Foscam device type to support dynamic input of credentials
 *  and enable / disable motion alarm to easily integrate into homemade
 *  security systems (when away, mark "alarmStatus" as "on", when present, mark
 *  "alarmStatus" as "off".  For use with email or FTP image uploading built
 *  into Foscam cameras.
 *
 *  Capability: Image Capture, Polling
 *  Custom Attributes: setStatus, alarmStatus
 *  Custom Commands: alarmOn, alarmOff, toggleAlarm, left, right, up, down,
 *                   pause, set, preset, preset1, preset2, preset3
 */

preferences {
  input("username", "text",     title: "Username",   description: "Your Foscam username")
  input("password", "password", title: "Password",   description: "Your Foscam password")
  input("ip",       "text",     title: "IP address", description: "The IP address of your Foscam")
  input("port",     "text",     title: "Port",       description: "The port of your Foscam")
}

metadata {
  definition (name: "Foscam") {
    capability "Polling"
    capability "Image Capture"

    attribute "setStatus",  "string"
    attribute "alarmStats", "string"

    command "alarmOn"
    command "alarmOff"
    command "toggleAlarm"
    command "left"
    command "right"
    command "up"
    command "down"
    command "pause"
    command "set"
    command "preset"
    command "preset1"
    command "preset2"
    command "preset3"
  }

  tiles {
    carouselTile("cameraDetails", "device.image", width: 3, height: 2) { }

    standardTile("camera", "device.image", width: 1, height: 1, canChangeIcon: false, inactiveLabel: true, canChangeBackground: false) {
      state "default", label: "", action: "Image Capture.take", icon: "st.camera.dropcam-centered", backgroundColor: "#FFFFFF"
    }

    standardTile("take", "device.image", width: 1, height: 1, canChangeIcon: false, inactiveLabel: true, canChangeBackground: false, decoration: "flat") {
      state "take", label: "", action: "Image Capture.take", icon: "st.secondary.take", nextState:"taking"
    }

    standardTile("up", "device.image", width: 1, height: 1, canChangeIcon: false,  canChangeBackground: false, decoration: "flat") {
      state "take", label: "up", action: "up", icon: ""
    }

    standardTile("alarmStatus", "device.alarmStatus", width: 1, height: 1, canChangeIcon: false, inactiveLabel: true, canChangeBackground: false) {
      state "off", label: "off", action: "toggleAlarm", icon: "st.camera.dropcam-centered", backgroundColor: "#FFFFFF"
      state "on", label: "on", action: "toggleAlarm", icon: "st.camera.dropcam-centered",  backgroundColor: "#53A7C0"
    }

    standardTile("left", "device.image", width: 1, height: 1, canChangeIcon: false,  canChangeBackground: false, decoration: "flat") {
      state "take", label: "left", action: "left", icon: ""
    }

    standardTile("pause", "device.image", width: 1, height: 1, canChangeIcon: false,  canChangeBackground: false, decoration: "flat") {
      state "pause", label: "pause", action: "pause", icon: ""
    }
    
    standardTile("right", "device.image", width: 1, height: 1, canChangeIcon: false,  canChangeBackground: false, decoration: "flat") {
      state "take", label: "right", action: "right", icon: ""
    }

    standardTile("blank", "device.image", width: 1, height: 1, canChangeIcon: false,  canChangeBackground: false, decoration: "flat") {
      state "pause", label: "", action: "pause", icon: ""
    }

    standardTile("down", "device.image", width: 1, height: 1, canChangeIcon: false, canChangeBackground: false, decoration: "flat") {
      state "down", label: "down", action: "down", icon: ""
    }

    standardTile("set", "device.setStatus", width: 1, height: 1, canChangeIcon: false, inactiveLabel: true, canChangeBackground: false) {
      state "set", label: "set", action: "set", icon: "",  backgroundColor: "#FFFFFF"
      state "setting", label: "set mode", action: "set", icon: "", backgroundColor: "#53A7C0"
    }

    standardTile("preset1", "device.image", width: 1, height: 1, canChangeIcon: false, canChangeBackground: false, decoration: "flat") {
      state "preset1", label: "preset 1", action: "preset1", icon: ""
    }

    standardTile("preset2", "device.image", width: 1, height: 1, canChangeIcon: false, canChangeBackground: false, decoration: "flat") {
      state "preset2", label: "preset 2", action: "preset2", icon: ""
    }

    standardTile("preset3", "device.image", width: 1, height: 1, canChangeIcon: false, canChangeBackground: false, decoration: "flat") {
      state "preset3", label: "preset 3", action: "preset3", icon: ""
    }

    standardTile("refresh", "device.alarmStatus", inactiveLabel: false, decoration: "flat") {
      state "default", action:"polling.poll", icon:"st.secondary.refresh"
    }

    main "alarmStatus"
      details(["cameraDetails", "take", "up", "alarmStatus", "left", "pause", "right", "blank", "down", "set", "preset1", "preset2", "preset3", "refresh"])
  }
}

private getPictureName() {
  def pictureUuid = java.util.UUID.randomUUID().toString().replaceAll('-', '')
  "image" + "_$pictureUuid" + ".jpg"
}

private take() {
  log.debug("Take a photo")

  api("snapshot", "") {
    log.debug("Image captured")

    if(it.headers.'Content-Type'.contains("image/jpeg")) {
      if(it.data) {
        storeImage(getPictureName(), it.data)
      }
    }
  }
}

def toggleAlarm() {
  if(device.currentValue("alarmStatus") == "on") {
    alarmOff()
  }

  else {
    alarmOn()
  }
}

private alarmOn() {
  api("set_alarm", "motion_armed=1") {
    log.debug("Alarm changed to: on")
    sendEvent(name: "alarmStatus", value: "on");
  }
}

private alarmOff() {
  api("set_alarm", "motion_armed=0") {
    log.debug("Alarm changed to: off")
    sendEvent(name: "alarmStatus", value: "off");
  }
}

def left() {
  api("decoder_control", "command=6") {
    log.debug("Executing 'left'")
  }
}

def right() {
  api("decoder_control", "command=4") {
    log.debug("Executing 'right'")
  }
}

def up() {
  api("decoder_control", "command=0") {
    log.debug("Executing 'up'")
  }
}

def down() {
  api("decoder_control", "command=2") {
    log.debug("Executing 'down'")
  }
}

def pause() {
  api("decoder_control", "command=1") {}
}

def preset1() {
  preset(1)
}

def preset2() {
  preset(2)
}

def preset3() {
  preset(3)
}

//go to a preset location
def preset(def num) {
  if(num == null) return

  if(device.currentValue("setStatus") == "setting") {
    setPreset(num)
  }

  else {
    log.debug("Go To Preset Location")
    //1 is 31, 2 is 33, 3 is 35
    def cmd = 30 + (num * 2) - 1

    api("decoder_control", "command=${cmd}") {}
  }
}

//set the preset number to the current location
def setPreset(def num) {
  log.debug("Set Preset")
  //1 is 30, 2 is 32, 3 is 34... 8 is 44
  int cmd = 28 + (num * 2)
  sendCmd(cmd)

  log.debug("Exit Set Mode")
  sendEvent(name: "setStatus", value: "set");
}

//toggle the the mode to set the preset
def set() {
  if(device.currentValue("setStatus") == "set") {
    log.debug("Entering Set Mode")
    sendEvent(name: "setStatus", value: "setting");
  }

  else {
    log.debug("Exit Set Mode")
    sendEvent(name: "setStatus", value: "set");
  }
}

def api(method, args = [], success = {}) {
  def methods = [
    "decoder_control": [uri: "http://${ip}:${port}/decoder_control.cgi${login()}&${args}", type: "post"],
    "snapshot":        [uri: "http://${ip}:${port}/snapshot.cgi${login()}&${args}",        type: "post"],
    "set_alarm":       [uri: "http://${ip}:${port}/set_alarm.cgi${login()}&${args}",       type: "post"],
    "reboot":          [uri: "http://${ip}:${port}/reboot.cgi${login()}&${args}",          type: "post"],
    "camera_control":  [uri: "http://${ip}:${port}/camera_control.cgi${login()}&${args}",  type: "post"],
    "get_params":      [uri: "http://${ip}:${port}/get_params.cgi${login()}",              type: "get"],
    "videostream":     [uri: "http://${ip}:${port}/videostream.cgi${login()}",             type: "get"]
  ]

  def request = methods.getAt(method)

  doRequest(request.uri, request.type, success)
}

private doRequest(uri, type, success) {
  log.debug(uri)

  if(type == "post") {
    httpPost(uri , "", success)
  }

  else if(type == "get") {
    httpGet(uri, success)
  }
}

private login() {
  return "?user=${username}&pwd=${password}"
}

def poll() {
  api("get_params", []) {
    it.data.eachLine {
      if(it.startsWith("var alarm_motion_armed=0")) {
        log.info("Polled: Alarm off")
        sendEvent(name: "alarmStatus", value: "off");
      }

      if(it.startsWith("var alarm_motion_armed=1")) {
        log.info("Polled: Alarm on")
        sendEvent(name: "alarmStatus", value: "on");
      }
    }
  }
}

Thanks again man!!!

Thatā€™s a pretty damn cool camera!

http://www.amazon.com/Mobotix-Q25M-Sec-D12-Hemispheric-Security-BV-TECH/dp/B00IOSHRCY

Agreed. I can imagine Nest collaborating with Dropcam to add such a camera to their smoke detector soon.

LOLā€¦ the $900 smoke detector from Google!

Thanks - I love the Camera too. MOBOTIX quality is really 2nd to none, I was amazed with it when I first opened it up. Its was pricy investment but honestly I would need 2-3 cameras to cover the area it does.
For me it was about getting something that was at heart a security camera, not a pet or child monitor. It also needed to be very inconspicuous, no moving parts and cover a large area (plus it stores local SD and sends to remote storage).
If anyone is interested in talking home security cams let me know as I have done a lot of research.
PS Amazon price is jacked up ~50% from what I purchased it for from a reputable distributor.

I too have a Hikvision camera and using the same path as Sean and same issue with getting stuck on ā€œtakingā€