feat: Wärmepumpenmodul integrieren
Tests / test (push) Successful in 49s

This commit is contained in:
dh
2026-09-22 14:34:23 +00:00
parent d7308663da
commit 7845c66fd8
14 changed files with 2127 additions and 51 deletions
+5 -13
View File
@@ -8,24 +8,16 @@ use PHPUnit\Framework\TestCase;
final class DokumentationsstrukturTest extends TestCase
{
/** @return list<array{string}> */
public function modulReadmes(): array
public function testWaermepumpeDokumentiertDenImplementiertenStand(): void
{
return [
['Waermepumpe'],
];
}
/** @dataProvider modulReadmes */
public function testJederModulentwurfHatEinReadme(string $modul): void
{
$pfad = __DIR__ . '/../docs/module/' . $modul . '/README.md';
$pfad = __DIR__ . '/../docs/module/Waermepumpe/README.md';
self::assertFileExists($pfad);
$inhalt = file_get_contents($pfad);
self::assertNotFalse($inhalt);
self::assertStringContainsString('Status: Diskussionsentwurf', $inhalt);
self::assertStringContainsString('Properties', $inhalt);
self::assertStringContainsString('Status: Implementiert', $inhalt);
self::assertStringContainsString('Konfiguration', $inhalt);
self::assertStringContainsString('Adaption aus Enelix 1', $inhalt);
}
public function testBatterieDokumentiertDenImplementiertenStand(): void
+10
View File
@@ -74,6 +74,16 @@ return [
'libs/Nachrichtenvertrag.php',
],
],
'Waermepumpe' => [
'file' => __DIR__ . '/modules/Waermepumpe.php',
'paths' => [
'Waermepumpe/*',
'libs/WaermepumpenRegler.php',
'libs/VerbraucherBasisTrait.php',
'libs/VerbraucherSchnittstelle.php',
'libs/Nachrichtenvertrag.php',
],
],
'Warmwassererwaermer' => [
'file' => __DIR__ . '/modules/Warmwassererwaermer.php',
'paths' => [
+140
View File
@@ -0,0 +1,140 @@
<?php
declare(strict_types=1);
use Belevo\EnelixEMS\SymconTest\TestContext;
return static function (TestContext $test): void {
$kontakt1 = $test->createActionVariable('WP Kontakt 1');
$kontakt2 = $test->createActionVariable('WP Kontakt 2');
$rueckmeldung = $test->createVariable(0, 'WP läuft', false);
$waermepumpe = $test->createInstance(
'{A31C9274-54F7-4BD2-9804-7AF23E86C4D1}',
'Wärmepumpe',
[
'Kontaktart' => 0,
'Kontakt1VariableID' => $kontakt1,
'Kontakt2VariableID' => $kontakt2,
'Nennleistung' => 5000,
'Rueckmeldungsart' => 1,
'IstleistungVariableID' => $rueckmeldung,
'Anlaufwartezeit' => 1,
'Wiederholsperre' => 3,
'Mindestlaufzeit' => 1,
'Mindestsperrzeit' => 0,
'MaximaleSperrzeit' => 2,
'MaximaleSperrzeit24h' => 60,
'VorgabeTimeout' => 30,
'DiagnosevariablenAnzeigen' => true,
]
);
$manager = $test->createInstance(
'{6F771B18-59D4-4C8A-B951-3B2FE9F6A2C4}',
'WP Hilfsmanager',
[
'AutomatischeSuche' => false,
'VerbraucherZuordnung' => json_encode([
['InstanzID' => $waermepumpe, 'Aktiv' => true],
], JSON_THROW_ON_ERROR),
]
);
$senden = static function (?int $leistung, string $betriebsart) use (
$manager,
$waermepumpe
): void {
IPS_RequestAction(
$waermepumpe,
'ManagerdatenEmpfangen',
json_encode([
'Kopf' => [
'Version' => '4.0',
'AbsenderID' => $manager,
'EmpfaengerID' => $waermepumpe,
'Zeitpunkt' => time(),
],
'Betriebsart' => $betriebsart,
'Sollleistung_W' => $leistung,
], JSON_THROW_ON_ERROR)
);
};
$test->runCase('Instanz und Diagnosevariablen werden angelegt', static function (
TestContext $test
) use ($waermepumpe): void {
$test->assertInstanceStatus($waermepumpe);
foreach ([
'Aktiv',
'Waermepumpenzustand',
'WaermepumpeLaeuft',
'SperreAktiv',
'ErhoehungAktiv',
'SGReadyZustand',
'Istleistung',
'Sollleistung',
'AnlaufAusstehend',
'Sperrzeit24h',
] as $ident) {
$test->objectByIdent($ident, $waermepumpe);
}
});
$test->runCase('PV fordert Erhoehung an und bestaetigt den Anlauf', static function (
TestContext $test
) use ($waermepumpe, $kontakt1, $kontakt2, $rueckmeldung, $senden): void {
IPS_RequestAction($waermepumpe, 'Aktiv', true);
$senden(null, 'PV');
$senden(5000, 'PV');
$test->waitUntil(static fn (): bool => GetValue($kontakt2) === true);
$test->assertSame(false, GetValue($kontakt1), 'Kontakt 1 muss im PV-Anlauf aus sein.');
$test->assertSame(true, GetValue(
$test->objectByIdent('AnlaufAusstehend', $waermepumpe)
), 'Der PV-Anlauf muss bis zur Rueckmeldung ausstehend sein.');
$test->waitUntil(static fn (): bool => GetValue($kontakt2) === false, 2500);
$test->waitUntil(static fn (): bool => GetValue(
$test->objectByIdent('WiederholsperreAktiv', $waermepumpe)
) === true);
$test->assertSame(true, GetValue(
$test->objectByIdent('WiederholsperreAktiv', $waermepumpe)
), 'Nach dem fehlgeschlagenen Anlauf muss die Wiederholsperre aktiv sein.');
$test->assertSame(false, GetValue(
$test->objectByIdent('AenderungMoeglich', $waermepumpe)
), 'Waehrend der Wiederholsperre darf keine Aenderung angeboten werden.');
$test->waitUntil(static fn (): bool => GetValue(
$test->objectByIdent('WiederholsperreAktiv', $waermepumpe)
) === false, 4500);
$senden(5000, 'PV');
$test->waitUntil(static fn (): bool => GetValue($kontakt2) === true);
SetValue($rueckmeldung, true);
$test->waitUntil(static fn (): bool => GetValue(
$test->objectByIdent('WaermepumpeLaeuft', $waermepumpe)
) === true);
$test->assertSame(5000, (int) GetValue(
$test->objectByIdent('Istleistung', $waermepumpe)
));
IPS_Sleep(1100);
});
$test->runCase('Peak sperrt und maximale Sperrzeit erzwingt Normalbetrieb', static function (
TestContext $test
) use ($waermepumpe, $kontakt1, $kontakt2, $senden): void {
$senden(null, 'Peak');
$senden(0, 'Peak');
$test->waitUntil(static fn (): bool => GetValue($kontakt1) === true);
$test->assertSame(false, GetValue($kontakt2));
$test->assertSame(true, GetValue(
$test->objectByIdent('SperreAktiv', $waermepumpe)
));
$test->waitUntil(static fn (): bool => GetValue($kontakt1) === false, 2500);
$test->assertSame(true, GetValue(
$test->objectByIdent('SperrerholungAktiv', $waermepumpe)
));
$test->assertSame(false, GetValue(
$test->objectByIdent('SperreAktiv', $waermepumpe)
));
});
};
+2
View File
@@ -65,6 +65,7 @@ final class SymconTestContractTest extends TestCase
'Manager',
'Pufferspeicher',
'VerbraucherEinStufig',
'Waermepumpe',
'Warmwassererwaermer',
],
$this->resolveAffectedModules(['libs/Nachrichtenvertrag.php'])
@@ -90,6 +91,7 @@ final class SymconTestContractTest extends TestCase
'Manager',
'Pufferspeicher',
'VerbraucherEinStufig',
'Waermepumpe',
'Warmwassererwaermer',
],
$this->resolveAffectedModules(['tests/Symcon/TestContext.php'])
+136
View File
@@ -0,0 +1,136 @@
<?php
declare(strict_types=1);
namespace Belevo\EnelixEMS\Tests;
use PHPUnit\Framework\TestCase;
final class WaermepumpeModulstrukturTest extends TestCase
{
private const MODULPFAD = __DIR__ . '/../Waermepumpe/';
private const MODULID = '{A31C9274-54F7-4BD2-9804-7AF23E86C4D1}';
public function testModulIstVollstaendigUndMetadatenSindGueltig(): void
{
foreach (['module.json', 'form.json', 'module.php'] as $datei) {
self::assertFileExists(self::MODULPFAD . $datei);
}
$metadaten = json_decode(
(string) file_get_contents(self::MODULPFAD . 'module.json'),
true,
512,
JSON_THROW_ON_ERROR
);
self::assertSame('VerbraucherWaermepumpe', $metadaten['name']);
self::assertSame('Enelix', $metadaten['vendor']);
self::assertSame(['Waermepumpe'], $metadaten['aliases']);
self::assertSame(self::MODULID, $metadaten['id']);
self::assertSame(3, $metadaten['type']);
}
public function testAllePropertiesSindRegistriertUndImFormularEnthalten(): void
{
$basis = (string) file_get_contents(__DIR__ . '/../libs/VerbraucherBasisTrait.php');
$modul = (string) file_get_contents(self::MODULPFAD . 'module.php');
preg_match_all(
'/RegisterProperty(?:Integer|Float|Boolean|String)\(\'([^\']+)\'/',
$basis . $modul,
$treffer
);
$formular = (string) file_get_contents(self::MODULPFAD . 'form.json');
foreach (array_values(array_unique($treffer[1])) as $property) {
self::assertStringContainsString('"name": "' . $property . '"', $formular, $property);
}
}
public function testVertrag40UndBetriebsartabhaengigesAngebotWerdenVerwendet(): void
{
$modul = (string) file_get_contents(self::MODULPFAD . 'module.php');
self::assertStringContainsString('implements VerbraucherSchnittstelle', $modul);
self::assertStringContainsString('ManagerdatenEmpfangen(array $daten)', $modul);
self::assertStringContainsString('Nachrichtenvertrag::pruefeManagerdaten', $modul);
self::assertStringContainsString('Nachrichtenvertrag::pruefeVerbraucherdaten', $modul);
self::assertStringContainsString("['Betriebsart']", $modul);
self::assertStringNotContainsString("RegisterPropertyInteger('ManagerID'", $modul);
self::assertStringNotContainsString("RegisterVariableString('PowerSteps'", $modul);
}
public function testRegelungIstEreignisbasiertUndBegrenztSperrzeiten(): void
{
$modul = (string) file_get_contents(self::MODULPFAD . 'module.php');
self::assertStringContainsString('public function MessageSink', $modul);
self::assertStringContainsString('RegisterMessage($neueID, self::VM_UPDATE)', $modul);
foreach ([
'Meldezyklus',
'MeldungVerzoegert',
'Zustandspruefung',
'VorgabeTimeout',
] as $timer) {
self::assertStringContainsString("'" . $timer . "'", $modul);
}
foreach ([
'Mindestlaufzeit',
'Mindestsperrzeit',
'MaximaleSperrzeit',
'MaximaleSperrzeit24h',
'Anlaufwartezeit',
'Wiederholsperre',
] as $property) {
self::assertStringContainsString("'" . $property . "'", $modul);
}
}
public function testKontakteWerdenBreakBeforeMakeGeschaltet(): void
{
$modul = (string) file_get_contents(self::MODULPFAD . 'module.php');
$aus = strpos($modul, '$this->setzeKontakt(1, false)');
$ein = strpos($modul, '$this->setzeKontakt(1, true)');
self::assertIsInt($aus);
self::assertIsInt($ein);
self::assertLessThan($ein, $aus);
}
public function testFachUndDiagnosevariablenSindVorhanden(): void
{
$modul = (string) file_get_contents(self::MODULPFAD . 'module.php');
foreach ([
'Waermepumpenzustand',
'WaermepumpeLaeuft',
'SperreAktiv',
'ErhoehungAktiv',
'SGReadyZustand',
'Laufzeit',
'AnlaufAusstehend',
'WiederholsperreAktiv',
'SperrerholungAktiv',
'Sperrzeit24h',
] as $ident) {
self::assertStringContainsString("'" . $ident . "'", $modul);
}
self::assertStringContainsString("ReadPropertyBoolean('DiagnosevariablenAnzeigen')", $modul);
self::assertStringContainsString('UnregisterVariable($ident)', $modul);
}
public function testManagerKenntDieModulIDGenauEinmal(): void
{
$manager = (string) file_get_contents(__DIR__ . '/../Manager/module.php');
self::assertSame(1, substr_count($manager, self::MODULID));
}
public function testDokumentationBeschreibtImplementiertenStand(): void
{
$dokumentation = (string) file_get_contents(
__DIR__ . '/../docs/module/Waermepumpe/README.md'
);
self::assertStringContainsString('Status: Implementiert', $dokumentation);
self::assertStringContainsString('nicht uebernommen', $dokumentation);
}
}
+162
View File
@@ -0,0 +1,162 @@
<?php
declare(strict_types=1);
namespace Belevo\EnelixEMS\Tests;
use Belevo\EnelixEMS\Nachrichtenvertrag;
use Belevo\EnelixEMS\WaermepumpenRegler;
use InvalidArgumentException;
use PHPUnit\Framework\TestCase;
final class WaermepumpenReglerTest extends TestCase
{
public function testKontaktwerteBildenNormalSperreUndErhoehungAb(): void
{
self::assertSame(
['Kontakt1' => false, 'Kontakt2' => false],
WaermepumpenRegler::kontaktwerte(0, WaermepumpenRegler::STEUERZUSTAND_NORMAL)
);
self::assertSame(
['Kontakt1' => true, 'Kontakt2' => false],
WaermepumpenRegler::kontaktwerte(1, WaermepumpenRegler::STEUERZUSTAND_SPERRE)
);
self::assertSame(
['Kontakt1' => false, 'Kontakt2' => true],
WaermepumpenRegler::kontaktwerte(1, WaermepumpenRegler::STEUERZUSTAND_ERHOEHUNG)
);
}
public function testLeistungUndBetriebsstatusBestimmenLaufUndIstleistung(): void
{
self::assertTrue(WaermepumpenRegler::laeuft(650.0, 0, 100.0));
self::assertFalse(WaermepumpenRegler::laeuft(50.0, 0, 100.0));
self::assertTrue(WaermepumpenRegler::laeuft(true, 1, 100.0));
self::assertSame(650.0, WaermepumpenRegler::istleistung(650.0, 0, 5000));
self::assertSame(5000.0, WaermepumpenRegler::istleistung(true, 1, 5000));
}
public function testPvBietetStartUndErhoehungNurBeiFreigabeAn(): void
{
self::assertSame(
[0, 5000],
WaermepumpenRegler::leistungsangebot(
5000,
Nachrichtenvertrag::BETRIEBSART_PV,
false,
true,
false,
false,
true,
true,
false
)
);
self::assertSame(
[0],
WaermepumpenRegler::leistungsangebot(
5000,
Nachrichtenvertrag::BETRIEBSART_PV,
false,
true,
false,
true,
true,
false,
false
)
);
}
public function testPeakBietetSperreNurBeiLaufenderUndFreigegebenerPumpeAn(): void
{
self::assertSame(
[0, 5000],
WaermepumpenRegler::leistungsangebot(
5000,
Nachrichtenvertrag::BETRIEBSART_PEAK,
true,
true,
false,
false,
true,
true,
false
)
);
self::assertSame(
[5000],
WaermepumpenRegler::leistungsangebot(
5000,
Nachrichtenvertrag::BETRIEBSART_PEAK,
true,
true,
true,
false,
false,
true,
false
)
);
self::assertSame(
[0],
WaermepumpenRegler::leistungsangebot(
5000,
Nachrichtenvertrag::BETRIEBSART_PEAK,
false,
true,
false,
false,
true,
true,
false
)
);
}
public function testSperrerholungHaeltDenBestaetigtenBetrieb(): void
{
self::assertSame(
[5000],
WaermepumpenRegler::leistungsangebot(
5000,
Nachrichtenvertrag::BETRIEBSART_PEAK,
true,
true,
false,
false,
true,
true,
true
)
);
}
public function testRestzeitWirdNieNegativ(): void
{
self::assertSame(20, WaermepumpenRegler::restzeit(100, 30, 110));
self::assertSame(0, WaermepumpenRegler::restzeit(100, 30, 140));
self::assertSame(0, WaermepumpenRegler::restzeit(0, 30, 10));
}
public function testSperrzeit24hSchneidetAlteIntervalleAb(): void
{
$jetzt = 200000;
$historie = [
['Von' => $jetzt - 90000, 'Bis' => $jetzt - 80000],
['Von' => $jetzt - 1000, 'Bis' => $jetzt - 400],
];
self::assertSame(
7400,
WaermepumpenRegler::sperrsekunden24h($historie, $jetzt, $jetzt - 400)
);
}
public function testUngueltigeKontaktartWirdAbgewiesen(): void
{
$this->expectException(InvalidArgumentException::class);
WaermepumpenRegler::kontaktwerte(99, WaermepumpenRegler::STEUERZUSTAND_NORMAL);
}
}