Skip to content

Instantly share code, notes, and snippets.

@dnoegel
Last active June 11, 2026 06:00
Show Gist options
  • Select an option

  • Save dnoegel/c6cb7f176d25199c0575dce97ee87253 to your computer and use it in GitHub Desktop.

Select an option

Save dnoegel/c6cb7f176d25199c0575dce97ee87253 to your computer and use it in GitHub Desktop.
Typescript Modbus Iobroker
var dec, number, bitPosition, einspeisung, timeout2, timeout;
// Beschreibe diese Funktion …
async function extractRunningStates(dec) {
function decToBit(dec, bitPosition) {
return (dec & (1 << bitPosition)) === 0 ? false : true;
}
// Creates a state if it does not exist yet
function createStateIfNotExists(state, name)
{
// createState(state, 0, true, {name: name, type: "number", role: 'value'}, function () {});
if ( !existsState(state )) {
createState(state, 0, false, {name: name, type: "number", role: 'value'}, function () {});
}
}
// Create states
createStateIfNotExists("0_userdata.0.PV.PvToLoad", "Power from PV to load");
createStateIfNotExists("0_userdata.0.PV.PvToBat", "Power from PV to bat");
createStateIfNotExists("0_userdata.0.PV.PvToGrid", "Power from PV to grid");
createStateIfNotExists("0_userdata.0.PV.BatToLoad", "Power from Bat to load");
createStateIfNotExists("0_userdata.0.PV.BatToGrid", "Power from Bat to grid");
createStateIfNotExists("0_userdata.0.PV.GridToLoad", "Power from grid to load");
createStateIfNotExists("0_userdata.0.PV.GridToBat", "Power from Gridto battery");
createStateIfNotExists("0_userdata.0.PV.SignedBat", "Battery but with minus sign for charging");
// Decode running state flags
$powerGeneratedFromPV = decToBit(dec, 0);
$batteryCharging = decToBit(dec, 1);
$batteryDischarging = decToBit(dec, 2);
$loadActive = decToBit(dec, 3);
$powerFeedIntoGrid = decToBit(dec, 4);
$powerImportFromGrid = decToBit(dec, 5);
$powerGeneratedFromLoad = decToBit(dec, 7);
// Save running state more speaking fields
setState("0_userdata.0.PV.PowerGeneratedFromPV", $powerGeneratedFromPV, true);
setState("0_userdata.0.PV.BatteryCharging", $batteryCharging, true);
setState("0_userdata.0.PV.BatteryDischarging", $batteryDischarging, true);
setState("0_userdata.0.PV.LoadActive", $loadActive, true);
setState("0_userdata.0.PV.PowerFeedIntoGrid", $powerFeedIntoGrid, true);
setState("0_userdata.0.PV.PowerImportFromGrid", $powerImportFromGrid, true);
setState("0_userdata.0.PV.PowerGeneratedFromLoad", $powerGeneratedFromLoad, true);
// Read current power levels of bat, pv, load and grid
$load = getState("modbus.0.inputRegisters.13007_Load_power_").val;
$grid = getState("modbus.0.inputRegisters.13009_Export_power").val;
$pv = getState("modbus.0.inputRegisters.5016_Total_DC_Power").val;
//$pv = getState("modbus.0.inputRegisters.13033_Total_active_power").val; // This register gives more plausible values regarding pvToGrid and the actual grid values. But was wrong by a few 100 watts in the mornung
$battery = Math.abs(getState('modbus.0.inputRegisters.13021_Battery_power_').val);
// Write signed bat
setState("0_userdata.0.PV.SignedBat", $batteryCharging ? $battery * -1 : $battery, true);
// Calculate PV
$pvToBat = 0;
$pvToLoad = 0;
$pvToGrid = 0;
$loadRemaining = $load;
if ($powerGeneratedFromPV) {
$remaining = $pv;
if ($remaining > $loadRemaining) {
$pvToLoad = $loadRemaining;
$remaining -= $load;
$loadRemaining = 0;
} else {
$pvToLoad = $remaining;
$loadRemaining = $load - $remaining;
$remaining = 0;
}
if ($batteryCharging) {
if ($remaining > $battery) {
$pvToBat = $battery;
$remaining -= $battery;
} else {
$pvToBat = $remaining;
$remaining = 0;
}
}
if ($grid > 0) {
$pvToGrid = Math.min($grid, $remaining);
}
}
setState("0_userdata.0.PV.PvToLoad", $pvToLoad);
setState("0_userdata.0.PV.PvToBat", $pvToBat);
setState("0_userdata.0.PV.PvToGrid", $pvToGrid);
// Calculate Bat
$batToLoad = 0;
$batToGrid = 0;
if ($batteryDischarging) {
$remaining = $battery;
if ($remaining > $loadRemaining) {
$batToLoad = $loadRemaining;
$remaining -= $loadRemaining;
$loadRemaining = 0;
} else {
$batToLoad = $remaining;
$loadRemaining -= $remaining;
$remaining = 0;
}
if ($powerFeedIntoGrid) {
$batToGrid = Math.min($grid, $remaining);
}
}
setState("0_userdata.0.PV.BatToLoad", $batToLoad);
setState("0_userdata.0.PV.BatToGrid", $batToGrid);
// Calculate grid
$gridToBat = 0;
$gridToLoad = 0;
if ($grid < 0) {
$remaining = Math.abs($grid);
if ($remaining > $loadRemaining) {
$gridToLoad = $loadRemaining;
$remaining -= $loadRemaining;
$loadRemaining = 0;
} else {
$gridToLoad = $remaining;
$loadRemaining -= $remaining;
$remaining = 0;
}
if ($batteryCharging) {
$gridToBat = Math.min($battery, $remaining);;
}
}
setState("0_userdata.0.PV.GridToLoad", $gridToLoad);
setState("0_userdata.0.PV.GridToBat", $gridToBat);
}
// Beschreibe diese Funktion …
async function getBit(number, bitPosition) {
return (number & (1 << bitPosition)) === 0 ? 0 : 1;
}
// Beschreibe diese Funktion …
async function debug_output(dec) {
// create some debug messages so we can check state changes in the log after the fact
console.log(('Power generation' + String(await getBit(dec, 0))));
console.log(('Battery charging' + String(await getBit(dec, 1))));
console.log(('Battery discharging' + String(await getBit(dec, 2))));
console.log(('Positive load' + String(await getBit(dec, 3))));
console.log(('Power feed into the grid' + String(await getBit(dec, 4))));
console.log(('Power from the grid' + String(await getBit(dec, 5))));
console.log(('Power from load' + String(await getBit(dec, 7))));
}
// MPPT Leistung ausmultiplizieren
on({id: [].concat(['modbus.0.inputRegisters.5010_MPPT_1_Voltage']).concat(['modbus.0.inputRegisters.5012_MPPT_2_Voltage']), change: "ne"}, async function (obj) {
setState("0_userdata.0.PV.MPPT1"/*MPPT1*/, (Math.round(parseFloat(getState("modbus.0.inputRegisters.5010_MPPT_1_Voltage").val) * getState("modbus.0.inputRegisters.5011_MPPT_1_Current").val)), true);
setState("0_userdata.0.PV.MPPT2"/*MPPT2*/, (Math.round(parseFloat(getState("modbus.0.inputRegisters.5012_MPPT_2_Voltage").val) * getState("modbus.0.inputRegisters.5013_MPPT_2_Current").val)), true);
});
// Bei Änderung von Status oder Wirkleistung
// Running state + Leistungen extrahieren
on({id: [].concat(['modbus.0.inputRegisters.13000_Running_State']).concat(['modbus.0.inputRegisters.13007_Load_power_']), change: "ne"}, async function (obj) {
await wait(50);
await extractRunningStates(getState("modbus.0.inputRegisters.13000_Running_State").val);
setState("0_userdata.0.PV.EnergyConsumptionToday"/*EnergyConsumptionToday*/, Math.round(([getState("modbus.0.inputRegisters.13016_Daily_direct__Energy_Consumption").val, getState("modbus.0.inputRegisters.13035_Daily_Import_Energy").val, getState("modbus.0.inputRegisters.13025_Daily_battery_discharge_Energy").val].reduce(function(x, y) {return x + y;}))*10)/10, true);
});
// Alle 10 Minuten: Running States ausgeben
schedule("*/10 * * * *", async function () {
});
await debug_output(getState("modbus.0.inputRegisters.13000_Running_State").val);
@dnoegel

dnoegel commented Jul 25, 2025

Copy link
Copy Markdown
Author

Thanks, I updated it

@fredivanwensen

Copy link
Copy Markdown

I have updated the Sungrow firmware, but the 13021 still seems to be a UINT. Changing it to INT sends me funny information. Can you tell me which Firmware you have installed?
How was the code previously? Using 32.768 as Zero?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment