Wemo switch and insight smart plug


#61

Hmmm...good point. Well, the wall switches should show up as lights, but the outlets should be outlets. Possibly I just need to declare an additional capability.

On the other hand, Alexa (at least mine) manages WeMo devices natively, so you probably don't need to export them from Hubitat. I just end up with duplicate devices when I do.


#62

I want the wemo switches to be controlled by rule machine automations so I thought it best to control them by alexa through hubitat rather than directly so that the control and therefore status was all done in hubitat. That way I hope to avoid any status being out of sych while polling takes place.

If you can fix it great, otherwise I'll take a look or control them directly as you do.

Thanks


#63

I understand where you're coming from. I had exactly the same issue with Hue lights recently when the new Hubitat Alexa integration stopped showing them to Alexa. I had been having Alexa control my Hues through Hubitat rather than directly through the Hue hub to avoid the update lag in Hubitat.

Luckily the WeMo devices don't poll -- they push updates to Hubitat, so they should always be up-to-date whether you're controlling them with an external service like Alexa or manually flipping the switch on the wall. That said, I'll still try to take a look at this at some point.


#64

Ok so now I have the WeMo Insight working for on/off. Any way to get the power consumption to register correctly?


#65

Did you load all the device drivers? I'm still stuck in the loop of it saying it discovered my Insights but it never creates them when it click done.


#66

I just installed the insight driver as I only have the one device. I use it to monitor power from the tv to control lights in the living room.


#67

@jason0x43

Any thoughts surrounding the power information not registering?


#68

Is the issue that the power value never changes, or that it's not in a human-readable format?


#69

Never changes


#70

I pushed an update to the Insight driver with more detailed logging. Install that, toggle the switch, and see what the logs say. You should see a GetInsightParamsResponse message go by at some point; that's where energy usage would come from.


#71

@jason0x43

Ok now the energy is changing but to be honest it isn't usable. Power of 483265 is in the logs but my iOS app shows 120W being used as an example.
See the logs:

dev:3442018-09-14 13:20:58.332:traceparse()

dev:3442018-09-14 13:20:29.693:traceparse()

dev:3442018-09-14 13:20:28.690:traceparse()

dev:3442018-09-14 13:20:27.172:traceparse()

dev:3442018-09-14 13:20:17.679:traceCreating power event for 483265267

dev:3442018-09-14 13:20:17.677:traceCreating energy event for 164983

dev:3442018-09-14 13:20:17.676:traceEvent params: [1, 1536949216, 0, 0, 249790, 1209600, 117, 99485, 164983, 483265267]

dev:3442018-09-14 13:20:17.674:traceCreating binary state event for 1

dev:3442018-09-14 13:20:17.672:traceGot BinaryState notification: 1|1536949216|0|0|249790|1209600|117|99485|164983|483265267

dev:3442018-09-14 13:20:17.637:traceparse()

dev:3442018-09-14 13:19:57.988:traceparse()

dev:3442018-09-14 13:19:54.953:traceparse()

dev:3442018-09-14 13:19:53.835:traceCreating power event for 483265057

dev:3442018-09-14 13:19:53.833:traceCreating energy event for 164773

dev:3442018-09-14 13:19:53.832:traceEvent params: [8, 1536896687, 5998, 0, 249789, 1209600, 118, 220, 164773, 483265057]

dev:3442018-09-14 13:19:53.831:traceCreating binary state event for 8

dev:3442018-09-14 13:19:53.830:traceGot BinaryState notification: 8|1536896687|5998|0|249789|1209600|118|220|164773|483265057

dev:3442018-09-14 13:19:53.826:traceScheduling resubscription in 270 s

dev:3442018-09-14 13:19:53.823:traceUpdating subscriptionId to c6d35c76-1dd1-11b2-8ea2-f7603d7b1453

dev:3442018-09-14 13:19:53.809:traceparse()

dev:3442018-09-14 13:19:53.793:traceparse()

dev:3442018-09-14 13:19:53.743:traceEvent params: [8]

dev:3442018-09-14 13:19:53.743:traceCreating binary state event for 8

dev:3442018-09-14 13:19:53.742:traceGot GetBinaryResponse: 8

dev:3442018-09-14 13:19:53.685:traceparse()

dev:3442018-09-14 13:19:53.564:debugpoll()

dev:3442018-09-14 13:19:53.466:debugrefresh()

dev:3442018-09-14 13:19:52.964:traceparse()

dev:3442018-09-14 13:19:51.884:traceCreating power event for 483265057

dev:3442018-09-14 13:19:51.882:traceCreating energy event for 164773

dev:3442018-09-14 13:19:51.882:traceEvent params: [8, 1536896687, 5998, 0, 249789, 1209600, 118, 215, 164773, 483265057]

