2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-01-31 06:39:11 +00:00

added: New modules 10_pilight_ctrl, 30_pilight_dimmer, 30_pilight_switch and 30_pilight_temp to support pilight (sending and receiving)

git-svn-id: https://svn.fhem.de/fhem/trunk@8479 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
risiko79 2015-04-25 18:20:34 +00:00
parent c3f84f295f
commit 0866553941
7 changed files with 1265 additions and 0 deletions

View File

@ -1,5 +1,7 @@
# Add changes at the top of the list. Keep it in ASCII, and 80-char wide.
# Do not insert empty lines here, update check depends on it.
- added: New modules 10_pilight_ctrl, 30_pilight_dimmer, 30_pilight_switch
and 30_pilight_temp to support pilight (sending and receiving)
- bugfix: 98_EDIPLUG: change kW/h to kWh and fix missing Id
- feature: FB_CALLMONITOR: new reverse search method "textfile" for custom
definitions

View File

@ -0,0 +1,761 @@
##############################################
# $Id: 10_pilight_ctrl.pm 0.50 2015-04-17 Risiko $
#
# Usage
#
# define <name> pilight_ctrl <host:port> [5.0]
#
# Changelog
#
# V 0.10 2015-02-22 - initial beta version
# V 0.20 2015-02-25 - new: dimmer
# V 0.21 2015-03-01 - API 6.0 as default
# V 0.22 2015-03-03 - support more switch protocols
# V 0.23 2015-03-14 - fix: id isn't numeric
# V 0.24 2015-03-20 - new: add cleverwatts protocol
# V 0.25 2015-03-26 - new: cleverwatts unit all
# - fix: unit isn't numeric
# V 0.26 2015-03-29 - new: temperature and humidity sensor support (pilight_temp)
# V 0.27 2015-03-30 - new: ignore complete protocols with <protocol>:* in attr ignore
# 2015-03-30 - new: GPIO temperature and humidity sensors
# V 0.28 2015-04-09 - fix: if not connected to pilight-daemon, do not try to send messages
# V 0.29 2015-04-12 - fix: identify intertechno_old as switch
# V 0.50 2015-04-17 - fix: queue of sending messages
# - fix: same spelling errors - thanks to pattex
##############################################
package main;
use strict;
use warnings;
use Time::HiRes qw(gettimeofday);
use JSON; #libjson-perl
use Switch; #libswitch-perl
sub pilight_ctrl_Parse($$);
sub pilight_ctrl_Read($);
sub pilight_ctrl_Ready($);
sub pilight_ctrl_Write($@);
sub pilight_ctrl_SimpleWrite(@);
sub pilight_ctrl_ClientAccepted(@);
sub pilight_ctrl_Send($);
my %sets = ( "reset:noArg" => "");
my %matchList = ( "1:pilight_switch" => "^SWITCH",
"2:pilight_dimmer" => "^SWITCH|^DIMMER",
"3:pilight_temp" => "^PITEMP") ;
my @idList = ("id","systemcode","gpio");
my @unitList = ("unit","unitcode","programcode");
#ignore tfa:0,... list of <protocol>:<id> to ignore
#brands arctech:kaku,... list of <search>:<replace> protocol names
#ContactAsSwitch 1234,... list of ids where contact is transformed to switch
sub pilight_ctrl_Initialize($)
{
my ($hash) = @_;
require "$attr{global}{modpath}/FHEM/DevIo.pm";
require "$attr{global}{modpath}/FHEM/Blocking.pm";
$hash->{ReadFn} = "pilight_ctrl_Read";
$hash->{WriteFn} = "pilight_ctrl_Write";
$hash->{ReadyFn} = "pilight_ctrl_Ready";
$hash->{DefFn} = "pilight_ctrl_Define";
$hash->{UndefFn} = "pilight_ctrl_Undef";
$hash->{SetFn} = "pilight_ctrl_Set";
$hash->{AttrList}= "ignore brands ContactAsSwitch ".$readingFnAttributes;
$hash->{Clients} = ":pilight_switch:pilight_dimmer:pilight_temp:";
#$hash->{MatchList} = \%matchList; #only for autocreate
}
#####################################
sub pilight_ctrl_Define($$)
{
my ($hash, $def) = @_;
my @a = split("[ \t][ \t]*", $def);
if(@a < 3) {
my $msg = "wrong syntax: define <name> pilight_ctrl hostname:port [5.0]";
Log3 undef, 2, $msg;
return $msg;
}
DevIo_CloseDev($hash);
RemoveInternalTimer($hash);
my $me = $a[0];
my $dev = $a[2];
$hash->{DeviceName} = $dev;
$hash->{STATE} = "defined";
$hash->{API} = "6.0";
$hash->{API} = "5.0" if (defined($a[3]));
$hash->{RETRY_INTERVAL} = 60;
$hash->{helper}{CON} = "define";
$hash->{helper}{CHECK} = 0;
my @sendQueue = ();
$hash->{helper}->{sendQueue} = \@sendQueue;
#$attr{$me}{verbose} = 5;
return pilight_ctrl_TryConnect($hash);
}
sub pilight_ctrl_Close($)
{
my $hash = shift;
my $me = $hash->{NAME};
BlockingKill($hash->{helper}{RUNNING_PID}) if(defined($hash->{helper}{RUNNING_PID}));
RemoveInternalTimer($hash);
foreach my $d (sort keys %defs) {
if(defined($defs{$d}) &&
defined($defs{$d}{IODev}) &&
$defs{$d}{IODev} == $hash)
{
delete $defs{$d}{IODev};
}
}
DevIo_CloseDev($hash);
}
#####################################
sub pilight_ctrl_Undef($$)
{
my ($hash, $arg) = @_;
my $me = $hash->{NAME};
pilight_ctrl_Close($hash);
return undef;
}
#####################################
sub pilight_ctrl_TryConnect($)
{
my $hash = shift;
my $me = $hash->{NAME};
$hash->{helper}{CHECK} = 0;
RemoveInternalTimer($hash);
delete $hash->{NEXT_OPEN};
my $ret = DevIo_OpenDev($hash, 0, "pilight_ctrl_DoInit");
delete $hash->{NEXT_OPEN};
$hash->{helper}{NEXT_TRY} = time()+$hash->{RETRY_INTERVAL};
InternalTimer(gettimeofday()+1,"pilight_ctrl_Check", $hash, 0);
return $ret;
}
#####################################
sub pilight_ctrl_Set($@)
{
my ($hash, @a) = @_;
return "set $hash->{NAME} needs at least one parameter" if(@a < 2);
my $me = shift @a;
my $cmd = shift @a;
return join(" ", sort keys %sets) if ($cmd eq "?");
if ($cmd eq "reset")
{
pilight_ctrl_Close($hash);
return pilight_ctrl_TryConnect($hash);
}
return "Unknown argument $cmd, choose one of ". join(" ", sort keys %sets);
}
#####################################
sub pilight_ctrl_Check($)
{
my $hash = shift;
my $me = $hash->{NAME};
RemoveInternalTimer($hash);
$hash->{helper}{CHECK} = 0 if (!isdigit($hash->{helper}{CHECK}));
$hash->{helper}{CHECK} +=1;
Log3 $me, 5, "$me(Check): $hash->{helper}{CON}";
if($hash->{STATE} eq "disconnected" && !defined($hash->{BASE})) {
Log3 $me, 2, "$me(Check): Could not connect to pilight-daemon $hash->{DeviceName}";
$hash->{helper}{CON} = "disconnected";
}
return if ($hash->{helper}{CON} eq "disconnected");
if ($hash->{helper}{CON} eq "define") {
Log3 $me, 2, "$me(Check): connection to $hash->{DeviceName} failed";
$hash->{helper}{CHECK} = 0;
$hash->{helper}{NEXT_TRY} = time()+$hash->{RETRY_INTERVAL};
return;
}
if ($hash->{helper}{CON} eq "identify") {
if ($hash->{helper}{CHECK} % 3 == 0 && $hash->{helper}{CHECK} < 12) { #retry
pilight_ctrl_DoInit($hash);
} elsif ($hash->{helper}{CHECK} >= 12) {
Log3 $me, 4, "$me(Check): Could not connect to pilight-daemon $hash->{DeviceName} - maybe wrong api version or port";
DevIo_Disconnected($hash);
$hash->{helper}{CHECK} = 0;
$hash->{helper}{CON} = "disconnected";
$hash->{STATE} = "disconnected";
$hash->{helper}{NEXT_TRY} = time()+$hash->{RETRY_INTERVAL};
return;
}
}
if ($hash->{helper}{CON} eq "identify-failed" || $hash->{helper}{CHECK} > 20) {
delete $hash->{helper}{CHECK};
$hash->{helper}{CON} = "disconnected";
Log3 $me, 2, "$me(Check): identification to pilight-daemon $hash->{DeviceName} failed";
$hash->{helper}{NEXT_TRY} = time()+$hash->{RETRY_INTERVAL};
return;
}
if ($hash->{helper}{CON} eq "identify-rejected" || $hash->{helper}{CHECK} > 20) {
Log3 $me, 2, "$me(Parse): connection to pilight-daemon $hash->{DeviceName} rejected";
delete $hash->{helper}{CHECK};
$hash->{helper}{CON} = "disconnected";
$hash->{helper}{NEXT_TRY} = time()+$hash->{RETRY_INTERVAL};
return;
}
if ($hash->{helper}{CON} eq "connected") {
delete $hash->{helper}{CHECK};
delete $hash->{helper}{NEXT_TRY};
return;
}
InternalTimer(gettimeofday()+1,"pilight_ctrl_Check", $hash, 0);
return 1;
}
#####################################
sub pilight_ctrl_DoInit($)
{
my $hash = shift;
return "No FD" if(!$hash || ($^O !~ /Win/ && !defined($hash->{FD})));
my $me = $hash->{NAME};
my $msg;
my $api;
$hash->{helper}{CON} = "identify";
if ($hash->{API} eq "6.0") {
$msg = '{"action":"identify","options":{"receiver":1},"media":"all"}';
} else {
$msg = "{ \"message\": \"client receiver\" }";
}
Log3 $me, 5, "$me(DoInit): send $msg";
pilight_ctrl_SimpleWrite($hash,$msg);
return;
}
#####################################
sub pilight_ctrl_Write($@)
{
my ($hash,$rmsg) = @_;
my $me = $hash->{NAME};
if ($hash->{helper}{CON} ne "connected") {
Log3 $me, 2, "$me(Write): ERROR: no connection to pilight-daemon $hash->{DeviceName}";
return;
}
my ($cName,$state,@args) = split(",",$rmsg);
my $cType = lc($defs{$cName}->{TYPE});
Log3 $me, 4, "$me(Write): RCV ($cType) -> $rmsg";
my $proto = $defs{$cName}->{PROTOCOL};
my $id = $defs{$cName}->{ID};
my $unit = $defs{$cName}->{UNIT};
$id = "\"".$id."\"" if (!isdigit($id));
$unit = "\"".$unit."\"" if (!isdigit($unit));
my $code;
switch($cType){
case m/switch/ {
$code = "{\"protocol\":[\"$proto\"],";
switch ($proto) {
case m/elro/ {$code .= "\"systemcode\":$id,\"unitcode\":$unit,";}
case m/silvercrest/ {$code .= "\"systemcode\":$id,\"unitcode\":$unit,";}
case m/mumbi/ {$code .= "\"systemcode\":$id,\"unitcode\":$unit,";}
case m/brennenstuhl/ {$code .= "\"systemcode\":$id,\"unitcode\":$unit,";}
case m/pollin/ {$code .= "\"systemcode\":$id,\"unitcode\":$unit,";}
case m/impuls/ {$code .= "\"systemcode\":$id,\"programcode\":$unit,";}
case m/rsl366/ {$code .= "\"systemcode\":$id,\"programcode\":$unit,";}
case m/cleverwatts/ { $code .= "\"id\":$id,";
if ($unit eq "\"all\"") {
$code .= "\"all\":1,";
} else {
$code .= "\"unit\":$unit,";
}
}
else {$code .= "\"id\":$id,\"unit\":$unit,";}
}
$code .= "\"$state\":1}";
}
case m/dimmer/ {
$code = "{\"protocol\":[\"$proto\"],\"id\":$id,\"unit\":$unit,\"$state\":1";
$code .= ",\"dimlevel\":$args[0]" if (defined($args[0]));
$code .= "}";
}
else {Log3 $me, 3, "$me(Write): unsupported client ($cName) -> $cType"; return;}
}
return if (!defined($code));
my $msg;
if ($hash->{API} eq "6.0") {
$msg = "{\"action\":\"send\",\"code\":$code}";
} else {
$msg = "{\"message\":\"send\",\"code\":$code}";
}
Log3 $me, 4, "$me(Write): $msg";
# we can't use the same connection because the pilight-daemon close the connection after sending
# we have to create a second connection for sending data
# we do not update the readings - we will do this at the response message
push @{$hash->{helper}->{sendQueue}}, $msg;
pilight_ctrl_SendNonBlocking($hash);
}
#####################################
sub pilight_ctrl_Send($)
{
my ($string) = @_;
my ($me, $host,$data) = split("\\|", $string);
my $hash = $defs{$me};
my ($remote_ip,$remote_port) = split(":",$host);
my $socket = new IO::Socket::INET (
PeerHost => $remote_ip,
PeerPort => $remote_port,
Proto => 'tcp',
);
if (!$socket) {
Log3 $me, 2, "$me(Send): ERROR. Can't open socket to pilight-daemon $remote_ip:$remote_port";
return "$me|0";
}
# we only need a identification to send in 5.0 version
if ($hash->{API} eq "5.0") {
my $msg = "{ \"message\": \"client sender\" }";
my $rcv;
$socket->send($msg);
$socket->recv($rcv,1024);
$rcv =~ s/\n/ /g;
Log3 $me, 5, "$me(Send): RCV -> $rcv";
my $json = JSON->new;
my $jsondata = $json->decode($rcv);
if (!$jsondata)
{
Log3 $me, 2, "$me(Send): ERROR. no JSON response message";
$socket->close();
return "$me|0";
}
my $ret = pilight_ctrl_ClientAccepted($hash,$jsondata);
if ( $ret != 1 ) {
Log3 $me, 2, "$me(Send): ERROR. Connection rejected from pilight-daemon";
$socket->close();
return return "$me|0";
}
}
Log3 $me, 5, "$me(Send): $data";
$socket->send($data);
#6.0 we get a response message
if ($hash->{API} eq "6.0") {
my $rcv;
$socket->recv($rcv,1024);
$rcv =~ s/\n/ /g;
Log3 $me, 5, "$me(Send): RCV -> $rcv";
}
$socket->close();
return "$me|1";
}
#####################################
sub pilight_ctrl_SendDone($)
{
my ($string) = @_;
my ($me, $ok) = split("\\|", $string);
my $hash = $defs{$me};
Log3 $me, 4, "$me(SendDone): message successfully send" if ($ok);
delete($hash->{helper}{RUNNING_PID});
}
#####################################
sub pilight_ctrl_SendAbort($)
{
my ($hash) = @_;
my $me = $hash->{NAME};
Log3 $me, 2, "$me(SendAbort): ERROR. sending aborted";
delete($hash->{helper}{RUNNING_PID});
}
#####################################
sub pilight_ctrl_SendNonBlocking($)
{
my ($hash) = @_;
my $me = $hash->{NAME};
RemoveInternalTimer($hash);
my $queueSize = @{$hash->{helper}->{sendQueue}};
Log3 $me, 5, "$me(SendNonBlocking): queue size $queueSize";
return if ($queueSize <=0);
if (!(exists($hash->{helper}{RUNNING_PID}))) {
my $data = shift @{$hash->{helper}->{sendQueue}};
my $blockingFn = "pilight_ctrl_Send";
my $arg = $me."|".$hash->{DeviceName}."|".$data;
my $finishFn = "pilight_ctrl_SendDone";
my $timeout = 4;
my $abortFn = "pilight_ctrl_SendAbort";
$hash->{helper}{RUNNING_PID} = BlockingCall($blockingFn, $arg, $finishFn, $timeout, $abortFn, $hash);
$hash->{helper}{LAST_SEND_RAW} = $data;
} else {
Log3 $me, 5, "$me(Write): Blocking Call running - will try it later";
}
InternalTimer(gettimeofday()+0.5,"pilight_ctrl_SendNonBlocking", $hash, 0) if ($queueSize > 0);
}
#####################################
sub pilight_ctrl_ClientAccepted(@)
{
my ($hash,$data) = @_;
my $me = $hash->{NAME};
my $ret = 0;
if ($hash->{API} eq "5.0") {
my $msg = (defined($data->{message})) ? $data->{message} : "";
$ret = 1 if(index($msg,"accept") >= 0);
$ret = -1 if(index($msg,"reject") >= 0);
}
else {
my $status = (defined($data->{status})) ? $data->{status} : "";
$ret = 1 if(index($status,"success") >= 0);
$ret = -1 if(index($status,"reject") >= 0);
}
return $ret;
}
#####################################
# called from the global loop, when the select for hash->{FD} reports data
sub pilight_ctrl_Read($)
{
my ($hash) = @_;
my $me = $hash->{NAME};
my $buf = DevIo_SimpleRead($hash);
return "" if(!defined($buf));
my $recdata = $hash->{PARTIAL};
#Log3 $me, 5, "$me(Read): RCV->$buf";
$recdata .= $buf;
while($recdata =~ m/\n/)
{
my $rmsg;
($rmsg,$recdata) = split("\n", $recdata, 2);
$rmsg =~ s/\r//;
pilight_ctrl_Parse($hash, $rmsg) if($rmsg);
}
$hash->{PARTIAL} = $recdata;
}
###########################################
sub pilight_ctrl_Parse($$)
{
my ($hash, $rmsg) = @_;
my $me = $hash->{NAME};
Log3 $me, 4, "$me(Parse): RCV -> $rmsg";
next if(!$rmsg || length($rmsg) < 1);
$hash->{helper}{LAST_RCV_RAW} = $rmsg;
my $json = JSON->new;
my $data = $json->decode($rmsg);
return if (!$data);
if ($hash->{helper}{CON} eq "identify") # we are in identify process
{
$hash->{helper}{CON} = "identify-failed";
my $ret = pilight_ctrl_ClientAccepted($hash,$data);
switch ($ret) {
case 1 { $hash->{helper}{CON} = "connected"; }
case -1 { $hash->{helper}{CON} = "identify-rejected"; }
else { Log3 $me, 3, "$me(Parse): internal error"; }
}
pilight_ctrl_Check($hash);
return;
}
$hash->{helper}{LAST_RCV_JSON} = $json;
my $proto = (defined($data->{protocol})) ? $data->{protocol} : "";
if (!$proto)
{
Log3 $me, 3, "$me(Parse): unknown message -> $rmsg";
return;
}
#brands
my @brands = split(",",AttrVal($me, "brands",""));
foreach my $brand (@brands){
my($search,$replace) = split(":",$brand);
next if (!defined($search) || !defined($replace));
$proto =~ s/$search/$replace/g;
}
$hash->{helper}{LAST_RCV_PROTOCOL} = $proto;
my $s = ($hash->{API} eq "5.0") ? "code" : "message";
my $state = (defined($data->{$s}{state})) ? $data->{$s}{state} : "";
my $all = (defined($data->{$s}{all})) ? $data->{$s}{all} : "";
my $id = "";
foreach my $sid (@idList) {
$id = (defined($data->{$s}{$sid})) ? $data->{$s}{$sid} : "";
last if ($id ne "");
}
my $unit = "";
foreach my $sunit (@unitList) {
$unit = (defined($data->{$s}{$sunit})) ? $data->{$s}{$sunit} : "";
last if ($unit ne "");
}
my @ignoreIDs = split(",",AttrVal($me, "ignore",""));
my %ignoreHash;
@ignoreHash{@ignoreIDs}=();
if (exists $ignoreHash{"$proto:$id"} || exists $ignoreHash{"$proto:*"}) {
Log3 $me, 5, "$me(Parse): $proto:$id is in ignore list";
return;
}
readingsBeginUpdate($hash);
readingsBulkUpdate($hash,"rcv_raw",$rmsg);
readingsEndUpdate($hash, 1);
$unit = "all" if ($unit eq "" && $all ne "");
my $protoID = -1;
switch($proto){
#switch
case m/switch/ {$protoID = 1;}
case m/elro/ {$protoID = 1;}
case m/silvercrest/ {$protoID = 1;}
case m/mumbi/ {$protoID = 1;}
case m/brennenstuhl/{$protoID = 1;}
case m/pollin/ {$protoID = 1;}
case m/impuls/ {$protoID = 1;}
case m/rsl366/ {$protoID = 1;}
case m/cleverwatts/ {$protoID = 1;}
case m/intertechno_old/ {$protoID = 1;}
case m/dimmer/ {$protoID = 2;}
case m/contact/ {$protoID = 3;}
#Weather Stations temperature, humidity
case m/alecto/ {$protoID = 4;}
case m/auriol/ {$protoID = 4;}
case m/ninjablocks/ {$protoID = 4;}
case m/tfa/ {$protoID = 4;}
case m/teknihall/ {$protoID = 4;}
#gpio temperature, humidity sensors
case m/dht11/ {$protoID = 4;}
case m/dht22/ {$protoID = 4;}
case m/ds18b20/ {$protoID = 4;}
case m/ds18s20/ {$protoID = 4;}
case m/cpu_temp/ {$protoID = 4;}
case m/lm75/ {$protoID = 4;}
case m/lm76/ {$protoID = 4;}
case m/screen/ {return;}
case m/firmware/ {return;}
else {Log3 $me, 3, "$me(Parse): unknown protocol -> $proto"; return;}
}
if ($id eq "") {
Log3 $me, 3, "$me(Parse): ERROR no or unknown id $rmsg";
return;
}
switch($protoID){
case 1 { return Dispatch($hash, "SWITCH,$proto,$id,$unit,$state",undef ); }
case 2 {
my $dimlevel = (defined($data->{$s}{dimlevel})) ? $data->{$s}{dimlevel} : "";
my $msg = "DIMMER,$proto,$id,$unit,$state";
$msg.= ",$dimlevel" if ($dimlevel ne "");
return Dispatch($hash, $msg ,undef);
}
case 3 {
my $asSwitch = $attr{$me}{ContactAsSwitch};
if ( defined($asSwitch) && $asSwitch =~ /$id/) {
$proto =~ s/contact/switch/g;
$state =~ s/opened/on/g;
$state =~ s/closed/off/g;
Log3 $me, 5, "$me(Parse): contact as switch for $id";
return Dispatch($hash, "SWITCH,$proto,$id,$unit,$state",undef);
}
return;
}
case 4 {
my $temp = (defined($data->{$s}{temperature})) ? $data->{$s}{temperature} : "";
return if ($temp eq "");
my $humidity = (defined($data->{$s}{humidity})) ? $data->{$s}{humidity} : "";
my $msg = "PITEMP,$proto,$id,$temp,$humidity";
return Dispatch($hash, $msg,undef);
}
else {Log3 $me, 3, "$me(Parse): unknown protocol -> $proto"; return;}
}
return;
}
#####################################
# called from gobal loop to try reconnection
sub pilight_ctrl_Ready($)
{
my ($hash) = @_;
my $me = $hash->{NAME};
if($hash->{STATE} eq "disconnected" && !defined($hash->{BASE}))
{
return if(defined($hash->{helper}{NEXT_TRY}) && $hash->{helper}{NEXT_TRY} && time() < $hash->{helper}{NEXT_TRY});
return pilight_ctrl_TryConnect($hash);
}
}
#####################################
sub pilight_ctrl_SimpleWrite(@)
{
my ($hash, $msg, $nonl) = @_;
return if(!$hash);
my $me = $hash->{NAME};
Log3 $me, 4, "$me(SimpleWrite): snd -> $msg";
$msg .= "\n" unless($nonl);
DevIo_SimpleWrite($hash,$msg,0);
}
1;
=pod
=begin html
<a name="pilight_ctrl"></a>
<h3>pilight_ctrl</h3>
<ul>
pilight_ctrl is the base device for the communication (sending and receiving) with the pilight-daemon.<br>
You have to define client devices e.q. pilight_switch for switches.<br>
Further information to pilight: <a href="http://www.pilight.org/">http://www.pilight.org/</a><br><br>
Further information to pilight protocols: <a href="http://wiki.pilight.org/doku.php/protocols#protocols">http://wiki.pilight.org/doku.php/protocols#protocols</a><br>
Currently supported: <br>
<ul>
<li>Switches:</li>
<li>Dimmers:</li>
<li>Temperature and humitity sensors</li>
</ul>
<br><br>
<a name="pilight_ctrl_define"></a>
<b>Define</b>
<ul>
<code>define &lt;name&gt; pilight_ctrl ip:port [api]</code>
ip:port is the IP address and port of the pilight-daemon<br>
api specifies the pilight api version - default 6.0<br>
<br>
Example:
<ul>
<code>define myctrl pilight_ctrl localhost:5000 5.0</code><br>
<code>define myctrl pilight_ctrl 192.168.1.1:5000</code><br>
</ul>
</ul>
<br>
<a name="pilight_ctrl_set"></a>
<p><b>Set</b></p>
<ul>
<li>
<b>reset</b>
</li>
</ul>
<br>
<a name="pilight_ctrl_readings"></a>
<p><b>Readings</b></p>
<ul>
<li>
rcv_raw<br>
The last complete received message in json format.
</li>
</ul>
<br>
<a name="pilight_ctrl_attr"></a>
<b>Attributes</b>
<ul>
<li><a name="ignore">ignore</a><br>
Comma separated list of protocol:id combinations to ignore.<br>
Example: <code>ignore tfa:0 </code>
</li>
<li><a name="brands">brands</a><br>
Comma separated list of <search>:<replace> combinations to rename protocol names. <br>
pilight uses different protocol names for the same protocol e.q. arctech_switch and kaku_switch<br>
Example: <code>brands archtech:kaku</code>
</li>
<li><a name="ContactAsSwitch">ContactAsSwitch</a><br>
Comma separated list of ids which correspond to a contact but will be interpreted as switch. <br>
In this case opened will be interpreted as on and closed as off.<br>
Example: <code>ContactAsSwitch 12345</code>
</li>
</ul>
<br>
</ul>
=end html
=cut

View File

@ -0,0 +1,188 @@
##############################################
# $Id: 20_pilight_dimmer.pm 2015-02-26 Risiko $
#
# Usage
#
# define <name> pilight_dimmer <protocol> <id> <unit>
#
# Changelog
#
# V 0.10 2015-02-26 - initial beta version
##############################################
package main;
use strict;
use warnings;
use Time::HiRes qw(gettimeofday);
use JSON;
sub pilight_dimmer_Parse($$);
sub pilight_dimmer_Define($$);
sub pilight_dimmer_Fingerprint($$);
sub pilight_dimmer_Initialize($)
{
my ($hash) = @_;
$hash->{DefFn} = "pilight_dimmer_Define";
$hash->{Match} = "^SWITCH|^DIMMER";
$hash->{ParseFn} = "pilight_dimmer_Parse";
$hash->{SetFn} = "pilight_dimmer_Set";
$hash->{AttrList} = "dimlevel_max ".$readingFnAttributes;
}
#####################################
sub pilight_dimmer_Define($$)
{
my ($hash, $def) = @_;
my @a = split("[ \t][ \t]*", $def);
if(@a < 5) {
my $msg = "wrong syntax: define <name> pilight_dimmer <protocol> <id> <unit>";
Log3 undef, 2, $msg;
return $msg;
}
my $me = $a[0];
my $protocol = $a[2];
my $id = $a[3];
my $unit = $a[4];
$hash->{STATE} = "defined";
$hash->{PROTOCOL} = lc($protocol);
$hash->{ID} = $id;
$hash->{UNIT} = $unit;
#$attr{$me}{verbose} = 5;
$modules{pilight_dimmer}{defptr}{lc($id)}{$me} = $hash;
AssignIoPort($hash);
return undef;
}
###########################################
sub pilight_dimmer_Parse($$)
{
my ($mhash, $rmsg, $rawdata) = @_;
my $backend = $mhash->{NAME};
Log3 $backend, 4, "pilight_dimmer_Parse: RCV -> $rmsg";
my ($dev,$protocol,$id,$unit,$state,$dimlevel) = split(",",$rmsg);
return () if($dev ne "SWITCH" && $dev ne "DIMMER");
my $chash;
foreach my $n (keys %{ $modules{pilight_dimmer}{defptr}{lc($id)} }) {
my $lh = $modules{pilight_dimmer}{defptr}{$id}{$n};
next if ( !defined($lh->{UNIT}) );
if ($lh->{ID} eq $id && $lh->{UNIT} eq $unit) {
$chash = $lh;
last;
}
}
return () if (!defined($chash->{NAME}));
readingsBeginUpdate($chash);
readingsBulkUpdate($chash,"state",$state);
readingsBulkUpdate($chash,"dimlevel",$dimlevel) if (defined($dimlevel));
readingsEndUpdate($chash, 1);
return $chash->{NAME};
}
#####################################
sub pilight_dimmer_Set($$)
{
my ($hash, @a) = @_;
my $me = shift @a;
return "no set value specified" if(int(@a) < 1);
my $maxlevel = $attr{$me}{dimlevel_max};
$maxlevel = 15 if (!defined($maxlevel));
return "Unknown argument ?, choose one of on off dimlevel:slider,0,1,$maxlevel" if($a[0] eq "?");
my $msg = "$me,";
if ($a[0] eq "dimlevel") {
$msg .= "on,".$a[1];
} else {
$msg .= $a[0];
}
Log3 $me, 4, "$me(Set): $msg";
IOWrite($hash, $msg);
}
1;
=pod
=begin html
<a name="pilight_dimmer"></a>
<h3>pilight_dimmer</h3>
<ul>
pilight_dimmer represents a dimmmer controled with\from pilight<br>
You have to define the base device pilight_ctrl first.<br>
Further information to pilight: <a href="http://www.pilight.org/">http://www.pilight.org/</a><br>
Supported dimmers: <a href="http://wiki.pilight.org/doku.php/protocols#dimmers">http://wiki.pilight.org/doku.php/protocols#dimmers</a><br>
<br><br>
<a name="pilight_dimmer_define"></a>
<b>Define</b>
<ul>
<code>define &lt;name&gt; pilight_dimmer protocol id unit</code>
<br><br>
Example:
<ul>
<code>define myctrl pilight_dimmer kaku_dimmer 13483668 0</code><br>
</ul>
</ul>
<br>
<a name="pilight_dimmer_set"></a>
<p><b>Set</b></p>
<ul>
<li>
<b>on</b>
</li>
<li>
<b>off</b>
</li>
<li>
<b>dimlevel</b>
</li>
</ul>
<br>
<a name="pilight_dimmer_readings"></a>
<p><b>Readings</b></p>
<ul>
<li>
state<br>
state of the dimmer on or off
</li>
<li>
dimlevel<br>
dimlevel of the dimmer
</li>
</ul>
<br>
<a name="pilight_dimmer_attr"></a>
<b>Attributes</b>
<ul>
<li><a name="dimlevel_max">dimlevel_max</a><br>
Maximum of the dimlevel - default 15<br>
</li>
</ul>
<br>
</ul>
=end html
=cut

