Ik heb een verbeterde regeling gemaakt voor mijn laad/ontlaad script.. In de vorige versie werkt ik met " de vier duurste" en " vier goedkoopste". uren per 12 uur blok. Maar er zijn me te veel dagen met geen twee pieken, maar 1 piek, of zelfs helemaal geen piek. Dat werkte op zich ook , maar kon beter... Ik heb nu een ander uitgangspunt gekozen..
Ook weer een staaltje vibecoden met chatgtp, ik heb de resultaten gecheckt en een lijkt prima te werken:
Trots op me @
Twan_V ? ;-)
———————————————————
Zo werkt het:
🔹 1. Prijzen van morgen ophalen
Het script haalt de Tibber-uurprijzen op voor de volgende dag. Als dat mislukt of als er geen 24 prijzen beschikbaar zijn, zet het script automatisch een speciale foutvariabele op true en schakelt alles veilig naar “Hold”.
🔹 2. Beste laad- en ontlaaduur bepalen
De prijzen worden gesorteerd van laag naar hoog.
Daarna berekent het script voor k = 1 t/m 12 steeds:
• de k goedkoopste uren
• de k duurste uren
• en het percentage prijsverschil tussen die twee sets
🔹 3. Dynamische drempel (instelbaar)
Ik heb een variabele waarin ik zelf een drempel instel, bijv. 20%.
Alleen als het prijsverschil tussen goedkoop en duur boven die drempel ligt, is het economisch zinvol om te laden/ontladen.
Het script zoekt vervolgens de grootste k waarbij dat nog steeds rendabel is.
Dus als 8 goedkoopste en 8 duurste uren samen >20% verschil geven, maar 9 niet meer → dan wordt k=8 gekozen.
🔹 4. Uur-variabelen zetten
Voor elk uur van de dag bestaat een variabele zoals:
• Dynprice_00
• Dynprice_01
• …
• Dynprice_23
Het script vult die met:
• “Charge” → in de goedkoopste k uren
• “Discharge” → in de duurste k uren
• “Hold” → in de overige uren
Deze variabelen kunnen daarna gewoon in normale Homey-flows gebruikt worden om apparaten en de batterij daadwerkelijk te schakelen.
🔹 5. Veiligheid ingebouwd
Als er iets misgaat (bijv. geen data of onverwachte waarden):
• alle 24 uurvariabelen worden op “Hold” gezet
• de error-variabele wordt op true gezet
• Homey kan daarop reageren of loggen
🔹 Resultaat
Elke dag krijg ik automatisch:
• de beste laaduren
• de beste ontlaaduren
• optimale arbitrage op basis van echte data
• en volledige controle in Homey-flows
De code:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
| **
* HomeyScript – Dynamische uurstrategie (maximale k, som-variant)
* Kijkt altijd naar de prijzen van MORGEN.
*
* Variabelen:
* - Dynprice_00 t/m Dynprice_23 (string: "Charge", "Discharge", "Hold")
* - Sessy_ontlaad_drempel (percentage, numeriek)
* - ontladen_error (boolean)
*/
const TOKEN = 'VUL HIER JE TIBBER API CODE IN';
const query = `
{
viewer {
homes {
currentSubscription {
priceInfo {
tomorrow {
startsAt
total
}
}
}
}
}
}`;
async function main() {
const vars = await Homey.logic.getVariables();
async function setVar(name, value) {
const v = Object.values(vars).find(x => x.name === name);
if (!v) throw new Error(`Variabele '${name}' niet gevonden`);
await Homey.logic.updateVariable({ id: v.id, variable: { value } });
}
async function fail(reason) {
for (let h = 0; h < 24; h++) {
await setVar(`Dynprice_${String(h).padStart(2, '0')}`, "Hold");
}
await setVar("ontladen_error", true);
return `❌ Fout: ${reason}`;
}
// --- Tibber ophalen ---
let res;
try {
res = await fetch("https://api.tibber.com/v1-beta/gql", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer " + TOKEN
},
body: JSON.stringify({ query })
});
} catch (e) {
return fail("Netwerkfout bij ophalen Tibber");
}
const json = await res.json();
const raw = json?.data?.viewer?.homes?.[0]?.currentSubscription?.priceInfo?.tomorrow;
if (!Array.isArray(raw) || raw.length !== 24) {
return fail(`Tibber gaf ${raw?.length ?? "null"} prijsregels voor morgen i.p.v. 24`);
}
// --- Prijzen + uurindex ---
const hours = raw.map((p, idx) => ({
hour: idx,
price: parseFloat(p.total)
}));
if (hours.some(x => isNaN(x.price))) {
return fail("Onleesbare prijsdata ontvangen");
}
// --- Drempel inlezen ---
const drempelVar = Object.values(vars).find(v => v.name === "Sessy_ontlaad_drempel");
let threshold = 20;
if (drempelVar) {
const v = parseFloat(drempelVar.value);
if (!isNaN(v)) threshold = v;
}
// --- Sorteren op prijs (laag → hoog) ---
const sorted = [...hours].sort((a, b) => a.price - b.price);
// --- Maximale k bepalen op basis van SOM-diff ---
let optimalK = 0;
const debugLines = [];
for (let k = 1; k <= 12; k++) {
const lowest = sorted.slice(0, k);
const highest = sorted.slice(24 - k);
const sumLow = lowest.reduce((a, x) => a + x.price, 0);
const sumHigh = highest.reduce((a, x) => a + x.price, 0);
if (sumLow === 0) continue;
const diff = ((sumHigh - sumLow) / sumLow) * 100;
debugLines.push(
`k=${k}: sumLow=${sumLow.toFixed(4)}, sumHigh=${sumHigh.toFixed(4)}, diff=${diff.toFixed(2)}%`
);
if (diff >= threshold) {
optimalK = k; // steeds de grootste k bewaren die nog voldoet
}
}
if (optimalK === 0) {
for (let h = 0; h < 24; h++) {
await setVar(`Dynprice_${String(h).padStart(2, '0')}`, "Hold");
}
await setVar("ontladen_error", false);
return `ℹ️ Geen strategie voor morgen (verschil < ${threshold}%). Alles HOLD.\n` +
debugLines.join('\n');
}
// --- Sets van uren bepalen ---
const lowSet = new Set(sorted.slice(0, optimalK).map(x => x.hour));
const highSet = new Set(sorted.slice(24 - optimalK).map(x => x.hour));
for (let h = 0; h < 24; h++) {
const varName = `Dynprice_${String(h).padStart(2, '0')}`;
let val = "Hold";
if (lowSet.has(h)) val = "Charge";
if (highSet.has(h)) val = "Discharge";
await setVar(varName, val);
}
await setVar("ontladen_error", false);
return `✅ Strategie voor morgen: k=${optimalK}, drempel=${threshold}%.\n` +
debugLines.join('\n');
}
return main(); |