Skip to content

Releases

v0.8.1

Choose a tag to compare

@github-actions github-actions released this 03 Nov 12:28
v0.8.1
2507eb2

Fixed

  • The water heater cards no longer show "Configuration error" after a
    restart.
    • The cause: the cards were added to pages only once the integration
      had started. A page opened earlier, which the phone app does as soon as
      Home Assistant comes back, never got them until it was refreshed.
    • Now: each start copies the card to
      www/icomm_local/icomm-water-heater-card.js and keeps a dashboard
      resource pointing at it, the way HACS installs cards. Home Assistant
      serves both from the moment it starts.
    • Fallback: on a first start without a www folder, or with YAML
      dashboard resources, the cards load from the integration's own path
      as before.
    • Cleanup: deleting the last relay removes the resource and the copy.

v0.8.0

Choose a tag to compare

@github-actions github-actions released this 03 Nov 02:07
v0.8.0
efb1a95

Added

  • Water heater cards that come with the integration. They load
    themselves on every dashboard: no resource to add, and HACS updates
    them with the integration. Details in
    docs/USING.md.
    • custom:icomm-water-heater-card: Home Assistant's own dial, with a
      spinner beside Target from your first tap until the tank's own data
      shows the new value, and a red line if it never does. Also power, time
      to recover, hot water available, the mode buttons, and Vacation with a
      number of days.
    • custom:icomm-water-heater-tile: one row with the current
      temperature, the mode's icon, power and time to recover. Tap opens the
      full card in a popup; long-press opens more-info.
    • Repeated taps: taps on + or − count from the value on its way to
      the tank, not from the old setpoint, so the number doesn't jump back
      while a change is in flight.

v0.7.13

Choose a tag to compare

@github-actions github-actions released this 29 Oct 16:45
v0.7.13
8dbcdda

Fixed

  • No more lost request and BadHttpMethod error after the heater's
    GET /ping.
    This time the fix is based on a decrypted capture.
    • How the heater sends requests: each request's headers go in two
      TLS records. The second holds the last header and the blank line;
      for a GET that's Content-Length: 0, which completes the request.
    • The quirk: only /ping already ends its first record with a blank
      line, so the second record arrives after a complete request. The relay
      answered it 400 and closed the connection, and the heater retried 16
      seconds later.
    • Now: the relay drops that record only when it follows a complete
      request, and counts it in /state as stray_headers.
    • Why 0.7.10 failed: it dropped the record always, which cut the
      end off every normal request.

v0.7.12

Choose a tag to compare

@github-actions github-actions released this 29 Oct 16:12
v0.7.12
5a31cbb

Fixed

  • A request from the relay's own host no longer counts as the heater
    reaching the cloud.
    A curl to the relay's /ping from Home Assistant's
    host cleared cloud_down and looked like a dial. Loopback requests are still
    answered, just not counted.
  • The network switch's reset count survives a Home Assistant restart. It
    read 0 after a restart, though the 6-hour cooldown (from the saved time of
    the last reset) was kept.

Added

  • debug_tls_keylog, a hidden debugging option. It writes the TLS session
    keys of the water heater's connections to /data/tls-keylog.txt (0600), so a
    packet capture can be decrypted. It logs a warning while on. Turn it off
    and delete the file afterwards.

v0.7.11

Choose a tag to compare

@github-actions github-actions released this 29 Oct 13:00
v0.7.11
c8e9693

Fixed

  • Reverted 0.7.10's stray-header guard: it stopped the heater reaching the
    cloud after a reconnect.
    With the guard, the heater's GET /ping was no
    longer answered, so its cloud side never came back after a network drop. The
    relay is back to 0.7.9's behaviour: a harmless BadHttpMethod error in the
    log after /ping. The stray_headers count is gone from /state.

v0.7.10

Choose a tag to compare

@github-actions github-actions released this 29 Oct 12:46
v0.7.10
7e550ed

Fixed

  • The heater's first requests after a reconnect are no longer lost.
    • The quirk: after GET /ping, the heater sends a stray
      Content-Length: 0 line on its own, after the request has already ended.
    • What it cost: the relay took that line for the start of a new request,
      logged BadHttpMethod … Invalid method encountered, answered 400 and
      closed the connection. That lost the request queued behind it, and the
      heater retried about 16 seconds later on a new connection.
    • Now: the relay drops a chunk that is only that line, and counts it in
      /state as stray_headers.

v0.7.9

Choose a tag to compare

@github-actions github-actions released this 29 Oct 03:24
v0.7.9
4b44df4

Fixed

  • Pressing + or − repeatedly on the water heater card no longer jumps back.
    The water heater's state changed on every 10-second poll (its
    last_telemetry_age_s attribute ticked), and Home Assistant's frontend
    before 2026.11 resets the card's target on every state change, so a run of
    presses was undone partway. The attribute is gone from the water heater;
    the Relay health sensor still has it.

v0.7.8

Choose a tag to compare

@github-actions github-actions released this 28 Oct 23:12
v0.7.8
ee09a6f

Added

  • The relay notices when the heater has stopped talking to Ayla, within
    minutes.
    • The problem: after a relay outage of a few minutes (a reboot of the
      Home Assistant host), the heater's Wi-Fi module stops connecting to
      the cloud. It still looks up the cloud's name but opens no connection,
      until its network connection drops. Seen live for 8.5 hours, while health
      said unknown: too few dials observed yet and no repair appeared.
    • The probe: a healthy heater passes a local reporting-mask write on to
      the cloud about 100 ms before it confirms it locally; a broken one
      confirms it with nothing sent to the cloud. The relay sends the mask
      when the cloud has been quiet (5 minutes after start, then every 30), and
      every routine renewal counts too.
    • Result: health degraded with code cloud_down, and the repair
      Water heater isn't reaching Ayla. /state has cloud_probe, and the
      Relay health sensor shows it as an attribute. CLOUD_PROBE=0 turns it
      off; CLOUD_PROBE_AFTER sets the interval.
  • Optional automatic fix: a network switch (the integration's
    Configure).
    • What it does: when the cloud side is down, Home Assistant turns the
      switch off for 90 seconds, then on, so the heater rejoins the network and
      reconnects, as after a power cycle. At most once every 6 hours; turning it
      back on is retried, and a Home Assistant restart mid-way turns it back on
      at the next start.
    • Which switch: anything that cuts the heater's network access while
      it's off. With UniFi, add the heater to the UniFi integration's block
      client
      list and pick that switch. A quick Wi-Fi "reconnect" doesn't
      work: the heater rejoins in seconds without resetting.
    • Measured: a 90-second block, then unblock, brought the cloud side
      back at once (connection, config fetch, Local Connect re-registered).
    • With several heaters, the switch is used for the first heater only.

Docs

  • "Open in Home Assistant" buttons in the install steps: one adds the
    repository and opens the add-on, one opens the integration in HACS, one
    starts the integration's setup.
  • A much shorter README (437 → about 150 lines): what you get, what you
    need, four install steps, good-to-know and uninstall. The rest moved,
    unchanged, to new pages:
  • DNS_STEERING.md opens with a 3-step quick start: turn on
    dns_server, point the router's DHCP DNS at Home Assistant and check it,
    then power-cycle the heater. The README links straight to it.
  • STEERING.md gains Before you start (find and reserve the heater's IP)
    and the log lines that show Local Connect is armed.
  • Dropped: the README's old release steps, superseded by
    docs/RELEASING.md.

v0.7.7

Choose a tag to compare

@github-actions github-actions released this 27 Oct 01:36
v0.7.7
06d4e34

Fixed (relay, from a code review)

  • A damaged LAN key file no longer strands the heater.
    • Before: a corrupt or wrong-shaped lanip.json made the relay mint a
      new key over it, so the module was left holding the old one.
    • Now: the relay renames the file to lanip.json.corrupt-<time>, logs
      an error, then mints a key (and /state counts it in key_quarantined).
    • Writes: the key file, the write queue and the DSN evidence files are
      written atomically and created 0600.
  • Telemetry always reaches Ayla: a bug while decoding a report no longer
    turns the module's POST into a 500. It's counted (ingest_errors) and the
    report is forwarded anyway.
  • Telemetry decoding:
    • an empty part no longer shifts later blocks into the wrong place;
    • a block too short to decode is a counted decode error, not a stream of
      zeros.
  • Firmware hold covers every path: OTA commands in commands.xml replies
    are held even for untrusted or foreign-device requests.
  • Several heaters:
    • while the primary's device ID is still unknown, another heater's
      /devices/<id>/ requests no longer reach the primary;
    • the refused-heater list is capped at 64.
  • The built-in DNS server:
    • answers only private, link-local and loopback addresses;
    • caps concurrent forwards (128) and each client's rate (100 per second);
    • drops duplicate retransmits, responses and runt packets;
    • answers other opcodes with NOTIMP;
    • refuses to start when dns_upstream points back at itself or has a bad
      port;
    • a client asking over TCP now gets the full answer: a truncated UDP reply
      is re-asked over TCP;
    • drops are counted in /state dns.dropped;
    • new DNS_HOST sets the listening address.
  • Robust loading: a queue or device file of the wrong shape no longer
    crashes start-up, and malformed queued writes are dropped.
  • Malformed LAN updates: they're refused with a 400 instead of a 500, and
    don't mark the session verified.
  • Local Connect callback: the callback address the module is given is
    worked out again when the module's address changes. A relay_ip that is
    set is kept.
  • Suspected encryption desync: after 3 unanswered LAN deliveries in a row,
    the relay logs one warning and counts it (lan.suspected_desyncs). It
    doesn't reconnect.

Fixed (integration, from a code review)

  • Options dialog on HA before 2024.11: it crashed, because older HA
    doesn't give the flow its entry. It now looks the entry up itself.
  • Odd relay data can't take the integration down.
    • Before: one unexpected value in the relay's /state (a string where
      a number belongs, a list where an object belongs) could stop every later
      listener and leave entities frozen.
    • Now:
      • values are type-checked;
      • numeric sensors show unknown rather than raise;
      • each repair check is isolated;
      • a reply that isn't a JSON object is a normal update failure.
  • No write to the wrong heater: a write for a heater the relay no longer
    lists is refused. It used to go to the primary heater.
  • The same relay can't be added twice: its URL is the entry's unique ID.
  • Tank temperatures keep long-term statistics: top, bottom and average
    now have a measurement state class.
  • Setting the operation to "off" is refused with a message to use away
    mode. On older HA it used to reach the relay.
  • Mode and Heating element show their icons on HA 2024.1 (icon
    translations arrived in 2024.2).
  • Connections: responses are always released, including on errors.

Docs

  • DNS steering:
    • the built-in dns_server is marked tested (live since 0.5.5), with its
      timeline;
    • a check step to run before the power cycle, taken from a fresh-install
      dry run of 0.7.6, with the log lines to expect;
    • dns_upstream for gateways that don't answer DNS;
    • relay_ip for a heater on its own VLAN.
  • README:
    • one power-cycle time (30 s) and the full log sequence;
    • the IP reservation is optional with DNS steering;
    • VLANs are workable;
    • the dashboard wording matches 0.7.5.
  • The add-on's Documentation tab now has content: relay/ADDON.md is
    renamed to relay/DOCS.md, the file Home Assistant shows. It covers DNS and
    NAT steering, where the keys live, and the update caveat.
  • Corrections across the docs (from a review against the code):
    • STEERING.md no longer says mesh or ISP routers can't be used. They can,
      with dns_server.
    • The trusted peers are listed per port: 8888 doesn't trust the gateway.
    • The LAN key's path is /data/heaters/<DSN>/lanip.json.
    • Across VLANs needs DNAT and SNAT for the callback, plus relay_ip.
    • The own-resolver steps are under their own heading, and say that
      listen_port: 443 is needed.
    • The README's configuration table lists the remaining environment
      variables (DNS_REWRITE for other models, DNS_PORT, CLOUD_IDLE_AFTER,
      and others).
    • Changelog links are absolute, so the add-on's copy works too.
  • NAT is no longer presented as the only option:
    • "How it works" names DNS and NAT as the two ways to steer the heater;
    • the add-on's docs page describes dns_server, replacing the pre-0.5.5
      listen_port: 443 + module_ip recipe;
    • the troubleshooting rows cover both;
    • Uninstall says to restore the router's DHCP DNS before removing the
      add-on.
      Otherwise every device using it loses DNS.

CI and tests (from a code review)

  • Home Assistant 2024.1 is tested. hacs.json promised it, but nothing
    checked it. A CI job now runs the integration tests on HA 2024.1.5, and it
    found the options-dialog crash fixed above (integration).
  • Least-privilege releases:
    • the release workflow is split into read-only verify, test and image jobs,
      and a publish job, the only one that can write;
    • a tag must be on main, and the release reruns the full test matrix and
      the image build;
    • checkouts don't keep the token.
  • Pinned tools: dev tools are hash-locked, the HA test harness is pinned,
    and so is the busybox test image.
  • Stricter gates:
    • branch coverage, with a 98.5% minimum;
    • a PR that bumps the version runs the release checks;
    • the changelog section for a release needs at least one entry;
    • missing openssl fails CI instead of skipping the TLS tests.
  • Tests:
    • relay modules are imported one way only, so there's one copy of each;
    • environment variables are isolated per test;
    • DNS tests listen on loopback;
    • the freeze thresholds are checked at their exact boundaries;
    • DNS over TCP is covered.
  • Release order is documented in docs/RELEASING.md: the add-on ships
    from main, so a version bump has to be green before it merges.

v0.7.6

Choose a tag to compare

@github-actions github-actions released this 26 Oct 21:47
v0.7.6
0787802

Fixed

  • Code-scanning cleanup: all 11 open CodeQL alerts on main, none of
    them from 0.7.5.
    • A telemetry report that fails to decode is no longer swallowed
      silently.
      The relay keeps going, as before. It now counts the failure
      (decode_errors on /state) and logs the exception type.
    • Log lines no longer echo outside text (py/log-injection):
      • allowing a firmware update logs the number allowed, not the key from
        the request;
      • the heater-directory rename doesn't log the DSN-derived name.
    • Tests:
      • a DNS test binds 127.0.0.1 instead of every interface;
      • the clock patches use string targets instead of re-importing modules;
      • unnecessary lambdas are gone;
      • a cancelled task is checked with gather.