142 lines
5.5 KiB
JavaScript
142 lines
5.5 KiB
JavaScript
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"
|
|
);
|
|
});
|