Skip to content

mac_mapping_* entries with non-sequential numbering are silently dropped (only first ~25 imported) #43

Description

@mok726

First of all, thanks for this great tool. I ran into an issue and used AI to help me resolve it, so I'm sharing here my findings and solution, hope it helps

Description

When mac_mapping_* entries in configuration.yaml use non-sequential numbering (e.g., mac_mapping_1, _2, _3, _6, _8...), only the first batch up to the first gap is imported. All mappings after the gap are silently dropped.
In my case, I have 91 mappings defined in YAML but only 15 (with ID less or equal 25) were saved to the config entry.

Root Cause

Both config_flow.py and sensor.py iterate MAC mappings sequentially and break on the first missing key:

config_flow.py — the while True loop starting at i=26:

i = 26
while True:
    key = f"mac_mapping_{i}"
    if key in yaml_config:
        ...
        i += 1
    else:
        break  # Stops here at first gap

sensor.py — same pattern in async_setup_entry:

i = 25
while True:
    key = f"mac_mapping_{i+1}"
    if key in config_entry.data:
        ...
        i += 1
    else:
        break  # Stops here at first gap

This means any gap in numbering (e.g., no mac_mapping_26 because numbering jumps to _28) causes everything after to be lost.

Additionally
config_flow.py passes user_input directly to async_create_entry(). Since all mac_mapping_* fields are vol.Optional, any field the user doesn't explicitly fill in is dropped from user_input. With 90+ fields this means most mappings are lost even if the form renders them.

Suggested Fix

config_flow.py — collect all mac_mapping_* keys dynamically instead of sequential iteration:

Replace the for/while loops with:

mac_keys = sorted(
    (k for k in yaml_config if k.startswith("mac_mapping_")),
    key=lambda k: int(k.split("_")[2]),
)
for key in mac_keys:
    data_schema_dict[vol.Optional(key, description={"suggested_value": yaml_config.get(key)})] = str

And merge YAML mappings on form submit so optional fields left blank aren't lost:

if user_input is not None:
    merged = dict(user_input)
    for key, value in yaml_config.items():
        if key.startswith("mac_mapping_") and key not in merged:
            merged[key] = value
    return self.async_create_entry(title="Network Scanner", data=merged)

sensor.py — same dynamic collection:

mac_keys = sorted(
    (k for k in config_entry.data if k.startswith("mac_mapping_")),
    key=lambda k: int(k.split("_")[2]),
)
mac_mappings_list = [config_entry.data.get(k, "") for k in mac_keys]

NetworkScannerOptionsFlow — same pattern for the options flow form.

Steps to Reproduce

  1. Define mac_mapping_* entries in configuration.yaml with non-sequential numbers (e.g., _1, _2, _3, _6, _8, _28, _50...)
  2. Set up the integration via the UI
  3. Check config entry data — only mappings up to the first gap are saved

Environment

  • Integration version: v1.0.7
  • Home Assistant version: 2025.10.3

Regards, Marianok

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions