Repository navigation
Releases
Releases · ericboehs/ha-icomm-local
v0.8.1
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.jsand 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
wwwfolder, 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.
- The cause: the cards were added to pages only once the integration
v0.8.0
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
Fixed
- No more lost request and
BadHttpMethoderror 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 aGETthat'sContent-Length: 0, which completes the request. - The quirk: only
/pingalready 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/stateasstray_headers. - Why 0.7.10 failed: it dropped the record always, which cut the
end off every normal request.
- How the heater sends requests: each request's headers go in two
v0.7.12
Fixed
- A request from the relay's own host no longer counts as the heater
reaching the cloud. Acurlto the relay's/pingfrom Home Assistant's
host clearedcloud_downand 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
Fixed
- Reverted 0.7.10's stray-header guard: it stopped the heater reaching the
cloud after a reconnect. With the guard, the heater'sGET /pingwas 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 harmlessBadHttpMethoderror in the
log after/ping. Thestray_headerscount is gone from/state.
v0.7.10
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: 0line on its own, after the request has already ended. - What it cost: the relay took that line for the start of a new request,
loggedBadHttpMethod … 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
/stateasstray_headers.
- The quirk: after
v0.7.9
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_sattribute 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
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
saidunknown: too few dials observed yetand 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
degradedwith codecloud_down, and the repair
Water heater isn't reaching Ayla./statehascloud_probe, and the
Relay health sensor shows it as an attribute.CLOUD_PROBE=0turns it
off;CLOUD_PROBE_AFTERsets the interval.
- The problem: after a relay outage of a few minutes (a reboot of the
- 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.
- What it does: when the cloud side is down, Home Assistant turns the
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:- docs/USING.md: every entity, writes, the dashboard, outages, uninstalling in detail;
- docs/TROUBLESHOOTING.md;
- docs/CONFIGURATION.md: every option, and running without the add-on;
- docs/HOW_IT_WORKS.md: the design and security;
- docs/DEVELOPMENT.md.
- 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
Fixed (relay, from a code review)
- A damaged LAN key file no longer strands the heater.
- Before: a corrupt or wrong-shaped
lanip.jsonmade 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/statecounts it inkey_quarantined). - Writes: the key file, the write queue and the DSN evidence files are
written atomically and created 0600.
- Before: a corrupt or wrong-shaped
- 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.xmlreplies
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.
- while the primary's device ID is still unknown, another heater's
- 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_upstreampoints 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
/statedns.dropped; - new
DNS_HOSTsets 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. Arelay_ipthat 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.
- Before: one unexpected value in the relay's
- 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_serveris 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_upstreamfor gateways that don't answer DNS;relay_ipfor a heater on its own VLAN.
- the built-in
- 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.mdis
renamed torelay/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,
withdns_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: 443is needed. - The README's configuration table lists the remaining environment
variables (DNS_REWRITEfor other models,DNS_PORT,CLOUD_IDLE_AFTER,
and others). - Changelog links are absolute, so the add-on's copy works too.
- STEERING.md no longer says mesh or ISP routers can't be used. They can,
- 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_iprecipe; - 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.jsonpromised 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.
- the release workflow is split into read-only verify, test and image jobs,
- 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
frommain, so a version bump has to be green before it merges.
v0.7.6
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_errorson/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.
- allowing a firmware update logs the number allowed, not the key from
- 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.
- A telemetry report that fails to decode is no longer swallowed