Ideas for electricity price based heating automation

Hi! I have a Thermia geothermal heat pump that powers the whole-house underfloor water heating. In Home Assistant I have an hourly price for electricity:
Screenshot 2022-02-06 at 12.36.48
and the following controls for the pump via ThermIQ-MQTT:

Here are the following automations I’m either running or planning to implement:

  • When 3kw or 6kw heaters turn on
    • turn off the heaters if the electricity price is too high (I’ll have to figure out how not to create favourable conditions for legionella)
  • during summertime, the compressor turns on approximately once per day, so I am planning to schedule this run for a time when the electricity price is cheapest
  • when I have a calendar event such as “Vacation” or “Visiting wife’s parents” that lasts for more than 2 days, then I plan on turning the pump off ahead of the event (e.g. 1 day before) and turning the pump back again based on the actual and desired room temperature difference and the time when we’ll be back

I probably forgot something, but feel free to suggest your ideas. Also, feel free to point out the parameters that I could play with that I’m probably not aware of right now. I’m mostly setting the indoor target temperature and hotwater start and stop temperatures. And also turning the pump on and off. I’m not messing with the curve.

I have thermostats installed in all rooms, but the thermostat system is only turning off and on different parts of the floor heating based on the temperature. It cannot directly call for heat from the heat pump. I could do that, but I haven’t seen the need, as setting the general indoor temperature, plus, adjusting certain rooms to cool down for the night is usually enough.

I plan on measuring price differences compared to not running any automations to figure out whether it’s worth it. I have quite a well-insulated house and floor heating has huge thermal inertia, so short-term changes are not likely to affect the bill that much. I’m not expecting huge gains, but it’s still nice to do all of this and know that it at least saves money rather than increases our utility bills.

1 Like

Thanks for sharing. I just bought Husdata H60 for my thermia diplomat G3.

Maybe this give u inspiration?

Thanks! I’ll take a look.

Hi!

I’m also working on a smart heating solution, which takes advantage of low electricity market prices. Although it’s a custom solution and not based on Home Assistant, we can share ideas.

I have the Nibe F1226 thermal pump without the bus adapter. It has two binary AUX inputs, one of which is used by a cooling convector upstairs for controlling the circulation. The remaining one can be used for one of a number of functions, such as increasing the curve or preventing all heating. The latter carries the risk of structural damage, should my software crash leaving the relay to an unwanted “on” state.

If I had both AUX inputs at my disposal, I could use the “SG ready” feature to control the Nibe using four tariff levels, “extremely low”, “low”, “normal” and “very expensive”. I’m not exactly sure how the device would adjust it’s behaviour based on this information, but the manual claims an extra menu item would pop up allowing me to configure the feature.

Room specific adjustments are not available to my controller, so I’m simply increasing the overall floor water temperature during low tariff. There’s a separate timer which will fire up the extra boiler heaters nightly or weekly, since the thermal pump can’t quite produce 180 + 300 liters of water in legionella-free temperatures. In the future, I might control also the boiler a bit smarter.

So, what I have now is pretty simple. There’s a Raspberry 3B running some python magic, which results in a Grove relay clicking on and off based on price thresholds I have specified. The logic is now that a low price switches the relay on, and the relay connects to the Nibe AUX2, which is set for the “increase curve” function. The experimentation is ongoing, but it looks like I may be able to do all or at least most of the heating during the cheap hours. Time will tell what the prices will be during the upcoming, politically challenging winter.

To compensate for the lack of cheap hours, the software will select some of the expensive hours, cheapest first. Since I can’t measure any temperatures yet, there’s only a variable saying how many hours are needed for minimum heating. In the future, outdoor and indoor temperature measurements may be a part of the equation.

Hope this gives you some additional ideas. But sounds like you probably discovered a nice solution for your needs already. It would be interesting to hear more about it.

– Eki

1 Like

Thanks for sharing! It seems you have a solid start!

As for my solution, I didn’t get a chance to do anything a lot more complex than simply turning the pump on or setting the temperature (curve) to the max when prices are low or high. I decided to use Node-RED mainly because it allows using variables without manually creating entities in home assistant:


Here, I have custom thresholds and when the price is lower or higher than these (in the trigger-state node, by using “is state” and a relevant operator with these types of expressions: $number($globalContext("homeassistant.homeAssistant.states['input_number.heating_electricity_high_price'].state")) ). I’m also storing what the previous mode and temperature of the heatpump was, so that I can restore it later.

P.S the “Previous ThermIQ Mode Not empty” input was triggered by the trigger-state node whenever the “opposite” of the threshold crossings happened. I had some problems with it if I recall correctly. So, here the 4th and 6th outputs from the trigger-state node are not actually usable.

I had grander plans as well, such as:

  • notifying me in the morning with the info when the pump will be off/on and allowing me to override that
  • looking ahead at the prices and choosing the heating and “off” times dynamically rather than static thresholds. This would solve the pump not being turned off or not pre-heating when the price is not too volatile, and also the pump being off for too long and the house getting cold (I have another automation that checks for this at the moment)

This is where I left off. I used a javascript function node to store the prices in a flow variable that would then be used to do all of the above.

function convert_price_to_kwh(e) {
    return {
        timestamp: e.timestamp,
        price: e.price * 0.0012 // Convert MW to kW and add 20% VAT
    }
}

if(msg && msg.data && msg.data.attributes){
    const prices = msg.data.attributes.ee.map(convert_price_to_kwh);
    if (prices) {
        flow.set("prices", prices);
        return prices;
    }
}

However, I’ll not be looking deeper into that for now, as here in Estonia we have been offered a government-subsidised fixed-price electricity package that negates all the volatility and removes the need for such automations. I’ll probably come back to it once I have a solar installation and will need to switch back to exchange-based pricing again.

Thanks for the update! Looks like that would give you already substantial savings.

During the extremely cheap and consequently, very sweaty weekend, I’ve been thinking about the actual need for my smart energy saver. Originally I was thinking that I should store heat to the structures whenever the price is low. It seems that the structures may saturate relatively fast when you have a week long discount period (like the one we had last week due to plentiful wind).

What I actually want is to minimise the cost during a high price period. This does not require me to heat excessively whenever the price is low, like I do now, but to actually get the structures close to the saturation point just before the price increases. Naturally it would be beneficial to know the prices for a longer period, but since that is not readily available, I could merely start increasing the floor temperature during the last cheap hours. If no cheap hours are available, maintain the minimum heat setting which has proven comfortable.

BTW, I wonder who takes the hit for the difference between the spot price and the mandated price in Estonia. In Finland, smaller operators are going bankrupt left and right, while the bigger ones that have a more complex ownership structure are trapped between bankruptcy and leaching on their parent companies.

I agree, using cheap hours to pre-heat if no expensive period lies ahead doesn’t have too much sense. At least in my use case.

As for the difference, I’m no expert, but part of our bills has been covered by the government, I believe. This is clearly not a long-term solution, but I don’t have too deep knowledge of the energy market to speculate…

1 Like

My above strategy was not really working the way I wanted, so I changed to a new one. Now, the water heater increases temperature with a timer starting at 1 (since I don’t have enough inputs in my Nibe to make it smart). All heating is switched off when the price is high enough. This is similar to the logic the power company uses when electricity demand surpasses supply. Then, if the price is high all day, the cheapest 8 hours are used for heating.

I think the general idea behind this os good, but it would require quite a few conditions to work well. Defining a limit in terms of room temp, looking at upcoming weather conditions and so on…
Not sure it will ever really “pay off” but I will definitely play around with it in preparation of my heat pump being delivered some time this year.

It’s a pity there aren’t more heat pumps that support this directly, like Nibe does, my Vaillant doesn’t even allow adjusting the temp curve via the SG Ready signal

Hi keex, do you mind sharing the card at the top showing the graph?

Sure! Here it is:

type: custom:apexcharts-card
header:
  show: true
  show_states: true
  colorize_states: true
apex_config:
  grid:
    show: false
  legend:
    show: false
  tooltip:
    shared: true
    marker:
      show: true
  chart:
    height: 100px
graph_span: 48h
span:
  start: day
yaxis:
  - id: price_chart
    opposite: true
    apex_config:
      tickAmount: 1
      labels:
        formatter: |
          EVAL:v => `€${v.toFixed(2)}`
  - id: hidden
    show: false
  - id: consumption
    apex_config:
      tickAmount: 1
      labels:
        formatter: |
          EVAL:v => `${v.toFixed(0)} W`
series:
  - entity: sensor.nordpool_api
    yaxis_id: price_chart
    type: column
    stroke_width: 2
    color: green
    float_precision: 4
    show:
      in_header: false
    data_generator: |
      return entity.attributes.ee.map((p, i) => {
        const price = p.price * 12 / 10000;
        const timestamp = p.timestamp * 1000;
        return [timestamp, price];
      })
  - entity: sensor.nordpool_price
    yaxis_id: hidden
    color: green
    float_precision: 4
    show:
      in_chart: false
  - entity: sensor.house_energy_cost
    name: Cost
    yaxis_id: hidden
    color: green
    float_precision: 2
    show:
      in_chart: false
  - entity: sensor.house_power
    name: Consumption
    yaxis_id: consumption
    color: orange
    stroke_width: 3
    opacity: 0.5
    float_precision: 2
    curve: stepline
    extend_to: now
    show:
      in_header: false
    group_by:
      func: avg
      duration: 1h
      fill: last
  - entity: sensor.house_power
    name: Instant power
    yaxis_id: hidden
    color: orange
    float_precision: 0
    show:
      in_chart: false

And Nordpool API sensor looks like this:

  - platform: rest
    name: Nordpool API
    resource_template: https://dashboard.elering.ee/api/nps/price?start={{ (now()).replace(hour=0, minute=0, second=0, microsecond=0).isoformat() | regex_replace(find='\+', replace='%2B') }}&end={{ (now() + timedelta(days=2)).replace(hour=0, minute=0, second=0, microsecond=0).isoformat() | regex_replace(find='\+', replace='%2B') }}
    value_template: '1'
    json_attributes_path: data
    json_attributes:
      - ee
    force_update: true

Nordpool API value:

ee:
  - timestamp: 1675980000
    price: 20.07
  - timestamp: 1675983600
    price: 18.32
  - timestamp: 1675987200
    price: 17.87
  - timestamp: 1675990800
    price: 17.84
  - timestamp: 1675994400
    price: 18.88
  - timestamp: 1675998000
    price: 23.86
  - timestamp: 1676001600
    price: 34.99
  - timestamp: 1676005200
    price: 90
  - timestamp: 1676008800
    price: 91.62
  - timestamp: 1676012400
    price: 116.42
  - timestamp: 1676016000
    price: 111.11
  - timestamp: 1676019600
    price: 85.9
  - timestamp: 1676023200
    price: 73.75
  - timestamp: 1676026800
    price: 94.93
  - timestamp: 1676030400
    price: 89.54
  - timestamp: 1676034000
    price: 111.12
  - timestamp: 1676037600
    price: 67.27
  - timestamp: 1676041200
    price: 111.15
  - timestamp: 1676044800
    price: 111.15
  - timestamp: 1676048400
    price: 85.36
  - timestamp: 1676052000
    price: 111.14
  - timestamp: 1676055600
    price: 85.08
  - timestamp: 1676059200
    price: 111.12
  - timestamp: 1676062800
    price: 30.06
  - timestamp: 1676066400
    price: 30.01
friendly_name: Nordpool API

I’m looking at something similar, using three components:

The ductless units are currently automated in HA using Sensibo and HiSense integrations/add-ons. They can heat and cool the house. The future propane Vitodens 100-W combi boiler will be the primary winter water heat and backup home (baseboard) heat, has WiFi connectivity, and should integrate with HA via (I can’t remember which) integration. The future hybrid water heater I haven’t picked out yet will be primary water heat in the summer, and backup hot water capacity/availability all year. as it will remove unwanted heat from my house. Many have WiFi connectivity, and I’ll seek out one that works with HA.

My objective is to be able to automate setpoints of the devices–primarily the water heat devices–to take advantage of ambient conditions and energy pricing.

I do have a non-HA question, though… would it make more sense to have the hybrid water heater before or after the propane combi boiler in series? I don’t believe it makes sense to put them in parallel.

1 Like

Hello I am new here and I am trying something like you did in this post here. I am using Node-Red to check the energy prices and send values to KNX according to the energy prices. I have not succeeded in doing this yet. Is it possible to post the json of the flow off your project please so i can take a look on how all this works?

Hi! I’m sorry but the Node-RED flow has been quite neglected as I’ve moved the automations to the native HA interface, but for what it’s worth, here’s the JSON:

[{"id":"e1ad9d5c1595a613","type":"tab","label":"Heatpump Optimization","disabled":true,"info":"","env":[]},{"id":"d1ee5130eec5e32f","type":"debug","z":"e1ad9d5c1595a613","name":"dump msg object","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":830,"y":60,"wires":[]},{"id":"d4713e5bbb8e29ab","type":"api-current-state","z":"e1ad9d5c1595a613","name":"Nordpool API","server":"580d2bfb.cc52e4","version":3,"outputs":1,"halt_if":"","halt_if_type":"str","halt_if_compare":"is","entity_id":"sensor.nordpool_api","state_type":"str","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"entity"}],"for":"0","forType":"num","forUnits":"minutes","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":320,"y":60,"wires":[["74d424d0bccd401c","52c58f018a11461b"]]},{"id":"2b52c423c1fb6d17","type":"inject","z":"e1ad9d5c1595a613","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":100,"y":60,"wires":[["d4713e5bbb8e29ab"]]},{"id":"74d424d0bccd401c","type":"debug","z":"e1ad9d5c1595a613","name":"dump attributes.ee","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"data.attributes.ee","targetType":"msg","statusVal":"","statusType":"auto","x":570,"y":120,"wires":[]},{"id":"52c58f018a11461b","type":"function","z":"e1ad9d5c1595a613","name":"Set \"prices\" Flow object","func":"function convert_price_to_kwh(e) {\n    return {\n        timestamp: e.timestamp,\n        price: e.price * 0.0012 // Convert MW to kW and add 20% VAT\n    }\n}\n\nif(msg && msg.data && msg.data.attributes){\n    const prices = msg.data.attributes.ee.map(convert_price_to_kwh);\n    if (prices) {\n        flow.set(\"prices\", prices);\n        return prices;\n    }\n}","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":590,"y":60,"wires":[["d1ee5130eec5e32f"]]},{"id":"bc9a19ddd23be41d","type":"api-call-service","z":"e1ad9d5c1595a613","name":"Heat Max","server":"580d2bfb.cc52e4","version":5,"debugenabled":false,"domain":"script","service":"heat_max","areaId":[],"deviceId":[],"entityId":[],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"Max Heat","valueType":"str"}],"queue":"none","x":480,"y":480,"wires":[["df2a1d4430402cd4"]]},{"id":"e4cf305cf274b4ba","type":"api-call-service","z":"e1ad9d5c1595a613","name":"Heat Min","server":"580d2bfb.cc52e4","version":5,"debugenabled":false,"domain":"script","service":"heat_min","areaId":[],"deviceId":[],"entityId":[],"data":"","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":460,"y":820,"wires":[[]]},{"id":"e61544252173eefe","type":"api-call-service","z":"e1ad9d5c1595a613","name":"Heatpump off","server":"580d2bfb.cc52e4","version":5,"debugenabled":false,"domain":"input_select","service":"select_option","areaId":[],"deviceId":[],"entityId":["input_select.thermiq_mqtt_main_mode"],"data":"{\"option\": \"Off\"}","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"Off","valueType":"str"}],"queue":"none","x":500,"y":360,"wires":[["df2a1d4430402cd4"]]},{"id":"f90bc8f26cf7b3b1","type":"api-call-service","z":"e1ad9d5c1595a613","name":"Heatpump restore","server":"580d2bfb.cc52e4","version":5,"debugenabled":false,"domain":"input_select","service":"select_option","areaId":[],"deviceId":[],"entityId":["input_select.thermiq_mqtt_main_mode"],"data":"{\t   \"option\": $flowContext('previous_thermiq_mqtt_main_mode')\t}","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"Restored","valueType":"str"}],"queue":"none","x":830,"y":520,"wires":[["9a95e35e920699b8","df2a1d4430402cd4"]]},{"id":"567f332e42b5a52c","type":"function","z":"e1ad9d5c1595a613","name":"Store previous ThermIQ Mode","func":"const mode = global.get(\"homeassistant.homeAssistant.states['input_select.thermiq_mqtt_main_mode'].state\");\nflow.set(\"previous_thermiq_mqtt_main_mode\", mode);\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":550,"y":420,"wires":[[]]},{"id":"eb5cec720daf93c2","type":"switch","z":"e1ad9d5c1595a613","name":"Previous ThermIQ Mode not empty","property":"previous_thermiq_mqtt_main_mode","propertyType":"flow","rules":[{"t":"nempty"},{"t":"eq","v":"Off","vt":"str"}],"checkall":"true","repair":false,"outputs":2,"x":560,"y":540,"wires":[["f90bc8f26cf7b3b1"],["1db83c7bec7ba15b"]]},{"id":"9a95e35e920699b8","type":"function","z":"e1ad9d5c1595a613","name":"Clear previous ThermIQ Mode","func":"flow.set(\"previous_thermiq_mqtt_main_mode\");\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1110,"y":520,"wires":[[]]},{"id":"1db83c7bec7ba15b","type":"api-call-service","z":"e1ad9d5c1595a613","name":"Heatpump Auto","server":"580d2bfb.cc52e4","version":5,"debugenabled":false,"domain":"input_select","service":"select_option","areaId":[],"deviceId":[],"entityId":["input_select.thermiq_mqtt_main_mode"],"data":"{\"option\": \"Auto\"}","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"Auto","valueType":"str"}],"queue":"none","x":820,"y":580,"wires":[["df2a1d4430402cd4"]]},{"id":"df2a1d4430402cd4","type":"api-call-service","z":"e1ad9d5c1595a613","name":"Notify","server":"580d2bfb.cc52e4","version":5,"debugenabled":false,"domain":"notify","service":"mobile_app_galaxy_s10","areaId":[],"deviceId":[],"entityId":[],"data":"{\t    \"title\": \"Heatpump optimization\",\t    \"message\": \"Heating \" & payload & \". Nordpool €{{ states('sensor.nordpool_price') }}/kWh -> {{ states('input_select.thermiq_mqtt_main_mode') }} {{ states('input_number.thermiq_mqtt_indoor_requested_t') }}°C\",\t    \"data\": {\t        \"actions\": [\t            {\t                \"title\": \"Undo\",\t                \"action\": \"HEATING_RESTORE\"\t            },\t            {\t                \"title\": \"Heat settings\",\t                \"action\": \"URI\",\t                \"uri\": \"/lovelace/thermiq\"\t            },\t            {\t                \"title\": \"View prices\",\t                \"action\": \"URI\",\t                \"uri\": \"entityId:sensor.nordpool_price\"\t            }\t        ]\t    }\t}","dataType":"jsonata","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1050,"y":360,"wires":[[]]},{"id":"c2ac332fb3e71018","type":"server-state-changed","z":"e1ad9d5c1595a613","name":"price >= high_threshold","server":"580d2bfb.cc52e4","version":4,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"entityidfilter":"sensor.nordpool_price","entityidfiltertype":"exact","outputinitially":false,"state_type":"str","haltifstate":"$number($globalContext(\"homeassistant.homeAssistant.states['input_number.heating_electricity_high_price'].state\"))","halt_if_type":"jsonata","halt_if_compare":"gte","outputs":2,"output_only_on_state_change":true,"for":"0","forType":"num","forUnits":"minutes","ignorePrevStateNull":false,"ignorePrevStateUnknown":false,"ignorePrevStateUnavailable":false,"ignoreCurrentStateUnknown":true,"ignoreCurrentStateUnavailable":true,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"eventData"},{"property":"topic","propertyType":"msg","value":"","valueType":"triggerId"}],"x":140,"y":820,"wires":[[],[]]},{"id":"4710f3a3454a6d75","type":"server-state-changed","z":"e1ad9d5c1595a613","name":"price <= low_threshold","server":"580d2bfb.cc52e4","version":4,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"entityidfilter":"sensor.nordpool_price","entityidfiltertype":"exact","outputinitially":false,"state_type":"str","haltifstate":"$number($globalContext(\"homeassistant.homeAssistant.states['input_number.heating_electricity_low_price'].state\"))","halt_if_type":"jsonata","halt_if_compare":"lte","outputs":2,"output_only_on_state_change":true,"for":"0","forType":"num","forUnits":"minutes","ignorePrevStateNull":false,"ignorePrevStateUnknown":false,"ignorePrevStateUnavailable":false,"ignoreCurrentStateUnknown":true,"ignoreCurrentStateUnavailable":true,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"eventData"},{"property":"topic","propertyType":"msg","value":"","valueType":"triggerId"}],"x":140,"y":880,"wires":[[],[]]},{"id":"ed4f0d0d32594458","type":"trigger-state","z":"e1ad9d5c1595a613","name":"","server":"580d2bfb.cc52e4","version":2,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"entityid":"sensor.nordpool_price","entityidfiltertype":"exact","debugenabled":false,"constraints":[],"inputs":1,"outputs":6,"customoutputs":[{"messageType":"default","messageValue":"","messageValueType":"json","comparatorPropertyType":"current_state","comparatorPropertyValue":"new_state.state","comparatorType":">=","comparatorValue":"$number($globalContext(\"homeassistant.homeAssistant.states['input_number.heating_electricity_high_price'].state\"))","comparatorValueDataType":"jsonata"},{"messageType":"default","messageValue":"","messageValueType":"json","comparatorPropertyType":"current_state","comparatorPropertyValue":"new_state.state","comparatorType":"<","comparatorValue":"$number($globalContext(\"homeassistant.homeAssistant.states['input_number.heating_electricity_high_price'].state\"))","comparatorValueDataType":"str"},{"messageType":"default","messageValue":"","messageValueType":"json","comparatorPropertyType":"current_state","comparatorPropertyValue":"new_state.state","comparatorType":"<=","comparatorValue":"$number($globalContext(\"homeassistant.homeAssistant.states['input_number.heating_electricity_low_price'].state\"))","comparatorValueDataType":"jsonata"},{"messageType":"default","messageValue":"","messageValueType":"json","comparatorPropertyType":"current_state","comparatorPropertyValue":"new_state.state","comparatorType":">","comparatorValue":"$number($globalContext(\"homeassistant.homeAssistant.states['input_number.heating_electricity_low_price'].state\"))","comparatorValueDataType":"str"}],"outputinitially":true,"state_type":"num","enableInput":true,"x":180,"y":420,"wires":[[],[],["e61544252173eefe","567f332e42b5a52c"],[],["bc9a19ddd23be41d","567f332e42b5a52c"],[]]},{"id":"580d2bfb.cc52e4","type":"server","name":"Home Assistant","version":4,"addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true,"cacheJson":true,"heartbeat":false,"heartbeatInterval":30,"areaSelector":"friendlyName","deviceSelector":"friendlyName","entitySelector":"friendlyName","statusSeparator":"at: ","statusYear":"hidden","statusMonth":"short","statusDay":"numeric","statusHourCycle":"h23","statusTimeFormat":"h:m"}]
1 Like

Hi, I came across this apex chart and I love it however I cannot manage to understand how to use it properly. I use the nordpool integration to extract the prices. Any suggestions how to switch the API sensor series into using the integrated component instead ?

Here’s an updated version that I now use. It now uses the Nordpool integration:

The legend layout wasn’t working for me any more, so I decided to use a button card and have my custom layout for showing at-a-glance instant electricity price information.

type: vertical-stack
cards:
  - type: custom:button-card
    entity: sensor.nordpool_ee
    triggers_update: sensor.house_power
    show_icon: false
    show_name: false
    tap_action:
      action: more-info
    hold_action:
      action: more-info
      entity: sensor.house_power
    styles:
      card:
        - font-size: 1rem
        - padding: 16px
      grid:
        - grid-template-areas: '"aaa bbb ccc" "ddd eee fff" "ggg hhh iii"'
        - grid-template-columns: 1fr 1fr 1fr
        - grid-template-rows: 1fr 1fr
        - justify-content: start
      custom_fields:
        aaa:
          - justify-self: start
        bbb:
          - justify-self: start
        ccc:
          - justify-self: start
        ddd:
          - justify-self: start
        eee:
          - justify-self: start
        fff:
          - justify-self: start
    extra_styles: |
      ha-icon {
        width: 1.3em;
        height: 1.3em;
        color: var(--paper-item-icon-color);
        margin-right: -5px!important;
      }
      b {
        font-size: 1.3em;
      }
      i {
        font-style: normal;
        font-weight: 300;
        font-size: .9em;
      }
    custom_fields:
      aaa: |
        [[[
          let e = 'sensor.nordpool_ee';
          return `<ha-icon icon="mdi:chart-line"></ha-icon>
            <b style="color: dodgerblue;">${Math.round(states[e].state * 10000)/100}</b><i>c/kWh</i>`
        ]]]
      bbb: |
        [[[
          let e = 'sensor.electricity_transmission_price';
          return `<ha-icon icon="mdi:transmission-tower"></ha-icon>
            <b style="color: dodgerblue;">${Math.round(states[e].state * 10000)/100}</b><i>c/kWh</i>`
        ]]]
      ccc: |
        [[[
          let e = 'sensor.total_electricity_price';
          return `<ha-icon icon="mdi:sigma"></ha-icon>
            <b style="color: dodgerblue;">${Math.round(states[e].state * 10000)/100}</b><i>c/kWh</i>`
        ]]]
      ddd: |
        [[[
          let e = 'sensor.house_power';
          return `<ha-icon icon="mdi:home-lightning-bolt"></ha-icon>
            <b style="color: orange;">${Math.round(states[e].state)}</b><i>W</i>`
        ]]]
      fff: |
        [[[
          let e = 'sensor.house_energy_cost';
          return `<ha-icon icon="mdi:currency-eur"></ha-icon>
            <b style="color: green;">${Math.round(states[e].state * 10000)/100}</b><i>c/h</i>`

        ]]]
      eee: |
        [[[
          return ``
        ]]]
  - type: custom:apexcharts-card
    apex_config:
      grid:
        show: false
      legend:
        show: false
      tooltip:
        shared: true
        marker:
          show: true
      chart:
        height: 100px
    now:
      show: true
      color: yellow
    graph_span: 48h
    update_interval: 1min
    span:
      start: day
    yaxis:
      - id: price_chart
        opposite: true
        apex_config:
          tickAmount: 1
          labels:
            formatter: |
              EVAL:v => `${v.toFixed(2)}c`
      - id: consumption
        apex_config:
          tickAmount: 1
          labels:
            formatter: |
              EVAL:v => `${v.toFixed(0)} W`
    series:
      - entity: sensor.nordpool_ee
        name: Nordpool
        yaxis_id: price_chart
        type: column
        stroke_width: 2
        color: dodgerblue
        float_precision: 4
        show:
          in_header: false
        unit: c/kWh
        data_generator: >
          const twoDays =
          entity.attributes.raw_today.concat(entity.attributes.raw_tomorrow);

          return twoDays.map((d, i) => {
            const timestamp = (new Date(d.start)).valueOf()
            return [timestamp, d.value*100];
          })
      - entity: sensor.house_power
        yaxis_id: consumption
        color: orange
        stroke_width: 3
        opacity: 0.5
        float_precision: 0
        curve: stepline
        extend_to: now
        group_by:
          func: avg
          duration: 1h
          fill: last

Let me know if you get stuck and I can help with something.

Thank you so much. it looks great. I try it out and have 2 questions:

  1. “{Math.round(states[e].state * 10000)/100}” adjusted to your currency. I use Sek. how would I change it to reflect Sek instead of Euro.
  2. House_energy_cost. is it some template that gives you this sensor to reflect the total cost of the current day ?
  1. ${Math.round(states[e].state * 10000)/100} just takes the state of the sensor.nordpool_ee which is in euros, and rounds cents to 2 decimal places. If your Nordpool sensor reports in SEK, this will report SEK cents as well. If you prefer to display whatever your Nordpool sensor reports, just use ${states[e].state} instead of ${Math.round(states[e].state * 10000)/100}.
  2. This sensor reports instantaneous cost according to the current draw and electricity market price, including transmission costs, taxes etc… It’s a template sensor defined in configuration.yaml:
template:
  - sensor:
      - name: "House Energy Cost"
        unique_id: house_energy_cost
        unit_of_measurement: €/h
        state: >-
          {{ states('sensor.house_power')|float / 1000 * states('sensor.total_electricity_price')|float(0) }}
1 Like

thank you very much! I managed to get it working. very nice! much lighter and more compact than what I tried before! many thanks again!

1 Like