Energenie LAN Management Power Strip (4-Way) - Plug in (Idea)

Hi

I’ve just bought an Energenie PMS-LAN unit and I wanted to see if it could be controlled by Vera - as many of you know my programming skills are basic at best, but I’m always interested to learn.

Product - EnerGenie: Green energy for our planet

They have published an SDK and theres a very basic web interface, so I’m optimistic it’s possible to do, but based on a conversation with @futzle (http://forum.micasaverde.com/index.php/topic,16888.0.html) I’m likely to entering a bumpy road of learning…

http://www.energenie.com/Repository/6668/EG-PMS-LAN_driver_90F124ED-5B9A-45FA-B3D8-63C87F69A9D0.zip

A few examples of people integrations are here too.

Has anyone had any experience with Energenie products ?

While the SDK talks about encryption and decryption I’ve been enthusiastically been trying to work out what the browser based commands are to turn sockets on or off )and mimic them) With some help we think ‘ctrl’ is the identifier of sockets, so ctrl1=1 or ctrl2=0 but we’re at a dead end. Just some of the many failed examples below.

http://192.168.1.28?ctl1=1&ctl2=0&ctl3=0&ctl4=0
http://192.168.1.28/status.html?ctl1=1&ctl2=0&ctl3=0&ctl4=0
http://192.168.1.28/status.html?ctl1=0
http://192.168.1.28/status.html?ctl2=0
http://192.168.1.28:5000?ctl1=0
http://192.168.1.28:5000?ctl1=1&ctl2=0&ctl3=0&ctl4=0
http://192.168.1.28:5000/status.html?ctl1=0
http://192.168.1.28:5000/status.html?ctl2=0
http://192.168.1.28:5000/status.html?ctl1=1&ctl2=0&ctl3=0&ctl4=0

All of which was not helped by the fact that it seems you have to be logged in too, and that was not working either.

http://192.168.1.28:5000?pw=1

I know you all do not have visibility to the devices built in website to see what I’m seeing, but I wanted to shared my progress. I’m going to see if I can get wireshark installed on an old PC to see if that can tell me anything.

I’ve not been able to look at this for a while, but as I had some time today I thought I’d see what I could find something that might help. And using Google Chrome and then right clicking on the web consoles on/off buttons

Choosing ‘Inspect Element’ each of the on and off buttons look like they run some javascript.

Power socket 1 (On/Off)

ON OFF
OFF ON

Power Socket 2 (On/Off)

ON OFF
ON ON

the same for socket 3 & 4

Does anyone know of a way to create a URL (I assume a http request GET or Curl) that could call any one of the above via a URL ?

I was looking at http://wiki.micasaverde.com/index.php/Luup_Scenes_Events#Access_the_web but not sure what I can do.
I’m guessing there’s no way to do it from a URL, but thought I would ask (I’m clutching at straws :slight_smile: )…

To share what else I have found - there looks to be two files reported by Chrome…

http://192.168.1.25/s.css
http://192.168.1.25/eg.js

eg.js - shows the following. (if that means anything to anyone who might be able to help)

