Control Particle Photon with Smartthings App


#1

I have a Particle Photon and a Smartthings Hub. I would like to start simple and set a pin HIGH or LOW on the Photon using the Smartthings App. In other words, a simple switch. I’ve scoured the internet for examples and tried a bunch such as the blind servo motor example but haven’t been able to figure it out. What should the Particle Code look like and how should I set up the handler and device in Smartthings IDE?


#2

Welcome! Several people have done work with particle photon devices. Probably the fastest way to find them is to look on the quick browse lists in the community – created wiki, check the device type handler section, and look on the “miscellaneous” list which is at the very end of that section. You should find several topics on particle photon handlers that should give you a good starting point. :sunglasses:

http://thingsthataresmart.wiki/index.php?title=How_to_Quick_Browse_the_Community-Created_SmartApps_Forum_Section


( Cosmo) #3

@bscuderi13 wasn’t it a photon that you used on all the pool stuff and the squeezebox?


(Bscuderi13) #4

Yeah it’s not too bad to do. I’ve got several devices that a switch in smartthings does stuff in the photon. I’ll post you some examples in a minute…


(Jared) #5

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


(Bscuderi13) #6

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…


(Jared) #7

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!


#8

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?


(Bscuderi13) #9

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


(Bscuderi13) #10

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


(Jared) #11

@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!


(Bscuderi13) #12

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)}
}

(Bscuderi13) #13

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.


(Jared) #14

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!?”


(Bscuderi13) #15

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:


(Bscuderi13) #16

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??


(Bscuderi13) #17

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.


(Bscuderi13) #18

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)
}

(Jared) #19

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


(Bscuderi13) #20

@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.