Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Goodwe support #57

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
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