function ChangeState(sn){f = document.forms.tForm;ind = sn * 1 - 1;f["cte" + sn].value = Math.abs(1 - sockstates[ind]);f.submit();}function ActivateDeactivate(){f = document.forms.tForm2;f["activate"].value = actbtn;f.submit();}function TimerFunction(){clearTimeout(timer);if(trycon==1&&active==1){document.location.href = "energenie.html";}}function StartTimer(){timer = setTimeout(TimerFunction, period);}window.onload = function (){for(i = 0; i<4; i++){if(sockstates[i]==0){clsname = 'offstate';str1 = 'OFF';str2 = 'ON';}else{clsname = 'onstate';str1 = 'ON';str2 = 'OFF';}strhtml = '<span class="' + clsname + '">' + str1 + '</span>&nbsp;<a href="javascript: ChangeState(\'' + (i+1) + '\')" class="onoffbtn">' + str2 + '</a>';el = document.getElementById('stCont' + i);el.innerHTML = strhtml;}statA = '';statB = '';statC = '';rmsg = '';if(ipid!=0){statA = "Registered - ";tmpel = document.getElementById('regBtn');tmpel.innerHTML = 'Login';}else{rmsg = "Register to manage EG-PMS-LAN from Internet ( free service )";}if(active==1){statB = "Activated - ";}else{statB = "Not activated";}if(active==1){if(trycon==1){statC = "Trying to connect";}else if(serv==1){statC = "Connected";}else if(serv==0){statC = "Not connected";}}statAel = document.getElementById('statusA');statAel.innerHTML = statA;statBel = document.getElementById('statusB');statBel.innerHTML = statB;statCel = document.getElementById('statusC');statCel.innerHTML = statC;rmsgel = document.getElementById('regmsg');rmsgel.innerHTML = rmsg;actBtnEl = document.getElementById("actBtn");if(actbtn==1){actBtnEl.innerHTML = 'Activate';}else{actBtnEl.innerHTML = 'Deactivate';}regBtnEl = document.getElementById("regBtn");regBtnEl.href = "http://www.energenie.com/user/register.aspx?mac=" + mac;if (warn==1) {alert("Failed to connect. Please, check DNS server settings.");}if (warn==2) {alert("Failed to activate. Please, check, that device is registered.");}StartTimer();}

Well you peeled the first layer … you need to digg deeper into the JavaScript to where it make a Web request.

You might be able to see it if switch to the Network tab on the Chrome developer tools while you interact with the web page.

Hi @RTS

I’ve attached what it shows in the Network tab on the Chrome browser when i click on the on or off button on the web interface. Does that help ?

The eg.js looked the most interesting…

function ChangeState(sn){f = document.forms.tForm;ind = sn * 1 - 1;f["cte" + sn].value = Math.abs(1 - sockstates[ind]);f.submit();}function ActivateDeactivate(){f = document.forms.tForm2;f["activate"].value = actbtn;f.submit();}function TimerFunction(){clearTimeout(timer);if(trycon==1&&active==1){document.location.href = "energenie.html";}}function StartTimer(){timer = setTimeout(TimerFunction, period);}window.onload = function (){for(i = 0; i<4; i++){if(sockstates[i]==0){clsname = 'offstate';str1 = 'OFF';str2 = 'ON';}else{clsname = 'onstate';str1 = 'ON';str2 = 'OFF';}strhtml = '' + str1 + ' ' + str2 + '';el = document.getElementById('stCont' + i);el.innerHTML = strhtml;}statA = '';statB = '';statC = '';rmsg = '';if(ipid!=0){statA = "Registered - ";tmpel = document.getElementById('regBtn');tmpel.innerHTML = 'Login';}else{rmsg = "Register to manage EG-PMS-LAN from Internet ( free service )";}if(active==1){statB = "Activated - ";}else{statB = "Not activated";}if(active==1){if(trycon==1){statC = "Trying to connect";}else if(serv==1){statC = "Connected";}else if(serv==0){statC = "Not connected";}}statAel = document.getElementById('statusA');statAel.innerHTML = statA;statBel = document.getElementById('statusB');statBel.innerHTML = statB;statCel = document.getElementById('statusC');statCel.innerHTML = statC;rmsgel = document.getElementById('regmsg');rmsgel.innerHTML = rmsg;actBtnEl = document.getElementById("actBtn");if(actbtn==1){actBtnEl.innerHTML = 'Activate';}else{actBtnEl.innerHTML = 'Deactivate';}regBtnEl = document.getElementById("regBtn");regBtnEl.href = "http://www.energenie.com/user/register.aspx?mac=" + mac;if (warn==1) {alert("Failed to connect. Please, check DNS server settings.");}if (warn==2) {alert("Failed to activate. Please, check, that device is registered.");}StartTimer();}

