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

Add support for setting accessory model and serial #22

Merged
merged 8 commits into from
Jul 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ npm i -g homebridge-fronius-inverter-lights@latest
"platform": "FroniusInverterLightsPlatform",
"inverterIp": "192.168.1.124",
"pollInterval": 2,
"pvMaxPower": 6000,
"battery": false
},

Expand All @@ -39,7 +38,6 @@ npm i -g homebridge-fronius-inverter-lights@latest
- `platform` (required) the name of the plugin, must be `FroniusInverterLightsPlatform`
- `inverterIp` (required) the IP address of your Fronius inverter
- `pollInterval` (required) the polling frequency in seconds
- `pvMaxPower` (optional) the max capacity of your PV in watts (to show the PV lightbulb brightness % as a percentage of your max capacity)
- `battery` (optional) enable battery accessory to show your battery SOC and usage

## Enable Solar API on newer Fronius inverters
Expand Down
7 changes: 0 additions & 7 deletions config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,6 @@
"required": true,
"default": 10
},
"pvMaxPower": {
"title": "PV max power capacity (watts)",
"description": "If configured, shows the PV lightbulb brightness % as a proportion to the max PV capacity",
"type": "integer",
"required": false,
"placeholder": "6000"
},
"battery": {
"title": "Show battery accessories",
"type": "boolean",
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "homebridge-fronius-inverter-lights",
"version": "1.6.2",
"version": "1.7.0",
"description": "Homebridge plugin for Fronius inverter with smart meter as a lightbulb accessory",
"main": "dist/fronius-platform.js",
"scripts": {
Expand Down
1 change: 0 additions & 1 deletion src/config.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
export type Config = {
inverterIp: string;
pollInterval: number;
pvMaxPower?: number;
battery?: boolean;
};
202 changes: 113 additions & 89 deletions src/fronius-accessory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,25 @@ export class FroniusAccessory implements AccessoryPlugin {
private brightnessValue: number | Error = 0;
private luxValue: number | Error = 0;

constructor(
hap: HAP,
log: Logging,
metering: Metering,
froniusApi: FroniusApi,
pollInterval: number,
pvMaxPower?: number,
) {
constructor({
hap,
log,
metering,
froniusApi,
pollInterval,
pvMaxPower,
model,
serialNumber,
}: {
hap: HAP;
log: Logging;
metering: Metering;
froniusApi: FroniusApi;
pollInterval: number;
pvMaxPower?: number;
model?: string;
serialNumber?: string;
}) {
this.hap = hap;
this.log = log;
this.name = metering.toString();
Expand Down Expand Up @@ -92,9 +103,25 @@ export class FroniusAccessory implements AccessoryPlugin {
return this.luxValue;
});

this.informationService = new hap.Service.AccessoryInformation()
.setCharacteristic(hap.Characteristic.Manufacturer, 'Fronius')
.setCharacteristic(hap.Characteristic.Model, 'Inverter');
this.informationService =
new hap.Service.AccessoryInformation().setCharacteristic(
hap.Characteristic.Manufacturer,
'Fronius',
);

if (model) {
this.informationService.setCharacteristic(
hap.Characteristic.Model,
model,
);
}

if (serialNumber) {
this.informationService.setCharacteristic(
hap.Characteristic.SerialNumber,
serialNumber,
);
}

setInterval(async () => {
await this.scheduledUpdate();
Expand All @@ -117,87 +144,84 @@ export class FroniusAccessory implements AccessoryPlugin {
}

async updateValues() {
const data = await this.froniusApi.getInverterData();

if (data) {
// P_Akku should be positive when discharging and negative when charging
const batteryValue = data.Site.P_Akku ?? 0;
const batteryState =
batteryValue < 0
? 'charging'
: batteryValue > 0
? 'discharging'
: 'idle';

switch (this.metering) {
case 'Export':
case 'Import': {
const gridValue = data.Site.P_Grid;
const autonomyValue = data.Site.rel_Autonomy;
const selfConsumptionValue = data.Site.rel_SelfConsumption || 100;
const isImport = this.metering === 'Import';

this.onValue =
// on/off is calculated whether autonomy/selfConsumption is less than 100
(isImport ? autonomyValue : selfConsumptionValue) < 100;
this.brightnessValue =
// percentage of import/export is calculated from 100 - autonomy/selfConsumption
100 - (isImport ? autonomyValue : selfConsumptionValue);
this.luxValue = isImport
? gridValue > 0
? gridValue
: 0 // import watts, value must be positive
: gridValue < 0
? -gridValue
: 0; // export watts, value must be negative
break;
}
case 'Load': {
const loadValue = Math.abs(data.Site.P_Load);
this.brightnessValue = 100;
this.onValue = loadValue > 0;
this.luxValue = loadValue;
break;
}
case 'PV': {
const pvValue = data.Site.P_PV;
this.brightnessValue = this.pvMaxPower
? Math.min(
((pvValue ?? 0) / this.pvMaxPower) * 100, // calculate PV output as a percentage of PV max power
100,
) // cap to 100%
: 100;
this.onValue = pvValue !== null;
this.luxValue = pvValue ?? 0;
break;
}
case 'Battery %': {
// if the site has multiple inverters, average all the inverter SOCs
const socs = Object.values(data.Inverters).map((inv) => inv.SOC ?? 0);
const socAvg = socs.reduce((a, b) => a + b, 0) / socs.length;
this.brightnessValue = socAvg;
this.onValue = socAvg > 0;
break;
}
case 'Battery charging': {
const isCharging = batteryState === 'charging';
this.brightnessValue = isCharging ? 100 : 0;
this.onValue = isCharging;
this.luxValue = Math.abs(batteryValue);
break;
}
case 'Battery discharging': {
const isDischarging = batteryState === 'discharging';
this.brightnessValue = isDischarging ? 100 : 0;
this.onValue = isDischarging;
this.luxValue = Math.abs(batteryValue);
break;
}
}
} else {
const data = (await this.froniusApi.getPowerFlowRealtimeData())?.Body.Data;

if (!data) {
this.onValue = new Error('Error fetching value');
this.brightnessValue = new Error('Error fetching value');
this.luxValue = new Error('Error fetching value');
return;
}

// P_Akku should be positive when discharging and negative when charging
const batteryValue = data.Site.P_Akku ?? 0;
const batteryState =
batteryValue < 0 ? 'charging' : batteryValue > 0 ? 'discharging' : 'idle';

switch (this.metering) {
case 'Export':
case 'Import': {
const gridValue = data.Site.P_Grid;
const autonomyValue = data.Site.rel_Autonomy;
const selfConsumptionValue = data.Site.rel_SelfConsumption || 100;
const isImport = this.metering === 'Import';

this.onValue =
// on/off is calculated whether autonomy/selfConsumption is less than 100
(isImport ? autonomyValue : selfConsumptionValue) < 100;
this.brightnessValue =
// percentage of import/export is calculated from 100 - autonomy/selfConsumption
100 - (isImport ? autonomyValue : selfConsumptionValue);
this.luxValue = isImport
? gridValue > 0
? gridValue
: 0 // import watts, value must be positive
: gridValue < 0
? -gridValue
: 0; // export watts, value must be negative
break;
}
case 'Load': {
const loadValue = Math.abs(data.Site.P_Load);
this.brightnessValue = 100;
this.onValue = loadValue > 0;
this.luxValue = loadValue;
break;
}
case 'PV': {
const pvValue = data.Site.P_PV;
this.brightnessValue = this.pvMaxPower
? Math.min(
((pvValue ?? 0) / this.pvMaxPower) * 100, // calculate PV output as a percentage of PV max power
100,
) // cap to 100%
: 100;
this.onValue = pvValue !== null;
this.luxValue = pvValue ?? 0;
break;
}
case 'Battery %': {
// if the site has multiple inverters, average all the inverter SOCs
const socs = Object.values(data.Inverters).map((inv) => inv.SOC ?? 0);
const socAvg = socs.reduce((a, b) => a + b, 0) / socs.length;
this.brightnessValue = socAvg;
this.onValue = socAvg > 0;
break;
}
case 'Battery charging': {
const isCharging = batteryState === 'charging';
this.brightnessValue = isCharging ? 100 : 0;
this.onValue = isCharging;
this.luxValue = Math.abs(batteryValue);
break;
}
case 'Battery discharging': {
const isDischarging = batteryState === 'discharging';
this.brightnessValue = isDischarging ? 100 : 0;
this.onValue = isDischarging;
this.luxValue = Math.abs(batteryValue);
break;
}
}
}

Expand Down
Loading
Loading