WeMo Insight Switches

Anyone who managed to have their insight switch working and displaying the energy consumption data?

I’ve adapted the Smartthings sample code for Wemo Switches to work for Wemo Insight Switches. There are still a few problems with it. The code overrides the device handler for Wemo Switch but cannot deal with old (non-insight) Wemo Switches. Therefore, it is only suitable if you only have Wemo Insight Switches and no Wemo Switches.

The device handler adds a Power Meter capability and can respond to changes in energy consumption. I use it to monitor our washing machine and dryer in an outbuilding and send me a notification when they are done.

The device handler reports:

  • Energy consumption.
  • The correct on/off status of the device (previously, Wemo Insight Switches that were on but consumed little energy were reported as off.
  • Time on now
  • Time on today

To install the code,

  • Create or overwrite the Wemo Switch device handler with the code below.

  • Run the Wemo (Connect) SmartApp on your mobile device.

    /**

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

    • Wemo Switch

    • Author: superuser

    • Date: 2013-10-11

    • Updated for Wemo Insight Switch

    • Author: Jeroen Keppens, 14/3/2016
      */
      metadata {
      definition (name: “Wemo Switch”, namespace: “smartthings”, author: “SmartThings”) {
      capability “Actuator”
      capability “Switch”
      capability “Polling”
      capability “Power Meter”
      capability “Refresh”
      capability “Sensor”

      attribute "status", "string"
      attribute "onNow", "string"
      attribute "onToday", "string"
      
      command "subscribe"
      command "resubscribe"
      command "unsubscribe"
      

      }

      // simulator metadata
      simulator {}

      // UI 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"
      }
      valueTile(“power”, “device.power”, decoration: “flat”, width: 1, height: 1) {
      state “power”, label:’{currentValue} W' } standardTile("status", "device.status", width: 1, height: 1 ) { state( "on", label: 'ON', backgroundColor: "#79b821" ) state( "standby", label: 'STANDBY', backgroundColor: "#FFA500" ) state( "off", label: 'OFF', backgroundColor: "#ffffff" ) } valueTile("onNow", "device.onNow", decoration: "flat", width: 1, height: 1) { state "onNow", label:'{currentValue}’
      }
      valueTile(“onToday”, “device.onToday”, decoration: “flat”, width: 1, height: 1) {
      state “onToday”, label:’${currentValue}’
      }
      standardTile(“refresh”, “device.switch”, inactiveLabel: false, decoration: “flat”, width: 1, height: 1) {
      state “default”, label:’’, action:“refresh.refresh”, icon:“st.secondary.refresh”
      }

      main "switch"
      details (["switch", "power", "status", "onNow", "onToday", "refresh"])
      

      }
      }

    private def parseBinaryStateString(stateString) {
    //log.debug “stateString: stateString" //log.debug "stateString.size(): {stateString.size()}”
    def states =
    def token = “”
    for(int i=0; i<stateString.size(); i++) {
    if (stateString[i] ==~ /[0-9]/) {
    //log.debug “symbol: ${stateString[i]}”
    token = token + stateString[i]
    } else {
    //log.debug “adding token: $token”
    int state = token.toLong()
    states << state
    token = “”
    }
    }
    //log.debug “states: $states”
    return states
    }

    private def convertSecondsToTimeString(seconds) {
    log.debug “convertSecondsToTimeString of $seconds seconds”
    int h = seconds.intdiv(3600)
    int m = (seconds-h3600).intdiv(60)
    int s = seconds-(h
    3600)-(m*60)
    log.debug “h: $h, m: $m, s: $s”
    def timeString = “”
    if (h==0) {
    timeString = timeString + “00”
    } else if (h<=9) {
    timeString = timeString + “0$h”
    } else {
    timeString = timeString + “$h”
    }
    if (m==0) {
    timeString = timeString + “:00”
    } else if (m<=9) {
    timeString = timeString + “:0$m”
    } else {
    timeString = timeString + “:$m”
    }
    if (s==0) {
    timeString = timeString + “:00”
    } else if (s<=9) {
    timeString = timeString + “:0$s”
    } else {
    timeString = timeString + “:$s”
    }
    return timeString
    }

    // parse events into attributes
    def parse(String description) {
    log.debug “Parsing ‘${description}’”

      def msg = parseLanMessage(description)
      def headerString = msg.header
    
      if (headerString?.contains("SID: uuid:")) {
      	def sid = (headerString =~ /SID: uuid:.*/) ? ( headerString =~ /SID: uuid:.*/)[0] : "0"
      	sid -= "SID: uuid:".trim()
    
      	updateDataValue("subscriptionId", sid)
      }
    
      def result = []
      def bodyString = msg.body
      if (bodyString) {
      	def body = new XmlSlurper().parseText(bodyString)
          //log.info "Line 67 bodyString: $bodyString"
          //log.info "Line 68 body: $body"
    
      	if (body?.property?.TimeSyncRequest?.text()) {
      		log.trace "Got TimeSyncRequest"
      		result << timeSyncResponse()
      	} else if (body?.Body?.SetBinaryStateResponse?.BinaryState?.text()) {
      		log.trace "Got SetBinaryStateResponse = ${body?.Body?.SetBinaryStateResponse?.BinaryState?.text()}"
      	} else if (body?.property?.BinaryState?.text()) {
          // To Do: Refactor
              def wemoStateString = body?.property?.BinaryState?.text()
              log.info "State string: $wemoStateString"
              def states = parseBinaryStateString(wemoStateString)
              def value = states[0] == 0 ? "off" : "on"
              log.trace "Notify: BinaryState = ${value}"
              result << createEvent(name: "switch", value: value)
              if (states[0]==0) {
              	result << createEvent(name: "status", value: "off")
              } else if (states[0]==1) {
              	result << createEvent(name: "status", value: "on")
              } else {
              	result << createEvent(name: "status", value: "standby")
              }
              if (states[2] != null) {
              	log.debug "states[2]: ${states[2]}"
                  def onNow = convertSecondsToTimeString(states[2])
                  log.trace "Notify: Time on now = ${onNow}"
                  result << createEvent(name: "onNow", value: "on now  $onNow")
              }
              if (states[3] != null) {
              	log.debug "states[3]: ${states[3]}"
                  def onToday = convertSecondsToTimeString(states[3])
                  log.trace "Notify: Time on today = ${onToday}"
                  result << createEvent(name: "onToday", value: "on today $onToday")
              }
              if (states[7] != null) {
              	log.debug "states[7]: ${states[7]}"
              	def power = (int)Math.round(states[7]/1000)
              	log.trace "Notify: Current power consumption = ${power}"
              	result << createEvent(name: "power", value: power)
              }
      		//def value = body?.property?.BinaryState?.text().toInteger() == 1 ? "on" : "off"
      		//log.trace "Notify: BinaryState = ${value}"
      	} else if (body?.property?.TimeZoneNotification?.text()) {
      		log.debug "Notify: TimeZoneNotification = ${body?.property?.TimeZoneNotification?.text()}"
      	} else if (body?.Body?.GetBinaryStateResponse?.BinaryState?.text()) {
          	// To Do: Revise
      		def wemoStateString = body?.Body?.GetBinaryStateResponse?.BinaryState?.text()
              log.info "Binary State Response (not processed at this point): $wemoStateString"
      	}
      }
    
      result
    

    }

    private getTime() {
    // This is essentially System.currentTimeMillis()/1000, but System is disallowed by the sandbox.
    ((new GregorianCalendar().time.time / 1000l).toInteger()).toString()
    }

    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}"
      	}
      }
      log.debug "Using ip: ${ip} and port: ${port} for device: ${device.id}"
      return convertHexToIP(ip) + ":" + convertHexToInt(port)
    

    }

    def on() {
    log.debug “Executing ‘on’”
    sendEvent(name: “switch”, value: “on”)
    def turnOn = new physicalgraph.device.HubAction("""POST /upnp/control/basicevent1 HTTP/1.1
    SOAPAction: “urn:Belkin:service:basicevent:1#SetBinaryState”
    Host: ${getHostAddress()}
    Content-Type: text/xml
    Content-Length: 333

    <?xml version="1.0"?>

    <SOAP-ENV:Envelope xmlns:SOAP-ENV=“http://schemas.xmlsoap.org/soap/envelope/” SOAP-ENV:encodingStyle=“http://schemas.xmlsoap.org/soap/encoding/”>
    SOAP-ENV:Body
    <m:SetBinaryState xmlns:m=“urn:Belkin:service:basicevent:1”>
    1
    </m:SetBinaryState>
    </SOAP-ENV:Body>
    </SOAP-ENV:Envelope>""", physicalgraph.device.Protocol.LAN)
    }

    def off() {
    log.debug “Executing ‘off’”
    sendEvent(name: “switch”, value: “off”)
    def turnOff = new physicalgraph.device.HubAction("""POST /upnp/control/basicevent1 HTTP/1.1
    SOAPAction: “urn:Belkin:service:basicevent:1#SetBinaryState”
    Host: ${getHostAddress()}
    Content-Type: text/xml
    Content-Length: 333

    <?xml version="1.0"?>

    <SOAP-ENV:Envelope xmlns:SOAP-ENV=“http://schemas.xmlsoap.org/soap/envelope/” SOAP-ENV:encodingStyle=“http://schemas.xmlsoap.org/soap/encoding/”>
    SOAP-ENV:Body
    <m:SetBinaryState xmlns:m=“urn:Belkin:service:basicevent:1”>
    0
    </m:SetBinaryState>
    </SOAP-ENV:Body>
    </SOAP-ENV:Envelope>""", physicalgraph.device.Protocol.LAN)
    }

    /*def refresh() {
    log.debug “Executing ‘refresh’”
    new physicalgraph.device.HubAction("""POST /upnp/control/basicevent1 HTTP/1.1
    SOAPACTION: “urn:Belkin:service:basicevent:1#GetBinaryState”
    Content-Length: 277
    Content-Type: text/xml; charset=“utf-8”
    HOST: ${getHostAddress()}
    User-Agent: CyberGarage-HTTP/1.0

    <?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:GetBinaryState xmlns:u=“urn:Belkin:service:basicevent:1”>
    </u:GetBinaryState>
    </s:Body>
    </s:Envelope>""", physicalgraph.device.Protocol.LAN)
    }*/

    def refresh() {
    log.debug “Executing WeMo Switch ‘subscribe’, then ‘timeSyncResponse’, then ‘poll’”
    [subscribe(), timeSyncResponse(), poll()]
    }

    def subscribe(hostAddress) {
    log.debug “Executing ‘subscribe()’”
    def address = getCallBackAddress()
    new physicalgraph.device.HubAction("""SUBSCRIBE /upnp/event/basicevent1 HTTP/1.1
    HOST: {hostAddress} CALLBACK: <http://{address}/>
    NT: upnp:event
    TIMEOUT: Second-5400
    User-Agent: CyberGarage-HTTP/1.0

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

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

    ////////////////////////////
    //TODO: Use UTC Timezone
    def timeSyncResponse() {
    log.debug “Executing ‘timeSyncResponse()’”
    new physicalgraph.device.HubAction("""POST /upnp/control/timesync1 HTTP/1.1
    Content-Type: text/xml; charset=“utf-8”
    SOAPACTION: “urn:Belkin:service:timesync:1#TimeSync”
    Content-Length: 376
    HOST: ${getHostAddress()}
    User-Agent: CyberGarage-HTTP/1.0

    <?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:TimeSync xmlns:u=“urn:Belkin:service:timesync:1”>
    ${getTime()}
    -05.00
    1
    1
    </u:TimeSync>
    </s:Body>
    </s:Envelope>
    “”", physicalgraph.device.Protocol.LAN)
    }

    def poll() {
    log.debug “Executing ‘poll’”
    new physicalgraph.device.HubAction("""POST /upnp/control/basicevent1 HTTP/1.1
    SOAPACTION: “urn:Belkin:service:basicevent:1#GetBinaryState”
    Content-Length: 277
    Content-Type: text/xml; charset=“utf-8”
    HOST: ${getHostAddress()}
    User-Agent: CyberGarage-HTTP/1.0

    <?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:GetBinaryState xmlns:u=“urn:Belkin:service:basicevent:1”>
    </u:GetBinaryState>
    </s:Body>
    </s:Envelope>""", physicalgraph.device.Protocol.LAN)
    }

8 Likes

I’m trying to set this up, but it’s not reporting power data. The on/off function is working tho. Any idea how to fix this?

Is there a way to estimate the cost using this code? Like this device https://github.com/bigpunk6/device-type.AeonSmartEnergySwitch-

Installed this today, its awesome compared the stock Wemo Switch device handler. Thanks.

Can you create a GitHub like [RELEASE] Iris Smart Plug (3210-L) Zigbee Plug with Z-wave Repeater has for us to connect to? Makes monitoring for and getting updates much easier as well as allows submission of suggested improvements.

Better yet maybe just submit a MR to this one that people are already using: https://github.com/zzarbi/smartthings/tree/master/wemo

awesome! hope that some day this will support both standard and insight switches. I have both :frowning:

I’m getting the following error after adding switches…

physicalgraph.app.exception.UnknownDeviceTypeException: Device type ‘Wemo Switch’ in namespace ‘mujica’ not found. @ line 323

has anyone experienced this?

Thanks for this update. It’s working.

Thanks @bbodenmiller! This is really helpful.

[quote=“canadadry, post:59, topic:1704, full:true”]
I’m getting the following error after adding switches…

physicalgraph.app.exception.UnknownDeviceTypeException: Device type ‘Wemo Switch’ in namespace ‘mujica’ not found. @ line 323

Details:
-All WeMo items show up in the WeMo app
-Can’t add outlets with the Smart things app function find things
-Some outlets connect to the main network via a wireless repeater and the ones that do show the MAC address of the repeater not the device
-WeMo outlets both insight and regular are set to static IPs

It appears my wemo insight switches are now LAN Wemo Switches and this no longer works :frowning:

It is a known issue caused by who knows what. Mine are all not working also.

I’m not having issues with my Switch or Insight.
I have allocated mine fixed ip addresses when I first got them 18 months ago.
Do you guys who are having issues use static ip?
I’m just trying to help and make suggestions.

Sorry, I meant that the switch still works but it’s gone back to being just switch. The custom code allowed for power usage monitoring. It worked really well until the update and I had notifications when the power usage dropped between a certain wattage. Now all I can do is turn it on and off

Hmm… I just found this, and I went an changed the device type in the ide from lan Wemo switch to the device handler listed here and clicked save Seems to be working for that paricular insight

2 Likes

Uh duh. I’m an idiot. Thank you so much for that!! What an easy fix!! Thanks!!!

Running into some problems though, it’s not refreshing automatically the power usage. Trying to figure out core, with some minimal luck… if you get somewhere with that let us know,

Mine’s back to working as normal. Just ran the dryer for 20 seconds. I did press refresh to see the power usage. Then I turned the dryer off and walked away and got my notification that power usage had dropped below my set 7W down to 3W. All is well in the world again :stuck_out_tongue: