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 +
        +
      • no gets
        +

      • +
      + + + 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