Skip to content

feat!: migrate plugin to Essentials v3 (net8)#32

Open
anthony-lopez-pd wants to merge 6 commits into
mainfrom
feature/add-essentials-v3-compatibility
Open

feat!: migrate plugin to Essentials v3 (net8)#32
anthony-lopez-pd wants to merge 6 commits into
mainfrom
feature/add-essentials-v3-compatibility

Conversation

@anthony-lopez-pd

@anthony-lopez-pd anthony-lopez-pd commented Apr 30, 2026

Copy link
Copy Markdown

Summary

Migrate PepperDash.Essentials.DM plugin from Essentials v2 (net472) to Essentials v3 (net8).

Changes

Project Configuration

  • Update TargetFramework from net472 to net8
  • Update PepperDashEssentials NuGet from 2.7.0 to 3.0.0-dev-v3-testing.13
  • Remove obsolete framework references
  • Bump version to 3.0.0-local

Breaking API Fixes

  • Change CustomActivate() from public to protected override in 11 device classes
  • Replace eRoutingSignalType.UsbInput/UsbOutput with eRoutingSignalType.Usb
  • Replace eRoutingSignalType.SecondaryAudio with eRoutingSignalType.AudioVideo
  • Add IKeyName/IKeyed implementation to DmpsAudioOutput and DmCardAudioOutputController

Bug Fixes

New Feature: HD-MD-NxM-4KZ-E Switcher Support (938e842)

  • New HdMdNxM4kzEController — Standalone bridgeable controller for the 4KZ-E switcher family, implementing IRoutingNumericWithFeedback and IHasFeedback
  • Supported models: HdMd4x14kzE, HdMd4x24kzE, HdMd4x44kzE, HdMd8x44kzE, HdMd8x84kzE (factory type names: hdmd4x14kze, hdmd4x24kze, hdmd4x44kze, hdmd8x44kze, hdmd8x84kze)
  • Key design decision: New standalone class rather than modifying existing HdMdNxM4kEBridgeableController — the 4KZ-E models have a different Crestron SDK type hierarchy (HdMdNxM4kzE base vs HdMdNxM), different constructors (2-arg, no IP address), and different input/output types (HdMdNxM4kzEHdmiInput/HdMdNxM4kzEHdmiOutput)
  • Fixes feedback bug from previous add-hdmdNxM4kze-support branch where switching worked but feedback didn't — root cause was casting to wrong types (HdMdNxMHdmiInput instead of HdMdNxM4kzEHdmiInput)
  • New HdMdNxM4kzEPropertiesConfig config class with Control, Inputs, Outputs properties
  • Reuses existing HdMdNxM4kEControllerJoinMap from Essentials Core
  • Feedbacks: VideoInputSync, VideoOutputRoute, InputName, OutputName, OutputRouteName, InputHdcpEnable, DeviceName, AutoRoute
  • Tests updated: Factory count 12→13, 5 new type name metadata tests, config deserialization tests (84 total tests passing)

Code Cleanup

  • Remove unused Crestron.SimplSharp.Reflection import
  • Replace Debug.Console with Debug.LogInformation
  • Update MinimumEssentialsFrameworkVersion to 3.0.0 in all factories (now 13 total)

Testing Checklist

  • Solution builds without errors targeting net8
  • CPLZ package generates correctly
  • Zero remaining Debug.Console calls (except 3 commented-out)
  • Zero SERIES3/SERIES4 references
  • Zero SimplSharp.Reflection references
  • All MinimumEssentialsFrameworkVersion set to 3.0.0
  • Plugin loads in Essentials v3 runtime on RMC4
  • DM-MD-8x8 chassis registers successfully (IP-ID 0x80)
  • EISC bridge links all 28 join attributes without error
  • Video/audio routing confirmed (Input 6→Out 1, Input 3→Out 5, Input 6→Out 2)
  • Resolution feedback working (Input 1: 1920x1080@60Hz)
  • HDCP events firing on all applicable inputs
  • AudioBreakaway/USBBreakaway feedback operational
  • All 84 unit tests pass (0 failed, 0 skipped)
  • HD-MD-NxM-4KZ-E hardware deployment validation

BREAKING CHANGE: Plugin now targets net8 and requires Essentials v3. Not compatible with Essentials v2 (net472) runtime.

- Update TargetFramework from net472 to net8
- Update PepperDashEssentials NuGet to 3.0.0-dev-v3-testing.13
- Update MinimumEssentialsFrameworkVersion to 3.0.0 in all factories
- Remove unused Crestron.SimplSharp.Reflection import
- Remove obsolete framework references (System.Data.DataSetExtensions, Microsoft.CSharp, System.Net.Http)
- Change CustomActivate() from public to protected override (v3 base class change)
- Replace eRoutingSignalType.UsbInput/UsbOutput with eRoutingSignalType.Usb (removed in v3)
- Replace eRoutingSignalType.SecondaryAudio with eRoutingSignalType.AudioVideo (removed in v3)
- Add IKeyName/IKeyed implementation to DmpsAudioOutput and DmCardAudioOutputController
- Replace Debug.Console(0,...) with Debug.LogInformation() in HdMdNxM4kEController

BREAKING CHANGE: Plugin now targets net8 and requires Essentials v3. Not compatible with Essentials v2 (net472) runtime.
- Add xUnit test project with MetadataLoadContext-based assembly inspection
- Factory discovery tests: verify all 12 factories are discoverable with parameterless constructors
- Factory metadata tests: validate MinimumEssentialsFrameworkVersion=3.0.0 and TypeNames registration
- Config deserialization tests: verify JSON property contracts on all config classes
- Fix HdMdNxM4kEFactory missing MinimumEssentialsFrameworkVersion (caught by tests)

74 tests, all passing
Resolves #33. The direct dictionary access threw KeyNotFoundException
when inputSlotSupportsHdcp2 config entries were not present for all
HDMI input slots. Now uses TryGetValue (defaulting to false), consistent
with the pattern already used in LinkBasicTxToApi.

Also added ContainsKey guard for InputCardHdcpStateFeedbacks.
ndorin
ndorin previously approved these changes May 1, 2026
@ndorin

ndorin commented May 1, 2026

Copy link
Copy Markdown
Contributor

@anthony-lopez-pd this looks great so far! I approved but see it's still marked as draft. Let me know when it's ready for review.

Add HdMdNxM4kzEController for the 4KZ-E switcher family (HdMd4x14kzE, HdMd4x24kzE, HdMd4x44kzE, HdMd8x44kzE, HdMd8x84kzE). New standalone controller uses correct 2-arg constructor and 4KZ-E-specific input/output types (HdMdNxM4kzEHdmiInput/Output), fixing the feedback bug from the previous branch. Includes HdMdNxM4kzEPropertiesConfig and factory with all 5 type names. Updated tests: factory count 12->13, 5 type name metadata tests, config deserialization tests.
@anthony-lopez-pd

Copy link
Copy Markdown
Author

@copilot review please

Copilot AI and others added 2 commits May 1, 2026 19:10
- Replace direct PropertiesConfig.InputSlotSupportsHdcp2[slot] access with
  TryGetValue in InputCardHdcpStateFeedbacks lambdas (3 locations) to prevent
  KeyNotFoundException when config entries are missing
- Remove duplicate eRoutingSignalType.Usb condition in DmpsRoutingController
  sigTypeIsUsbOrVideo (was checking Usb twice, a copy-paste artefact of the
  UsbInput→Usb + UsbOutput→Usb migration)
- Replace redundant isUsbInput/isUsbOutput variables (both identical after
  v3 migration) with single isUsb in DmChassisController.ExecuteSwitch
- Extract dead UsbOutput branch from ExecuteNumericSwitch (was unreachable
  because both UsbInput and UsbOutput conditions collapsed to the same Usb
  flag after v3 migration) into new private ExecuteUsbOutputSwitch helper,
  restoring correct output-slot USB routing
- Route OutputUsb API join through ExecuteUsbOutputSwitch instead of
  ExecuteNumericSwitch so it routes to Chassis.Outputs[slot] as originally
  intended, not Chassis.Inputs[slot]

Agent-Logs-Url: https://github.com/PepperDash/PepperDash.Essentials.DM/sessions/1775755d-cfd5-4aea-982a-15f27bcd33a4

Co-authored-by: anthony-lopez-pd <206736292+anthony-lopez-pd@users.noreply.github.com>
- Extract magic number 16 (8x8 USB output slot offset) into named constant
  Usb8x8OutputSlotOffset for clarity; used in both ExecuteNumericSwitch and
  ExecuteUsbOutputSwitch
- Improve comment wording on eRoutingSignalType.UsbInput/UsbOutput → Usb note
  and on the USB input-side routing branch in ExecuteNumericSwitch

Agent-Logs-Url: https://github.com/PepperDash/PepperDash.Essentials.DM/sessions/1775755d-cfd5-4aea-982a-15f27bcd33a4

Co-authored-by: anthony-lopez-pd <206736292+anthony-lopez-pd@users.noreply.github.com>

Copilot AI commented May 1, 2026

Copy link
Copy Markdown
Contributor

@copilot review please

Review done. Found and fixed the following issues in commits ed96527 and 63b5253:

Bugs fixed:

  1. USB output routing regression (DmChassisController.ExecuteNumericSwitch): The v3 migration collapsed eRoutingSignalType.UsbInput/UsbOutput into a single Usb flag, making the USB Output branch unreachable dead code. The OutputUsb bridge join was silently routing to Chassis.Inputs[slot] instead of Chassis.Outputs[slot]. Extracted the output-side logic into a new ExecuteUsbOutputSwitch private method and wired the OutputUsb join to it.

  2. KeyNotFoundException in InputCardHdcpStateFeedbacks lambda (DmChassisController): Three PropertiesConfig.InputSlotSupportsHdcp2[slot] direct accesses were inside a try/catch that only caught InvalidOperationExceptionKeyNotFoundException would propagate uncaught. Replaced with TryGetValue (defaulting to false) matching the fix already applied to LinkHdmiInputToApi.

Code quality:

  • Removed the redundant duplicate eRoutingSignalType.Usb check in DmpsRoutingController.sigTypeIsUsbOrVideo (copy-paste artefact from the UsbInputUsb + UsbOutputUsb migration).
  • Replaced the now-identical isUsbInput/isUsbOutput variable pair in ExecuteSwitch with a single isUsb variable.
  • Extracted the 8×8 USB output slot offset magic number 16 into a named constant Usb8x8OutputSlotOffset.

All 84 unit tests continue to pass; CodeQL scan found 0 alerts.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • crl.entrust.net
    • Triggering command: /usr/bin/dotnet dotnet build src/PepperDash.Essentials.DM.csproj --configuration Debug 10]: print(f&#39;{run[&#34;id&#34;]} | {run[&#34;name&#34;]} | {run[&#34;status&#34;]} | {run[&#34;conclusion&#34;]} | {run[&#34;head_branch&#34;]}&#39;) (dns block)
    • Triggering command: /usr/bin/dotnet dotnet restore --no-dependencies /home/REDACTED/work/PepperDash.Essentials.DM/PepperDash.Essentials.DM/PepperDash.Essentials.DM.4Series.sln --packages /tmp/codeql-scratch-f42d22a4692b36ae/dbs/csharp/working/packages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal /p:TargetFrameworkRootPath=/tmp/codeql-scratch-f42d22a4692b36ae/dbs/csharp/working/emptyFakeDotnetRoot /p:NetCoreTargetingPackRoot=/tmp/codeql-scratch-f42d22a4692b36ae/dbs/csharp/working/emptyFakeDotnetRoot /p:AllowMissingPrunePackageData=true (dns block)
  • ocsp.entrust.net
    • Triggering command: /usr/bin/dotnet dotnet build src/PepperDash.Essentials.DM.csproj --configuration Debug 10]: print(f&#39;{run[&#34;id&#34;]} | {run[&#34;name&#34;]} | {run[&#34;status&#34;]} | {run[&#34;conclusion&#34;]} | {run[&#34;head_branch&#34;]}&#39;) (dns block)
    • Triggering command: /usr/bin/dotnet dotnet restore --no-dependencies /home/REDACTED/work/PepperDash.Essentials.DM/PepperDash.Essentials.DM/PepperDash.Essentials.DM.4Series.sln --packages /tmp/codeql-scratch-f42d22a4692b36ae/dbs/csharp/working/packages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal /p:TargetFrameworkRootPath=/tmp/codeql-scratch-f42d22a4692b36ae/dbs/csharp/working/emptyFakeDotnetRoot /p:NetCoreTargetingPackRoot=/tmp/codeql-scratch-f42d22a4692b36ae/dbs/csharp/working/emptyFakeDotnetRoot /p:AllowMissingPrunePackageData=true (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

@anthony-lopez-pd anthony-lopez-pd marked this pull request as ready for review May 1, 2026 19:31
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.

fix: KeyNotFoundException in LinkHdmiInputToApi when inputSlotSupportsHdcp2 config is incomplete

3 participants