From 1b57cb284411f5b53cf9d3d78e601082c79b6c31 Mon Sep 17 00:00:00 2001
From: HCS <>
Date: Mon, 24 Jul 2017 10:48:30 +0000
Subject: [PATCH] 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
---
fhem/FHEM/36_EleroDrive.pm | 44 +++--
fhem/FHEM/36_EleroStick.pm | 140 ++++++++-------
fhem/FHEM/36_EleroSwitch.pm | 345 ++++++++++++++++++++++++++++++++++++
3 files changed, 445 insertions(+), 84 deletions(-)
create mode 100644 fhem/FHEM/36_EleroSwitch.pm
diff --git a/fhem/FHEM/36_EleroDrive.pm b/fhem/FHEM/36_EleroDrive.pm
index da128f9cb..f30d950b3 100644
--- a/fhem/FHEM/36_EleroDrive.pm
+++ b/fhem/FHEM/36_EleroDrive.pm
@@ -25,6 +25,8 @@ sub EleroDrive_Initialize($) {
$hash->{AttrFn} = "EleroDrive_Attr";
$hash->{AttrList} = "IODev " .
"TopToBottomTime " .
+ "TiltPercent " .
+ "IntermediatePercent " .
"$readingFnAttributes ";
$hash->{noAutocreatedFilelog} = 1;
@@ -83,7 +85,6 @@ sub EleroDrive_ToFixPosition($$) {
my ( $hash, $position) = @_;
my $channel = $hash->{channel};
- ###my $iodev = $hash->{IODev}->{NAME};
my $head = 'aa';
my $msgLength = '05';
@@ -134,9 +135,7 @@ sub EleroDrive_ToFixPosition($$) {
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($$) {
my ( $hash, $position) = @_;
my $name = $hash->{NAME};
-
- ###debugLog("ToAnyPosition: $position");
}
#=======================================================================================
@@ -228,9 +225,21 @@ sub EleroDrive_Parse($$) {
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;
+ }
+ }
+
+ my $rhash = $modules{EleroDrive}{defptr}{$channel};
+ my $rname = $rhash->{NAME};
+
# get status
my $statusByte = substr($buffer,10,2);
@@ -256,8 +265,8 @@ sub EleroDrive_Parse($$) {
my %percentDefinitions = ('00' => 50,
'01' => 0,
'02' => 100,
- '03' => 50,
- '04' => 50,
+ '03' => AttrVal($rname, "IntermediatePercent", 50),
+ '04' => AttrVal($rname, "TiltPercent", 50),
'05' => -1,
'06' => -1,
'07' => -1,
@@ -274,13 +283,8 @@ sub EleroDrive_Parse($$) {
my $newstate = $deviceStati{$statusByte};
my $percentClosed = $percentDefinitions{$statusByte};
-
- my $rhash = $modules{EleroDrive}{defptr}{$channel};
- my $rname = $rhash->{NAME};
-
+
if($modules{EleroDrive}{defptr}{$channel}) {
- ###debugLog("$rname -> parsed $msg for channel $channel: $newstate");
-
readingsBeginUpdate($rhash);
readingsBulkUpdate($rhash, "state", $newstate);
readingsBulkUpdate($rhash, "position", $newstate);
@@ -294,7 +298,6 @@ sub EleroDrive_Parse($$) {
return @list;
}
else {
- ###debugLog("$name -> AUTOCREATE " . $hash->{IODev}->{NAME});
return "UNDEFINED EleroDrive_$channel EleroDrive $channel";
}
}
@@ -374,6 +377,13 @@ sub EleroDrive_OnRefreshTimer($$) {
TopToBottomTime
The time in seconds this drive needs for a complete run from the top to the bottom or vice versa
+
+ IntermediatePercent
+ Percent open when in intermediate position
+
+ TiltPercent
+ Percent open when in tilt position
+
diff --git a/fhem/FHEM/36_EleroStick.pm b/fhem/FHEM/36_EleroStick.pm
index 2f0f6f2ed..724fa4bf7 100644
--- a/fhem/FHEM/36_EleroStick.pm
+++ b/fhem/FHEM/36_EleroStick.pm
@@ -1,23 +1,14 @@
# $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;
use strict;
use warnings;
use Time::HiRes qw(gettimeofday);
-my $clients = ":EleroDrive";
-my %matchList = ("1:EleroDrive" => ".*",);
+my $clients = ":EleroDrive:EleroSwitch";
+my %matchList = ("1:EleroDrive" => ".*",
+ "2:EleroSwitch" => ".*");
# Answer Types
my $easy_confirm = "aa044b";
@@ -45,6 +36,7 @@ sub EleroStick_Initialize($) {
"Interval " .
"Delay " .
"DisableTimer:1,0 " .
+ "SwitchChannels " .
"$readingFnAttributes ";
}
@@ -61,8 +53,7 @@ sub EleroStick_Enqueue($$) {
if(!$hash->{QUEUE}) {
$hash->{QUEUE} = [""];
- ###debugLog($name, "QUEUE created with: $data");
-
+
EleroStick_SimpleWrite($hash, $data);
my $timerName = $name . "#QueueTimer";
my $interval = 0.1;
@@ -71,7 +62,6 @@ sub EleroStick_Enqueue($$) {
}
else {
push(@{$hash->{QUEUE}}, $data);
- ###debugLog($name, "Pushed to QUEUE: $data");
}
}
@@ -84,12 +74,9 @@ sub EleroStick_StartQueueTimer($) {
my $interval = AttrVal($name, "Delay", 0.5);
InternalTimer(gettimeofday() + $interval, "EleroStick_OnQueueTimer", $timerName, 0);
-
- ####debugLog($name, "Timer started: $timerName");
}
-
#=======================================================================================
sub EleroStick_OnQueueTimer($) {
my ($timerName) = @_;
@@ -97,13 +84,10 @@ sub EleroStick_OnQueueTimer($) {
my $hash = $defs{$name};
my $queue = $hash->{QUEUE};
- ###debugLog($name, "OnQueueTimer");
-
if (defined($queue) && @{$queue} > 0) {
my $data = $queue->[0];
if ($data ne "") {
EleroStick_SimpleWrite($hash, $data);
- ###debugLog($name, "Timer msg=$data");
}
shift(@{$queue});
@@ -203,8 +187,6 @@ sub EleroStick_SendEasyCheck($) {
my $byteMsg = $head.$msgLength.$msgCmd.$checksum;
EleroStick_Enqueue($hash, $byteMsg);
- ###EleroStick_SimpleWrite($hash, $byteMsg)
-
}
}
@@ -272,7 +254,6 @@ sub EleroStick_OnTimer($$) {
my $now = index($channels, "x");
if($now ne -1) {
substr($channels, $now, 1, "1");
- ###debugLog($name, "now " . ($now +1));
EleroStick_SendEasyInfo($hash, $now +1);
for(my $i = $now +1; $i<15; $i++) {
@@ -301,6 +282,22 @@ sub EleroStick_OnTimer($$) {
#=======================================================================================
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;
}
@@ -318,18 +315,61 @@ sub EleroStick_Write($$) {
# Send to the transmitter stick
if($cmd eq 'send'){
- ###debugLog($name, "EleroStick send cmd=send msg=$msg");
EleroStick_Enqueue($hash, $msg);
}
# Request status for a channel
elsif ($cmd eq 'refresh') {
- ###debugLog($name, "EleroStick cmd=refresh msg=$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($) {
@@ -357,45 +397,7 @@ sub EleroStick_Read($) {
my $calLen = ($strLen * 2) + 4;
if($calLen == length($hash->{buffer})){
- # Die Länge der Nachricht entspricht der Vorgabe im Header
-
- 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};
+ EleroStick_Parse($hash);
}
else {
# Wait for the rest of the data
@@ -416,7 +418,6 @@ sub EleroStick_Ready($) {
}
else {
EleroStick_SendEasyCheck($hash);
- ###debugLog($name, "EleroStick_Ready -> SendEasyInfo");
}
return $openResult if($hash->{STATE} eq "disconnected");
@@ -536,7 +537,12 @@ sub EleroStick_Attr(@) {
DisableTimer
Disables the periodically request of the status. Should normally not be set to 1.
-
+
+
+ SwitchChannels
+ Comma separated list of channels that are a switch device.
+
+
Interval
When all channels are checkt, this number of seconds will be waited, until the channels will be checked again.
diff --git a/fhem/FHEM/36_EleroSwitch.pm b/fhem/FHEM/36_EleroSwitch.pm
new file mode 100644
index 000000000..c63f625fb
--- /dev/null
+++ b/fhem/FHEM/36_EleroSwitch.pm
@@ -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 EleroSwitch " 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
+
+
+EleroSwitch
+
+
+ This mudule implements an Elero switch. It uses EleroStick as IO-Device.
+
+
+
+ Define
+
+ define <name> EleroSwitch <channel>
+ <channel> specifies the channel of the transmitter stick that shall be used.
+
+
+
+
+ Set
+
+ - on
+
+ - off
+
+ - dim1
+
+ - dim2
+
+ - refresh
+
+
+
+
+
+ Get
+
+
+
+ Attributes
+
+ - IODev
+ The name of the IO-Device, normally the name of the EleroStick definition
+
+
+
+ Readings
+
+ - state
+ Current state of the switch (on, off, dim1, dim2)
+
+
+
+
+=end html
+=cut