From d3f368e7cd2fa941c00add79bdcf57a3412a6ff7 Mon Sep 17 00:00:00 2001
From: wuehler <>
Date: Sat, 9 Jan 2021 15:14:50 +0000
Subject: [PATCH] =?UTF-8?q?74=5FUnifi:=20get=20deviceData=20hinzugef=C3=BC?=
 =?UTF-8?q?gt?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

git-svn-id: https://svn.fhem.de/fhem/trunk@23500 2b470e98-0d58-463d-a4d8-8e2adae1ed80
---
 fhem/CHANGED                |  2 +
 fhem/FHEM/74_Unifi.pm       | 82 +++++++++++++++++++++++++++++++------
 fhem/FHEM/74_UnifiSwitch.pm | 42 +++++++++++--------
 3 files changed, 96 insertions(+), 30 deletions(-)

diff --git a/fhem/CHANGED b/fhem/CHANGED
index ada28a39b..9e634cc64 100644
--- a/fhem/CHANGED
+++ b/fhem/CHANGED
@@ -1,5 +1,7 @@
 # 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_Unifi: get deviceData hinzugefügt
+ - feature: 74_UnifiSwitch: new setter portProfile
  - bugfix:  60_Watches: avoid JS error which appeared in seldom cases
  - feature: 98_WeekdayTimer: Add options WDT_sendDelay and WDT_eventMap
  - change:  98_WeekdayTimer: break compability to Heating_Control
diff --git a/fhem/FHEM/74_Unifi.pm b/fhem/FHEM/74_Unifi.pm
index 9038fa422..0560d3d77 100644
--- a/fhem/FHEM/74_Unifi.pm
+++ b/fhem/FHEM/74_Unifi.pm
@@ -77,10 +77,14 @@
 #  - feature: 74_Unifi: new setter to start RF-Scan
 # V 3.5.0
 #  - feature: 74_Unifi: support UDM
+# V 3.5.1
+#  - fixed:   74_Unifi: autocreate-naming-fallback for udm fixed to mac (from ip)
+# V 3.5.2
+#  - feature: 74_Unifi: get deviceData hinzugef�gt
 
 
 package main;
-my $version="3.5.0";
+my $version="3.5.2";
 # Default f�r clientReadings 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 ;-)
