Heat pump experiment review after two years

Hello @MyForest . This looks really interesting work. I will need to rev-up my brain in order to digest it all. I am not the fastest at understanding things.
I would love to get my old 5kW Ecodan controlled better, but my FTC3 is far too old for MelCloud sadly.

Hi @johncantor,

Thanks for the kind words.

If you think it would help I’m happy to chat to you directly or even show you some of the experiments. Just let me know when would suit you. I’m up in Yorkshire and I believe you are not, so we may end up doing it virtually, but I can drive to places if that helps. It’s the least I can do for you considering the efforts you have put in over the years (including your book sat next to my arm right now which I show to friends who ask about these things).

I don’t think I’ll be able to get MELCloud on your FTC3 though :slight_smile:


Hi. I’m a newbie here, but I know some python and other stuff. Great stuff so far and thanks for all the info and code.

The following line looks like it would be sending the data to emoncms.org — could someone tell me what sort of line I should be using to feed the data directly into my local emoncms on a RPi running emonsd-21Jul21, please? Or even which bit of TFM I should R for such info.


Hi @mjr,

I do this, which is practically the same as Trystan’s example, but if you are familiar with Python you’ll see me doing some other things which might be interesting to you.

encoded_data = (
        "time=" + str(int(readingDateTimeUTC.timestamp())) + "&node=ecodan&apikey=some_secret_string&data=" + json.dumps(toPost, separators=(",", ":"))

headers = {"Content-Type": "application/x-www-form-urlencoded; charset=utf-8", "User-Agent": "ecodan/post_to_emon.py"}

encoded_data = encoded_data.encode("utf-8")

http = urllib3.PoolManager()

r = http.request(
    "POST", "https://my-server/input/post", headers=headers, body=encoded_data, timeout=urllib3.Timeout(connect=1.0, read=2.0), retries=False

result = r.data.decode("utf-8")
if result != "ok":
    raise Exception("Unable to post")

I tend to use requests rather than urllib but obviously the day I wrote this I decided to use urllib. Of course string concatenation is very bad too so I wouldn’t propose anyone does that.


1 Like

Thanks for that. So I think I just POST to /input/post on the pi running emonsd. Maybe it will become obvious to me what to do after that! Most of this seems to become obvious once I start but I feel like I’m failing to find developer/customisation docs in the Guide or the pydoc or somehere.

And just as an aside @MyForest, your heat pump experiments were a big encouragement for us to go for an ecodan (and gave some hints on how to configure it more efficiently than the installer did even with the stock FTC controller — they basically set it on room temperature apparently without a weather compensation curve or using the outdoor sensor at all), as well as motivating me to finally dig out the RPi and connect it up again, to see summary detail on what the heating is doing a bit more easily than risking repetitive strain injury tap-tap-tapping the melcloud app.

I’ve also plans for Total World Domination using smart radiator valves and so on, but one step at a time and no risking the RHI repayments!

1 Like

Hi @mjr

You want to go to /input/api and you’ll see all the help you can dream of.

I’m assuming you saw this:


Particularly, the TRVs are at something like 23 °C at the moment but my algorithm keeps turning off the heat pump before the air temperature gets close to that. The air temp is actually about 20 °C. I’ve been scratching my head trying to work out what aspect of the system is keeping the occupants comfortable without any room temperature interaction but across a wide range of external temperatures. At the moment I can’t explain why my system is working so well in this respect.

1 Like

Yes, I’ve got that, but I mean more like what’s the recommended location and launch/relaunch method for my data collection scripts for the least painful updates in future? Are there ways that the usual upgrades are unlikely to break?

Yes, I saw that post. I think you’re correct to view Thermostatic Radiator Valves as basically an upper limit on what that room will take from the heating loop. That’s my first task for a controllable TRV, to turn down heating in a room at times when no-one is in it, but ideally with some pushbutton to start heating it as soon as we know someone wants it, even if we’re not home, before they start their journey (in part because our low-temperature radiators don’t heat rooms quickly and I don’t want to use the old heater if we can avoid it). Expect some questions from me on a post about smart/remote TRVs some time soon!

I also don’t understand why some TRVs say they won’t work with heat pumps, unless maybe some don’t work well with low-temperature radiators and need the old 60-70 degree heating loops.

Why is your system working so well? As I understand it, your control program does “feels like” weather compensation with some extra logic to reduce cycling and avoid pumping water when it’s not being heated, so I think you’ve effectively implemented a curve that matches your heat loss and desired target temperature. And one of your tweaks is to stop heating when the radiators aren’t losing enough heat, which I think would happen when all rooms are close to that curve’s target value, effectively using the heating loop as a whole-house thermostat! If one room is disproportionately cold, I think its TRV would be more open so it would take heated water more quickly than the others and lose enough heat to keep the pump running, and eventually bring that room back up and balance the system again, allowing the return temperature to get close to the flow and stop.

How good is your program at recovering from events like someone foolish switching the heating off in error for a few hours in winter? Using the Mitsubishi controller’s curve settings, I have at least once lost control of the house temperature so it wasn’t coming back on its own any time soon. I brute-forced a recovery by switching to flow temperature mode and setting a fairly high flow temperature until analogue thermometers around the house all read OK. Then I set a new curve using the learning (new values and a bigger bend to account for how exposed to the wind we are), switched back to curve mode, rinsed and repeated.

I suspect your control logic would do better than Mitsubishi’s because it could factor in other sensors and take more extreme actions. The Mitsubishi’s curve mode seems to be reacting to the outdoor sensor alone, pays no attention to the room thermometer and does not react much to the flow-return difference: as far as I can tell, it stops heating if the flow-return difference gets small, but it still runs the pump most of the time and it never reacts to a large flow-return gap by increasing the flow temperature temporarily.

I hope that you will permit me to slightly drift into two other quirks that I found while experimenting with our ecodan about which I’d like to ask for other experiences:

  1. It seems that our ecodan always heats water up to the set temperature every time hot water comes out of a “prohibit” timer phase, even if the tank sensor temperature is within than the allowed drop. Is this normal? Do others? I currently exploit this to heat most of our water with cheaper overnight electricity (by having a prohibit ending at something like 4am) but it is generally a bit annoying because there are times of the day/week when I’d like not to heat water unless it’s really really needed, without always reheating the tank at the end of such a period. I guess I could use pymelcloud to turn the tank set temperature down and up on a schedule but that may be a bad idea for some reason I’m not thinking of, or, even more intrusively, have no schedule and use pymelcloud to press the virtual “Heat Now” button if a mix of time and tank temperature sensor conditions are met.

  2. Does the Mitsubishi’s room thermostat mode heat the radiators to higher flow temperatures but more intermittently, for shorter times in total? Is that how it is expected to work? That seemed to combine with our windswept location to deliver a poor CoP (closer to 3 than 4) and higher bills in early November (and 6-9°C out so not that cold) and motivated me to start reading and then experimenting.

Ah, I see. I’m using this all in Docker so I just keep my scripts outside and they don’t get impacted by updates. Sorry, that probably isn’t helpful.

I’ve got location data from our EV in realtime. Maybe you could just see if the car is moving, predict how long it’ll take to get home and how long the heating will take and just turn it on automatically. That’s assuming you don’t stop off on the way home to take your mum some nice flowers.

Yep, even radbot says it in their FAQ. I ran out of time to ask why.

You might be on to something there. I hadn’t thought of it that way, but that is what I’m doing.

That’s been happening for the last few weeks. We have an intermittent power fault unrelated to the ASHP. When the power is restored the dumb regular thermostat receiver that we don’t even use defaults to “off” so the house starts getting colder. Sometimes it’ll be hours later before we realise and manually kick the manual thermostat to unblock things. The dumb thermostat is set to 26 C so it should always be “on” but it’s waiting for a state transition so just sits around shrugging it’s shoulders whilst everyone else is working to do a good job.

So basically it doesn’t get cold from that type of event because the house is already warm.

We do have a daily occurrence of what you describe though because we don’t heat the house overnight (we’re all asleep under duvets). So in the morning it has to get going. Here’s a typical long run (in this case with a weird gap) that we see in the morning. You can see it takes an hour to get up to temp so just trundles along until it’s been at the max temp for a few minutes.

Yep, that would be frustrating.

You can see my algorithm is responding to a small deltaT by lifting the target temp so the deltaT gets bigger again (for a while at least).

So my strategy of walking up the temperature means it’s running at very low temps for quite a while to start with, such as that 28 C for 10 mins at the beginning. That’s similar to what @johncantor and @TrystanLea see with their FTC2 slowly lifting the flow temp rather than my FTC5 and John’s FTC6 (and presumably yours) which are ramping up the flow temp really quickly.

I very much do the last thing. Mine is on prohibit the whole time and my algorithm decides when to poke the ForcedHotWaterMode button.

To do your “overnight” thing I have this line of code to bump up the desired temp so I’m using it as a heat battery:

if OctopusGo.powerWillBeCheapForNextFifteenMinutes(calculationMoment, deviceInfos):
    desiredWaterTemperature = 52

and the other code that looks at the discrepancy might decide that’s reason enough to jump in and turn on the heat pump, or maybe not, especially if it’s already pretty warm.

1 Like

I’m afraid I didn’t run it long enough to be sure. It kept cycling so much and running the circulating pump so long that I took over in 2019-12 and haven’t looked back. I did experiment with the different modes but nothing made it behave well.

Just for your reference, here’s how ours behaved in 2021-11 before it went a bit haywire when I lost control. It’s about 10 heating runs per day.

Well here’s the worst it’s been since we got the heat pump @mjr. Something else keeps taking the power down and we ended up turning things off for hours on the 8th when it was already cold. We were actually unhappy for the first time.

This graph also shows how the temperature normally drops a degree or two each night when the heat pump is shut down.

I’ve zoomed in on the evening of the 8th after we got things running smoothly again.

So it looks to me like your system raises temperature by at most 1 degree in 45 minutes. Would you be willing and able to plot energy consumption and production over that zoomed in bit? I’m wondering how hard it was driving the heat pump to do that or if there is scope for a “quick warm-up” process to do more.

Here you go @mjr

Your query is a good example of why I built my app.

You’ll have to ignore the spikes on the hour which are a hiccup due to this MELCloud fun I’m having.

Normally my flow tops out at about 40 °C but you can see I was playing with it to “boost” it as you say (by re-compiling my Docker container :slight_smile: )

This is the first time I’ve felt the need to boost it. I could put something in my algorithm that looks at the room temps to auto-boost it I suppose. Our house doesn’t usually get cold so it’s not been something I was interested in.

Thanks. Apart from about 2130, it looks like your 14kW unit still has some capacity to spare.

I’ve been thinking more about weather compensation curves and quick warm-up and occupant tolerance for temperature fluctuations. In one way, weather compensation is very clever, proactively changing the heat output in response to external temperature change before it causes heat loss. In another way, it does the wrong thing, working the heatpump harder when its performance gets worse due to the falling temperature. I wonder whether it might be a good idea to increase the radiator temperature on a mild afternoon, putting more heat into the house when the heatpump CoP is better and letting the temperature fall slightly as it gets colder outside… or whether the occupants would notice that, feel cold and boost the heating anyway.

But because I cannot see how to remote control the +/- curve adjustment from melcloud, this will remain as idle wondering for me until I take the plunge and let the Pi set the flow temperature, and I’ve an LCD display and some buttons I want to get working before that!

Yep, 21:30 was it doing the hot water in it’s usual enthusiastic way.

When we tried overheating the house the people noticed. Also, having it get colder as the outdoor temp fell would make them more upset. The heat pump was fitted to keep us warm.

You’re right it’s bad to heat the house when it’s cold outside. If you wanted optimal efficiency you could heat it in the Summer and ensure it was well insulated :slight_smile:

One thing extra control has helped with is a 10 minute nudge at 20:00 in September which makes everyone feel warm for very little cost. That’s how to earn brownie points.

1 Like

Ha! I wonder what temperature would be required in September to last the winter(!)

Going back to the melcloud output: does anyone know how to tell if the heating controller is in flow or curve mode? I can detect room mode because there’s a Zone1InRoomMode flag, plus TargetHCTemperatureZone1 and SetHeatFlowTemperatureZone1 differ in that mode, but I’ve yet to spot the difference between flow and curve modes.

Why this matters: if I give the Pi control based on the data stored in emoncms, I want it to give up if it sees settings being changed on the controller — such as someone putting it back into curve mode or changing the target temperatures — rather than start a fight it’ll probably lose because local hardware usually beats cloud service commands.

While I’m asking, does anyone know if pymelcloud can see or set the curve delta, the +/- 0-9°c that appears on the controller unit in curve mode? I don’t think it can, but I could be wrong.

1 Like

You mean like this :slight_smile:

icon = {
    0: "operation-mode-room",
    1: "operation-mode-flow",
    2: "operation-mode-curve",
}.get(deviceInfo["OperationModeZone1"], "")

operationModeZone1Title = {
    0: "Using room temperature in utility room to decide when to run",
    1: "Using the flow temperature like the oil boiler did",
    2: "Adapting the flow temperature based on how cold it is outside",
}.get(deviceInfo["OperationModeZone1"], "")

That’s really important for the humans. In our house it allowed them to feel in control. Also, it allowed the algorithm to be a bit over-zealous and have it’s nose tweaked by the humans when it went too far.

I don’t think so either. It uses the same API I do and I didn’t see flags for it because it’s not being set via the app. Here’s the flags I bothered to record:

flags = {
    "Power": 1,
    "OperationMode": 2,
    "EcoHotWater": 4,
    "OperationModeZone1": 8,
    "OperationModeZone2": 16,
    "SetTankWaterTemperature": 32,
    "TargetHCTemperatureZone1": 128,
    "TargetHCTemperatureZone2": 512,
    "ForcedHotWaterMode": 65536,
    "HolidayMode": 131072,
    "ProhibitHotWater": 262144,
    "ProhibitHeatingZone1": 524288,
    "ProhibitCoolingZone1": 1048576,
    "ProhibitHeatingZone2": 2097152,
    "ProhibitCoolingZone2": 4194304,
    "Demand": 67108864,
    "ThermostatTemperatureZone1": 8589934592,
    "ThermostatTemperatureZone2": 34359738368,
    "SetFlowTemperature": 281474976710656,
    "After here I added manually": True,
    "SetHeatFlowTemperatureZone1": 281474976710656,
    "SetTemperatureZone1": 33554432,
    "ProhibitZone1": 524288,

Just so. It hadn’t occurred to me that the values of OperationModeZone1 meant different things to the same values in OperationMode. I was expecting it to flit between 0 and 2 depending on what OperationMode was doing. As they say around here: that’ll learn me!

1 Like

That json.dumps call seems to need to be json.dumps(data, separators=(',', ':')) else most of the created feeds have spaces on the front of their names which messes up @MyForest’s MMSPHeatPump app in ways I found non-obvious.

1 Like

Signed up to thank @MyForest for all the useful information he’s posted here and on other threads.

I’ve had a heat pump for 2 months now (EcoDan 11.2 kW in 1960s bungalow with rads near York), and quickly had my own PHP* code pulling all the necessary data out of MELCloud into Emoncms.
(*Python’s okay and all, but I just prefer semi-colons and curly brackets :smiley: ).

I’ve also got some code to control the HP via MELCloud, though only simple basic time-based settings so far, such as reducing the flow temperature overnight, gradually increasing it in the early morning. My unit really likes to run in 20 minute cycles, and I’ve not yet figured out how to convince it to run longer. Maybe I’ll try controlling the HP more dynamically instead.

Edit: I’ve just realised that they installed a “smart” room thermostat with the heat pump, which is setup to do 3 cycles per hour. Given that I have temperature sensors in every room feeding EmonCMS, I may implement a thermostat in software instead.

Here are some other observations I’d like to share:

  • I was also looking for a way to read and control the ‘curve delta’ setting, but it doesn’t look to be exposed by MELCloud. The only way would be to take over the weather curve function entirely, which I may try one day.
  • if you don’t have proper metering, then the HeatPumpFrequency is the % power being used. Multiply this by the maximum input power of the unit (3730 watts in my case), and you get a half decent measure of the power its drawing.
  • I’m using openweathermap.org/api to get outside temperatures for my location, which seems to match the thermometer on the back of the HP pretty well. This also gives me future temperatures, which I might try factoring into a control algorithm.

Still figuring out how best to get the best out of the HP, but it seems to be working pretty well so far.