Skip to content

ux: group device filter by home (structural redesign + wording clarification) - #2920

Open
jingyulong wants to merge 4 commits into
al-one:masterfrom
jingyulong:improve-home-filter-wording
Open

ux: group device filter by home (structural redesign + wording clarification)#2920
jingyulong wants to merge 4 commits into
al-one:masterfrom
jingyulong:improve-home-filter-wording

Conversation

@jingyulong

@jingyulong jingyulong commented Aug 13, 2026

Copy link
Copy Markdown

Background: how the problem was discovered

While setting up the integration in a real environment, a Mi account with two homes (the user's own home with 23 devices + a shared home with 15 devices) ended up importing the shared home's devices into HA and none of the user's own — only discovered when comparing device MACs/IPs against the local network.

Reviewing the filter flow surfaced several UX problems:

  1. The device list was one flat checkbox list mixing devices from all homes, so home membership could only be guessed from IP prefixes;
  2. The 筛选家庭 (留空保存) / Filter Home (Leave blank to save) label was misleading — blank actually means no home filtering at all (import every home), not "safe";
  3. The exclude/include radio labels (排除 Exclude / 包含 Include) carried no semantics — unclear what checking a box does;
  4. home_ids was a two-step filter and was not persisted.

Changes

Per-home grouped device filtering (structural redesign)

  • Removed the flat did_list multi-select and the two-step home_ids filter; each home now gets its own multi-select (home__<home name>), and home-less devices get their own home__unassigned group, always rendered last;
  • On submit, all home__* groups are merged back into did_list for persistence (handled in both the initial config flow and the options flow);
  • Devices within a group are sorted by name; the existing selection is preserved in exclude mode (otherwise the default "nothing checked" would mean removing all devices).

Rendering notes

Home-group selectors use cv.multi_select; the HA frontend picks the rendering automatically: < 6 options → inline checkboxes, ≥ 6 options → dropdown (SHOW_ALL_ENTRIES_LIMIT = 6 in ha-form-multi_select.ts). No explicit selector is needed, and small groups (like unassigned devices) naturally render as checkboxes at the end.

Wording clarifications

  • Step description now spells out exclude/include semantics: in exclude mode, "checking none integrates all devices";
  • Exclude/include radio labels are now self-explanatory: 排除 Exclude(勾选 = 不接入这些设备) / 包含 Include(只接入勾选的设备);
  • New group label for home-less devices: 未分配家庭的设备(附近的/离线的) / Unassigned devices (nearby/offline).

Tests

New tests/test_cloud_filter_grouped.py (3 cases):

  1. Grouped schema: devices are grouped per home; did_list / home_ids no longer appear in the schema;
  2. Preserved selection: the options flow pre-fills home__* defaults from the saved did_list;
  3. Submit merge: home__* values are merged back into did_list and persisted.

Local pytest passes: 21 passed (including options flow and translation cases).

Verification

Tested against a real environment (Home Assistant 2026.8.1, Mi account with two homes / 38 devices) via options → update device list: home groups rendered as dropdown multi-selects, the unassigned group rendered as checkboxes and stayed last; after saving, 26 devices were imported, all from the intended home with no cross-home leakage.

Screenshots

Before After
before after

(Sensitive data such as account IDs and IPs is masked in the screenshots.)

Related discussion: #2921

@jingyulong jingyulong changed the title i18n: clarify home filter wording in device filter flow (zh-Hans + en) i18n: clarify confusing wording in device filter flow (home filter + exclude/include modes) Aug 13, 2026
The cloud device filter step mixed devices from every home into one
flat multi-select, with a confusing two-step "home_ids" filter that
required submitting the form twice. Users routinely imported devices
from shared homes by accident.

- Render one multi-select per home, keyed home__<home name>, with
  devices that have no home grouped under "Default Home"
- Remove the home_ids two-step filter from the form
- Merge all home__* groups back into did_list on submit (both the
  config and options flows)
- Add tests for the grouped schema and the submit merge

Wording fixes for the filter step are in PR al-one#2920.
- Devices without a home assignment (nearby/offline devices) are no
  longer shown in a synthetic 'Default Home' group
- Keep the exclude/include labels and filter_did description in sync
  with PR al-one#2920
- Replace the flat cv.multi_select (inline checkbox list) with
  selector.SelectSelector(multiple, mode=dropdown) so each home group
  renders as a standard dropdown with removable chips
- Drop the redundant filter_did field description; fold the
  "checking none includes all" warning into the step description
- Label home-less devices as "Unassigned devices (nearby/offline)"
  via a static translation key, placed after the real homes
The HA frontend renders multi_select fields automatically: groups with
fewer than 6 options as checkboxes and larger groups as dropdowns
(SHOW_ALL_ENTRIES_LIMIT in ha-form-multi_select), so the explicit
SelectSelector(dropdown) is redundant.
@jingyulong
jingyulong force-pushed the improve-home-filter-wording branch from 9bb9824 to 42771cc Compare August 13, 2026 15:37
@jingyulong jingyulong changed the title i18n: clarify confusing wording in device filter flow (home filter + exclude/include modes) ux: group device filter by home (structural redesign + wording clarification) Aug 13, 2026
@al-one

al-one commented Aug 24, 2026

Copy link
Copy Markdown
Owner

Thanks for the PR! I reviewed the changes. Here is my bilingual feedback (English / 中文). Overall the per-home grouping is a clear improvement. One critical bug and a couple of minor nits below.

谢谢这个 PR!我复查了变更,以下是双语反馈(英文 / 中文)。总体而言,按家庭分组是一处清晰的改进。下面是一个严重缺陷和几个小问题。


🔴 Critical bug: users cannot clear the device filter via the UI / 严重缺陷:用户无法通过 UI 清空设备过滤

Location 位置: config_flow.py L434 (initial flow 初始流) & L1152 (options flow 选项流)

The merge logic only writes did_list when it is truthy:

合并逻辑只有在 did_list 非空时才会写入:

did_list = []
for k in list(user_input.keys()):
    if k.startswith('home__'):
        did_list += user_input.pop(k) or []
if did_list:
    user_input['did_list'] = did_list   # skipped when did_list == [] / 当 did_list 为空时被跳过

Repro 复现: a user with did_list=['d1','d2'] and filter_did='exclude' unchecks every device across all home__* groups to clear the filter and submits. did_list becomes [], if did_list: is falsy, user_input['did_list'] is never set, and config_data.update() keeps the previously saved did_list. The user expects to integrate all devices but d1/d2 stay excluded, with no UI path to recover (short of removing and re-adding the config entry).

假设用户有 did_list=['d1','d2']filter_did='exclude',在选项流中取消勾选所有 home__* 分组里的设备,想借此清空过滤并提交。此时 did_list 变成 []if did_list: 为假,user_input['did_list'] 永远不会被设置,config_data.update() 保留了之前保存的 did_list。用户期望接入全部设备,但 d1/d2 仍然被排除,而且除了删除配置重新添加之外,UI 上没有其他恢复途径。

Suggested fix 修复建议 — always write the key / 始终写入该键:

if did_list:
    user_input['did_list'] = did_list
else:
    user_input['did_list'] = []

or simply / 或更简单:user_input['did_list'] = did_list


🟡 Minor: duplicated home__* merge logic / 轻微:home__* 合并逻辑重复

Location 位置: config_flow.py L430-435 & L1148-1153

The identical "pop home__* keys and concatenate into did_list" block appears in both the initial flow and the options flow. Any future fix (including the one above) has to be applied twice; missing one copy makes the two flows diverge. Consider extracting a small helper, e.g. a @staticmethod _merge_home_groups(user_input).

"弹出 home__* 键并合并进 did_list"的相同代码块在初始流和选项流中各出现一次。任何未来的修复(包括上面那个)都需要改两处;漏改一处就会导致两个流行为不一致。建议提取一个小辅助方法,例如 @staticmethod _merge_home_groups(user_input)


🟡 Minor: translation files missing trailing newline / 轻微:翻译文件缺少尾随换行符

Location 位置: translations/en.json and translations/zh-Hans.json (last line)

Both files are committed without a trailing newline (the diff shows \ No newline at end of file), while master had one. This will trip pre-commit's end-of-file-fixer (or stricter text-lint hooks in CI). Please add the trailing newline back.

两个文件提交时都缺少尾随换行符(diff 中显示 \ No newline at end of file),而 master 上原本是有换行符的。这会触发 pre-commitend-of-file-fixer(或 CI 中更严格的文本 lint 钩子)。请把末尾换行符补回去。


⚪ False alarm: home_ids filter / 非问题:home_ids 过滤

Location 位置: config_flow.py L219

The new if home_ids and d.get('home_id') not in home_ids: could look like a regression vs. the old walrus version, but home_ids is no longer in the schema (the field was removed from the grouped flow), so it is always [] and the branch never fires. Not a bug — just noting it since it reads as suspicious at first glance.

新的 if home_ids and d.get('home_id') not in home_ids: 相比旧版的海象表达式看起来像是回归,但 home_ids 已不再出现在 schema 中(该字段已从分组流里移除),因此它恒为 [],这个分支永远不会触发。不是 bug——只是说明一下,因为它乍一看有歧义。


Conclusion 结论: please fix the 🔴 bug before merging — it's a real regression that affects users who want to turn the filter off. The rest are code-quality suggestions. The grouped-by-home schema and the wording clarifications look good, and the tests cover the intended behavior well.

请务必在合并前修复 🔴 这个缺陷——它是真实的回归,会影响想关闭过滤的用户。其余都是代码质量建议。按家庭分组的 schema 和文案澄清都很好,测试也很好地覆盖了预期行为。


Generated by Claude Code while reviewing PR #2920; verified against the pr-2920 branch. / 由 Claude Code 在复查 PR #2920 时生成,已在 pr-2920 分支上核实。

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