Service Manager with Device Type--help needed

Hello Community,
I’m getting really frustrated about the lack of Informations about how to create a Service Manager with a Device Handler. I really need someone who can help me out of that. So first of all I want to explain what my project is about. I want to integrate a Thermostat, that communicates with a cloud. I want that Smartthings just talks to the 3rd Party Cloud. For this I need a Service Manager. I created a Service Manager with all functions I need, and all work when I try them in the Initialize method. So the Functions are not the Problem, they work fine. The Problem is, now I got a Smart App ( The Service Manager ) with all functions. I also created a Device Handler with the Capability “Thermostat”.

Now.

  1. How do I make, that if I install the Service Manager and configurate my Device, that a Device Type gets created so I can Control the Device with the Tiles.
  2. How can I call the functions from the Service Manager in de Device Handler? I know that I must do it with “parent.function()” but I dont know how I connect the Service Manager with the Device Handler. Do I need to install both?
  3. How can I save the return Value of the Service Manager in a Attribute of the thermostat capability?

Im really frustrated and would be happy if someone could Help me.

Here is the code for the Service Manager:

////////////////////////////////////////
Code for the Device Type:
////////////////////////////////////////

/**

  • SmartThermostatDeviceHandler
  • Copyright 2017 Dany Van der Meij
  • 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: “SmartThermostatDeviceHandler”, namespace: “Dany Van der Meij”, author: “Dany Van der Meij”) {
capability “thermostat”
}
simulator {
// TODO: define status and reply messages here
}

tiles (scale: 2) {
	multiAttributeTile(name:"thermostatFull", type:"thermostat", width:6, height:4) {
		tileAttribute("device.temperature", key: "PRIMARY_CONTROL") {
    		attributeState("temp", label:'${currentValue}', unit:"dF", defaultState: true)
		}
		tileAttribute("device.heatingSetpoint", key: "VALUE_CONTROL") {
        	attributeState("VALUE_UP", action: "tempUp")
    		attributeState("VALUE_DOWN", action: "tempDown")
		}
		tileAttribute("device.humidity", key: "SECONDARY_CONTROL") {
   		 attributeState("humidity", label:'${currentValue}', unit:"%", defaultState: true)
		}
		tileAttribute("device.thermostatOperatingState", key: "OPERATING_STATE") {
    		attributeState("idle", backgroundColor:"#00A0DC")
    		attributeState("heating", backgroundColor:"#e86d13")
    		attributeState("cooling", backgroundColor:"#00A0DC")
		}
		tileAttribute("device.thermostatMode", key: "THERMOSTAT_MODE") {
    		attributeState("off", label:'${name}')
    		attributeState("heat", label:'${name}')
   	 		attributeState("cool", label:'${name}')
    		attributeState("auto", label:'${name}')
		}
		tileAttribute("device.heatingSetpoint", key: "HEATING_SETPOINT") {
    		attributeState("heatingSetpoint", label:'${currentValue}', unit:"dF", defaultState: true)
		}
		tileAttribute("device.coolingSetpoint", key: "COOLING_SETPOINT") {
    		attributeState("coolingSetpoint", label:'${currentValue}', unit:"dF", defaultState: true)
		}
	}
    
}

}

def installed(){
log.debug "Installed"
initialize()
}

def initialize(){
log.debug “Initialize”
}

def poll(){
log.debug “Executing poll”
}

private getUpdate(){

}

// parse events into attributes
def parse(Map description) {

}

/************** Betriebsfunktionen **************/
def auto()
{
log.debug “auto()”
}

def cool()
{
log.debug “cool()”
}

def emergencyHeat()
{
log.debug “emergencyHeat()”
}

def fanAuto()
{
log.debug “fanAuto()”
}

def fanCirculate()
{
log.debug “fanCirculate()”
}

def fanOn()
{
log.debug “fanOn()”
}

def heat()
{
log.debug “heat()”
}

def off()
{
log.debug “off()”
}

/************** Setter Methods **************/

def tempup()
{
log.debug “Tempup()”
}

def tempdown()
{
log.debug “Tempdown()”
}

def setCoolingSetpoint(Number setpoint)
{
log.debug “setCoolingSetpoint()”
}

def setHeatingSetpoint(Number setpoint)
{
log.debug “setHeatingSetpoint()”
}

def setThermostatFanMode(Enum fanmode)
{
log.debug “setThermostatFanMode()”
}

def setThermostatMode(Enum mode)
{
log.debug “setThermostatMode()”
}

/////////////////////////////////////////////////
Code for the Service Manager:
////////////////////////////////////////////////
/**

  • ClimatixIc (Connect)
  • Copyright 2017 Dany Van der Meij
  • 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.

*/

/****************************************/
/************************************** CONTENTS /
/
/

// 1) Librarys
// 2) Setter Methods
// 3) Preferences
// 4) Installed Method
// 5) Updated Method
// 6) Initialzie Method

//
/
/

// 7) Climatix API Calls

// 7.1) Getter Calls
// 7.1.1) GetClimatixToken()
// 7.1.2) GetRoomTemp()
// 7.1.3) GetRoomHumidity()
// 7.1.4) GetAirQuality()
// 7.1.5) GetOperatingMode()
// 7.1.6) GetRoomSetpoint()
// 7.1.7) GetComfortSetpoint()
// 7.1.8) GetEcoSetpoint()
// 7.1.9) GetPreComfortSetpoint()

// 7.2) Setter Calls
// 7.2.1) UpdateRoomOperatingMode()
// 7.2.2) UpdateRoomSetpoint()
// 7.2.3) UpdateComfortSetpoint()
// 7.2.4) UpdatePreComfortSetpoint()
// 7.2.5) UpdateEcoSetpoint()

//
/
/
/****************************************************************************************/

/********************************************/

// 1) Librarys
include 'localization’
include ‘asynchttp_v1’

/********************************************/

// 2) Definitions
definition(
name: “ClimatixIc (Connect)”,
namespace: “Dany3337”,
author: “Dany Van der Meij”,
description: "This is the Service Manager for ClimatixIc ",
category: “”,
iconUrl: “https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png”,
iconX2Url: “https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png”,
iconX3Url: “https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png”)

/********************************************/

// 3) Preferences
preferences {
input name: “climatix_username”, type: “text”, title: “Email”, description: “Enter Text”, required: true
input name: “climatix_password”, type: “text”, title: “Password”, description: “Enter Text”, required: true
input name: “climatix_device”, type: “text”, title: “Device”, description: “Enter Text”, required: true
}

/********************************************/

// 4) Installed Method
def installed() {
log.debug "Installed with settings: ${settings}"
initialize()
}

/********************************************/

// 5) Updated Method
def updated() {
log.debug "Updated with settings: ${settings}"
unsubscribe()
initialize()
}

/********************************************/

// 6) Initialize Method
def initialize() {
// Initialize Variables
state.ClimatixToken = ""
state.ClimatixUser = climatix_username
state.ClimatixPassword = climatix_password
state.ClimatixDevice = climatix_device
state.ClimatixKey = “2733467e26b541d7969f76f2e6dac966”

// Check if Token is already set
if(state.ClimatixToken == ""){
	// Get the Token
	getClimatixToken()
}
else{
	log.error "Token schon vorhanden"
}

// Methods
//UpdateRoomSetpoint(25)
//UpdateOperatingMode(1)
UpdateEcoSetpoint(16)
UpdatePrecomfortSetpoint(18)

getRoomTemp()
getRoomSetpoint()
getRoomHumidity()
getComfortSetpoint()
getEcoSetpoint()
getPreComfortSetpoint()
getRoomAirQuality()
getOperatingMode()

}

def pollChildren(){
def pollParams = [
uri: “https://api.thirdpartysite.com”,
path: “/device”,
requestContentType: “application/json”,
query: [format:“json”,body: jsonRequestBody]
]

httpGet(pollParams) 
{ resp ->
	state.devices = resp.data.devices 
    { collector, stat ->
    def dni = [ app.id, stat.identifier ].join('.')
    def data = [
    	attribute1: stat.attributeValue,
        attribute2: stat.attribute2Value
    ]
    collector[dni] = [data:data]
    return collector
    }
}

}

