From 3b6f28f083ce831dff879db7b9bf4416bf12e9e5 Mon Sep 17 00:00:00 2001 From: wzut <> Date: Thu, 23 Apr 2015 17:46:01 +0000 Subject: [PATCH] Forum: http://forum.fhem.de/index.php/topic,35722.0.html git-svn-id: https://svn.fhem.de/fhem/trunk@8471 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/FHEM/98_UbiquitiMP.pm | 934 ++++++++++++++++++++++++++++++++++++ fhem/FHEM/98_UbiquitiOut.pm | 129 +++++ 2 files changed, 1063 insertions(+) create mode 100644 fhem/FHEM/98_UbiquitiMP.pm create mode 100644 fhem/FHEM/98_UbiquitiOut.pm diff --git a/fhem/FHEM/98_UbiquitiMP.pm b/fhem/FHEM/98_UbiquitiMP.pm new file mode 100644 index 000000000..536e1aa84 --- /dev/null +++ b/fhem/FHEM/98_UbiquitiMP.pm @@ -0,0 +1,934 @@ +################################################################ +# +# $Id$ +# +# (c) 2015 Copyright: Wzut +# forum : http://forum.fhem.de/index.php/topic,34131.0.html +# All rights reserved +# +# This code is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# The GNU General Public License can be found at +# http://www.gnu.org/copyleft/gpl.html. +# A copy is found in the textfile GPL.txt and important notices to the license +# from the author is found in LICENSE.txt distributed with these scripts. +# This script is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +################################################################ +# Changelog: +# 25.3.15 add force for set on and off +# 10.04.15 add enable/disable +# 18.04.15 add toggle +# 20.04.15 add Groups + +package main; + + +use strict; +use warnings; +use Time::HiRes qw(gettimeofday); +use Blocking; # http://www.fhemwiki.de/wiki/Blocking_Call +use SetExtensions; +use Net::Telnet; +use JSON; + +my %sets = (); +my $setcmds = "on,off,toggle,enable,disable,lock,unlock,reset"; + +######################################################################### + +sub UbiquitiMP_Initialize($) +{ + my ($hash) = @_; + $hash->{DefFn} = "UbiquitiMP_Define"; + $hash->{UndefFn} = "UbiquitiMP_Undef"; + $hash->{SetFn} = "UbiquitiMP_Set"; + $hash->{GetFn} = "UbiquitiMP_Get"; + $hash->{AttrFn} = "UbiquitiMP_Attr"; + $hash->{FW_summaryFn} = "UbiquitiMP_summaryFn"; + $hash->{AttrList} = "interval timeout user password subDevices:0,1 ignoreList ledconnect:off,blue,yellow,both,alternate groupPorts ".$readingFnAttributes; +} + +sub UbiquitiMP_updateConfig($) +{ + # this routine is called 10 sec after the last define of a restart + # this gives FHEM sufficient time to fill in attributes + + my ($hash) = @_; + my $name = $hash->{NAME}; + + $hash->{INTERVAL} = AttrVal($name, "interval", 300); + + readingsSingleUpdate($hash,"state","Initialized",1); + + $hash->{".led"} = AttrVal($name, "ledconnect", 0); + if ($hash->{".led"}) # Farben nach Kommando + { + $hash->{".led"} = 0 if ($hash->{".led"} eq "off"); + $hash->{".led"} = 1 if ($hash->{".led"} eq "blue"); + $hash->{".led"} = 2 if ($hash->{".led"} eq "yellow"); + $hash->{".led"} = 3 if ($hash->{".led"} eq "both"); + $hash->{".led"} = 4 if ($hash->{".led"} eq "alternate"); + } + + #$hash->{SNAME} = ""; + #$hash->{BNAME} = ""; + $hash->{MAC} = ""; + $hash->{lastcmd} = "Init"; + + RemoveInternalTimer($hash); + InternalTimer(gettimeofday()+($hash->{".timeout"}*2), "UbiquitiMP_GetStatus",$hash, 0) if ($hash->{".timeout"}); + + return undef; +} + +################################################################################ + +sub UbiquitiMP_Define($$) { + + my ($hash, $def) = @_; + my $name = $hash->{NAME}; + my @a = split("[ \t][ \t]*", $def); + + return "wrong syntax: define UbiquitiMP " if(int(@a) < 3); + + $hash->{".host"} = $a[2]; + + if( !defined( $attr{$a[0]}{user} ) ) { $attr{$a[0]}{user} = "ubnt";} + $hash->{".user"} = $attr{$a[0]}{user}; + + if( !defined( $attr{$a[0]}{password} ) ) { $attr{$a[0]}{password} = "ubnt";} + $hash->{".pwd"} = $attr{$a[0]}{password}; + + if( !defined( $attr{$a[0]}{subDevices} ) ) { $attr{$a[0]}{subDevices} = "1";} + + if( !defined( $attr{$a[0]}{timeout} ) ) { $attr{$a[0]}{timeout} = "2"} + $hash->{".timeout"} = (int($attr{$a[0]}{timeout}) > 1) ? $attr{$a[0]}{timeout} : "2"; + + if( !defined( $attr{$a[0]}{subDevices} ) ) { $attr{$a[0]}{subDevices} = "1"} + $hash->{".subdevices"} = $attr{$a[0]}{subDevices}; + + $hash->{Clients} = ":UbiquitiOut:"; + $hash->{PORTS} = 0; + $hash->{force} = 0; + $hash->{ERRORCOUNT} = 0; + + readingsSingleUpdate($hash, "state", "defined",0); + + RemoveInternalTimer($hash); + InternalTimer(gettimeofday()+10, "UbiquitiMP_updateConfig",$hash,0); # in 10 Sekunden machen wir den Rest + + return undef; +} + +################################################################################ + +sub UbiquitiMP_Undef($$) +{ + my ($hash, $arg) = @_; + RemoveInternalTimer($hash); + if(defined($hash->{helper}{RUNNING_PID})) + { + BlockingKill($hash->{helper}{RUNNING_PID}); + } + return undef; +} + +################################################################################ + +sub UbiquitiMP_force($) +{ + my ($hash) = @_; + my $name = $hash->{NAME}; + + my $cmdlist = $hash->{lastcmd}; + + Log3 $name, 4, "$name, force called for $cmdlist"; + + $hash->{helper}{RUNNING_PID} = BlockingCall("UbiquitiMP_BCStart", $cmdlist, "UbiquitiMP_BCDone",(int($hash->{".timeout"})*3),"UbiquitiMP_BCAborted", $hash) unless(exists($hash->{helper}{RUNNING_PID})); + + RemoveInternalTimer($hash); + + if($hash->{helper}{RUNNING_PID}) + { + Log3 $name, 5, "$name, BC force process started with PID(".$hash->{helper}{RUNNING_PID}{pid}.") cmd : $cmdlist"; + } + else + { # das war wohl schon wieder nix :( + InternalTimer(gettimeofday()+(int($hash->{".timeout"})*3), "UbiquitiMP_force",$hash, 0); + } + + return undef; +} + +################################################################################ + +sub UbiquitiMP_Attr(@) +{ + + my ($cmd,$name, $attrName,$attrVal) = @_; + my $hash = $defs{$name}; + + if ($cmd eq "set") + { + + if ($attrName eq "timeout") + { + if (int($attrVal)<"2") {$attrVal="2";} + $hash->{".timeout"} = $attrVal; + $attr{$name}{timeout} = $attrVal; + } + elsif ($attrName eq "user") + { + $hash->{".user"} = $attrVal; + $attr{$name}{user} = $attrVal; + } + elsif ($attrName eq "password") + { + $hash->{".pwd"} = $attrVal; + $attr{$name}{password} = $attrVal; + } + elsif ($attrName eq "interval") + { + $hash->{INTERVAL} = $attrVal; + $attr{$name}{interval} = $attrVal; + } + + elsif ($attrName eq "subDevices") + { + $hash->{".subdevices"} = $attrVal; + $attr{$name}{subDevices} = $attrVal; + } + elsif ($attrName eq "ledconnect") + { + $hash->{".led"} = 0 if ($attrVal eq "off"); + $hash->{".led"} = 1 if ($attrVal eq "blue"); + $hash->{".led"} = 2 if ($attrVal eq "yellow"); + $hash->{".led"} = 3 if ($attrVal eq "both"); + $hash->{".led"} = 4 if ($attrVal eq "alternate"); + $attr{$name}{ledconnect} = $attrVal; + } + elsif ($attrName eq "groupPorts") + { + $attr{$name}{groupPorts} = $attrVal; + UbiquitiMP_createSets($hash); + } + } + + return undef; +} + +################################################################################ + +sub UbiquitiMP_Get($@) { + my ($hash, $name , @a) = @_; + my $cmd = $a[0]; + #Log3 $name, 5, "Get: ".join(" ", @a); + + return "get $name needs one argument" if (int(@a) != 1); + + return "Unknown argument $cmd, choose one of status:noArg info:noArg reboot:noArg" if($cmd !~ /^(status|info|reboot)$/); + + $hash->{force} = 0; # Get setzt IMMER force zurueck ! + + if ($cmd eq "info") + { $cmd = $name."#info#cat /etc/board.info | grep board;"; $hash->{lastcmd} ="GetInfo";} + elsif ($cmd eq "status") + { $cmd = $name."#status#/sbin/cgi /usr/www/mfi/sensors.cgi#awk '{print \"u=\"\$1}' < /proc/uptime#awk '{print \"l=\"\$1\" \"\$2\" \"\$3}' < /proc/loadavg#cat /proc/power/energy_sum* | tr '\\n' ' '"; $hash->{lastcmd} ="GetStatus";} + elsif ($cmd eq "reboot") + { $cmd = $name."#reboot#reboot"; $hash->{lastcmd} ="GetReboot";} + else { return undef; } # sollte eigentlich nie vorkommen + + $hash->{helper}{RUNNING_PID} = BlockingCall("UbiquitiMP_BCStart", $cmd, "UbiquitiMP_BCDone",(int($hash->{".timeout"})*2),"UbiquitiMP_BCAborted", $hash) unless(exists($hash->{helper}{RUNNING_PID})); + if($hash->{helper}{RUNNING_PID}) + { + RemoveInternalTimer($hash); + Log3 $name, 5, "$name, BC process started with PID(".$hash->{helper}{RUNNING_PID}{pid}.") cmd : $cmd"; + } + else + { # das war wohl nix :( + Log3 $name, 5, "$name, BC process start failed !"; + return $name.", can't execute get command as NonBlockingCall"; + } + + return undef; +} + +################################################################################ + +sub UbiquitiMP_Set($@) { + my ($hash, @a) = @_; + my $name = $hash->{NAME}; + my ($port,$cmd,$subcmd); + + return undef if (!$hash->{PORTS}); # ohne bekannte Ports geht hier nichts :) + + UbiquitiMP_createSets($hash) if(!defined($sets{Out1}) && $hash->{PORTS}); # neu aufbauen nach reload; + + if (int($hash->{PORTS}) > 1) + { + $port = (defined($a[1])) ? $a[1] : "?" ; + $cmd = (defined($a[2])) ? $a[2] : ""; + $subcmd = (defined($a[3])) ? $a[3] : ""; + + if(!defined($sets{$port})) + { + my @commands = (); + foreach my $key (sort keys %sets) + { + push @commands, $sets{$key} ? $key.":".join(",",$sets{$key}) : $key; + } + return "Unknown port $port, choose one of " . join(" ", @commands); + } + return "$name wrong command, please use on of on,off,toggle,lock,unlock,enable,disable or reset" if($cmd !~ /^(on|off|lock|unlock|reset|enable|disable|toggle)$/); + } + else + { + $port = "Out1"; + $cmd = (defined($a[1])) ? $a[1] : ""; + $subcmd = (defined($a[2])) ? $a[2] : ""; + $setcmds =~ s/\,/ /g; + shift(@a); + return SetExtensions($hash,$setcmds,$name,@a) if($cmd !~ /^(on|off|lock|unlock|reset|enable|disable|toggle)$/); + } + + my $cmdlist = "$name#$cmd"; + + if ($hash->{force}) + { + my $ret = "a force command is already active -> PID ".$hash->{force}." cmd : ".$hash->{lastcmd}; + Log3 $name, 3, "$name, $ret"; + return $ret; + } + + + if ((substr($port,0,3) eq "Out") && (int(substr($port,3,1)) > 0) && (int(substr($port,3,1)) <= int($hash->{PORTS}))) + { $cmdlist .= "#".substr($port,3,1); } # normaler einzelner Port + else # oder doch eine Port Gruppe ? + { + @a = split("," ,$hash->{"group_".$port}); + foreach (@a) + { + return "Unknown port $_ in group list $port !" if ((int($_) < 1) || (int($_) > int($hash->{PORTS}))); + $cmdlist .="#".$_; + } + } + + $hash->{lastcmd} = $cmdlist; + + $hash->{helper}{RUNNING_PID} = BlockingCall("UbiquitiMP_BCStart", $cmdlist, "UbiquitiMP_BCDone",(int($hash->{".timeout"})*3),"UbiquitiMP_BCAborted", $hash) unless(exists($hash->{helper}{RUNNING_PID})); + + if($hash->{helper}{RUNNING_PID}) + { + RemoveInternalTimer($hash); + $hash->{force} = ($subcmd eq "force") ? $hash->{helper}{RUNNING_PID}{pid} : 0; + Log3 $name, 5, "$name, BC process started with PID(".$hash->{helper}{RUNNING_PID}{pid}.") cmd : $cmdlist , subcmd : $subcmd"; + } + else + { # das war wohl nix :( + Log3 $name, 5, "$name, BC process start failed !"; + UbiquitiMP_force($hash) if ($hash->{force}) ; # muessen wir das wiederholen ? + return $name.", can't execute set as NonBlockingCall"; + } + + return undef; +} + +################################################################################ + +sub UbiquitiMP_BCStart($) +{ + my ($string) = @_; + return unless(defined($string)); + + my (@ret, $c, $v); + + my @a = split("\#" ,$string); + + my $output = $a[0]; # Name + shift(@a); + my $cmd = $a[0]; + shift(@a); + + my $hash = $defs{$output}; + my $name = $hash->{NAME}; + my $onoff = (($cmd eq "on") || ($cmd eq "lock") || ($cmd eq "enable")) ? "1" : "0"; + + Log3 $name, 5, "$name, BC cmd : $cmd -> ".join(" ",@a); + + my $sock = new Net::Telnet(Timeout => $hash->{".timeout"}, Errmode => 'return'); + $sock->open( Host => $hash->{".host"}, Port => 23 ); + if (!$sock->errmsg) + { + $sock->login( Name => $hash->{".user"}, Password => $hash->{".pwd"} ); + if (!$sock->errmsg) + { + $sock->cmd("echo '".$hash->{".led"}."' >/proc/led/status") if ($hash->{".led"}); + + if (($cmd eq "reset") || ($cmd eq "toggle") || + ($cmd eq "lock") || ($cmd eq "unlock") || + ($cmd eq "enable") || ($cmd eq "disable") || + ($cmd eq "on") || ($cmd eq "off")) + { + $output .= "|1|status|"; # Rueckgabe wie eine Statusabfrage + foreach (@a) + { + if ($cmd eq "reset") + { + $sock->cmd("cat /proc/power/relay".$_." > /tmp/relay".$_.".tmp"); # Akt. Zustand retten + $sock->cmd("echo 0 > /proc/power/reset".$_); # Messwerte Reset + $sock->cmd("cat /tmp/relay".$_.".tmp > /proc/power/output".$_); # alter Zustand wiederherstellen + } + elsif ($cmd eq "toggle") + { + @ret = $sock->cmd("awk '{print \$1=!\$1}' < /proc/power/relay".$_); # Akt. Zustand invers + $sock->cmd("echo '".$ret[0]."' > /proc/power/output".$_); + # ToDo : wer weiss wie man es in nur einer Zeile macht ? + # ala : awk '{print $1=!$1}' < /proc/power/relayX > /proc/power/outputX + } + elsif (($cmd eq "on") || ($cmd eq "off")) + { + $sock->cmd("echo $onoff > /proc/power/output".$_); + } + elsif (($cmd eq "lock") || ($cmd eq "unlock")) + { + $sock->cmd("echo $onoff > /proc/power/lock".$_); + } + elsif (($cmd eq "enable") || ($cmd eq "disable")) + { + $sock->cmd("echo $onoff > /proc/power/enabled".$_); + } + } # foreach + + select(undef, undef, undef, 0.5); # 500 ms warten ! + + @ret = $sock->cmd("/sbin/cgi /usr/www/mfi/sensors.cgi"); # neue Statuswerte holen + if($ret[2]) + { + ($ret[2],undef) = split("MF",$ret[2]); $output .= $ret[2]; + } + } + else # + { + $output .= "|1|$cmd|"; + foreach(@a) + { + select(undef, undef, undef, 0.5); + @ret = $sock->cmd($_); + Log3 $name, 5, "$name, ret -> ".$ret[0]; + if ($cmd eq "status") { if($ret[2]) { ($ret[2],undef) = split("MF",$ret[2]); $output .= $ret[2]; } else { if ($ret[0]) {$ret[0] =~s/^MF.*//g; $output .= "|".$ret[0];}}} + if ($cmd eq "info") { $c = join(";",@ret); $c =~s/\$//g; $c =~s/\"//g; ($c,$v) = split("MF.v",$c); $output .= $c; $output .= "version=$v";} + } + } + + $sock->cmd("echo '0' >/proc/led/status") if ($hash->{".led"}); + $sock->close; + + $output =~s/\n//g; + return $output; + } else { return $output."|0|$cmd|".$sock->errmsg; } + } else { return $output."|0|$cmd|".$sock->errmsg; } +} + +sub UbiquitiMP_BCDone($) +{ + my ($string) = @_; + return unless(defined($string)); + + my ($h,$ret,$cmd,$msg,@a) = split("\\|",$string); + my $hash = $defs{$h}; + + $msg = "" if (!defined($msg)) ; + + if ($hash->{helper}{RUNNING_PID}{pid}) + { + Log3 $h, 4, "$h, BCDone : PID ".$hash->{helper}{RUNNING_PID}{pid}; + delete($hash->{helper}{RUNNING_PID}); + } + + Log3 $h, 5, "$h, BCDone : $string"; + if ($ret eq "0") + { + $hash->{ERRORMSG} = $msg; + $hash->{ERRORTIME} = TimeNow(); + $hash->{ERRORCOUNT}++; + Log3 $h, 2, "$h, Error[".$hash->{ERRORCOUNT}."] cmd $cmd -> $msg"; + $hash->{INTERVAL} = 3600 if ($hash->{INTERVAL} && ($hash->{ERRORCOUNT}>9)); + readingsSingleUpdate($hash,"state","error",1); + if ($hash->{force}) # muessen wir wiederholen ? + { + UbiquitiMP_force($hash); + return; + } + } + else # das ging ja schon mal gut + { + $hash->{INTERVAL} = AttrVal($h, "interval", 300) if ($hash->{ERRORCOUNT} >9); + $hash->{ERRORCOUNT} = 0; + $hash->{force} = 0; + if ($cmd eq "info") { UbiquitiMP_Info($hash,$msg); } + elsif ($cmd eq "status") + { + if($msg ne "") # haben wir wieder mal einen leeren Status bekommen ? + { + UbiquitiMP_Status($hash,$msg,@a) if($msg ne "") ; + } + else + { + # etwas warten und versuchen den Status doch noch abzuschliessen + InternalTimer(gettimeofday()+$hash->{".timeout"}, "UbiquitiMP_GetStatus",$hash, 0); + return ; + } + } + } + + InternalTimer(gettimeofday()+$hash->{INTERVAL}, "UbiquitiMP_GetStatus",$hash, 0) if ($hash->{INTERVAL}); +} + +################################################################################ + +sub UbiquitiMP_BCAborted($) +{ + my ($hash) = @_; + delete($hash->{helper}{RUNNING_PID}); + $hash->{ERRORCOUNT}++; + $hash->{ERRORTIME} = TimeNow(); + Log3 $hash->{NAME}, 3, $hash->{NAME}.", BlockingCall for ".$hash->{NAME}." cmd ".$hash->{lastcmd}." aborted EC : ".$hash->{ERRORCOUNT}; + $hash->{INTERVAL} = 3600 if ($hash->{INTERVAL} && ($hash->{ERRORCOUNT}>9)); + + if ($hash->{force}) # ein abgebrochenes force ? + { + UbiquitiMP_force($hash); + } + elsif ($hash->{lastcmd} eq "GetStatus") # war das ein erfolgloser auto status update ? + { + InternalTimer(gettimeofday()+$hash->{INTERVAL}, "UbiquitiMP_GetStatus",$hash, 0) if($hash->{INTERVAL}); + } + + return; +} + +################################################################################ + +sub UbiquitiMP_Status($$@) +{ + my ($hash,$js,@a) = @_; + my $name = $hash->{NAME}; + + my $devstate; + my $devname; + my $state; + + my $sum_current; + my $sum_power; + my $sum_month; + my $sum_prevmonth; + my $sum_energy; + my @ener; + + my $json = (); + $json = JSON->new->utf8(0)->decode($js); + + my $sensors = scalar keys $json->{sensors}; + #$sensors = 1 if ($sensors); + + if ((!$hash->{PORTS}) && ($sensors > 0)) # nur einmal zu Begin bzw nach reload + { + $hash->{PORTS} = $sensors; + UbiquitiMP_createSets($hash); + } + + # bei der 1 Port Ubi default keine Subdevices , bei den anderen ja + my $subdev = (int($hash->{PORTS}) >1) ? AttrVal($name, "subDevices", 1) : AttrVal($name, "subDevices", 0); + + + @ener = split(" ",$a[2]) if (defined($a[2])); + + readingsBeginUpdate($hash); + + for (my $i=0; $i<$sensors; $i++) + { + if (index(AttrVal($name, "ignoreList", "") , $i+1) == -1) # welche Ports ignorieren ? + { + my $thismonth = ($json->{sensors}[$i]{thismonth}) ? sprintf("%.0f", $json->{sensors}[$i]{thismonth}*0.3125) : 0; # Verbrauch aktueller Monat + my $prevmonth = ($json->{sensors}[$i]{prevmonth}) ? sprintf("%.0f", $json->{sensors}[$i]{prevmonth}*0.3125) : 0; # Verbrauch letzter Monat , in welcher Einheit ? + my $powerfactor = sprintf("%.2f", $json->{sensors}[$i]{powerfactor}); $powerfactor +=0; # wer will 0.00 ? + my $output = $json->{sensors}[$i]{output}; # 1/0 fuer on/off + my $port = $json->{sensors}[$i]{port}; # Port Nr. 1- n + my $voltage = sprintf("%.0f", $json->{sensors}[$i]{voltage}); # V + my $power = sprintf("%.0f", $json->{sensors}[$i]{power}); # W + my $current = sprintf("%.2f", $json->{sensors}[$i]{current}); $current +=0; # A + my $lock = $json->{sensors}[$i]{lock}; + my $label = $json->{sensors}[$i]{label}; + my $enabled = $json->{sensors}[$i]{enabled}; # wann wird das angefasst ? + my $energy = (defined($ener[$i])) ? sprintf("%.2f",$ener[$i]*0.3125) : 0; + $energy += 0; + + my $eState ="E:$energy P:$power I:$current U:$voltage i:$powerfactor"; + + $sum_current += (defined($json->{sensors}[$i]{current})) ? $json->{sensors}[$i]{current} : 0; + $sum_month += (defined($json->{sensors}[$i]{thismonth})) ? $json->{sensors}[$i]{thismonth}*0.3125 : 0; + $sum_prevmonth += (defined($json->{sensors}[$i]{prevmonth})) ? $json->{sensors}[$i]{prevmonth}*0.3125 : 0; + $sum_power += (defined($json->{sensors}[$i]{power})) ? $json->{sensors}[$i]{power} : 0; + $sum_energy += (defined($ener[$i])) ? $ener[$i]*0.3125 : 0; + + $devstate = ($output eq "0") ? "off" : "on"; + $state .= ($i == $sensors) ? $devstate : $devstate." "; + $devname = "Out".$port; # Port kann eigenen Namen haben + + $hash->{helper}{$devname}{state} = $output; + $hash->{helper}{$devname}{name} = $devname; + $hash->{helper}{$devname}{lock} = $lock; + + if ($subdev) # aufteilen oder lieber alles am Stueck ? + { + my $defptr = $modules{UbiquitiOut}{defptr}{$name.$port}; + if (defined($defptr)) + { + readingsBeginUpdate($defptr); + readingsBulkUpdate($defptr, "state" , $devstate); + readingsBulkUpdate($defptr, "eState" , $eState); + readingsBulkUpdate($defptr, "power" , $power); + readingsBulkUpdate($defptr, "voltage" , $voltage); + readingsBulkUpdate($defptr, "current" , $current); + readingsBulkUpdate($defptr, "pf" , $powerfactor); + readingsBulkUpdate($defptr, "month" , $thismonth); + readingsBulkUpdate($defptr, "prevmonth" , $prevmonth) if ($prevmonth); + readingsBulkUpdate($defptr, "lock" , $lock); + readingsBulkUpdate($defptr, "label" , $label) if ($label); + readingsBulkUpdate($defptr, "enabled" , $enabled); + readingsBulkUpdate($defptr, "energy" , $energy); + readingsEndUpdate($defptr, 1 ); + } + else + { + Log3 $name, 3, "$name, autocreate sub device for $devname Port $port"; + CommandDefine(undef, $name."_".$devname." UbiquitiOut $name $port"); + } + } + else # all in one + { + if (int($hash->{PORTS}) > 1) + { + readingsBulkUpdate($hash, $devname."_state" , $devstate); + readingsBulkUpdate($hash, $devname."_eState" , $eState); + readingsBulkUpdate($hash, $devname."_power" , $power); + readingsBulkUpdate($hash, $devname."_voltage" , $voltage); + readingsBulkUpdate($hash, $devname."_current" , $current); + readingsBulkUpdate($hash, $devname."_pf" , $powerfactor); + readingsBulkUpdate($hash, $devname."_month" , $thismonth); + readingsBulkUpdate($hash, $devname."_prevmonth" , $prevmonth) if ($prevmonth); + readingsBulkUpdate($hash, $devname."_lock" , $lock); + readingsBulkUpdate($hash, $devname."_label" , $label) if ($label); + readingsBulkUpdate($hash, $devname."_enabled" , $enabled); + readingsBulkUpdate($hash, $devname."_energy" , $energy); + } + else # 1 Port Dose + { + readingsBulkUpdate($hash, "eState" , $eState); + readingsBulkUpdate($hash, "power" , $power); + readingsBulkUpdate($hash, "voltage" , $voltage); + readingsBulkUpdate($hash, "current" , $current); + readingsBulkUpdate($hash, "pf" , $powerfactor); + readingsBulkUpdate($hash, "month" , $thismonth); + readingsBulkUpdate($hash, "prevmonth" , $prevmonth) if ($prevmonth); + readingsBulkUpdate($hash, "lock" , $lock); + readingsBulkUpdate($hash, "label" , $label) if ($label); + readingsBulkUpdate($hash, "enabled" , $enabled); + readingsBulkUpdate($hash, "energy" , $energy); + } # 1 Port + } # all in one + } # if + } # for + + if(defined($a[0]) && (substr($a[0],0,2) eq "u=")) # uptime + { + my $sec; + (undef,$sec) = split("=",$a[0]); # u=xxxx.yyy + if (int($sec) > 0) + { + my ($seconds, $microseconds) = gettimeofday(); + my @t = localtime($seconds-int($sec)); + $hash->{powerd_on} = sprintf("%04d-%02d-%02d %02d:%02d:%02d", $t[5]+1900,$t[4]+1,$t[3],$t[2],$t[1],$t[0]); + my($d,$h,$m,$s,$up); + $d=int($sec/(24*60*60)); + $h=($sec/(60*60))%24; + $m=($sec/60)%60; + $s=$sec%60; + $up = "$d days, " if($d > 1); + $up = "1 day, " if($d == 1); + $up .= sprintf("%02s:%02s:%02s", $h, $m, $s); + readingsBulkUpdate($hash, "uptime", $up); + } + } + + if (defined($a[1]) && (substr($a[1],0,2) eq "l=")) # load + { + my $load; + (undef , $load) = split("=",$a[1]); # l=xxx yyy zzz + $hash->{load} = $load if ($load); + } + + if(int($hash->{PORTS}) > 1) + { + readingsBulkUpdate($hash, "all_current", sprintf("%.2f", $sum_current)); + readingsBulkUpdate($hash, "all_power", sprintf("%.2f", $sum_power)); + readingsBulkUpdate($hash, "all_month", sprintf("%.2f", $sum_month/1000)); + readingsBulkUpdate($hash, "all_prevmonth", sprintf("%.2f", $sum_prevmonth/1000)); + readingsBulkUpdate($hash, "all_energy", sprintf("%.2f", $sum_energy)); + } + + readingsBulkUpdate($hash, "state",$state); + readingsEndUpdate($hash, 1 ); + + return undef; + +} + +################################################################################ + +sub UbiquitiMP_Info($$) +{ + my ($hash,$info) = @_; + my $name = $hash->{NAME}; + my $var; + my $val; + my $ports; + my $board_id; + + my @a = split(";" , $info); + foreach (@a) + { + ($var,$val) = split("=" , $_); + Log3 $name, 5, "$name, $var = $val"; + $board_id = $val if ($var eq "board.sysid"); + $hash->{BNAME} = $val if ($var eq "board.name"); + $hash->{SNAME} = $val if ($var eq "board.shortname"); + $hash->{VERSION} = $val if ($var eq "version"); + $hash->{MAC} = $val if ($var eq "board.hwaddr"); + } + + if (($board_id) && (!$hash->{PORTS})) + { + if ($board_id eq "0xe648") { $ports = 8; } + elsif ($board_id eq "0xe656") { $ports = 6; } + elsif (($board_id eq "0xe653") || + ($board_id eq "0xe643")){ $ports = 3; } + elsif ($board_id eq "0xe642") { $ports = 2; } + else { $ports = 1; } + + if ($ports > 0) + { + $hash->{PORTS} = $ports; + #$hash->{PORTS} = 1; + UbiquitiMP_createSets($hash); + } + } + + if (defined($hash->{VERSION})) + { + my $v = $hash->{VERSION}; + my $msg = "Old version ".$hash->{VERSION}." found, please update to >= 2.1.8 !"; + $v =~s/\.//g; + if (int($v) < 218) + { + $hash->{VERSION} = $msg; + Log3 $name, 1, "$name, $msg"; + } + } + + return undef; +} + +################################################################################ + +sub UbiquitiMP_GetStatus($) +{ + my ($hash) = @_; + my $name = $hash->{NAME}; + my $cmd = $name; + + #RemoveInternalTimer($hash); + + Log3 $name, 5, "$name, GetStatus Interval : ".$hash->{INTERVAL}; + + if ($hash->{lastcmd} eq "Init") # kommen wir ueber einen Neustart ? + { + $cmd .= "#info#cat /etc/board.info | grep board"; + } + else + { + $cmd .= "#status#/sbin/cgi /usr/www/mfi/sensors.cgi"; + $cmd .= "#awk '{print\"u=\" \$1}' /proc/uptime"; + $cmd .= "#awk '{print \"l=\"\$1\" \"\$2\" \"\$3}' < /proc/loadavg"; + $cmd .= "#cat /proc/power/energy_sum* | tr '\\n' ' '"; + } + + $hash->{helper}{RUNNING_PID} = BlockingCall("UbiquitiMP_BCStart", $cmd, "UbiquitiMP_BCDone",(int($hash->{".timeout"})*2),"UbiquitiMP_BCAborted", $hash) unless(exists($hash->{helper}{RUNNING_PID})); + if($hash->{helper}{RUNNING_PID}) + { + Log3 $name, 5, "$name, BC process started with PID(".$hash->{helper}{RUNNING_PID}{pid}.") cmd : $cmd"; + $hash->{lastcmd} = "GetStatus"; + } + else + { # das ging schief wiederholen nach doppelter timeout Wartezeit + Log3 $name, 5, "$name, BC process start failed !"; + InternalTimer(gettimeofday()+(int($hash->{".timeout"})*2), "UbiquitiMP_GetStatus",$hash, 0); + } + + return; +} + +################################################################################ + +sub UbiquitiMP_summaryFn($$$$) { + my ($FW_wname, $hash, $room, $pageHash) = @_; + $hash = $defs{$hash}; + my $state = $hash->{STATE}; + my $name = $hash->{NAME}; + + return if ((AttrVal($name, "stateFormat", "")) || (int($hash->{PORTS}) < 2)); + + my ($icon,$html,$cmd,$i,$title,$txt,$a,$b); + + $html =""; + if (($state ne "defined") && ($state ne "error") && ($state ne "Initialized")) + { + for ($i=1; $i<= $hash->{PORTS}; $i++) + { + if (defined($hash->{helper}{"Out".$i}{state})) + { + if ($hash->{helper}{"Out".$i}{state}) + { + $cmd = "Out".$i." off"; + $title = $hash->{helper}{"Out".$i}{name}. " on"; + ($icon, undef, undef) = FW_dev2image($name,"on"); + ($a,$b) = split('title=\"on\"' , FW_makeImage($icon, "on")); + $txt = $a."title=\"".$title."\"".$b; + } + else + { + $cmd = "Out".$i." on"; + $title = $hash->{helper}{"Out".$i}{name}. " off"; + ($icon, undef, undef) = FW_dev2image($name,"off"); + ($a,$b) = split('title=\"off\"' , FW_makeImage($icon, "off")); + $txt = $a."title=\"".$title."\"".$b; + } + + if (!$hash->{helper}{"Out".$i}{lock}) + { + $html .= "$txt"; + } + else { $html .= $txt; } + + $html .= "  "; + + } + } + } else { $html .= $state }; + + $html .= ""; + return $html; +} + +################################################################################ + +sub UbiquitiMP_createSets($) +{ + my ($hash) = @_; + my $name = $hash->{NAME}; + my $list = AttrVal($name, "groupPorts", ""); + my (@a, @b); + + return if (!$hash->{PORTS}); + + %sets = (); + # bei nur einem Port macht eine Gruppe keinen Sinn + if (int($hash->{PORTS}) == 1) + { + @a = split("," ,$setcmds); + foreach (@a) { $sets{$_} = "noArg"; } + #%sets = ($setcmds); + return; + } + + $hash->{group_ALL} = ""; + + for (my $j=1; $j<= $hash->{PORTS}; $j++) { $sets{"Out".$j} = $setcmds; $hash->{group_ALL} .= "$j,"; } + $sets{"ALL"} = $setcmds; + chop($hash->{group_ALL}); # das letzte Komma weg + + if ($list) + { + @a = split(" " , $list); + foreach (@a) + { + @b = split("=" , $_); + if ($b[0] && $b[1]) + { + $hash->{"group_".$b[0]} = $b[1]; + $sets{$b[0]} = $setcmds; + } + } + } + return; +} + +1; + +=pod +=begin html + + +

UbiquitiMP

+ +=end html diff --git a/fhem/FHEM/98_UbiquitiOut.pm b/fhem/FHEM/98_UbiquitiOut.pm new file mode 100644 index 000000000..c17b7c9f4 --- /dev/null +++ b/fhem/FHEM/98_UbiquitiOut.pm @@ -0,0 +1,129 @@ +################################################################ +# +# $Id$ +# +# (c) 2015 Copyright: Wzut +# forum : http://forum.fhem.de/index.php/topic,34131.0.html +# All rights reserved +# +# This code is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# The GNU General Public License can be found at +# http://www.gnu.org/copyleft/gpl.html. +# A copy is found in the textfile GPL.txt and important notices to the license +# from the author is found in LICENSE.txt distributed with these scripts. +# This script is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +################################################################ +# Changelog: + +package main; + +use strict; +use warnings; +use SetExtensions; + +sub UbiquitiOut_Initialize($) +{ + my ($hash) = @_; + $hash->{SetFn} = "UbiquitiOut_Set"; + $hash->{DefFn} = "UbiquitiOut_Define"; + $hash->{UndefFn} = "UbiquitiOut_Undef"; + $hash->{AttrList} = $readingFnAttributes; +} + +################################### + +sub UbiquitiOut_Define($$) +{ + my ($hash, $def) = @_; + my $name = $hash->{NAME}; + + my @a = split("[ \t][ \t]*", $def); + + return "wrong syntax: use define $name UbiquitiOut " if(int(@a) < 4); + + my $parent = $a[2]; + my $socket = $a[3]; + + $hash->{IODEV} = $parent; + $hash->{SOCKETNR} = $socket; + + $modules{UbiquitiOut}{defptr}{$parent.$socket} = $hash; + + if (defined($attr{$parent}{room})) { $attr{$name}{room} = $attr{$parent}{room}; } + + my $currentstate = ReadingsVal($parent, $name, "defined"); + $hash->{STATE} = $currentstate ; + $hash->{READINGS}{lock}{VAL} = 0; + return undef; +} + +################################### + +sub UbiquitiOut_Undef($$) +{ + my ($hash, undef) = @_; + my $parent = $hash->{IODEV}; + my $socket = $hash->{SOCKETNR}; + delete $modules{UbiquitiOut}{defptr}{$parent.$socket} ; + return undef; +} + +################################### + +sub UbiquitiOut_Set($@) +{ + my ($hash, $name , @a) = @_; + my $cmd = $a[0]; + + my $cmdList = "off:noArg on:noArg toggle:noArg"; + + return "$name, no set value specified" if(int(@a) < 1); + return "$name, I/O device not found please define UbiquitiPM device first" if(!defined($hash->{IODEV})); + + return undef if ($hash->{READINGS}{lock}{VAL} eq "1") && ($cmd eq "?"); + return "$name, set command is not available while device is locked !" if ($hash->{READINGS}{lock}{VAL} eq "1"); + + + if($cmd =~ /^(on|off|toggle)$/) # nur diese drei Kommandos kennt es selbst + { + CommandSet(undef,$hash->{IODEV}." Out".$hash->{SOCKETNR}. " $cmd"); + return undef; + } + + return SetExtensions($hash,$cmdList,$name,@a); +} + +##################################### + +1; + +=pod +=begin html + + +

UbiquitiOut

+
    +
    + sub device for the UbiquitiMP or InfratekPM modul +
    + + + Define +
      + define <name> UbiquitiOut <UbiquitiMP device> <Out #> +
    + + + Set + +
+=end html +