Skip to content

chore(cpn): application settings fixes housekeeping and refactoring#7362

Open
elecpower wants to merge 17 commits into
mainfrom
elecpower/cpn-upd-last-vers-per-profile
Open

chore(cpn): application settings fixes housekeeping and refactoring#7362
elecpower wants to merge 17 commits into
mainfrom
elecpower/cpn-upd-last-vers-per-profile

Conversation

@elecpower
Copy link
Copy Markdown
Collaborator

@elecpower elecpower commented May 11, 2026

Fixes #7360

Summary of changes:

  • store component update last release information against each radio profile
  • remove last release information from each component
  • fix enum settings storing @invalid()
  • fix export settings not following hierarchy
  • convert joystick settings as a consequence of fixing export
  • fix search for previous versions after upgrade
  • fix profile copy to include all hierarchy levels
  • fix joystick calibration copy and load - inv was missed
  • housekeeping

Summary by CodeRabbit

  • New Features
    • Per-profile, per-component release metadata persisted (release name, version, ID, date) so profiles track releases independently.
  • Refactor
    • Release state and initialization moved into profile-scoped storage and accessors.
  • Bug Fixes
    • Update/check/save/read operations now consistently use the active profile’s component release data, preventing cross-profile conflicts.

Review Change Stack

@elecpower elecpower added this to the 3.0 milestone May 11, 2026
@elecpower elecpower added companion Related to the companion software house keeping 🧹 Cleanup of code and house keeping labels May 11, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 11, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Moves component release metadata into per-profile ComponentReleaseData objects, adds Profile::compRelease with accessors, wires AppData to initialize/reset/store per-profile releases, updates CompStoreObj grouping/registrations, and refactors UpdateInterface to use the current profile's release entries.

Changes

Release Metadata Per-Profile Storage

Layer / File(s) Summary
Header: new type, profile fields, and registrations
companion/src/storage/appdata.h
Bumps CPN_SETTINGS_REVISION; adds ComponentReleaseData class and operator=; adds Profile::compRelease[MAX_COMPONENTS] and getCompRelease(int) declarations; removes ComponentData::releaseClear() declaration and per-component release Q_PROPERTY fields; updates setIndex/setIndexes declarations to register object mappings.
AppData implementation, grouping, and mapping
companion/src/storage/appdata.cpp
Adjusts CompStoreObj::splitGroupedPath to derive multi-segment groups; implements ComponentReleaseData ctor and Profile::getCompRelease(int); updates constructors to remove redundant addObjectMapping calls; registers Qt metatypes; sets profile[i].compRelease[j].setIndexes(i,j) in AppData ctor; initializes, resets, and stores per-profile compRelease entries in lifecycle methods; minor export/getComponent formatting and qDebug logging changes.
UpdateInterface: use profile-scoped release data
companion/src/updates/updateinterface.cpp
isReleaseLatest(), releaseClear(), releaseCurrent(), releaseSettingsSave(), and versionCurrent() now read/write/reset release metadata via g.currentProfile().getCompRelease(m_id) instead of global g.component[m_id].

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

I hopped through settings, one by one,
Per-profile releases, now they're done.
Each profile holds its own release line,
Stored and reset, neat and fine—hop, hop, time! 🐇

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 22.03% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ⚠️ Warning The PR title focuses on 'application settings fixes housekeeping and refactoring', but the actual changes primarily implement storing component update release information per radio profile and restructure the release data model. Update the title to reflect the main change: 'Store component update last release information per radio profile' to better represent the primary objective of this changeset.
✅ Passed checks (3 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The pull request description matches the required template with Issue #7360 referenced and a bullet-point summary of changes provided.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch elecpower/cpn-upd-last-vers-per-profile

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@companion/src/storage/appdata.cpp`:
- Around line 302-305: The constructor
ComponentReleaseData::ComponentReleaseData() calls
CompStoreObj::addObjectMapping(propertyGroup(), this) too early (propertyGroup()
reads profileIndex/index before it's initialized) and registers a stale mapping;
remove that call from the ctor and instead invoke
addObjectMapping(propertyGroup(), this) from setIndexes(...) immediately after
assigning the real profile id (or otherwise after profileIndex/index is
initialized) so the mapping is registered with the correct id; alternatively, if
you prefer to keep registration in ctor, ensure profileIndex/index is fully
initialized prior to calling propertyGroup() but the preferred change is to move
the addObjectMapping invocation into setIndexes.
- Around line 962-967: The reset path no longer clears moved release state
because resetUpdatesSettings() (the code block that calls
component[i].resetAll()) only clears component-level data; you must also clear
the per-profile last-known release stored in profile[].compRelease[] to avoid
stale update status. Update resetUpdatesSettings() to iterate over all profiles
and for each profile set compRelease[k] to a neutral/empty value (or call an
existing profile method that resets component release state) for k in
0..MAX_COMPONENTS-1 after/alongside component[i].resetAll(), and ensure any
related moved-release flags in profile or component (e.g., compRelease,
movedRelease flags) are cleared as well.

In `@companion/src/storage/appdata.h`:
- Around line 498-501: Profile's custom copy/assignment currently omits the new
compRelease[MAX_COMPONENTS] member so copied/assigned Profile objects lose
per-component release state; update Profile's copy constructor and operator=
(and any clone/assign helpers) to copy the entire compRelease array (e.g., copy
each ComponentReleaseData in compRelease or use std::copy) and ensure
getCompRelease semantics remain consistent so reordering and other copy paths
preserve per-component release state.
- Around line 462-488: The refactor moved persisted fields (release, releaseId,
version, prerelease, date) from the old "Components/component%1/*" path into
ComponentReleaseData's new propertyGroup()/settingsPath() under
"Profiles/profile%1/component%2/"; add a one-time migration that runs when the
stored settings version is bumped: locate where settings are loaded (e.g.
AppData initialization or Profile load) and for each profile/component attempt
to read legacy keys from
"Components/component%1/<release|releaseId|version|prerelease|date>" and, if
present, write them into the new ComponentReleaseData setters (or new settings
path) and remove/mark the old keys migrated; also increment the settings-version
constant so the migration executes exactly once.
- Around line 474-475: ComponentReleaseData now stores keys under
"Profiles/profile%1/component%2/" but CompStoreObj's validation only checks the
first path segment so keys like release/version/releaseId are not recognized;
update the CompStoreObj validation method (the is-known-key / validation
routine) to resolve nested settings paths by either stripping leading
"Profiles/profile<index>/" or by splitting the settings path and checking the
final segment(s) against known keys, so keys stored by
propertyGroup()/settingsPath() (the inline methods propertyGroup() and
settingsPath() in appdata.h) are correctly recognized during validation and
export/import cleanup.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e4aec7d0-e793-4b57-96fc-3df176592ed4

📥 Commits

Reviewing files that changed from the base of the PR and between 00d7544 and 6818add.

📒 Files selected for processing (3)
  • companion/src/storage/appdata.cpp
  • companion/src/storage/appdata.h
  • companion/src/updates/updateinterface.cpp

Comment thread companion/src/storage/appdata.cpp
Comment thread companion/src/storage/appdata.cpp
Comment thread companion/src/storage/appdata.h
Comment thread companion/src/storage/appdata.h
Comment thread companion/src/storage/appdata.h
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (2)
companion/src/storage/appdata.cpp (1)

944-961: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

resetUpdatesSettings() does not reset the per-profile release state.

After refactoring, the last-known release/version data now lives in profile[*].compRelease[*], but this function only resets component[i] and its assets. Users who reset update settings will retain stale release tracking data.

🔧 Suggested fix
 void AppData::resetUpdatesSettings()
 {
   updateCheckFreqReset();
   downloadDirReset();
   decompressDirReset();
   decompressDirUseDwnldReset();
   updateDirReset();
   updateDirUseSDReset();
   updDelDownloadsReset();
   updLogLevelReset();

   for (int i = 0; i < MAX_COMPONENTS; i++) {
     component[i].resetAll();

     for (int j = 0; j < MAX_COMPONENT_ASSETS; j++)
       component[i].asset[j].resetAll();
   }
+
+  for (int i = 0; i < MAX_PROFILES; i++) {
+    for (int j = 0; j < MAX_COMPONENTS; j++)
+      profile[i].compRelease[j].resetAll();
+  }
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@companion/src/storage/appdata.cpp` around lines 944 - 961,
resetUpdatesSettings() currently resets component[] and asset[] state but misses
clearing per-profile release/version tracking stored in profile[].compRelease[],
leaving stale release data after a reset; update resetUpdatesSettings() to
iterate over all profiles and all components and call/reset each
profile[p].compRelease[c] (or invoke a provided reset method on compRelease
entries) so every profile's per-component release state is cleared alongside
component[i].resetAll(), referencing profile and compRelease to locate the
fields to clear.
companion/src/storage/appdata.h (1)

503-506: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Profile copy semantics do not include compRelease array.

Profile::operator= (in appdata.cpp) only copies Q_PROPERTY members via Qt's meta-object system. The compRelease[MAX_COMPONENTS] array is a regular member and won't be copied when profiles are assigned or reordered (e.g., in moveCurrentProfileToTop()).

🔧 Suggested fix in Profile::operator= (appdata.cpp)
 Profile & Profile::operator= (const Profile & rhs)
 {
   for (int i = metaObject()->propertyOffset(), e = metaObject()->propertyCount(); i < e; ++i) {
     const QMetaProperty & prop = metaObject()->property(i);
     if (!prop.isValid() || !prop.isWritable()) {
       qWarning() << "Could not copy property" << QString(prop.name()) << "isValid:" << prop.isValid() << "isWritable:" << prop.isWritable();
       continue;
     }
     prop.write(this, prop.read(&rhs));
   }
+  for (int i = 0; i < MAX_COMPONENTS; ++i) {
+    compRelease[i] = rhs.compRelease[i];
+  }
   return *this;
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@companion/src/storage/appdata.h` around lines 503 - 506, Profile::operator=
currently only copies Q_PROPERTY members via Qt meta-object reflection and
therefore omits the regular member array compRelease[MAX_COMPONENTS], causing
compRelease to be lost on assignment or when reordering profiles (e.g.,
moveCurrentProfileToTop()). Fix operator= in appdata.cpp (the Profile::operator=
implementation) to explicitly copy the compRelease array into the destination
Profile — e.g., use std::copy or a loop to copy MAX_COMPONENTS entries of
ComponentReleaseData from the source to the target — so that compRelease and any
data returned by getCompRelease(int) are preserved during assignment and moves.
🧹 Nitpick comments (1)
companion/src/storage/appdata.cpp (1)

296-300: 💤 Low value

profileIndex is not initialized in the constructor.

While index is initialized to -1, profileIndex is left uninitialized. This is safe because setIndexes() is called before use, but explicit initialization improves defensive coding and prevents potential issues if usage patterns change.

♻️ Suggested improvement
-ComponentReleaseData::ComponentReleaseData() : CompStoreObj(), index(-1)
+ComponentReleaseData::ComponentReleaseData() : CompStoreObj(), profileIndex(-1), index(-1)
 {
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@companion/src/storage/appdata.cpp` around lines 296 - 300, The
ComponentReleaseData constructor leaves profileIndex uninitialized; update
ComponentReleaseData::ComponentReleaseData() to initialize profileIndex (e.g.,
to -1) alongside index to make the object defensively initialized; you can
verify usage via setIndexes() but ensure the constructor sets profileIndex to a
safe default to prevent potential future UB.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@companion/src/storage/appdata.cpp`:
- Around line 205-211: splitGroupedPath is using list.size() - 2 to derive the
group which breaks 5+ segment paths (e.g.,
NamedJSData/0/JsCalibration/1/stick_axe); change the logic so that for paths
with 5 or more segments you return the first two segments as the group (join
list.mid(0,2)), otherwise keep the existing behavior (for size>1 return
list.mid(0, list.size()-2).join("/"), fallback "General"); update
CompStoreObj::splitGroupedPath so
propertyPathIsValidNonDefault()/clearUnusedSettings()/exportSettings see the
correct registered group for nested NamedJSData paths.

In `@companion/src/storage/appdata.h`:
- Line 467: Implement the missing copy assignment for ComponentReleaseData in
appdata.cpp by defining ComponentReleaseData::operator=(const
ComponentReleaseData &rhs) to copy all Qt properties using
metaObject()->propertyOffset()/propertyCount() and QMetaProperty::read/write,
logging a warning if a property is invalid or not writable; return *this at the
end. Place this implementation alongside the ComponentReleaseData constructor in
appdata.cpp so copying compRelease data links correctly.

---

Duplicate comments:
In `@companion/src/storage/appdata.cpp`:
- Around line 944-961: resetUpdatesSettings() currently resets component[] and
asset[] state but misses clearing per-profile release/version tracking stored in
profile[].compRelease[], leaving stale release data after a reset; update
resetUpdatesSettings() to iterate over all profiles and all components and
call/reset each profile[p].compRelease[c] (or invoke a provided reset method on
compRelease entries) so every profile's per-component release state is cleared
alongside component[i].resetAll(), referencing profile and compRelease to locate
the fields to clear.

In `@companion/src/storage/appdata.h`:
- Around line 503-506: Profile::operator= currently only copies Q_PROPERTY
members via Qt meta-object reflection and therefore omits the regular member
array compRelease[MAX_COMPONENTS], causing compRelease to be lost on assignment
or when reordering profiles (e.g., moveCurrentProfileToTop()). Fix operator= in
appdata.cpp (the Profile::operator= implementation) to explicitly copy the
compRelease array into the destination Profile — e.g., use std::copy or a loop
to copy MAX_COMPONENTS entries of ComponentReleaseData from the source to the
target — so that compRelease and any data returned by getCompRelease(int) are
preserved during assignment and moves.

---

Nitpick comments:
In `@companion/src/storage/appdata.cpp`:
- Around line 296-300: The ComponentReleaseData constructor leaves profileIndex
uninitialized; update ComponentReleaseData::ComponentReleaseData() to initialize
profileIndex (e.g., to -1) alongside index to make the object defensively
initialized; you can verify usage via setIndexes() but ensure the constructor
sets profileIndex to a safe default to prevent potential future UB.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 764b5e3d-b336-41a0-8ea4-22fd2aaa6933

📥 Commits

Reviewing files that changed from the base of the PR and between 6818add and f8c7afe.

📒 Files selected for processing (2)
  • companion/src/storage/appdata.cpp
  • companion/src/storage/appdata.h

Comment thread companion/src/storage/appdata.cpp
Comment thread companion/src/storage/appdata.h
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@companion/src/storage/appdata.cpp`:
- Around line 981-984: The loop is using MAX_COMPONENT_ASSETS to clear
profile[i].compRelease but compRelease is a component-sized array elsewhere;
change the inner loop to iterate MAX_COMPONENTS instead of MAX_COMPONENT_ASSETS
so that profile[i].compRelease[j].resetAll() covers the actual compRelease
bounds (update the loop that references MAX_COMPONENT_ASSETS, keeping the call
to resetAll() on profile[i].compRelease[j]).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: d1225e6e-82e3-4bf3-8e74-d018490bd5d9

📥 Commits

Reviewing files that changed from the base of the PR and between f8c7afe and 729b3bd.

📒 Files selected for processing (1)
  • companion/src/storage/appdata.cpp

Comment thread companion/src/storage/appdata.cpp
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
companion/src/storage/appdata.cpp (2)

242-242: 💤 Low value

Remove debug logging before merge.

This qDebug() statement will emit logging output during normal operation when keyToNameMap() is first accessed.

🧹 Suggested fix
       map.insert(it.key(), grpMap);
-      qDebug() << it.key() << grpMap;
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@companion/src/storage/appdata.cpp` at line 242, Remove the stray debug output
by deleting or guarding the qDebug() << it.key() << grpMap; call inside
keyToNameMap() (or the function that iterates with variable it and grpMap) so
normal operation does not emit logs; either remove the statement entirely or
wrap it in a debug-only guard (e.g., `#ifdef` QT_DEBUG) to ensure it only runs in
debug builds.

925-932: 💤 Low value

Remove debug logging from exportSettings.

These qDebug() statements will produce verbose output during normal export operations.

🧹 Suggested fix
   foreach (const QString & key, m_settings.allKeys()) {
     const QVariant newVal = m_settings.value(key);
-    qDebug() << key << newVal;
     // Skip export if property does not exist or is the default value.
     if (newVal.isValid() && CompStoreObj::propertyPathIsValidNonDefault(key, newVal))
       toSettings->setValue(key, newVal);
-    else qDebug() << "SKIPPING:" << key << newVal;
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@companion/src/storage/appdata.cpp` around lines 925 - 932, Remove the
temporary debug logging inside the settings export loop: delete the two qDebug()
calls so exportSettings no longer prints every key/value or "SKIPPING" lines;
keep the logic that checks m_settings.allKeys(), uses m_settings.value(key),
calls CompStoreObj::propertyPathIsValidNonDefault(key, newVal), and writes to
toSettings->setValue(key, newVal) unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@companion/src/storage/appdata.cpp`:
- Around line 498-503: The code is mistakenly writing stick_min twice and never
saving the inversion flag; in the block that assigns joystick fields (the
snippet updating namedJS[i].joystick[j]) replace the duplicated
namedJS[i].joystick[j].stick_min(joystick[j].stick_min()) with
namedJS[i].joystick[j].stick_inv(joystick[j].stick_inv()); do the same
corrective change in loadNamedJS where stick_min is duplicated (replace the
second stick_min assignment with stick_inv) so stick_inv is persisted and
restored correctly.

---

Nitpick comments:
In `@companion/src/storage/appdata.cpp`:
- Line 242: Remove the stray debug output by deleting or guarding the qDebug()
<< it.key() << grpMap; call inside keyToNameMap() (or the function that iterates
with variable it and grpMap) so normal operation does not emit logs; either
remove the statement entirely or wrap it in a debug-only guard (e.g., `#ifdef`
QT_DEBUG) to ensure it only runs in debug builds.
- Around line 925-932: Remove the temporary debug logging inside the settings
export loop: delete the two qDebug() calls so exportSettings no longer prints
every key/value or "SKIPPING" lines; keep the logic that checks
m_settings.allKeys(), uses m_settings.value(key), calls
CompStoreObj::propertyPathIsValidNonDefault(key, newVal), and writes to
toSettings->setValue(key, newVal) unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 1752a57d-cf0c-46b2-b7b8-6e4eec80552d

📥 Commits

Reviewing files that changed from the base of the PR and between 80292c3 and be32f68.

📒 Files selected for processing (2)
  • companion/src/storage/appdata.cpp
  • companion/src/storage/appdata.h

Comment thread companion/src/storage/appdata.cpp
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 13, 2026

Note

Unit test generation is a public access feature. Expect some limitations and changes as we gather feedback and continue to improve it.


Generating unit tests... This may take up to 20 minutes.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 13, 2026

✅ Unit tests committed locally. Commit: c49b1c226b6d0c5f46796d57adaa2959d19597e2

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 14, 2026

Note

Unit test generation is a public access feature. Expect some limitations and changes as we gather feedback and continue to improve it.


Generating unit tests... This may take up to 20 minutes.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 14, 2026

✅ Created PR with unit tests: #7367

@elecpower elecpower marked this pull request as draft May 15, 2026 05:25
@elecpower elecpower changed the title chore(cpn): component update last release information stored per radio profile chore(cpn): application settings fixes housekeeping and refactoring May 17, 2026
@elecpower elecpower marked this pull request as ready for review May 17, 2026 10:26
@elecpower elecpower added the backport/2.12 To be backported to a 2.12 release also. label May 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport/2.12 To be backported to a 2.12 release also. companion Related to the companion software house keeping 🧹 Cleanup of code and house keeping

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Companion check update for different radio type

1 participant