Holmes Large Room Smart Heater with WeMo & SmartThings


(Jason Anderson) #1

Wasn’t sure of the best place for this, hope this is it. I’m thinking about getting a Holmes Large Room Smart Heater with WeMo. I was curious if this might be able to connect with SmartThings in some way. Does anyone know if this does or if it will in the future if not currently? Or maybe if there is some community code that has been created for this integration. Thanks.


(Kevin Tierney) #2

It won’'t work right out of the box, but based on my experiences adding a WeMo Crockpot on my own, it isn’t too hard to add a new WeMo device to ST. I’m an amateur programmer at best and it took me a couple of days and i was able to get the crockpot working. The challenge with that device, which is similar to what I had to deal with on the Crockpot is that it’s more then just an on/off device, so you’ll have to implement those other functionalities (High/Low/Eco and Temp)


(Tyler Lucas) #3

Did you ever get this working? I am looking at doing the same thing due to a room that is always much colder and only has one radiator in it.


(Matt) #4

Bump. I got one of the Wemo Heaters and so far have been very impressed. I really want to integrate it with my motion/temp sensor though. Anyone have luck with integrating it into SmartThings?


#5

I too have a Holmes WeMo heater I would love to be able to auto set frost protect if no motion in room for say 3 hours. Has anyone done any work on a custom device handler for one of these?


(Tim McClain) #6

Anyone get anywhere on this?


(Jason Anderson) #7

Nope kind of gave up on it.


#8

bring up and old post here but anyone ever developed a DTH for this?


#9

So I started working on a DT for this and made some progress. It seems I can only perform GetAttributes. I haven’t figure out why the SetAttributes isn’t working which mean the DT is only good for viewing the status of the heater (might be a good thing anyway just to ONLY view the status of the heater). This is not pretty, I hacked together the code using @kevintierney and @btk. Maybe actual coder can take a look and complete it for us :slight_smile:

Device Type:

/**
 *  Holmes Smart Humidifier With WeMo
 *
 *  Author:  cuboy29  with special thanks to and code from Kevin Tierney and Brian Keifer.
 *  Version: 0.0.1
 */
metadata {
	definition (name: "Wemo Heater", namespace: "cuboy29", author: "Smartthings") {
		capability "Actuator"
		capability "Polling"
		capability "Refresh"
		capability "Temperature Measurement"
		capability "Switch"

        attribute "Mode", "string"
        attribute "Temperature", "number"
        attribute "SetTemperature", "number"


        command "Low"
        command "High"
        command "Eco"
        command "raiseSetpoint"
        command "lowerSetpoint"


	}

	simulator {
		// TODO: define status and reply messages here
	}

	tiles (scale: 2){
    
        multiAttributeTile(name: "status", type:"generic", width:6, height:4, canChangeIcon: true) {
            tileAttribute ("device.Temperature", key: "PRIMARY_CONTROL") {            		
                attributeState("Temperature", label:'${currentValue}',icon:"st.Appliances.appliances11", backgroundColors:[
            		[value: 0, color: "#ff0000"],
            		[value: 20, color: "#ffff00"],
            		[value: 40, color: "#00ff00"],
            		[value: 60, color: "#00ffff"],
            		[value: 80, color: "#0000ff"],
            		[value: 100, color: "#ff00ff"]
            	]
            )}
            tileAttribute("device.SetTemperature", key: "SECONDARY_CONTROL") {
                attributeState("default", label:'Current Setpoint: ${currentValue}')
                
            }
            tileAttribute("device.SetTemperature", key: "VALUE_CONTROL") {
             	attributeState("default", label:'Set Temperature: ${currentValue}')
                attributeState "VALUE_UP", action: "raiseSetpoint"
                attributeState "VALUE_DOWN", action: "lowerSetpoint"
            }
        }
       
        standardTile("low", "device.Mode", decoration: "flat", canChangeBackground: true, width: 2, height: 2) {
        	state "default", label: 'LOW', action: "fanOff", icon:"st.Appliances.appliances11"
            state "Low", label: 'LOW', action: "fanOff", icon:"st.Appliances.appliances11",backgroundColor:"#79b821"
        }

        standardTile("eco", "device.Mode", decoration: "flat", canChangeBackground: true, width: 2, height: 2) {
        	state "default", label: 'ECO', action: "fanOff", icon:"st.Appliances.appliances11"
            state "Eco", label: 'ECO', action: "fanOff", icon:"st.Appliances.appliances11",backgroundColor:"#79b821"
        }

        standardTile("high", "device.Mode", decoration: "flat", canChangeBackground: true, width: 2, height: 2) {
        	state "default", label: 'HIGH', action: "fanOff", icon:"st.Appliances.appliances11"
            state "High", label: 'HIGH', action: "fanOff", icon:"st.Appliances.appliances11",backgroundColor:"#79b821"
        }
        standardTile("frostprotect", "device.Mode", decoration: "flat", canChangeBackground: true, width: 2, height: 2) {
        	state "default", label: 'FROST', action: "fanOff", icon:"st.Appliances.appliances11"
            state "FrostProtect", label: 'FROST', action: "fanOff", icon:"st.Appliances.appliances11",backgroundColor:"#79b821"
        }
		standardTile("refresh", "device.switch", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
			state("default", action:"refresh.refresh", icon:"st.secondary.refresh")
		}
        
        main (["status"])
        details (["status", "high", "low", "eco", "frostprotect", "refresh"])
	}
}


def parse(String description) {

    def evtMessage = parseLanMessage(description)
    def evtHeader = evtMessage.header
    def evtBody = evtMessage.body

	if (evtBody) {
		evtBody = evtBody.replaceAll(~/&/, "&")
    	evtBody = evtBody.replaceAll(~/&lt;/, "<")
    	evtBody = evtBody.replaceAll(~/&gt;/, ">")
	}

    // log.debug("Header: ${evtHeader}")
    // log.debug("Body: ${evtBody}")

    if (evtHeader?.contains("SID: uuid:")) {
		def sid = (evtHeader =~ /SID: uuid:.*/) ? ( evtHeader =~ /SID: uuid:.*/)[0] : "0"
		sid -= "SID: uuid:".trim()
        log.debug "Subscription updated!  New SID: ${sid}"
    	updateDataValue("subscriptionId", sid)
    }

    if (evtBody) {
        log.debug("evtBody: ${evtBody}")
        def body = new XmlSlurper().parseText(evtBody)
        if (body == 0) {
            log.debug ("Command succeeded!")
            return [getAttributes()]
        } else {
            log.debug("ELSE!: ${body.Body}")
            try {
                def matchResponse = body.Body =~ /Mode(\d)Temperature([\d\.]+)SetTemperature([\d\.]+)AutoOffTime(\d)RunMode(\d)TimeRemaining(\d)WemoDisabled(\d)TempUnit(\d)/
                log.debug("mFM: ${matchResponse[0]}")
                def result = []
                def Mode
                def Temperature = matchResponse[0][2]
                def SetTemperature = matchResponse[0][3]
                def autoOffTime = matchResponse[0][4]
                def RunMode = matchResponse[0][5]
                def TimeRemaining = matchResponse[0][6]

                switch(matchResponse[0][1].toInteger()) {
				case 0:
                        Mode = "Off"
                        break
                    case 1:
                        Mode = "FrostProtect"
                        break
                    case 2:
                        Mode = "High"
                        break
                    case 3:
                        Mode = "Low"                        
                        break
                    case 4:
                        Mode = "Eco"                        
                        break
                }
                
				log.debug "current mode is: " + Mode
                log.debug "current temperature is: " + Temperature
                log.debug "current settemperature is: " +  SetTemperature
                log.debug "autoOffTime is: " + autoOffTime
                log.debug "RunMode is: " + RunMode
                log.debug "TimeRemaining is: " + TimeRemaining
                result += createEvent(name: "Temperature", value:matchResponse[0][2])
                result += createEvent(name: "SetTemperature", value:matchResponse[0][3])
                result += createEvent(name: "Mode", value:Mode)

                return result
            } catch (e) {
                log.debug("Exception ${e}")
            }
        }
    }
}



void raiseSetpoint() {

	def currentTemperature = device.currentValue("SetTemperature")
    log.debug "testttttt " + device.currentValue("Temperature")
    
    def newTemperature = currentTemperature + 1.0
     
    log.debug "new Setpoint " + newTemperature
     
    updateDeviceData(newTemperature)
	setNewTemperature(newTemperature)
    
}


def updateDeviceData(newTemperature) {

	sendEvent(name: "SetTemperature", value: newTemperature)
    log.debug "testing " + device.currentValue("SetTemperature")
}




def setNewTemperature(newTemp) {
	log.debug "Setting new temperature setpoint of: " + newTemp
	setAttribute("SetTemperature", newTemp)
}


void lowerSetpoint() {

	log.debug "Lower Setpoint"
    
    def currentTemperature = device.currentValue("SetTemperature")
    log.debug "testttttt " + device.currentValue("Temperature")
    
    def newTemperature = currentTemperature - 1.0
     
    log.debug "new Setpoint " + newTemperature
     
    updateDeviceData(newTemperature)
    setNewTemperature(newTemperature)

}


private getCallBackAddress() {
    device.hub.getDataValue("localIP") + ":" + device.hub.getDataValue("localSrvPortTCP")
}


private Integer convertHexToInt(hex) {
    Integer.parseInt(hex,16)
}


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


private getHostAddress() {
    def ip = getDataValue("ip")
    def port = getDataValue("port")

    if (!ip || !port) {
        def parts = device.deviceNetworkId.split(":")
        if (parts.length == 2) {
            ip = parts[0]
            port = parts[1]
        } else {
            //log.warn "Can't figure out ip and port for device: ${device.id}"
        }
    }

    ip = convertHexToIP(ip)
    port = convertHexToInt(port)
    return ip + ":" + port
}


private postRequest(path, SOAPaction, body) {
    // Send  a post request
    def result = new physicalgraph.device.HubAction([
        'method': 'POST',
        'path': path,
        'body': body,
        'headers': [
        'HOST': getHostAddress(),
        'Content-type': 'text/xml; charset=utf-8',
        'SOAPAction': "\"${SOAPaction}\""
        ]
        ], device.deviceNetworkId)
        log.debug result
    return result
}


def poll() {
	refresh()
}

def modeOff(){
	def newMode = 0
	setAttribute("Mode", newMode)
}

def modeOn(){
	def newMode = 1
	setAttribute("Mode", newMode)
}

def modeLow(){
	def newMode = 3
	setAttribute("Mode", newMode)
}


def modeHigh(){
	def newMode = 2
	setAttribute("Mode", newMode)
}
def modeEco(){
	def newMode = 4
	log.debug "set ECO"
	setAttribute("Mode", newMode)
}


/*

def setAttribute(name, value) {
    log.debug("setAttributes()")
	def body = """
	<?xml version="1.0" encoding="utf-8"?>
	<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
	<s:Body>
	<u:SetAttributes xmlns:u="urn:Belkin:service:deviceevent:1">
	<attributeList>&lt;attribute&gt;&lt;name&gt;${name}&lt;/name&gt;&lt;value&gt;${value}&lt;/value&gt;&lt;/attribute&gt;</attributeList>
	</u:SetAttributes>
	</s:Body>
	</s:Envelope>
	"""
	postRequest('/upnp/control/deviceevent1', 'urn:Belkin:service:deviceevent:1#SetAttributes', body)
    log.debug body
}

*/

def setAttribute(name, value) {
	log.trace "setAttribute called"
    def bodyMap = [mode:"SetTemperature",Temp:value]
	//sendEvent(name: "cookMode", value:"warm" )
    sendCommand('/upnp/control/deviceevent1','urn:Belkin:service:deviceevent1:1','SetAttributes',bodyMap)
}



def getAttributes() {
    log.debug("getAttributes()")
    def body = """
    <?xml version="1.0" encoding="utf-8"?>
    <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
    <s:Body>
    <u:GetAttributes xmlns:u="urn:Belkin:service:deviceevent:1">
    </u:GetAttributes>
    </s:Body>
    </s:Envelope>
    """
    postRequest('/upnp/control/deviceevent1', 'urn:Belkin:service:deviceevent:1#GetAttributes', body)
}


def sendCommand(path,urn,action,body){
	log.debug "Send command called with path: ${path} , urn: ${urn}, action: ${action} , body: ${body}"
	 def result = new physicalgraph.device.HubSoapAction(
        path:    path,
        urn:     urn,
        action:  action,
        body:    body,
        headers: [Host:getHostAddress(), CONNECTION: "close"]
    )
    return result
}


def refresh() {
    //log.debug "Executing WeMo Switch 'subscribe', then 'timeSyncResponse', then 'poll'"
    log.debug("Refresh requested!")
	subscribe()

    getAttributes()
    
    
    
    //poll()
}


private subscribeAction(path, callbackPath="") {
    log.trace "subscribe($path, $callbackPath)"
    def address = getCallBackAddress()
	log.debug("address: ${address}")
    def ip = getHostAddress()

    def result = new physicalgraph.device.HubAction(
        method: "SUBSCRIBE",
        path: path,
        headers: [
            HOST: ip,
            CALLBACK: "<http://${address}/>",
            NT: "upnp:event",
            TIMEOUT: "Second-28800"
        ]
    )

    log.trace "SUBSCRIBE $path"
    log.trace "RESULT: ${result}"
    result
}

def subscribe(hostAddress) {
    log.debug "Subscribing to ${hostAddress}"
    subscribeAction("/upnp/event/basicevent1")
}


def subscribe() {
    subscribe(getHostAddress())
}


def subscribe(ip, port) {
    def existingIp = getDataValue("ip")
    def existingPort = getDataValue("port")
    if (ip && ip != existingIp) {
        log.debug "Updating ip from $existingIp to $ip"
        updateDataValue("ip", ip)
    }
    if (port && port != existingPort) {
        log.debug "Updating port from $existingPort to $port"
        updateDataValue("port", port)
    }

    subscribe("${ip}:${port}")
}


def resubscribe() {
    //log.debug "Executing 'resubscribe()'"
    def sid = getDeviceDataByName("subscriptionId")

    new physicalgraph.device.HubAction("""SUBSCRIBE /upnp/event/basicevent1 HTTP/1.1
    HOST: ${getHostAddress()}
    SID: uuid:${sid}
    TIMEOUT: Second-5400


    """, physicalgraph.device.Protocol.LAN)

}


def unsubscribe() {
    def sid = getDeviceDataByName("subscriptionId")
    new physicalgraph.device.HubAction("""UNSUBSCRIBE publisher path HTTP/1.1
    HOST: ${getHostAddress()}
    SID: uuid:${sid}


    """, physicalgraph.device.Protocol.LAN)
}

Connect App:

/**
*  WeMo Heater (Connect)
*
*  v:0.0.1 - Initial Version
*
*  Pending: Name, Refresh/Pool time
*
*  Based on the Holmes Humidifier code by Brian Keifer and Rudimar Prunzel
*
*  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.
*
**/

definition(
    name: "WeMo Heater (Connect)",
    namespace: "cuboy29",
    author: "cuboy29",
    description: "Allows you to integrate your Holmes Smart Heater WeMo with SmartThings.",
    category: "Convenience",
    iconUrl: "https://s3.amazonaws.com/smartapp-icons/Partner/wemo.png",
    iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Partner/wemo@2x.png",
    iconX3Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png"
)

preferences {
    page(name:"firstPage", title:"WeMo Heater Device Setup", content:"firstPage", uninstall: true)
}

def getVersion() {
	return "0/.0.1" 
}

private discoverAllWemoTypes()
{
    sendHubCommand(new physicalgraph.device.HubAction("lan discovery urn:Belkin:device:HeaterA:1", physicalgraph.device.Protocol.LAN))
}

private getFriendlyName(String deviceNetworkId) {
    sendHubCommand(new physicalgraph.device.HubAction("""GET /setup.xml HTTP/1.1
HOST: ${deviceNetworkId}

""", physicalgraph.device.Protocol.LAN, "${deviceNetworkId}"))
}

private verifyDevices() {
    def Heaters = getWemoHeaters().findAll { it?.value?.verified != true }

    Heaters.each {
        getFriendlyName((it.value.ip + ":" + it.value.port))
    }
}

def firstPage()
{
    if(canInstallLabs())
    {
        int refreshCount = !state.refreshCount ? 0 : state.refreshCount as int
            state.refreshCount = refreshCount + 1
        def refreshInterval = 5

        //log.debug "REFRESH COUNT :: ${refreshCount}"

        if(!state.subscribe) {
            subscribe(location, null, locationHandler, [filterEvents:false])
            state.subscribe = true
        }

        //ssdp request every 25 seconds
        if((refreshCount % 5) == 0) {
            discoverAllWemoTypes()
        }

        //setup.xml request every 5 seconds except on discoveries
        if(((refreshCount % 1) == 0) && ((refreshCount % 5) != 0)) {
            verifyDevices()
        }


        def HeatersDiscovered = HeatersDiscovered()

        return dynamicPage(name:"firstPage", title:"Discovery Started!", nextPage:"", refreshInterval: refreshInterval, install:true, uninstall: selectedHeaters != null) {
            section("Select a device...") {
                input "selectedHeaters", "enum", required:false, title:"Select Wemo Heater\n(${HeatersDiscovered.size() ?: 0} found)", multiple:true, options:HeatersDiscovered
            }
            section("Options:") {
                input(name: "refreshTime", title: "Refresh Time (Minutes: 1 - 60)", type: "number", range: "01..60", required: true)
            	input "detailDebug", "bool", title: "Enable Debug logs", defaultValue: false, submitOnChange: true
                input
                label(title: "Assign a name (Optional)", required: false)
            }
            section ("Version " + "${getVersion()}") { }
        }
    }
    else
    {
        def upgradeNeeded = """To use SmartThings Labs, your Hub should be completely up to date.

To update your Hub, access Location Settings in the Main Menu (tap the gear next to your location name), select your Hub, and choose "Update Hub"."""

        return dynamicPage(name:"firstPage", title:"Upgrade needed!", nextPage:"", install:false, uninstall: true) {
            section("Upgrade") {
                paragraph "$upgradeNeeded"
            }
        }
    }
}

def devicesDiscovered() {
    def Heaters = getWemoHeaters()
    def list = []
    list = Heaterst{ [app.id, it.ssdpUSN].join('.') }
}


def HeatersDiscovered() {
    def Heaters = getWemoHeaters().findAll { it?.value?.verified == true }
    def map = [:]
    Heaters.each {
        //def value = it.value.name ?: "Wemo Heater ${it.value.ssdpUSN.split(':')[1][-3..-1]}"
        def value = "Wemo Heater ${it.value.mac}"
        def key = it.value.mac
        map["${key}"] = value
    }
    map
}

def getWemoHeaters()
{
    if (!state.Heaters) { state.Heaters = [:] }
    state.Heaters
}

def installed() {
    log.info "--- Installed with settings: ${settings}"
    initialize()
}

def updated() {
    log.info "--- Updated with settings: ${settings}"
    initialize()
}

def initialize() {
    log.info "--- WeMo Heater (Connect) - Version: ${getVersion()}"

    // remove location subscription afterwards
    unsubscribe()
    state.subscribe = false

    // Verify and Add Selected devices
    if (selectedHeaters)
    {
        addHeaters()
        runIn(5, "subscribeToDevices") //initial subscriptions delayed by 5 seconds
    }
}

def resubscribe() {
    log.info "--- Resubscribe called, delegating to refresh()"
    refresh()
}

def refresh() {
    log.info "--- Refresh Devices"
    refreshDevices()
}

def refreshDevices() {
    if (settings.detailDebug) log.debug "refreshDevices() called"
    def devices = getAllChildDevices()
    devices.each { d ->
        log.info "Calling refresh() on device: ${d.id}"
        d.refresh()
    }
}

def subscribeToDevices() {
    if (settings.detailDebug) log.debug "subscribeToDevices() called"
    def devices = getAllChildDevices()
    devices.each { d ->
        d.subscribe()
    }

    // Schedule Refresh & Device Sync
    int refreshMin = settings.refreshTime ? settings.refreshTime : 5
    String refreshSchedule = "0 0/" + refreshMin.toString() + " * 1/1 * ? *"
    schedule(refreshSchedule, "refresh")
    //int refreshSec = refreshMin * 60
    //runIn(refreshSec, "refresh")

    schedule("0 0/30 * 1/1 * ? *", "doDeviceSync")  // setup ip:port syncing every 30 minutes
    // runIn(900, "doDeviceSync") //setup ip:port syncing every 15 minutes

}
def addHeaters() {
    def Heaters = getWemoHeaters()

    selectedHeaters.each { dni ->
        def selectedHeater = Heaters.find { it.value.mac == dni } ?: switches.find { "${it.value.ip}:${it.value.port}" == dni }
        def d
        if (selectedHeater) {
            d = getChildDevices()?.find {
                it.dni == selectedHeater.value.mac || it.device.getDataValue("mac") == selectedHeater.value.mac
            }
        }

        if (!d) {
            log.info "Creating WeMo Heater with DNI: ${selectedHeater.value.mac}"
            log.info "IP: ${selectedHeater.value.ip} - PORT: ${selectedHeater.value.port}"
            d = addChildDevice("cuboy29", "WeMo Heater", selectedHeater.value.mac, selectedHeater?.value.hub, [
                "label": "WeMo Heater ${selectedHeater.value.mac}",
                "data": [
                    "mac": selectedHeater.value.mac,
                    "ip": selectedHeater.value.ip,
                    "port": selectedHeater.value.port
                ]
            ])

            log.info "Created ${d.displayName} with id: ${d.id}, dni: ${d.deviceNetworkId}"
        } else {
            if (settings.detailDebug) log.debug "found ${d.displayName} with id $dni already exists"
        }
    }
}

def locationHandler(evt) {
    def description = evt.description
    def hub = evt?.hubId
    def parsedEvent = parseDiscoveryMessage(description)
    parsedEvent << ["hub":hub]
    if (settings.detailDebug) log.debug("PARSED: ${parsedEvent}")
    if (parsedEvent?.ssdpTerm?.contains("Belkin:device:HeaterA")) {
        if (settings.detailDebug) log.debug("FOUND WeMo Heater")
        def Heaters = getWemoHeaters()

        if (!(Heaters."${parsedEvent.ssdpUSN.toString()}"))
        { //if it doesn't already exist
            if (settings.detailDebug) log.debug ("Creating Heaters")
            Heaters << ["${parsedEvent.ssdpUSN.toString()}":parsedEvent]
        }
        else
        { // just update the values

            if (settings.detailDebug) log.debug "Device was already found in state..."

            def d = Heaters."${parsedEvent.ssdpUSN.toString()}"
            boolean deviceChangedValues = false

            if(d.ip != parsedEvent.ip || d.port != parsedEvent.port) {
                log.info "Device's port or ip changed..."
                log.info("old ip: ${d.ip}")
                log.info("new ip: ${parsedEvent.ip}")
                log.info("old port: ${d.port}")
                log.info("new port: ${parsedEvent.port}")
                d.ip = parsedEvent.ip
                d.port = parsedEvent.port
                deviceChangedValues = true
            }

            if (deviceChangedValues) {
                def children = getChildDevices()
                if (settings.detailDebug) log.debug "Found children ${children}"
                children.each {
                    if (it.getDeviceDataByName("mac") == parsedEvent.mac) {
                        log.info "updating ip and port, and resubscribing, for device ${it} with mac ${parsedEvent.mac}"
                        it.subscribe(parsedEvent.ip, parsedEvent.port)
                    }
                }
            }

        }

    }


    else if (parsedEvent.headers && parsedEvent.body) {

        def headerString = new String(parsedEvent.headers.decodeBase64())
        def bodyString = new String(parsedEvent?.body?.decodeBase64())
        def body = new XmlSlurper().parseText(bodyString)

        if (body?.device?.deviceType?.text().startsWith("urn:Belkin:device:HeaterA:1"))
        {
            def Heaters = getWemoHeaters()
            def wemoHeater = Heaters.find {it?.key?.contains(body?.device?.UDN?.text())}
            if (wemoHeater)
            {
                wemoHeater.value << [name:body?.device?.friendlyName?.text(), verified: true]
            }
            else
            {
                log.error "/setup.xml returned a wemo device that didn't exist"
            }
        }


    }
}

private def parseDiscoveryMessage(String description) {
    def device = [:]
    def parts = description?.split(',')
    parts.each { part ->
        part = part.trim()
        if (part.startsWith('devicetype:')) {
            def valueString = part.split(":")[1].trim()
            device.devicetype = valueString
        }
        else if (part.startsWith('mac:')) {
            def valueString = part.split(":")[1].trim()
            if (valueString) {
                device.mac = valueString
            }
        }
        else if (part.startsWith('networkAddress:')) {
            def valueString = part.split(":")[1].trim()
            if (valueString) {
                device.ip = valueString
            }
        }
        else if (part.startsWith('deviceAddress:')) {
            def valueString = part.split(":")[1].trim()
            if (valueString) {
                device.port = valueString
            }
        }
        else if (part.startsWith('ssdpPath:')) {
            def valueString = part.split(":")[1].trim()
            if (valueString) {
                device.ssdpPath = valueString
            }
        }
        else if (part.startsWith('ssdpUSN:')) {
            part -= "ssdpUSN:"
            def valueString = part.trim()
            if (valueString) {
                device.ssdpUSN = valueString
            }
        }
        else if (part.startsWith('ssdpTerm:')) {
            part -= "ssdpTerm:"
            def valueString = part.trim()
            if (valueString) {
                device.ssdpTerm = valueString
            }
        }
        else if (part.startsWith('headers')) {
            part -= "headers:"
            def valueString = part.trim()
            if (valueString) {
                device.headers = valueString
            }
        }
        else if (part.startsWith('body')) {
            part -= "body:"
            def valueString = part.trim()
            if (valueString) {
                device.body = valueString
            }
        }
    }

    device
}

def doDeviceSync(){
    log.info "--- Verifying Devices"
    if(!state.subscribe) {
        subscribe(location, null, locationHandler, [filterEvents:false])
        state.subscribe = true
    }

    discoverAllWemoTypes()
}

def pollChildren() {
    def devices = getAllChildDevices()
    devices.each { d ->
        //only poll switches?
        d.poll()
    }
}

def delayPoll() {
    if (settings.detailDebug) log.debug "Executing 'delayPoll'"

    runIn(5, "pollChildren")
}



private Boolean canInstallLabs()
{
    return hasAllHubsOver("000.011.00603")
}

private Boolean hasAllHubsOver(String desiredFirmware)
{
    return realHubFirmwareVersions.every { fw -> fw >= desiredFirmware }
}

private List getRealHubFirmwareVersions()
{
    return location.hubs*.firmwareVersionString.findAll { it }
}

(Matthews Family) #10

Any update?


#11

Sadly no… haven’t work on it for awhile.


(Matthews Family) #12

Could you send me what you got and I will add to finish it up.


#13

everything I have is in the post above :slight_smile:


(Matthews Family) #14

Thanks. Will create a SmartApp for it.


(Matthews Family) #15

Almost done. I have changed some of the information. I am able to adjust from High, Low, Eco & FrostProtect. I am working on the SetTemperature now. That one is giving me a little bit of the a fit. It does appear it is handing the information off but the heater is not getting it.


(Matthews Family) #16

I know the problem but not really sure how to fix it. When I’m setting a temperature for SetTemperature, it is requiring a XX number and I am sending XX.X number. Does anyone know how to stop sending a decimal number. I am just wanting a whole number.

Wemo apparently does NOT like decimal numbers.


(Brent R.) #17

Can you use something like ‘Integer.parseInt()’ as in the sample here to force an integer value? Just a guess - not my area of expertise.

http://docs.smartthings.com/en/latest/ref-docs/device-handler-ref.html

Or what happens if you pass it as a string? If Wemo is dumb enough to not be able to handle decimals, perhaps it’s also dumb enough to not know the difference between numbers and strings.


(Matthews Family) #18

Thanks for that. Tried that AND tried for hardcode a XX number into place and it still isn’t passing through. It really doesn’t make sense because it I am able to adjust the mode but not the setTemperature even though they run off the same setAttributes command.

Back to the drawing board I guess.


#19

I ran into the same issue :slight_smile:


(Matthews Family) #20

I ran into an issue with the SmartApp not discovering. Main issue was the label being HeaterB:1 not HeaterA:1.

None of the Modes were communicating correctly. I fixed all those, it’s just those dang SetTemperature that I can’t figure out.