diff --git a/config.schema.json b/config.schema.json index fa51f1b..ca22c79 100644 --- a/config.schema.json +++ b/config.schema.json @@ -412,6 +412,39 @@ "description": "MQTT meter configuration" } ] + }, + "publish": { + "type": "object", + "properties": { + "mqtt": { + "type": "object", + "properties": { + "host": { + "type": "string", + "description": "The host of the MQTT broker, including \"mqtt://\"" + }, + "username": { + "type": "string", + "description": "The username for the MQTT broker" + }, + "password": { + "type": "string", + "description": "The password for the MQTT broker" + }, + "topic": { + "type": "string", + "description": "The topic to publish limits" + } + }, + "required": [ + "host", + "topic" + ], + "additionalProperties": false + } + }, + "additionalProperties": false, + "description": "Publish active control limits" } }, "required": [ diff --git a/src/coordinator/helpers/inverterController.ts b/src/coordinator/helpers/inverterController.ts index 2c6ce3b..557aca7 100644 --- a/src/coordinator/helpers/inverterController.ts +++ b/src/coordinator/helpers/inverterController.ts @@ -22,6 +22,7 @@ import { CappedArrayStack } from '../../helpers/cappedArrayStack.js'; import { timeWeightedAverage } from '../../helpers/timeWeightedAverage.js'; import { differenceInSeconds } from 'date-fns'; import { type ControlsModel } from '../../connections/sunspec/models/controls.js'; +import { Publish } from './publish.js'; export type SupportedControlTypes = Extract< ControlType, @@ -69,6 +70,7 @@ const defaultValues = { } as const satisfies Record; export class InverterController { + private activeLimitOutput: Publish; private cachedDerSample = new CappedArrayStack({ limit: 100 }); private cachedSiteSample = new CappedArrayStack({ limit: 100 }); private logger: Logger; @@ -105,6 +107,7 @@ export class InverterController { inverterConfiguration: InverterConfiguration, ) => Promise; }) { + this.activeLimitOutput = new Publish({ config }); this.secondsToSample = config.inverterControl.sampleSeconds; this.controlFrequencyMinimumSeconds = config.inverterControl.controlFrequencyMinimumSeconds; @@ -172,6 +175,10 @@ export class InverterController { writeActiveControlLimit({ limit: activeInverterControlLimit }); + this.activeLimitOutput.onActiveInverterControlLimit({ + limit: activeInverterControlLimit, + }); + this.controlLimitsCache = { controlLimitsByLimiter, activeInverterControlLimit, diff --git a/src/coordinator/helpers/publish.ts b/src/coordinator/helpers/publish.ts new file mode 100644 index 0000000..08a7893 --- /dev/null +++ b/src/coordinator/helpers/publish.ts @@ -0,0 +1,29 @@ +import mqtt from 'mqtt'; +import { type Config } from '../../helpers/config.js'; +import { type ActiveInverterControlLimit } from './inverterController.js'; + +export class Publish { + private mqttClient: mqtt.MqttClient | undefined; + + constructor({ config }: { config: Config }) { + if (config.publish?.mqtt) { + this.mqttClient = mqtt.connect(config.publish.mqtt.host, { + username: config.publish.mqtt.username, + password: config.publish.mqtt.password, + }); + } + } + + onActiveInverterControlLimit({ + limit, + }: { + limit: ActiveInverterControlLimit; + }) { + if (this.mqttClient) { + this.mqttClient.publish( + 'inverterControlLimit', + JSON.stringify(limit), + ); + } + } +} diff --git a/src/helpers/config.ts b/src/helpers/config.ts index 8019f4a..fd80655 100644 --- a/src/helpers/config.ts +++ b/src/helpers/config.ts @@ -237,6 +237,29 @@ A longer time will smooth out load changes but may result in overshoot.`, }) .describe('MQTT meter configuration'), ]), + publish: z + .object({ + mqtt: z + .object({ + host: z + .string() + .describe( + 'The host of the MQTT broker, including "mqtt://"', + ), + username: z + .string() + .optional() + .describe('The username for the MQTT broker'), + password: z + .string() + .optional() + .describe('The password for the MQTT broker'), + topic: z.string().describe('The topic to publish limits'), + }) + .optional(), + }) + .describe('Publish active control limits') + .optional(), }); export type Config = z.infer;