Skip to content

Fix issue #787 - #788

Open
Noxeus wants to merge 2 commits into
BenPru:chore/rename-dhw-blocking-timesfrom
Noxeus:main
Open

Fix issue #787#788
Noxeus wants to merge 2 commits into
BenPru:chore/rename-dhw-blocking-timesfrom
Noxeus:main

Conversation

@Noxeus

@Noxeus Noxeus commented Sep 5, 2026

Copy link
Copy Markdown

🔍 What this fixes

Fixes pair times not parsing 'end time' 24:00.

✨ Changes

Only changes the text.py _PAIR_PATTERN regex.

🧪 Tests

Tested the regex on time pairs.

rhammen and others added 2 commits September 4, 2026 21:55
…imes

fix(text): 🩹 rename the DHW schedule entities to blocking times
Previously end block time 24:00 wasn't valid. But the HP accepts and parses this as "the end of day"
@rhammen

rhammen commented Sep 5, 2026

Copy link
Copy Markdown
Collaborator

Thanks for digging into this and sending a PR straight away — much appreciated.

You're right that 24:00 is a legitimate way to write an end-of-day time. ISO 8601 recognizes it specifically as the end of a calendar day (only ever 24:00, never 24:30), and it's exactly the position it occupies in a schedule string. So the input makes sense.

The problem is what happens after validation. 24:00 doesn't stop there: _parse_schedule passes it on, TimeOfDay.to_heatpump converts it to 86400, and that value goes to the controller — and I don't think the register can hold it:

  • I checked the diagnostics corpus in this repo: 45,360 schedule values across 81 dumps from ~29 different heat pumps. The highest value that has ever appeared is 86340, i.e. 23:59. Not one occurrence of 86400.
  • On my own MSW4-16 (V3.92.1) I can't enter 24:00 either — not on the controller display, and not in the Alpha Innotec app. Both stop at 23:59.
  • The controller stores midnight as 0 seconds, which is why schedules read back as HH:MM-00:00.

If we let 24:00 through as-is, the controller either rejects it or silently normalizes it to 0. Either way the read-back won't match what we sent, and async_write_many raises a HomeAssistantError — so you'd get an error saying the write failed, quite possibly while it actually applied correctly as 00:00.

What I'd like to do instead: accept 24:00, but normalize it

Rather than close this, I'd like to take your change with one addition — keep accepting 24:00 at the input boundary, but convert it to the controller's own spelling before anything is written. That way you get the notation you expect, and the device only ever sees a value it can actually store:

_PAIR_PATTERN = re.compile(r"^([01]\d|2[0-3]):[0-5]\d-(([01]\d|2[0-3]):[0-5]\d|24:00)$")

and in _parse_schedule:

return [
    (entry[:5], "00:00" if entry[6:] == "24:00" else entry[6:])
    for entry in entries
]

Your template output then works unchanged:

00:00-04:30/05:30-13:00/14:00-24:00

One thing to be aware of: the entity will read back as 14:00-00:00, because that's what the controller stores. That's harmless — if an automation re-sends the 24:00 version, it normalizes to the same value the device already holds, so no write is issued at all. But if you'd rather have the state match your template exactly, writing 00:00 directly in the template is the cleaner option.

Worth knowing about 00:00 as an end time

An end time of 00:00 already means "until midnight", not "unset" — a row only counts as unused when both halves are 00:00. So 14:00-00:00 is a normal, working window and always has been. That's documented in TIMER_SCHEDULES.md, but I can see why you wouldn't have gone looking for it: if your heat pump lets you type 24:00, there's no reason to suspect 00:00 is the way to say the same thing. I built the validation against my own unit, where 24:00 simply isn't enterable — so the integration ended up stricter than your controller is. Normalizing is the right fix for that mismatch.

Two things I'd like you to check, if you have a few minutes

  1. Try 00:00 as an end time directly on the heat pump (or in the app) — e.g. set a window to 14:00-00:00. Does the controller accept it, and does it keep it after you leave the menu? I'd like to confirm your firmware treats it as end-of-day the same way mine does.
  2. Set a window to 24:00 in the WebUI, then reload the page (or download a diagnostics dump from the integration) and tell me what it shows afterwards. If it comes back as 00:00, that confirms the WebUI is just normalizing your input. If it genuinely round-trips as 24:00, that's a firmware difference from my unit that I'd definitely want to know about before merging.

Happy to push the normalization onto your branch, or you can add it yourself — whatever you prefer. Thanks again for the report and the PR.

@Noxeus

Noxeus commented Sep 5, 2026

Copy link
Copy Markdown
Author

Hi,

I've done both and the result is as follows.

afbeelding

I've also edited the custom_component locally and only changing the regex it seems to just ... work? For me at least! Not sure if your concerns are valid, but they seem legit.

afbeelding

@rhammen

rhammen commented Sep 5, 2026

Copy link
Copy Markdown
Collaborator

after you changed it to 24:00 in the webUI, what does the entity in HA show as end time?
can you also add a diagnostics dump to this thread?

@Noxeus

Noxeus commented Sep 5, 2026

Copy link
Copy Markdown
Author

It does show as 24:00 in HA if I enter that in the WebUI or the HP itself. It's how I figured out what I had to enter for 'end of day' after a couple of tries like 0:00.
I see I didn't read your other thingy right, but here it is. If I enter 14:00-00:00 it shows as 14:00 - 00:00. But I do not know what that entices in firmware terms.

@rhammen

rhammen commented Sep 5, 2026

Copy link
Copy Markdown
Collaborator
  1. could you set the end time as 00:00 in the webui, or not?
  2. same question for setting 00:00 as endtime on the controller. Is that possible?

@rhammen

rhammen commented Sep 5, 2026

Copy link
Copy Markdown
Collaborator

when I try to set 24:00 as end time on heatpump24.com, I get "Input data out of bounds. Please try again."

@Noxeus

Noxeus commented Sep 5, 2026

Copy link
Copy Markdown
Author

LIke I said:
If I enter this

afbeelding

it resolves to this:

afbeelding

which is

00:00-04:30/05:30-13:00/14:00-00:00
in HA.

and
afbeelding
on the HP

This is at the local ip WebUI btw.

@rhammen

rhammen commented Sep 5, 2026

Copy link
Copy Markdown
Collaborator

LIke I said: If I enter this
afbeelding

it resolves to this:
afbeelding which is 00:00-04:30/05:30-13:00/14:00-00:00 in HA.

and afbeelding on the HP

This is at the local ip WebUI btw.

Thanks.
So my proposal would be to stick with this.
I expect this to also work in your case, but please check.
If that works, then we can change 24:00>00:00 both when a user enters it in HA, and when it reads back 24:00 from the heatpump.
Would that work for you?

@Noxeus

Noxeus commented Sep 5, 2026

Copy link
Copy Markdown
Author

I think 24:00 as 'end of day' is much more intuitive tbh. Especially because we're defining a scheme for the whole day. But I can't vouch for all other Luxtronic devices if it works.
So I'll check tomorrow how the HP behaves and if it works with 0:00.

@Noxeus

Noxeus commented Sep 5, 2026

Copy link
Copy Markdown
Author

Also I'm at 3.92.3 on a MSW2-9S

@rhammen

rhammen commented Sep 5, 2026

Copy link
Copy Markdown
Collaborator

The only other approach I see would be to make this behavior dependant on something, like manufacturer or firmware.
But it is difficult to correlate this behavior to something like that, because you are the first user that has 24:00 stored as endtime.... What manufacturer and FW is your heatpump?

EDIT: Crossposted...

@Noxeus

Noxeus commented Sep 5, 2026

Copy link
Copy Markdown
Author

Yeah, I can see that being a complicated decision. I'll leave that in your (probably more capable) hands! I'll report tomorrow to tell if it only became active during the two 'open' hours when I enter 0:00 as end of day.

@rhammen

rhammen commented Sep 5, 2026

Copy link
Copy Markdown
Collaborator

Thanks for testing both — that was genuinely useful, and one of your answers changed my mind about what's going on.

Your pump really does store 86400. You're the first unit I've seen do it: an MSW2-9S on V3.92.3 that accepts 24:00 on the local WebUI and on the controller itself, and reads back as 24:00 in HA. My MSW4-16 (V3.92.1) refuses it everywhere I can reach — the controller keypad only offers hours 00–23, the Alpha Innotec app's analog clock stops at 23, and heatpump24.com answers "Input data out of bounds". And across the diagnostics corpus in this repo — 45,360 schedule values from 81 dumps — 86400 never appears once. So the two controllers genuinely differ, and I have no way to tell in advance which behaviour a given user's pump has.

Your 00:00 result matters just as much, and it's the one the fix below actually rests on: you confirmed your controller accepts 14:00-00:00 and keeps it. So please do run the check you mentioned for tomorrow — whether it genuinely behaves as end-of-day on your firmware, not just whether it's accepted. That's the single result I'd most like confirmed, because if 00:00 meant something different on your pump than on mine, the approach below would need rethinking.

That difference is also why I can't ship the regex-only version even though it works for you. On your pump 86400 is storable, so the write confirms and everything looks fine. On a pump that clamps it to 0, the value we sent back doesn't match what the device reports, and async_write_many raises a HomeAssistantError — the user gets told the write failed, on a write the controller very likely applied correctly as midnight. I'd be fixing your pump and breaking an untestable number of others, including every Luxtronik 2.0/2.1 on V1.x/V2.x firmware.

What I'd like to build instead

Four small changes that behave correctly on both our pumps, with no manufacturer or firmware gating anywhere:

1. Accept 24:00 in the pattern — your change, kept as-is:

_PAIR_PATTERN = re.compile(r"^([01]\d|2[0-3]):[0-5]\d-(([01]\d|2[0-3]):[0-5]\d|24:00)$")

2. Normalize it to 00:00 on the way out, in _parse_schedule, so we never originate an 86400 write on any pump:

return [
    (entry[:5], "00:00" if entry[6:] == "24:00" else entry[6:])
    for entry in entries
]

3. Preserve whatever the device holds on the way in. No normalization on read — if your register contains 86400, HA shows 24:00, because that's the truth about your device.

4. Treat both spellings as equal when deciding what to write, in async_set_value:

_MIDNIGHT = {"00:00", "24:00"}

current_end = get_sensor_data(data, f"parameters.{end_name}")
if current_end != end and not (current_end in _MIDNIGHT and end in _MIDNIGHT):
    writes.append((end_name, end))

That last one matters specifically for you. Without it, once your register holds 86400, editing any other row in the same block would see "00:00" != "24:00" and silently rewrite your 86400 to 0 — changing a row you never touched. With it, your value is left alone.

What this means for you in practice

Straight answer, including the part you won't love:

  • Your template string 00:00-04:30/05:30-13:00/14:00-24:00 is accepted. That's the bug from Time blocks for Heat Water is not accepting 24:00. #787, fixed.
  • But a 24:00 you type in HA will read back as 00:00, because we deliberately write the value every controller can store.
  • If you want HA to show 24:00, set that row once on the WebUI or the controller. We preserve it on read and never overwrite it, so it will keep showing 14:00-24:00 and keep accepting it. As a bonus, your template output would then match the entity state exactly, instead of differing by a spelling.

On which notation is more intuitive — I'd honestly say 00:00, since that's what a digital clock or my phone shows at midnight. But ISO 8601 does recognize 24:00 specifically as the end of a day, and I can see the logic when you're describing a whole day rather than a moment in it. Both readings are legitimate, and I'm happy to accommodate yours as far as we reasonably can. The limit here isn't the notation — it's that I can't safely send 86400 to hardware I have no way to test.

One housekeeping thing

This PR currently targets chore/rename-dhw-blocking-times, which is already merged — could you retarget it to main? After that I'm happy to push the normalization commits onto your branch so it stays your PR, or you can add them yourself. Whichever you prefer. I'll add tests, update the validation error message in all five languages, and note the 24:00 handling in TIMER_SCHEDULES.md.

Thanks again — the report was right, and your testing is what made the fix correct rather than just plausible.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants