Zoals sommige weten heb ik een slimme schakeling zitten (mag die helaas niet delen hier want... regels)
Om het verdienmodel uit te leggen heb je een paar issues en ik zal uitleggen waar je rekening mee moet houden. Want gedurende de tijd heb ik heel veel bij geprogrammeerd in HASS om dit te monitoren.
Je hebt 2 contracten, voor mij 1 vast 47 cent (22ct TLV) en 1 variabel
Je hebt daardoor ook 2 salderingsperioden. 1 per meter, niet altijd heb je contracten gelijktijdig afgesloten. Kortom, je moet rekening houden met terugleveren. Wat ik heb gedaan nu is dat ik geforceerd teruglever op de eerste saldering die afloopt. Voorheen had ik 61 ct TLV, dat was ruim hoger dan variabel incl. belasting.
Echter loopt nu variabel saldering af in februari. Wegens overproductie lever ik nu dus geforceerd terug op variabel, waar bij 22ct de drempel is (de TLV van vast) Komt hij daaronder incl. belastingen schakelt het systeem de zaak om. de laatste keer was dit 24 november in de nacht, dus alleen mijn verbruik is dan goedkoper dan 22 cent. Maar het komt echt nauwelijks meer voor. (nou werkt het weer ook echt niet mee)
Na de salderingsperiode begint de teller voor variabel weer op 0 en dan kan het zomaar weer anders zijn. Maar dan moet ik vol terugleveren weer op reële tarieven.
Kortom, je moet soms niet vergeten dat je salderingsperiodes ook nog meetellen in wat je uiteindelijk betaald.
Ik snap die drempels dan ook niet zo die hier genoemd worden?
Natuurlijk is het anders als je geen overproductie hebt of een accu hebt. Maar bij een overproductie is de TLV van het vaste contract leidend als drempel in schakelen. Wat ik wel wil is minimaal 3 uur aan een stuk goedkoper en minimaal 1,5 ct verschil om het aantal schakelingen voor nauwelijkse winsten te voorkomen.
Anyway, ik heb een sensor gebouwd die het volgende gebruikt:
- PAP: Vaste prijs en TLV, (zijn text velden)
- SAP Prijs ex belastingen en incl belastingen (2 nordpool sensors)
- Salderings status (dus werkelijk verbuik/teruglevering) (word met een automation berekend:)
YAML:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| alias: Netto Energie SAP Rekenining
description: ""
trigger:
- platform: state
entity_id:
- sensor.netto_amount_consumption_sap
condition: []
action:
- service: input_text.set_value
data:
value: >-
{{ ((currentstate_sap) | float) + ((((trigger.to_state.state) | float) -
((trigger.from_state.state) | float)) *
(states('sensor.geldend_tarief_sap') | float)) }}
target:
entity_id: input_text.netto_rekening_sap_test
mode: single
variables:
currentstate_sap: "{{ states('input_text.netto_rekening_sap_test') }}" |
- Status van de verbuikers en teruglevering (Wie staat op de PAP en wie op de SAP, dat zijn bij mij de ATS-en)
- Huidig tarief SAP en PAP afhankelijk van salderings status
YAML:
1
2
3
4
5
6
7
8
9
10
11
12
| - name: 'geldend_tarief_SAP'
# Set unique_id to a 32-digit random number or UUID
unique_id: '5aa7ba7e3447a8266d81071ca11129f4'
device_class: 'monetary'
state_class: 'measurement'
unit_of_measurement: 'Eur'
state: >
{% if ((states('sensor.netto_amount_consumption_sap') | float) + (states('input_text.tussenstand_kwh_verbruik_sap') | float )) < 0 %}
{{ (states('sensor.nordpool_kwh_nl_eur_4_08_0') | float ) }}
{% else %}
{{ (states('sensor.nordpool_kwh_nl_eur_3_09_0') | float ) }}
{% endif %} |
- Per meter geforceerde stand (dus TLV of ex belasting gebruiken om saldering te forceren i.v.m. verschillende salderings periodes)
Dit alles gebruik ik om een eigen versie te maken van de tarieven:
YAML:
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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
| - name: 'prices_pap_sap_switching'
# Set unique_id to a 32-digit random number or UUID
unique_id: '5da7bd7e3447a8266d81171ca11129f6'
device_class: 'monetary'
state_class: 'measurement'
unit_of_measurement: 'Eur'
state: 0
attributes:
source: >
{% set SAPOveruleActive = states.input_boolean.overrule_naar_tlv_sap_tarief.state %}
{% if (states('sensor.geldend_tarief_sap') | float) == (states('sensor.nordpool_kwh_nl_eur_4_08_0') | float) and SAPOveruleActive == 'off' %}
{% set SAPPrices = states.sensor.nordpool_kwh_nl_eur_4_08_0.attributes.raw_today %}
{% if state_attr('sensor.nordpool_kwh_nl_eur_4_08_0', 'tomorrow_valid') == true %}
{% set SAPPrices = SAPPrices + states.sensor.nordpool_kwh_nl_eur_4_08_0.attributes.raw_tomorrow %}
{% endif %}
{% else %}
{% set SAPPrices = states.sensor.nordpool_kwh_nl_eur_4_09_0.attributes.raw_today %}
{% if state_attr('sensor.nordpool_kwh_nl_eur_4_09_0', 'tomorrow_valid') == true %}
{% set SAPPrices = SAPPrices + states.sensor.nordpool_kwh_nl_eur_4_09_0.attributes.raw_tomorrow %}
{% endif %}
{% endif %}
{% set PAPOveruleActive = states.input_boolean.overrule_naar_tlv_pap_tarief.state %}
{% if PAPOveruleActive == 'on' %}
{% set PAPPrice = states('input_text.tarief_tlv') | float %}
{% else %}
{% set PAPPrice = states('sensor.geldend_tarief_pap') | float %}
{% endif %}
{% set UsageOnSap = states('input_boolean.verbruikers_op_sap') %}
{% set DeleviryOnSap = states('input_boolean.pv_op_sap') %}
{% set CorrectedHourUsagePrice = 0 %}
{% set CorrectedHourDeliveryPrice = 0 %}
{% set MinimumSaldingReached = states('binary_sensor.minimum_salding_reached')%}
{% set MinumHoursOfDiference = states('input_number.minimaal_aantal_uur_sappap_verschil') | float%}
{% set MinimumPriceDifference = states('input_text.minimaal_prijs_verschil') | float %}
{% set ns = namespace(
Corrected=[],
duration=[],
sortedduration=[],
avaragedelivery=0,
avarageusageprice=0,
switchtimes=[],
firstswitch=0,
switchwanted = false,
EndOfTrend=false,
SAPPriceHigher=false,
switched=false,
overruled=false,
enoughhours = true,
enoughpricedifference=true,
priceavaragedifference=0,
ForcedSwitchedState = false,
SwitchedWrongNow = false
) %}
{% if UsageOnSap == 'on' and DeleviryOnSap == 'off' %}
{% set ns.switched = true%}
{% else %}
{% set ns.switched = false%}
{% endif %}
{% if ns.switched and SAPPrices[now().hour].value > PAPPrice %}
{% set ns.ForcedSwitchedState = true%}
{% set ns.SwitchedWrongNow = true%}
{% elif not ns.switched and SAPPrices[now().hour].value < PAPPrice %}
{% set ns.ForcedSwitchedState = true%}
{% set ns.SwitchedWrongNow = true%}
{% endif %}
{% for i in range(0, SAPPrices | length) %}
{% set ns.switchwanted = false %}
{% set ns.priceavaragedifference = 0%}
{% if UsageOnSap == 'on' and DeleviryOnSap == 'on' %}
{% set CorrectedHourUsagePrice = SAPPrices[i].value%}
{% set CorrectedHourDeliveryPrice = SAPPrices[i].value%}
{% set ns.overruled = true%}
{% elif UsageOnSap == 'off' and DeleviryOnSap == 'off' %}
{% set CorrectedHourUsagePrice = PAPPrice%}
{% set CorrectedHourDeliveryPrice = PAPPrice%}
{% set ns.overruled = true%}
{% elif MinimumSaldingReached == 'off' %}
{% if UsageOnSap == 'on' %}
{% set CorrectedHourUsagePrice = SAPPrices[i].value%}
{%else%}
{% set CorrectedHourUsagePrice = PAPPrice%}
{%endif%}
{% if DeleviryOnSap == 'on' %}
{% set CorrectedHourDeliveryPrice = SAPPrices[i].value%}
{%else%}
{% set CorrectedHourDeliveryPrice = PAPPrice%}
{%endif%}
{% else %}
{% if SAPPrices[i].value >= PAPPrice and not ns.switched%}
{% set ns.SAPPriceHigher = true%}
{% set CorrectedHourUsagePrice = PAPPrice%}
{% set CorrectedHourDeliveryPrice = SAPPrices[i].value%}
{% set ns.ForcedSwitchedState = false%}
{% elif SAPPrices[i].value < PAPPrice and ns.switched %}
{% set ns.SAPPriceHigher = false%}
{% set CorrectedHourUsagePrice = SAPPrices[i].value%}
{% set CorrectedHourDeliveryPrice = PAPPrice%}
{% set ns.ForcedSwitchedState = false%}
{% elif SAPPrices[i].value >= PAPPrice and ns.ForcedSwitchedState %}
{% set CorrectedHourUsagePrice = SAPPrices[i].value%}
{% set CorrectedHourDeliveryPrice = PAPPrice%}
{% elif SAPPrices[i].value < PAPPrice and ns.ForcedSwitchedState %}
{% set CorrectedHourUsagePrice = PAPPrice%}
{% set CorrectedHourDeliveryPrice = SAPPrices[i].value%}
{% else %}
{% set ns.EndOfTrend = false%}
{% set ns.SAPPriceHigher = false %}
{% set ns.duration = []%}
{% if SAPPrices[i].value > PAPPrice %}
{% set ns.SAPPriceHigher = true%}
{% endif %}
{% if ns.switchwanted == false %}
{% for j in range(i, SAPPrices | length) %}
{%if SAPPrices[j].value > PAPPrice and ns.SAPPriceHigher == true and ns.EndOfTrend == false and j < (SAPPrices | length -1)%}
{% set ns.duration = ns.duration + [SAPPrices[j].value]%}
{%elif SAPPrices[j].value <= PAPPrice and ns.SAPPriceHigher == false and ns.EndOfTrend == false and j < (SAPPrices | length -1)%}
{% set ns.duration = ns.duration + [SAPPrices[j].value]%}
{%elif ns.EndOfTrend == false %}
{%if SAPPrices[j].value > PAPPrice and ns.SAPPriceHigher == true and j == SAPPrices | length%}
{% set ns.duration = ns.duration + [SAPPrices[j].value]%}
{%elif SAPPrices[j].value <= PAPPrice and ns.SAPPriceHigher == false and j == SAPPrices | length%}
{% set ns.duration = ns.duration + [SAPPrices[j].value]%}
{% endif%}
{% if ns.SAPPriceHigher%}
{% for price in ns.duration|sort(reverse = true)%}
{% if ns.sortedduration | length < MinumHoursOfDiference%}
{% set ns.sortedduration = ns.sortedduration + [price]%}
{%endif%}
{% endfor %}
{%else%}
{% for price in ns.duration|sort()%}
{% if ns.sortedduration | length < MinumHoursOfDiference%}
{% set ns.sortedduration = ns.sortedduration + [price]%}
{%endif%}
{% endfor %}
{%endif%}
{% set ns.EndOfTrend = true %}
{% set ns.priceavaragedifference = PAPPrice - (ns.sortedduration | average(default=0)) %}
{% if ns.priceavaragedifference < 0 %}
{% set ns.priceavaragedifference = -ns.priceavaragedifference%}
{%endif%}
{%set ns.enoughhours = true %}
{%set ns.enoughpricedifference = true %}
{% if ns.duration | length < MinumHoursOfDiference %}
{%set ns.enoughhours = false %}
{%endif%}
{%if ns.priceavaragedifference < MinimumPriceDifference %}
{%set ns.enoughpricedifference = false %}
{%endif%}
{%if ns.enoughpricedifference == true and ns.enoughhours == true%}
{%set ns.switchwanted = true %}
{% if ((SAPPrices[i].start | as_timestamp + (5*60)) > now() | as_timestamp)%}
{%set ns.switchtimes = ns.switchtimes + [SAPPrices[i].start | string]%}
{%endif%}
{%endif%}
{%endif%}
{% endfor %}
{% endif %}
{% if ns.switchwanted == true %}
{% if SAPPrices[i].value >= PAPPrice %}
{% set CorrectedHourUsagePrice = PAPPrice%}
{% set CorrectedHourDeliveryPrice = SAPPrices[i].value%}
{% set ns.switched = false%}
{% else %}
{% set CorrectedHourUsagePrice = SAPPrices[i].value%}
{% set CorrectedHourDeliveryPrice = PAPPrice%}
{% set ns.switched = true%}
{% endif %}
{% elif (UsageOnSap == 'off' and DeleviryOnSap == 'on') or (UsageOnSap == 'on' and DeleviryOnSap == 'off')%}
{% if SAPPrices[i].value >= PAPPrice %}
{% set CorrectedHourUsagePrice = SAPPrices[i].value%}
{% set CorrectedHourDeliveryPrice = PAPPrice%}
{% else %}
{% set CorrectedHourUsagePrice = PAPPrice%}
{% set CorrectedHourDeliveryPrice = SAPPrices[i].value%}
{% endif %}
{% else %}
{% if UsageOnSap == 'on' %}
{% set CorrectedHourUsagePrice = SAPPrices[i].value%}
{% else %}
{% set CorrectedHourUsagePrice = PAPPrice%}
{% endif %}
{% if DeleviryOnSap == 'on' %}
{% set CorrectedHourDeliveryPrice = SAPPrices[i].value%}
{% else %}
{% set CorrectedHourUsagePrice = PAPPrice%}
{% endif %}
{% endif %}
{% endif %}
{% endif %}
{%set ns.avaragedelivery = ns.avaragedelivery + CorrectedHourDeliveryPrice| float %}
{%set ns.avarageusageprice = ns.avarageusageprice + CorrectedHourUsagePrice| float %}
{% set totalpricedifference = 0 %}
{%if ns.switchwanted %}
{%set totalpricedifference = (PAPPrice - (ns.duration | average(default=0))) | round(3)%}
{% if totalpricedifference < 0 %}
{% set totalpricedifference = -totalpricedifference%}
{%endif%}
{%endif%}
{% set ns.Corrected = ns.Corrected + [{'start': SAPPrices[i].start | string,
'end': SAPPrices[i].end | string,
'usageprice':CorrectedHourUsagePrice,
'deliveryprice': CorrectedHourDeliveryPrice,
'switchthishour':ns.switchwanted,
'switchedmode':ns.switched,
'SAPPriceHigher':ns.SAPPriceHigher,
'switchlength': ns.duration | length,
'avaragepricedifferenceminumumswitchhours':ns.priceavaragedifference | round(3),
'avaragepricedifferencetotalswitchtime': totalpricedifference,
'overruled': ns.overruled,
'enoughhours':ns.enoughhours,
'enoughpricedifference':ns.enoughpricedifference }] %}
{% endfor %}
{% if ns.switchtimes | length > 0 %}
{% set ns.firstswitch = ns.switchtimes[0]%}
{% endif %}
{{ {'raw_data': ns.Corrected,
'avaragedeliveryprice': ns.avaragedelivery / (ns.Corrected | length),
'avarageusageprice': ns.avarageusageprice / (ns.Corrected | length),
'switchtimes':ns.switchtimes,
'amountofswitches': ns.switchtimes | length,
'firstswitch':ns.firstswitch,
'currentusageprice':ns.Corrected[now().hour].usageprice,
'currentdeliveryprice':ns.Corrected[now().hour].deliveryprice,
'currentpricedifference':(ns.Corrected[now().hour].deliveryprice - ns.Corrected[now().hour].usageprice) | round (3),
'switchedwrongnow': ns.SwitchedWrongNow} }}
rawdata: "{{this.attributes.source.raw_data}}"
avaragedeliveryprice: "{{this.attributes.source.avaragedeliveryprice}}"
avarageusageprice: "{{this.attributes.source.avarageusageprice}}"
switchtimes: "{{this.attributes.source.switchtimes}}"
amountofswitches: "{{this.attributes.source.amountofswitches}}"
firstswitch: "{{this.attributes.source.firstswitch}}"
currentusageprice: "{{this.attributes.source.currentusageprice}}"
currentdeliveryprice: "{{this.attributes.source.currentdeliveryprice}}"
currentpricedifference: "{{this.attributes.source.currentpricedifference}}"
switchedwrongnow: "{{this.attributes.source.switchedwrongnow}}" |
Dit zorgt voor een actuele stand van de tarieven. Hiervoor gebruik ik de volgende apex chart om dit bij te houden:
YAML:
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
| type: custom:apexcharts-card
apex_config:
chart:
type: bar
height: 450
offsetY: -15
offsetX: 0
width: 95%
plotOptions:
bar:
borderRadius: 0
dataLabels:
position: top
stroke:
show: true
width: 3
curve: smooth
graph_span: 32h
span:
start: hour
offset: '-4h'
now:
show: true
label: Nu
header:
show: true
title: Pijzen verbuik en teruglevering
show_states: true
colorize_states: true
yaxis:
- decimals: 3
min: ~0
max: ~0
apex_config:
border: false
forceNiceScale: true
series:
- entity: sensor.prices_pap_sap_switching
name: Teruglevering
show:
legend_value: false
in_header: false
float_precision: 3
type: column
opacity: 0.8
color: '#02cb80'
data_generator: |
return (entity.attributes.rawdata.map((start, index) => {
return [new Date(start["start"]).getTime()+1800000, entity.attributes.rawdata[index]["deliveryprice"]];
}));
- entity: sensor.prices_pap_sap_switching
name: Teruglevering
show:
legend_value: false
in_header: before_now
float_precision: 3
type: line
opacity: 0
color: '#02cb80'
data_generator: |
return (entity.attributes.rawdata.map((start, index) => {
return [new Date(start["start"]).getTime(), entity.attributes.rawdata[index]["deliveryprice"]];
}));
- entity: sensor.avarage_delivery
name: Gemiddeld Teruglevering
show:
legend_value: false
in_header: raw
float_precision: 3
type: line
opacity: 0.8
color: '#cb02b2'
fill_raw: last
- entity: sensor.prices_pap_sap_switching
name: Verbruik
show:
legend_value: false
in_header: false
float_precision: 3
type: column
opacity: 0.3
color: '#cb8002'
data_generator: |
return (entity.attributes.rawdata.map((start, index) => {
return [new Date(start["start"]).getTime()+1800000, entity.attributes.rawdata[index]["usageprice"]];
}));
- entity: sensor.prices_pap_sap_switching
name: Verbruik
show:
legend_value: false
in_header: before_now
float_precision: 3
type: line
opacity: 0
color: '#cb8002'
data_generator: |
return (entity.attributes.rawdata.map((start, index) => {
return [new Date(start["start"]).getTime(), entity.attributes.rawdata[index]["usageprice"]];
}));
- entity: sensor.avarage_usage
name: Gemiddeld verbruik
show:
legend_value: false
in_header: raw
float_precision: 3
type: line
opacity: 0.8
color: '#8002cb'
fill_raw: last |
All he voorgaande komt samen in deze chart:
Omdat ik echter ROI's bijhoud van de zonnepanelen en de sap investering bereken ik deze los van deze chart met daadwerkelijk actieve tarieven. Die loopt daardoor nu een beetje scheef ivm met die salderings periodes.
Kortom, ik ben er achter gekomen dat het zeer complex is om dit echt goed en naar realiteit te berekenen. Op dit moment staat mijn SAP na 10 maanden met een grote periode leuke TLV nog 200 euro in de min na een investering van 1000 euro.
Nu zou ik dat niet meer doen maar nu hij er hangt is het wel erg handig. Bottom line is vooral, ik betaal niet meer voor verbuik dan de TLV, en krijg altijd meer voor opwekking dan de TLV.