diagnostics: report discovered cloud cycles in full, names included

Trimming the names out of the dump last commit was the wrong call. Half of
what goes wrong with this feature is a configuration question -- which
programs got named, which Download course was confirmed, whether a payload
was ever captured for a slot the device advertises -- and none of that is
answerable from the payloads alone. A report saying "my download cycle isn't
showing up" is exactly the case that needs it.

The names are still the user's own words, so this block stays the one place
they appear; they reach a dump only because its owner chose to download and
share it. `resources` is unaffected either way -- it goes on reporting
exactly what the appliance said, via device_resources().
This commit is contained in:
Marc Billow committed 2026-08-10 03:33:28 +00:00
1 parent 2265c52c77
commit cef187b7af
2 files changed
+25 -18

No files matched your search

+12 -9
View File
@@ -140,17 +140,20 @@ async def async_get_config_entry_diagnostics(
"codes": coordinator.learned_snapshot(),
},
# Cloud "Download" programs discovered on this device (issue #342),
# reported separately for the same reason as learned_modes above.
# Payloads are the useful part for triage -- they are the only record
# of what a downloaded program contains. The names are not included:
# they are the user's own words, and a dump gets pasted into public
# issues. Which slots are named is still visible, which is all the
# triage question ("is this set up?") actually needs.
# reported separately from `resources` for the same reason as
# learned_modes above -- the dump there stays exactly what the device
# said, and this is what the integration made of it.
#
# Reported in full, names included. Half of what can go wrong with
# this feature is a configuration question -- which programs got
# named, which Download course was confirmed, whether a payload was
# ever captured for a slot the device advertises -- and none of that
# is answerable from the payloads alone. The names are the user's own
# words, so this is the one place they appear; they reach a dump only
# because its owner chose to download and share it.
"cloud_courses": {
"advertised_slots": cloudcourse.advertised_slots(coordinator.cloud_course_rep()),
"download_course": cloud_courses["download_course"],
"payloads": {slot: rec["blob"] for slot, rec in cloud_courses["slots"].items()},
"named_slots": sorted(s for s, rec in cloud_courses["slots"].items() if rec["name"]),
**cloud_courses,
},
"integration_version": integration.version,
"smartthings_local_version": stl_version,
+13 -9
View File
@@ -346,17 +346,21 @@ async def test_the_synthetic_field_never_reaches_diagnostics(
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator
diag = await async_get_config_entry_diagnostics(hass, entry)
dump = json.dumps(diag)
assert cloudcourse.FIELD not in dump
assert cloudcourse.FIELD not in json.dumps(diag["resources"])
# The device's own tokens are still there -- resources stays what it said.
assert "CloudExtraCourse_0A5C286B2D0C55301A" in dump
assert "CloudExtraCourse_0A5C286B2D0C55301A" in json.dumps(diag["resources"])
# The store is reported in its own block, so a triager can see the
# payloads -- but not the user's chosen names.
assert "Marc's weekend towels" not in dump
assert diag["cloud_courses"]["payloads"]["55"] == SPORTS
assert diag["cloud_courses"]["named_slots"] == ["55"]
assert diag["cloud_courses"]["download_course"] == "87"
# The store is reported in full in its own block: what was discovered,
# what the user named it, and which course they confirmed. Half of what
# goes wrong here is a configuration question and none of it is
# answerable from the payloads alone.
cloud = diag["cloud_courses"]
assert cloud["advertised_slots"] == ["0A", "5C", "28", "6B", "2D", "0C", "55", "30", "1A"]
assert cloud["download_course"] == "87"
assert cloud["slots"]["55"] == {"blob": SPORTS, "name": "Marc's weekend towels"}
# A slot seen but never named is reported too -- that gap is the thing a
# "why isn't my download cycle showing up" report needs to reveal.
assert cloud["slots"]["6B"] == {"blob": JEANS, "name": ""}
async def test_the_debug_read_service_reports_only_device_state(hass: HomeAssistant):