@enerlence/suntropy-cli 0.11.7 → 0.11.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin/suntropy.js
CHANGED
|
@@ -1324,7 +1324,11 @@ function createResourceCommands(cfg) {
|
|
|
1324
1324
|
const client = createServiceClient(service, global);
|
|
1325
1325
|
const body = parseData(opts.data);
|
|
1326
1326
|
if (cfg.putBodyOnly) {
|
|
1327
|
-
const
|
|
1327
|
+
const numericId = Number(id);
|
|
1328
|
+
const res = await client.put(cfg.basePath, {
|
|
1329
|
+
...body,
|
|
1330
|
+
[cfg.idField]: Number.isFinite(numericId) ? numericId : id
|
|
1331
|
+
});
|
|
1328
1332
|
output(res.data, global);
|
|
1329
1333
|
} else {
|
|
1330
1334
|
const res = await client.put(`${cfg.basePath}/${id}`, body);
|
|
@@ -2141,6 +2145,21 @@ function updateStudy(filePath, updater) {
|
|
|
2141
2145
|
cascadeResets
|
|
2142
2146
|
};
|
|
2143
2147
|
}
|
|
2148
|
+
function distributePanels(surfaces, total) {
|
|
2149
|
+
if (!surfaces.length) return [];
|
|
2150
|
+
if (surfaces.length === 1) return [total];
|
|
2151
|
+
const current = surfaces.map((s) => Number(s.panelNumber) || 0);
|
|
2152
|
+
const currentTotal = current.reduce((a, b) => a + b, 0);
|
|
2153
|
+
const weights = currentTotal > 0 ? current.map((n) => n / currentTotal) : surfaces.map(() => 1 / surfaces.length);
|
|
2154
|
+
const counts = weights.map((w) => Math.floor(total * w));
|
|
2155
|
+
let remainder = total - counts.reduce((a, b) => a + b, 0);
|
|
2156
|
+
const order = counts.map((n, i) => ({ n, i })).sort((a, b) => b.n - a.n).map((x) => x.i);
|
|
2157
|
+
for (let k = 0; remainder > 0; k = (k + 1) % order.length) {
|
|
2158
|
+
counts[order[k]] += 1;
|
|
2159
|
+
remainder -= 1;
|
|
2160
|
+
}
|
|
2161
|
+
return counts;
|
|
2162
|
+
}
|
|
2144
2163
|
function deepMerge(target, source) {
|
|
2145
2164
|
for (const key of Object.keys(source)) {
|
|
2146
2165
|
const sv = source[key];
|
|
@@ -2559,26 +2578,45 @@ Examples:
|
|
|
2559
2578
|
}
|
|
2560
2579
|
});
|
|
2561
2580
|
set.command("panel").description(
|
|
2562
|
-
'Set solar panel for the study. Auto-sets peakPowerIntroductionMode to "solarPanel".\nFetches full panel data from inventory.\nExample: suntropy studies set panel --file study.json --panel-id 456 --panels-count 12'
|
|
2563
|
-
).option("--file <path>", "Study file path").requiredOption("--panel-id <n>", "Solar panel ID from inventory").option("--panels-count <n>", "
|
|
2581
|
+
'Set solar panel for the study. Auto-sets peakPowerIntroductionMode to "solarPanel".\nFetches full panel data from inventory.\n--panels-count is the TOTAL for the study: it is spread over the surfaces keeping\ntheir current proportion (use --surface-index to target a single surface).\nThe response lists the resulting panelNumber per surface.\nExample: suntropy studies set panel --file study.json --panel-id 456 --panels-count 12'
|
|
2582
|
+
).option("--file <path>", "Study file path").requiredOption("--panel-id <n>", "Solar panel ID from inventory").option("--panels-count <n>", "TOTAL number of panels of the study, spread over its surfaces keeping their current proportion").option("--surface-index <n>", "Apply --panels-count to this surface only (0-based) instead of spreading it").action(async (opts) => {
|
|
2564
2583
|
try {
|
|
2565
2584
|
const global = getGlobalOpts7(studies);
|
|
2566
2585
|
const solarClient = createServiceClient("solar", global);
|
|
2567
2586
|
const panelRes = await solarClient.get(`/solar-panels/${opts.panelId}`);
|
|
2568
2587
|
const panel = panelRes.data;
|
|
2588
|
+
let distribution = [];
|
|
2569
2589
|
const result = updateStudy(resolveFile(opts), (study) => {
|
|
2570
2590
|
study.solarPanel = panel;
|
|
2571
2591
|
study.peakPowerIntroductionMode = "solarPanel";
|
|
2572
2592
|
study.solarKit = void 0;
|
|
2573
2593
|
if (opts.panelsCount) {
|
|
2574
2594
|
const surfaces = study.surfaces;
|
|
2595
|
+
const total = parseInt(opts.panelsCount);
|
|
2575
2596
|
if (surfaces?.length) {
|
|
2576
|
-
|
|
2597
|
+
if (opts.surfaceIndex !== void 0) {
|
|
2598
|
+
const idx = parseInt(opts.surfaceIndex);
|
|
2599
|
+
if (!Number.isInteger(idx) || idx < 0 || idx >= surfaces.length) {
|
|
2600
|
+
throw new Error(
|
|
2601
|
+
`--surface-index ${opts.surfaceIndex} is out of range: the study has ${surfaces.length} surface(s)`
|
|
2602
|
+
);
|
|
2603
|
+
}
|
|
2604
|
+
surfaces[idx].panelNumber = total;
|
|
2605
|
+
} else {
|
|
2606
|
+
const counts = distributePanels(surfaces, total);
|
|
2607
|
+
surfaces.forEach((surface, i) => {
|
|
2608
|
+
surface.panelNumber = counts[i];
|
|
2609
|
+
});
|
|
2610
|
+
}
|
|
2611
|
+
distribution = surfaces.map((surface, i) => ({
|
|
2612
|
+
surfaceIndex: i,
|
|
2613
|
+
panelNumber: Number(surface.panelNumber) || 0
|
|
2614
|
+
}));
|
|
2577
2615
|
}
|
|
2578
2616
|
}
|
|
2579
2617
|
return void 0;
|
|
2580
2618
|
});
|
|
2581
|
-
output(result, global);
|
|
2619
|
+
output(distribution.length ? { ...result, surfaces: distribution } : result, global);
|
|
2582
2620
|
} catch (err) {
|
|
2583
2621
|
outputError(handleApiError(err));
|
|
2584
2622
|
}
|
|
@@ -2610,7 +2648,7 @@ Examples:
|
|
|
2610
2648
|
}
|
|
2611
2649
|
});
|
|
2612
2650
|
set.command("inverter").description(
|
|
2613
|
-
"Set inverter(s) for the study (when using solarPanel mode).\nExample: suntropy studies set inverter --file study.json --inverter-id 789"
|
|
2651
|
+
"Set inverter(s) for the study (when using solarPanel mode).\nREPLACES the whole list: pass every inverter of the installation, comma-separated,\nrepeating an id as many times as units there are. The response reports how many\nwere there before and how many are there now.\nExample: suntropy studies set inverter --file study.json --inverter-id 789,789"
|
|
2614
2652
|
).option("--file <path>", "Study file path").requiredOption("--inverter-id <ids>", "Inverter ID(s), comma-separated for multiple").action(async (opts) => {
|
|
2615
2653
|
try {
|
|
2616
2654
|
const global = getGlobalOpts7(studies);
|
|
@@ -2621,11 +2659,13 @@ Examples:
|
|
|
2621
2659
|
const res = await solarClient.get(`/solar-inverter/${id}`);
|
|
2622
2660
|
inverters.push(res.data);
|
|
2623
2661
|
}
|
|
2662
|
+
let previousCount = 0;
|
|
2624
2663
|
const result = updateStudy(resolveFile(opts), (study) => {
|
|
2664
|
+
previousCount = study.solarInverters?.length ?? 0;
|
|
2625
2665
|
study.solarInverters = inverters;
|
|
2626
2666
|
return void 0;
|
|
2627
2667
|
});
|
|
2628
|
-
output(result, global);
|
|
2668
|
+
output({ ...result, inverters: { before: previousCount, after: inverters.length } }, global);
|
|
2629
2669
|
} catch (err) {
|
|
2630
2670
|
outputError(handleApiError(err));
|
|
2631
2671
|
}
|
|
@@ -3329,7 +3369,7 @@ async function generateFromProfile(global, tariff, market, year, mode, data) {
|
|
|
3329
3369
|
if (!profile || !Array.isArray(profile) || profile.length === 0) {
|
|
3330
3370
|
throw new Error("Failed to fetch REE profiles");
|
|
3331
3371
|
}
|
|
3332
|
-
const { applyProfileToConsumption, ConsumptionIntroductionModes } = await import("energy-types/lib/energy/calculations/applyProfileToConsumption");
|
|
3372
|
+
const { applyProfileToConsumption, ConsumptionIntroductionModes } = await import("energy-types/lib/energy/calculations/applyProfileToConsumption.js");
|
|
3333
3373
|
if (mode === "CONSUMPTION_BY_PERIOD") {
|
|
3334
3374
|
const tariffIdMap = { "2.0TD": 13, "3.0TD": 14, "6.1TD": 15 };
|
|
3335
3375
|
const tariffId = tariffIdMap[tariff] || 14;
|
|
@@ -4729,7 +4769,7 @@ function registerCommandProfileCommand(program2) {
|
|
|
4729
4769
|
}
|
|
4730
4770
|
|
|
4731
4771
|
// src/index.ts
|
|
4732
|
-
var CLI_VERSION = true ? "0.11.
|
|
4772
|
+
var CLI_VERSION = true ? "0.11.9" : "0.0.0-dev";
|
|
4733
4773
|
function createProgram() {
|
|
4734
4774
|
const program2 = new Command4();
|
|
4735
4775
|
program2.name("suntropy").description("Agent-first CLI for Suntropy solar platform. Optimized for programmatic data manipulation and progressive exploration.").version(CLI_VERSION).option("--format <format>", "Output format: json (default), human, csv", "json").option("--fields <fields>", "Comma-separated fields to include in output").option("--server <url>", "Override API server URL").option("--token <jwt>", "Override authentication token").option("--profile <name>", "Use a specific config profile").option("--verbose", "Show HTTP request/response details on stderr").option("--quiet", "Suppress non-data output").option("--save <file>", "Save output to file (also writes to stdout)");
|