Skip to content

luci-app-usteer: per-SSID settings tabs and known-station tracking - #8869

Open
grzegorz914 wants to merge 2 commits into
openwrt:masterfrom
grzegorz914:usteer-per-ssid-known-stations
Open

luci-app-usteer: per-SSID settings tabs and known-station tracking#8869
grzegorz914 wants to merge 2 commits into
openwrt:masterfrom
grzegorz914:usteer-per-ssid-known-stations

Conversation

@grzegorz914

@grzegorz914 grzegorz914 commented Jul 25, 2026

Copy link
Copy Markdown

Summary

Companion LuCI frontend for openwrt/usteer#22 (per-SSID configuration overrides and known-station tracking with cold-start exploratory candidates).

  • Add a settings tab per configured SSID (nested under Settings), each bound to its own usteer_ssid UCI section, mirroring the daemon's new per-SSID configuration overrides. The SSID list determines which tabs are shown live, without a page reload.
  • Add a "Known stations" tab showing the daemon's persisted best-signal-ever-seen data per node, with a Delete button per local entry (new delete_known ubus call, granted in the app's rpcd ACL) and a description of the cold-start "exploratory" placeholder semantics. Remote nodes' entries have no Delete button, since removing one only clears the local synced copy, not the owning peer's own record.
  • Add live auto-refresh for newly seen MAC addresses on the Hearing map, removing the previous "refresh the page" notice.
  • Move the "Band steering interval" and its dependent fields to the per-SSID tab; setting the interval to 0 disables band steering for that SSID, matching the daemon's only convention for it (config.band_steering_interval).
  • Improve labels, descriptions, placeholders and default-value documentation across the existing global settings fields (units on time/signal fields, dual SNR/dBm convention explained where used).
  • Update the translation template and fully translate the new/changed strings into Polish.

Test plan

  • Built and installed on 4 production APs (mixed ipq806x/ramips) for this session
  • Verified per-SSID tabs render correctly, save to the right usteer_ssid UCI section, and fields fall back to the global setting when left blank
  • Verified Known stations tab, Delete button, and Hearing map live-refresh against real client activity

Update

Extended the per-SSID tab with the remaining station/node-scoped policy fields that were still global-only: sta_block_timeout, local_sta_timeout, max_retry_band, seen_policy_timeout, assoc_steering, probe_steering, max_neighbor_reports, load_balancing_threshold, steer_reject_timeout, roam_process_timeout, roam_kick_delay, initial_connect_delay and node_up_script (moved from the global Settings tab, matching the daemon's extended usteer_ssid overrides).

Also fixed a real bug found after the first round landed: the SSID tab nesting was only wired to the very first render - Map.prototype.save() re-renders the map's content in place on every Save via the same renderContents() used for the initial render, which silently undid the relocation (SSID tabs would jump back into the top-level tab bar after any Save). Fixed by wrapping renderContents() itself instead of reacting only to the first m.render() call, so relocation reruns on every render/Save/Reset.

  • Verified per-SSID tabs (including the newly-moved batch) render correctly, save to the right usteer_ssid UCI section, and fall back to the global setting when left blank
  • Verified tab nesting survives Save and Save & Apply, not just the initial page load

Update 2

The bullet above about "improving" the existing global settings fields undersold what actually happened to them: all 35 moved to the per-SSID tab, and the global Settings tab no longer has them at all - it now only carries the whole-instance options (network, syslog, debug level, remote-sync tuning, SSID list, etc.).

That created a real gap: on a daemon that reads the new usteer_ssid sections, the shared usteer section's values (which the daemon still falls back to whenever a per-SSID field is left blank) had nowhere left to be edited or cleared from LuCI - only visible as a placeholder hint on each per-SSID field. Added a new Defaults tab, bound to the shared usteer section, holding faithful copies of the same 35 fields in the exact order the per-SSID tab uses - so there's still a place to actually manage the fallback every SSID inherits from.

Defaults nests first in the same sub-tab bar as the per-SSID tabs (under Settings) on a daemon with usteer_ssid support, and per-SSID Value field placeholders now read that same shared value live (with its unit) instead of always showing usteer's compiled-in default regardless of what's actually configured. On a daemon without usteer_ssid support, Defaults stays a plain top-level tab instead - the per-SSID tabs never get created there, so its description says "usteer's settings" rather than talking about per-SSID fallbacks that don't exist on that build.

  • Verified Defaults renders with all 35 fields in per-SSID order, edits save to the shared usteer section, and per-SSID placeholders reflect a value set there
  • Verified Defaults nests correctly under Settings alongside per-SSID tabs, survives Save/Reset, and falls back to a plain top-level tab with the legacy-daemon wording when usteer_ssid isn't detected in the init script

@openwrt openwrt Bot added the not following guidelines Pull request does not follow formatting guidelines label Jul 25, 2026
@grzegorz914
grzegorz914 force-pushed the usteer-per-ssid-known-stations branch from 279269a to ff4374e Compare July 25, 2026 09:15
@openwrt openwrt Bot removed the not following guidelines Pull request does not follow formatting guidelines label Jul 25, 2026

@openwrt-ai openwrt-ai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the two new commits (per-SSID settings UI + translation refresh). The backend dependency (openwrt/usteer#22) is disclosed in the PR body, so the new UCI options / delete_known ubus call are not flagged as frontend-only. Both commit messages match their diffs. Two inline questions about the per-SSID section handling below.


Generated by Claude Code

Comment thread applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js Outdated

@openwrt-ai openwrt-ai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the two new commits since ff4374e (aab5f29 lazy per-SSID section creation + collision-free ids, 18f43f1 re-nest SSID tabs after every render). Both previous inline findings are addressed. The lazy-write approach checks out against form.js/uci.js: uci.set() is indeed a no-op on a section that doesn't exist yet, so creating the section inside the write() wrapper rather than at render time is the right hook, and AbstractValue.write() honours ucisection, so the wrapper lands in the correct section.

One follow-on issue with the collision fix and two nits inline.

Commit checks

  • 18f43f1 "luci-app-usteer: re-nest SSID tabs after every render, not just the first" — the body's claim "Pre-existing bug, not introduced by the per-SSID tabs commit itself (reproduced on an older build too)" doesn't hold against this tree: master (96a255d) has no tab-relocation code in usteer.js at all — no data-tab DOM manipulation, no knowndevices tab, no relocateSsidTabs equivalent. All of it is added by cd58ab1 in this same PR, so this is fixing a bug introduced two commits earlier in the series, not a pre-existing one. Worth rewording (or dropping the sentence) so the history doesn't mislead someone bisecting later.

Generated by Claude Code

Comment thread applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js Outdated
@grzegorz914
grzegorz914 force-pushed the usteer-per-ssid-known-stations branch from 18f43f1 to cae4bcd Compare July 26, 2026 06:03

@openwrt-ai openwrt-ai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the new work since 18f43f1. The branch was force-pushed: e1488b2 squashes the previously reviewed cd58ab1 + aab5f29 + 18f43f1 together with this round's ssidSectionId() fixes, cae4bcd carries the i18n, and 2a5c682 + 1443c8e are genuinely new. All four commit messages match their diffs, so no Commit checks section.

The ssidSectionId() rework addresses the previous round's findings correctly — ids are now resolved from each existing usteer_ssid section's own ssid option before any allocation, usedSsidSectionIds is seeded from the existing section names, and both maps are Object.create(null). uci.load('usteer') is part of load(), so uci.sections() has real data by the time render() calls ssidSectionId(). The known_stations_timeout o.default = '0' removal is right: renderWidget() seeds the input with this.default when cfgvalue is null, which is what was pre-filling a literal 0 over the placeholder.

The band steering toggle itself behaves as the commit message describes — with default = '1' and optional, Flag.parse() removes the option when checked and writes '0' when unchecked, so the daemon's implicit on-by-default is preserved and no section is created just by leaving the box ticked. One issue with how the four dependent fields are wired to it, plus one series nit, inline.


Generated by Claude Code

Comment thread applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js Outdated
Comment thread applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js Outdated
@grzegorz914
grzegorz914 force-pushed the usteer-per-ssid-known-stations branch from 1443c8e to 3d84344 Compare July 26, 2026 17:02

@openwrt-ai openwrt-ai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the force-pushed series (now b3f29ea + 3d84344, replacing e1488b2/cae4bcd/2a5c682/1443c8e). The i18n squash asked for last round is done: the only non-reference change in the po files since 1443c8e is one re-wrapped Polish line in po/pl, and the .pot references line up with the final usteer.js (e.g. Band steering → usteer.js:667). Both commit messages match their diffs — b3f29ea touches only usteer.js, 3d84344 only the po files — so no Commit checks section.

The hasBandSteeringToggle hoist resolves the previous round's finding correctly: all four depends(sid + '_band_steering_enabled', '1') sites are now behind the same Initscript.includes() check that guards the toggle itself, so on a usteer build without band_steering_enabled the four parameter fields stay visible and parse() no longer strips their stored values.

New this round: the delete_known plumbing added by b3f29ea looks like it cannot work as shipped — the method is missing from the app's rpcd ACL, and the RPC declaration lacks reject: true, which turns any failure into a silent fake success. Details plus one question about remote nodes inline.


Generated by Claude Code

Comment thread applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js Outdated
@grzegorz914
grzegorz914 force-pushed the usteer-per-ssid-known-stations branch from 3d84344 to 17fead0 Compare July 27, 2026 05:49

@openwrt-ai openwrt-ai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the force-pushed series (now 2516ae3 + 17fead0, replacing b3f29ea/3d84344). The delta since 3d84344 is small: reject: true on the delete_known declaration, the catch() now surfacing err.message via ui.addNotification(), the isLocal flag on collectKnownStaEntries(), and the ACL grant. Both commit messages match their diffs — 2516ae3 touches only usteer.js and acl.d/luci-app-usteer.json, 17fead0 only the po files — so no Commit checks section.

All three findings from the previous round are resolved correctly:

  • reject: true is set, so handleCallReply() now raises on a non-zero ubus status instead of resolving with the status code, and the catch() branch is reachable. L.raise('RPCError', …) produces a real Error, so err.message is populated.
  • "usteer": [ "delete_known" ] landed under write.ubus, which is the right block for a state-mutating call.
  • The remote-node question is answered by dropping the button on Remoteinfo rows rather than shipping one that silently undoes itself on the next remote_update_interval; both call sites (lines 200-201 and 802-803) pass the flag consistently.

The i18n commit is now purely reference-line churn relative to 3d84344 — no msgid or msgstr changed — and the regenerated references match the final file (e.g. Delete → usteer.js:171). FormalityCheck is green on 17fead0.

One nit inline.


Generated by Claude Code

Comment thread applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js Outdated
@grzegorz914
grzegorz914 force-pushed the usteer-per-ssid-known-stations branch from 17fead0 to ced26b7 Compare July 27, 2026 17:00

@openwrt-ai openwrt-ai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the force-pushed series (now 9a7e04e + ced26b7, replacing 2516ae3/17fead0). The delta since 17fead0 is one line of usteer.js plus the i18n propagation of that single msgid. Both commit messages match their diffs — 9a7e04e touches only usteer.js and acl.d/luci-app-usteer.json, ced26b7 only the po files — so no Commit checks section.

The previous round's nit is addressed: the Known-stations help text now explains why remote rows carry no Delete button, and it gained the trailing period the two preceding sentences already had. The .pot reference still resolves to the right place (usteer.js:184), the new msgid is propagated to all 31 language files plus the template, and the Polish msgstr is a complete translation of the new wording. All three FormalityCheck runs are green on ced26b7.

Two i18n nits inline, both confined to po/pl/usteer.po; nothing blocking.


Generated by Claude Code

Comment thread applications/luci-app-usteer/po/pl/usteer.po Outdated
Comment thread applications/luci-app-usteer/po/pl/usteer.po Outdated
@grzegorz914
grzegorz914 force-pushed the usteer-per-ssid-known-stations branch from ced26b7 to c763e7d Compare July 28, 2026 05:32

@openwrt-ai openwrt-ai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the force-pushed series (now 8ae5020 + c763e7d, replacing 9a7e04e/ced26b7). 8ae5020 is byte-identical to the previously reviewed 9a7e04e — same tree, same message — so the entire delta is the po/pl/usteer.po rebuild plus the paragraph documenting it in c763e7d.

Both nits from the previous round are resolved, and the rebuild's claims check out against the tree:

  • Obsolete entries in po/pl/usteer.po drop from 52 to 31, and the resulting set is exactly the set po/de/usteer.po (and the other translated languages) gains from this PR — I compared the two files entry by entry, the msgid sets match with nothing extra on either side. The 21 PR-internal leftovers are gone.
  • All 165 live entries are still present, none went empty or fuzzy, and the #: reference lines and msgid ordering are identical to po/de/usteer.po, i.e. the file really was merged against this PR's final .pot (166 entries incl. header, matching po/templates/usteer.pot).
  • The Weblate header block (Project-Id-Version, PO-Revision-Date: 2026-03-29, Last-Translator, Plural-Forms, X-Generator) is preserved from master's copy rather than replaced by the template's, so the rebuild won't look like a translator change to Weblate.
  • The DostępneWypełniane revert landed and the Polish sentence now tracks the English Only populated while … again.

All three FormalityCheck runs are green on c763e7d. One wording issue on the new commit message below; nothing blocking.

Commit checks

  • c763e7d "luci-app-usteer: update translation template and Polish translation" — the new paragraph ends with "was diffed msgid-by-msgid against the carried-forward copy to confirm all 165 live translations are unchanged", but the commit does change one live translation: po/pl/usteer.po:587-590 restores Dostępne tylko gdyWypełniane tylko gdy in the usteer.js:184 description. That revert is correct and was the point of the previous round's second nit — but it is the only substantive msgstr change in the whole commit, and the message currently asserts the opposite. Everything else really is obsolete-entry churn, so a reader trusting the message would skip the one hunk that isn't. Suggest amending the last sentence to something like "…to confirm all 165 live translations are unchanged apart from the Only populated while … description, whose opening verb is restored to Wypełniane."

Generated by Claude Code

@grzegorz914
grzegorz914 force-pushed the usteer-per-ssid-known-stations branch from c763e7d to 51a1bf4 Compare July 28, 2026 16:44
@grzegorz914

Copy link
Copy Markdown
Author

Fixed in 51a1bf4 (amend, no file changes): reworded the commit message's last sentence to call out the Dostępne→Wypełniane revert as the one substantive msgstr change, instead of implying nothing changed.

@openwrt-ai openwrt-ai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed 1 new commit; no new issues found.

51a1bf4 replaces c763e7d as the i18n commit and 8ae5020 is carried over unchanged. git diff c763e7d..51a1bf4 is empty — the trees are byte-identical, so the entire delta is the commit-message amend the author described, exactly as stated in the PR comment.

The previous round's Commit checks finding is resolved: the last sentence of the rebuild paragraph now reads "…are unchanged apart from the "Only populated while ..." description, whose opening verb is restored to "Wypełniane"", and a new paragraph explains the choice. That matches the tree — po/pl/usteer.po:584-588 is the one live msgstr the rebuild changed relative to the carried-forward copy, and the message no longer asserts the opposite.

All three FormalityCheck runs are green on 51a1bf4.


Generated by Claude Code

@grzegorz914
grzegorz914 force-pushed the usteer-per-ssid-known-stations branch from 51a1bf4 to 1b0a234 Compare July 29, 2026 07:01

@openwrt-ai openwrt-ai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the force-pushed series (now 3a4cee1 + 1b0a234, replacing 8ae5020/51a1bf4). This round is substantially bigger than the last few: 3a4cee1 moves thirteen options (sta_block_timeout, local_sta_timeout, max_retry_band, seen_policy_timeout, assoc_steering, probe_steering, max_neighbor_reports, load_balancing_threshold, steer_reject_timeout, roam_process_timeout, roam_kick_delay, initial_connect_delay, node_up_script) out of the global Settings tab into addSsidTab() and reorders the per-SSID field list, and 1b0a234 regenerates the whole i18n set for it.

What checks out:

  • The .pot really was regenerated against the final usteer.js — spot-checked references resolve (Sta block timeout → usteer.js:576, Band steering threshold → usteer.js:602, Node up script → usteer.js:786), and the msgids dropped by this round's rewording (default false, empty means all, Default: 3) are gone from the template while Default: survives because usteer.js:780`` still uses it.
  • The obsolete-entry claim holds: po/pl, po/de, po/cs and po/ru all carry exactly 47 #~ msgid entries and 162 live ones, matching the 162-entry template, so the pl file is in line with the rest as the commit message says. No fuzzy and no empty msgstr in po/pl.
  • All three FormalityCheck runs are green on 1b0a234.

Two problems introduced by this round, plus one question and one nit, inline. The i18n one is the bigger of the two — the rebuild carried in 19 changed Polish translations of msgids this PR doesn't touch on the English side.

Commit checks

  • 3a4cee1 "luci-app-usteer: add per-SSID settings tabs and known-station tracking UI" — the message says "The four band-steering parameter fields depend on it and hide when disabled", but after this revision's reorder only three do: band_steering_threshold was moved up into the station-policy cluster at usteer.js:602-604`` and lost its if (hasBandSteeringToggle) o.depends(…) in the process. Either restore the dependency (see inline) or drop the "four" from the message.
  • 1b0a234 "luci-app-usteer: update translation template and Polish translation" — the message says the rebuilt po/pl/usteer.po was "verified msgid-by-msgid against the working copy first to confirm no live translation regressed", but 19 live msgstr differ from the previously reviewed 51a1bf4 copy, and 13 of those had matched master exactly (Event log types, Interval (ms) between sending state updates to other APs, Local sta timeout, Local sta update, Max neighbor reports, Measurement report timeout, Message types to include in log., Node up script, Remote node timeout, Remote update interval, Seen policy timeout, Setting the interval to 0 disables link-measurements., Sta block timeout). None of those msgids changed in English in this PR, so the merge should have left them alone. Details inline on po/pl/usteer.po:288.

Generated by Claude Code

Comment thread applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js Outdated
Comment thread applications/luci-app-usteer/po/pl/usteer.po Outdated
Comment thread applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js Outdated
Comment thread applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js Outdated
@grzegorz914
grzegorz914 force-pushed the usteer-per-ssid-known-stations branch from 1b0a234 to bf996b4 Compare July 29, 2026 17:00
@openwrt openwrt Bot added the not following guidelines Pull request does not follow formatting guidelines label Jul 29, 2026
@grzegorz914
grzegorz914 force-pushed the usteer-per-ssid-known-stations branch 2 times, most recently from daf4473 to c5b1468 Compare July 29, 2026 17:19
@openwrt openwrt Bot removed the not following guidelines Pull request does not follow formatting guidelines label Jul 29, 2026
@grzegorz914
grzegorz914 force-pushed the usteer-per-ssid-known-stations branch 3 times, most recently from 643bd11 to 9f1345e Compare July 29, 2026 19:59

@openwrt-ai openwrt-ai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the force-pushed series (now 46a98d9 + 9f1345e, replacing 3a4cee1/1b0a234). The code delta since 1b0a234 is 56 lines of usteer.js: hasBandSteeringToggle hoisted to the top of addSsidTab(), the band_steering_threshold dependency restored, local_sta_update moved out of the global Settings tab into the per-SSID tab, and assoc_steering/probe_steering converted from form.Flag to a three-way form.ListValue.

Both fixes from the previous round check out:

  • band_steering_threshold is guarded again (usteer.js:633-634), and hoisting the const above the field cluster removes the temporal-dead-zone problem; the toggle is still declared later in the same section, which is fine since depends() resolves by name at checkDepends() time.
  • The tri-state does what the new comment claims. With o.default = '' and optional, parse() removes the option when the blank choice is picked — so no usteer_ssid section is forced into existence — while '0' is a real write that can override a globally-enabled setting, which the old Flag could not express. And because the choice list contains '', ui.Select reuses that choice's own label instead of prepending a second -- Please choose -- entry, so the dropdown has exactly three items.

i18n: the template really was regenerated against the final file — the three new msgids resolve to usteer.js:615-617/621-623 and the reworded local_sta_update description to usteer.js:594. po/de, po/cs and po/ru are clean mechanical merges (exactly the four msgid additions and one removal, no msgstr touched). po/pl has the same msgid set as the template, 0 fuzzy and 0 untranslated, but also rewrites 10 live translations of msgids this PR leaves alone — inline.

All three FormalityCheck runs are green on 9f1345e.

Commit checks

  • 9f1345e "luci-app-usteer: update translation template and Polish translation" — the message scopes the regeneration to "the string changed by moving local_sta_update to the per-SSID tab", but this is the PR's only i18n commit: the template it produces adds 61 and drops 48 msgids relative to master, and this revision alone contributes three more (On, Off, Use global setting) beyond the local_sta_update rewording. The message also says it "translates the new/reworded Polish strings", while the commit additionally rewrites 10 live po/pl msgstr whose English text this PR does not change (details inline on po/pl/usteer.po:372).
  • 46a98d9 "luci-app-usteer: add per-SSID settings tabs and known-station tracking UI" — the content matches the diff, but the body is written as a changelog against states that will never exist in master: "plus fixes from review: reject:true and ACL grant for delete_known, Delete button hidden for remote-owned entries, band_steering_enabled dependencies guarded against builds lacking the toggle…". This is the commit that introduces delete_known, its ACL entry and those dependencies in the first place, so in git log the sentence reads as if an earlier commit had shipped them broken. Dropping "plus fixes from review:" and stating the behaviour directly keeps the same information without the phantom history.

Generated by Claude Code

Comment thread applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js Outdated
Comment thread applications/luci-app-usteer/po/pl/usteer.po Outdated
@grzegorz914
grzegorz914 force-pushed the usteer-per-ssid-known-stations branch from 9f1345e to aec0084 Compare July 30, 2026 05:28
@grzegorz914
grzegorz914 force-pushed the usteer-per-ssid-known-stations branch from a14333d to de6a51b Compare August 28, 2026 20:31

@openwrt-ai openwrt-ai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the force-pushed series (now 0307d7a + de6a51b, replacing 5fa58dc/eafda37f/70deda34). Both findings from the last round are fixed, and the rest of the delta is a rebase onto current master (19365a7):

  • The empty 70deda34 "retrigger CI checks" commit is gone — the branch is back to two commits.
  • po/sv/usteer.po is merged against this PR's template now. It carries the template's 162 live msgids exactly (0 missing, 0 extra), the same 48 obsolete entries as po/de/po/cs/po/ru (identical msgid set), and 59 untranslated — the same count as every other fully-merged language — with not a single Swedish msgstr differing from master's copy. The Weblate header block is preserved (PO-Revision-Date: 2026-08-13, Last-Translator, X-Generator), so it won't read as a translator change. All 31 languages now land on 162/162 against po/templates/usteer.pot, in identical msgid order.
  • usteer.js and acl.d/luci-app-usteer.json are byte-identical to the previously reviewed 5fa58dc3, and both commit messages are unchanged, so there is no code delta this round. The .pot still matches the file it was generated from — all 194 usteer.js:NNN references resolve to a line whose _() call carries that msgid — and po/pl is unchanged since eafda37 (no msgstr diff, 0 fuzzy, 0 untranslated).
  • The rebase is clean: master's only change to this app in the window is a three-line Weblate update to po/lt/usteer.po, and head's po/lt carries master's current msgstr verbatim, with only #: reference lines differing.
  • All three FormalityCheck runs are green on de6a51b.

One cosmetic difference worth naming so it isn't mistaken for a missed merge: sv's #~ tail is ordered alphabetically while the other 30 languages keep msgmerge's default order. Same 48 entries either way, and dead text regardless.

The two inline threads from the last round are still open — the Band-steering threshold / Band steering threshold label collision, and the migration question about the options that no longer appear on the global Settings tab — not repeated here.

Commit checks

  • de6a51b "luci-app-usteer: update translation template and Polish translation" — the message is unchanged from eafda37, so both wording problems raised on it stand. It enumerates the regeneration as dropping the band-steering toggle's strings and picking up the restored "A value of 0 disables band-steering." sentence, but never mentions the On/OffEnable/Disable relabel, which is the other half of what the template picks up: it drops On and Off and gains Enable and Disable, and those two are the only strings besides the reworded interval description that po/pl needed new translations for. And "Dropped the resulting pl-only obsolete entries … matching every other language" still describes no hunk in this commit: relative to master, po/pl/usteer.po gains 48 obsolete entries (173 #~ lines) and removes none — that cleanup happened in the since-squashed 2f59d60. The end state the sentence claims is true (pl's obsolete set is now identical to de/cs/ru's), but as written it points at a removal the diff doesn't contain. "merges into every language's po file" is accurate now that sv is included.

Generated by Claude Code

@grzegorz914
grzegorz914 force-pushed the usteer-per-ssid-known-stations branch from de6a51b to cac79e7 Compare August 29, 2026 04:40

@openwrt-ai openwrt-ai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the force-pushed series (now 3057a13 + cac79e7, replacing 46a98d9/9f1345e; the branch was also rebased onto current master). The usteer.js delta since 9f1345e is ~140 lines: the band_steering_enabled toggle and its four depends() sites reverted, On/Off relabelled to Enable/Disable, band_steering_min_snr restored to master's label, and every per-SSID o.placeholder = … replaced by the new setPlaceholder() helper.

What checks out:

  • setPlaceholder()'s lookup works as intended: uci.resolveSID() does expand the @type[idx] form before get() reads it, so uci.get('usteer', '@usteer[0]', field) resolves to the anonymous shared section. uci.load('usteer') runs in load(), and addSsidTab() is called from render() (line 931), so the values are there. Evaluating the placeholder once at render is fine here precisely because these options are no longer editable on the page.
  • The band-steering revert is complete and self-consistent — no depends() left pointing at the removed band_steering_enabled, and the interval field regained master's "A value of 0 disables band-steering." sentence.
  • i18n: this round does not repeat the recurring problem. Diffing po/pl/usteer.po entry by entry against 9f1345e, zero live msgstr changed; the only delta is 4 msgids added (Enable, Disable, Band steering min SNR, the reworded interval description) and 6 removed. po/pl has 0 fuzzy and 0 untranslated, and all 31 languages plus the template carry the same 163 live msgids.
  • All three FormalityCheck runs are green on cac79e7.

Three things introduced or missed by this round are inline: a hard-coded band-steering-interval fallback that drops master's init-script-conditional default, the 35 options this revision removes from the global Settings tab with no feature guard, and a .pot whose references are two lines stale. Plus one nit.

Commit checks

  • 3057a13 "luci-app-usteer: add per-SSID settings tabs and known-station tracking UI" — the message is a botched squash: Signed-off-by: Grzegorz Kaczor <…> appears at line 24, in the middle of the body, followed by two more prose paragraphs and a second, duplicate Signed-off-by at line 42. A trailer block has to be the last thing in the message; as it stands git interpret-trailers and anything else parsing the footer sees a body paragraph after the sign-off. Fold the two trailing paragraphs into the body above and keep a single sign-off at the end.

    The same two paragraphs also bring back the phantom-history framing you removed in 3e2f5d7: "Previously the placeholder always showed the compiled-in default…" and "band_steering_min_snr keeps its master label … instead of 'Band-steering threshold'" describe intermediate states of this PR that will never exist in master. This commit is the one that introduces setPlaceholder() and the band_steering_min_snr field in the merged history, so there is no earlier state for it to be correcting. Stating the behaviour directly keeps the rationale without the review-round narration.

  • cac79e7 "luci-app-usteer: update translation template and Polish translation" — three claims don't match the tree:

    • "Regenerates usteer.pot against the final usteer.js" — it doesn't; 132 of the template's references are two lines stale (details inline on po/templates/usteer.pot:678).
    • "all 31 languages now carry the template's 162 live msgids and the same 47 obsolete entries" — the live count is 163, not 162, and the obsolete count is nowhere near uniform: 47 in cs, de, es, ga, ko, lo, lt, pl, ru, sv, uk, zh_Hans; 42 in ta and tr; 10 in it; 9 in zh_Hant; 4 in pt and ro; 2 in fr and pt_BR; and 0 in az, be, bn_BD, da, fi, hu, ja, nb_NO, nl, sk, vi. That is the expected outcome (msgmerge only preserves obsolete entries that had a translation), so the fix is to the sentence, not the files.
    • "Polish needs no new translation work this round" — the commit adds two brand-new Polish translations, Enable → "Włącz" (po/pl/usteer.po:203-204) and Disable → "Wyłącz" (po/pl/usteer.po:194-195). Neither msgid existed in 9f1345e's po/pl, as a live entry or an obsolete one, so they are not reused wording. The Band steering min SNR reuse the message describes is real (#~ msgid at 9f1345e's po/pl/usteer.po:913); the Enable/Disable pair is not.

Generated by Claude Code

Comment thread applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js Outdated
Comment thread applications/luci-app-usteer/po/templates/usteer.pot Outdated
Comment thread applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js Outdated
@grzegorz914
grzegorz914 force-pushed the usteer-per-ssid-known-stations branch from cac79e7 to d0e31d9 Compare August 29, 2026 17:43

@openwrt-ai openwrt-ai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the force-pushed series (now 112078a + d0e31d9, replacing 3057a13/cac79e7). Two things this round: usteer.js gains 228 lines — setPlaceholder() learns to carry the fallback's unit onto the global value (plus the snrDbm sign-based variant), and a !Initscript.includes('usteer_ssid') block restores the 35 removed options to the global Settings tab — and the whole i18n set is regenerated for it.

What checks out:

  • The previous round's band_steering_interval finding is fixed: line 761 now uses Initscript.includes('aggressiveness') ? '30000 (ms)' : '120000 (ms)', matching master's conditional at usteer.js:679.``
  • The unit-annotation regex is safe against the fallbacks actually passed. /^-?[0-9]+ (\(.+\))$/ would greedily swallow 0 (SNR) or -95 (dBm) whole, but all five dual-convention fields (min_snr, min_connect_snr, roam_scan_snr, roam_trigger_snr, band_steering_min_snr) pass snrDbm = true and return before reaching it; every other fallback is either a bare number or a single N (unit) string.
  • The restored block is a faithful copy of master's, not a re-invention: diffing it line-by-line against 19365a7's s.taboption('settings', …) run, the labels, descriptions, placeholders, datatypes and the aggressiveness / aggressiveness_mac_list / band_steering_signal_threshold sub-guards are identical, and the field count really is 35. It also quietly fixes master's 'band_steering_signal_threshold ' option name, which carried a trailing space.
  • No option-name collision between the two paths: opt() prefixes every per-SSID key with the section id, so min_snr and ssid_foo_min_snr coexist in the same CBI section.
  • The last commit's Signed-off-by placement is fixed — one trailer, at the end.
  • i18n is mechanically clean. All 276 usteer.js:NNN references in the regenerated .pot resolve to a line whose _() call carries that msgid, all 31 languages plus the template land on the same 206 live msgids in the same order, and — notably — this round does not repeat the recurring problem: diffing po/pl and po/de entry by entry against cac79e7, zero surviving msgstr changed; the delta is 43 msgid additions (the restored global-tab strings) and nothing removed. po/pl is 0 fuzzy / 0 untranslated. The obsolete-count claim holds exactly: 15 languages on 0 (the listed set), 14 on 4, plus it on 1 and zh_Hant on 2.
  • All three FormalityCheck runs are green on d0e31d9.

One issue with how the new guard interacts with addSsidTab(), plus a nit, inline. The still-open thread about the shared usteer values being read-only placeholders on a new daemon (and a device with no configured SSID having no editable copy at all) isn't repeated here.

Commit checks

  • 112078a "luci-app-usteer: add per-SSID settings tabs and known-station tracking UI" — the message says these fields are removed from the global tab "unless the running usteer build's init script has no usteer_ssid support … on such a build the tab falls back to registering them there instead". The diff doesn't implement the "instead": the guard covers only the global block, while addSsidTab() is called unconditionally, so an old daemon gets both sets of widgets, one of which silently does nothing. Details inline on usteer.js:1084 — either guard the allSsidNames.forEach(...) too, or drop "instead" from the message.

  • d0e31d9 "luci-app-usteer: update translation template and Polish translation" — two counts don't match the tree:

    • "Regenerates usteer.pot from the final usteer.js (207 live msgids)" — the template has 207 msgid entries, but one of them is the PO header (msgid ""), so the live count is 206. po/pl's "all 207 live msgids" in the last paragraph is the same off-by-one.
    • "Relative to master, the template drops the band-steering toggle's four strings" — the four msgids actually dropped relative to 19365a7 are List of SSIDs to enable steering on, Refresh page to get new mac addresses to show up, default true and empty means all. The band_steering_enabled toggle's strings were never in master's template; they were added and reverted inside this PR, so they can't be part of a delta against master. The same sentence then says the template gains Enable/Disable "replacing On/Off" and Band steering min SNR "back under its master label" — but On/Off were also never in master, and Band steering min SNR is present in master's template and this one alike, so it isn't gained either. The sentence is describing the delta against the previous revision of this PR while claiming to describe the delta against master; the obsolete-count paragraph that follows then attributes the per-language 0/4 split to "this round's four-string removal", which is the correct count attached to the wrong four strings.

Generated by Claude Code

Comment thread applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js Outdated
Comment thread applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js Outdated
@grzegorz914
grzegorz914 force-pushed the usteer-per-ssid-known-stations branch from d0e31d9 to 68e6a6f Compare August 30, 2026 04:26

@openwrt-ai openwrt-ai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the force-pushed series (now d1b9b95 + 68e6a6f, replacing 112078a/d0e31d9). The delta since d0e31d9 is 21 lines of usteer.js plus pure #: reference churn in the po files.

Both findings from the previous round are fixed:

  • hasSsidConfig is hoisted to line 945 and now guards the allSsidNames.forEach(addSsidTab) call as well as the restored global block, so the two paths really are mutually exclusive and an old daemon no longer gets both sets of widgets. One leftover at the third site, inline.
  • load_kick_reason_code's description lost master's unbalanced trailing ) and wraps Default: in _(), matching how the per-SSID copy at usteer.js:806`` already spells it. No new msgid, since Default: was already in the template.

The i18n commit is clean this round and its counts all check out against the tree:

  • The delta since d0e31d9 is only reference lines — across all 31 languages plus the template, zero msgids added or removed, zero msgstr changed, zero obsolete entries changed.
  • The template really was regenerated from the final usteer.js: all 277 usteer.js:NNN references resolve to a line whose _() call carries that msgid.
  • "206 live msgids" is right (207 msgid entries minus the PO header; the two Signal entries are msgctxt-distinguished, not a duplicate). Relative to master the template drops exactly the four named strings and gains exactly 59.
  • The per-language obsolete split matches the message exactly: az be bn_BD da fi fr hu ja nb_NO nl pt pt_BR ro sk vi on 0, fourteen on 4, it on 1, zh_Hant on 2. po/pl is 0 fuzzy / 0 untranslated across all 206.

All three FormalityCheck runs are green on 68e6a6f.

One bug introduced by an earlier round that is still live, plus the leftover guard, inline. The still-open threads about the shared usteer values being read-only placeholders on a new daemon, and the Polish msgstr that diverge from master, aren't repeated here.

Commit checks

  • d1b9b95 "luci-app-usteer: add per-SSID settings tabs and known-station tracking UI" — the third paragraph says the restored global fields keep "the same labels, descriptions and defaults master's copy of this file used", but one description deliberately doesn't: load_kick_reason_code at usteer.js:1231-1232`` drops master's stray ) and makes `Default:` translatable. It's the right change and it's the only one, so it's cheap to name it ("…the same labels, descriptions and defaults master used, apart from `load_kick_reason_code`'s description, whose unbalanced trailing paren is dropped and whose `Default:` is made translatable") rather than have a reader diff the block to find the one intentional deviation.

Generated by Claude Code

Comment thread applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js Outdated
Comment thread applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js Outdated
@grzegorz914
grzegorz914 force-pushed the usteer-per-ssid-known-stations branch from 68e6a6f to e9579a7 Compare August 30, 2026 16:24

@openwrt-ai openwrt-ai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the force-pushed series (now d1b9b95 + 23870d4 + e9579a7, replacing 46a98d9/9f1345e). Big round: setPlaceholder() sources every per-SSID Value placeholder from the shared usteer section, the band_steering_enabled toggle and its four depends() sites are reverted, On/Off become Enable/Disable, band_steering_min_snr gets master's label back, and 35 global fields are restored to the Settings tab behind !hasSsidConfig.

Every finding from the previous round is resolved, and I re-verified each against the tree:

  • The 35 deleted global options are back. Diffing the restored if (!hasSsidConfig) block against master's copy line by line, the labels, descriptions, placeholders and datatypes are identical, and addSsidTab() is now gated on the same flag, so the two paths really are mutually exclusive. Two silent improvements ride along: master's option name 'band_steering_signal_threshold ' (trailing space, so it wrote a UCI key nobody reads) loses the space, and load_kick_reason_code's unbalanced trailing paren is fixed.
  • band_steering_interval's fallback now follows Initscript.includes('aggressiveness') instead of a hard-coded 30000.
  • .pot references. I resolved all 277 usteer.js:NNN references in the regenerated template against the head file: 277 correct, 0 off. The previous round's uniform +2 shift is gone.
  • po/sv is merged again — it lands on the same 205 live / 4 obsolete shape as de/cs/ru/uk/zh_Hans, and is no longer byte-identical to master.
  • == null and the hasSsidConfig && relocation guard landed in 23870d4, so a stock config no longer renders undefined (ms) and a pre-usteer_ssid daemon no longer gets an empty bordered box above Save/Reset.

e9579a7's i18n bookkeeping claims all hold: the template is 206 live msgids, drops exactly the four named strings (List of SSIDs to enable steering on, empty means all, Refresh page to get new mac addresses to show up, default true) and gains 59, and the per-language obsolete counts are exactly as listed — 15 languages at 0 (az, be, bn_BD, da, fi, fr, hu, ja, nb_NO, nl, pt, pt_BR, ro, sk, vi), 14 at 4, it at 1, zh_Hant at 2. po/pl has 0 fuzzy and 0 untranslated. All three FormalityCheck runs are green on e9579a7.

One issue in addSsidTab(), one in the Polish merge, and one nit inline.

Commit checks

  • d1b9b95 + 23870d4 "luci-app-usteer: add per-SSID settings tabs and known-station tracking UI" — two commits with the same subject and near-identical full-feature bodies, where 23870d4 is a two-line fixup of d1b9b95 (=== null== null in setPlaceholder(), and hasSsidConfig && added to the tab-relocation guard). Its message describes the whole feature rather than its own change, so git log shows the feature twice and the delta nowhere. It also leaves d1b9b95's message wrong about d1b9b95's own code: that message promises the placeholder falls back to "that fallback itself once the global option is unset too", but with === null an unset-but-present option takes the undefined branch and the field renders undefined (ms) / undefined (SNR) on a stock config — so a bisect landing on d1b9b95 gets a commit whose message contradicts its behaviour. Squashing 23870d4 into d1b9b95 and keeping 23870d4's (already updated) message fixes both, and is the same squash this series did at e1488b2 and b3f29ea.
  • e9579a7 "luci-app-usteer: update translation template and Polish translation" — the message presents the po/pl change as a plain merge ("every string this revision touches … already had a Polish translation to merge against"), but the commit also rewrites 36 live msgstr whose English text is untouched by this PR, 13 of them on global-only options the diff never goes near. The other 30 languages are clean mechanical merges with 0 changed msgstr; pl is the only exception and the message doesn't mention it. Details on po/pl/usteer.po:353.

Generated by Claude Code

Comment thread applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js Outdated
Comment thread applications/luci-app-usteer/po/pl/usteer.po Outdated
Comment thread applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js Outdated
@grzegorz914
grzegorz914 force-pushed the usteer-per-ssid-known-stations branch from e9579a7 to 5e75730 Compare August 31, 2026 04:57

@openwrt-ai openwrt-ai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the force-pushed series (now f751d37 + 5e75730, replacing d1b9b95/23870d4/e9579a7). The series is back to two commits, and the code delta since e9579a7 is 20 lines of usteer.js; everything else is the i18n regeneration.

Both findings from the previous round are addressed, and the fixes check out:

  • load_kick_enabled is now the same three-way ListValue as assoc_steering/probe_steering (''/'1'/'0', default = '', optional via opt()), so "unchecked" and "inherit" are no longer the same state and an SSID can override a globally-enabled setting. The four dependent fields each gained a second depends(sid + '_load_kick_enabled', ''), and that really does keep them active in the blank state — isDependencySatisfied() compares the widget value with isEqual(), which holds for ('', ''), so only an explicit '0' makes them inactive and triggers the parse()remove() path.
  • The bare _('Signal') now carries the Status/Hearing-map msgctxt, so the template no longer ships two msgid "Signal" entries, and the plain one lands as po/pl's fifth obsolete block exactly as the message describes.

Re-verified against the tree:

  • All 221 usteer.js:NNN references in the regenerated .pot resolve to a line whose _() call carries that msgid — 221 correct, 0 off.
  • All 31 languages plus the template carry the same msgid set (206 msgid lines each) in the same order, and the per-language obsolete split is exactly as listed: 15 at 0 (az, be, bn_BD, da, fi, fr, hu, ja, nb_NO, nl, pt, pt_BR, ro, sk, vi), 13 at 4, it 1, zh_Hant 2, pl 5.
  • Relative to master the template drops exactly the four named strings (List of SSIDs to enable steering on, empty means all, Refresh page to get new mac addresses to show up, default true).
  • "30 of the 31 languages are clean mechanical merges" holds — I diffed every language entry by entry against e9579a7: zero msgstr changed anywhere except po/pl, which changes exactly 34. 33 of those 34 land on master's wording byte-for-byte; the 34th, plus two that the revert missed, are inline.
  • po/pl is 0 fuzzy, 1 untranslated — and that one untranslated entry is the regression below, not a pre-existing gap.
  • The restored if (!hasSsidConfig) block still registers exactly 35 settings options, and hasSsidConfig still gates all three sites (usteer.js:967, 1107, 1343). f751d37 touches only usteer.js + acl.d/luci-app-usteer.json, 5e75730 only the po set, so the split is clean.
  • All three FormalityCheck runs are green on 5e75730.

The aggressiveness half of the previous round's load_kick_enabled thread (default = '3' with rmempty, so 3 can never be written as an explicit override of a global 4) is unchanged and that thread is still open — not repeated here.

Commit checks

  • 5e75730 "luci-app-usteer: update translation template and Polish translation" — three bookkeeping claims don't match the tree:
    • "206 live msgids, one fewer than before" — the template has 206 msgid entries, one of which is the PO header, so the live count is 205. The delta is right (the previous revision was 206 live); the absolute number wasn't decremented with it. Same off-by-one in the last paragraph's "0 fuzzy across its 206 live msgids".
    • "and gains 59" — master's template is 151 live; this one drops the four named strings and gains 58, landing at 205. 59 was the correct figure for the previous revision, before the Signal merge removed one.
    • "po/pl carries 1 untranslated entry (matching master exactly - Number of remote update intervals after which a remote-node is deleted has no Polish translation upstream either)" — master does translate it, at po/pl/usteer.po:491-493. This commit empties it rather than reverting it, so the file ships one fewer Polish translation than master for a string the PR doesn't touch. Details inline on po/pl/usteer.po:796.

Generated by Claude Code

Comment thread applications/luci-app-usteer/po/pl/usteer.po Outdated
Comment thread applications/luci-app-usteer/po/pl/usteer.po Outdated
Comment thread applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js Outdated
@grzegorz914
grzegorz914 force-pushed the usteer-per-ssid-known-stations branch from 5e75730 to 87c1a27 Compare August 31, 2026 16:38

@openwrt-ai openwrt-ai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the force-pushed series (now 3a11173 + 87c1a27, replacing 46a98d9/9f1345e; the branch was also rebased onto current master, so the raw range diff carries a lot of unrelated tree churn — I diffed 9f1345e..87c1a27 restricted to applications/luci-app-usteer/). The code delta is ~400 lines of usteer.js: the new setPlaceholder() helper, the band_steering_enabled toggle dropped again, load_kick_enabled converted to the same tri-state as assoc_steering/probe_steering, On/Off relabelled Enable/Disable, band_steering_min_snr relabelled, the Known-stations Signal header given the shared msgctxt, and the new hasSsidConfig guard plus the 35 restored global fields behind it.

Everything flagged in the previous rounds is resolved, and the claims check out against the tree:

  • The 35 global options are back under Initscript.includes('usteer_ssid') and the set is exactly the per-SSID set — I diffed the option names both ways, 35/35 with nothing extra on either side, and each restored field's label, description, placeholder and datatype is byte-identical to master's copy (the one intended exception, load_kick_reason_code, is as described).
  • band_steering_threshold's lost dependency is moot now that the toggle is gone, and the relocation guard at usteer.js:1347 got the hasSsidConfig treatment, so the two paths really are mutually exclusive.
  • depends(sid + '_load_kick_enabled', '') is a supported shape — isDependencySatisfied() compares formvalue() with isEqual() and ListValue inherits AbstractValue.formvalue(), which returns the raw '' (the formvalue() override that maps default→null is RangeSliderValue's, not ListValue's) — and it matches the nine existing depends(x, '') sites in the tree. The tri-state itself matches tools/network.js:1145-1150`` as the message says.
  • setPlaceholder()'s uci.get('usteer', '@usteer[0]', field) resolves — resolveSID() expands @type[n] — and == null catches the undefined an absent option returns. All five dual SNR/dBm fields pass snrDbm, and the unit regex reproduces the old annotations for the rest.
  • i18n: every claim in 87c1a27's message verifies mechanically. 205 live msgids in the template, all 31 po files carry exactly that msgid set, the four dropped and 58 gained msgids are as listed, and the obsolete counts land exactly where the message says (0 for the 15 named languages, 4 for 13 more, it 1, zh_Hant 2, pl 5 — the fifth being the now-obsolete plain "Signal"). All 280 usteer.js #: references in the template resolve to the right line of the final file. po/pl has 0 fuzzy and 0 untranslated, and — the point of this round — zero msgstr now differ from master across all 31 languages, so the long-running Polish-drift finding is fully closed.
  • All three FormalityCheck runs are green on 87c1a27.

One question about what happens to the shared-section values on a usteer_ssid-capable daemon, plus one i18n nit, inline.

Commit checks

  • 3a11173 "luci-app-usteer: add per-SSID settings tabs and known-station tracking UI" — the restored-fields paragraph presents its exception list as exhaustive ("with the same labels, descriptions and defaults master's copy of this file used - apart from load_kick_reason_code's description…"), but there is a second deviation: master registers the option as 'band_steering_signal_threshold ' with a trailing space at usteer.js:757, and the restored copy at [usteer.js:1267](https://github.com/openwrt/luci/blob/87c1a27bdee09dadb761e6e62e38a881a76a9289/applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js#L1267)`` silently drops it. The fix is clearly right — the trailing space is not a valid UCI option name, so master's global widget can never have written anything the daemon reads — but it changes which key a legacy-daemon user's value lands under, and it's the kind of thing that deserves a clause of its own rather than arriving inside a block advertised as a faithful restore.
  • 87c1a27 "luci-app-usteer: update translation template and Polish translation" — the po/pl paragraph says "36 msgstr this PR had previously carried with reworded (or, for one, emptied) Polish text … are reverted to master's wording". Comparing 9f1345e:po/pl/usteer.po with this commit's copy, the count is 35, not 36, and none of them was empty in 9f1345e — that file had 0 untranslated entries, as the previous round's review confirmed. The "emptied" one belongs to an earlier revision of the series that 9f1345e had already fixed. The 13 named global-only msgids are all in the 35, and every one of them lands back on master's exact wording, so only the count and the parenthetical need adjusting.

Generated by Claude Code

Comment thread applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js Outdated
Comment thread applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js Outdated
@grzegorz914
grzegorz914 force-pushed the usteer-per-ssid-known-stations branch 2 times, most recently from 924222d to 2c1b4f8 Compare September 1, 2026 04:52

@openwrt-ai openwrt-ai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the force-pushed series (now 1912921 + 2c1b4f8, replacing 3a11173/87c1a27). The branch is still on the same base (6e1eb21), so the range diff is clean: 412 lines of usteer.js plus the i18n regeneration.

The headline change is the one the previous round asked about: the 35 shared-section options are no longer gated behind !hasSsidConfig on the Settings tab — they now live in an always-registered Defaults tab bound to @usteer[0], nested first in the same sub-tab bar as the per-SSID tabs when the daemon reads usteer_ssid. That closes the "visible-but-unreachable fallback" finding, and the previous round's A value of 0 disables threshold. nit landed as well.

Verified mechanically against the tree:

  • The Defaults set is exactly the per-SSID set — 35/35 option names, in the same order addSsidTab() registers them. I diffed both lists; nothing extra on either side, and the order matches element for element, as the message claims.
  • Each Defaults widget is a faithful copy of master's global original. I diffed all 35 blocks against 6e1eb21's usteer.js (label, description, placeholder, datatype). The only textual deviation is load_kick_reason_code, exactly the one the message calls out, and master really does register 'band_steering_signal_threshold ' with a trailing space at usteer.js:757,`` so the second correction is real too. No option name is registered on both the settings and `defaults` tabs.
  • i18n. The .pot was regenerated against the final file: all 284 usteer.js:NNN references resolve to a line whose _() call carries that msgid — 284 correct, 0 off. The template drops precisely the four named msgids and gains 59, landing at 206 live. Obsolete counts are exactly as listed: 0 for the 15 named languages (az, be, bn_BD, da, fi, fr, hu, ja, nb_NO, nl, pt, pt_BR, ro, sk, vi), 4 for 13 more, it 1, zh_Hant 2, pl 6. po/pl is 0 fuzzy and 0 untranslated.
  • No translation drift. Comparing all 31 po files against master entry by entry, zero msgstr differ for any shared msgid, and pl is the only language that translates the 59 new ones — so the long-running Polish-drift thread stays closed. (A handful of entries in es/ga/ko/lo/lt/sv differ only in msgmerge line wrapping, so "byte-identical" is a touch strong, but no text changed.)
  • All three FormalityCheck runs are green on 2c1b4f8.

One issue with the Defaults action bar, one question about the Defaults tab on a pre-usteer_ssid daemon, and three nits inline.

Commit checks

  • 2c1b4f8 "luci-app-usteer: update translation template and Polish translation" — "of the 206 live msgids, 204 already existed in master" contradicts the same message's own "Relative to master, the template … gains 59". Both can't hold: 206 − 59 = 147 of these msgids exist in master's usteer.pot, not 204. 204 is the figure relative to the previous revision of this PR87c1a27's template carried 205 live msgids, this one adds Defaults and its description and drops the unpunctuated A value of 0 disables threshold, landing at 206 — which is also what makes the next sentence about "the two genuinely new msgids … translated fresh for Polish" correct. Naming the previous revision instead of master in that one clause makes the paragraph consistent; the msgstr claim it introduces holds either way.

Generated by Claude Code

Comment thread applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js Outdated
Comment thread applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js Outdated
Comment thread applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js Outdated
@grzegorz914
grzegorz914 force-pushed the usteer-per-ssid-known-stations branch from 2c1b4f8 to 3a06a98 Compare September 1, 2026 17:08

@openwrt-ai openwrt-ai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the force-pushed series (now 8822860 + 3a06a98, replacing 1912921/2c1b4f8). Same base (6e1eb21), so the range diff is clean: 41 lines of usteer.js plus the i18n regeneration.

All five findings from the previous round are resolved:

  • Duplicate Defaults action barDefaultsfooter's registration is now behind if (!hasSsidConfig) at usteer.js:1347-1351,`` exactly the suggested shape. On a usteer_ssid-capable daemon the nested Defaults pane relies on the Settings bar like every SSID sub-tab does; on a legacy build, where Defaults is a top-level tab with no Settings footer beneath it, it keeps its own.
  • Defaults heading on a pre-usteer_ssid daemonhasSsidConfig is hoisted to module scope and Defaultstitle renders "Settings for this instance of usteer." there instead of advertising a per-SSID fallback the build has no per-SSID tabs for.
  • defaults / SSID key collision — the sub-tab click handler compares by identity now (usteer.js:1499),`` so a wifi network literally named defaults can't activate two panes at once. `tabs` is rebuilt per `rebuildSsidSubTabs()` call and each handler closes over its own `t`, so identity is stable across rebuilds.
  • Stale hasSsidConfig comment — rewritten and moved next to the assignment.
  • PR body — the new "Update 2" section documents the Defaults tab and the placeholder change.

i18n, verified mechanically against the tree:

  • The template really was regenerated against the final file: all 285 usteer.js:NNN references in usteer.pot resolve to a line carrying that msgid — 285 correct, 0 off. 207 live msgids, dropping precisely the four named (List of SSIDs to enable steering on, empty means all, Refresh page to get new mac addresses to show up, default true) and gaining 60 relative to master's 151.
  • The "206 already existed in the previous revision … msgstr unchanged" claim holds exactly. Comparing all 31 po files against 2c1b4f8 entry by entry, zero msgstr differ for any shared msgid, and the single new msgid Settings for this instance of usteer. is translated only in pl (Ustawienia tej instancji usteer.).
  • Obsolete counts land where the message says: 0 for az, be, bn_BD, da, fi, fr, hu, ja, nb_NO, nl, pt, pt_BR, ro, sk, vi (15), 4 for cs, de, es, ga, ko, lo, lt, ru, sv, ta, tr, uk, zh_Hans (13), it 1, zh_Hant 2, pl 6. All 31 files carry the template's exact msgid set, and po/pl is 0 fuzzy / 0 untranslated across all 207.
  • Against master, zero msgstr differ for any shared msgid in any of the 31 languages — the long-running Polish-drift thread stays closed.

All three FormalityCheck runs are green on 3a06a98. Both commit messages match their diffs — 8822860 touches only usteer.js and acl.d/luci-app-usteer.json, 3a06a98 only the po files — so no Commit checks section.

Two nits inline; nothing blocking.


Generated by Claude Code

Comment thread applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js Outdated
Comment thread applications/luci-app-usteer/htdocs/luci-static/resources/view/usteer/usteer.js Outdated
@grzegorz914
grzegorz914 force-pushed the usteer-per-ssid-known-stations branch from 3a06a98 to 2e4cd28 Compare September 2, 2026 04:24

@openwrt-ai openwrt-ai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the force-pushed series (now c416f9c + 2e4cd28, replacing 46a98d9/9f1345e). The branch was also rebased from d7a654e onto 6e1eb21, so part of the raw po diff is master's own Weblate churn rather than this PR's. This round adds the Defaults tab: the 35 options the earlier revisions moved off the global Settings tab now land in their own section-bound tab instead of being unreachable from LuCI.

The core of it checks out against the tree:

  • Nothing was lost from Settings. Master's settings tab declares 47 options; the new tree has 12 of them still on settings (plus known_stations/known_stations_timeout from an earlier revision of this PR) and the other 35 on defaults — 12 + 35 = 47, no gaps, no duplicate option names between the two tabs (the per-SSID keys are all ssid_<sid>_<field>, so they can't collide with the plain names either).
  • "Faithful copies" is literally true. I diffed every defaults widget against master's settings original — class, label, description, placeholder, datatype — and the only textual difference in the whole set is load_kick_reason_code's description, exactly as the commit message says. The other documented correction, dropping the trailing space from master's 'band_steering_signal_threshold ' option name, is there too; that key really was unwritable as-is.
  • The field order matches addSsidTab() position for position, all 35, as claimed.
  • uci.get('usteer', '@usteer[0]', field) resolves — resolveSID() handles the @type[n] extended notation — and returning undefined (not null) for an absent option in a loaded section is what get() actually does, so == null is the right test.
  • The two addFooter() calls are safe: the base implementation builds a fresh E([]) on every call, so Settings and Defaults get independent trees, and defaultsfooterdata is only ever produced under the same !hasSsidConfig guard that registers Defaultsfooter.
  • The new depends(sid + '_load_kick_enabled', '') sites work as intended: isEqual('', '') is true and a blank ui.Select choice's formvalue() is '', so the four load-kick parameter fields stay active in the inherit state and parse() won't strip them.
  • The Enable/Disable/blank tri-state does match tools/network.js — same shape as autoneg/rxpause/txpause there.``
  • i18n, master-relative: the important claim holds. I compared all 31 languages entry by entry against master (6e1eb21): zero msgstr differ for any shared msgid, in any language. The template is 207 live msgids (151 − 4 + 60 vs master), every language's msgid set matches it exactly, po/pl has 0 fuzzy and 0 untranslated, and the obsolete counts are exactly as the commit message enumerates (15 languages at 0, 13 at 4, it 1, zh_Hant 2, pl 6). .pot references resolve against the final usteer.js (Defaults → usteer.js:439/991/1468, Settings for this instance of usteer. → usteer.js:442, Band steering min SNR → usteer.js:795/1290). FormalityCheck is green on 2e4cd28.

c416f9c's message matches its diff throughout — including the "object identity, not string keys" detail, which does hold: an SSID literally named defaults gets its own {key, pane} entry and the click handler compares t2 === t.

Two nits inline; nothing blocking.

Commit checks

  • 2e4cd28 "luci-app-usteer: update translation template and Polish translation" — the second paragraph's arithmetic is against the wrong baseline. It says "of the 207 live msgids, 206 already existed in the previous revision of this PR, and every language's msgstr for them is unchanged from that revision" and that "The one genuinely new msgid ("Settings for this instance of usteer.")" is the only addition. Measured against the previously reviewed 9f1345e, the template gains 50 msgids and drops 8; six of those are new against master and 9f1345e (Defaults, Enable, Disable, Settings for this instance of usteer., The fallback value for every SSID that leaves the corresponding per-SSID field blank., Attempting to steer clients on this SSID to a higher frequency-band every n ms. A value of 0 disables band-steering.) — the other 44 are master strings coming back with the Defaults tab. Likewise, msgstr did change relative to 9f1345e: pl in 35 places, sv in 71, es/ja/lt in one each. All of that is benign — it's the rebase pulling in master's own Weblate syncs, and it's why the master-relative comparison in the next sentence comes out clean — but as written the paragraph tells a reader that only one string is new and that no translated text moved, which would send them past 50 real msgid additions. The master-relative sentence right after it is both accurate and the one that matters; scoping the whole paragraph that way (and noting the rebase) would fix it.

Generated by Claude Code

@grzegorz914
grzegorz914 force-pushed the usteer-per-ssid-known-stations branch from 2e4cd28 to 4bff264 Compare September 2, 2026 17:05

@openwrt-ai openwrt-ai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Commit checks

  • 4bff264e "luci-app-usteer: update translation template and Polish translation" — the new parenthetical claiming the branch "was also rebased onto master's own latest Weblate sync this round" doesn't hold: this revision and the previously pushed 2e4cd28 share the same parent (6e1eb21f), and the entire po delta between them is #: reference renumbering — not one msgid or msgstr line differs in any of the 32 files. That rebase was the previous push; drop the parenthetical and keep the previous-revision-relative claim it displaced, which is what tells a reader what this push actually changed.

Generated by Claude Code

@grzegorz914
grzegorz914 force-pushed the usteer-per-ssid-known-stations branch from 4bff264 to 4d51fc4 Compare September 3, 2026 05:29
…g UI

Adds a per-SSID configuration tab, letting each SSID override the
fields that were previously shared across the whole usteer instance:
roaming-policy (station tracking/policy timeouts, SNR/signal
thresholds, roaming, band steering, load kick, local_sta_update's
per-node update interval) and the per-node up script. A field left
blank on a per-SSID tab falls back to whatever the shared 'usteer'
section has stored for it. assoc_steering, probe_steering and
load_kick_enabled use a three-way Enable/Disable/Use global setting
choice (matching tools/network.js's convention for this kind of
tri-state), since a plain checkbox can't express turning a
globally-enabled setting off for just one SSID - a Flag with
optional/rmempty writes nothing when unchecked, so "unchecked" and
"inherit" collapse into the same state. load_kick_enabled's four
dependent fields (threshold, delay, min_clients, reason_code) depend
on it twice, for '1' and for '' (inherit), so they stay visible in
both states a save could otherwise silently strip them in - not
because inheriting is known to mean load kicking is active (it
defaults to off), but because this widget has no way to tell what
the inherited value actually is, and hiding on blank would delete
whatever this SSID already has stored for them on the next save.
Only an explicit '0' hides them. Band steering is disabled by
setting its interval to 0, matching link_measurement_interval's
convention - there is no separate enable/disable toggle for it.

Per-SSID Value fields' placeholders show what a blank field actually
resolves to via the new setPlaceholder() helper: the value stored in
the shared 'usteer' section (uci.get('usteer', '@usteer[0]', field)),
annotated with the same unit its compiled-in fallback carries, or
that fallback itself once the global option is unset too - checked
with == rather than === against null, since uci.get() returns
undefined (not null) for an option that's simply absent from an
otherwise-loaded section, which is the normal state of every one of
these on a stock config. Defined as a getter rather than a plain
assignment, so editing a value on the new Defaults tab (below) and
hitting Save updates every per-SSID tab's placeholder immediately -
Map.prototype.save() re-renders the form over the same option
objects, and a getter re-reads uci.get() on each of those renders
where a plain assignment would only ever have shown whatever was
loaded at page-open. The five dual SNR/dBm-convention fields
(min_snr, min_connect_snr, roam_scan_snr, roam_trigger_snr,
band_steering_min_snr) pick whichever unit the global value's own
sign means, since a bare number carries no unit of its own -
band_steering_min_snr's own fallback text is corrected from a dual
"0 (SNR) or -60 (dBm)" to plain "-60 (dBm)" in the process, since
usteer's compiled-in default for it (main.c's
config.band_steering_min_snr = -60) is unconditionally negative, so
usteer_snr_to_signal() in policy.c always takes the dBm branch for
it - there is no 0-SNR case this field's fallback ever actually
takes. band_steering_interval's fallback follows the same
init-script-conditional default (30000ms once 'aggressiveness'
support is present, 120000ms otherwise) the global field already
used.

The 35 fields these per-SSID tabs move off the global Settings tab
land in a new Defaults tab instead of disappearing outright: bound
to the shared 'usteer' section rather than any per-SSID one, in the
exact field order addSsidTab() below uses (declaration order of
struct usteer_config, not master's older grouping) since it's the
same set of settings shown the same way, just against a different
section - and there has to be a place in LuCI that can still edit or
clear the values setPlaceholder() reads as every SSID's fallback.
Its widgets are otherwise faithful copies of master's global
originals - same labels, descriptions, placeholders and datatypes -
apart from two corrections: load_kick_reason_code's description
drops master's unbalanced trailing paren and makes "Default:"
translatable (matching the per-SSID copy), and
band_steering_signal_threshold loses the trailing space in master's
option name, which is not a valid UCI key and meant that field could
never actually read or write anything the daemon looks at.

Per-SSID tabs (addSsidTab()) and Defaults are both nested under
Settings, in the same sub-tab bar the per-SSID tabs already used,
with Defaults sorting first and - unlike the SSIDs, which follow the
"SSID list" filter - always present regardless of it, since it isn't
an SSID (compared by object identity in the tab-switch handler, not
by name, so a wifi network actually named "defaults" can't collide
with it the way string keys would). Both this relocation and
addSsidTab() itself are gated on one hasSsidConfig flag
(Initscript.includes('usteer_ssid')), module-scoped like Initscript
itself since Defaultstitle's description below reads it too, and
checked at both sites so a daemon that doesn't read 'usteer_ssid'
sections never gets tabs that write to them; Defaults then stays a
plain top-level tab, since there is no per-SSID list for it to be
nested alongside, that description reads as "usteer's settings"
rather than a per-SSID fallback's, and Defaultsfooter (Save/Reset) -
registered, and refreshed on every handleSave()/handleReset(), only
under the same !hasSsidConfig condition - renders on the tab itself
instead of relying on Settings' below it, which exists only once
nested and would otherwise double up if both copies rendered
together. Settings and Defaults each get their own addFooter() call
rather than sharing one DOM node, since a single node can only be
attached in one place and reusing it would silently move it out of
whichever pane rendered first.

The Known-stations table's Signal column now shares its msgctxt with
the Status/Hearing-map tables' identical header instead of standing
alone as a second, context-free "Signal" msgid, and
band_steering_signal_threshold's "A value of 0 disables threshold"
gains the trailing period its two sibling descriptions already have
and master's own copy of the string carries.

Also adds known-station tracking: a per-node table of stations usteer
has ever seen, with a Delete button shown only for locally-owned
entries (deleting a remote node's synced copy doesn't propagate to
the node that actually owns it) that calls the daemon's delete_known
method over ubus - granted in acl.d/luci-app-usteer.json's write
block, and surfaced through ui.addNotification() on failure.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Grzegorz Kaczor <grzegorz914@icloud.com>
@grzegorz914
grzegorz914 force-pushed the usteer-per-ssid-known-stations branch from 4d51fc4 to 5f2e9b5 Compare September 3, 2026 05:30

@openwrt-ai openwrt-ai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Commit checks

  • 5f2e9b5 "luci-app-usteer: update translation template and Polish translation" — the conflict note says taking master's po/tr outright is "the same rule every other language's merge already follows", but no other language does that: the other 30 all carry the regenerated msgid set and tr is the only one left at master's. Either merge tr too (see inline) or drop the claim that this matches the other languages.
  • 5f2e9b5 — the obsolete-entry counts are stale for the same reason: tr now has 0 obsolete entries, not 4, so the actual split is 16 languages on 0 and 12 on 4, not the 15 and 13 the message states.

Generated by Claude Code

Comment thread applications/luci-app-usteer/po/templates/usteer.pot
Regenerates usteer.pot from the final usteer.js (207 live msgids)
and merges it into every language's po file. Relative to master, the
template drops four strings no longer used - "List of SSIDs to
enable steering on" and "empty means all" (ssid_list's description
is now one combined string with a different wrapping), "Refresh page
to get new mac addresses to show up" (the Hearing map auto-refreshes
live instead) and "default true" (syslog's Flag description was
reworded) - and gains 60, among them the per-SSID field descriptions,
the Known-stations UI text, "Enable"/"Disable" on the per-SSID
tri-state fields (matching tools/network.js's convention), and the
Defaults tab's title and its two descriptions (per-SSID-fallback
wording when the daemon supports usteer_ssid, plain "usteer's
settings" wording otherwise).

The branch was rebased onto master's latest Weblate sync this round,
which conflicted on po/tr (master's own Turkish update against this
PR's reference-only change to the same lines) - resolved by taking
master's copy of that file, then re-running the same msgmerge every
language goes through against this commit's regenerated template, so
tr ends up with the same 207-msgid set as everyone else instead of
master's un-merged 151. Comparing every language's po file entry by
entry against master's own copy: zero msgstr differ for any shared
msgid, in any of the 31 languages - the long-running Polish-drift
thread stays closed - and "Settings for this instance of usteer."
(the Defaults tab's legacy-daemon description) remains translated
only for Polish, same as any other string pending Weblate for the
other 30 languages.

Obsolete-entry counts differ per language, since msgmerge only keeps
an obsolete entry where the removed msgid had a translation: 15
languages (az, be, bn_BD, da, fi, fr, hu, ja, nb_NO, nl, pt, pt_BR,
ro, sk, vi) land on 0, 13 more on 4, it on 1, zh_Hant on 2, and pl on
6 - two more than its usual 4, for the now-obsolete plain "Signal"
msgid the Known-stations fix merges into the msgctxt'd entry, and
for "A value of 0 disables threshold" gaining the trailing period
its two siblings and master's own copy already carry.

po/pl carries 0 untranslated and 0 fuzzy entries across all 207 live
msgids.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Grzegorz Kaczor <grzegorz914@icloud.com>
@grzegorz914
grzegorz914 force-pushed the usteer-per-ssid-known-stations branch from 5f2e9b5 to ee044e0 Compare September 3, 2026 16:46

@openwrt-ai openwrt-ai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed 1 new commit; no new issues found.


Generated by Claude Code

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