Modify state of virtual contact (open/closed)?


(Kent) #1

I’m trying to send a closed command to a virtual contact and while I never get any error messages the status never seems to change.
The code in question is mostly from the rest api endpoint tutorial which I was able to get working properly.
I then started modifying it to allow me to send a closed command to a virtual contact.
I can get the status of the contact (it’s always ‘null’ for a virtual one?) and other data from it just fine but I can’t seem to make it update it’s status from null to open or closed.
I can’t seem to find documentation on the sendEvent command either but found it while searching online for ways to change the state.

Any suggestions welcomed, thanks!

def device = devices.find { it.id == params.id }
sendEvent(name: "${device.currentState("contact")}", value: 'closed', isStateChange:true)

(Andrew Urman) #2

Try:

sendEvent(name: "contact", value: "closed")

(Kent) #3

Thanks for the suggestion but that doesn’t seem to do anything.
I’m able to modify the state of a switch for example with device.on() or device.off() but I can’t seem to send a event for motion (active/inactive) or a contact (open/closed).

Does anyone know where the documentation for sendEvent/createEvent is?
Seems like neither of those commands work at all to events.


(Florian Z) #4

Did you give your device the “Contact Sensor” capability?

Also, the sendEvent call goes into the device handler, not the SmartApp, right?


(Steve S) #5

@sanity - I don’t think you can send a command to the Virtual Contact switch - because like a real contact switch, you can’t “command” the sensor to close (because that requires the magnet and sensor to be moved close to each other - you have to do that manually). You can only read whether the contact is closed or open.


(Kent) #6

This is driving me crazy, I feel like I’m missing something simple here.
I want a contact or motion sensor or whatever that is not directly a physical device and using the REST API endpoints send a command to modify the “state” of any of those non-physical devices. I have tried numerous things in the simulator and on my iPhone via the ST app.

Here is the scenario and maybe I’m just missing something simple?

Create a new device type
-> Call it “DSC Zone”
-> Select “Contact Sensor”
-> Leave everything else at default and click “Create”
-> Select the browse code box on the right and choose “Open/Close Sensor”, don’t make any changes to the code and click “Save”.

Create a new device
-> Call it “Zone1”
-> Assign a fake network ID of your choosing, I’m using “ZONE1” for example
-> Click the Type drop down and choose the type we created above “DSC Zone”
-> Click Location “Home” (or whatever locations you have)
-> Assign to a Hub or Don’t assign to a hub? (I have found various posts saying to not assign it to the Hub and I have tried it both ways many times without any differences)
-> Leave group alone (unless you want to group it?) and click “Create”

Now you have a Device called “Zone1” that shows up in your iOS or Android app and on the WebUI.

Create a new app
-> Call it “DSC Test”
-> Give it a description “test”
-> Pick category “My Apps”
-> Click “Enable OAuth in Smart App” and write down id’s
-> Click “Create”
-> Paste in the code below
-> When done click save and then Publish “For Me”

On iPhone or Android -> Add new app -> My Apps -> "DSC Test"
For your contact device pick the device you created above “Zone1” click install

You now have the app and your device, the device by default says “open” and shows the open icon.
Try to change it by sending the proper REST API:
https://graph.api.smartthings.com/api/smartapps/installations/${appID}/contacts/${contactID}/closed?access_code=${accessCode}

I have all this working properly so that I see the events in the WebUI log and on the iPhone activity log but no matter what attribute I ask for or try to change the device “Zone1” icon always says Open. I have also tried using motion sensors (also not real ones) and can’t change those either. The only successful device I changed was a fake switch using the device.on() and device.off() functions of a fake switch. That updated the tile properly but nothing using attributes ever updates.

Is it impossible to change the attributes/status of something that is not an actual Z-wave or Zigbee device?
Seems like there should be a simple way to change it’s attributes/status but I can’t figure out how.
How does a real device get it’s tile to change?

Note: Most of this code is taken directly from the REST API endpoint tutorial from Smartthings, all of the other bits I have found in various forum postings and just tried them to see if any of it would work. None of this code throws any errors but the tile(s) belonging to the devices you pick never change their status.

preferences {
	section("Allow Endpoint to Control These Things...") {
		input "contacts", "capability.contactSensor ", title: "Which Doors?", multiple: true, required: false
        }
}
mappings {
	path("/contacts") {
		action: [
			GET: "listContacts"
		]
	}
	path("/contacts/:id") {
		action: [
			GET: "showContacts"
		]
	}
	path("/contacts/:id/:command") {
		action: [
			GET: "updateContact"
		]
	}
}

def installed() {}
def updated() {}

def listContacts() {
	contacts.collect{device(it,"contact")}
}