@@ -588,13 +592,14 @@ sub Unifi_Get($@) {
     my $voucherNotes=join(",", keys %voucherNotesHash);
     
     my $clientNames = Unifi_ClientNames($hash);
+    my $deviceNames = Unifi_ApNames($hash);
     
-    if($getName !~ /events|clientData|unarchivedAlerts|voucherList|voucher|showAccount/) {
+    if($getName !~ /events|clientData|unarchivedAlerts|voucherList|voucher|showAccount|deviceData/) {
         return "Unknown argument $getName, choose one of "
                .((defined $hash->{events}[0] && scalar @{$hash->{events}}) ? "events:noArg " : "")
                .((defined $hash->{alerts_unarchived}[0] && scalar @{$hash->{alerts_unarchived}}) ? "unarchivedAlerts:noArg " : "")
                .(($clientNames) ? "clientData:all,$clientNames " : "")
-               ."voucherList:all,$voucherNotes voucher:$voucherNotes showAccount";
+               ."voucherList:all,$voucherNotes voucher:$voucherNotes showAccount deviceData:all,$deviceNames";
     }
     elsif ($getName eq 'unarchivedAlerts' && defined $hash->{alerts_unarchived}[0] && scalar @{$hash->{alerts_unarchived}}) {
         my $alerts = "====================================================\n";
@@ -704,6 +709,21 @@ sub Unifi_Get($@) {
 
       return "user: $user\npassword: $password";
     }
+    elsif( $getName eq 'deviceData' ) {
+      my $deviceData = '';
+      if ($getVal && $getVal ne 'all') {
+          $getVal = Unifi_ApNames($hash,$getVal,'makeID');
+      } 
+      if (!$getVal || $getVal eq 'all') {
+          return Unifi_FormatJson(encode_json($hash->{accespoints}));
+      } 
+      elsif(defined($hash->{accespoints}->{$getVal})) {
+          return Unifi_FormatJson(encode_json($hash->{accespoints}->{$getVal}));
+      } 
+      else {
+          return "$hash->{NAME}: Unknown device '$getVal' in command '$getName', choose one of: all,$deviceNames";
+      }
+    }
     return undef;
 }
 ###############################################################################
@@ -1264,9 +1284,9 @@ sub Unifi_GetSysinfo_Receive($) {
                 Log3 $name, 5, "$name ($self) - state:'$data->{meta}->{rc}'";
 				
                 for my $h (@{$data->{data}}) {
-					$hash->{UC_VERSION}=$h->{version} if defined $h->{version};
-					Log3 $name, 5, "$name ($self) - uc_version: ".$hash->{UC_VERSION};
-				}
+                  $hash->{UC_VERSION}=$h->{version} if defined $h->{version};
+                  Log3 $name, 5, "$name ($self) - uc_version: ".$hash->{UC_VERSION};
+                }
             }
             else { Unifi_ReceiveFailure($hash,$data->{meta}); }
         } else {
@@ -1436,11 +1456,12 @@ sub Unifi_GetAccesspoints_Receive($) {# TODO Umbenennen in Unifi_GetDevices_Rece
                     $hash->{accespoints}->{$h->{_id}} = $h;
                     #TODO: Switch-Modelle anders festlegen ? Oder passt usw?
                    if (defined $h->{type} && (($h->{type} eq "usw") || ($h->{type} eq "udm"))){
-                         my $usw_name="";
+                        my $usw_name="";
+                        if ($h->{type} eq "udm") { $usw_name="udm_";}
                         if (defined $h->{name}){
-                          $usw_name=makeDeviceName($h->{name});
+                          $usw_name.=makeDeviceName($h->{name});
                         }else{
-                          $usw_name=makeDeviceName($h->{ip});
+                          ($h->{type} eq "udm") ? $usw_name.=makeDeviceName($h->{mac}) : $usw_name.=makeDeviceName($h->{ip});;
                         }
                         Dispatch($hash,"UnifiSwitch_".$usw_name.encode_json($h),undef);
                     }
@@ -1703,6 +1724,7 @@ sub Unifi_SetAccesspointReadings($) {
           readingsBulkUpdate($hash,'-AP_'.$apName.'_utilizationNG',$apRef->{'ng_cu_total'}) if( defined($apRef->{'ng_cu_total'}) );
         }
         readingsBulkUpdate($hash,'-AP_'.$apName.'_locate',(!defined $apRef->{locating}) ? 'unknown' : ($apRef->{locating}) ? 'on' : 'off');
+        readingsBulkUpdate($hash,'-AP-lastUpdate', gmtime(time()));
         #my $poe_power;
         #for my $port (@{$apRef->{port_table}}) {
         #  next if( !$port->{port_poe} );
@@ -2245,11 +2267,11 @@ sub Unifi_ClientNames($@) {
             $ID = $1;
         }elsif ( $attrCustomClientNames && defined $clientRef->{$attrCustomClientNames}){
             $ID = makeReadingName($clientRef->{$attrCustomClientNames});
-		}elsif ( (defined $clientRef->{name} && $clientRef->{name} =~ /^([\w\.\-]+)$/)
+		    }elsif ( (defined $clientRef->{name} && $clientRef->{name} =~ /^([\w\.\-]+)$/)
               || (defined $clientRef->{hostname} && $clientRef->{hostname} =~ /^([\w\.\-]+)$/)
-			){
+			  ){
             $ID = $1;
-		}
+		    } 
         return $ID;
     }
     elsif (defined $ID && defined $W && $W eq 'makeID') {   # Return ID from Alias
@@ -2414,7 +2436,7 @@ sub Unifi_initCustomClientReadings($){
 
 sub Unifi_initVoucherCache($){
     my ($hash) = @_;
-    #return if ( ! defined $hash->{hotspot}->{voucherCache}->{attr_value});
+    return if ( ! defined $hash->{hotspot}->{voucherCache}->{attr_value});
     my @voucherCaches=split(/,/, $hash->{hotspot}->{voucherCache}->{attr_value});
     my @notes=();
     foreach(@voucherCaches){
@@ -2492,6 +2514,37 @@ sub Unifi_NextUpdateFn($$) {
 }
 ###############################################################################
 
+sub Unifi_FormatJson($) {
+    my ($json) = @_;
+    my $ret="";
+    my $deep="";
+    foreach my $char (split('', $json)) {
+      if ($char eq "{" || $char eq "["){
+        $ret=$ret."\n".$deep; 
+      }elsif ($char eq "}" || $char eq "]"){
+        chop($deep);
+        chop($deep);
+        $ret=$ret."\n".$deep; 
+      }
+      
+      $ret=$ret.$char;
+      
+      if ($char eq "{" || $char eq "["){
+        $deep=$deep."  " ;
+        $ret=$ret."\n".$deep; 
+      }elsif ($char eq "}" || $char eq "]"){
+        $ret=$ret."\n".$deep; 
+      }elsif ($char eq ","){
+        $ret=$ret."\n".$deep; 
+      }
+    }
+    #Zeilenvorsch�be vorne und hinten entfernen
+    $ret =~ s/^.//; 
+    chop($ret);
+    return $ret;
+}
+###############################################################################
+
 sub Unifi_ReceiveFailure($$) {
     my ($hash,$meta) = @_;
     my ($name,$self) = ($hash->{NAME},Unifi_Whowasi());
@@ -2736,6 +2789,9 @@ Or you can use the other readings or set and get features to control your unifi-
     <li><code>get &lt;name&gt; clientData &lt;all|user_id|controllerAlias|hostname|devAlias&gt;</code><br>
     Show more details about clients.</li>
     <br>
+    <li><code>get &lt;name&gt; deviceData &lt;all|AP-Name;</code><br>
+    Show data of the specified (or all) device(s) in (simple formatted) json. Can be used for userreadings (use decode_json(..) in myUtils.pm).</li>
+    <br>
     <li><code>get &lt;name&gt; events</code><br>
     Show events in specified 'eventPeriod'.</li>
     <br>
diff --git a/fhem/FHEM/74_UnifiSwitch.pm b/fhem/FHEM/74_UnifiSwitch.pm
index f514deab5..16f3465b3 100755
--- a/fhem/FHEM/74_UnifiSwitch.pm
+++ b/fhem/FHEM/74_UnifiSwitch.pm
@@ -28,15 +28,17 @@
 # V 0.0.96
 # - fixed:   74_UnifiSwitch: Log-Messages
 # V 0.0.97
-# - fixed:   74_UnifiSwitch: new readings for general_temperature, overheating, fan_level, cpu and mem
+# - feature:   74_UnifiSwitch: new readings for general_temperature, overheating, fan_level, cpu and mem
 # V 1.0.00
-# - fixed:   74_UnifiSwitch: supports UDM-Switch
+# - feature:   74_UnifiSwitch: supports UDM-Switch
+# V 1.0.01
+# - feature:   74_UnifiSwitch: new setter portProfile
 # 
 # TODOs:
 # - state des USW für weiter state-Numbers korrekt in Worte übersetzen 
 
 package main;
-my $version="1.0.00";
+my $version="1.0.01";
 # Laden evtl. abhängiger Perl- bzw. FHEM-Module
 use strict;
 use warnings;
@@ -134,9 +136,9 @@ sub UnifiSwitch_Set($@){
 	my $portProfileDisableID=AttrVal($name,"portProfileDisableID",undef);
 	my $isPortprofileID = "";
 	$isPortprofileID ="disablePort" if (defined $portProfileDisableID);
-  if($setName !~ /clear|poeMode|disablePort/) {
+  if($setName !~ /clear|poeMode|disablePort|portProfile/) {
 		return "Unknown argument $setName, choose one of "
-           ."clear:all,readings poeMode ".$isPortprofileID; #TODO: PortNamen sowie die Modes als Auswahl anhängen 
+           ."clear:all,readings poeMode ".$isPortprofileID." portProfile"; #TODO: PortNamen sowie die Modes als Auswahl anhängen 
   }elsif ($setName eq 'clear') {
 		if ($setVal eq 'readings' || $setVal eq 'all') {
 			for (keys %{$hash->{READINGS}}) {
@@ -180,6 +182,10 @@ sub UnifiSwitch_Set($@){
 		if( $setName eq 'disablePort' ) {
 			$port_overrides->[$idx]{portconf_id} = $portProfileDisableID;
 			IOWrite($hash, "Unifi_DeviceRestJson_Send", $apRef->{device_id}, $port_overrides);
+		}
+		if( $setName eq 'portProfile' ) {
+			$port_overrides->[$idx]{portconf_id} = $setVal;
+			IOWrite($hash, "Unifi_DeviceRestJson_Send", $apRef->{device_id}, $port_overrides);
 		}elsif ($setName eq 'poeMode') {
 			return "usage: $setName  <port> <off|auto|passive|passthrough|restart>" if( !$setVal );		  
 			return "port $setVal of switch '$apRef->{name}' is not poe capable" if( !@{$apRef->{port_table}}[$setVal-1]->{port_poe} );
@@ -233,9 +239,9 @@ sub UnifiSwitch_Get($@){
     elsif ($getName eq 'poeState') {
         my $poeState;
         my $apRef = $hash->{usw};
-        next if( $apRef->{type} ne 'usw' );
-        next if( !$apRef->{port_table} );
-        next if( $getVal && $getVal ne $apRef->{mac} && $getVal ne $apRef->{device_id} && $apRef->{name} !~ $getVal );
+        #next if( $apRef->{type} ne 'usw' );
+        return "no ports?!"  if( !$apRef->{port_table} );
+        return "us with mac, deviceID or deviceName" if( $getVal && $getVal ne $apRef->{mac} && $getVal ne $apRef->{device_id} && $apRef->{name} !~ $getVal );
         $poeState .= "\n" if( $poeState );
         $poeState .= sprintf( "%-20s (mac:%-17s, id:%s)\n", $apRef->{name}, $apRef->{mac}, $apRef->{device_id} );
         $poeState .= sprintf( "  %2s  %-15s", "id", "name" );
@@ -256,20 +262,20 @@ sub UnifiSwitch_Get($@){
     elsif ($getName eq 'portOverrides') {
         my $portOverrides;
         my $apRef = $hash->{usw};
-        next if( $apRef->{type} ne 'usw' );
-        next if( !$apRef->{port_table} );
-        next if( $getVal && $getVal ne $apRef->{mac} && $getVal ne $apRef->{device_id} && $apRef->{name} !~ $getVal );		
+        #next if( $apRef->{type} ne 'usw' );
+        return "no ports?!" if( !$apRef->{port_table} );
+        return "us with mac, deviceID or deviceName" if( $getVal && $getVal ne $apRef->{mac} && $getVal ne $apRef->{device_id} && $apRef->{name} !~ $getVal );		
         $portOverrides .= "\n" if( $portOverrides );
         $portOverrides .= sprintf( "%-20s (mac:%-17s, id:%s)\n", $apRef->{name}, $apRef->{mac}, $apRef->{device_id} );
         $portOverrides .= sprintf( "  %2s  %-15s", "id", "name" );
         $portOverrides .= sprintf( " %-10s %-25s", "poe_mode", "PortConfigID" );
         $portOverrides .= "\n";
         for my $port (@{$apRef->{port_overrides}}) {
-			if(defined $port->{port_idx}){
-				$portOverrides .= sprintf( "  %2i  %-15s", $port->{port_idx}, $port->{name}?$port->{name}:"" );
-				$portOverrides .= sprintf( " %-10s %-25s", $port->{poe_mode}?$port->{poe_mode}:"", $port->{portconf_id}?$port->{portconf_id}:"");
-				$portOverrides .= "\n";
-			}
+          if(defined $port->{port_idx}){
+            $portOverrides .= sprintf( "  %2i  %-15s", $port->{port_idx}, $port->{name}?$port->{name}:"" );
+            $portOverrides .= sprintf( " %-10s %-25s", $port->{poe_mode}?$port->{poe_mode}:"", $port->{portconf_id}?$port->{portconf_id}:"");
+            $portOverrides .= "\n";
+          }
         }
         
         $portOverrides = "====================================================\n". $portOverrides;
@@ -416,8 +422,10 @@ You can use the readings or set features to control your unifi-switch.
     Set PoE mode for &lt;port&gt;. </li>
     <br>
     <li><code>set &lt;name&gt; disablePort &lt;port&gt;</code><br>
-	Only visible when Attr portProfileDisableID is set.<br>
+	  Only visible when Attr portProfileDisableID is set.<br>
     Set the PortProfile from Attr portProfileDisableID for &lt;port&gt;. </li>
+    <li><code>set &lt;name&gt; portProfile &lt;port&gt;</code><br>
+	  Set the PortProfile ID. The ID can be found with get portOverrides. </li>
 </ul>
 
 <h4>Get</h4>