Hi @parkerc

I’ve had some success switching the sockets on and off with basic html pages. Only post method works, so url querystring isn’t accepted. See if the below code works for you. Create a .htm on your PC, open in a browser, and press the submit button. You will need to edit the IP address.

Unfortunately, it doesn’t seem possible to pass the password (pw) variable and the switch status in the same form, due to the specific login.html url that must first be called. So my sample htm file has two forms, with the top one for login (default pw=1) with its own submit button, and the second form is for controlling the sockets with separate submit button. Put a 1 or 0 in the relevant socket. I have found that you can only submit a change to one socket at a time, so make sure the other sockets are blank before you submit the form.

[code]
pw



1
2
3
4
[/code]

The challenges now for a plugin are:
1). How to submit the login form and maintain the session
2). How to capture the current status of the 4 sockets from the energenie.html page
3). How to submit the form that will change socket power on or off (and capture confirmation of state change)
4). How to log out (to free up session).*

  • I noticed that if any web page is logged in, no other web session are permitted. So if a plugin was developed, it would have to:
    a) immediately release a session after completing an action
    b) be tolerant to the fact it will fail if someone is manually connected by another web browser session.

Thanks @jtmoore

Any idea how we can make this work with Lua, so at the very least it can be run within a scene etc. I’m keen to not use a PC.

There are number of entries on this forum about http POST.

http://forum.micasaverde.com/index.php?topic=11745.0
http://forum.micasaverde.com/index.php?topic=11043.0

And here too.

I had thought something in Javascript (if that’s the right term) would work well considering that seems to be how the energenies web client looks to work, but it sounds like you’re onto something. So I had a go based on the //w3.impa.br example in the link above, but not sure how I can create a return in the log to know if it works or not? (To be honest I doubt it does as it does not work straight into a browser)

[code]-- load required modules
http = require(“socket.http”)
mime = require(“mime”)