def showContacts() {
	show(contacts, "contact")
}
void updateContact() {
	update(contacts)
}
private List update(devices) {
   def results = []
   log.debug "update, request: params: ${params}, devices: $devices.id"    
   def command = params.command

   if (command) {
    def device = devices.find { it.id == params.id }
    log.debug "Device: ${device}"
    if (!device) {
	httpError(404, "Device not found")
    } else {
        log.debug "Device: $device"
        log.debug "Command: $command"
        log.debug "Caps: $device.supportedAttributes"
        if(command == "closed") {
             def latestState = device.latestState("contact")
              log.debug "latestState value: $latestState"
              def currentState = device.currentState()
              log.debug "currentState value: $currentState"
              currentState = 'closed'
              results << createEvent(name: "contact", value: "closed")
              results << createEvent(name: "status", value: "closed")
              log.debug "Status Closed"
          }
     }
  }
  results
}
private show(devices, type) {
  def device = devices.find { it.id == params.id }
  if (!device) {
    httpError(404, "This Device not found")
  }
  else {
      def supportedAttributes = device.supportedAttributes
        def attributes = []
        def a = ""
        for(attribute in supportedAttributes){
          a = device.currentState(attribute.toString())
            if ( a != null)
            attributes += [attribute: a]
        }
        [id: device.id, name: device.name, label: device.displayName, attributes: attributes]
  }
}

private device(it, type) {
	it ? [id: it.id, label: it.label, type: type] : null
}

(Steve S) #7

@sanity - I think you are missing something very fundamental here… by using the code from the Open/Close sensor - it is expecting to talk to a REAL device. If you look at the code, there are no commands it can receive - all it can do is parse incoming zigbee events (again, which would be generated by a real hardware device). So you probably see the app sending commands to the device, but since the device has no code to deal with the commands, it just gets ignored.

You should use the “On/Off Button Tile” as your device…then use your app to send on or off commands to that tile. Then you can use other apps to monitor the state of the tile and take actions based on that.


(Kent) #8

The code from the open/closed sensor (and many other sensors) do not appear to bespecific to Zigbee or Z-Wave as far as I can tell.
You have to add parse code to deal with incoming Zigbee/Z-Wave messages for any device.
The default for any “sensor” does not seem to be protocol specific, that’s the whole point of the Smartthings platform, a contact is a contact is a contact regardless of it’s backend.

It’s still bugging me that I can’t get a simple tile to update via an attribute.
If I use a device that has commands like on/off (device.on() for example) it works great.

Lets ignore the open/closed/default contact sensor bit for a moment.
If I create a device type that has a valueTile in it (or any tile for that matter) and use attributes for my values they never show up on the tile, ever, they don’t seem to show up on the device itself either in the Activity screen on iOS for example.

Seems like something is seriously broken or incorrectly documented for just the attributes on a device.
Commands on devices work fine, attributes do not.


(Kent) #9

I finally figured this out, hopefully this helps someone else struggling with this.

Seems like the disconnect was that I’m calling sendEvent() from the SmartApp instead of from inside the device.
So I setup a active() and inactive() command inside the Device and then call the command from the SmartApp.

The code below was added to my device type and now in the SmartApp code instead of using sendEvent() I use device.active() or device.inactive() and the device tile immediately updates!

def active() {
    log.debug "In DSCZoneNew active() - Motion detected"
    sendEvent(name: "motion", value: "active")
}

def inactive() {
    log.debug "In DSCZoneNew inactive() - No motion detected"
    sendEvent(name: "motion", value: "inactive")
}

So the moral of the story is you can’t modify attributes/capabilities of a device from a SmartApp (you can GET them but you can’t CHANGE them) but you CAN call a command associated with a device and have the devices command send the actual event.

Maybe there is some other way to send an event directly from a SmartApp to a Device but so far, in reading all the available documentation I can find, I can’t figure out a way to do it.


(Matt Martz) #10

@sanity I think I’m trying to do the same thing you are (setting up your DSC alarm system to have virtual contacts/sensors so you can add them to the dashboard/have individual tiles for doors and such… Mind sharing your code? The above is helpful and will probably give me a good start.

Are you using the AlarmServer method or the Arduino/SmartShield method of interfacing with the Alarm Panel? Right now I’m using AlarmServer but I have a SmartShield and will probably switch to that (it looks like it’d be more reliable).

Cheers,

Matt


(Chrisb) #11

Not sure of the problem here, but if I may ask: Why are you trying to get a virtual device to change to open/closed? What I mean is, what is it that you’re trying to accomplish with your efforts here?


(Kent) #12

@mattmartz I’m using a custom branch on the AlarmServer repo at the location below.
My device code and smartapp are at the second repo.
I have not had many issues with the AlarmServer route, it’s pretty stable unless my internet gets flaky.
I tend to restart the AlarmServer daemon weekly just for giggles.

Alarmserver repo (custom branch for Smartthings):
https://github.com/juggie/AlarmServer/tree/smartthings

My SmartApp and custom Device location:
https://github.com/kholloway/smartthings-dsc-alarm


(Kent) #13

@chrisb I was building (I built) a DSC Alarm panel integration to Smartthings (it works great!).
The “virtual panel device” is just that “virtual” in that it doesn’t talk Z-wave or Zigbee and is based off my own custom code.
The panel device has zones that are virtual contact switches (open/closed) and match up to my real DSC Alarm panel zones.
When a zone opens or closes at my house I send a REST API call to my Smartapp that sends a open() or close() command to my virtual panel (and to a particular zone) in the smartthings app.

All this works great it just took me a while to stumble past the documentation issues and a lot of trial and error.
My code for all of this is in the post above ^^ or can be found on another post here in the forums related to Alarms in general.

Let me know if you have any questions.

-Kent


(Matt Martz) #14

@sanity Thanks for the code. Turns out this was the code I am already using.

I’m running into a weird problem (still debugging) where the log shows the correct status but:

a) the device shows the wrong status (offset somehow) and
b) all of the zones show the same status

Also… I don’t see anything in your code related to a previous post about you using device.active() / device.inactive(). Do you have a 2nd device type that is just the contact sensor? I’m hoping that making a virtual contact sensor will allow it to show up individually in the “Dashboard”.

Sorry for all the questions, I’m new to SmartThings development. I’m using my own version of AlarmServer (https://github.com/oehokie/NodeAlarmProxy) built on NodeJS instead of python. Like I said, the smartthing logs say the status is the same.


(Matt Martz) #15

@sanity: I think I figured it out… I looked through my Event Logs from the device and it was showing: “DSC Alarm Panel partitionpartition1 is ready” and “DSC Alarm Panel zonezone1 is closed”… so I changed the code from…

sendEvent (name: "zone${zone}", value: "${state}")
and
sendEvent (name: "partition${partition}", value: "${state}")

to

sendEvent (name: "${zone}", value: "${state}")
and
sendEvent (name: "${partition}", value: "${state}")

I’m not home (on work travel)… but this makes sense because :zoneorpart from the mappings is ‘zone1’ in the URL and not just ‘1’ (at least in my code).

The next question is how to make this work for multiple devices (one for each zone) so that I can associate them with the dashboard…


(Kent) #16

@mattmartz I started with active/inactive on a contact sensor and eventually changed to open/closed since that made more sense for things like doors/windows coming from the DSC panel.

So I don’t have code for it right now but if you want to break this out into individual devices instead of a single panel it will take a bit of work. The basics are create a device type for open/closed (basically just use the standardTile settings and the open/closed commands from my code and remove all the rest including the panel stuff, etc so it becomes a single tile). Now that you have a new device type for just open/close make a bunch of them and name them whatever you like (probably match the zone names from the DSC?).

Then the new smartapp needs to ask for you to select all your new device types and somehow map them to the proper zones (again probably lookup the device name instead of the device ID) then use the rest of my code to map zones/partitions to the resulting devices.

Smartthings new lab setup for dropcam has some way of auto-creating icons on the dashboard for you, we should look into that code and try to repurpose it for auto-creating zones/devices from the DSC…??

If you start working on this let me know (link to Github or whatever) and I will be happy to contribute to it also.

-Kent


(Matt Martz) #17

@sanity I actually got it working pretty well (broken out for individual sensors). I’m going to clean up your code/fork it and issue a PR on github.

Basically what you end up doing is making a “DSC Zone” Device Type, and then add an individual device for each zone (the network id would be replaced with “zone1”, etc.

I’ve tested it out and it works really well, and it shows up in the dashboard. I need to clean some stuff up though. Will submit the PR tonight or tomorrow.


(Kent) #18

@mattmartz That’s great! I look forward to seeing your changes.

I’m looking at the addChildDevice example code from Smartthings now to auto-add zones (The dropcam app and the Lifx bulb app both have addChildDevice functions to look at), probably will have to add a zone discovery page of some kind to the AlarmServer code but if that works out it should greatly simplify the setup/build of all this and make all the devices a consistent type so they can be integrated to other smartapps.

-Kent


(Matt Martz) #19

@sanity Nice. I submitted the PR. I’ll keep an eye on your repo for future changes. At some point I want to add a method for arming/disarming the system, but that may get done using an arduino and smartshield and ANOTHER device type.

Like I said in the PR… I suck at formatting tiles. There could be a lot of improvement there


(Kent) #20

@mattmartz great thanks, I will take a look at the PR tomorrow.

So I have added (but not pushed code to Github yet) code to open/close my garage door from that same device panel I have been using (push to toggle door with open/opening/closing/closed pictures from the Smartthings icons), just tap either garage door to toggle.

I’m using a intermediary service called ThingSpeak (https://thingspeak.com), take a look at it, in particular the TalkBack App that they have available.

I’m using that to submit the event to open the garage door controller (a Rasp Pi with a dual relay device attached), the process is Put event on queue in Talkback from SmartApp then have Rasp Pi poll the queue until work shows up. Verify it and then toggle the garage door. This way my Rasp Pi and my AlarmServer code are not on the public internet, it’s just checking a private queue that only my account has access to.

You could do something similar for ARM/Disarm without exposing your NodeJS server to the internet which is something I was always trying to avoid with my server. Instead use the Talkback thing as a third party queue to do the dirty work for you.

-Kent