'confirmed_feedback_trial_disabled', 'startedAt' => gmdate('c'), 'actuatorPermissionGranted' => false, 'serverUpdateRequired' => true, 'acceptedModel' => 'virtual_split', ]; try { $hashes = json_decode((string) file_get_contents(__DIR__ . '/PACKAGE_HASHES.json'), true, 32, JSON_THROW_ON_ERROR); foreach ($hashes as $name => $hash) { if (str_contains($name, '..') || str_starts_with($name, '/') || is_link(__DIR__ . '/' . $name) || hash_file('sha256', __DIR__ . '/' . $name) !== $hash) { throw new RuntimeException('Vorbereitetes Paket veraendert.'); } } foreach ([ $manager => '{6F771B18-59D4-4C8A-B951-3B2FE9F6A2C4}', $battery => '{437FB683-517F-4FEC-8CCB-FE6B0A62B69E}', ] as $id => $module) { if (!IPS_InstanceExists($id) || IPS_GetInstance($id)['ModuleInfo']['ModuleID'] !== $module) { throw new RuntimeException('Modulzuordnung ungueltig.'); } if (IPS_HasChanges($id)) { throw new RuntimeException('Offene Aenderungen zuerst speichern oder verwerfen.'); } } $beforeManager = json_decode(IPS_GetConfiguration($manager), true, 128, JSON_THROW_ON_ERROR); $beforeBattery = json_decode(IPS_GetConfiguration($battery), true, 128, JSON_THROW_ON_ERROR); if (($beforeManager['NetzfahrplanAktiv'] ?? null) !== false || ($beforeManager['NetzfahrplanV4RegeltestErlaubt'] ?? false) !== false || ($beforeBattery['NetzfahrplanV4RegeltestErlaubt'] ?? false) !== false) { throw new RuntimeException('Alte Fahrplanausgabe und beide Regeltestfreigaben muessen AUS bleiben.'); } $configuration = json_decode((string) file_get_contents(__DIR__ . '/feedback-config.json'), true, 32, JSON_THROW_ON_ERROR); $manifest = json_decode((string) file_get_contents(__DIR__ . '/MANIFEST.json'), true, 32, JSON_THROW_ON_ERROR); if (($configuration['managerId'] ?? null) !== $manager || ($configuration['batteryInstanceId'] ?? null) !== $battery || ($configuration['mode'] ?? null) !== 'virtual_split' || ($configuration['allowEstimatedForTrial'] ?? null) !== true || hash_file('sha256', __DIR__ . '/feedback-config.json') !== $manifest['configurationSha256']) { throw new RuntimeException('Falsche Rueckmeldekonfiguration.'); } $desired = json_encode($configuration, JSON_THROW_ON_ERROR | JSON_PRESERVE_ZERO_FRACTION); $previousConfiguration = $configuration; $previousConfiguration['allowEstimatedForTrial'] = false; $previousDesired = json_encode($previousConfiguration, JSON_THROW_ON_ERROR | JSON_PRESERVE_ZERO_FRACTION); $oldMapping = $beforeBattery['NetzfahrplanV4RueckmeldungKonfiguration'] ?? '{}'; if (!in_array($oldMapping, [$previousDesired, $desired], true)) { throw new RuntimeException('Abweichende Rueckmeldekonfiguration nicht ueberschrieben.'); } $controls = []; foreach (IPS_GetModuleList() as $module) { if ((IPS_GetModule($module)['Prefix'] ?? '') === 'MC') { $controls = array_merge($controls, IPS_GetInstanceListByModuleID($module)); } } if (count($controls) !== 1 || !function_exists('MC_ReloadModule')) { throw new RuntimeException('Module Control nicht eindeutig.'); } $locked = IPS_SemaphoreEnter('ENELIX.V4.ConfirmedFeedbackInstall', 3000); if (!$locked) { throw new RuntimeException('Installation bereits aktiv.'); } $report['files'] = v4ConfirmedFiles(__DIR__, '/var/lib/symcon/modules/Enelix-EMS'); if (MC_ReloadModule($controls[0], 'Enelix-EMS') === false) { throw new RuntimeException('Bibliotheks-Reload fehlgeschlagen.'); } $afterManager = json_decode(IPS_GetConfiguration($manager), true, 128, JSON_THROW_ON_ERROR); $afterBattery = json_decode(IPS_GetConfiguration($battery), true, 128, JSON_THROW_ON_ERROR); foreach ([[$beforeManager, $afterManager], [$beforeBattery, $afterBattery]] as $pair) { foreach ($pair[0] as $key => $value) { if (!array_key_exists($key, $pair[1]) || $pair[1][$key] !== $value) { throw new RuntimeException('Bestehender Parameter beim Reload veraendert: ' . $key); } } } if (!function_exists('ENELIX_GetV4BatterieRueckmeldung')) { $report['status'] = 'waiting_for_registration'; echo "Dateien installiert. Modulregistrierung wird abgeschlossen; diesen Aufruf einmal wiederholen. Keine Stellfreigabe.\n"; } else { if ($afterBattery['NetzfahrplanV4RueckmeldungKonfiguration'] !== $desired) { IPS_SetProperty($battery, 'NetzfahrplanV4RueckmeldungKonfiguration', $desired); $changedMapping = true; IPS_ApplyChanges($battery); } $finalManager = json_decode(IPS_GetConfiguration($manager), true, 128, JSON_THROW_ON_ERROR); $finalBattery = json_decode(IPS_GetConfiguration($battery), true, 128, JSON_THROW_ON_ERROR); foreach ([[$beforeManager, $finalManager], [$beforeBattery, $finalBattery]] as $pair) { foreach ($pair[0] as $key => $value) { if ($key !== 'NetzfahrplanV4RueckmeldungKonfiguration' && (!array_key_exists($key, $pair[1]) || $pair[1][$key] !== $value)) { throw new RuntimeException('Bestehender Parameter unerwartet veraendert: ' . $key); } } } if (($finalManager['NetzfahrplanAktiv'] ?? null) !== false || ($finalManager['NetzfahrplanV4RegeltestErlaubt'] ?? null) !== false || ($finalBattery['NetzfahrplanV4RegeltestErlaubt'] ?? null) !== false) { throw new RuntimeException('Unerwartete Stellfreigabe nach Installation.'); } $feedback = json_decode(ENELIX_GetV4BatterieRueckmeldung($battery), true, 32, JSON_THROW_ON_ERROR); $report['feedback'] = array_intersect_key($feedback, array_flip([ 'status', 'kind', 'method', 'reason', 'reasons', 'estimated', 'batteryW', 'gridW', 'sourceOldestAt', 'sourceVariableOldestAt', 'confirmedAt', 'checkedAt', 'configHash', 'deviceReadConfirmed', 'usableForTrial', 'canDispatch', ])); $report['status'] = 'confirmed_feedback_installed_trial_disabled'; $report['originalParametersPreserved'] = true; echo "OK: Geraeteabfrage bestaetigt die physischen Messwerte; virtual_split ist fuer den begrenzten Regeltest akzeptiert.\n"; echo 'Rueckmeldung: ' . ($feedback['status'] ?? 'available') . '; Geraeteabfrage: ' . (($feedback['deviceReadConfirmed'] ?? false) ? 'bestaetigt' : 'nicht bestaetigt') . '; Modellanteil: ' . (($feedback['estimated'] ?? false) ? 'ja' : 'nein') . ".\n"; if (isset($feedback['reason'])) { echo 'Aktueller Hinweis: ' . $feedback['reason'] . "\n"; } echo "Beide Regeltestfreigaben AUS. Kein Stellbefehl ohne separat nachgewiesenen Geraete-Watchdog und Serverfreigabe.\n"; } } catch (Throwable $error) { $report['status'] = 'needs_review'; $report['reason'] = substr($error->getMessage(), 0, 260); if ($changedMapping && $oldMapping !== null) { try { IPS_SetProperty($battery, 'NetzfahrplanV4RueckmeldungKonfiguration', $oldMapping); IPS_ApplyChanges($battery); $report['mappingRestored'] = true; } catch (Throwable $ignored) { $report['mappingRestored'] = false; } } echo 'FEHLER: ' . $report['reason'] . "\nKeine V4-Stellfreigabe erteilt.\n"; } finally { $report['finishedAt'] = gmdate('c'); v4ConfirmedWrite(__DIR__ . '/INSTALL_RESULT.json', json_encode($report, JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT) . "\n", 0644); if ($locked) { IPS_SemaphoreLeave('ENELIX.V4.ConfirmedFeedbackInstall'); } echo 'CONFIRMED FEEDBACK INSTALL REPORT: ' . __DIR__ . "/INSTALL_RESULT.json\n"; }