From c21944fe11d1dfcd8410095443d3076dc4df3f3c Mon Sep 17 00:00:00 2001 From: wuehler Date: Sun, 19 May 2019 06:19:20 +0000 Subject: [PATCH] 74_Unifi.pm: added New Module 74_UnifiClient.pm git-svn-id: https://svn.fhem.de/fhem/trunk@19404 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/CHANGED | 5 + fhem/FHEM/74_Unifi.pm | 375 +++++++++++++++++++++++++----- fhem/FHEM/74_UnifiClient.pm | 440 ++++++++++++++++++++++++++++++++++++ fhem/FHEM/74_UnifiSwitch.pm | 12 +- 4 files changed, 775 insertions(+), 57 deletions(-) create mode 100755 fhem/FHEM/74_UnifiClient.pm diff --git a/fhem/CHANGED b/fhem/CHANGED index 6ed1d6624..518aef739 100644 --- a/fhem/CHANGED +++ b/fhem/CHANGED @@ -1,5 +1,10 @@ # 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. + - feature: 74_UnifiClient: add new Module UnifiClient as BETA + - feature: 74_Unifi: add new Module UnifiClient + added setter for Usergroups an UC-Version + restores clients after restart + reads Client-Insights to update blocked reading - bugfix: 59_Weather: fix little bug then create weblink - bugfix: 88_HMCCU: Flag for disabling initial device update - bugfix: 10_MYSENSORS_DEVICE: prevent fhem crashing by ack timeout diff --git a/fhem/FHEM/74_Unifi.pm b/fhem/FHEM/74_Unifi.pm index 07d01cf8a..64b13a7c4 100644 --- a/fhem/FHEM/74_Unifi.pm +++ b/fhem/FHEM/74_Unifi.pm @@ -51,16 +51,23 @@ # V 3.2.3 # - feature: 74_Unifi: new customClientReading _f_last_seen_duration # V 3.2.4 -# - feature: 74_Unifi: new cattribute customClientNames +# - feature: 74_Unifi: new attribute customClientNames # V 3.2.5 # - fixed: 74_Unifi: fixed createVoucher and (un-)blockClient for UC-V5.10 # V 3.2.6 # - fixed: 74_Unifi: fixed locate/restartAP and disconnectClient for UC-V5.10 # V 3.2.7 # - fixed: 74_Unifi: fixed reading-Update for disconnected clients +# V 3.2.8 +# - feature: 74_Unifi: read the client insights to update blocked reading +# V 3.3.0 +# - feature: 74_Unifi: supports new module UnifiClient +# - feature: 74_Unifi: read usergroups at define-module, new setter "refreshUsergroups" +# - feature: 74_Unifi: new setter "removeClientReadings" +# - feature: 74_Unifi: persist disconnected clients and rebuild them after fhem-restart package main; -my $version="3.2.7"; +my $version="3.3.0"; # Default für clientRedings setzen. Die Readings waren der Standard vor Einführung des Attributes customClientReadings. # Eine Änderung hat Auswirkungen auf (alte) Moduldefinitionen ohne dieses Attribut. my $defaultClientReadings=".:^accesspoint|^essid|^hostname|^last_seen|^snr|^uptime"; #ist wegen snr vs rssi nur halb korrekt, wird aber auch nicht wirklich verwendet ;-) @@ -81,12 +88,14 @@ sub Unifi_Notify($$); sub Unifi_Set($@); sub Unifi_Get($@); sub Unifi_Attr(@); -sub Unifi_Write($$); +sub Unifi_Write($@); sub Unifi_DoUpdate($@); sub Unifi_Login_Send($); sub Unifi_Login_Receive($); sub Unifi_GetClients_Send($); sub Unifi_GetClients_Receive($); +sub Unifi_GetClientInsights_Send($); +sub Unifi_GetClientInsights_Receive($); sub Unifi_GetWlans_Send($); sub Unifi_GetWlans_Receive($); sub Unifi_GetHealth_Send($); @@ -109,7 +118,11 @@ sub Unifi_SetWlanReadings($); sub Unifi_DisconnectClient_Send($@); sub Unifi_DisconnectClient_Receive($); sub Unifi_ApCmd_Send($$@); -sub Unifi_ApCmd_Receive($); +sub Unifi_DeviceRestJson_Send($$$); +sub Unifi_UserRestJson_Send($$$); +sub Unifi_DeviceCmd_Receive($); +sub Unifi_UsergroupRestJson_Send($); +sub Unifi_UsergroupRestJson_Receive($); sub Unifi_ArchiveAlerts_Send($); sub Unifi_Cmd_Receive($); sub Unifi_ClientNames($@); @@ -161,10 +174,12 @@ sub Unifi_Initialize($$) { ."voucherCache " ."customClientReadings:textField-long " ."customClientNames " + # ."readClientInsights " .$readingFnAttributes; - $hash->{Clients} = "UnifiSwitch"; - $hash->{MatchList} = { "1:UnifiSwitch" => "^UnifiSwitch" }; + $hash->{Clients} = "UnifiSwitch:UnifiClient"; + $hash->{MatchList} = { "1:UnifiSwitch" => "^UnifiSwitch", + "2:UnifiClient" => "^UnifiClient"}; } ############################################################################### @@ -207,8 +222,6 @@ sub Unifi_Define($$) { $define.=" $a[7]" if($a[7]); $hash->{DEF} = $define; - $hash->{unifi}->{customClientReadings}->{attr_value} = AttrVal($name,"customClientReadings",$defaultClientReadings); - Unifi_initCustomClientReadings($hash); Log3 $name, 5, "$name: Defined with url:$hash->{unifi}->{url}, interval:$hash->{unifi}->{interval}"; return undef; } @@ -239,6 +252,38 @@ sub Unifi_Notify($$) { Unifi_CONNECTED($hash,'initialized'); Unifi_DoUpdate($hash); } + + if($dev->{NAME} eq "global"){ #INITIALIZED|REREADCFG + $hash->{unifi}->{customClientReadings}->{attr_value} = AttrVal($name,"customClientReadings",$defaultClientReadings); + Unifi_initCustomClientReadings($hash); + + for my $readingName (keys %{$hash->{READINGS}}) { + #Log3 $name, 1, "$name ($self) - checking 1 $readingName"; + if($readingName =~ m/\..*_user_id$/){ + #Log3 $name, 1, "$name ($self) - found 1 $readingName"; + my $readingValue=ReadingsVal($name,$readingName,""); + if($readingValue =~ m/^[a-fA-F0-9]*$/g){ + my $clientName=substr($readingName,1,index($readingName,"_user_id")-1); + $hash->{clients}->{$readingValue}->{name} = $clientName; + Log3 $name, 5, "$name ($self) - restored client $clientName with ID $readingValue"; + } + } + } + for my $clientID (keys %{$hash->{clients}}) { + my $clientName=$hash->{clients}->{$clientID}->{name}."_"; + for my $readingName (keys %{$hash->{READINGS}}) { + #Log3 $name, 1, "$name ($self) - checking 2 $readingName for $clientName"; + if($readingName =~ m/^$clientName.*/){ + #Log3 $name, 1, "$name ($self) - found 2 $readingName"; + my $readingValue=ReadingsVal($name,$readingName,""); + my $readingName=substr($readingName,length($clientName)); + $hash->{clients}->{$clientID}->{$readingName} = $readingValue; + Log3 $name, 5, "$name ($self) - restored internal $readingName = $readingValue for client $clientName"; + } + } + } + } + return undef; } ############################################################################### @@ -257,19 +302,44 @@ sub Unifi_Set($@) { Log3 $name, 5, "$name: set called with $setName but device is disabled!" if($setName ne "?"); return undef; } - - my $clientNames = Unifi_ClientNames($hash); + my $blockedClientNames =""; + my $unblockedClientNames =""; + my $connectedClientNames =""; + my $disconnectedClientNames =""; + my $clientName=""; + for my $clientID (keys %{$hash->{clients}}) { + my $clientName=Unifi_ClientNames($hash,$clientID,'makeAlias'); + if(defined $hash->{clients}->{$clientID}->{blocked} && $hash->{clients}->{$clientID}->{blocked} eq JSON::true){ + $blockedClientNames.=$clientName.','; + }else{ + $unblockedClientNames.=$clientName.','; + } + if(defined $hash->{unifi}->{connectedClients}->{$clientID}){ + $connectedClientNames.=$clientName.','; + }else{ + $disconnectedClientNames.=$clientName.','; + } + } + $blockedClientNames =~ s/.$//; + $unblockedClientNames =~ s/.$//; + $connectedClientNames =~ s/.$//; + $disconnectedClientNames =~ s/.$//; + + #my $clientNames = Unifi_ClientNames($hash); my $apNames = Unifi_ApNames($hash); my $SSIDs = Unifi_SSIDs($hash); - if($setName !~ /archiveAlerts|restartAP|setLocateAP|unsetLocateAP|disconnectClient|update|updateClient|clear|blockClient|unblockClient|enableWLAN|disableWLAN|switchSiteLEDs|createVoucher/) { + if($setName !~ /archiveAlerts|restartAP|setLocateAP|unsetLocateAP|disconnectClient|update|updateClient|clear|blockClient|unblockClient|enableWLAN|disableWLAN|switchSiteLEDs|createVoucher|removeClientReadings|refreshUsergroups/) { return "Unknown argument $setName, choose one of update:noArg " ."clear:all,readings,clientData,allData,voucherCache " .((defined $hash->{alerts_unarchived}[0] && scalar @{$hash->{alerts_unarchived}}) ? "archiveAlerts:noArg " : "") .(($apNames && Unifi_CONNECTED($hash)) ? "restartAP:all,$apNames setLocateAP:all,$apNames unsetLocateAP:all,$apNames " : "") - .(($clientNames && Unifi_CONNECTED($hash)) ? "disconnectClient:all,$clientNames " : "") + .(($connectedClientNames && Unifi_CONNECTED($hash)) ? "disconnectClient:all,$connectedClientNames " : "") + .(($disconnectedClientNames && Unifi_CONNECTED($hash)) ? "removeClientReadings:$disconnectedClientNames " : "") + .(($unblockedClientNames && Unifi_CONNECTED($hash)) ? "blockClient:$unblockedClientNames " : "") + .(($blockedClientNames && Unifi_CONNECTED($hash)) ? "unblockClient:$blockedClientNames " : "") ."createVoucher enableWLAN:$SSIDs disableWLAN:$SSIDs " - ."blockClient:$clientNames unblockClient:$clientNames switchSiteLEDs:on,off updateClient"; + ."switchSiteLEDs:on,off updateClient refreshUsergroups:noArg"; } else { Log3 $name, 4, "$name: set $setName"; @@ -282,7 +352,7 @@ sub Unifi_Set($@) { Unifi_DisconnectClient_Send($hash,$setVal); } else { - return "$hash->{NAME}: Unknown client '$setVal' in command '$setName', choose one of: all,$clientNames"; + return "$hash->{NAME}: Unknown client '$setVal' in command '$setName', choose one of: all,$connectedClientNames"; } } elsif (!$setVal || $setVal eq 'all') { @@ -299,8 +369,10 @@ sub Unifi_Set($@) { } if($mac ne "x"){ Unifi_BlockClient_Send($hash,$mac); + $hash->{clients}->{$id}->{blocked}=JSON::true; # eigentlich vorauseilender Gehorsam, aber leider kommt beim blockClient_Receive keine ID zurück um dort zu setzen. + delete $hash->{unifi}->{connectedClients}->{$id}; }else { - return "$hash->{NAME}: Unknown client '$setVal' in command '$setName', use mac or choose one of: $clientNames"; + return "$hash->{NAME}: Unknown client '$setVal' in command '$setName', use mac or choose one of: $unblockedClientNames"; } } elsif ($setName eq 'unblockClient') { @@ -313,8 +385,9 @@ sub Unifi_Set($@) { } if($mac ne "x"){ Unifi_UnblockClient_Send($hash,$mac); + $hash->{clients}->{$id}->{blocked}=JSON::false; # eigentlich vorauseilender Gehorsam, aber leider kommt beim unblockClient_Receive keine ID zurück um dort zu setzen. }else { - return "$hash->{NAME}: Unknown client '$setVal' in command '$setName', use mac or choose one of: $clientNames"; + return "$hash->{NAME}: Unknown client '$setVal' in command '$setName', use mac or choose one of: $blockedClientNames"; } } elsif ($setName eq 'switchSiteLEDs') { @@ -396,6 +469,24 @@ sub Unifi_Set($@) { Unifi_ApCmd_Send($hash,'unset-locate',keys(%{$hash->{accespoints}})); } } + elsif ($setName eq 'removeClientReadings') { + if($setVal && $setVal ne ""){ + my $id = Unifi_ClientNames($hash,$setVal,'makeID'); + if(defined $hash->{clients}->{$id}){ + readingsDelete($hash, $setVal); + my $readingspec= '^' . $setVal . '_.*$'; + foreach my $reading (grep { /$readingspec/ } + keys %{$hash->{READINGS}}) { + readingsDelete($hash, $reading); + } + delete $hash->{clients}->{$id}; + }else{ + return "$hash->{NAME}: Unknown clientName '$setVal' in command '$setName', choose one of: $disconnectedClientNames"; + } + }else{ + return "$hash->{NAME}: No clientName in command '$setName', choose one of: $disconnectedClientNames"; + } + } elsif ($setName eq 'createVoucher') { if (!looks_like_number($setVal) || int($setVal) < 1 || !looks_like_number($setVal2) || int($setVal2) < 1 || @@ -411,7 +502,10 @@ sub Unifi_Set($@) { } elsif ($setName eq 'refreshUCversion') { Unifi_GetSysinfo_Send($hash); - } + } + elsif ($setName eq 'refreshUsergroups') { + Unifi_UsergroupRestJson_Send($hash); + } } if ($setName eq 'update') { RemoveInternalTimer($hash); @@ -644,6 +738,13 @@ sub Unifi_Attr(@) { elsif($attr_name eq "customClientNames") { $hash->{unifi}->{customClientNames}->{attr_value} = $attr_value; } + #elsif($attr_name eq "readClientInsights") { + # if (!looks_like_number($attr_value) || int($attr_value) < 1 || int($attr_value) > 365) { + # return "$name: Value \"$attr_value\" is not allowed.\n" + # ."readClientInsights must be a number between 1 and 365." + # } + # $hash->{unifi}->{readClientInsights}->{attr_value} = $attr_value*24; + #} } elsif($cmd eq "del") { if($attr_name eq "disable" && Unifi_CONNECTED($hash) eq "disabled") { @@ -667,22 +768,30 @@ sub Unifi_Attr(@) { elsif($attr_name eq "customClientNames") { $hash->{unifi}->{customClientNames}->{attr_value} = $customClientName; } + #elsif($attr_name eq "readClientInsights") { + # $hash->{unifi}->{readClientInsights} = undef; + #} } return undef; } ############################################################################### -sub Unifi_Write($$){ - my ( $hash, $type, $ap_id, $data) = @_; #TODO: ap_id und port_overrides in @a, damit es für andere $type auch geht. - - my ($name,$self) = ($hash->{NAME},Unifi_Whoami()); - Log3 $name, 5, "$name ($self) - executed with ".$type; - if($type eq "Unifi_RestJson_Send"){ - Unifi_RestJson_Send($hash, $ap_id, {port_overrides => $data }); - } - if($type eq "Unifi_ApJson_Send"){ - Unifi_ApJson_Send($hash, $data ); - } +sub Unifi_Write($@){ + my ($hash, @args) = @_; + my ($type, $id, $data)=@args; + my ($name,$self) = ($hash->{NAME},Unifi_Whoami()); + Log3 $name, 3, "$name ($self) - executed with ".$type; + if($type eq "Unifi_DeviceRestJson_Send"){ + Unifi_DeviceRestJson_Send($hash, $id, {port_overrides => $data });#id=ap_id + }elsif($type eq "Unifi_ApJson_Send"){ + Unifi_ApJson_Send($hash, $data); + }elsif($type eq "Unifi_BlockClient_Send"){ + Unifi_BlockClient_Send($hash, $id); # id=mac + }elsif($type eq "Unifi_UnblockClient_Send"){ + Unifi_UnblockClient_Send($hash, $id); # id=mac + }elsif($type eq "Unifi_UserRestJson_Send"){ + Unifi_UserRestJson_Send($hash, $id, $data); # id=_id + } return undef; } @@ -702,6 +811,8 @@ sub Unifi_DoUpdate($@) { $hash->{unifi}->{updateStartTime} = time(); $hash->{updateDispatch} = { # {updateDispatch}->{callFn}[callFnRef,'receiveFn',receiveFnRef] Unifi_GetClients_Send => [\&Unifi_GetClients_Send,'Unifi_GetClients_Receive',\&Unifi_GetClients_Receive], + Unifi_GetClientInsights_Send => [\&Unifi_GetClientInsights_Send,'Unifi_GetClientInsights_Receive',\&Unifi_GetClientInsights_Receive], + #Unifi_UsergroupRestJson_Send => [\&Unifi_UsergroupRestJson_Send,'Unifi_UsergroupRestJson_Receive',\&Unifi_UsergroupRestJson_Receive], Unifi_GetAccesspoints_Send => [\&Unifi_GetAccesspoints_Send,'Unifi_GetAccesspoints_Receive',\&Unifi_GetAccesspoints_Receive], Unifi_GetWlans_Send => [\&Unifi_GetWlans_Send,'Unifi_GetWlans_Receive',\&Unifi_GetWlans_Receive], Unifi_GetVoucherList_Send => [\&Unifi_GetVoucherList_Send,'Unifi_GetVoucherList_Receive',\&Unifi_GetVoucherList_Receive], @@ -768,6 +879,7 @@ sub Unifi_Login_Receive($) { Log3 $name, 5, "$name ($self) - Login successfully! $hash->{httpParams}->{header}"; Unifi_CONNECTED($hash,'connected'); Unifi_GetSysinfo_Send($hash); + Unifi_UsergroupRestJson_Send($hash); #Unifi_DoUpdate($hash); InternalTimer(time()+$hash->{unifi}->{interval}, 'Unifi_DoUpdate', $hash, 0); return undef; @@ -833,11 +945,20 @@ sub Unifi_GetClients_Receive($) { if ($data->{meta}->{rc} eq "ok") { Log3 $name, 5, "$name ($self) - state:'$data->{meta}->{rc}'"; - + $hash->{unifi}->{connectedClients} = undef; for my $h (@{$data->{data}}) { $hash->{unifi}->{connectedClients}->{$h->{user_id}} = 1; $hash->{clients}->{$h->{user_id}} = $h; + + # mergen mit den clientInsights. ACHTUNG: dasselbe muss aufgrund unbekannter Reihenfolge der Aufrufe auch in GetClientInsights_Receive und UpdateClient_Receive durchgeführt werden. + if (defined $hash->{unifi}->{clientInsights}->{$h->{user_id}}){ + if (defined $hash->{unifi}->{clientInsights}->{$h->{user_id}}->{blocked} && $hash->{unifi}->{clientInsights}->{$h->{user_id}}->{blocked} eq JSON::true){ + $hash->{clients}->{$h->{user_id}}->{blocked}=JSON::true; + }else{ + $hash->{clients}->{$h->{user_id}}->{blocked}=JSON::false; + } + } } } else { Unifi_ReceiveFailure($hash,$data->{meta}); } @@ -850,6 +971,61 @@ sub Unifi_GetClients_Receive($) { } ############################################################################### +sub Unifi_GetClientInsights_Send($) { + my ($hash) = @_; + my ($name,$self) = ($hash->{NAME},Unifi_Whoami()); + Log3 $name, 5, "$name ($self) - executed."; + #my $within=$hash->{unifi}->{readClientInsights}->{attr_value} if defined $hash->{unifi}->{readClientInsights}; + #if (defined $within){ + HttpUtils_NonblockingGet( { + %{$hash->{httpParams}}, + method => "GET", + url => $hash->{unifi}->{url}."stat/alluser?within=".(365*24), + callback => \&Unifi_GetClientInsights_Receive, + } ); + #} + return undef; +} +sub Unifi_GetClientInsights_Receive($) { + my ($param, $err, $data) = @_; + my ($name,$self,$hash) = ($param->{hash}->{NAME},Unifi_Whoami(),$param->{hash}); + Log3 $name, 5, "$name ($self) - executed."; + + if ($err ne "") { + Unifi_ReceiveFailure($hash,{rc => 'Error while requesting', msg => $param->{url}." - $err"}); + } + elsif ($data ne "") { + if ($param->{code} == 200 || $param->{code} == 400 || $param->{code} == 401) { + eval { $data = decode_json($data); 1; } or do { $data = { meta => {rc => 'error.decode_json', msg => $@} }; }; + + if ($data->{meta}->{rc} eq "ok") { + Log3 $name, 5, "$name ($self) - state:'$data->{meta}->{rc}'"; + + $hash->{unifi}->{clientInsights} = undef; + for my $h (@{$data->{data}}) { + $hash->{unifi}->{clientInsights}->{$h->{_id}} = $h; + + # mergen mit den clients. ACHTUNG: dasselbe muss aufgrund unbekannter Reihenfolge der Aufrufe auch in GetClients_Receive und UpdateClient_Receive durchgeführt werden. + if (defined $hash->{clients}->{$h->{_id}}){ + if (defined $h->{blocked} && $h->{blocked} eq JSON::true){ + $hash->{clients}->{$h->{_id}}->{blocked}=JSON::true; + }else{ + $hash->{clients}->{$h->{_id}}->{blocked}=JSON::false; + } + } + } + } + else { Unifi_ReceiveFailure($hash,$data->{meta}); } + } else { + Unifi_ReceiveFailure($hash,{rc => $param->{code}, msg => "Failed with HTTP Code $param->{code}."}); + } + } + + Unifi_NextUpdateFn($hash,$self); + return undef; +} +############################################################################### + sub Unifi_UpdateClient_Send($$) { my ($hash,$mac) = @_; my ($name,$self) = ($hash->{NAME},Unifi_Whoami()); @@ -885,16 +1061,21 @@ sub Unifi_UpdateClient_Receive($) { $apNames->{$apRef->{mac}} = $apRef->{name} ? $apRef->{name} : $apRef->{ip}; } - #$hash->{unifi}->{connectedClients} = undef; for my $h (@{$data->{data}}) { $hash->{unifi}->{connectedClients}->{$h->{user_id}} = 1; $hash->{clients}->{$h->{user_id}} = $h; - + # mergen mit den clientInsights. ACHTUNG: dasselbe muss aufgrund unbekannter Reihenfolge der Aufrufe auch in GetClientInsights_Receive und GetClients_Receive durchgeführt werden. + if (defined $hash->{unifi}->{clientInsights}->{$h->{user_id}}){ + if (defined $hash->{unifi}->{clientInsights}->{$h->{user_id}}->{blocked} && $hash->{unifi}->{clientInsights}->{$h->{user_id}}->{blocked} eq JSON::true){ + $hash->{clients}->{$h->{user_id}}->{blocked}=JSON::true; + }else{ + $hash->{clients}->{$h->{user_id}}->{blocked}=JSON::false; + } + } readingsBeginUpdate($hash); - Unifi_setClientReadings($hash); + Unifi_setClientReadings($hash); readingsEndUpdate($hash,1); } - #$hash->{clients}->{$data->{data}[0]->{user_id}} = $data->{data}; } else { Unifi_ReceiveFailure($hash,$data->{meta}); } } else { @@ -1257,8 +1438,8 @@ sub Unifi_SetClientReadings($) { my $ignoreWireless = AttrVal($name,"ignoreWirelessClients",undef); my ($apName,$clientName,$clientRef); - my $sep=""; my $newClients=""; + my $blockedClients=""; for my $clientID (keys %{$hash->{clients}}) { $clientRef = $hash->{clients}->{$clientID}; $clientName = Unifi_ClientNames($hash,$clientID,'makeAlias'); @@ -1317,19 +1498,34 @@ sub Unifi_SetClientReadings($) { my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)= gmtime($clientRef->{_uptime_by_uap}); $clientRef->{_f_uptime_by_uap}=$yday."d ".$hour."h ".$min."m ".$sec."s"; } - } - - - if (defined $hash->{unifi}->{connectedClients}->{$clientID}) { - - if (! defined ReadingsVal($hash->{NAME},$clientName,undef)){ - $newClients.=$sep.$clientName; - $sep=","; + my $tx=ReadingsVal($name,$clientName."_tx_bytes",""); + if ($tx ne "" && defined $clientRef->{tx_bytes}){ + $clientRef->{_f_diff_tx_bytes}=($clientRef->{tx_bytes})-($tx); } - readingsBulkUpdate($hash,$clientName,'connected'); + + if (defined $clientRef->{usergroup_id} && defined $hash->{unifi}->{usergroups}->{$clientRef->{usergroup_id}}){ + $clientRef->{_f_usergroup_name}=$hash->{unifi}->{usergroups}->{$clientRef->{usergroup_id}}->{name}; + }else{ + $clientRef->{_f_usergroup_name}="Default"; + } + + $clientRef->{fhem_clientName}=$clientName; } - elsif (defined($hash->{READINGS}->{$clientName}) && $hash->{READINGS}->{$clientName}->{VAL} ne 'disconnected') { + + if (defined $hash->{unifi}->{connectedClients}->{$clientID}) { + if (! defined ReadingsVal($hash->{NAME},$clientName,undef)){ + $newClients.=$clientName.","; + } + $clientRef->{fhem_state}="connected"; + readingsBulkUpdate($hash,$clientName,'connected'); + readingsBulkUpdate($hash,".".$clientName."_user_id",$clientRef->{user_id}); + } + elsif ((!defined($hash->{READINGS}->{$clientName})) || (defined($hash->{READINGS}->{$clientName}) && $hash->{READINGS}->{$clientName} ne "disconnected")) { Log3 $name, 5, "$name ($self) - Client '$clientName' previously connected is now disconnected."; + if(defined $clientRef->{blocked} && $clientRef->{blocked} eq JSON::true){ + $blockedClients.=$clientName.','; + } + $clientRef->{fhem_state}="disconnected"; readingsBulkUpdate($hash,$clientName,'disconnected'); } @@ -1371,8 +1567,15 @@ sub Unifi_SetClientReadings($) { } } } + + Log3 $name, 5, "$name ($self) - Dispatch: ".$clientName; + Dispatch($hash,"UnifiClient_".$clientName.encode_json($clientRef),undef); } + + $newClients =~ s/.$// if $newClients ne ""; + $blockedClients =~ s/.$// if $blockedClients ne ""; readingsBulkUpdate($hash,"-UC_newClients",$newClients); + readingsBulkUpdate($hash,"-UC_blockedClients",$blockedClients); return undef; } @@ -1422,7 +1625,7 @@ sub Unifi_SetAccesspointReadings($) { my $essid = 'none'; } - #nochmal genauer ins json schauen, ob hier wirklich eine Schleife notwendig ist. (cu_total mehrfach vorhanden? + #nochmal genauer ins json schauen, ob hier wirklich eine Schleife notwendig ist. (cu_total mehrfach vorhanden?) if (defined $apRef->{'radio_table_stats'} ) { for my $rts (@{$apRef->{'radio_table_stats'}}) { $utiliz .= makeReadingName($rts->{'cu_total'}).','; # TODO: hier nicht unbedingt notwendig, aufgrund des funktionierenden schnellen fixes wegen neuer controller-Version bleibt es aber erstmal so drin. @@ -1808,7 +2011,7 @@ sub Unifi_ApCmd_Send($$@) { #cmd: 'set-locate', 'unset-locate', 'restart' HttpUtils_NonblockingGet( { %{$hash->{httpParams}}, url => $hash->{unifi}->{url}."cmd/devmgr", - callback => \&Unifi_ApCmd_Receive, + callback => \&Unifi_DeviceCmd_Receive, aps => [@aps], cmd => $cmd, data => "{\"mac\":\"".$hash->{accespoints}->{$id}->{mac}."\", \"cmd\":\"".$cmd."\"}", @@ -1823,13 +2026,13 @@ sub Unifi_ApJson_Send($$) { HttpUtils_NonblockingGet( { %{$hash->{httpParams}}, url => $hash->{unifi}->{url}."cmd/devmgr", - callback => \&Unifi_ApCmd_Receive, + callback => \&Unifi_DeviceCmd_Receive, aps => [], data => $json, } ); return undef; } -sub Unifi_RestJson_Send($$$) { +sub Unifi_DeviceRestJson_Send($$$) { my ($hash,$id,$data) = @_; my ($name,$self) = ($hash->{NAME},Unifi_Whoami()); my $json = encode_json( $data ); @@ -1838,14 +2041,28 @@ sub Unifi_RestJson_Send($$$) { %{$hash->{httpParams}}, method => "PUT", url => $hash->{unifi}->{url}."rest/device/".$id, - callback => \&Unifi_ApCmd_Receive, + callback => \&Unifi_DeviceCmd_Receive, + aps => [], + data => $json, + } ); + return undef; +} +sub Unifi_UserRestJson_Send($$$) { + my ($hash,$id,$json) = @_; + my ($name,$self) = ($hash->{NAME},Unifi_Whoami()); + Log3 $name, 5, "$name ($self) - executed for id $id with $json."; + HttpUtils_NonblockingGet( { + %{$hash->{httpParams}}, + method => "PUT", + url => $hash->{unifi}->{url}."rest/user/".$id, + callback => \&Unifi_DeviceCmd_Receive, aps => [], data => $json, } ); return undef; } ############################################################################### -sub Unifi_ApCmd_Receive($) { +sub Unifi_DeviceCmd_Receive($) { my ($param, $err, $data) = @_; my ($name,$self,$hash) = ($param->{hash}->{NAME},Unifi_Whoami(),$param->{hash}); Log3 $name, 5, "$name ($self) - executed."; @@ -1873,6 +2090,45 @@ sub Unifi_ApCmd_Receive($) { return undef; } ############################################################################### +sub Unifi_UsergroupRestJson_Send($) { + my ($hash) = @_; + my ($name,$self) = ($hash->{NAME},Unifi_Whoami()); + Log3 $name, 5, "$name ($self) - executed."; + HttpUtils_NonblockingGet( { + %{$hash->{httpParams}}, + method => "GET", + url => $hash->{unifi}->{url}."rest/usergroup/", + callback => \&Unifi_UsergroupRestJson_Receive, + } ); + return undef; +} +sub Unifi_UsergroupRestJson_Receive($) { + my ($param, $err, $data) = @_; + my ($name,$self,$hash) = ($param->{hash}->{NAME},Unifi_Whoami(),$param->{hash}); + Log3 $name, 5, "$name ($self) - executed."; + + if ($err ne "") { + Unifi_ReceiveFailure($hash,{rc => 'Error while requesting', msg => $param->{url}." - $err"}); + } + elsif ($data ne "") { + if ($param->{code} == 200 || $param->{code} == 400 || $param->{code} == 401) { + eval { $data = decode_json($data); 1; } or do { $data = { meta => {rc => 'error.decode_json', msg => $@} }; }; + + if ($data->{meta}->{rc} eq "ok") { + Log3 $name, 5, "$name ($self) - state:'$data->{meta}->{rc}'"; + for my $h (@{$data->{data}}) { + $hash->{unifi}->{usergroups}->{$h->{_id}} = $h; + } + } + else { Unifi_ReceiveFailure($hash,$data->{meta}); } + } else { + Unifi_ReceiveFailure($hash,{rc => $param->{code}, msg => "Failed with HTTP Code $param->{code}."}); + } + } + + return undef; +} +############################################################################### sub Unifi_ArchiveAlerts_Send($) { my ($hash) = @_; @@ -2394,6 +2650,15 @@ Or you can use the other readings or set and get features to control your unifi-
  • set <name> createVoucher <expire> <n> <quota> <note>
    Creates <n> vouchers that expires after <expire> minutes, are usable <quota>-times with a <note>no spaces in note allowed

  • +
  • set <name> removeClientReadings <clientname>
    + Deletes the readings for <clientname>. Only disconnected clients should be removed, because connected clients will create readings in next update-cycle.
  • +
    +
  • set <name> refreshUCversion
    + Refresh the INTERNAL-value for the version of your unifi-controller.
  • +
    +
  • set <name> refreshUsergroups
    + Usergroups won't be updated automatically. Use this setter to update usergroups manually.
  • +
    @@ -2472,7 +2737,11 @@ Or you can use the other readings or set and get features to control your unifi-
  • attr customClientNames <value>
    Can be used to control the naming convention for client readings. Any valid clientData field is allowed, though only mac seems to be useful. For a list, see <unifi> get ClientData all.
    - Client naming follows these rules: + When using formatted readings (reading is named like <clientName>_f_...) with disconnected clients e.g. in DOIF or notifies, also specify the unformatted value as customClientReading.
    + e.g. ^_f_last_seen_duration$|^last_seen$
    + Reason: Disconnected clients won't be send by UnifiController and are loosing their values when restarting fhem. + The UnifiModul tries to restore the clients from existing readings. This won't work for formatted readings if the unformatted value hasn't a reading itself.
    + Client naming follows these rules:
    1. devAlias (if present for this client)
    2. attribute customClientNames (if it is set and the corresponding data field exists for client)
    3. @@ -2487,8 +2756,10 @@ Or you can use the other readings or set and get features to control your unifi-

      Readings

        Note: All readings generate events. You can control this with these global attributes. +
      • When clientnames start with a . (dot), FHEMWEB hides readings for this client when global showInternal Values ne 1.
      • Each client has 7 readings for connection-state, SNR, uptime, last_seen-time, connected-AP, essid and hostname if attribute customClientReadings is not used.
      • -
      • -UC_newClients shows nameof a new client, could be a comma-separated list. Will be set to empty at next interval.
      • +
      • -UC_newClients shows names of a new clients, could be a comma-separated list. Will be set to empty at next interval.
      • +
      • -UC_blockedClients shows names of a blocked clients, could be a comma-separated list.
      • Each AP has 5 readings for state (can be 'ok' or 'error'), essid's, utilization, locate and count of connected-clients.
      • The unifi-controller has 6 readings for event-count in configured 'timePeriod', unarchived-alert count, accesspoint count, overall wlan-state (can be 'ok', 'warning', or other?), connected user count and connected guest count.
      • The unifi-controller has also readings for wan_ip and results of the speedtest (UC_speed_ping,UC_speed_down,UC_speed_up).
      • diff --git a/fhem/FHEM/74_UnifiClient.pm b/fhem/FHEM/74_UnifiClient.pm new file mode 100755 index 000000000..417b7d379 --- /dev/null +++ b/fhem/FHEM/74_UnifiClient.pm @@ -0,0 +1,440 @@ +############################################################################## +# $Id$ +# 74_UnifiClient.pm + +# CHANGED +############################################################################## +# V 0.0.1 BETA +# - feature: 74_UnifiClient: initial version + +package main; +my $version="0.0.1 BETA"; +my $thresholdBytesPerMinuteDefault=75000; +my $maxOnlineMinutesPerDayDefault=2000;# deutlich mehr als 1440=24h +# Laden evtl. abhängiger Perl- bzw. FHEM-Module +use strict; +use warnings; +use POSIX; +use JSON qw(decode_json); + +### Forward declarations ####################################################{ +sub UnifiClient_Initialize($$); +sub UnifiClient_Define($$); +sub UnifiClient_Undef($$); +sub UnifiClient_Attr(@); +sub UnifiClient_Notify($$); +sub UnifiClient_Set($@); +sub UnifiClient_Get($@); +sub UnifiClient_Parse($$); +sub UnifiClient_DailyReset($); +sub UnifiClient_Whoami(); +sub UnifiClient_Whowasi(); + + +sub UnifiClient_Initialize($$) { + my ($hash) = @_; + $hash->{DefFn} = "UnifiClient_Define"; + $hash->{UndefFn} = "UnifiClient_Undef"; + $hash->{ParseFn} = "UnifiClient_Parse"; + $hash->{AttrFn} = "UnifiClient_Attr"; + $hash->{SetFn} = "UnifiClient_Set"; + $hash->{GetFn} = "UnifiClient_Get"; + $hash->{AttrList} = "maxOnlineMinutesPerDay " + ."thresholdBytesPerMinute " + ."blockingUsergroup " + .$readingFnAttributes; + $hash->{Match} = "^UnifiClient"; +} + +sub UnifiClient_Define($$) { + my ( $hash, $def) = @_; + my @a = split("[ \t][ \t]*", $def); + my $name = $a[0]; + Log3 $name, 3, "UnifiClient_Define - executed. 0 "; + return "Wrong syntax: use define UnifiClient " if(int(@a) < 2); + # zweites Argument ist die eindeutige Geräteadresse + my $address = $a[2]; + + if(defined($modules{UnifiClient}{defptr}{$address})){ + return "Client with name $address already defined in ".($modules{UnifiClient}{defptr}{$address}{NAME}); + } + $hash->{CODE}=$address; + $hash->{VERSION}=$version; + $hash->{NOTIFYDEV} = "global"; + # Adresse rückwärts dem Hash zuordnen (für ParseFn) + $modules{UnifiClient}{defptr}{$address} = $hash; + + + Log3 $name, 5, "UnifiClient_Define - Adress: ".$address; + AssignIoPort($hash); + + + # TODO: Ab hier nach notify verschieben + $hash->{timeControl}->{clientblocked}=0;# TODO: Ändern in $hash->{unifiClient}->{usedOnlineTime} ??? oder anders speichern? + # clientBlocked und usedOnlineTime gehen verloren bei einem Neustart von fhem. + # im code mit TODO? markiert + + #$hash->{timeControl}->{usedOnlineTime}=0; # wird in DailyReset gesetzt -> Damit beim einem Neustart auf 0! TODO!!! + $hash->{timeControl}->{maxOnlineMinutesPerDay}=$maxOnlineMinutesPerDayDefault if ! defined $hash->{timeControl}->{maxOnlineMinutesPerDay}; + $hash->{timeControl}->{thresholdBytesPerMinute} = $thresholdBytesPerMinuteDefault if ! defined $hash->{timeControl}->{thresholdBytesPerMinute}; + UnifiClient_DailyReset($hash); +} + +sub UnifiClient_Undef($$){ + my ($hash, $name) = @_; + Log3 $name, 3, "$name (UnifiClient_Undef) - executed.".$hash->{CODE}; + if(defined($hash->{CODE}) && defined($modules{UnifiClient}{defptr}{$hash->{CODE}})){ + delete($modules{UnifiClient}{defptr}{$hash->{CODE}}); + } + + RemoveInternalTimer($hash); + return undef; +} + +sub UnifiClient_Notify($$) +{ + my ($hash,$dev) = @_; + my ($name,$self) = ($hash->{NAME},Unifi_Whoami()); + Log3 $name, 5, "$name ($self) - executed."; + + return if($dev->{NAME} ne "global"); + + return if(!grep(m/^DEFINED $name|MODIFIED $name|INITIALIZED|REREADCFG$/, @{$dev->{CHANGED}})); + + UnifiClient_DailyReset($hash); + #Todo: $hash->{timeControl}->{blockingUsergroupID} = $usergroupID; + + return undef; +} +sub UnifiClient_Attr(@){ + my ($cmd,$name,$attr_name,$attr_value) = @_; + my $hash = $defs{$name}; + + if($cmd eq "set") { + if($attr_name eq "disable") { + if($attr_value == 1) { + RemoveInternalTimer($hash); + } + else{ + # TODO: ggf. weitere Werte neu setzen? + UnifiClient_DailyReset($hash); + } + } + if($attr_name eq "maxOnlineMinutesPerDay") { + $hash->{timeControl}->{maxOnlineMinutesPerDay} = $attr_value; + }elsif($attr_name eq "thresholdBytesPerMinute") { + $hash->{timeControl}->{thresholdBytesPerMinute} = $attr_value; + }elsif($attr_name eq "blockingUsergroup") { + + #doppelter Code siehe UnifiClient_Set() + my $usergroupID=""; + if (defined $defs{$hash->{IODev}->{NAME}}->{unifi}->{usergroups} && $attr_value ne "Default"){ + for my $ugID (keys %{$defs{$hash->{IODev}->{NAME}}->{unifi}->{usergroups}}) { + $usergroupID=$ugID; + last if $attr_value eq $defs{$hash->{IODev}->{NAME}}->{unifi}->{usergroups}->{$usergroupID}->{name}; + } + } + $hash->{timeControl}->{blockingUsergroupID} = $usergroupID; + } + } + elsif($cmd eq "del") { + if($attr_name eq "maxOnlineMinutesPerDay") { + $hash->{timeControl}->{maxOnlineMinutesPerDay} = $maxOnlineMinutesPerDayDefault; + }elsif($attr_name eq "thresholdBytesPerMinute") { + $hash->{timeControl}->{thresholdBytesPerMinute} = $thresholdBytesPerMinuteDefault; + }elsif($attr_name eq "blockingUsergroup") { + delete $hash->{timeControl}->{blockingUsergroupID}; + } + } + return undef; +} + + +sub UnifiClient_Set($@){ + my ($hash,@a) = @_; + my ($name,$setName,$setVal,$setVal2) = @a; + Log3 $name, 5, "$name: set called with $setName " . ($setVal ? $setVal : "")." ". ($setVal2 ? $setVal2 : "") if ($setName ne "?"); + + my $usergroups=""; + my $clientUsergroupID=""; + $clientUsergroupID=$hash->{unifiClient}->{usergroup_id} if defined $hash->{unifiClient}->{usergroup_id}; + if (defined $defs{$hash->{IODev}->{NAME}}->{unifi}->{usergroups}){ + for my $usergroupID (keys %{$defs{$hash->{IODev}->{NAME}}->{unifi}->{usergroups}}) { + if($usergroupID ne $clientUsergroupID){ # die schon vorhandene Gruppe braucht nicht erneut gesetzt zu werden + $usergroups .=$defs{$hash->{IODev}->{NAME}}->{unifi}->{usergroups}->{$usergroupID}->{name}.","; + } + } + } + $usergroups =~ s/.$//; + if($setName !~ /clear|blockClient|unblockClient|usergroup/) { + return "Unknown argument $setName, choose one of " + ."clear:readings,usedOnlineTime " + .(($hash->{unifiClient}->{blocked} eq JSON::false) ? "blockClient:noArg " : "") + .(($hash->{unifiClient}->{blocked} eq JSON::true) ? "unblockClient:noArg " : "") + .(($usergroups ne "") ? "usergroup:$usergroups" : ""); + } elsif ($setName eq 'clear') { + if ($setVal eq 'readings') { + for (keys %{$hash->{READINGS}}) { + delete $hash->{READINGS}->{$_} if($_ ne 'state'); + } + }elsif ($setVal eq 'usedOnlineTime') { + $hash->{timeControl}->{usedOnlineTime}=0; + } + } elsif ($setName eq 'blockClient') { + IOWrite($hash, "Unifi_BlockClient_Send", $hash->{unifiClient}->{mac}); + $hash->{unifiClient}->{blocked}=JSON::true; + } elsif ($setName eq 'unblockClient') { + IOWrite($hash, "Unifi_UnblockClient_Send", $hash->{unifiClient}->{mac}); + $hash->{unifiClient}->{blocked}=JSON::false; + } elsif ($setName eq 'usergroup') { + my $usergroupID=""; + if (defined $defs{$hash->{IODev}->{NAME}}->{unifi}->{usergroups}){ + for my $ugID (keys %{$defs{$hash->{IODev}->{NAME}}->{unifi}->{usergroups}}) { + $usergroupID=$ugID; + last if $setVal eq $defs{$hash->{IODev}->{NAME}}->{unifi}->{usergroups}->{$usergroupID}->{name}; + } + } + my $clientnameUC = "?"; + if (defined $hash->{unifiClient}->{name}){ + $clientnameUC = $hash->{unifiClient}->{name}; + }elsif (defined $hash->{unifiClient}->{hostname}){ + $clientnameUC = $hash->{unifiClient}->{hostname}; + } + IOWrite($hash, "Unifi_UserRestJson_Send", $hash->{unifiClient}->{_id},"{\"usergroup_id\":\"".$usergroupID."\",\"name\":\"".$clientnameUC."\"}"); + } + return undef; +} + + +sub UnifiClient_Get($@){ + my ($hash,@a) = @_; + return "\"get $hash->{NAME}\" needs at least one argument" if ( @a < 2 ); + my ($name,$getName,$getVal) = @a; + + if (defined $getVal){ + Log3 $name, 5, "$name: get called with $getName $getVal." ; + }else{ + Log3 $name, 5, "$name: get called with $getName."; + } + + if($getName !~ /usergroups/) { + return "Unknown argument $getName, choose one of usergroups:noArg"; + } + elsif ($getName eq 'usergroups') { + my $usergroups=""; + if (defined $defs{$hash->{IODev}->{NAME}}->{unifi}->{usergroups}){ + for my $usergroupID (keys %{$defs{$hash->{IODev}->{NAME}}->{unifi}->{usergroups}}) { + $usergroups .="name: ".$defs{$hash->{IODev}->{NAME}}->{unifi}->{usergroups}->{$usergroupID}->{name}."\n"; + $usergroups .="max_down: ".$defs{$hash->{IODev}->{NAME}}->{unifi}->{usergroups}->{$usergroupID}->{qos_rate_max_down}."\n"; + $usergroups .="max_up: ".$defs{$hash->{IODev}->{NAME}}->{unifi}->{usergroups}->{$usergroupID}->{qos_rate_max_up}."\n"; + $usergroups .= "====================================================\n"; + } + } + $usergroups = "====================================================\n". $usergroups; + $usergroups .= "====================================================\n"; + return $usergroups; + } + return undef; +} + +sub UnifiClient_Parse($$) { + my ($io_hash, $message) = @_; + my ($name,$self) = ($io_hash->{NAME},UnifiClient_Whoami()); + my $i1=index($message,"_")+1; + my $i2=index($message,"{")-$i1; + my $address = substr($message, $i1, $i2); + Log3 $name, 5, "$name ($self) - executed. UnifiClient: Adress: ".$address; + my $message_json=substr($message,$i1+$i2); + Log3 $name, 5, "$name ($self) - executed. UnifiClient: message_json: ".$message_json; + + + # wenn bereits eine Gerätedefinition existiert (via Definition Pointer aus Define-Funktion) + if(my $hash = $modules{UnifiClient}{defptr}{$address}){ + # Nachricht für $hash verarbeiten + my $clientRef = decode_json($message_json); + $hash->{unifiClient} = $clientRef; + $hash->{STATE} = $clientRef->{fhem_state}; + $hash->{MODEL}=$clientRef->{oui}; + + my $old_tx=ReadingsVal($hash->{NAME},"tx_bytes",undef); + my $seconds=ReadingsAge($hash->{NAME},"tx_bytes",1); + $seconds=0.1 if ($seconds eq 0 || ! defined $seconds); + my $tx_used=0; + if (defined $old_tx && defined $clientRef->{tx_bytes}){ + $tx_used=($clientRef->{tx_bytes})-($old_tx); + $clientRef->{_f_diff_tx_bytes}=$tx_used; + } + my $usedPerMinute=60*$tx_used/$seconds; + if ($usedPerMinute > $hash->{timeControl}->{thresholdBytesPerMinute}){ + $hash->{timeControl}->{usedOnlineTime}=$hash->{timeControl}->{usedOnlineTime}+$seconds;# TODO? + } + $clientRef->{fhem_usedOnlineTime}=floor(($hash->{timeControl}->{usedOnlineTime})/60)." Minuten";# TODO? + + my $blockingUsergroupID=$hash->{timeControl}->{blockingUsergroupID};# TODO? + #doppelter Code siehe UnifiClient_Set() + my $clientnameUC = "?"; + if (defined $hash->{unifiClient}->{name}){ + $clientnameUC = $hash->{unifiClient}->{name}; + }elsif (defined $hash->{unifiClient}->{hostname}){ + $clientnameUC = $hash->{unifiClient}->{hostname}; + } + if($hash->{timeControl}->{usedOnlineTime} > (60*$hash->{timeControl}->{maxOnlineMinutesPerDay})){ # mit 60 multiplizieren, da usedOnlineTime in Sekunden gespeichert wird.# TODO: Ändern in $hash->{unifiClient}->{usedOnlineTime} + if(! $hash->{timeControl}->{clientblocked}){# TODO? + if (defined $blockingUsergroupID){ + my $origUsergroup=""; # = Default + $origUsergroup=$clientRef->{usergroup_id} if defined $clientRef->{usergroup_id}; + $hash->{timeControl}->{origUsergroup}=$origUsergroup;# TODO: Überlebt das einen Neustart von fhem? Oder besser ein neues Reading? + + IOWrite($hash, "Unifi_UserRestJson_Send", $clientRef->{_id},"{\"usergroup_id\":\"".$blockingUsergroupID."\",\"name\":\"".$clientnameUC."\"}"); + $clientRef->{usergroup_id}=$blockingUsergroupID;# ggf. auch _f_usergroupName setzen + } else{ + IOWrite($hash, "Unifi_BlockClient_Send", $clientRef->{mac}); + $clientRef->{blocked}=JSON::true; + } + } + $hash->{timeControl}->{clientblocked}=1;# TODO? + }elsif($hash->{timeControl}->{clientblocked}){# TODO? + if (defined $blockingUsergroupID){ + IOWrite($hash, "Unifi_UserRestJson_Send", $clientRef->{_id},"{\"usergroup_id\":\"".$hash->{timeControl}->{origUsergroup}."\",\"name\":\"".$clientnameUC."\"}"); + $clientRef->{usergroup_id}=$hash->{timeControl}->{origUsergroup}; # ggf. auch _f_usergroupName setzen + } else{ + IOWrite($hash, "Unifi_UnblockClient_Send", $clientRef->{mac}); + $clientRef->{blocked}=JSON::false; + } + $hash->{timeControl}->{clientblocked}=0;# TODO? + } + + readingsBeginUpdate($hash); + for my $key (keys %{$clientRef}) { + readingsBulkUpdate($hash,$key,$clientRef->{$key}); + } + readingsEndUpdate($hash,1); + + + Log3 $name, 5, "$name ($self) - return: ".$hash->{NAME}; + return $hash->{NAME}; # Rückgabe des Gerätenamens, für welches die Nachricht bestimmt ist. + } + else{ + # Keine Gerätedefinition verfügbar + # Daher Vorschlag define-Befehl: + Log3 $name, 3, "$name ($self) - return: UNDEFINED UnifiClient_".$address." UnifiClient $address"; + #return "UNDEFINED ".$address." UnifiClient $address"; + # lieber kein autocreate ;-) + return undef; + } +} + +############################################################################### +sub UnifiClient_DailyReset($){ + my ($hash) = @_; + my ($name,$self) = ($hash->{NAME},UnifiClient_Whoami()); + Log3 $name, 5, "$name ($self) - executed."; + RemoveInternalTimer($hash, 'UnifiClient_DailyReset'); + $hash->{timeControl}->{usedOnlineTime}=0; # TODO? + + my @l = localtime(); + $l[0] = 0; + $l[1] = 0; + $l[2] = 0; + my $localmidnighttime = timelocal(@l)+(60*60*24); + #my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)= gmtime($localmidnighttime); + #Log3 $name, 1, "$name ($self) - next reset at: $wday $mday.$mon.$year - $hour:$min:$sec"; + #my ($sec2,$min2,$hour2,$mday2,$mon2,$year2,$wday2,$yday2,$isdst2)= gmtime(time()); + #Log3 $name, 1, "$name ($self) - now: $wday2 $mday2.$mon2.$year2 - $hour2:$min2:$sec2"; + InternalTimer($localmidnighttime, 'UnifiClient_DailyReset', $hash, 0); +} + +############################################################################### + +sub UnifiClient_Whoami() { return (split('::',(caller(1))[3]))[1] || ''; } +sub UnifiClient_Whowasi() { return (split('::',(caller(2))[3]))[1] || ''; } + +# Eval-Rückgabewert für erfolgreiches +# Laden des Moduls +1; + + +# Beginn der Commandref + +=pod +=item device +=item summary Show info and control a UnifiClient (Unifi-Device required). +=item summary_DE Zeigt Infos zu einem UnifiClient an und steuert diesen. + +=begin html + + +

        UnifiClient

        +
          + +UnifiClient is the FHEM module for the Ubiquiti Networks (UBNT) Client.
          +
          +You can use the readings or set features to control your clients. +
          +

          Prerequisites

          +
            +
          • + A connected Unifi-Device as IODev. +
          • +
          • + The Perl module JSON is required.
            + On Debian/Raspbian: apt-get install libjson-perl
            + Via CPAN: cpan install JSON +
          • +
          + +

          Define

          +
            + define <name> UnifiClient <clientName> +
            + <name>: +
              + The FHEM device name for the device.
              +
            + <clientName>: +
              + The name of the client in unifi-module.
              +
            +
          + +

          Set

          +
            +
          • set <name> clear <readings|usedOnlineTime>
            + Clears the readings or set the usedOnlimeTime=0.
          • +
          + +

          Get

          +
            +
          • get <name> todo
            + todo.
          • +
            + +
          + +

          Attributes

          +
            +
          • attr maxOnlineMinutesPerDay <number>
            + Defines the maximum minutes this client is allowed to use the unifi-network. The client will be blocked when the reading fhem_usedOnlineTime is above this attribute.
          • +
            +
          • attr thresholdBytesPerMinute <number>
            + Clients often use the network without user interaction. Define a threshold thats allowed per Minute without counting to usedOnlineTime.
            + Default: 75000
          • +
            +
          • readingFnAttributes
          • +
          + +

          Readings

          + +
          + +
        + +=end html + +# Ende der Commandref +=cut \ No newline at end of file diff --git a/fhem/FHEM/74_UnifiSwitch.pm b/fhem/FHEM/74_UnifiSwitch.pm index 0130c0654..8535070db 100755 --- a/fhem/FHEM/74_UnifiSwitch.pm +++ b/fhem/FHEM/74_UnifiSwitch.pm @@ -21,12 +21,14 @@ # - fixed: 74_UnififSwitch: fixed possible log-error in eq in line 135 # V 0.93 # - bugfix: 74_UnififSwitch: fixed poe restart +# V 0.0.94 +# - feature: 74_UnififSwitch: supports new module UnifiClient # # TODOs: # - state des USW für weiter state-Numbers korrekt in Worte übersetzen package main; -my $version="0.93"; +my $version="0.0.94"; # Laden evtl. abhängiger Perl- bzw. FHEM-Module use strict; use warnings; @@ -149,22 +151,22 @@ sub UnifiSwitch_Set($@){ if( $setVal2 eq 'off' ) { $port_overrides->[$idx]{poe_mode} = "off"; - IOWrite($hash, "Unifi_RestJson_Send", $apRef->{device_id}, $port_overrides); + IOWrite($hash, "Unifi_DeviceRestJson_Send", $apRef->{device_id}, $port_overrides); } elsif( $setVal2 eq 'auto' || $setVal2 eq 'poe+' ) { #return "port $setVal not auto poe capable" if( @{$apRef->{port_table}}[$setVal-1]->{poe_caps} & 0x03 ) ; $port_overrides->[$idx]{poe_mode} = "auto"; - IOWrite($hash, "Unifi_RestJson_Send", $apRef->{device_id}, $port_overrides ); + IOWrite($hash, "Unifi_DeviceRestJson_Send", $apRef->{device_id}, $port_overrides ); } elsif( $setVal2 eq 'passive' ) { #return "port $setVal not passive poe capable" if( @{$apRef->{port_table}}[$setVal-1]->{poe_caps} & 0x04 ) ; $port_overrides->[$idx]{poe_mode} = "pasv24"; - IOWrite($hash, "Unifi_RestJson_Send", $apRef->{device_id}, $port_overrides); + IOWrite($hash, "Unifi_DeviceRestJson_Send", $apRef->{device_id}, $port_overrides); } elsif( $setVal2 eq 'passthrough' ) { #return "port $setVal not passthrough poe capable" if( @{$apRef->{port_table}}[$setVal-1]->{poe_caps} & 0x08 ) ; $port_overrides->[$idx]{poe_mode} = "passthrough"; - IOWrite($hash, "Unifi_RestJson_Send", $apRef->{device_id}, $port_overrides); + IOWrite($hash, "Unifi_DeviceRestJson_Send", $apRef->{device_id}, $port_overrides); } elsif( $setVal2 eq 'restart' ) {#TODO: Was wir hier gemacht? Funktioniert das noch? IOWrite($hash, "Unifi_ApJson_Send", $apRef->{device_id}, {cmd => 'power-cycle', mac => $apRef->{mac}, port_idx => $setVal});