Hallo Zusammen,
ich hatte die Herausforderung einen Raum (room) automatisch mit Geräten zu füllen und dabei die bestehenden Räume nicht zu verändern. Dazu müssen die vorhandenen room Attribute ergänzt werden. In meinem Fall möchte ich alle Geräte mit dem Attribut alexaName in einem Raum zusammenfassen, ohne die bestehenden Räume zu überschreiben. Dabei ist das angefügte myUtils entstanden, welches ich in einem at nutze. Die alte attr Syntax bleibt ansonsten erhalten.
attr <devspec> room {+|-}<roomname>
##############################################
# $Id: 99_myUtils.pm 0001 2026-02-22 09:45:00Z Gemini $
package main;
use strict;
use warnings;
use POSIX;
# Variable für die Sicherung der Originalfunktion
use vars qw($oldCommandAttr);
sub myUtils_Initialize($$) {
my ($hash) = @_;
}
{
no warnings 'redefine';
# Sicherung der originalen Funktion (einmalig beim Laden)
if (!defined($oldCommandAttr)) {
$oldCommandAttr = \&main::CommandAttr;
}
# Überlagerung der Funktion CommandAttr
*main::CommandAttr = sub {
my ($cl, $param) = @_;
# Zerlegen des Befehls (Device, Attribut, Neuer Wert)
my ($dev, $attrName, $newVal) = split("[ \t]+", $param, 3);
# Wir springen sofort raus, wenn:
# - kein Attributname vorhanden ist
# - es nicht um 'room' geht
# - eine Option wie -a oder -silent verwendet wird (erkannt am führenden - in $dev)
if (!$attrName || $attrName ne "room" || ($dev && $dev =~ /^-/)) {
return $oldCommandAttr->($cl, $param);
}
# Prüfung: Wenn das erste Zeichen + oder - ist
if ($newVal && $newVal =~ /^[\+\-]/) {
# Prüfung: Abbruch, wenn kein Einzel-Device ist (DevSpec Schutz)
if ($dev && !defined($defs{$dev})) {
return "Fehler: Die Nutzung von +/- ist bei DevSpecs (Filtern) nicht erlaubt!";
}
my $modifier = substr($newVal, 0, 1);
# Prüfung: Abbruch bei Komma (mehrere Räume nicht erlaubt)
if (substr($newVal, 1) =~ /,/) {
return "Fehler: Mehrere Räume (Komma) sind bei +/- nicht erlaubt!";
}
# Prüfung: Abbruch wenn nach +/- nichts kommt
if (length(substr($newVal, 1)) == 0) {
return "Fehler: Kein Raumname nach $modifier angegeben!";
}
# Aktuelle Räume des Devices laden
my $currentRooms = $attr{$dev}{room} // "";
my @roomList = split(',', $currentRooms);
# Zielraum isolieren und Leerzeichen säubern
my $targetRoom = substr($newVal, 1);
$targetRoom =~ s/^\s+|\s+$//g;
if ($modifier eq "+") {
# Hinzufügen, falls noch nicht vorhanden
push(@roomList, $targetRoom) unless grep { $_ eq $targetRoom } @roomList;
}
elsif ($modifier eq "-") {
# Entfernen des spezifischen Raums
@roomList = grep { $_ ne $targetRoom } @roomList;
}
# Bereinigung: Leere Einträge entfernen
@roomList = grep { length($_) > 0 } @roomList;
# Sortierung: Alphabetisch (Standard / Case-Sensitive)
@roomList = sort { $a cmp $b } @roomList;
# Wenn kein Raum mehr übrig ist: Attribut komplett löschen
if (scalar @roomList == 0) {
Log 3, "CustomAttr: Letzter Raum entfernt, lösche Attribut room für $dev";
return main::CommandDeleteAttr($cl, "$dev room");
}
# Parameter für die Original-Funktion mit der neuen Liste neu aufbauen
my $finalRooms = join(',', @roomList);
$param = "$dev room $finalRooms";
}
# Aufruf der Originallogik
return $oldCommandAttr->($cl, $param);
};
}
1;
Alternativ koennte man auch attr -a verwenden: https://fhem.de/commandref_modular.html#attr
Fuer die Ueberlagerung von Befehlen gibt es auch eine Alternative: cmdalias: https://fhem.de/commandref_modular.html#cmdalias
Update mit devspec-Unterstützung:
##############################################
# $Id: 99_myAttrRoom.pm 0001 2026-02-22 12:30:00Z Gemini $
# FORK: Unterstützung für room +/- mit DevSpecs
##############################################
package main;
use strict;
use warnings;
use POSIX;
# Variable für die Sicherung der Originalfunktion
use vars qw($oldCommandAttr);
sub myAttrRoom_Initialize($$) {
my ($hash) = @_;
Log3(undef, 1, "myAttrRoom: custom CommandAttr hook loaded - support for room +/- enabled");
}
{
no warnings 'redefine';
# Sicherung der originalen Funktion (einmalig beim Laden)
if (!defined($oldCommandAttr)) {
$oldCommandAttr = \&main::CommandAttr;
}
# Überlagerung der Funktion CommandAttr
*main::CommandAttr = sub {
my ($cl, $param) = @_;
# Zerlegen des Befehls (Device, Attribut, Neuer Wert)
my ($dev, $attrName, $newVal) = split("[ \t]+", $param, 3);
# Wir springen sofort raus, wenn:
# - kein Attributname vorhanden ist
# - es nicht um 'room' geht
# - eine Option wie -a oder -silent verwendet wird (erkannt am führenden - in $dev)
if (!$attrName || $attrName ne "room" || ($dev && $dev =~ /^-/)) {
return $oldCommandAttr->($cl, $param);
}
# Prüfung: Wenn das erste Zeichen + oder - ist
if ($newVal && $newVal =~ /^[\+\-]/) {
# Prüfung: Abbruch bei Komma (mehrere Räume nicht erlaubt)
if (substr($newVal, 1) =~ /,/) {
return "Fehler: Mehrere Räume (Komma) sind bei +/- nicht erlaubt!";
}
# Fehler: Kein Name
my $modifier = substr($newVal, 0, 1);
# Zielraum isolieren und Leerzeichen säubern
my $targetRoom = substr($newVal, 1);
$targetRoom =~ s/^\s+|\s+$//g;
if (length($targetRoom) == 0) {
return "Fehler: Kein Raumname nach $modifier angegeben!";
}
# Fehler: Ziel nicht auffindbar
my @devices = devspec2array($dev);
if (scalar @devices == 0) {
return "Fehler: Device/DevSpec $dev nicht gefunden!";
}
# --- VERARBEITUNG ---
foreach my $singleDev (@devices) {
# Aktuelle Räume des Devices laden
my $currentRooms = $attr{$singleDev}{room} // "";
my @roomList = split(',', $currentRooms);
if ($modifier eq "+") {
push(@roomList, $targetRoom) unless grep { $_ eq $targetRoom } @roomList;
}
elsif ($modifier eq "-") {
@roomList = grep { $_ ne $targetRoom } @roomList;
}
# Bereinigung: Leere Einträge entfernen
@roomList = grep { length($_) > 0 } @roomList;
# Sortierung: Alphabetisch (Standard / Case-Sensitive)
@roomList = sort { $a cmp $b } @roomList;
# Wenn kein Raum mehr übrig ist: Attribut komplett löschen
if (scalar @roomList == 0) {
Log 3, "myAttrRoom: Letzter Raum entfernt, lösche Attribut room für $singleDev";
main::CommandDeleteAttr($cl, "$singleDev room");
} else {
my $finalRooms = join(',', @roomList);
Log 4, "myAttrRoom: $singleDev -> neue Räume: $finalRooms"; # Detail-Log
$oldCommandAttr->($cl, "$singleDev room $finalRooms");
}
}
return undef; # Erfolg
}
# Aufruf der Originallogik
return $oldCommandAttr->($cl, $param);
};
}
1;
Zitat von: rudolfkoenig am 22 Februar 2026, 11:45:45Alternativ koennte man auch attr -a verwenden: https://fhem.de/commandref_modular.html#attr
Fuer die Ueberlagerung von Befehlen gibt es auch eine Alternative: cmdalias: https://fhem.de/commandref_modular.html#cmdalias
Danke für den Hinweis. Mit attr -a konnte ich das nicht so "schön" umsetzen. Überlagerung muss ich mir mal ansehen.
Es gäbe da auch noch den show-Befehl, mit dem man dynamisch Räume erstellen bzw. anzeigen kann.