View File

@ -0,0 +1,165 @@
##############################################
# $Id: 20_pilight_switch.pm 0.11 2015-03-29 Risiko $
#
# Usage
#
# define <name> pilight_switch <protocol> <id> <unit>
#
# Changelog
#
# V 0.10 2015-02-22 - initial beta version
# V 0.11 2015-03-29 - FIX: $readingFnAttributes
##############################################
package main;
use strict;
use warnings;
use Time::HiRes qw(gettimeofday);
use JSON;
sub pilight_switch_Parse($$);
sub pilight_switch_Define($$);
sub pilight_switch_Fingerprint($$);
sub pilight_switch_Initialize($)
{
my ($hash) = @_;
$hash->{DefFn} = "pilight_switch_Define";
$hash->{Match} = "^SWITCH";
$hash->{ParseFn} = "pilight_switch_Parse";
$hash->{SetFn} = "pilight_switch_Set";
$hash->{AttrList} = $readingFnAttributes;
}
#####################################
sub pilight_switch_Define($$)
{
my ($hash, $def) = @_;
my @a = split("[ \t][ \t]*", $def);
if(@a < 5) {
my $msg = "wrong syntax: define <name> pilight_switch <protocol> <id> <unit>";
Log3 undef, 2, $msg;
return $msg;
}
my $me = $a[0];
my $protocol = $a[2];
my $id = $a[3];
my $unit = $a[4];
$hash->{STATE} = "defined";
$hash->{PROTOCOL} = lc($protocol);
$hash->{ID} = $id;
$hash->{UNIT} = $unit;
#$attr{$me}{verbose} = 5;
$modules{pilight_switch}{defptr}{lc($protocol)}{$me} = $hash;
AssignIoPort($hash);
return undef;
}
###########################################
sub pilight_switch_Parse($$)
{
my ($mhash, $rmsg, $rawdata) = @_;
my $backend = $mhash->{NAME};
Log3 $backend, 4, "pilight_switch_Parse: RCV -> $rmsg";
my ($dev,$protocol,$id,$unit,$state,@args) = split(",",$rmsg);
return () if($dev ne "SWITCH");
my $chash;
foreach my $n (keys %{ $modules{pilight_switch}{defptr}{lc($protocol)} }) {
my $lh = $modules{pilight_switch}{defptr}{$protocol}{$n};
next if ( !defined($lh->{ID}) || !defined($lh->{UNIT}) );
if ($lh->{ID} eq $id && $lh->{UNIT} eq $unit) {
$chash = $lh;
last;
}
}
return () if (!defined($chash->{NAME}));
readingsBeginUpdate($chash);
readingsBulkUpdate($chash,"state",$state);
readingsEndUpdate($chash, 1);
return $chash->{NAME};
}
#####################################
sub pilight_switch_Set($$)
{
my ($hash, @a) = @_;
my $me = shift @a;
return "no set value specified" if(int(@a) < 1);
return "Unknown argument ?, choose one of on off" if($a[0] eq "?");
my $v = join(" ", @a);
Log3 $me, 4, "$me(Set): $v";
#readingsSingleUpdate($hash,"state",$v,1);
my $msg = "$me,$v";
IOWrite($hash, $msg);
}
1;
=pod
=begin html
<a name="pilight_switch"></a>
<h3>pilight_switch</h3>
<ul>
pilight_switch represents a switch controled with\from pilight<br>
You have to define the base device pilight_ctrl first.<br>
Further information to pilight: <a href="http://www.pilight.org/">http://www.pilight.org/</a><br>
Supported switches: <a href="http://wiki.pilight.org/doku.php/protocols#switches">http://wiki.pilight.org/doku.php/protocols#switches</a><br>
<br>
<a name="pilight_switch_define"></a>
<b>Define</b>
<ul>
<code>define &lt;name&gt; pilight_switch protocol id unit</code>
<br><br>
Example:
<ul>
<code>define myctrl pilight_switch kaku_switch_old 0 0</code><br>
</ul>
</ul>
<br>
<a name="pilight_switch_set"></a>
<p><b>Set</b></p>
<ul>
<li>
<b>on</b>
</li>
<li>
<b>off</b>
</li>
</ul>
<br>
<a name="pilight_switch_readings"></a>
<p><b>Readings</b></p>
<ul>
<li>
state<br>
state of the switch on or off
</li>
</ul>
<br>
</ul>
=end html
=cut

View File

@ -0,0 +1,142 @@
##############################################
# $Id: 20_pilight_temp.pm 0.11 2015-03-29 Risiko $
#
# Usage
#
# define <name> pilight_temp <protocol> <id>
#
# Changelog
#
# V 0.10 2015-03-29 - initial beta version
# V 0.11 2015-03-29 - FIX: $readingFnAttributes
##############################################
package main;
use strict;
use warnings;
use Time::HiRes qw(gettimeofday);
use JSON;
sub pilight_temp_Parse($$);
sub pilight_temp_Define($$);
sub pilight_temp_Fingerprint($$);
sub pilight_temp_Initialize($)
{
my ($hash) = @_;
$hash->{DefFn} = "pilight_temp_Define";
$hash->{Match} = "^PITEMP";
$hash->{ParseFn} = "pilight_temp_Parse";
$hash->{AttrList} = $readingFnAttributes;
}
#####################################
sub pilight_temp_Define($$)
{
my ($hash, $def) = @_;
my @a = split("[ \t][ \t]*", $def);
if(@a < 4) {
my $msg = "wrong syntax: define <name> pilight_temp <protocol> <id>";
Log3 undef, 2, $msg;
return $msg;
}
my $me = $a[0];
my $protocol = $a[2];
my $id = $a[3];
$hash->{STATE} = "defined";
$hash->{PROTOCOL} = lc($protocol);
$hash->{ID} = $id;
#$attr{$me}{verbose} = 5;
$modules{pilight_temp}{defptr}{lc($protocol)}{$me} = $hash;
AssignIoPort($hash);
return undef;
}
###########################################
sub pilight_temp_Parse($$)
{
my ($mhash, $rmsg, $rawdata) = @_;
my $backend = $mhash->{NAME};
Log3 $backend, 4, "pilight_temp_Parse: RCV -> $rmsg";
my ($dev,$protocol,$id,$temp,$humidity,@args) = split(",",$rmsg);
return () if($dev ne "PITEMP");
my $chash;
foreach my $n (keys %{ $modules{pilight_temp}{defptr}{lc($protocol)} }) {
my $lh = $modules{pilight_temp}{defptr}{$protocol}{$n};
next if ( !defined($lh->{ID}) );
if ($lh->{ID} eq $id) {
$chash = $lh;
last;
}
}
return () if (!defined($chash->{NAME}));
readingsBeginUpdate($chash);
readingsBulkUpdate($chash,"state",$temp);
readingsBulkUpdate($chash,"temperature",$temp);
readingsBulkUpdate($chash,"humidity",$humidity) if (defined($humidity) && $humidity ne "");
readingsEndUpdate($chash, 1);
return $chash->{NAME};
}
1;
=pod
=begin html
<a name="pilight_temp"></a>
<h3>pilight_temp</h3>
<ul>
pilight_temp represents a temperature and humidity sensor receiving dat from pilight<br>
You have to define the base device pilight_ctrl first.<br>
Further information to pilight: <a href="http://www.pilight.org/">http://www.pilight.org/</a><br>
Supported Sensors: <a href="http://wiki.pilight.org/doku.php/protocols#switches">http://wiki.pilight.org/doku.php/protocols#weather_stations</a><br>
<br>
<a name="pilight_temp_define"></a>
<b>Define</b>
<ul>
<code>define &lt;name&gt; pilight_temp protocol id</code>
<br><br>
Example:
<ul>
<code>define myctrl pilight_temp alecto_wsd17 100</code><br>
</ul>
</ul>
<br>
<a name="pilight_temp_readings"></a>
<p><b>Readings</b></p>
<ul>
<li>
state<br>
present the current temperature
</li>
<li>
temperature<br>
present the current temperature
</li>
<li>
humidity<br>
present the current humidity (if sensor support it)
</li>
</ul>
<br>
</ul>
=end html
=cut

View File

@ -627,3 +627,6 @@
- Sat Mar 21 2015 (loredo)
- reading residentsGuests in module 10_RESIDENTS was renamed to residentsTotalGuests
- introducing new readings in module 10_RESIDENTS: residentsTotalGuestsAbsent and residentsTotalGuestsPresent
- Sat Apr 25 2015 (risiko)
- added new modules to support pilight (sending and receiving): switch, dimmer, temperature and humidity

View File

@ -61,6 +61,7 @@ FHEM/10_SOMFY.pm thdankert http://forum.fhem.de Sonstiges
FHEM/10_UNIRoll.pm c-herrmann http://forum.fhem.de SlowRF
FHEM/10_ZWave.pm rudolfkoenig http://forum.fhem.de ZWave
FHEM/10_RESIDENTS.pm loredo http://forum.fhem.de Automatisierung
FHEM/10_pilight_ctrl.pm risiko http://forum.fhem.de Sonstige Systeme
FHEM/11_FHT.pm rudolfkoenig http://forum.fhem.de SlowRF
FHEM/11_FHT8V.pm rudolfkoenig http://forum.fhem.de SlowRF
FHEM/11_OWDevice.pm borisneubert/mfr69bs http://forum.fhem.de 1Wire
@ -111,6 +112,9 @@ FHEM/30_HUEBridge.pm justme1968 http://forum.fhem.de Beleuchtu
FHEM/30_LIGHTIFY.pm justme1968 http://forum.fhem.de Beleuchtung
FHEM/30_MilightBridge.pm mattwire http://forum.fhem.de Beleuchtung
FHEM/30_ENECSYSGW.pm akw http://forum.fhem.de Sonstige Systeme
FHEM/30_pilight_dimmer.pm risiko http://forum.fhem.de Sonstige Systeme
FHEM/30_pilight_switch.pm risiko http://forum.fhem.de Sonstige Systeme
FHEM/30_pilight_temp.pm risiko http://forum.fhem.de Sonstige Systeme
FHEM/31_HUEDevice.pm justme1968 http://forum.fhem.de Beleuchtung
FHEM/31_MilightDevice.pm mattwire http://forum.fhem.de Beleuchtung
FHEM/31_ENECSYSINV.pm akw http://forum.fhem.de Sonstige Systeme