Das habe ich mich (und die KI) auch gefragt. Zusammen haben wir herausgefunden, dass es geht 
<?php
declare(strict_types=1);
// Installierte Libraries + Quelle (Store/ModuleControl) + Instanz-IDs (pro Library)
$storeGuids = getStoreLibraryGuidSet(); // library-guid(lower) => true
$instancesByLibrary = buildInstanceIdsByLibraryReport(); // library-guid(lower) => [instanceId, ...]
$rows = [];
foreach (IPS_GetLibraryList() as $guid) {
$lib = IPS_GetLibrary($guid);
$libGuid = (string)($lib['GUID'] ?? $guid);
$libKey = strtolower($libGuid);
$rows[] = [
'Name' => $lib['Name'] ?? '(ohne Name)',
'Version' => (string)($lib['Version'] ?? '(ohne Version)'),
'Id' => $libGuid,
'Quelle' => isset($storeGuids[$libKey]) ? 'Store' : 'ModuleControl',
'Instanzen' => formatInstanceSummary($instancesByLibrary[$libKey] ?? [], 3),
];
}
usort($rows, static fn($a, $b) => strcmp($a['Name'], $b['Name']));
echo "Installierte Libraries (Store + ModuleControl):\n\n";
printTable(
[
'Name' => 'Name',
'Version' => 'Version',
'Id' => 'Library-GUID',
'Quelle' => 'Quelle',
'Instanzen' => 'Instanzen (Anz: IDs)',
],
$rows
);
/**
* Report-fokussiert:
* LibraryGUID -> [InstanceIDs...]
*
* Optional:
* - $includeBuiltIn: Built-In Library mit aufnehmen
* - $onlyWithInstances: Libraries ohne Instanzen weglassen
*/
function buildInstanceIdsByLibraryReport(bool $includeBuiltIn = true, bool $onlyWithInstances = false): array
{
$builtInLibraryGuid = '{0945206A-47AA-4FDD-9093-99051E410E82}'; // Built-In
$map = [];
foreach (IPS_GetLibraryList() as $libraryGuid) {
$libraryGuid = (string)$libraryGuid;
if (!$includeBuiltIn && strcasecmp($libraryGuid, $builtInLibraryGuid) === 0) {
continue;
}
$instanceIds = [];
foreach (IPS_GetLibraryModules($libraryGuid) as $moduleGuid) {
$moduleGuid = (string)$moduleGuid;
$ids = IPS_GetInstanceListByModuleID($moduleGuid);
if ($ids === []) {
continue;
}
foreach ($ids as $id) {
$instanceIds[] = (int)$id;
}
}
$instanceIds = array_values(array_unique($instanceIds));
sort($instanceIds, SORT_NUMERIC);
if ($onlyWithInstances && $instanceIds === []) {
continue;
}
// Key normalisieren (für konsistente Keys)
$map[strtolower($libraryGuid)] = $instanceIds;
}
ksort($map);
return $map;
}
/** Ausgabeformat: "Anzahl: id1, id2, id3, …" */
function formatInstanceSummary(array $ids, int $limit = 3): string
{
$count = count($ids);
if ($count === 0) {
return '0';
}
$preview = array_slice($ids, 0, max(0, $limit));
$suffix = $count > count($preview) ? ', …' : '';
return $count . ': ' . implode(', ', array_map('strval', $preview)) . $suffix;
}
/** Store-Erkennung über modules/.store/<bundle>/library.json */
function getStoreLibraryGuidSet(): array
{
$storeDir = IPS_GetKernelDir() . 'modules/.store';
if (!is_dir($storeDir)) {
return [];
}
$set = [];
foreach (glob($storeDir . '/*/library.json') ? : [] as $file) {
$json = json_decode((string)file_get_contents($file), true);
$id = $json['id'] ?? null;
if (is_string($id) && $id !== '') {
$set[strtolower($id)] = true;
}
}
return $set;
}
function printTable(array $headers, array $rows): void
{
$keys = array_keys($headers);
$strWidth = static function (string $s): int {
// sichtbare Breite (Terminal/Monospace-Logik), nicht Bytes
return mb_strwidth($s, 'UTF-8');
};
$padDisplay = static function (string $s, int $width): string {
$w = mb_strwidth($s, 'UTF-8');
if ($w >= $width) {
return $s;
}
return $s . str_repeat(' ', $width - $w);
};
$width = array_fill_keys($keys, 0);
foreach ($headers as $k => $label) {
$width[$k] = $strWidth((string)$label);
}
foreach ($rows as $row) {
foreach ($keys as $k) {
$width[$k] = max($width[$k], $strWidth((string)($row[$k] ?? '')));
}
}
// Header
$lineParts = [];
foreach ($keys as $k) {
$lineParts[] = $padDisplay((string)$headers[$k], $width[$k]);
}
echo implode(' ', $lineParts) . "\n";
// Separator
$sepParts = [];
foreach ($keys as $k) {
$sepParts[] = str_repeat('-', $width[$k]);
}
echo implode(' ', $sepParts) . "\n";
// Rows
foreach ($rows as $row) {
$values = [];
foreach ($keys as $k) {
$values[] = $padDisplay((string)($row[$k] ?? ''), $width[$k]);
}
echo implode(' ', $values) . "\n";
}
}
Die Ausgabe ist jetzt:
Installierte Libraries (Store + ModuleControl):
Name Version Library-GUID Quelle Instanzen (Anz: IDs)
------------------------------ ------- -------------------------------------- ------------- ------------------------------
Active List 1.2 {658E494A-2ACE-B8FE-3943-40B6638217F3} Store 3: 38739, 53363, 54400
Alexa 1.5 {8712139D-34F2-4C61-AB21-DB4DA3DB55A1} Store 1: 10110
Almanac 5.7 {5C868298-0E0E-460C-B2EE-171513972D42} Store 1: 18241
Ambientika 1.0 {E9850CE3-9FE3-15AB-0FAC-C9D0F7A7AE72} ModuleControl 10: 10426, 10547, 12275, …
Astyc84Misc 2.0 {C9166CCE-244A-4024-B819-DBD9450A1419} ModuleControl 0
EDIT:
Die Ausrichtung der Spalten war bei Umlauten noch nicht perfekt. Das haben wir noch gerade korrigiert.