today I´d like to describe a method to access HM actors & sensor from outside (PC or Linx ...) via PHP-script without installing additional software on the CCU.
First of all a little bit about the HomeMatic basic-communication to understand the solution:
Internally HM-software uses " a kind of" rpc-calls (remote procedure calls) to communicate between
- the "logic-processor (system)"
- the "wired controller"
- the "wireless controller"
these processes comunicate via the tpc-ports 1999-2002.
The process-communication is based on a open-source project "xmlrpc++" XML-Remote-Procedure-Calls.
To avoid the "overhead" of XML-processing, EQ3 has modified this project (and following the open-source license - put into it´s open-source part of the HomeMatic and added a relatively simple "binary" interface to be used by the HomeMatic processes.
The workflow:
1. create a binary package with the commands (request)
2. send it via socket-communication to the system port 2001
3. receive the answer (binary package)
4. analyze the answer
There is a lot of functionality behind these rpc-calls (all you can do with the Web-UI), but this example demonstrates only a small part :
- setting actors (e.g. switches on/off, timer, inhibit, toggle )
- get the state of actors (on or off ?)
- get the state of sensors (e.g. weathersensor - temperature/humidity)
the base part is a php-class which is used to create and analyze the request/answers. hmcontroller.php
Code: Alles auswählen
<?php
class Class_hmController
{
public $rpc_method;
public $rpc_argct = 0;
public $arg_data = array();
public $arg_type = array();
public $crlf="\r\n";
private $answer;
private $blocksize = 128;
private $request;
private $requestlen;
public function __construct($ip = "", $port = 0)
{
$this->ip = $ip;
$this->port = $port;
}
/************************************************************************************\
Add a rpc parameter
\************************************************************************************/
public function AddParam ($value, $type)
{
if ($value == "") return;
$this->arg_type[$this->rpc_argct] = $type;
$this->arg_data[$this->rpc_argct] = $value;
$this->rpc_argct++;
}
/************************************************************************************\
Analyze the binary answer of the HM
\************************************************************************************/
public function AnalyzeRequest()
{
echo $this->crlf."Answer:".$this->crlf;
if (strlen($this->answer) >= 8) // minimum size of a valid answer (empty)
if (ord(substr($this->answer,3,1)) == 0x01)
{
$arr = unpack("Nint", substr($this->answer,4,4));
$length = $arr["int"];
$ct = 0;
echo "Length:".$length.$this->crlf;
while ($length > $ct)
{
$arr = unpack("Nint", substr($this->answer,8+$ct,4));
$type = $arr["int"];
$ct+=4;
switch ($type)
{
case 1: //4byte integer
$arr = unpack("Nint", substr($this->answer,8+$ct,4));
$value = $arr["int"];
$ct+=4;
break;
case 2: //boolean
$arr = unpack("Cint", substr($this->answer,8+$ct,1));
$value = $arr["int"];
$ct++;
break;
case 3: //string
$arr = unpack("Nint", substr($this->answer,8+$ct,4));
$slen = $arr["int"];
$value = substr($this->answer,8+$ct+1,$slen);
$ct += 4+$slen;
break;
case 4: //double value
$arr = unpack("N2int", substr($this->answer,8+$ct,8));
$mantissa = $arr["int1"];
$exponent = $arr["int2"];
$value = round (((double)$mantissa / (double)0x40000000) * (double)(Pow(2, $exponent)),2);
$ct+=8;
break;
}
if ($type == 3 && $value == "")
echo "Value: (empty = OK)!".$this->crlf;
else
echo "Value: ".$value.$this->crlf;
}
}
else
{
echo "Error".$this->crlf;
}
}
/************************************************************************************\
print the request to screen
\*************************************************************************************/
public function DumpRequest()
{
echo "Request: \"".$this->rpc_method."\"".$this->crlf;
for ($a=0; $a< $this->rpc_argct; $a++)
echo " Param[".$a,"]=\"".$this->arg_data[$a]."\" Type(".$this->arg_type[$a].")".$this->crlf;
}
/************************************************************************************\
Execute the request and get back the binary answer
\************************************************************************************/
public function ExecuteRequest ()
{
// method
$this->requestlen = strlen ($this->rpc_method)+8;
$this->request = pack("N", strlen ($this->rpc_method)).$this->rpc_method.pack("N",$this->rpc_argct);
// the arguments of the request
for ($a=0; $a<$this->rpc_argct; $a++)
{
$this->request .= pack("N",$this->arg_type[$a]);
$this->requestlen += 4;
switch($this->arg_type[$a])
{
case 3 : //string
$this->request .= pack("N",strlen($this->arg_data[$a])).$this->arg_data[$a];
$this->requestlen += 4 + strlen($this->arg_data[$a]);
break;
case 2 : //boolean (1byte) all values different from zero are accepted as "true"
$this->request .= chr($this->arg_data[$a]);
$this->requestlen ++;
break;
case 4 : //double
$Exponent = 0;
$tmp = Abs($this->arg_data[$a]);
while ($tmp >= 2 )
{
$tmp = Abs($this->arg_data[$a])/ Pow(2, $Exponent++);
echo "t:".$tmp."<br>";
}
$tmp = $this->arg_data[$a] / Pow(2, $Exponent);
$Mantissa = ((double)$tmp * 0x40000000);
$this->request .= pack("NN",$Mantissa, $Exponent);
$this->requestlen +=8;
break;
}
}
// create the complete request (header + requestpart)
$this->request="Bin".chr(0).pack("N",$this->requestlen).$this->request;
// send the reqest and get the answer
$fs = fsockopen ($this->ip, $this->port, $errno, $errstr, 3);
stream_set_timeout($fs, 1);
if ($fs != 0)
{
fwrite ($fs, $this->request, strlen($this->request));
$this->answer = "";
do
{
$block = fread($fs,$this->blocksize);
$this->answer .= $block;
}
while (strlen($block) == $this->blocksize);
fclose ($fs);
}
return (strlen($this->answer) > 0); // execute is done return true if there is an answer
}
}
?>
Code: Alles auswählen
<?php
include "hmcontroller.php";
$hm = new Class_hmController("10.9.1.2", 2001); // change this to your HM-address
$hm->crlf="<br>";
// set the classes parameter
$hm->rpc_method = $HTTP_GET_VARS["method"];
$hm->AddParam($HTTP_GET_VARS["address"],3);
switch ($HTTP_GET_VARS["method"])
{
case "getValue":
$hm->AddParam($HTTP_GET_VARS["what"],3);
break;
case "setValue":
$hm->AddParam($HTTP_GET_VARS["what"],3);
if ( $HTTP_GET_VARS["what"] == STATE
|| $HTTP_GET_VARS["what"] == INSTALL_TEST
|| $HTTP_GET_VARS["what"] == INHIBIT) //value is boolean type
$hm->AddParam($HTTP_GET_VARS["value"],2);
if ($HTTP_GET_VARS["what"] == ON_TIME) //value is floatingpoint (seconds)
$hm->AddParam($HTTP_GET_VARS["value"],4);
break;
default:
break;
}
$hm->DumpRequest();
if ($hm->ExecuteRequest())
$hm->AnalyzeRequest();
?>
How to use:
- copy both files on a web-server with php-support into the same directory
- edit hmcontrol.php to change the ip-address to your HomeMatic
call the wrapper hmcontrol.php from a browser.
examples:
Code: Alles auswählen
http://10.9.1.2/cgi-bin/hmcontrol.php?method=setValue&address=EEQ0255873:1&what=STATE&value=1
* address is the serial of your devices (take it from the WEB-UI)+ ":channel"
* what is a keyword access the functionality
* value is a parameter used with some functionality
possible functions: method address what value
setValue AddressOfActor:1 STATE 1 -> switch on the actor channel 1
setValue AddressOfActor:2 STATE 0 -> switch off the actor channel 2
setValue AddressOfActor:2 INHIBIT 1 -> inhibit (lock) actor channel 2
setValue AddressOfActor INSTALL_TEST 1 -> toggle state of the actor
getValue AddressOfActor:2 STATE -> query the state of actor channel 2
getValue AddressOfWeatherSensor:1 HUMIDIYT -> get humidity-value
getValue AddressOfWeatherSensor:1 TEMPERATURE -> get temperature-value
set getValue AddressOfActor:2 ON_TIME 2.3
+
setValue AddressOfActor:2 STATE 1 -> switch on actor channel 2 for 2.3 seconds
you can also emulate the pressing of remote-controls via the CCU by using their adresses.
Hope this is clear enough to start your own experiments
Bye
Alex