import assert from "node:assert/strict"; import test from "node:test"; import { consumerFields, normalizeSystemConfiguration, publicWizardCatalog } from "../portal-domain.mjs"; import { buildSymconPackage } from "../symcon-package.mjs"; const emptyDevices = () => ({ battery: { quantity: 0, items: [] }, buffer_storage: { quantity: 0, items: [] }, ev_charger: { quantity: 0, items: [] }, consumer_single: { quantity: 0, items: [] }, boiler_multi: { quantity: 0, items: [] }, heat_pump: { quantity: 0, items: [] } }); const baseConfiguration = () => ({ name: "Testsystem", location: "Zürich", manager: { properties: {} }, gridMeasurement: { profile: "manual", sourceVariableId: 42 }, pvSystem: { enabled: false }, devices: emptyDevices() }); test("wizard catalog contains complete manager, consumer and integration schemas", () => { const catalog = publicWizardCatalog(); assert.equal(catalog.schemaVersion, 4); assert.equal(catalog.managerFields.length, 11); assert.deepEqual(Object.keys(catalog.consumerFields).sort(), Object.keys(emptyDevices()).sort()); assert.equal(catalog.evVariants.length, 2); assert.ok(catalog.integrations.energy.some(profile => profile.id === "fronius_solar_api_v1")); assert.ok(catalog.integrations.energy.some(profile => profile.id === "huawei_sun2000_modbus")); assert.ok(catalog.integrations.energy.some(profile => profile.id === "sma_sunspec_modbus")); assert.ok(catalog.integrations.energy.some(profile => profile.id === "goodwe_et_modbus")); assert.ok(catalog.integrations.energy.some(profile => profile.id === "solaredge_sunspec_modbus")); assert.ok(catalog.integrations.storage.some(profile => profile.id === "sigenergy_sigenstor")); assert.ok(catalog.integrations.actuator.some(profile => profile.id === "shelly_pro_4pm")); assert.ok(consumerFields.battery.length > 20); }); test("normalizes individual device properties and integration metadata", () => { const configuration = baseConfiguration(); configuration.manager.properties.MesswertMaxAlter = 15; configuration.devices.consumer_single = { quantity: 1, items: [{ name: "Wärmepumpe", integration: { profile: "shelly_pro_4pm", topicPrefix: "shellypro4pm-test", channel: 3 }, properties: { Nennleistung: 4200, PrioritaetPV: 2 } }] }; const result = normalizeSystemConfiguration(configuration); assert.equal(result.manager.properties.MesswertMaxAlter, 15); assert.equal(result.devices.consumer_single.items[0].integration.channel, 3); assert.equal(result.devices.consumer_single.items[0].properties.Nennleistung, 4200); assert.equal(result.devices.consumer_single.items[0].properties.PrioritaetPV, 2); }); test("derives battery type and enables charging by default", () => { const configuration = baseConfiguration(); configuration.devices.battery = { quantity: 2, items: [{ name: "Hausspeicher", integration: { profile: "goodwe_lynx", host: "192.0.2.10" }, properties: { Batterietyp: 0 } }, { name: "Gewerbespeicher", integration: { profile: "sigenergy_sigenstor", host: "192.0.2.11" }, properties: { Batterietyp: 0 } }] }; configuration.devices.ev_charger = { quantity: 2, items: [{ name: "Direkt", variant: "standalone", properties: {} }, { name: "Gateway", variant: "gateway", properties: {} }] }; const result = normalizeSystemConfiguration(configuration); assert.equal(result.devices.battery.items[0].properties.Batterietyp, 2); assert.equal(result.devices.battery.items[1].properties.Batterietyp, 4); assert.equal(result.devices.ev_charger.items[0].properties.Ladefreigabe, true); assert.equal(result.devices.ev_charger.items[1].properties.Ladefreigabe, true); }); test("package wires gateway, Shelly feedback, levels, sensors and visualisation", () => { const configuration = baseConfiguration(); configuration.devices.ev_charger = { quantity: 1, items: [{ name: "Garage", variant: "gateway", properties: { Ladestationskennung: "EH123" } }] }; configuration.devices.consumer_single = { quantity: 1, items: [{ name: "Heizstab", integration: { profile: "shelly_plus_1pm", topicPrefix: "shelly/heizstab" }, properties: {} }] }; configuration.devices.boiler_multi = { quantity: 1, items: [{ name: "Boiler", properties: {}, powerLevels: [{ Stufe: 1, Leistung: 3000, integration: { profile: "shelly_pro_2pm", topicPrefix: "shelly/boiler", channel: 1 } }], schedule: [{ Uhrzeit: "06:30", Solltemperatur: 55 }], sensor: { profile: "shelly_addon_temperature", topicPrefix: "shelly/boiler" } }] }; const archive = buildSymconPackage(configuration).toString("utf8"); assert.match(archive, /RueckmeldungVariableID/); assert.match(archive, /Boilerfuehler_PT1/); assert.match(archive, /IPS_ConnectInstance/); assert.match(archive, /Energie-Kreisdiagramm/); }); test("requires a host for network profiles", () => { const configuration = baseConfiguration(); configuration.gridMeasurement = { profile: "sma_sunspec_modbus" }; assert.throws( () => normalizeSystemConfiguration(configuration), error => error.message === "integration_host_missing" ); }); test("requires an MQTT topic for Shelly profiles", () => { const configuration = baseConfiguration(); configuration.devices.consumer_single = { quantity: 1, items: [{ name: "Heizstab", integration: { profile: "shelly_plus_1pm" }, properties: {} }] }; assert.throws( () => normalizeSystemConfiguration(configuration), error => error.message === "integration_topic_missing" ); });