//
/
/
/********************************************/

// 7) Climtiax API Calls

//
/
/
/********************************************/

// 7.1) Getter Calls

//
/
/

// 7.1.1) GetClimatixToken | Function to get the API Token
def getClimatixToken(){
def bodystr = "grant_type=password&username=${state.ClimatixUser}&password=${state.ClimatixPassword}"
def Params = [
uri: “https://api.climatixic.com/Staging/Token”,
headers: [‘Ocp-Apim-Subscription-Key’: “${state.ClimatixKey}”],
body: “${bodystr}”
]
try {
httpPost(Params) { resp ->
// Set Token
state.ClimatixToken = “${resp.data.token_type} ${resp.data.access_token}”
}
} catch (e) {
log.error "Fehler bei getClimatixToken"
log.error “Fehler: ${e}”
}
}

/********************************************/

// 7.1.2) GetRoomTemp | Function to get the actual Room Temperature
def getRoomTemp(){
def Params = [
uri: “https://api.climatixic.com/Staging/Datapoints/${state.ClimatixDevice};1!002000086000055”,
headers: [‘Ocp-Apim-Subscription-Key’: “${state.ClimatixKey}”, Authorization: “${state.ClimatixToken}”]
]
try {
httpGet(Params) { resp ->
def Temperature = resp.data.values."${state.ClimatixDevice};1!002000086000055".value.value
log.debug “Actual Temperature: ${Temperature}”
}
} catch (e) {
log.error "SS Fehler bei der Anfrage"
log.error “Fehler: ${e}”
}
}

/********************************************/

// 7.1.3) GetRoomHumidity | Function to get Room Humidity
def getRoomHumidity(){
def Params = [
uri: “https://api.climatixic.com/Staging/Datapoints/${state.ClimatixDevice};1!002000085000055”,
headers: [‘Ocp-Apim-Subscription-Key’: “${state.ClimatixKey}”, Authorization: “${state.ClimatixToken}”]
]
try {
httpGet(Params) { resp ->
def Setpoint = resp.data.values."${state.ClimatixDevice};1!002000085000055".value.value
log.debug “Room Humidity: ${Setpoint}”
}
} catch (e) {
log.error "SS Fehler bei der Anfrage"
log.error “Fehler: ${e}”
}
}

/********************************************/

// 7.1.4) GetRoomAirQuality | Function to get Room Air Quality
def getRoomAirQuality(){
def Params = [
uri: “https://api.climatixic.com/Staging/Datapoints/${state.ClimatixDevice};1!0020000CD000055”,
headers: [‘Ocp-Apim-Subscription-Key’: “${state.ClimatixKey}”, Authorization: “${state.ClimatixToken}”]
]
try {
httpGet(Params) { resp ->
def quality = resp.data.values."${state.ClimatixDevice};1!0020000CD000055".value.value
log.debug “Room Air Quality: ${quality}”
}
} catch (e) {
log.error "SS Fehler bei der Anfrage"
log.error “Fehler: ${e}”
}
}

/********************************************/

// 7.1.5) GetRoomOperatingMode | Function to get Operating Mode
def getOperatingMode(){
def Params = [
uri: “https://api.climatixic.com/Staging/Datapoints/${state.ClimatixDevice};1!01300005A000055”,
headers: [‘Ocp-Apim-Subscription-Key’: “${state.ClimatixKey}”, Authorization: “${state.ClimatixToken}”]
]
try {
httpGet(Params) { resp ->
def mode = resp.data.values."${state.ClimatixDevice};1!01300005A000055".value.value
log.debug “Operating Mode: ${mode}”
}
} catch (e) {
log.error "SS Fehler bei der Anfrage"
log.error “Fehler: ${e}”
}
}

/********************************************/

// 7.1.6) GetRoomSetpoint | Function to get the Room Setpoint
def getRoomSetpoint(){
def Params = [
uri: “https://api.climatixic.com/Staging/Datapoints/${state.ClimatixDevice};1!002000083000055”,
headers: [‘Ocp-Apim-Subscription-Key’: “${state.ClimatixKey}”, Authorization: “${state.ClimatixToken}”]
]
try {
httpGet(Params) { resp ->
def Setpoint = resp.data.values."${state.ClimatixDevice};1!002000083000055".value.value
log.debug “Actual Setpoint: ${Setpoint}”
}
} catch (e) {
log.error "SS Fehler bei der Anfrage"
log.error “Fehler: ${e}”
}
}

/********************************************/

// 7.1.7) GetComfortSetpoint | Function to get Comfort Setpoint
def getComfortSetpoint(){
def Params = [
uri: “https://api.climatixic.com/Staging/Datapoints/${state.ClimatixDevice};1!00200007F000055”,
headers: [‘Ocp-Apim-Subscription-Key’: “${state.ClimatixKey}”, Authorization: “${state.ClimatixToken}”]
]
try {
httpGet(Params) { resp ->
def Setpoint = resp.data.values."${state.ClimatixDevice};1!00200007F000055".value.value
log.debug “Comfort Setpoint: ${Setpoint}”
}
} catch (e) {
log.error "SS Fehler bei der Anfrage"
log.error “Fehler: ${e}”
}
}

/********************************************/

// 7.1.8) GetEcoSetpoint | Function to get Eco Setpoint
def getEcoSetpoint(){
def Params = [
uri: “https://api.climatixic.com/Staging/Datapoints/${state.ClimatixDevice};1!002000080000055”,
headers: [‘Ocp-Apim-Subscription-Key’: “${state.ClimatixKey}”, Authorization: “${state.ClimatixToken}”]
]
try {
httpGet(Params) { resp ->
def Setpoint = resp.data.values."${state.ClimatixDevice};1!002000080000055".value.value
log.debug “Pre-Comfort Setpoint: ${Setpoint}”
}
} catch (e) {
log.error "SS Fehler bei der Anfrage"
log.error “Fehler: ${e}”
}
}

/********************************************/

// 7.1.9) GetPreComfortSetpoint | Function to get Pre-Comfort Setpoint
def getPreComfortSetpoint(){
def Params = [
uri: “https://api.climatixic.com/Staging/Datapoints/${state.ClimatixDevice};1!002000081000055”,
headers: [‘Ocp-Apim-Subscription-Key’: “${state.ClimatixKey}”, Authorization: “${state.ClimatixToken}”]
]
try {
httpGet(Params) { resp ->
def Setpoint = resp.data.values."${state.ClimatixDevice};1!002000081000055".value.value
log.debug “Economy Setpoint: ${Setpoint}”
}
} catch (e) {
log.error "SS Fehler bei der Anfrage"
log.error “Fehler: ${e}”
}
}

//
/
/

// 7.2) Setter Calls

//
/
/

// 7.2.1) SetOperatingMode | Function to Set the Operating Mode of the SmartThermostat

def UpdateOperatingMode(Pmode){
def bodystr = ‘{“value”:’+Pmode+’}'
log.debug “Bodystring: ${bodystr}“
def Params = [
uri: “https://api.climatixic.com/Staging/Datapoints/${state.ClimatixDevice};1!01300005A000055”,
headers: [‘Ocp-Apim-Subscription-Key’: “${state.ClimatixKey}”, Authorization: “${state.ClimatixToken}”, “Content-Type” : “application/json”],
body: bodystr
]
try {
httpPut(Params) { resp ->
log.debug “UpdateOperatingMode Status: ${resp.data.stateTexts.”${state.ClimatixDevice};1!01300005A000055”}”
}
} catch (e) {
log.error "SetSmartSetpoint Fehler bei der Anfrage"
log.error “Fehler: ${e}”
}
}

/********************************************/

// 7.2.2) SetRoomSetpoint | Function to set the Setpoint of the Room

def UpdateRoomSetpoint(Psetpoint){
def bodystr = ‘{“value”:’+Psetpoint+’}'
def Params = [
uri: “https://api.climatixic.com/Staging/Datapoints/${state.ClimatixDevice};1!002000083000055”,
headers: [‘Ocp-Apim-Subscription-Key’: “${state.ClimatixKey}”, Authorization: “${state.ClimatixToken}”, “Content-Type” : “application/json”],
body: bodystr
]
try {
httpPut(Params) { resp ->
log.debug “UpdateRoomSetpoint Status: ${resp.data.stateTexts.”${state.ClimatixDevice};1!002000083000055"}"
}
} catch (e) {
log.error "SetSmartSetpoint Fehler bei der Anfrage"
log.error “Fehler: ${e}”
}
}

/********************************************/

// 7.2.3) SetComfortSetpoint | Function to set the Setpoint of the Comfort Mode

def UpdateComfortSetpoint(Psetpoint){
def bodystr = ‘{“value”:’+Psetpoint+’}'
def Params = [
uri: “https://api.climatixic.com/Staging/Datapoints/${state.ClimatixDevice};1!002000083000055”,
headers: [‘Ocp-Apim-Subscription-Key’: “${state.ClimatixKey}”, Authorization: “${state.ClimatixToken}”, “Content-Type” : “application/json”],
body: bodystr
]
try {
httpPut(Params) { resp ->
log.debug “UpdateRoomSetpoint Status: ${resp.data.stateTexts.”${state.ClimatixDevice};1!002000083000055"}"
}
} catch (e) {
log.error "SetSmartSetpoint Fehler bei der Anfrage"
log.error “Fehler: ${e}”
}
}

/********************************************/

// 7.2.4) SetPreComfortSetpoint | Function to Set the Setpoint of the Precomfort Mode

def UpdatePrecomfortSetpoint(Psetpoint){
def bodystr = ‘{“value”:’+Psetpoint+’}'
def Params = [
uri: “https://api.climatixic.com/Staging/Datapoints/${state.ClimatixDevice};1!002000081000055”,
headers: [‘Ocp-Apim-Subscription-Key’: “${state.ClimatixKey}”, Authorization: “${state.ClimatixToken}”, “Content-Type” : “application/json”],
body: bodystr
]
try {
httpPut(Params) { resp ->
log.debug “UpdatePrecomfortSetpoint Status: ${resp.data.stateTexts.”${state.ClimatixDevice};1!002000081000055"}"
}
} catch (e) {
log.error "SetSmartSetpoint Fehler bei der Anfrage"
log.error “Fehler: ${e}”
}
}

/********************************************/

// 7.2.5) SetEcoSetpoint | Function to Set the Setpoint of the Ecomode

def UpdateEcoSetpoint(Psetpoint){
def bodystr = ‘{“value”:’+Psetpoint+’}'
def Params = [
uri: “https://api.climatixic.com/Staging/Datapoints/${state.ClimatixDevice};1!002000080000055”,
headers: [‘Ocp-Apim-Subscription-Key’: “${state.ClimatixKey}”, Authorization: “${state.ClimatixToken}”, “Content-Type” : “application/json”],
body: bodystr
]
try {
httpPut(Params) { resp ->
log.debug “UpdateEcoSetpoint Status: ${resp.data.stateTexts.”${state.ClimatixDevice};1!002000080000055"}"
}
} catch (e) {
log.error "SetSmartSetpoint Fehler bei der Anfrage"
log.error “Fehler: ${e}”
}
}

//
/
/

I would recommend you take a look of some of the other cloud-to-cloud thermostat integrations that already exist. These will provide you with a very good starting point.

From a high level, your SmartApp (Service Manager) must create the child device programatically. The Child Device will then have a relationship with its parent to allow it to call functions within the parent, and likewise the parent call call functions within the child. This is how data is exchanged between the Device Handler code and the Service Manager.

Other SmartApps, will interact directly with the Child Device.

Hopefully this helps to clarify the architecture.

It takes a while to figure out the SmartThings architecture, but once it clicks, you’ll see that it is a pretty nice, simple design that is very flexible.