– Connect to energenie powerstrip “//192.168.1.111” and tries to retrieve
– “login.html”, using the provided name and password to
– authenticate the request
b, c, h = http.request(“http://pw:1@192.168.1.111/login.html”)

– Alternatively, one could fill the appropriate header and authenticate
– the request directly.
r, c = http.request {
url = “http://192.168.1.111/login.html”,
headers = { authentication = "Basic " … (mime.b64(“pw:1”)) }
}
[/code]

Also it seems this needs to be followed with a request to turn on or off a socket. It would also be great to get the status of the sockets but I’m getting ahead of myself now :slight_smile:

Hi all,

I wrote some Perl scripts that can power on/off those plugs, see [url=http://forum.micasaverde.com/index.php?topic=16888.0]http://forum.micasaverde.com/index.php?topic=16888.0[/url] (the post is not yet approved). Thsi is not using the webGUI (which is an approach I don’t really like), but uses the documented protocol on port 5000 in their SDK (which is a bit messy and weird, involving some simple challenge&response authentication). I don’t know about LUA, but I think it should be possible to dapt the code accordingly.

Cheers, Andy

Thanks @klaymen

I’ve quoted your post in the other thread here to, so we can see if it can be converted to some LUA which people with this socket can use.

[quote=“klaymen, post:11, topic:177214”]Hi all,

I got this device too. Using the protocol description I successfully wrote a small perl script that can turn on and off the plugs using the protocol on port 5000 (not the webserver itself). If your’e interested, it looks like this (should not be difficult to implement it in any other language), $pass and $ip must be filled in of course:

use strict;
use IO::Socket::INET;

my $pass = "...";
my $ip = "...";

sub response($$)
{
  my @d = map (ord, split //,shift);
  my @k = map (ord, split //,shift);
  my $v1 = (($d[0]^$k[2]) * $k[0])  ^  ($k[6] | ($k[4]<<8))  ^  $d[2];
  my $v2 = (($d[1]^$k[3]) * $k[1])  ^  ($k[7] | ($k[5]<<8))  ^  $d[3];
  return join "",map(chr, ($v1%256,$v1>>8,$v2%256,$v2>>8));
}

sub encrypt($$$)
{
  my @d = map (ord, split //,shift);
  my @k = map (ord, split //,shift);
  my @t = map (ord, split //,shift);
  my @r = (0,0,0,0);
  my $x;
  for (my $i=0; $i<4; $i++)
  {
    $x = $d[3-$i];
    $x ^=  $t[2];
    $x += $t[3];
    $x ^= $k[0];
    $x += $k[1];
    $r[$i] = 0xff & $x;
  }
  return join "",map(chr, @r);
}

sub decrypt($$$)
{
  my @d = map (ord, split //,shift);
  my @k = map (ord, split //,shift);
  my @t = map (ord, split //,shift);
  my @r = (0,0,0,0);
  my $x;
  for (my $i=0; $i<4; $i++)
  {
    $x = $d[$i];
    $x -= $k[1];
    $x ^= $k[0];
    $x -= $t[3];    
    $x ^= $t[2];
    $r[3-$i] = 0xff & $x;   
  }
  return join "",map(chr, @r);
}

sub setPlug($$$)
{
  my $ip = shift;
  my $idx = shift;
  return 0 if $idx<0 or $idx>3; # plugs 0-3
  my $flg = shift;
  return 0 if $flg<0 or $flg>2; # 0: turn off, 1: turn on, 2: toggle

  my $done = 0;
  while (!$done)
  {
	  my $s;
	  eval
	  {
		local $SIG{ALRM} = sub { die 'Timed Out'; };
		alarm 10; 
		my ($t,$d);
		$s =  new IO::Socket::INET (
		  PeerHost => $ip,
		  PeerPort => '5000',
		  Proto => 'tcp',
		) or return 0;

		$s->send("\x11");
		$s->recv($t,4);

		$s->send(response($t,$pass));
		$s->recv($d,4);
		$d = decrypt($d,$pass,$t);
  
		my $ctrl = "\x04\x04\x04\x04";  # default is: no action on any plug
		if ($flg==0) # turn off, unless it already is off
		{
		  substr($ctrl,$idx,1) = "\x02" unless substr($d,$idx,1) eq "\x82";
		}
		if ($flg==1) # turn on, unless it alredy is on
		{
		  substr($ctrl,$idx,1) = "\x01" unless substr($d,$idx,1) eq "\x41";
		}
		if ($flg==2) # toggle
		{
		  substr($ctrl,$idx,1) = chr((ord(substr($d,$idx,1))&1) + 1);
		}

		$s->send(encrypt($ctrl,$pass,$t));
		$s->recv($d,4);
		$d = decrypt($d,$pass,$t);
		$done=1;

		$s->send("\x01\x02\x03\x04");
		alarm 0;
	  };
	  alarm 0;
      $s->close() if defined $s;
  }
  return 1;
}


setPlug($ip,0,0); # turn off plug 1
setPlug($ip,0,1); # turn on plug 1
setPlug($ip,0,2); # toggle plug 1

The while/eval construct is just there to repeat the TCP connection in case of a problem. You might want to increase/decrase the “alarm 10” value - in this setup, the connection is re-tries after 10 seconds if it does not succeeds (timeout).

Hope it helps,

Andy[/quote]

Sadly I’m still learning Lua and have little knowledge of Perl. But I’m keen to get this working with Vera.

It’s taken me a while, but I think I’ve finally found out how to do it…

http://www.linux-hardware-guide.com/uk/2014-04-19-energenie-eg-pm2-lan-programmable-6x-ip-outlet-socket-lan

The EnerGenie EG-PM2-LAN is a programmable 6-times multiple socket outlet, which can be accessed via Ethernet. In contrast to the USB version there is no dedicated tool available for this LAN version. However, it is possible to send commands to the Web interface, which is embedded in the EG-PM2-LAN. In the following it is described how these commands can be sent via ?curl?.

First, the web server needs a login with a password (?PASSWORD? is used in the following)

curl -sd ‘pw=PASSWORD’ http://EG-PM2-LAN-IP-Adress | fgrep -q Status

The return value should be ?true?. As one can see, the password is sent unencrypted, which shows the low safety measures of the EG-PM2-LAN and could be a drawback for certain scenarios.

The integrated web server does not support sessions. Instead, after transmitting the password each process from the same IP address of this login can access the EG-PM2-LAN. Furthermore, all other IPs are blocked, while this login is still active. After some minutes of idling the login times out and a new login is necessary to access the web frontend.

Now, it is possible to switch via shell command one of the sockets (X = 1 ? 4) on (Y=1) or off (Y=0). E.g., if socket 3 should be powered on, this is done by the command:

curl -sd ‘cte3=1’ http://EG-PM2-LAN-IP-Adresse | fgrep -q Status
or in general by

curl -sd ‘cteX=Y’ http://EG-PM2-LAN-IP-Adresse | fgrep -q Status
The logout is achieved by:

curl -s http://EG-PM2-LAN-IP-Adresse | fgrep -q password

And now I need to see if I can get it to work via Vera,.

So far I’ve been trying to get the fist one to work by using os.execute () but no joy. My ‘Lua Test’ result is below, I’ve tried a few things, but not sure what I’m missing…

LuaTest 1.5.2

Lua file: /nas/luaenergenie.lua

Results
Code error: Line 2: ‘)’ expected near ‘pw’

Print output
(none)

Code
1
2 os.execute(‘curl -sd ‘pw=1’ http://192.168.1.90 | fgrep -q Status’)

My Luatest result is below, I've tried a few things, but not sure what I'm missing...

The argument for os.execute(…) needs to be a single string and yours isn’t. Try something like:

os.execute("curl -sd 'pw=1' http://192.168.1.90 | fgrep -q Status")

Thanks Rex,

I had tried speech marks around the IP address etc., but missed that one :frowning:

It seemed to go through alright, but it does not seem to be doing anything to the power socket, I try to turn off Socket 3. Humm, I’m going to need to keep playing…

LuaTest 1.5.2

Lua file: /nas/luaenergenie.lua

Results
No errors
Runtime: 164.3 ms
Code returned: nil

Print output
Receive:

Code
1
2 os.execute(“curl -sd ‘pw=1’ http://192.168.1.90 | fgrep -q Status”)
3
4 os.execute(“curl -sd ‘cte3=0’ http://192.168.1.90 | fgrep -q Status”)
5
6 print (“Receive:”, data, rerr)

Reading an old post I found from @Guessed, (http://forum.micasaverde.com/index.php/topic,17627.msg138882.html#msg138882) it seems calling Curl, via a shell process places an overhead on Vera, so if I’ve understood his direction correctly, I would need to ensure any coding I do has this at the beginning? Does that sound right.

local url = require("socket.url")
local socket = require("socket")
local http = require("socket.http")
local ltn12 = require("ltn12")

-- 5 Second timeout
http.TIMEOUT = 5

Launching a shell process does have an overhead. May not be a problem if it only happens a few times in a day but not great for frequent use.

@Guessed was suggesting that it would be preferable to use the Post method with Lua Socket. He provided links to examples.

Because your device requires a log-on prior to accepting a command, the issue may be one of timing. You need to understand the timing and return values before you can code it properly. Do the cURL commands work when issued from Vera’s command line? Try them without the | fgrep -q Status suffix as well.

Hi
I managed to get the following working in Zerobrane lua IDE but so far no success running it in a scene, in case its any help :slight_smile: You’ll need to fill in your details for SERVERNAME, PASSWORD and ENERGENIEIPADDRESS. This is just a first working example, at the moment just turning socket number 1 (cte1) on (change cte1=0 to turn off) - line 4.

local http = require “socket.http”
local ltn12 = require “ltn12”
local passbody = “SERVERNAME&pw=PASSWORD”
local sw1body = “cte1=1”
local respbody = {}
local body, code, headers, status = http.request {
method = “POST”,
url = “http://ENERGENIEIPADDRESS/login.html”,
source = ltn12.source.string(passbody),
headers =
{
[“Accept”] = “/”,
[“Accept-Encoding”] = “gzip, deflate”,
[“Accept-Language”] = “en-us”,
[“Content-Type”] = “application/x-www-form-urlencoded”,
[“content-length”] = string.len(passbody)
},
sink = ltn12.sink.table(respbody)
}
socket.sleep(2)
local b, c, h, s = http.request {
method = “POST”,
url = “http://ENERGENIEIPADDRESS”,
source = ltn12.source.string(sw1body),
headers =
{
[“Accept”] = “/”,
[“Accept-Encoding”] = “gzip, deflate”,
[“Accept-Language”] = “en-us”,
[“Content-Type”] = “application/x-www-form-urlencoded”,
[“content-length”] = string.len(sw1body)
},
sink = ltn12.sink.table(respbody)
}

Hi @ChrisAB

Finally found time to have a go with this, but I’m not sure what is the Servername would be ?

I know the IP and Password ?

UPDATE : Scrap that I found it, the defaults are :slight_smile:

SERVERNAME = Server 1
PASSWORD = 1

Sadly no joy, I tried it in the test Lua window, it was sent successfully, but it had no affect on my Energenie EG-PSM-LAN . The code below show the default server name and password.

local http = require "socket.http" local ltn12 = require "ltn12" local passbody = "Server 1&pw=1" local sw1body = "cte1=1" local respbody = {} local body, code, headers, status = http.request { method = "POST", url = "http://192.168.1.90/login.html", source = ltn12.source.string(passbody), headers = { ["Accept"] = "*/*", ["Accept-Encoding"] = "gzip, deflate", ["Accept-Language"] = "en-us", ["Content-Type"] = "application/x-www-form-urlencoded", ["content-length"] = string.len(passbody) }, sink = ltn12.sink.table(respbody) } socket.sleep(2) local b, c, h, s = http.request { method = "POST", url = "http://192.168.1.90", source = ltn12.source.string(sw1body), headers = { ["Accept"] = "*/*", ["Accept-Encoding"] = "gzip, deflate", ["Accept-Language"] = "en-us", ["Content-Type"] = "application/x-www-form-urlencoded", ["content-length"] = string.len(sw1body) }, sink = ltn12.sink.table(respbody) }

Hi, sorry, haven’t been on the forum for a while.
I have also posted this under http://forum.micasaverde.com/index.php/topic,27291.msg195079/topicseen.html#msg195079
I can’t get it to work in Vera either and so far no solution has been suggested ???
I’ll post again if I get anywhere…
Cheers

I’ve been attempting this too. Whilst I also get the code to work in ZeroBrane, I can’t get it to work using the Test Luup Code in Vera. I put some luup.log into the code, and the statusMsg returns “timeout” at the very first step (login.htm). Unfortunately I know nothing about Luup coding so I have no idea why timeout occurs only in Vera (despite http.TIMEOUT = 5 being defined). :cry:

I’m not able to test this fully tonight, and it might be nothing, but the first page I go to, with the server name and the password prompt on it, looks to be http://192.168.1.90/energenie.html not http://192.168.1.90/login.html

http://192.168.1.90/login.html is the one I’m taken to after I’ve logged in…

UPDATE - Actually it looks like both of the above pages and anything you want to put after the IP e.g http://192.168.1.90/micasaverde.html will bring up the password prompt (wierd!!)