dev:3442018-09-14 13:19:51.881:traceCreating binary state event for 8

dev:3442018-09-14 13:19:51.880:traceGot BinaryState notification: 8|1536896687|5998|0|249789|1209600|118|215|164773|483265057

dev:3442018-09-14 13:19:51.823:traceparse()

dev:3442018-09-14 13:19:51.816:traceScheduling resubscription in 270 s

dev:3442018-09-14 13:19:51.814:traceUpdating subscriptionId to c5a414ee-1dd1-11b2-8ea2-f7603d7b1453

dev:3442018-09-14 13:19:51.802:traceparse()

dev:3442018-09-14 13:19:51.771:traceEvent params: [8]

dev:3442018-09-14 13:19:51.770:traceCreating binary state event for 8

dev:3442018-09-14 13:19:51.769:traceGot GetBinaryResponse: 8

dev:3442018-09-14 13:19:51.746:traceparse()

dev:3442018-09-14 13:19:51.708:debugpoll()

dev:3442018-09-14 13:19:51.668:debugrefresh()

dev:3442018-09-14 13:19:50.939:traceparse()

dev:3442018-09-14 13:19:50.446:traceScheduling resubscription in 270 s

dev:3442018-09-14 13:19:50.443:traceUpdating subscriptionId to c4ce64fc-1dd1-11b2-8ea2-f7603d7b1453

dev:3442018-09-14 13:19:50.439:traceCreating power event for 483265057

dev:3442018-09-14 13:19:50.437:traceCreating energy event for 164773

dev:3442018-09-14 13:19:50.436:traceEvent params: [8, 1536896687, 5998, 0, 249789, 1209600, 118, 190, 164773, 483265057]

dev:3442018-09-14 13:19:50.435:traceCreating binary state event for 8

dev:3442018-09-14 13:19:50.434:traceGot BinaryState notification: 8|1536896687|5998|0|249789|1209600|118|190|164773|483265057

dev:3442018-09-14 13:19:50.418:traceparse()

dev:3442018-09-14 13:19:50.415:traceparse()

dev:3442018-09-14 13:19:50.367:traceEvent params: [8]

dev:3442018-09-14 13:19:50.365:traceCreating binary state event for 8

dev:3442018-09-14 13:19:50.364:traceGot GetBinaryResponse: 8

dev:3442018-09-14 13:19:50.306:traceparse()

dev:3442018-09-14 13:19:50.230:debugpoll()

dev:3442018-09-14 13:19:50.014:debugrefresh()


#72

That's fine. The main question was whether energy usage data was making it to the driver. Formatting it into something human-friendly is the easy part. :slight_smile:


#73

Actually, it looks like I'm probably just using the wrong parameter in the response for the power usage.


#74

Let me know when you have an update and I can test for you. :slight_smile:


#75

@jason0x43

Any updates?


#76

Sorry, nothing yet. I’ll see about make an update sometime today or this evening.


#77

Ok, I updated the Insight driver to report instantaneous power in Watts and daily energy use in Watt-hours. Give that a try.


#78

Where is this in the code? It still doesn’t match up with what the wemo says is current wattage being used. Maybe I can mess with it.


#79

There are two power-related events being created when a message comes in, one for daily energy and one for instaneous power. That happens near the bottom of the driver source.

What is it reporting for power now, and what does the WeMo app say it should be? Unfortunately the message format isn’t well documented, and I don’t have an Insight switch, so I’m piecing together information from various open source drivers. :slight_smile:


#80

I found it and fixed it. Both the energy and WATT usage calculations were incorrect. Here is the updated code working correctly.

/**
 * WeMo Insight Switch driver
 *
 * Author: Jason Cheatham
 * Last updated: 2018-09-20, 21:27:31-0400
 *
 * Based on the original Wemo Switch driver by Juan Risso at SmartThings,
 * 2015-10-11.
 *
 * 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.
 */

metadata {
definition(
    name: 'Wemo Insight Switch',
    namespace: 'jason0x43',
    author: 'Jason Cheatham'
) {
    capability 'Actuator'
    capability 'Switch'
    capability 'Polling'
    capability 'Refresh'
    capability 'Sensor'
    capability 'Power Meter'
    capability 'Energy Meter'

    command 'subscribe'
    command 'unsubscribe'
    command 'resubscribe'
}
}

def on() {
log.debug 'on()'
parent.childSetBinaryState(device, '1')
}

def off() {
log.debug 'off()'
parent.childSetBinaryState(device, '0')
}

def parse(description) {
log.trace 'parse()'

def msg = parseLanMessage(description)

def subscriptionData = parent.getSubscriptionData(msg)
if (subscriptionData != null) {
    log.trace "Updating subscriptionId to ${subscriptionData.sid}"
    updateDataValue('subscriptionId', subscriptionData.sid)
    log.trace "Scheduling resubscription in ${subscriptionData.timeout} s"
    runIn(subscriptionData.timeout, resubscribe)
}

def result = []
def bodyString = msg.body
if (bodyString) {
    try {
        unschedule('setOffline')
    } catch (e) {
        log.error 'unschedule("setOffline")'
    }

    def body = new XmlSlurper().parseText(bodyString)

    if (body?.property?.TimeSyncRequest?.text()) {
        log.trace 'Got TimeSyncRequest'
        result << timeSyncResponse()
    } else if (body?.Body?.SetBinaryStateResponse?.BinaryState?.text()) {
        def rawValue = body.Body.SetBinaryStateResponse.BinaryState.text()
        log.trace "Got SetBinaryStateResponse: ${rawValue}"
        result += createStateEvents(rawValue)
    } else if (body?.property?.BinaryState?.text()) {
        def rawValue = body.property.BinaryState.text()
        log.trace "Got BinaryState notification: ${rawValue}"
        result += createStateEvents(rawValue)
    } else if (body?.property?.TimeZoneNotification?.text()) {
        log.debug "Got TimeZoneNotification: Response = ${body.property.TimeZoneNotification.text()}"
    } else if (body?.Body?.GetBinaryStateResponse?.BinaryState?.text()) {
        def rawValue = body.Body.GetBinaryStateResponse.BinaryState.text()
        log.trace "Got GetBinaryResponse: ${rawValue}"
        result += createStateEvents(rawValue)
    } else if (body?.Body?.GetInsightParamsResponse?.InsightParams?.text()) {
        def rawValue = body.Body.GetInsightParamsResponse.InsightParams.text()
        log.trace "Got GetInsightParamsResponse: ${rawValue}"
        result += createStateEvents(rawValue)
    }
}

result
}

def poll() {
log.debug 'poll()'
if (device.currentValue('switch') != 'offline') {
    runIn(30, setOffline)
}
parent.childGetBinaryState(device)
}

def refresh() {
log.debug 'refresh()'
parent.childRefresh(device)
}

def resubscribe() {
log.debug 'resubscribe()'
runIn(10, subscribeIfNecessary)
parent.childResubscribe(device)
}

def scheduleResubscribe(timeout) {
runIn(resubscribeTimeout, resubscribe)
}

def setOffline() {
sendEvent(
    name: 'switch',
    value: 'offline',
    descriptionText: 'The device is offline'
)
}

def subscribe() {
log.debug 'subscribe()'
parent.childSubscribe(device)
}

def subscribeIfNecessary() {
log.trace 'subscribeIfNecessary'
parent.childSubscribeIfNecessary(device)
}

def sync(ip, port) {
log.debug 'sync()'
parent.childSync(device, ip, port)
}

def timeSyncResponse() {
log.debug 'Executing timeSyncResponse()'
parent.childTimeSyncResponse(device)
}

def unsubscribe() {
log.debug 'unsubscribe()'
parent.childUnsubscribe(device)
}

def updated() {
log.debug 'Updated'
refresh()
}

private createBinaryStateEvent(rawValue) {
// Insight switches actually support 3 values:
//   0: off
//   1: on
//   8: standby
// We consider 'standby' to be 'on'.
log.trace "Creating binary state event for ${rawValue}"
def value = rawValue == '0' ? 'off' : 'on';
createEvent(
    name: 'switch',
    value: value,
    descriptionText: "Switch is ${value}"
)
}

private createEnergyEvent(rawValue) {
log.trace "Creating energy event for ${rawValue}"
def value = (rawValue.toDouble() / 60000000).round(2)
createEvent(
    name: 'energy',
    value: value,
    descriptionText: "Energy today is ${value} WH"
)
}

private createPowerEvent(rawValue) {
log.trace "Creating power event for ${rawValue}"
def value = Math.round(rawValue.toInteger() / 1000)
createEvent(
    name: 'power',
    value: value,
    descriptionText: "Power is ${value} W"
)
}

// A state event will probably look like:
//   8|1536896687|5998|0|249789|1209600|118|190|164773|483265057
// Fields are:
//   8            on/off 
//   1536896687   last changed at (UNIX timestamp)
//   5998         last on for (seconds?)
//   0            on today
//   249789       on total
//   1209600      window (seconds) over which onTotal is aggregated
//   110          average power (Watts)
//   190          current power (Watts?)
//   164773       energy today (mW mins)
//   483265057    energy total (mW mins)
private createStateEvents(stateString) {
def params = stateString.split('\\|')
log.trace "Event params: ${params}"
def events = []
events << createBinaryStateEvent(params[0])
events << createPowerEvent(params[7])
events << createEnergyEvent(params[8])
return events
}