EssentialsPartitionController.PartitionPresent threw a NullReferenceException in Auto mode when the partition sensor's PartitionPresentFeedback was not yet initialized, breaking combiner fullStatus serialization to MC clients. Fall back to the last-known _partitionPresent value instead of dereferencing a null sensor feedback.
- ConnectedClientVersions now deep-copies each ConnectedClientVersionInfo
when snapshotting, so external callers can't mutate the internally
tracked, lock-protected instances (Copilot review).
- ShowInfo now prints "(not configured)" for an empty ExpectedAppVersion,
matching the match/mismatch calculation which already treats empty the
same as not-configured (Copilot review).
- GetPackageManifestRequestHandler.PopulatePackages filters out null
entries from the config-supplied packages list before processing, so a
malformed "packages": [null, ...] in user-edited config JSON degrades
gracefully instead of throwing (Copilot review).
Extend the /system/clientJoined handler to accept an optional
`appVersion` field reported by connecting UI clients (e.g. the
React app's build-time APP_VERSION), so Essentials can record and
validate what's actually running against the configured
versions.touchpanelWrapperApp version.
- Add ConnectedClientVersionInfo to capture clientId, roomKey,
touchpanelKey, reported/expected app version, and last-seen time.
- Add MobileControlSystemController.TrackClientAppVersion(), storing
results in a new thread-safe ConnectedClientVersions dictionary and
logging a warning on version mismatch.
- Expose ConnectedClientVersions as a public read-only property for
diagnostics.
- Surface reported vs. expected versions per client in the
`mobileinfo` console command output.
No wire protocol changes required; content is passed as-is over the
existing clientJoined message.
- Determine config version by presence of "system" and "template" nodes
instead of "versions", since a v2 config can also include "versions"
and was previously being skipped from merging as a result.
- Preserve the "versions" node after merging a v1 config, since
PortalConfigReader.MergeConfigs does not carry it forward.
WHAT / WHY
----------
GetPackageManifestRequestHandler previously identified a loaded plugin's
NuGet PackageId by fuzzy-matching AssemblyTitle/AssemblyName against the
config's packageId - a chain that silently fails for any plugin whose
AssemblyTitle/AssemblyName doesn't happen to match its PackageId (verified
against real shipped plugin DLLs; see FINDINGS-nuget-packageid-gaps.md).
epi-symetrix-dsp and epi-shure-mxa have already backported a
Directory.Build.props change that embeds
`<AssemblyMetadata Include="PackageId" Value="$(PackageId)" />`, giving
Essentials an unambiguous, authoritative PackageId via
AssemblyMetadataAttribute("PackageId", ...) instead of guessing. Validated
by building both plugins and inspecting the generated AssemblyInfo.cs.
Changes:
- src/Directory.Build.props: add the same AssemblyMetadata PackageId item,
so every Essentials-owned assembly (Core, Essentials, Devices.Common,
MobileControl, MobileControl.Messengers) now embeds its real PackageId
too - previously none of them did.
- GetPackageManifestRequestHandler.cs:
- PopulatePackages: read AssemblyMetadataAttribute("PackageId", ...) from
loaded plugin assemblies and use it as the first-priority match/identity
signal, ahead of the AssemblyTitle -> AssemblyName -> AssemblyName-minus-
".4Series" fallback chain. Loaded-but-unconfigured plugins that carry
this metadata now report a PackageId in the manifest instead of null.
- PopulateEssentials: replace the hardcoded "PepperDash.Essentials"
fallback (which matched none of the real PackageIds) with the reflected
value from PepperDash.Essentials.Core's own assembly metadata.
Fully backward compatible: plugins without the updated Directory.Build.props
(most existing epi-* repos today) fall through to the prior fallback chain
unchanged.
RECOMMENDATIONS - Essentials & sub-projects (this repo)
--------------------------------------------------------
- AssemblyName/AssemblyTitle drift from PackageId across sub-projects
(confirmed via generated AssemblyInfo.cs, not assumed):
PepperDash.Essentials.Core: PackageId "PepperDash.Essentials.Core"
vs AssemblyName "PepperDash_Essentials_Core"
PepperDash.Essentials.Devices.Common: PackageId "PepperDash.Essentials.Devices.Common"
vs AssemblyName "Essentials Devices Common"
PepperDash.Essentials.MobileControl: PackageId "PepperDash.Essentials.MobileControl"
vs AssemblyName "epi-essentials-mobile-control"
PepperDash.Essentials.MobileControl.Messengers: PackageId "...Messengers"
vs AssemblyName "mobile-control-messengers"
Only PepperDash.Essentials and PepperDash.Core happen to agree. Fixing
AssemblyName changes the physical .dll filename for existing consumers,
so this needs a deliberate, versioned decision - not bundled here.
- Once this ships and bakes for a release or two, consider deleting the
now-redundant "PepperDash.Essentials" hardcoded string entirely and the
Product/AssemblyTitle-based Name fallback, since AssemblyMetadata PackageId
supersedes both for any assembly built after this change.
RECOMMENDATIONS - EPI plugin repos (epi-*)
-------------------------------------------
- Backport `<AssemblyMetadata Include="PackageId" Value="$(PackageId)" />`
into every existing epi-* repo's src/Directory.Build.props (recommendation
E from FINDINGS-nuget-packageid-gaps.md). This is opt-in and additive -
repos that skip it keep working via the existing fallback chain, but gain
nothing until they backport it and cut a new release.
- Land the corresponding fix in EssentialsPluginTemplate
(src/Directory.Build.props + src/epi-make-model.4Series.csproj) so all
*new* plugin repos get this by default, and fix the template's own
AssemblyTitle/PackageId drift ("Plugin" vs "Plugins") while there.
- Already-published plugin versions can't be retroactively fixed - this only
takes effect on a plugin's next release after adopting the template change.
WORKFLOW RECOMMENDATIONS
-------------------------
- Extend workflow-templates' essentialsplugins-4Series-builds.yml "Check
Package Name" step to validate the built DLL's embedded
AssemblyMetadataAttribute("PackageId", ...) (and/or AssemblyTitle as a
fallback) against the repo-derived expected package name - today it only
compares the .nupkg filename, which would not have caught drift like
epi-display-samsung-mdc's AssemblyTitle mismatch.
- Sequence this after the template + per-repo backports have landed and
baked for a release cycle, otherwise it will fail CI for every epi-*
repo that hasn't picked up the Directory.Build.props change yet. Gate it
behind the existing bypassPackageCheck input for repos not yet ready.
New GET https://{ip}/cws/app{xx}/api/packageManifest route returning a JSON
package manifest (Essentials + plugins + user interfaces) shaped to hydrate
the vsce-essentials-version-manager extension's VersionsSnapshot object.
- EssentialsConfig.cs: add [JsonProperty("versions")] to EssentialsConfig.Versions
(was attribute-less); add NugetVersion.Name; NullValueHandling.Ignore on
PackageId/RepoUrl/Name.
- New GetPackageManifestRequestHandler: deep-copies the config's VersionData
(never mutates the live config object), then enriches it via reflection:
- essentials: version from Global.AssemblyVersion, repoUrl/name from the
RepositoryUrl AssemblyMetadata + AssemblyProduct of
PepperDash.Essentials.Core's own assembly (PluginLoader.EssentialsAssembly.Assembly
is null at runtime due to a pre-existing name-matching bug, so this route
reads its own loaded assembly instead), packageId from config or a constant.
- packages[]: merges PluginLoader.EssentialsPluginAssemblies (matched to config
packages by packageId via AssemblyTitle -> AssemblyName -> AssemblyName minus
a trailing .4Series suffix) with reflection supplying version and filling
missing repoUrl/name; unmatched loaded assemblies are emitted without a
packageId; configured-but-not-loaded packages pass through unchanged.
- userInterfaces/touchpanelWrapperApp are passed through from config as-is.
- Entries with no resolvable version are skipped (the extension's parser drops
entries whose version isn't a string).
- EssentialsWebApi.cs: register the new packageManifest route next to versions.
Build verified clean (Core + Essentials program, 0 errors). Not yet tested on
hardware.
Add touchpanelWrapperApp, userInterfaces, and repoUrl to VersionData config schema
VersionData/NugetVersion previously only modeled the essentials and
packages entries under the versions node. This adds support for the
touchpanelWrapperApp (single object) and userInterfaces (array) nodes,
plus a repoUrl property on NugetVersion so it round-trips instead of
being silently dropped, matching the schema produced by the
vsce-essentials-version-manager extension.