Skip to content
52 changes: 52 additions & 0 deletions config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,32 @@
],
"additionalProperties": false,
"description": "SMA inverter configuration"
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "goodwe"
},
"model": {
"type": "string",
"const": "et"
},
"connection": {
"$ref": "#/definitions/config/properties/inverters/items/anyOf/0/properties/connection"
},
"unitId": {
"$ref": "#/definitions/config/properties/inverters/items/anyOf/0/properties/unitId"
}
},
"required": [
"type",
"model",
"connection"
],
"additionalProperties": false,
"description": "Goodwe inverter configuration"
}
]
},
Expand Down Expand Up @@ -349,6 +375,32 @@
"additionalProperties": false,
"description": "SMA meter configuration"
},
{
"type": "object",
"properties": {
"type": {
"type": "string",
"const": "goodwe"
},
"model": {
"type": "string",
"const": "et"
},
"connection": {
"$ref": "#/definitions/config/properties/inverters/items/anyOf/0/properties/connection"
},
"unitId": {
"$ref": "#/definitions/config/properties/inverters/items/anyOf/0/properties/unitId"
}
},
"required": [
"type",
"model",
"connection"
],
"additionalProperties": false,
"description": "Goodwe meter configuration"
},
{
"type": "object",
"properties": {
Expand Down
92 changes: 92 additions & 0 deletions src/connections/goodwe/et.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { type ModbusConnection } from '../modbus/connection/base.js';
import { type Logger } from 'pino';
import { getModbusConnection } from '../modbus/connections.js';
import { type ModbusSchema } from '../../helpers/config.js';
import {
GoodweEtDeviceParametersModel,
type GoodweEtDeviceParameters,
} from './models/et/deviceParameters.js';
import { type GoodweEtInverterRunningData1 } from './models/et/inverterRunningData1.js';
import { GoodweEtInverterRunningData1Model } from './models/et/inverterRunningData1.js';
import {
GoodweEtMeterDataModel,
type GoodweEtMeterData,
} from './models/et/meterData.js';
import { writeLatency } from '../../helpers/influxdb.js';

export class GoodweEtConnection {
protected readonly modbusConnection: ModbusConnection;
protected readonly unitId: number;
private logger: Logger;

// the device parameters model should never change so we can cache it
private deviceParameters: GoodweEtDeviceParameters | null = null;

constructor({ connection, unitId }: ModbusSchema) {
this.modbusConnection = getModbusConnection(connection);
this.unitId = unitId;
this.logger = this.modbusConnection.logger.child({
module: 'GoodweConnection',
unitId,
});
}

async getDeviceParameters(): Promise<GoodweEtDeviceParameters> {
if (this.deviceParameters) {
return this.deviceParameters;
}

const data = await GoodweEtDeviceParametersModel.read({
modbusConnection: this.modbusConnection,
address: {
start: 35001,
length: 15,
},
unitId: this.unitId,
});

this.deviceParameters = data;

return data;
}

async getInverterRunningData1(): Promise<GoodweEtInverterRunningData1> {
const data = await GoodweEtInverterRunningData1Model.read({
modbusConnection: this.modbusConnection,
address: {
start: 35121,
length: 24,
},
unitId: this.unitId,
});

return data;
}

async getMeterData(): Promise<GoodweEtMeterData> {
const start = performance.now();

const data = await GoodweEtMeterDataModel.read({
modbusConnection: this.modbusConnection,
address: {
start: 36003,
length: 55,
},
unitId: this.unitId,
});

writeLatency({
field: 'GoodweEtConnection',
duration: performance.now() - start,
tags: {
model: 'GoodweEtMeterDataModel',
},
});

return data;
}

public onDestroy(): void {
this.modbusConnection.close();
}
}
36 changes: 36 additions & 0 deletions src/connections/goodwe/models/et/deviceParameters.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { modbusModelFactory } from '../../../modbus/modbusModelFactory.js';
import {
registersToString,
registersToUint16,
} from '../../../modbus/helpers/converters.js';

export type GoodweEtDeviceParameters = {
// Inverter rated power
RatePower: number;
// Inverter serial number. ASCII,16 bytes,readtogether, include OEMproducts.
INVSN: string;
// ASCII,10 bytes
Modelname: string;
};

export const GoodweEtDeviceParametersModel =
modbusModelFactory<GoodweEtDeviceParameters>({
name: 'GoodweEtDeviceParametersModel',
mapping: {
RatePower: {
start: 0,
end: 1,
readConverter: registersToUint16,
},
INVSN: {
start: 2,
end: 11,
readConverter: registersToString,
},
Modelname: {
start: 12,
end: 22,
readConverter: registersToString,
},
},
});
143 changes: 143 additions & 0 deletions src/connections/goodwe/models/et/inverterRunningData1.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import { modbusModelFactory } from '../../../modbus/modbusModelFactory.js';
import {
registersToInt32,
registersToUint16,
} from '../../../modbus/helpers/converters.js';

export type GoodweEtInverterRunningData1 = {
// R phase Grid voltage
Vgrid_R: number;
// R phase Grid current
Igrid_R: number;
// R phase Grid Frequency
Fgrid_R: number;
// R phase Grid Power(Inv power)
Pgrid_R: number;
// S phase Grid voltage
Vgrid_S: number;
// S phase Grid current
Igrid_S: number;
// S phase Grid Frequency
Fgrid_S: number;
// S phase Grid Power(Inv power)
Pgrid_S: number;
// T phase Grid voltage
Vgrid_T: number;
// T phase Grid current
Igrid_T: number;
// T phase Grid Frequency
Fgrid_T: number;
// T phase Grid Power(Inv power)
Pgrid_T: number;
// Grid connection status
GridMode: GridMode;
// Total Power of Inverter(Inv power)
TotalINVPower: number;
// Total Active Power Of Inverter. (If meter connection ok, it is meter power.If meter connection fail, it is inverter on-grid port power)
ACActivePower: number;
// Total Reactive Power Of Inverter
ACReactivePower: number;
// Total Apparent Power Of Inverter
ACApparentPower: number;
};

export enum GridMode {
// 0x00 Loss, inverter disconnects to Grid
Loss = 0,
// 0x01 OK, inverter connects to Grid
OK = 1,
// 0x02 Fault, something is wrong
Fault = 2,
}

export const GoodweEtInverterRunningData1Model =
modbusModelFactory<GoodweEtInverterRunningData1>({
name: 'GoodweEtInverterRunningData1Model',
mapping: {
Vgrid_R: {
start: 0,
end: 1,
readConverter: (value) => registersToUint16(value, -1),
},
Igrid_R: {
start: 1,
end: 2,
readConverter: (value) => registersToUint16(value, -1),
},
Fgrid_R: {
start: 2,
end: 3,
readConverter: (value) => registersToUint16(value, -2),
},
Pgrid_R: {
start: 3,
end: 5,
readConverter: registersToInt32,
},
Vgrid_S: {
start: 5,
end: 6,
readConverter: (value) => registersToUint16(value, -1),
},
Igrid_S: {
start: 6,
end: 7,
readConverter: (value) => registersToUint16(value, -1),
},
Fgrid_S: {
start: 7,
end: 8,
readConverter: (value) => registersToUint16(value, -2),
},
Pgrid_S: {
start: 8,
end: 10,
readConverter: registersToInt32,
},
Vgrid_T: {
start: 10,
end: 11,
readConverter: (value) => registersToUint16(value, -1),
},
Igrid_T: {
start: 11,
end: 12,
readConverter: (value) => registersToUint16(value, -1),
},
Fgrid_T: {
start: 12,
end: 13,
readConverter: (value) => registersToUint16(value, -2),
},
Pgrid_T: {
start: 13,
end: 15,
readConverter: registersToInt32,
},
GridMode: {
start: 15,
end: 16,
readConverter: registersToUint16,
},
TotalINVPower: {
start: 16,
end: 18,
readConverter: registersToInt32,
},
ACActivePower: {
start: 18,
end: 20,
readConverter: registersToInt32,
},
ACReactivePower: {
start: 20,
end: 22,
readConverter: registersToInt32,
},
ACApparentPower: {
start: 22,
end: 24,
readConverter: registersToInt32,
},
},
});
Loading
Loading