From 005822f5e8371d1458cb9c655ca30e3313e482d5 Mon Sep 17 00:00:00 2001 From: loredo Date: Sun, 5 Mar 2017 13:02:40 +0000 Subject: [PATCH] 10_RESIDENTS,20_ROOMMATE,20_GUEST: implement dynamic slave handling; remove experimental dependencies git-svn-id: https://svn.fhem.de/fhem/trunk@13608 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/FHEM/10_RESIDENTS.pm | 304 ++++++++++++++------------------------ fhem/FHEM/20_GUEST.pm | 86 ++--------- fhem/FHEM/20_ROOMMATE.pm | 86 ++--------- fhem/FHEM/RESIDENTStk.pm | 29 ++++ 4 files changed, 171 insertions(+), 334 deletions(-) diff --git a/fhem/FHEM/10_RESIDENTS.pm b/fhem/FHEM/10_RESIDENTS.pm index 58b024f62..fd643af29 100644 --- a/fhem/FHEM/10_RESIDENTS.pm +++ b/fhem/FHEM/10_RESIDENTS.pm @@ -32,8 +32,6 @@ use Time::Local; use Data::Dumper; require RESIDENTStk; -no if $] >= 5.017011, warnings => 'experimental'; - sub RESIDENTS_Set($@); sub RESIDENTS_Define($$); sub RESIDENTS_Notify($$); @@ -62,8 +60,6 @@ sub RESIDENTS_Define($$) { Log3 $name, 5, "RESIDENTS $name: called function RESIDENTS_Define()"; - $hash->{TYPE} = "RESIDENTS"; - # set default settings on first define if ( $init_done && !defined( $hash->{OLDDEF} ) ) { $attr{$name}{alias} = "Residents"; @@ -91,6 +87,8 @@ sub RESIDENTS_Define($$) { sub RESIDENTS_Undefine($$) { my ( $hash, $name ) = @_; + RESIDENTStk_findResidentSlaves($hash); + # delete child roommates if ( defined( $hash->{ROOMMATES} ) && $hash->{ROOMMATES} ne "" ) @@ -125,135 +123,139 @@ sub RESIDENTS_Notify($$) { my ( $hash, $dev ) = @_; my $devName = $dev->{NAME}; my $hashName = $hash->{NAME}; + return unless ( $devName ne $hashName ); # only foreign events + return + unless ( defined( $defs{$devName} ) + && defined( $defs{$devName}{TYPE} ) + && $defs{$devName}{TYPE} =~ /^(ROOMMATE|GUEST|DUMMY)$/ ); - # process child notifies - if ( $devName ne $hashName ) { - my @registeredRoommates = - split( /,/, $hash->{ROOMMATES} ) - if ( defined( $hash->{ROOMMATES} ) - && $hash->{ROOMMATES} ne "" ); + RESIDENTStk_findResidentSlaves($hash); - my @registeredGuests = - split( /,/, $hash->{GUESTS} ) - if ( defined( $hash->{GUESTS} ) - && $hash->{GUESTS} ne "" ); + my @registeredRoommates = + split( /,/, $hash->{ROOMMATES} ) + if ( defined( $hash->{ROOMMATES} ) + && $hash->{ROOMMATES} ne "" ); - my @registeredWakeupdevs = - split( /,/, $attr{$hashName}{rgr_wakeupDevice} ) - if ( defined( $attr{$hashName}{rgr_wakeupDevice} ) - && $attr{$hashName}{rgr_wakeupDevice} ne "" ); + my @registeredGuests = + split( /,/, $hash->{GUESTS} ) + if ( defined( $hash->{GUESTS} ) + && $hash->{GUESTS} ne "" ); - # process only registered ROOMMATE or GUEST devices - if ( ( @registeredRoommates && $devName ~~ @registeredRoommates ) - || ( @registeredGuests && $devName ~~ @registeredGuests ) ) - { + my @registeredWakeupdevs = + split( /,/, $attr{$hashName}{rgr_wakeupDevice} ) + if ( defined( $attr{$hashName}{rgr_wakeupDevice} ) + && $attr{$hashName}{rgr_wakeupDevice} ne "" ); - return - if ( !$dev->{CHANGED} ); # Some previous notify deleted the array. + # process only registered ROOMMATE or GUEST devices + if ( ( @registeredRoommates && grep { /^$devName$/ } @registeredRoommates ) + || ( @registeredGuests && grep { /^$devName$/ } @registeredGuests ) ) + { - readingsBeginUpdate($hash); + return + if ( !$dev->{CHANGED} ); # Some previous notify deleted the array. - foreach my $change ( @{ $dev->{CHANGED} } ) { + readingsBeginUpdate($hash); - Log3 $hash, 5, - "RESIDENTS " . $hashName . ": processing change $change"; + foreach my $change ( @{ $dev->{CHANGED} } ) { - # state changed - if ( $change !~ /:/ - || $change =~ /wayhome:/ - || $change =~ /wakeup:/ ) - { - Log3 $hash, 4, - "RESIDENTS " - . $hashName . ": " - . $devName - . ": notify about change to $change"; + Log3 $hash, 5, + "RESIDENTS " . $hashName . ": processing change $change"; - RESIDENTS_UpdateReadings($hash); - } + # state changed + if ( $change !~ /:/ + || $change =~ /wayhome:/ + || $change =~ /wakeup:/ ) + { + Log3 $hash, 4, + "RESIDENTS " + . $hashName . ": " + . $devName + . ": notify about change to $change"; - # activity - if ( $change !~ /:/ ) { + RESIDENTS_UpdateReadings($hash); + } - # get user realname - my $realnamesrc; - if ( $dev->{TYPE} eq "GUEST" ) { - $realnamesrc = ( - defined( $attr{$devName}{rg_realname} ) - && $attr{$devName}{rg_realname} ne "" - ? $attr{$devName}{rg_realname} - : "alias" - ); - } - else { - $realnamesrc = ( - defined( $attr{$devName}{rr_realname} ) - && $attr{$devName}{rr_realname} ne "" - ? $attr{$devName}{rr_realname} - : "group" - ); - } + # activity + if ( $change !~ /:/ ) { - my $realname = ( - defined( $attr{$devName}{$realnamesrc} ) - && $attr{$devName}{$realnamesrc} ne "" - ? $attr{$devName}{$realnamesrc} - : $devName + # get user realname + my $realnamesrc; + if ( $dev->{TYPE} eq "GUEST" ) { + $realnamesrc = ( + defined( $attr{$devName}{rg_realname} ) + && $attr{$devName}{rg_realname} ne "" + ? $attr{$devName}{rg_realname} + : "alias" ); - - # update statistics - readingsBulkUpdate( $hash, "lastActivity", - ReadingsVal( $devName, "state", $change ) ); - readingsBulkUpdate( $hash, "lastActivityBy", $realname ); - readingsBulkUpdate( $hash, "lastActivityByDev", $devName ); - } + else { + $realnamesrc = ( + defined( $attr{$devName}{rr_realname} ) + && $attr{$devName}{rr_realname} ne "" + ? $attr{$devName}{rr_realname} + : "group" + ); + } + + my $realname = ( + defined( $attr{$devName}{$realnamesrc} ) + && $attr{$devName}{$realnamesrc} ne "" + ? $attr{$devName}{$realnamesrc} + : $devName + ); + + # update statistics + readingsBulkUpdate( $hash, "lastActivity", + ReadingsVal( $devName, "state", $change ) ); + readingsBulkUpdate( $hash, "lastActivityBy", $realname ); + readingsBulkUpdate( $hash, "lastActivityByDev", $devName ); } - readingsEndUpdate( $hash, 1 ); + } + + readingsEndUpdate( $hash, 1 ); + + return; + } + + # if we have registered wakeup devices + if (@registeredWakeupdevs) { + + # if this is a notification of a registered wakeup device + if ( grep { /^$devName$/ } @registeredWakeupdevs ) { + + # Some previous notify deleted the array. + return + if ( !$dev->{CHANGED} ); + + foreach my $change ( @{ $dev->{CHANGED} } ) { + RESIDENTStk_wakeupSet( $devName, $change ); + } return; } - # if we have registered wakeup devices - if (@registeredWakeupdevs) { + # process sub-child notifies: *_wakeupDevice + foreach my $wakeupDev (@registeredWakeupdevs) { - # if this is a notification of a registered wakeup device - if ( $devName ~~ @registeredWakeupdevs ) { + # if this is a notification of a registered sub dummy device + # of one of our wakeup devices + if ( defined( $attr{$wakeupDev}{wakeupResetSwitcher} ) + && $attr{$wakeupDev}{wakeupResetSwitcher} eq $devName + && $defs{$devName}{TYPE} eq "dummy" ) + { # Some previous notify deleted the array. return if ( !$dev->{CHANGED} ); foreach my $change ( @{ $dev->{CHANGED} } ) { - RESIDENTStk_wakeupSet( $devName, $change ); + RESIDENTStk_wakeupSet( $wakeupDev, $change ) + if ( $change ne "off" ); } - return; - } - - # process sub-child notifies: *_wakeupDevice - foreach my $wakeupDev (@registeredWakeupdevs) { - - # if this is a notification of a registered sub dummy device - # of one of our wakeup devices - if ( defined( $attr{$wakeupDev}{wakeupResetSwitcher} ) - && $attr{$wakeupDev}{wakeupResetSwitcher} eq $devName - && $defs{$devName}{TYPE} eq "dummy" ) - { - - # Some previous notify deleted the array. - return - if ( !$dev->{CHANGED} ); - - foreach my $change ( @{ $dev->{CHANGED} } ) { - RESIDENTStk_wakeupSet( $wakeupDev, $change ) - if ( $change ne "off" ); - } - - last; - } + last; } } } @@ -264,15 +266,17 @@ sub RESIDENTS_Notify($$) { ################################### sub RESIDENTS_Set($@) { my ( $hash, @a ) = @_; - my $name = $hash->{NAME}; - my $state = ReadingsVal( $name, "state", "initialized" ); - my $roommates = ( $hash->{ROOMMATES} ? $hash->{ROOMMATES} : "" ); - my $guests = ( $hash->{GUESTS} ? $hash->{GUESTS} : "" ); + my $name = $hash->{NAME}; + my $state = ReadingsVal( $name, "state", "initialized" ); Log3 $name, 5, "RESIDENTS $name: called function RESIDENTS_Set()"; return "No Argument given" if ( !defined( $a[1] ) ); + RESIDENTStk_findResidentSlaves($hash); + my $roommates = ( $hash->{ROOMMATES} ? $hash->{ROOMMATES} : "" ); + my $guests = ( $hash->{GUESTS} ? $hash->{GUESTS} : "" ); + # depending on current FHEMWEB instance's allowedCommands, # restrict set commands if there is "set-user" in it my $adminMode = 1; @@ -475,94 +479,6 @@ sub RESIDENTS_Set($@) { } } - # register - elsif ( $a[1] eq "register" ) { - if ( defined( $a[2] ) && $a[2] ne "" ) { - return "No such device " . $a[2] - if ( !defined( $defs{ $a[2] } ) ); - - # ROOMMATE - if ( $defs{ $a[2] }{TYPE} eq "ROOMMATE" ) { - Log3 $name, 4, "RESIDENTS $name: " . $a[2] . " registered"; - - # update readings - $roommates .= ( $roommates eq "" ? $a[2] : "," . $a[2] ) - if ( $roommates !~ /$a[2]/ ); - - $hash->{ROOMMATES} = $roommates; - } - - # GUEST - elsif ( $defs{ $a[2] }{TYPE} eq "GUEST" ) { - Log3 $name, 4, "RESIDENTS $name: " . $a[2] . " registered"; - - # update readings - $guests .= ( $guests eq "" ? $a[2] : "," . $a[2] ) - if ( $guests !~ /$a[2]/ ); - - $hash->{GUESTS} = $guests; - } - - # unsupported - else { - return "Device type is not supported."; - } - - } - else { - return "No Argument given, choose one of ROOMMATE GUEST "; - } - } - - # unregister - elsif ( $a[1] eq "unregister" ) { - if ( defined( $a[2] ) && $a[2] ne "" ) { - return "No such device " . $a[2] - if ( !defined( $defs{ $a[2] } ) ); - - # ROOMMATE - if ( $defs{ $a[2] }{TYPE} eq "ROOMMATE" ) { - Log3 $name, 4, "RESIDENTS $name: " . $a[2] . " unregistered"; - - # update readings - my $replace = "," . $a[2]; - $roommates =~ s/$replace//g; - $replace = $a[2] . ","; - $roommates =~ s/^$replace//g; - $roommates =~ s/^$a[2]//g; - - $hash->{ROOMMATES} = $roommates; - } - - # GUEST - elsif ( $defs{ $a[2] }{TYPE} eq "GUEST" ) { - Log3 $name, 4, "RESIDENTS $name: " . $a[2] . " unregistered"; - - # update readings - my $replace = "," . $a[2]; - $guests =~ s/$replace//g; - $replace = $a[2] . ","; - $guests =~ s/^$replace//g; - $guests =~ s/^$a[2]//g; - - $hash->{GUESTS} = $guests; - } - - # unsupported - else { - return "Device type is not supported."; - } - - } - else { - return "No Argument given, choose one of ROOMMATE GUEST "; - } - - readingsBeginUpdate($hash); - RESIDENTS_UpdateReadings($hash); - readingsEndUpdate( $hash, 1 ); - } - # create elsif ( $a[1] eq "create" ) { if ( defined( $a[2] ) && $a[2] eq "wakeuptimer" ) { diff --git a/fhem/FHEM/20_GUEST.pm b/fhem/FHEM/20_GUEST.pm index 1f223a593..b99842021 100644 --- a/fhem/FHEM/20_GUEST.pm +++ b/fhem/FHEM/20_GUEST.pm @@ -32,8 +32,6 @@ use Time::Local; use Data::Dumper; require RESIDENTStk; -no if $] >= 5.017011, warnings => 'experimental'; - sub GUEST_Set($@); sub GUEST_Define($$); sub GUEST_Notify($$); @@ -69,57 +67,14 @@ sub GUEST_Define($$) { return $msg; } - $hash->{TYPE} = "GUEST"; - - my $parents = ( defined( $a[2] ) ? $a[2] : "" ); - - # unregister at parent objects if we get modified - my @registeredResidentgroups; - my $modified = 0; - if ( defined( $hash->{RESIDENTGROUPS} ) && $hash->{RESIDENTGROUPS} ne "" ) { - $modified = 1; - @registeredResidentgroups = - split( /,/, $hash->{RESIDENTGROUPS} ); - - # unregister at parent objects - foreach my $parent (@registeredResidentgroups) { - if ( defined( $defs{$parent} ) - && $defs{$parent}{TYPE} eq "RESIDENTS" ) - { - fhem("set $parent unregister $name"); - Log3 $name, 4, - "GUEST $name: Unregistered at RESIDENTS device $parent"; - } + $hash->{RESIDENTGROUPS} = defined( $a[2] ) ? $a[2] : ""; + if ( defined( $hash->{RESIDENTGROUPS} ) ) { + foreach ( split( /,/, $hash->{RESIDENTGROUPS} ) ) { + RESIDENTStk_findResidentSlaves( $defs{$_} ) + if ( defined( $defs{$_} ) ); } } - # register at parent objects - $hash->{RESIDENTGROUPS} = ""; - if ( $parents ne "" ) { - @registeredResidentgroups = split( /,/, $parents ); - foreach my $parent (@registeredResidentgroups) { - if ( !defined( $defs{$parent} ) ) { - Log3 $name, 3, -"GUEST $name: Unable to register at RESIDENTS device $parent (not existing)"; - next; - } - - if ( $defs{$parent}{TYPE} ne "RESIDENTS" ) { - Log3 $name, 3, -"GUEST $name: Device $parent is not a RESIDENTS device (wrong type)"; - next; - } - - fhem("set $parent register $name"); - $hash->{RESIDENTGROUPS} .= $parent . ","; - Log3 $name, 4, - "GUEST $name: Registered at RESIDENTS device $parent"; - } - } - else { - $modified = 0; - } - # set reverse pointer $modules{GUEST}{defptr}{$name} = \$hash; @@ -139,15 +94,15 @@ sub GUEST_Define($$) { $attr{$name}{sortby} = "1"; $attr{$name}{webCmd} = "state"; - $attr{$name}{room} = $attr{ $registeredResidentgroups[0] }{room} - if ( @registeredResidentgroups - && exists( $attr{ $registeredResidentgroups[0] }{room} ) ); + my $firstRgr = $hash->{RESIDENTGROUPS}; + $firstRgr =~ s/,[\s\S]*$//gmi; + $attr{$name}{room} = $attr{$firstRgr}{room} + if ( exists( $attr{$firstRgr}{room} ) ); } # trigger for modified objects - unless ( $modified == 0 ) { - readingsBulkUpdate( $hash, "state", ReadingsVal( $name, "state", "" ) ); - } + readingsBulkUpdateIfChanged( $hash, "state", + ReadingsVal( $name, "state", "" ) ); readingsEndUpdate( $hash, 1 ); @@ -176,18 +131,9 @@ sub GUEST_Undefine($$) { RESIDENTStk_RemoveInternalTimer( "DurationTimer", $hash ); if ( defined( $hash->{RESIDENTGROUPS} ) ) { - my @registeredResidentgroups = - split( /,/, $hash->{RESIDENTGROUPS} ); - - # unregister at parent objects - foreach my $parent (@registeredResidentgroups) { - if ( defined( $defs{$parent} ) - && $defs{$parent}{TYPE} eq "RESIDENTS" ) - { - fhem("set $parent unregister $name"); - Log3 $name, 4, - "GUEST $name: Unregistered at RESIDENTS device $parent"; - } + foreach ( split( /,/, $hash->{RESIDENTGROUPS} ) ) { + RESIDENTStk_findResidentSlaves( $defs{$_} ) + if ( defined( $defs{$_} ) ); } } @@ -251,7 +197,7 @@ sub GUEST_Notify($$) { if (@registeredWakeupdevs) { # if this is a notification of a registered wakeup device - if ( $devName ~~ @registeredWakeupdevs ) { + if ( grep { /^$devName$/ } @registeredWakeupdevs ) { # Some previous notify deleted the array. return @@ -289,7 +235,7 @@ sub GUEST_Notify($$) { } # process PRESENCE - if ( @presenceDevices && $devName ~~ @presenceDevices ) { + if ( @presenceDevices && grep { /^$devName$/ } @presenceDevices ) { my $counter = { absent => 0, diff --git a/fhem/FHEM/20_ROOMMATE.pm b/fhem/FHEM/20_ROOMMATE.pm index fe939719c..ce59266d3 100644 --- a/fhem/FHEM/20_ROOMMATE.pm +++ b/fhem/FHEM/20_ROOMMATE.pm @@ -32,8 +32,6 @@ use Time::Local; use Data::Dumper; require RESIDENTStk; -no if $] >= 5.017011, warnings => 'experimental'; - sub ROOMMATE_Set($@); sub ROOMMATE_Define($$); sub ROOMMATE_Notify($$); @@ -70,57 +68,14 @@ sub ROOMMATE_Define($$) { return $msg; } - $hash->{TYPE} = "ROOMMATE"; - - my $parents = ( defined( $a[2] ) ? $a[2] : "" ); - - # unregister at parent objects if we get modified - my @registeredResidentgroups; - my $modified = 0; - if ( defined( $hash->{RESIDENTGROUPS} ) && $hash->{RESIDENTGROUPS} ne "" ) { - $modified = 1; - @registeredResidentgroups = - split( /,/, $hash->{RESIDENTGROUPS} ); - - # unregister at parent objects - foreach my $parent (@registeredResidentgroups) { - if ( defined( $defs{$parent} ) - && $defs{$parent}{TYPE} eq "RESIDENTS" ) - { - fhem("set $parent unregister $name"); - Log3 $name, 4, - "ROOMMATE $name: Unregistered at RESIDENTS device $parent"; - } + $hash->{RESIDENTGROUPS} = defined( $a[2] ) ? $a[2] : ""; + if ( defined( $hash->{RESIDENTGROUPS} ) ) { + foreach ( split( /,/, $hash->{RESIDENTGROUPS} ) ) { + RESIDENTStk_findResidentSlaves( $defs{$_} ) + if ( defined( $defs{$_} ) ); } } - # register at parent objects - $hash->{RESIDENTGROUPS} = ""; - if ( $parents ne "" ) { - @registeredResidentgroups = split( /,/, $parents ); - foreach my $parent (@registeredResidentgroups) { - if ( !defined( $defs{$parent} ) ) { - Log3 $name, 3, -"ROOMMATE $name: Unable to register at RESIDENTS device $parent (not existing)"; - next; - } - - if ( $defs{$parent}{TYPE} ne "RESIDENTS" ) { - Log3 $name, 3, -"ROOMMATE $name: Device $parent is not a RESIDENTS device (wrong type)"; - next; - } - - fhem("set $parent register $name"); - $hash->{RESIDENTGROUPS} .= $parent . ","; - Log3 $name, 4, - "ROOMMATE $name: Registered at RESIDENTS device $parent"; - } - } - else { - $modified = 0; - } - # set reverse pointer $modules{ROOMMATE}{defptr}{$name} = \$hash; @@ -140,15 +95,15 @@ sub ROOMMATE_Define($$) { $attr{$name}{sortby} = "1"; $attr{$name}{webCmd} = "state"; - $attr{$name}{room} = $attr{ $registeredResidentgroups[0] }{room} - if ( @registeredResidentgroups - && exists( $attr{ $registeredResidentgroups[0] }{room} ) ); + my $firstRgr = $hash->{RESIDENTGROUPS}; + $firstRgr =~ s/,[\s\S]*$//gmi; + $attr{$name}{room} = $attr{$firstRgr}{room} + if ( exists( $attr{$firstRgr}{room} ) ); } # trigger for modified objects - unless ( $modified == 0 ) { - readingsBulkUpdate( $hash, "state", ReadingsVal( $name, "state", "" ) ); - } + readingsBulkUpdateIfChanged( $hash, "state", + ReadingsVal( $name, "state", "" ) ); readingsEndUpdate( $hash, 1 ); @@ -181,18 +136,9 @@ sub ROOMMATE_Undefine($$) { RESIDENTStk_RemoveInternalTimer( "DurationTimer", $hash ); if ( defined( $hash->{RESIDENTGROUPS} ) ) { - my @registeredResidentgroups = - split( /,/, $hash->{RESIDENTGROUPS} ); - - # unregister at parent objects - foreach my $parent (@registeredResidentgroups) { - if ( defined( $defs{$parent} ) - && $defs{$parent}{TYPE} eq "RESIDENTS" ) - { - fhem("set $parent unregister $name"); - Log3 $name, 4, - "ROOMMATE $name: Unregistered at RESIDENTS device $parent"; - } + foreach ( split( /,/, $hash->{RESIDENTGROUPS} ) ) { + RESIDENTStk_findResidentSlaves( $defs{$_} ) + if ( defined( $defs{$_} ) ); } } @@ -256,7 +202,7 @@ sub ROOMMATE_Notify($$) { if (@registeredWakeupdevs) { # if this is a notification of a registered wakeup device - if ( $devName ~~ @registeredWakeupdevs ) { + if ( grep { /^$devName$/ } @registeredWakeupdevs ) { # Some previous notify deleted the array. return @@ -294,7 +240,7 @@ sub ROOMMATE_Notify($$) { } # process PRESENCE - if ( @presenceDevices && $devName ~~ @presenceDevices ) { + if ( @presenceDevices && grep { /^$devName$/ } @presenceDevices ) { my $counter = { absent => 0, diff --git a/fhem/FHEM/RESIDENTStk.pm b/fhem/FHEM/RESIDENTStk.pm index 4890b49a0..657a3affd 100644 --- a/fhem/FHEM/RESIDENTStk.pm +++ b/fhem/FHEM/RESIDENTStk.pm @@ -1715,4 +1715,33 @@ sub RESIDENTStk_RemoveInternalTimer($$) { } } +sub RESIDENTStk_findResidentSlaves($) { + my ($hash) = @_; + return unless ( ref($hash) eq "HASH" && defined( $hash->{NAME} ) ); + + delete $hash->{ROOMMATES}; + foreach ( devspec2array("TYPE=ROOMMATE") ) { + next + unless ( + defined( $defs{$_}{RESIDENTGROUPS} ) + && grep { /^$hash->{NAME}$/ } + split( /,/, $defs{$_}{RESIDENTGROUPS} ) + ); + $hash->{ROOMMATES} .= "," if ( $hash->{ROOMMATES} ); + $hash->{ROOMMATES} .= $_; + } + + delete $hash->{GUESTS}; + foreach ( devspec2array("TYPE=GUEST") ) { + next + unless ( + defined( $defs{$_}{RESIDENTGROUPS} ) + && grep { /^$hash->{NAME}$/ } + split( /,/, $defs{$_}{RESIDENTGROUPS} ) + ); + $hash->{GUESTS} .= "," if ( $hash->{GUESTS} ); + $hash->{GUESTS} .= $_; + } +} + 1;