UNPKG

node-red-contrib-genetic-charging-strategy

Version:

A module for Node-RED that adds a battery charging strategy to node-red-contrib-power-saver. It uses genetic algorithms to find the best schedule

139 lines (138 loc) 5.33 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const strategy_battery_charging_functions_1 = require("./strategy-battery-charging-functions"); const zod_1 = __importDefault(require("zod")); const moment_1 = __importDefault(require("moment")); const ValueShape = zod_1.default.object({ start: zod_1.default.string(), value: zod_1.default.number(), }); const ImportShape = zod_1.default.object({ start: zod_1.default.string(), importPrice: zod_1.default.number(), exportPrice: zod_1.default.number(), }); const PayloadSchema = zod_1.default.object({ priceData: zod_1.default .array(zod_1.default.union([ValueShape, ImportShape]).transform((data) => { var _a; return ({ start: (0, moment_1.default)(data.start).unix(), importPrice: 'importPrice' in data ? data.importPrice : data.value, exportPrice: 'exportPrice' in data ? ((_a = data.exportPrice) !== null && _a !== void 0 ? _a : data.importPrice) : data.value, }); })) .default([]), consumptionForecast: zod_1.default .array(zod_1.default .object({ start: zod_1.default.string(), value: zod_1.default.number(), }) .transform((data) => ({ start: (0, moment_1.default)(data.start).unix(), value: data.value, }))) .optional() .default([]), productionForecast: zod_1.default .array(zod_1.default .object({ start: zod_1.default.string(), value: zod_1.default.number(), }) .transform((data) => ({ start: (0, moment_1.default)(data.start).unix(), value: data.value, }))) .optional() .default([]), soc: zod_1.default .string() .transform((val, ctx) => { const num = Number(val); if (isNaN(num)) { ctx.addIssue({ code: zod_1.default.ZodIssueCode.custom, message: 'Not a valid number', }); return zod_1.default.NEVER; } return num; }) .pipe(zod_1.default.number().min(1).max(100)), schedule: zod_1.default .array(zod_1.default.object({ start: zod_1.default.string(), activity: zod_1.default.number(), })) .optional(), excessPvEnergyUse: zod_1.default.number().optional(), cost: zod_1.default.number().optional(), noBattery: zod_1.default .object({ schedule: zod_1.default.array(zod_1.default.object({ start: zod_1.default.string(), activity: zod_1.default.number(), })), excessPvEnergyUse: zod_1.default.number().optional(), cost: zod_1.default.number(), }) .optional(), }); exports.default = (RED) => { function StrategyBatteryChargingConstructor(config) { RED.nodes.createNode(this, config); const { populationSize, numberOfPricePeriods, generations, mutationRate, batteryMaxEnergy, batteryMaxInputPower, averageConsumption, batteryIdleLoss, } = config; this.on('input', (msg, send, done) => { const result = PayloadSchema.safeParse(msg.payload); if (!result.success) { msg.error = result.error; this.error(`Invalid payload format\n${result.error.message}`, msg); send(msg); done(); return; } const payload = result.data; const priceData = payload.priceData; const consumptionForecast = payload.consumptionForecast; const productionForecast = payload.productionForecast; const soc = payload.soc; const strategy = (0, strategy_battery_charging_functions_1.calculateBatteryChargingStrategy)({ priceData, consumptionForecast, productionForecast, populationSize, numberOfPricePeriods, generations, mutationRate: mutationRate / 100, batteryMaxEnergy, batteryMaxOutputPower: batteryMaxInputPower, batteryMaxInputPower, averageConsumption, excessPvEnergyUse: 0, // 0=Fed to grid, 1=Charge soc: soc / 100, batteryIdleLoss, }); // eslint-disable-next-line @typescript-eslint/no-explicit-any const output = msg.payload; if (strategy && Object.keys(strategy).length > 0) { output.schedule = strategy.best.schedule; output.excessPvEnergyUse = strategy.best.excessPvEnergyUse; output.cost = strategy.best.cost; output.noBattery = { schedule: strategy.noBattery.schedule, excessPvEnergyUse: strategy.noBattery.excessPvEnergyUse, cost: strategy.noBattery.cost, }; } msg.payload = output; send(msg); done(); }); } RED.nodes.registerType('enell-strategy-genetic-charging', StrategyBatteryChargingConstructor); };