Control Particle Photon with Smartthings App

Hey, guys. I have a Photon on order. Care to post those examples, @bscuderi13?

1 Like

Oh sorry I just realized I forgot about you…my bad… To connect with smartthings you want to use some of the cloud functions that are described pretty well in the particle documentation. For example to control a relay. use the particle funtion

#define relaypin     D1   //defines the pin for easier reference

void setup() {
Particle.function("relay", relayfunc);  // register a cloud function called relay that runs the code for relayfunc
}
void loop() {
}
int relayfunc(String command) // runs when called from the Particle.function
{
// cloud trigger for relay on
  if (command == "1") 
    {   
      digitalWrite(relaypin, HIGH);
      return 1;
    }
// cloud trigger for relay off
  else 
    {               
      digitalWrite(relaypin, LOW);
      return 0;
    }
}

for your device handler in smartthings it would look like this

/**
 *  PHOTON RELAY
 *
 *  Author: bscuderi
 *  Date: 2017-06-07
 */
 
preferences {
    input("token", "text", title: "Access Token")
    input("deviceId", "text", title: "Device ID")
    }
 
 // for the UI
metadata {
	definition (name: "PhotonRelay", author: "bscuderi") {
    	capability "Switch"
	}

    // tile definitions
	tiles {
		standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
		state "on", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#79b821"
		state "off", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff"
		}

		main "switch"
		details "switch"
	}
}

def parse(String description) {
	log.error "This device does not support incoming events"
	return null
}

def on() {
	put '1'
    sendEvent(name: 'switch', value: 'on')
}

def off() {
	put '0'
    sendEvent(name: 'switch', value: 'off')
}

private put(relaystate) {
    //Particle API Call in here you need to put your function name that you want to run whatever you called particle.Function as uri: "https://api.particle.io/v1/devices/${deviceId}PUT THE NAME OF THE FUNCTION YOU WANT TO RUN HERE",
	httpPost(
		uri: "https://api.particle.io/v1/devices/${deviceId}/relay",
        body: [access_token: token, command: relaystate],  
	) {response -> log.debug (response.data)}
}

after you publish device handler make a device with your handler go into smartthings app and click on the settings icon on your new device. input your photons device id and access token and it should now work… If you want any more examples let me know… I have photons sending sensor data into smartthings as in temp sensors etc… I also have photons sending push notifications through smartthings…

Thanks, man. Mine arrives in a couple days, so I’ll stay playing with it then. I’ll check back in when I hit that level! :smiley: Thanks again!

Do you have example showing how the photon can send status to ST? Like if the switch turns on via the photon, how do push that update to ST? I’m guessing the device handler need to have a REST API endpoint for the photon to call?

yeah for that i use particle.Publish. Then in the particle console there is an option to send webhooks when an event is published. I then have that webhook activate a webcore piston to update switch position or refresh etc

Heres a purely photon project that I just posted for some inspiration for your pending photon delivery :slight_smile:
Automated Pool Controller

@bscuderi13 So I have your DH and .ino running. I can call functions from SmartThings (freaking awesome), but I can’t for the life of me figure out how to see the status of a pin on the Photon in ST. I’m using a Relay Shield to toggle a garage door, and I’d like to be able to see if the door is open or closed from a microswitch that I have mounted on the door.

Thanks for the help so far!

Hey sorry I just noticed this but you could do a digital read of the pin state and store that to a particle variable that is called for kind of like the part of my automated pool controller device handler. I’ve posted that device handler below. See if you can comb through it and make sense of it but in a nutshell I used the fake minimote as a template to make this device handler and added more buttons and a couple of tiles that show the current state of the device ie power useage and rpm setting from the photon. You would want to model what I did with the rpm or power use parts as those just ask for the value of a particle variable whenever I refresh. I also have it refresh after I hit any button as well. If this doesn’t make sense or help you let me know and I can make something with all the fat trimmed off of it.

/**
 *  Copyright 2015 SmartThings
 *
 *  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.
 *
 */
preferences {
    input name: "deviceId", type: "text", title: "Device ID", required: true
    input name: "token", type: "password", title: "Access Token", required: true
    input name: "RPMVar", type: "text", title: "RPM Variable", required: true, defaultValue: "PumpRPM"
    input name: "wattVar", type: "text", title: "WATT Variable", required: true, defaultValue: "PowerUse"
    }
metadata {
	definition (name: "Simulated Pool Controller", namespace: "brennonsapps", author: "bscuderi") {
		capability "Actuator"
		capability "Button"
		capability "Sensor"
        attribute "RPM", "number"
       	capability "Power Meter"
        capability "Polling"
        capability "Refresh"
        capability "Temperature Measurement"
        attribute "temperature", "number"
        

        command "push1"
        command "push2"
        command "push3"
        command "push4"
        command "push5"
        command "push6"
        command "push7"
        command "push8"
	}

	simulator {
		status "button 1 pushed":  "command: 2001, payload: 01"
		status "button 5 pushed":  "command: 2001, payload: 15"
		status "button 2 pushed":  "command: 2001, payload: 29"
		status "button 6 pushed":  "command: 2001, payload: 3D"
		status "button 3 pushed":  "command: 2001, payload: 51"
		status "button 7 pushed":  "command: 2001, payload: 65"
		status "button 4 pushed":  "command: 2001, payload: 79"
		status "button 8 pushed":  "command: 2001, payload: 8D"
		status "wakeup":  "command: 8407, payload: "
	}
	tiles {
		     
		 valueTile("RPM", "device.RPM", width: 2, height: 1) {
            state("RPM", label:'${currentValue} RPM')
        }
         valueTile("WATT", "device.power", width: 1, height: 1) {
            state("power", label:'${currentValue} Watts')
        }
        standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width: 1, height: 1) {
            state "default", action:"refresh.refresh", icon:"st.secondary.refresh"
        }

        standardTile("button", "device.button") {
			state "default", label: "", icon: "st.unknown.zwave.remote-controller", backgroundColor: "#ffffff"
		}
 		standardTile("push1", "device.button", width: 1, height: 1, decoration: "flat") {
			state "default", label: "OFF", backgroundColor: "#ffffff", action: "push1"
		}
 		standardTile("push2", "device.button", width: 1, height: 1, decoration: "flat") {
			state "default", label: "1,000 RPM", backgroundColor: "#ffffff", action: "push2"
		}
 		standardTile("push3", "device.button", width: 1, height: 1, decoration: "flat") {
			state "default", label: "1,250 RPM", backgroundColor: "#ffffff", action: "push3"
		}
 		standardTile("push4", "device.button", width: 1, height: 1, decoration: "flat") {
			state "default", label: "1,500 RPM", backgroundColor: "#ffffff", action: "push4"
		}
 			standardTile("push5", "device.button", width: 1, height: 1, decoration: "flat") {
			state "default", label: "2,000 RPM", backgroundColor: "#ffffff", action: "push5"
		}
 		standardTile("push6", "device.button", width: 1, height: 1, decoration: "flat") {
			state "default", label: "2,500 RPM", backgroundColor: "#ffffff", action: "push6"
		}
 			standardTile("push7", "device.button", width: 1, height: 1, decoration: "flat") {
			state "default", label: "3,000 RPM", backgroundColor: "#ffffff", action: "push7"
		}
 		standardTile("push8", "device.button", width: 1, height: 1, decoration: "flat") {
			state "default", label: "3,450 RPM", backgroundColor: "#ffffff", action: "push8"
		}

		main (["RPM","WATT","button",])
		details(["RPM","WATT","push1","push2","push3","push4","push5","push6","push7","push8","refresh"])
	
       
		}

}

def parse(String description) {
    def pair = description.split(":")

    createEvent(name: pair[0].trim(), value: pair[1].trim())
}

def push1() {
    put(0)
    push(1)
    refresh()
}

def push2() {
	put(1)
    push(2)
    refresh()
}

def push3() {
	put(2)
    push(3)
    refresh()
}

def push4() {
    put(3)
    push(4)
    refresh()
}

def push5() {
	put(4)
    push(5)
    refresh()
}

def push6() {
	put(5)
    push(6)
    refresh()
}

def push7() {
	put(6)
    push(7)
    refresh()
}

def push8() {
	put(7)
    push(8)
    refresh()
}

private push(button) {
	log.debug "$device.displayName button $button was pushed"
	sendEvent(name: "button", value: "pushed", data: [buttonNumber: button], descriptionText: "$device.displayName button $button was pushed", isStateChange: true)
}


def installed() {
	initialize()
}

def updated() {
	initialize()
    // attempted fix
    log.debug "Updated !"
    state.RPM = 1    
    log.debug "device.RPM: ${device.RPM}"
}

def initialize() {
	sendEvent(name: "numberOfButtons", value: 8)
}


def poll() {
    log.debug "Executing 'poll'"
    getWATT()
    getRPM()
}

def refresh() {
    log.debug "Executing 'refresh'"
    getWATT()
    getRPM()
}
    


// get pool pump RPM
private getRPM() {
    def closure = { response ->
        log.debug "RPM request was successful, $response.data"

        sendEvent(name: "RPM", value: response.data.result)
    }

    httpGet("https://api.particle.io/v1/devices/${deviceId}/${RPMVar}?access_token=${token}", closure)
}
private getWATT() {
    def closure = { response ->
        log.debug "WATT request was successful, $response.data"

        sendEvent(name: "power", value: response.data.result)
    }

    httpGet("https://api.particle.io/v1/devices/${deviceId}/${wattVar}?access_token=${token}", closure)
}

// Send a button push to pump
private put(button) {
	httpPost(
		uri: "https://api.particle.io/v1/devices/${deviceId}/PumpSpeed",
        body: [access_token: token, command: button],  
	) {response -> log.debug (response.data)}
}

Here’s what this looks like in action you can see the rpm and power use from the device in my tiles so I know what state the photon is actually in.

Thanks, @bscuderi13!

Can you post your .ino for the pool controller as well so I can see how you set your functions up on that end? I can see my variables with the Particle app and IFTTT, so I know they’re floating around out there, but I can’t seem to get them to show up on SmartThings.

This is what I have so far:
ST:
https://github.com/jaredshearer/GarageControl/blob/master/GarageControl.groovy

Particle:
https://github.com/jaredshearer/GarageControl/blob/master/GarageControl.ino

I’m so noob that I don’t know how to do that fancy github embedding!

This is my first swing at coding, so be gentle. :slight_smile: It’s engaging, but it’s also maddening! “Why won’t you work!?”

sure bud no problem! Make no mistake all these photon smartthings projects are also my first attempt at coding too. I’m not a programmer but pilot by trade so I’m probably horrible about explaining what I have done. I can post my whole thing if you want but there is alot of extra crap in there it might be easy to lose what I have done for the part your interested in especially since I havent commented it the best. So with that in mind instead I will post a small example program that would do nothing but what you have asked for.

int microswitch = D0;   // define a pin for the microswitch
String garageContact = "unknown"; // on startup set the garageContact status to unknown and define the vaiable

void setup() {
Particle.variable("GarageStatus", garageContact);  // register a particle cloud variable called garage status with the value of the garageContact variable

pinMode(microswitch, INPUT_PULLDOWN);  // set pin on microswitch as input with the pulldown resistor engaged
}

void loop() {
int status = digitalRead(microswitch); // read the microswitch pin and store the value to status
if (status == HIGH){                   // check status if its high store open in the garageContact string
String garageContact = "open";
}
if (status == LOW){                   // check status if its low store closed in the garageContact string 
String garageContact = "closed";
}
}

its the particle variable that you would call with the smartthings app. If you look at my example for the pool controller you see my blurb that asks the photon for the variable and it would look like this to send for our sample “GarageStatus” variable…

httpGet("https://api.particle.io/v1/devices/yourdeviceID/GarageStatus?access_token=youraccessToken", closure)

if its not clear as mud or doesnt work let me know as we should be able to put our feeble crappy coding minds together and get something to work :wink:

okay I didnt look at your code before I sent mine but it looks like you have done essentially the same thing but I had a hard time finding an error. I would look on the particle photon console on the app and see if your particle variable show up correctly there. If they are then we at least know that the error is on the smarttings side of things reflecting the changes. Its difficult for me to troubleshoot since I cant see what your seeing when you control the device. From there the only thing I can think of is I used different names for my particle variable and the variable that stored the data for the particle variable. Im not sure if that matters or not maybe it’s confused what its asking for since they share the same name??

Third thought the more I look at it I wonder if it is working but you just think its not… When I look at your code it says you basically push a button and pass that info to particle to open or close your garage and then refresh which calls your get state functions. Well if you push a button the garage is going to start to close and it refreshes immediately but the garage takes a bit to close so the contact isn’t going to show closed for 30 seconds or however long the garage takes to close. By then you have already refreshed and it still shows the incorrect state unless you were to refresh again. It wont reffresh on its own you need some way to poll it. If this is what is happening you could combat that with your particle publishes you have in your code you could have a webhook fire when you publish the event that fires a webcore funtion to refresh the device. I use that alot in some of my devices. To easily fire a webhook when that event triggers go on the particle console and you will see create a webhook.

Okay so I decided to put some of the theory to test since I had a spare photon laying around. I did have some errors in my sample code Ill post the new ones here… But heres a video of my demo running

.ino

int microswitch = D0;   // define a pin for the microswitch
String garageContact = "unknown"; // on startup set the garageContact status to unknown and define the vaiable

void setup() {
Particle.variable("GarageStatus", garageContact);  // register a particle cloud variable called garage status with the value of the garageContact variable

pinMode(microswitch, INPUT_PULLDOWN);  // set pin on microswitch as input with the pulldown resistor engaged
}

void loop() {
int status = digitalRead(microswitch); // read the microswitch pin and store the value to status
if (status == HIGH){                   // check status if its high store open in the garageContact string
garageContact = "open";
}
if (status == LOW){                   // check status if its low store closed in the garageContact string 
garageContact = "closed";
}
}

groovy device handler

/**
 * sample garage status
 *
 * Author: Brennon Scuderi
 *
 * 
 *
 * 
 */

preferences {
    input name: "deviceId", type: "text", title: "Device ID", required: true
    input name: "token", type: "password", title: "Access Token", required: true
    input name: "GarageStatus", type: "text", title: "Garage Status Variable Name", required: true, defaultValue: "GarageStatus"
    }

metadata {
    definition (name: "sample garage status", author: "Brennon Scuderi") {
        capability "Polling"
        capability "Sensor"
        capability "Refresh"
        attribute "garagestatus", "string"
    }

    tiles(scale: 2) {
        valueTile("GarageStatus", "device.garagestatus", width: 4, height: 4) {
            state("garagestatus", label:'${currentValue}')
        }

      
        standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
            state "default", action:"refresh.refresh", icon:"st.secondary.refresh"
        }


    }
}


// create a new attribute called garagestatus
def updated() {
	initialize()
    // attempted fix
    log.debug "Updated !"
    state.garagestatus = 1    
    log.debug "device.garagestatus: ${device.garagestatus}"
}

def poll() {
    log.debug "Executing 'poll'"

    getStatus()
}

def refresh() {
    log.debug "Executing 'refresh'"

    getStatus()
}


// no clue what this does or if its needed
def parse(String description) {
    def pair = description.split(":")

    createEvent(name: pair[0].trim(), value: pair[1].trim())
}

// ask smartthings for the garage status
private getStatus() {
    def closure = { response ->
        log.debug "request was successful, $response.data"

        sendEvent(name: "garagestatus", value: response.data.result)
    }

    httpGet("https://api.particle.io/v1/devices/${deviceId}/${GarageStatus}?access_token=${token}", closure)
}
2 Likes

Dude. Above and beyond. Thanks for all of that. I’m implementing it now!

1 Like

@baconface The only thing I would for sure add to my code is a particle publish on a state change, (Which it looks like you had in yours), and a webcore piston that refreshes the status when you send a particle publish. That way it automatically shows the correct state from your sensor. To do that go to the console and click here. Make a webhook for when your event is published

For the url put the link found here in your piston. This url will execute your piston. So your piston doesn’t need any if statements just an action. IE refresh garage contact.

1 Like

I can confirm that your code works when I start from scratch with it, but I’m having problems integrating it into my code. I’m sure I’m missing something dumb. I’ll play with it more in the next day or so (and at a bigger screen!).

Thanks again, man!

1 Like

YES. I need to play with webhooks. I’ll also look at this in the next day or so.

The publish on state change was…wonky. I was getting integers of 8 instead of 0 or 1 sometimes. I think it was bouncing and giving 1 repeatedly, and they were being added together. It was odd. I didn’t have the _PULLDOWN after my INPUT though, so that may help. I am pretty sure that the state change code that I put in there is pretty basic, but I pulled that out of my ass and was pretty proud of it…right up to the point that it didn’t work!

@baconface Yeah the pin “floats” by default so it can bounce between high and low the pull down makes sure it stays low unless you add voltage so you don’t get undesired changes or vise versa you can pull it to high if you want depending on how the componant works your adding so you will definitely want that in your code. Also mechanical switches like a contact sensor “bounce” when they contact meaning they can go briefly back and forth between high and low really fast when they first change state. So depending on the accuracy you need for the state change you could add a capacitor to filter out the bounce by making it take a few milliseconds to actually charge up and deplete or try and do it with code too. Look up like a debounce filter code there should be examples out there. I don’t know how to do it just that you can or I’d help you. I ran into a similar issue with my particle photon mailbox but came across these two different solutions From members on their forums. Basically my mailbox would sometimes trigger an open event both when I opened and real quick when I closed it too. I decided for my application I didn’t care cause I just had my mailbox sensor only notify me on the first opening of the day anyways so I decided not to care.

Success!

It turned out that the issue was with reusing the same device in SmartThings. I made a whole new device, pointed it to the DH, and it worked. Weird.

I combined the individual state and command tiles into one for each door, added some icons and a splash of color, and updated my github with the working copy.

TO DO: Webhooks and/or(???) state change code with publishing.

Here is a screenshot of ST:

1 Like