Belkin Wemo plug & shellscript automated control
A client who uses paxton door access control for their gym wanted to have their TV switch on when a customer enters, and switch off after 1 hour. I bought a Belkin WeMo Switch and found a shellscript that can switch the wemo on and off (original script.)
For this I created a C# console application in combination with the paxton sdk that fetches the last time a user entered the premises, and it will write the desired state of the TV power switch to a simple txt file (ON or OFF):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Paxton.Net2.OemClientLibrary;
using System.Xml.Linq;
using System;
using System.IO;
using System.Collections.Generic;
using System.Data;
namespace ConsoleApplication2
{
class Program
{
private const int REMOTE_PORT = 8025;
private const string REMOTE_HOST = "192.168.2.2";
private OemClient _net2Client = new OemClient(REMOTE_HOST, REMOTE_PORT);
private static string switchFile = @"C:\cygwin64\home\user\switch.txt";
static void Main(string[] args)
{
Program main = new Program();
main.startUp();
System.Environment.Exit(0);
}
public int startUp()
{
bool res = AuthenticateUser("OEM Client", "password");
if (res != true)
{
Log("Authentication failed, exiting application");
return 1;
}
Log("Authentication success");
IEvents events = _net2Client.ViewEvents(1, "EventType = 20", "EventId desc");
IEventView evt = events.EventsList()[events.EventsList().Keys.ElementAt(events.EventsList().Count - 1)];
Log(evt.EventType + " " + evt.EventTypeDescription + " " + evt.EventTime + " " +evt.EventDate+" "+evt.EventDescription+ " " +evt.Username+ " " + evt.UserId);
double lastAccess = DateTimeToUnixTimestamp(stringToDateTime(evt.EventDate + " " + evt.EventTime));
double currentTime = DateTimeToUnixTimestamp(DateTime.Now);
Log(lastAccess.ToString());
Log(currentTime.ToString());
//Longer than an hour? switch off
if(Math.Round(currentTime - lastAccess) > 3600)
{
File.WriteAllText(switchFile, "OFF");
}
else
{
File.WriteAllText(switchFile, "ON");
}
return 1;
}
private DateTime stringToDateTime(string date)
{
// Date strings are interpreted according to the current culture.
// If the culture is en-US, this is interpreted as "January 8, 2008",
// but if the user's computer is fr-FR, this is interpreted as "August 1, 2008"
DateTime dt = Convert.ToDateTime(date);
return dt;
}
public static double DateTimeToUnixTimestamp(DateTime dateTime)
{
return (TimeZoneInfo.ConvertTimeToUtc(dateTime) - new DateTime(1970, 1, 1)).TotalSeconds;
}
/**
* Authenticate user
* For some reason we can't authenticate with username password, only with userId Password..
* So we need to get the userId and Authenticate with it..
**/
private bool AuthenticateUser(string userName, string password)
{
IOperators operators = _net2Client.GetListOfOperators();
Dictionary<int, string> operatorsList = operators.UsersDictionary();
foreach (int userID in operatorsList.Keys)
{
if (operatorsList[userID] == userName)
{
Dictionary<string, int> methodList = _net2Client.AuthenticateUser(userID, password);
return (methodList != null);
}
}
return false;
}
private void Log(string logMessage)
{
Console.WriteLine(logMessage);
Console.WriteLine("--------------------");
}
}
}
This application is scheduled every few minutes, after wich a shell script is called that will switch the tv to the desired state, the shellscript:
#!/bin/sh # # WeMo Control Script # # IP=192.168.2.21 #switch.txt contains ON or OFF COMMAND=`cat ~/switch.txt` PORT=0 #Do a quick port test (1 of 4 is used) for PTEST in 49152 49153 49154 49155 do PORTTEST=$(curl -s -m 3 $IP:$PTEST | grep "404") if [ "$PORTTEST" != "" ] then PORT=$PTEST break fi done if [ $PORT = 0 ] then echo "Cannot find a port" exit fi #get the state first, so we can decide if we need to switch at all or TV already has desired state STATE=$(curl -0 -A '' -X POST -H 'Accept: ' -H 'Content-type: text/xml; charset="utf-8"' -H "SOAPACTION: \"urn:Belkin:service:basicevent:1#GetBinaryState\"" --data '<?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"><BinaryState>1</BinaryState></u:GetBinaryState></s:Body></s:Envelope>' -s http://$IP:$PORT/upnp/control/basicevent1 | grep "<BinaryState" | cut -d">" -f2 | cut -d "<" -f1 | sed 's/0/OFF/g' | sed 's/1/ON/g') if [ $COMMAND = "ON" ] && [ $STATE != "ON" ] then curl -0 -A '' -X POST -H 'Accept: ' -H 'Content-type: text/xml; charset="utf-8"' -H "SOAPACTION: \"urn:Belkin:service:basicevent:1#SetBinaryState\"" --data '<?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:SetBinaryState xmlns:u="urn:Belkin:service:basicevent:1"><BinaryState>1</BinaryState></u:SetBinaryState></s:Body></s:Envelope>' -s http://$IP:$PORT/upnp/control/basicevent1 | grep "<BinaryState" | cut -d">" -f2 | cut -d "<" -f1 elif [ $COMMAND = "OFF" ] && [ $STATE != "OFF" ] then curl -0 -A '' -X POST -H 'Accept: ' -H 'Content-type: text/xml; charset="utf-8"' -H "SOAPACTION: \"urn:Belkin:service:basicevent:1#SetBinaryState\"" --data '<?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:SetBinaryState xmlns:u="urn:Belkin:service:basicevent:1"><BinaryState>0</BinaryState></u:SetBinaryState></s:Body></s:Envelope>' -s http://$IP:$PORT/upnp/control/basicevent1 | grep "<BinaryState" | cut -d">" -f2 | cut -d "<" -f1 else echo "No change in state" echo "" echo "" fi