Plugin - WeMo Switch

Python and python framework
Post Reply
mivo
Posts: 119
Joined: Friday 21 April 2017 8:58
Target OS: Raspberry Pi
Domoticz version: beta
Location: Czechia
Contact:

Plugin - WeMo Switch

Post by mivo » Sunday 13 August 2017 16:36

Hi all,

I am using my simple plugin for WeMo Switch for some time, now I am releasing in publicly. Feel free to test it and send me a comments etc. Plugin and Readme is available on GitHub:

https://github.com/milanvo/domoticz-plu ... emo-switch
My toys:
Raspberry Pi 3 + UPS PIco HV3.0 A Stack
Minibian (Raspbian Jessie) + Domoticz beta
RFLink 433 Gateway, 1wire DS18B20 temp sensors (GPIO)
RaZberry module + 2x Comet Z-Wave + Z-wave socket
---
Plugins: WeMo Switch, UPS PIco HV3.0A on GitHub

andi216
Posts: 56
Joined: Tuesday 14 March 2017 14:10
Target OS: Raspberry Pi
Domoticz version: 3.7243
Contact:

Re: Plugin - WeMo Switch

Post by andi216 » Wednesday 16 August 2017 20:30

@mivo
Thanks for the plugin. It works very well.
(Rasp B + v3.8153 and Rasp V2 v3.8312).
In the future can change the plugin to add new Wemo devices (similar to Ping device)?

mivo
Posts: 119
Joined: Friday 21 April 2017 8:58
Target OS: Raspberry Pi
Domoticz version: beta
Location: Czechia
Contact:

Re: Plugin - WeMo Switch

Post by mivo » Wednesday 16 August 2017 21:25

Hi,

thank you for feedback. I thought about more addresses for WeMo switches, Python plugins has limited options for configuration parameters. Ping hardware has sub-page for setup - native device in C++ code. Do you have more WeMo for testing ? I have only one ;). I will try to add option to specify more addresses in field, comma separated.
My toys:
Raspberry Pi 3 + UPS PIco HV3.0 A Stack
Minibian (Raspbian Jessie) + Domoticz beta
RFLink 433 Gateway, 1wire DS18B20 temp sensors (GPIO)
RaZberry module + 2x Comet Z-Wave + Z-wave socket
---
Plugins: WeMo Switch, UPS PIco HV3.0A on GitHub

r3wt3d
Posts: 28
Joined: Friday 10 November 2017 5:09
Target OS: Raspberry Pi
Domoticz version:
Contact:

Re: Plugin - WeMo Switch

Post by r3wt3d » Monday 13 November 2017 7:50

Hi

I tried this out and it seems to work, however I have error in my log

When I turn off I get this:
2017-11-13 14:45:47.025 Error: (xxxxxxx) Except updStatus: 0|1510551709|236|2059|357681|1209600|83|55680|1194829|473422122

When I turn on I get this:
2017-11-13 14:46:25.091 Error: (xxxxxxx) Except updStatus: 8|1510551947|236|2059|357681|1209600|48|0|1194829|473422122

Icon changes after onHeartbeat it seems.
2017-11-13 14:46:39.336 (zzzzzzzz) Updating device from 0:'0' to have values 1:'100'.
2017-11-13 14:46:39.347 (xxxxxxx) Updated to: 1

How can I fix it?

r3wt3d
Posts: 28
Joined: Friday 10 November 2017 5:09
Target OS: Raspberry Pi
Domoticz version:
Contact:

Re: Plugin - WeMo Switch

Post by r3wt3d » Thursday 16 November 2017 6:57

Ok I fixed it...and I additionally added dirty support for WeMo insight switch.
It seems that WeMo is only sending extended status back when turning on/off the switch,
I turn on the relay on every heartbeat if it is already switched on to receive the energy data.
Normally it seems it feeds with upnp, but I have no clue how to grab it on python.

You could probably use this and create a script to update via API:
https://github.com/timonreinhard/wemo-client (check the test directory)

While researching I found this:
https://ouimeaux.readthedocs.io/en/latest/wemo.html
http://moderntoil.com/?p=839
I didn't look deeply inside but maybe I could have used this aswell:
https://github.com/mgsg/domoticz-sonos-plugin

anyway here is the code..
I'm pretty new to python and Domoticz so maybe there are some mistakes you guys can correct...
This script will add a "Insight" True/False switch to Hardware and if you change it to True and restart domoticz
it will create a counter device if not done already, and updates every heartbeat with the latest energy consumption.
Don't forget to enable it.

Spoiler: show

Code: Select all

#!/usr/bin/python3
#
# Domoticz Python Plugin for WeMo Switch by Belkin
# Added support for WeMo Insight switch - r3wt3d 2017/11/16 
# Based on code from:
# https://gist.github.com/pruppert/af7d38cb7b7ca75584ef
# https://github.com/pdumoulin/blinky
#
# Author: mivo
#
"""
<plugin key="wemo-switch" name="WeMo Switch (plugin)" author="mivo" version="0.1" wikilink="http://www.domoticz.com/wiki/plugins" externallink="https://github.com/milanvo/domoticz-plugins">
    <params>
        <param field="Address" label="IP Address" width="200px" required="true"/>
        <param field="Port" label="Port" width="50px" required="true" default="49153"/>
        <param field="Mode1" label="Insight" width="50px">
            <options>
                <option label="True" value="YES"/>
                <option label="False" value="NO" default="true"/>
            </options>
        </param> 
        <param field="Mode6" label="Debug" width="75px">
            <options>
                <option label="True" value="Debug"/>
                <option label="False" value="Normal" default="true"/>
            </options>
        </param>
    </params>
</plugin>
"""
import Domoticz
import urllib.request

class wemoSwitch:
    ip = None
    ports = [49153, 49152, 49154, 49151, 49155]

    def __init__(self, switch_ip=None, switch_port=None):
        self.ip = switch_ip
        if switch_port:
            self.ports = [switch_port]

    def action(self, action):
        value = None

        if action == 'on':
            method, obj, value = ('Set', 'BinaryState', 1)
        elif action == 'off':
            method, obj, value = ('Set', 'BinaryState', 0)
        elif action == 'status':
            method, obj = ('Get', 'BinaryState')
        elif action == 'name':
            method, obj = ('Get', 'FriendlyName')
        elif action == 'signal':
            method, obj = ('Get', 'SignalStrength')

        return self._send(method, obj, value)

    def on(self):
        return self.action('on')

    def off(self):
        return self.action('off')

    def status(self):
        return self.action('status')

    def name(self):
        return self.action('name')

    def signal(self):
        return self.action('signal')

    def _get_header_xml(self, method, obj):
        method = method + obj
        return '"urn:Belkin:service:basicevent:1#%s"' % method

    def _get_body_xml(self, method, obj, value=0):
        method = method + obj
        return '<u:%s xmlns:u="urn:Belkin:service:basicevent:1"><%s>%s</%s></u:%s>' % (method, obj, value, obj, method)

    def _send(self, method, obj, value=None):
        body_xml = self._get_body_xml(method, obj, value)
        header_xml = self._get_header_xml(method, obj)
        for port in self.ports:
            result = self._try_send(self.ip, port, body_xml, header_xml, obj) 
            if result is not None:
                self.ports = [port]
            return result
        raise Exception("_send TimeoutOnAllPorts")

    def _try_send(self, ip, port, body, header, data):
        try:
            request = urllib.request.Request('http://%s:%s/upnp/control/basicevent1' % (ip, port))
            request.add_header('Content-type', 'text/xml; charset="utf-8"')
            request.add_header('SOAPACTION', header)
            request_body = '<?xml version="1.0" encoding="utf-8"?>'
            request_body += '<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">'
            request_body += '<s:Body>%s</s:Body></s:Envelope>' % body
            request.data = request_body.encode()
            result = urllib.request.urlopen(request, timeout=3)
            return self._extract(result.read().decode(), data)
        except Exception as e:
#        except:
#            raise
            print(str(e))
            return None

    def _get_request_data(self, method, obj, value=None):
        body_xml = self._get_body_xml(method, obj, value)
        header_xml = self._get_header_xml(method, obj)
        headers = dict()
        
        url = '/upnp/control/basicevent1'
        headers['Content-type'] = 'text/xml; charset="utf-8"'
        headers['SOAPACTION'] = header_xml

        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/">'
        body += '<s:Body>%s</s:Body></s:Envelope>' % body_xml

        headers['Content-Length'] = "%d"%(len(body))
        
        return dict(url=url, headers=headers, body=body)

    def _extract(self, XML, tagName):
        #print('XML:', XML)
        #print('tagName:', tagName)
        startTag = '<%s>' % (tagName)
        endTag = '</%s>' % (tagName)
        startPos = XML.find(startTag)
        endPos = XML.find(endTag, startPos+1)
        #print('start end:', startPos, endPos)
        if ((startPos == -1) or (endPos == -1)):
            print("'"+tagName+"' not found in supplied XML")
            raise Exception("_extract" + "'"+tagName+"' not found in supplied XML")
            return False
        #if ((startPos == -1) or (endPos == -1)): Domoticz.Error("'"+tagName+"' not found in supplied XML")
        #print('vystup:', XML[startPos+len(startTag):endPos])
        return XML[startPos+len(startTag):endPos]

global switch
switch = wemoSwitch()

def onStart():
    global switch

    if Parameters["Mode6"] == "Debug":
        Domoticz.Debugging(1)
    if (len(Devices) == 0):
        Domoticz.Device(Name="Switch 1", Unit=1, TypeName="Switch").Create()
        Domoticz.Log("Device created.")
    if (2 not in Devices and Parameters["Mode1"] == "YES"):
        Domoticz.Device(Name="Power", Unit=2, TypeName="kWh").Create() 
        Domoticz.Log("kWh Device created.")

    Domoticz.Heartbeat(30)
    
    switch.ip = Parameters["Address"]
    switch.port = Parameters["Port"]

    try:
        status = switch.status()
    except Exception as e:
        Domoticz.Error('Except onStart: ' + str(e))
        return

    updStatus(status)

    DumpConfigToLog()

def onStop():
    Domoticz.Log("Plugin is stopping.")

def onCommand(Unit, Command, Level, Hue):
    global switch

    try:
        if (Command.upper() == 'ON'):
            status = switch.on()
        else:
            status = switch.off()
    except Exception as e:
        Domoticz.Error('Except onCommand: ' + str(e))
        return
    
    updStatus(status)

def onHeartbeat():
    global switch

    try:
        status = switch.status()
    except Exception as e:
        Domoticz.Error('Except onHeartbeat: ' + str(e))
        return
    Domoticz.Debug(str(status))
    if int(status[0:1]) == 1 and Parameters["Mode1"] == "YES":
       power = str(switch.on()).split('|')
       kwh = str(int(power[7])/1000)
       Domoticz.Debug(kwh) 
       Devices[2].Update(0,kwh)

    if int(status[0:1]) == 0 and Parameters["Mode1"] == "YES":
       Devices[2].Update(0,"0.000")
    updStatus(status)

def updStatus(status):
    if not status:
        Domoticz.Error('False updStatus: ' + str(status))
        return

    try:
        istatus = int(status[0:1])
    except ValueError:
        Domoticz.Error('Except updStatus: ' + str(status))
        return

    Domoticz.Debug('Status: ' + str(status) + ' iStatus: ' + str(istatus))

    if istatus == 1:
        if (1 in Devices):
            if Devices[1].nValue == 0:
            #if Devices[1].sValue == "Off":
                Devices[1].Update(1,"On")
                Domoticz.Debug('Updated to: ' + str(istatus))
    elif istatus == 0:
        if (1 in Devices):
            if Devices[1].nValue == 1:
            #if Devices[1].sValue == "On":
                Devices[1].Update(0,"Off")
                Domoticz.Debug('Updated to: ' + str(istatus))
    elif istatus == 8:
        if (1 in Devices):
            if Devices[1].nValue == 0:
                Devices[1].Update(1,"On")
                Domoticz.Debug('Updated to: ' + str(istatus))
    #DumpConfigToLog()

# Generic helper functions
def DumpConfigToLog():
    for x in Parameters:
        if Parameters[x] != "":
            Domoticz.Debug( "'" + x + "':'" + str(Parameters[x]) + "'")
    Domoticz.Debug("Device count: " + str(len(Devices)))
    for x in Devices:
        Domoticz.Debug("Device:           " + str(x) + " - " + str(Devices[x]))
        Domoticz.Debug("Device ID:       '" + str(Devices[x].ID) + "'")
        Domoticz.Debug("Device Name:     '" + Devices[x].Name + "'")
        Domoticz.Debug("Device nValue:    " + str(Devices[x].nValue))
        Domoticz.Debug("Device sValue:   '" + Devices[x].sValue + "'")
        Domoticz.Debug("Device LastLevel: " + str(Devices[x].LastLevel))
    return

def main():
    import argparse

    parser = argparse.ArgumentParser(description='WeMo Switch control module for Python')
    parser.add_argument('action', choices=['on', 'off', 'status', 'name', 'signal'], help='Action')
    parser.add_argument('ip', help='IP address')
    parser.add_argument('port', nargs='?', default='49153', help='Port')
    args = parser.parse_args()

    IP=args.ip
    PORT=args.port

    switch = wemoSwitch(IP, PORT)

    if args.action:
        print(switch.action(args.action))

if __name__ == '__main__':
    main()

Post Reply

Who is online

Users browsing this forum: No registered users and 1 guest