Skip to content

Instantly share code, notes, and snippets.

@beaugunderson
Created August 6, 2023 08:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save beaugunderson/b6123a633051208865404f548d1d37a2 to your computer and use it in GitHub Desktop.
Save beaugunderson/b6123a633051208865404f548d1d37a2 to your computer and use it in GitHub Desktop.
#!/usr/bin/env node
const Modbus = require('@glanglois/jsmodbus');
const net = require('net');
const wifiName = require('wifi-name');
// const BATTERY_ITEMS = [
// ['battery voltage', 259, 100],
// ['battery current', 261, 10],
// ];
function fixUnderflow(value) {
return value >= 60000 ? value - 65535 : value;
}
const VE_BUS_ITEMS = [
['grid voltage', 3, (value) => `${value / 10}V`],
['grid current', 6, (value) => `${fixUnderflow(value) / 10}A`],
['grid frequency', 9, (value) => `${fixUnderflow(value) / 100}hz`],
['grid power', 12, (value) => `${fixUnderflow(value) / 0.1}W`]
];
const SOLAR_ITEMS = [
['PV voltage', 776, (value) => `${value / 100}V`],
['PV current', 777, (value) => `${value / 10}A`],
['PV power', 789, (value) => `${value / 10}W`],
];
const SYSTEM_ITEMS = [
['battery voltage', 840, (value) => `${value / 10}V`],
['battery current', 841, (value) => (fixUnderflow(value) / 10) + 'A'],
['battery power', 842, (value) => fixUnderflow(value) + 'W'],
['battery SoC', 843, (value) => `${value}%`],
['battery state', 844, (value) => value],
['battery consumed', 845, (value) => (value / -10) + 'AH'],
['battery time to go', 846, (value) => (value / 0.01 / 60 / 60).toFixed(2) + 'h'],
['charger power', 855, (value) => value],
['DC system power', 860, (value) => fixUnderflow(value) + 'W'],
['grid power', 820, (value) => value + 'W']
// ['VE.Bus charge current', 865, value => fixUnderflow(value) / 10],
// ['VE.Bus charge power', 866, fixUnderflow],
];
function getItems(unitId, items) {
return new Promise((resolve, reject) => {
const socket = new net.Socket();
const client = new Modbus.client.TCP(socket, unitId);
const options = {
'host': 'venus.local',
'port': 502,
};
let received = {};
socket.on('error', (e) => {
socket.end();
reject(e);
});
socket.on('connect', async () => {
try {
for (const [name, address, fn] of items) {
try {
const response = await client.readHoldingRegisters(address, 1);
const value = fn(response.response.body.valuesAsArray[0]);
received[name] = value;
} catch (e) {
return reject(e);
}
}
socket.end();
if (Object.keys(received).length === items.length) {
return resolve(received);
} else {
return reject(new Error(`incorrect number of items received: ${received.length} vs. ${items.length} expected`));
}
} catch (e) {
socket.end();
return reject(e);
}
});
try {
socket.connect(options);
} catch (e) {
reject(e);
}
});
}
function spacer(count) {
let spacer = '';
for (let i = 0; i < count; i++) {
spacer += ' ';
}
return spacer;
}
(async function main() {
try {
const ssid = await wifiName();
if (ssid !== 'Funkentelechy') {
console.log('');
process.exit(0);
}
} catch (e) {
console.log('');
process.exit(0);
}
try {
const results = {
...(await getItems(100, SYSTEM_ITEMS)),
...(await getItems(224, SOLAR_ITEMS)),
...(await getItems(227, VE_BUS_ITEMS)),
};
const longest = Math.max(...(Object.keys(results).map(item => item.length))) + 1;
if (parseInt(results['grid voltage'], 10) > 100) {
console.log(`⚡ ${results['grid voltage']} / ${results['grid current']} / ${results['grid power']}`);
} else {
console.log(`🏕 ${results['battery current']} / ${results['battery SoC']} / ${results['battery time to go']}`);
}
console.log('---');
console.log('refresh | refresh=true | font="DejaVuSansMono Nerd Font"');
for (const key of Object.keys(results)) {
console.log(`${key}:${spacer(longest - key.length)}${results[key]} | font="DejaVuSansMono Nerd Font"`);
}
} catch (e) {
console.error(`⚠️ ${e.code}`);
process.exit(0);
}
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment