2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-03-03 16:56:54 +00:00

36_EleroSwitch: new module for Elero switch devices

36_EleroStick: support for the new EleroSwitch module
36_EleroDrive: modifications to be compatible with EleroSwitch

git-svn-id: https://svn.fhem.de/fhem/trunk@14777 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
HCS 2017-07-24 10:48:30 +00:00
parent f190a4a460
commit 1b57cb2844
3 changed files with 445 additions and 84 deletions

View File

@ -25,6 +25,8 @@ sub EleroDrive_Initialize($) {
$hash->{AttrFn} = "EleroDrive_Attr"; $hash->{AttrFn} = "EleroDrive_Attr";
$hash->{AttrList} = "IODev " . $hash->{AttrList} = "IODev " .
"TopToBottomTime " . "TopToBottomTime " .
"TiltPercent " .
"IntermediatePercent " .
"$readingFnAttributes "; "$readingFnAttributes ";
$hash->{noAutocreatedFilelog} = 1; $hash->{noAutocreatedFilelog} = 1;
@ -83,7 +85,6 @@ sub EleroDrive_ToFixPosition($$) {
my ( $hash, $position) = @_; my ( $hash, $position) = @_;
my $channel = $hash->{channel}; my $channel = $hash->{channel};
###my $iodev = $hash->{IODev}->{NAME};
my $head = 'aa'; my $head = 'aa';
my $msgLength = '05'; my $msgLength = '05';
@ -134,9 +135,7 @@ sub EleroDrive_ToFixPosition($$) {
my $byteMsg = $head.$msgLength.$msgCmd.$firstChannels.$secondChannels.$payload.$checksum; my $byteMsg = $head.$msgLength.$msgCmd.$firstChannels.$secondChannels.$payload.$checksum;
###debugLog("EleroDrive_Set->IOWrite: byteMsg=$byteMsg"); IOWrite($hash, "send", $byteMsg);
IOWrite($hash, "send", $byteMsg);
} }
} }
@ -146,8 +145,6 @@ sub EleroDrive_ToFixPosition($$) {
sub EleroDrive_ToAnyPosition($$) { sub EleroDrive_ToAnyPosition($$) {
my ( $hash, $position) = @_; my ( $hash, $position) = @_;
my $name = $hash->{NAME}; my $name = $hash->{NAME};
###debugLog("ToAnyPosition: $position");
} }
#======================================================================================= #=======================================================================================
@ -228,9 +225,21 @@ sub EleroDrive_Parse($$) {
while ($bytes != 1 and $channel <= 15) { while ($bytes != 1 and $channel <= 15) {
$bytes = $bytes >> 1; $bytes = $bytes >> 1;
$channel++; $channel++;
} }
if($channel <= 15) { if($channel <= 15) {
# Check if it is defined as a switch device
my $switchChannels = AttrVal($name, "SwitchChannels", undef);
if(defined $switchChannels) {
my @channelList = split /,/, $switchChannels;
if ($channel ~~ @channelList) {
return undef;
}
}
my $rhash = $modules{EleroDrive}{defptr}{$channel};
my $rname = $rhash->{NAME};
# get status # get status
my $statusByte = substr($buffer,10,2); my $statusByte = substr($buffer,10,2);
@ -256,8 +265,8 @@ sub EleroDrive_Parse($$) {
my %percentDefinitions = ('00' => 50, my %percentDefinitions = ('00' => 50,
'01' => 0, '01' => 0,
'02' => 100, '02' => 100,
'03' => 50, '03' => AttrVal($rname, "IntermediatePercent", 50),
'04' => 50, '04' => AttrVal($rname, "TiltPercent", 50),
'05' => -1, '05' => -1,
'06' => -1, '06' => -1,
'07' => -1, '07' => -1,
@ -274,13 +283,8 @@ sub EleroDrive_Parse($$) {
my $newstate = $deviceStati{$statusByte}; my $newstate = $deviceStati{$statusByte};
my $percentClosed = $percentDefinitions{$statusByte}; my $percentClosed = $percentDefinitions{$statusByte};
my $rhash = $modules{EleroDrive}{defptr}{$channel};
my $rname = $rhash->{NAME};
if($modules{EleroDrive}{defptr}{$channel}) { if($modules{EleroDrive}{defptr}{$channel}) {
###debugLog("$rname -> parsed $msg for channel $channel: $newstate");
readingsBeginUpdate($rhash); readingsBeginUpdate($rhash);
readingsBulkUpdate($rhash, "state", $newstate); readingsBulkUpdate($rhash, "state", $newstate);
readingsBulkUpdate($rhash, "position", $newstate); readingsBulkUpdate($rhash, "position", $newstate);
@ -294,7 +298,6 @@ sub EleroDrive_Parse($$) {
return @list; return @list;
} }
else { else {
###debugLog("$name -> AUTOCREATE " . $hash->{IODev}->{NAME});
return "UNDEFINED EleroDrive_$channel EleroDrive $channel"; return "UNDEFINED EleroDrive_$channel EleroDrive $channel";
} }
} }
@ -374,6 +377,13 @@ sub EleroDrive_OnRefreshTimer($$) {
<li>TopToBottomTime<br> <li>TopToBottomTime<br>
The time in seconds this drive needs for a complete run from the top to the bottom or vice versa</li> The time in seconds this drive needs for a complete run from the top to the bottom or vice versa</li>
<li>IntermediatePercent<br>
Percent open when in intermediate position</li>
<li>TiltPercent<br>
Percent open when in tilt position</li>
</ul><br> </ul><br>
<a name="EleroDrive_Readings"></a> <a name="EleroDrive_Readings"></a>

View File

@ -1,23 +1,14 @@
# $Id$ # $Id$
# ToDo-List
# ---------
# [ ] Disable the timer when a Drive asks for information to avoid conflicts
# After getting a message in EleroStick_Write we must interupt the timer
# for "ChannelTimeout" seconds
#
# [ ] Perhaps we need a cache for incoming commands that delays the incoming commands
# for "ChannelTimeout" seconds to give the previous command a chance to hear its acknowledge
#
#
package main; package main;
use strict; use strict;
use warnings; use warnings;
use Time::HiRes qw(gettimeofday); use Time::HiRes qw(gettimeofday);
my $clients = ":EleroDrive"; my $clients = ":EleroDrive:EleroSwitch";
my %matchList = ("1:EleroDrive" => ".*",); my %matchList = ("1:EleroDrive" => ".*",
"2:EleroSwitch" => ".*");
# Answer Types # Answer Types
my $easy_confirm = "aa044b"; my $easy_confirm = "aa044b";
@ -45,6 +36,7 @@ sub EleroStick_Initialize($) {
"Interval " . "Interval " .
"Delay " . "Delay " .
"DisableTimer:1,0 " . "DisableTimer:1,0 " .
"SwitchChannels " .
"$readingFnAttributes "; "$readingFnAttributes ";
} }
@ -61,8 +53,7 @@ sub EleroStick_Enqueue($$) {
if(!$hash->{QUEUE}) { if(!$hash->{QUEUE}) {
$hash->{QUEUE} = [""]; $hash->{QUEUE} = [""];
###debugLog($name, "QUEUE created with: $data");
EleroStick_SimpleWrite($hash, $data); EleroStick_SimpleWrite($hash, $data);
my $timerName = $name . "#QueueTimer"; my $timerName = $name . "#QueueTimer";
my $interval = 0.1; my $interval = 0.1;
@ -71,7 +62,6 @@ sub EleroStick_Enqueue($$) {
} }
else { else {
push(@{$hash->{QUEUE}}, $data); push(@{$hash->{QUEUE}}, $data);
###debugLog($name, "Pushed to QUEUE: $data");
} }
} }
@ -84,12 +74,9 @@ sub EleroStick_StartQueueTimer($) {
my $interval = AttrVal($name, "Delay", 0.5); my $interval = AttrVal($name, "Delay", 0.5);
InternalTimer(gettimeofday() + $interval, "EleroStick_OnQueueTimer", $timerName, 0); InternalTimer(gettimeofday() + $interval, "EleroStick_OnQueueTimer", $timerName, 0);
####debugLog($name, "Timer started: $timerName");
} }
#======================================================================================= #=======================================================================================
sub EleroStick_OnQueueTimer($) { sub EleroStick_OnQueueTimer($) {
my ($timerName) = @_; my ($timerName) = @_;
@ -97,13 +84,10 @@ sub EleroStick_OnQueueTimer($) {
my $hash = $defs{$name}; my $hash = $defs{$name};
my $queue = $hash->{QUEUE}; my $queue = $hash->{QUEUE};
###debugLog($name, "OnQueueTimer");
if (defined($queue) && @{$queue} > 0) { if (defined($queue) && @{$queue} > 0) {
my $data = $queue->[0]; my $data = $queue->[0];
if ($data ne "") { if ($data ne "") {
EleroStick_SimpleWrite($hash, $data); EleroStick_SimpleWrite($hash, $data);
###debugLog($name, "Timer msg=$data");
} }
shift(@{$queue}); shift(@{$queue});
@ -203,8 +187,6 @@ sub EleroStick_SendEasyCheck($) {
my $byteMsg = $head.$msgLength.$msgCmd.$checksum; my $byteMsg = $head.$msgLength.$msgCmd.$checksum;
EleroStick_Enqueue($hash, $byteMsg); EleroStick_Enqueue($hash, $byteMsg);
###EleroStick_SimpleWrite($hash, $byteMsg)
} }
} }
@ -272,7 +254,6 @@ sub EleroStick_OnTimer($$) {
my $now = index($channels, "x"); my $now = index($channels, "x");
if($now ne -1) { if($now ne -1) {
substr($channels, $now, 1, "1"); substr($channels, $now, 1, "1");
###debugLog($name, "now " . ($now +1));
EleroStick_SendEasyInfo($hash, $now +1); EleroStick_SendEasyInfo($hash, $now +1);
for(my $i = $now +1; $i<15; $i++) { for(my $i = $now +1; $i<15; $i++) {
@ -301,6 +282,22 @@ sub EleroStick_OnTimer($$) {
#======================================================================================= #=======================================================================================
sub EleroStick_Set($@) { sub EleroStick_Set($@) {
my ($hash, @a) = @_;
my $name = shift @a;
my $cmd = shift @a;
my $arg = join(" ", @a);
my $list = "parse";
return $list if( $cmd eq '?' || $cmd eq '');
if ($cmd eq "parse") {
$hash->{buffer} = $arg;
EleroStick_Parse($hash);
}
else {
return "Unknown argument $cmd, choose one of ".$list;
}
return undef; return undef;
} }
@ -318,18 +315,61 @@ sub EleroStick_Write($$) {
# Send to the transmitter stick # Send to the transmitter stick
if($cmd eq 'send'){ if($cmd eq 'send'){
###debugLog($name, "EleroStick send cmd=send msg=$msg");
EleroStick_Enqueue($hash, $msg); EleroStick_Enqueue($hash, $msg);
} }
# Request status for a channel # Request status for a channel
elsif ($cmd eq 'refresh') { elsif ($cmd eq 'refresh') {
###debugLog($name, "EleroStick cmd=refresh msg=$msg");
EleroStick_SendEasyInfo($hash, $msg); EleroStick_SendEasyInfo($hash, $msg);
} }
} }
#=======================================================================================
sub EleroStick_Parse($) {
my ($hash) = @_;
my $name = $hash->{NAME};
readingsSingleUpdate($hash,'AnswerMsg', $hash->{buffer},1);
if(index($hash->{buffer}, $easy_confirm, 0) == 0) {
$hash->{lastAnswerType} = "easy_confirm";
my $cc = substr($hash->{buffer},6,4);
my $firstChannels = substr($cc,0,2);
my $secondChannels = substr($cc,2,2);
my $bytes = $firstChannels.$secondChannels ;
$bytes = hex ($bytes);
my $dummy="";
my $learndChannelFound = 0;
for (my $i=0; $i < 15; $i++) {
if($bytes & 1 << $i) {
if(!$learndChannelFound) {
$dummy = $dummy . "x";
$learndChannelFound = 1;
}
else {
$dummy = $dummy . "1";
}
}
else {
$dummy = $dummy . "0";
}
}
$hash->{channels} = $dummy;
}
elsif(index($hash->{buffer}, $easy_ack, 0) == 0) {
$hash->{lastAnswerType} = "easy_ack";
my $buffer = $hash->{buffer};
Dispatch($hash, $buffer, "");
}
readingsSingleUpdate($hash, 'AnswerType', $hash->{lastAnswerType}, 1);
Log3 $name, 4, "Current buffer content: " . $hash->{buffer}." Name ". $hash->{NAME};
}
#======================================================================================= #=======================================================================================
sub EleroStick_Read($) { sub EleroStick_Read($) {
@ -357,45 +397,7 @@ sub EleroStick_Read($) {
my $calLen = ($strLen * 2) + 4; my $calLen = ($strLen * 2) + 4;
if($calLen == length($hash->{buffer})){ if($calLen == length($hash->{buffer})){
# Die Länge der Nachricht entspricht der Vorgabe im Header EleroStick_Parse($hash);
readingsSingleUpdate($hash,'AnswerMsg', $hash->{buffer},1);
if(index($hash->{buffer}, $easy_confirm, 0) == 0) {
$hash->{lastAnswerType} = "easy_confirm";
my $cc = substr($hash->{buffer},6,4);
my $firstChannels = substr($cc,0,2);
my $secondChannels = substr($cc,2,2);
my $bytes = $firstChannels.$secondChannels ;
$bytes = hex ($bytes);
my $dummy="";
my $learndChannelFound = 0;
for (my $i=0; $i < 15; $i++) {
if($bytes & 1 << $i) {
if(!$learndChannelFound) {
$dummy = $dummy . "x";
$learndChannelFound = 1;
}
else {
$dummy = $dummy . "1";
}
}
else {
$dummy = $dummy . "0";
}
}
$hash->{channels} = $dummy;
}
elsif(index($hash->{buffer}, $easy_ack, 0) == 0) {
$hash->{lastAnswerType} = "easy_ack";
my $buffer = $hash->{buffer};
Dispatch($hash, $buffer, "");
}
readingsSingleUpdate($hash, 'AnswerType', $hash->{lastAnswerType}, 1);
Log3 $name, 4, "Current buffer content: " . $hash->{buffer}." Name ". $hash->{NAME};
} }
else { else {
# Wait for the rest of the data # Wait for the rest of the data
@ -416,7 +418,6 @@ sub EleroStick_Ready($) {
} }
else { else {
EleroStick_SendEasyCheck($hash); EleroStick_SendEasyCheck($hash);
###debugLog($name, "EleroStick_Ready -> SendEasyInfo");
} }
return $openResult if($hash->{STATE} eq "disconnected"); return $openResult if($hash->{STATE} eq "disconnected");
@ -536,7 +537,12 @@ sub EleroStick_Attr(@) {
<li>DisableTimer<br> <li>DisableTimer<br>
Disables the periodically request of the status. Should normally not be set to 1. Disables the periodically request of the status. Should normally not be set to 1.
</li> </li>
<br>
<li>SwitchChannels<br>
Comma separated list of channels that are a switch device.
</li>
<br> <br>
<li>Interval<br> <li>Interval<br>
When all channels are checkt, this number of seconds will be waited, until the channels will be checked again.<br> When all channels are checkt, this number of seconds will be waited, until the channels will be checked again.<br>

345
fhem/FHEM/36_EleroSwitch.pm Normal file
View File

@ -0,0 +1,345 @@
# $Id$
package main;
use strict;
use warnings;
use SetExtensions;
#=======================================================================================
sub EleroSwitch_Initialize($) {
my ($hash) = @_;
$hash->{Match} = ".*";
$hash->{DefFn} = "EleroSwitch_Define";
$hash->{UndefFn} = "EleroSwitch_Undef";
$hash->{FingerprintFn} = "EleroSwitch_Fingerprint";
$hash->{ParseFn} = "EleroSwitch_Parse";
$hash->{SetFn} = "EleroSwitch_Set";
$hash->{GetFn} = "EleroSwitch_Get";
$hash->{AttrFn} = "EleroSwitch_Attr";
$hash->{AttrList} = "IODev " .
"$readingFnAttributes ";
$hash->{noAutocreatedFilelog} = 1;
}
#=======================================================================================
sub EleroSwitch_Define($$) {
my ( $hash, $def ) = @_;
my @a = split( "[ \t][ \t]*", $def );
return "Usage: define <name> EleroSwitch <Channel>" if(@a < 3);
my $devName = $a[0];
my $type = $a[1];
my $channel = $a[2];
$hash->{STATE} = 'Initialized';
$hash->{NAME} = $devName;
$hash->{TYPE} = $type;
$hash->{channel} = $channel;
$modules{EleroSwitch}{defptr}{$channel} = $hash;
AssignIoPort($hash);
if(defined($hash->{IODev}->{NAME})) {
Log3 $devName, 4, "$devName: I/O device is " . $hash->{IODev}->{NAME};
}
else {
Log3 $devName, 1, "$devName: no I/O device";
}
return undef;
}
#=======================================================================================
sub EleroSwitch_Undef($$) {
my ($hash, $arg) = @_;
my $channel = $hash->{channel};
RemoveInternalTimer($hash);
delete( $modules{EleroSwitch}{defptr}{$channel} );
return undef;
}
#=======================================================================================
sub EleroSwitch_Get($@) {
return undef;
}
#=======================================================================================
sub EleroSwitch_Send($$) {
my ( $hash, $position) = @_;
my $channel = $hash->{channel};
my $head = 'aa';
my $msgLength = '05';
my $msgCmd = '4c';
my $firstBits = '';
my $firstChannels = '';
my $secondBits = '';
my $secondChannels = '';
my $checksum = '';
my $payload = '';
if($position eq 'off'){
# stop / off
$payload = '10';
}
elsif($position eq 'on'){
# top / on
$payload = '20';
}
elsif($position eq 'dim1'){
# intermediate / dim1
$payload = '44';
}
elsif($position eq 'dim2'){
# tilt / dim2
$payload = '24';
}
if($payload) {
if($channel <= 8){
$firstChannels = '00';
$secondChannels = 2**($channel-1);
$secondChannels = sprintf('%02x', $secondChannels);
}
else {
$secondChannels = '00';
$firstChannels = 2**($channel-1-8);
$firstChannels = sprintf('%02x', $firstChannels);
}
my $checksumNumber = hex($head) + hex($msgLength) + hex($msgCmd) + hex($firstChannels) + hex($secondChannels) + hex($payload);
my $byteUpperBound = 256;
my $upperBound = $byteUpperBound;
while($checksumNumber > $upperBound){
$upperBound = $upperBound + $byteUpperBound;
}
$checksumNumber = $upperBound - $checksumNumber;
$checksum = sprintf('%02x', $checksumNumber);
my $byteMsg = $head.$msgLength.$msgCmd.$firstChannels.$secondChannels.$payload.$checksum;
IOWrite($hash, "send", $byteMsg);
}
}
#=======================================================================================
sub EleroSwitch_Set($@) {
my ( $hash, $name, $cmd, @params ) = @_;
my $channel = $hash->{channel};
my $iodev = $hash->{IODev}->{NAME};
my $commands=("on:noArg off:noArg dim1:noArg dim2:noArg refresh:noArg");
return $commands if( $cmd eq '?' || $cmd eq '');
my $doRefresh = '0';
if($cmd eq 'refresh'){
IOWrite($hash, "refresh", $channel);
}
elsif($cmd eq 'on'){
EleroSwitch_Send($hash, "on");
$doRefresh = '1';
}
elsif($cmd eq 'off'){
EleroSwitch_Send($hash, "off");
$doRefresh = '1';
}
elsif($cmd eq 'dim1'){
EleroSwitch_Send($hash, "dim1");
}
elsif($cmd eq 'dim2'){
EleroSwitch_Send($hash, "dim2");
$doRefresh = '1';
}
else {
return "Unknown argument $cmd, choose one of $commands";
}
# Start a one time timer that refreshes this switch
if($doRefresh) {
RemoveInternalTimer($hash);
InternalTimer(gettimeofday() + 2, "EleroSwitch_OnRefreshTimer", $hash, 0);
}
return undef;
}
#=======================================================================================
sub EleroSwitch_Fingerprint($$) {
my ($name, $msg) = @_;
return ("", $msg);
}
#=======================================================================================
sub EleroSwitch_Parse($$) {
my ($hash, $msg) = @_;
my $name = $hash->{NAME};
my $buffer = $msg;
# get the channel
my $firstChannels = substr($buffer,6,2);
my $secondChannels = substr($buffer,8,2);
my $bytes = $firstChannels.$secondChannels ;
$bytes = hex ($bytes);
my $channel = 1;
while ($bytes != 1 and $channel <= 15) {
$bytes = $bytes >> 1;
$channel++;
}
if($channel <= 15) {
# Check if it is defined as a switch device
my $switchChannels = AttrVal($name, "SwitchChannels", undef);
if(defined $switchChannels) {
my @channelList = split /,/, $switchChannels;
if (!$channel ~~ @channelList) {
return undef;
}
}
else {
return undef;
}
# get status
my $statusByte = substr($buffer,10,2);
my %deviceStati = ('00' => "no_information",
'01' => "off",
'02' => "on",
'03' => "dim1",
'04' => "dim2",
'05' => "unknown",
'06' => "overheated",
'07' => "timeout",
'08' => "unknown",
'09' => "unknown",
'0a' => "unknown",
'0b' => "unknown",
'0d' => "unknown",
'0e' => "unknown",
'0f' => "unknown",
'10' => "off",
'11' => "on"
);
my $newstate = $deviceStati{$statusByte};
my $rhash = $modules{EleroSwitch}{defptr}{$channel};
my $rname = $rhash->{NAME};
if($modules{EleroSwitch}{defptr}{$channel}) {
readingsBeginUpdate($rhash);
readingsBulkUpdate($rhash, "state", $newstate);
readingsEndUpdate($rhash,1);
my @list;
push(@list, $rname);
return @list;
}
else {
return "UNDEFINED EleroSwitch_$channel EleroSwitch $channel";
}
}
}
#=======================================================================================
sub EleroSwitch_Attr(@) {
}
#=======================================================================================
sub EleroSwitch_OnRefreshTimer($$) {
my ($hash, @params) = @_;
my $name = $hash->{NAME};
my $channel = $hash->{channel};
IOWrite($hash, "refresh", $channel);
return undef;
}
1;
=pod
=item summary Represents an Elero switch
=item summary_DE Repräsentiert einen Elero switch
=begin html
<a name="EleroSwitch"></a>
<h3>EleroSwitch</h3>
<ul>
This mudule implements an Elero switch. It uses EleroStick as IO-Device.
<br><br>
<a name="EleroSwitch_Define"></a>
<b>Define</b>
<ul>
<code>define &lt;name&gt; EleroSwitch &lt;channel&gt;</code> <br>
&lt;channel&gt; specifies the channel of the transmitter stick that shall be used.
<br><br>
</ul>
<a name="EleroSwitch_Set"></a>
<b>Set</b>
<ul>
<li>on<br>
</li>
<li>off<br>
</li>
<li>dim1<br>
</li>
<li>dim2<br>
</li>
<li>refresh<br>
</li>
</ul>
<br>
<a name="EleroSwitch_Get"></a>
<b>Get</b>
<ul>
<li>no gets<br>
</li><br>
</ul>
<a name="EleroSwitch_Attr"></a>
<b>Attributes</b>
<ul>
<li>IODev<br>
The name of the IO-Device, normally the name of the EleroStick definition</li>
</ul><br>
<a name="EleroSwitch_Readings"></a>
<b>Readings</b>
<ul>
<li>state<br>
Current state of the switch (on, off, dim1, dim2)</li>
</ul><br>
</ul>
=end html
=cut