Skip to content

Instantly share code, notes, and snippets.

@sevenissimo
Last active October 1, 2025 10:28
Show Gist options
  • Select an option

  • Save sevenissimo/7df0f8e5a56a267faa071deaeb4084e3 to your computer and use it in GitHub Desktop.

Select an option

Save sevenissimo/7df0f8e5a56a267faa071deaeb4084e3 to your computer and use it in GitHub Desktop.
JavaScript snippets to download energy data (injected/withdrawn from e-distribuzione, or produced from Huawei inverter) in CSV format.
(async function() {
'use strict';
const wait = s => new Promise(fn => setTimeout(fn, Math.random() * s * 1000 + 500));
const isoStr = d => [d.getFullYear(), String(d.getMonth() + 1).padStart(2, '0'), String(d.getDate()).padStart(2, '0')].join('-');
const csvRow = (s, arr) => (s + ';' + arr.map(e => String(e).replace('--', '0').replace('.', ',')).join(';'));
const header = 'Giorno;'+Array.from({length:96}, (_, i) => new Date(i * 9e5).toLocaleTimeString('it-IT',{timeZone:'UTC'})).join(';');
let station;
async function init() {
console.log("βš™οΈ Inizializzazione script per Huawei FusionSolar...");
const match = window.location.hash.match(/NE=(\d+)/);
station = match ? match[1] : null;
if (!station) {
return console.error("❌ Errore: station non trovato nell'URL hash (#NE=...). Seleziona un impianto.");
}
console.log(`βœ… Inizializzazione completata. Impianto: ${station}`);
}
async function fetchMonth(year, month) {
if (!station) return [];
const date = new Date(year, month - 1, 1); // MonthIndex [0..11]
const end = new Date(year, month, 1); // EndOfMonth
const rows = [];
console.log(`➑️ Recupero dati per ${year}-${String(month).padStart(2, '0')}...`);
while (date < end) {
const d = isoStr(date); // format YYYY-MM-DD
const url = `https://uni003eu5.fusionsolar.huawei.com/rest/pvms/web/station/v3/overview/energy-balance`
+ `?stationDn=NE%3D${station}&timeDim=2&queryTime=${date.getTime()}&timeZone=2.0&timeZoneStr=Europe/Rome`
+ `&_=${Date.now()}&dateStr=${d}%2000%3A00%3A00`;
try {
const resp = await fetch(url);
if (!resp.ok) throw new Error(`Errore HTTP: ${resp.status}`);
const json = await resp.json();
if (json.success && json.data?.productPower) {
const power = json.data.productPower;
const energy = [];
// Converte i dati di potenza (kW) media per 5 minuti
// in energia totale (kWh) per il quarto d'ora: (P1+P2+P3) * (5/60 min)
for (let i=0; i < power.length; i += 3) {
energy.push((power.slice(i, i+3).reduce((sum, n) => sum + (parseFloat(n)||0), 0) / 12).toFixed(3));
}
rows.push(csvRow(d, energy));
} else {
console.warn(`❕ Nessun dato valido per ${d}.\n\t ${json.failReason}`);
}
} catch (error) {
console.error(`❌ Errore per ${d}:`, error.message);
return [];
}
date.setDate(date.getDate() + 1); // Next day
await wait(0.5);
}
return rows;
}
window.downloadMonth = async function(year, month) {
const d = `${year}-${String(month).padStart(2, '0')}`; // format YYYY-MM
console.log(`πŸ“¦ Avvio download per ${d}`);
const rows = await fetchMonth(year, month);
if (rows.length > 0) {
rows.unshift(header);
const a = document.createElement('a');
a.href = URL.createObjectURL(
new Blob([ rows.join('\n') ],
{ type:'text/csv;charset=utf-8;' }) );
a.download = `${d}-prodotta.csv`;
a.click();
URL.revokeObjectURL(a.href);
console.log(`\t βœ… Download avviato...`);
} else {
console.log(`\t ❕ Nessun dato trovato per ${d}.`);
}
console.log(`πŸŽ‰ Download per ${d} completato`);
}
window.downloadSince = async function(year, month) {
const date = new Date(year, month - 1, 1);
const today = new Date();
console.log("πŸš€ Avvio download massivo");
while (date <= today) {
await window.downloadMonth(date.getFullYear(), date.getMonth() + 1);
date.setMonth(date.getMonth() + 1); // Next month
}
console.log("🏁 Processo di download massivo completato!");
}
await init();
})();
(async function() {
'use strict';
const wait = s => new Promise(fn => setTimeout(fn, Math.random() * s * 1000 + 500));
const isoStr = d => [d.getFullYear(), String(d.getMonth() + 1).padStart(2, '0'), String(d.getDate()).padStart(2, '0')].join('-');
const csvRow = (s, arr) => (s + ';' + arr.map(e => String(e).replace('.', ',')).join(';'));
const header = 'Giorno;'+Array.from({length:96}, (_, i) => new Date(i * 9e5).toLocaleTimeString('it-IT',{timeZone:'UTC'})).join(';');
let token, context;
async function init() {
console.log("βš™οΈ Inizializzazione in corso...");
if (typeof aura === 'undefined') {
return console.error("❌ Errore: Framework Aura non trovato.");
}
token = localStorage.getItem('$AuraClientService.token$siteforce:communityApp');
if (!token) {
return console.error("❌ Errore: Token non trovato in Local Storage.");
}
const rawContext = aura.getContext();
context = {
mode: "PROD",
fwuid: rawContext.Hr,
app: "siteforce:communityApp",
loaded: rawContext.loaded,
dn: [],
globals: { srcdoc: true },
uad: true
};
console.log("βœ… Inizializzazione completata.");
}
async function fetchMonth(year, month, type) {
if (!token) return [];
const message = {
actions: [{
id: `${Math.random()};a`,
descriptor: 'apex://PED_CurveDiCaricoController/ACTION$QueryLoadProfile',
callingDescriptor: 'markup://c:PED_Curva_di_carico_main',
params: {
Startdate: isoStr(new Date(year, month - 1, 1)), // ISO format YYYY-MM-DD
Enddate: isoStr(new Date(year, month, 0)), // ISO format YYYY-MM-DD
Magnitude: type,
isDelegate: false },
longRunning: true
}],
};
const payload = {
'message': JSON.stringify(message),
'aura.context': JSON.stringify(context),
'aura.pageURI': window.location.pathname,
'aura.token': token,
};
try {
const resp = await fetch('/PortaleClienti/s/sfsites/aura?r=1&other.PED_CurveDiCarico.QueryLoadProfile=1', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
body: new URLSearchParams(payload).toString(),
});
const text = await resp.text();
const json = JSON.parse(text.replace("while(1);", "").replace("*/", ""));
const action = json.actions?.[0];
if (action?.state !== 'SUCCESS') throw new Error(action?.error?.[0]?.message || 'Risposta non valida.');
const data = action.returnValue.MappaDailyLoadProfile;
if ( !data || Object.keys(data).length === 0 ) return [];
return Object.keys(data).map(key => { // format YYYYMM-DD
return csvRow( `${key.substring(0, 4)}-${key.substring(4, 9)}`, // ISO format YYYY-MM-DD
Object.values( data[key] ));
});
} catch (error) {
console.error(`❌ Errore per ${year}-${month} (${type}):`, error.message);
return [];
}
}
window.downloadMonth = async function(year, month) {
const types = { 'A+': 'prelevata', 'A-': 'immessa' };
const d = `${year}-${String(month).padStart(2, '0')}`; // format YYYY-MM
console.log(`πŸ“¦ Avvio download per ${d}`);
for (const [type, desc] of Object.entries(types)) {
console.log(`\t ➑️ Recupero energia ${desc}...`);
const rows = await fetchMonth(year, month, type);
if (rows.length > 0) {
rows.unshift(header);
const a = document.createElement('a');
a.href = URL.createObjectURL(
new Blob([ rows.join('\n') ],
{ type:'text/csv;charset=utf-8;' }) );
a.download = `${d}-${desc}.csv`;
a.click();
URL.revokeObjectURL(a.href);
console.log(`\t βœ… Download avviato...`);
} else {
console.log(`\t ❕ Nessun dato trovato per energia ${desc}.`);
}
await wait(2);
}
console.log(`πŸŽ‰ Download per ${d} completato.`);
}
window.downloadSince = async function(year, month) {
const date = new Date(year, month - 1, 1); // MonthIndex [0..11]
const today = new Date();
console.log("πŸš€ Avvio download massivo.");
while (date <= today) {
await window.downloadMonth(date.getFullYear(), date.getMonth() + 1);
date.setMonth(date.getMonth() + 1);
}
console.log("🏁 Processo di download massivo completato!");
}
await init();
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment