Skip to content

Latest commit

 

History

History
433 lines (345 loc) · 17.4 KB

File metadata and controls

433 lines (345 loc) · 17.4 KB

Python to .NET Migration Guide

Comprehensive comparison between the Python pixelblaze-client library and the .NET PixelblazeDotNet port.

Project Status

Status: Wet Paint (Jan 2026) - Sprint 1-2 (protocol), Sprint 4 (feature parity), Sprint 5 (bug fixes) complete. Sprint 3 (connection resilience) parked.


Architecture Comparison

Aspect Python (pixelblaze-client) .NET (PixelblazeDotNet)
Structure Single 3000+ line class 11 modular classes
Async Synchronous All async/await
Logging Print statements Microsoft.Extensions.Logging
Typing Dynamic Strongly-typed with nullables
Caching Minimal Time-based caching (5 sec)
Connection WebSocket only WebSocket + TCP support
Message Handling Serial, blocking Single pipeline, serialized requests

Class Mapping

Python .NET
Pixelblaze (single class) PixelblazeClient (facade)
PixelblazeSequencer
PixelblazePatterns
PixelblazeMapper
PixelblazeSettings
PixelblazeStatistics
PixelblazeBinaryBackup
PixelblazeAdvanced
PixelblazeNetworkHelper
PixelblazeEnumerator PixelblazeDiscovery
PBB PixelblazeBinaryBackup
PBP PixelblazeBinaryPattern
EPE ElectromagePatternExport

Method Mapping

Connection & Discovery

Python .NET Status
Pixelblaze(ipAddress) new PixelblazeClient(ip, loggerFactory) Ported
EnumerateAddresses() PixelblazeDiscovery.DiscoverDevicesAsync() Ported
EnumerateDevices() PixelblazeClient.DiscoverClientsAsync() Ported
getPeers() - Not ported
Context manager (with) IAsyncDisposable Ported

Sequencer

Python .NET Status
getSequencerMode() Sequencer.GetSequencerModeAsync() Ported
setSequencerMode() Sequencer.SetSequencerModeAsync() Ported
getSequencerState() Sequencer.GetSequencerStateAsync() Ported
setSequencerState() Sequencer.SetSequencerStateAsync() Ported
playSequencer() Sequencer.PlaySequencerAsync() Ported
pauseSequencer() Sequencer.PauseSequencerAsync() Ported
nextSequencer() Sequencer.NextSequencerAsync() Ported
getSequencerShuffleTime() Sequencer.GetSequencerShuffleTimeAsync() Ported
setSequencerShuffleTime() Sequencer.SetSequencerShuffleTimeAsync() Ported
getSequencerPlaylist() Sequencer.GetSequencerPlaylistAsync() Ported
setSequencerPlaylist() Sequencer.SetSequencerPlaylistAsync() Ported
addToSequencerPlaylist() Sequencer.AddToSequencerPlaylistAsync() Ported

Pattern Management

Python .NET Status
getPatternList() Patterns.GetPatternListAsync() Ported
getActivePattern() Patterns.GetActivePatternAsync() Ported
setActivePattern() Patterns.SetActivePatternAsync() Ported
setActivePatternByName() Patterns.SetActivePatternByNameAsync() Ported
deletePattern() PixelblazeClient.DeletePatternAsync() Ported
getPatternAsEpe() Patterns.GetPatternAsEpeAsync() Ported
getPreviewImage() Via PixelblazeBinaryPattern.Jpeg Ported
getPatternSourceCode() Advanced.GetPatternSourceCodeAsync() Ported
getPatternControls() Patterns.GetPatternControlsAsync() Ported
sendPatternToRenderer() Patterns.SendPatternToRendererAsync() Ported
savePattern() Patterns.SavePatternAsync() Ported
- Patterns.DuplicatePatternAsync() .NET only

Variables & Controls

Python .NET Status
getActiveVariables() Patterns.GetActiveVariablesAsync() Ported
setActiveVariables() Patterns.SetActiveVariablesAsync() Ported
getActiveControls() Patterns.GetActiveControlsAsync() Ported
setActiveControls() Patterns.SetActiveControlsAsync() Ported
controlExists() Patterns.ControlExistsAsync() Ported
getColorControlName() Patterns.GetColorControlNameAsync() Ported
getColorControlNames() Patterns.GetColorControlNamesAsync() Ported
setColorControl() Patterns.SetColorControlAsync() Ported

Mapper

Uses HTTP file operations (/pixelmap.txt, /pixelmap.dat) like Python.

Python .NET Status
getMapFunction() Mapper.GetMapFunctionAsync() Ported (HTTP)
setMapFunction() Mapper.SetMapFunctionAsync() Ported (HTTP)
getMapData() Mapper.GetMapDataAsync() Ported (HTTP)
setMapData() Mapper.SetMapDataAsync() Ported (HTTP)
getMapCoordinates() Mapper.GetMapCoordinatesAsync() Ported
setMapCoordinates() Mapper.SetMapCoordinatesAsync() Ported
createMapData() Mapper.CreateMapDataAsync() Ported

Settings

Python .NET Status
getConfigSettings() Settings.GetAllSettingsAsync() Ported
getConfigSequencer() NetworkHelper.GetConfigSequencerAsync() Ported (internal)
getConfigExpander() Settings.GetConfigExpanderAsync() Ported
getDeviceName() Settings.GetDeviceNameAsync() Ported
setDeviceName() Settings.SetDeviceNameAsync() Ported
getDiscovery() Settings.GetDiscoveryAsync() Ported
setDiscovery() Settings.SetDiscoveryAsync() Ported
getTimezone() Settings.GetTimezoneAsync() Ported
setTimezone() Settings.SetTimezoneAsync() Ported
getAutoOffEnable() Settings.GetAutoOffEnableAsync() Ported
setAutoOffEnable() Settings.SetAutoOffEnableAsync() Ported
getAutoOffStart() Settings.GetAutoOffStartAsync() Ported*
setAutoOffStart() Settings.SetAutoOffStartAsync() Ported*
getAutoOffEnd() Settings.GetAutoOffEndAsync() Ported*
setAutoOffEnd() Settings.SetAutoOffEndAsync() Ported*
getBrightnessLimit() Settings.GetBrightnessLimitAsync() Ported
setBrightnessLimit() Settings.SetBrightnessLimitAsync() Ported
getBrightnessSlider() Settings.GetBrightnessAsync() Ported
setBrightnessSlider() Settings.SetBrightnessAsync() Ported
getLedType() Settings.GetLedTypeAsync() Ported
setLedType() Settings.SetLedTypeAsync() Ported
getPixelCount() Settings.GetPixelCountAsync() Ported
setPixelCount() Settings.SetPixelCountAsync() Ported
getDataSpeed() Settings.GetDataSpeedAsync() Ported
setDataSpeed() Settings.SetDataSpeedAsync() Ported
getColorOrder() Settings.GetColorOrderAsync() Ported
setColorOrder() Settings.SetColorOrderAsync() Ported
getCpuSpeed() Settings.GetCpuSpeedAsync() Ported
setCpuSpeed() Settings.SetCpuSpeedAsync() Ported
getNetworkPowerSave() Settings.GetNetworkPowerSaveAsync() Ported
setNetworkPowerSave() Settings.SetNetworkPowerSaveAsync() Ported
getBrandName() Settings.GetBrandNameAsync() Ported
setBrandName() Settings.SetBrandNameAsync() Ported
getSimpleUiMode() Settings.GetSimpleUiModeAsync() Ported
setSimpleUiMode() Settings.SetSimpleUiModeAsync() Ported
getLearningUiMode() Settings.GetLearningUiModeAsync() Ported
setLearningUiMode() Settings.SetLearningUiModeAsync() Ported

*Note: Python uses "HH:MM" string format; .NET Settings uses string, but Advanced uses int (minutes).

Statistics

Note: Pixelblaze broadcasts stats every second. Only broadcast fields are available.

Python .NET Status
getStatistics() Statistics.GetAllStatisticsAsync() Ported (broadcast fields only)
getFPS() Statistics.GetFPSAsync() Ported
getUptime() Statistics.GetUptimeAsync() Ported
getStorageSize() Statistics.GetStorageSizeAsync() Ported
getStorageUsed() Statistics.GetStorageUsedAsync() Ported
- Statistics.GetFreeStoragePercentageAsync() .NET only (calculated)
- Statistics.GetFreeRAMAsync() .NET only (mem field)
- Statistics.GetTotalRAMAsync() NotSupported†
- Statistics.GetRAMPercentageUsedAsync() NotSupported†
- Statistics.GetPatternCountAsync() NotSupported†
- Statistics.GetQueueLengthAsync() NotSupported†
- Statistics.WaitForEmptyQueueAsync() NotSupported†

†These methods throw NotSupportedException - data not available in Pixelblaze protocol.

Filesystem

Python .NET Status
getFileList() BinaryBackup.GetFileListAsync() Ported
getFile() BinaryBackup.GetFileAsync() Ported
putFile() BinaryBackup.PutFileAsync() Partial*
deleteFile() BinaryBackup.DeleteFileAsync() Partial*

*Note: .NET implementation currently only updates local cache, does not send to device.

Advanced

Python .NET Status
reboot() Advanced.RebootAsync() Ported
installUpdate() Advanced.InstallUpdateAsync() Ported
getUpdateState() Advanced.GetUpdateStateAsync() Ported
getVersion() Settings.GetVersionAsync() Ported
getVersionMajor() Settings.GetVersionMajorAsync() Ported
getVersionMinor() Settings.GetVersionMinorAsync() Ported
saveBackup() BinaryBackup.SaveAsync() Ported
restoreFromBackup() BinaryBackup.RestoreAsync() Ported

Preview & Rendering

Python .NET Status
setSendPreviewFrames() Renderer.SetSendPreviewFramesAsync() Ported
getPreviewFrame() Renderer.GetPreviewFrameAsync() Ported
pauseRenderer() Renderer.PauseRendererAsync() Ported

Low-Level

Python .NET Status
wsReceive() Internal Ported
wsSendJson() Internal Ported
wsSendBinary() Internal Ported
sendPing() PixelblazeNetworkHelper.SendPingAsync() Ported

Enum Mapping

Python .NET Status
sequencerModes - Not ported (use strings)
ledTypes - Not ported (use strings)
colorOrders - Not ported (use strings)
cpuSpeeds - Not ported (use strings)
updateStates - Not ported (use dynamic)
messageTypes Internal Ported
frameTypes Internal Ported
fileTypes PixelblazeFileType Ported

Class Equivalents

PBB (Binary Backup)

Python .NET Status
PBB.fromFile() BinaryBackup.RestoreAsync() Ported
PBB.fromIpAddress() Constructor + RefreshCacheAsync() Ported
PBB.fromPixelblaze() Constructor Ported
PBB.toFile() BinaryBackup.SaveAsync() Ported
PBB.toIpAddress() - Not ported
PBB.toPixelblaze() - Not ported
PBB.getFileList() BinaryBackup.GetFileListAsync() Ported
PBB.getFile() BinaryBackup.GetFileAsync() Ported
PBB.putFile() BinaryBackup.PutFileAsync() Partial
PBB.deleteFile() BinaryBackup.DeleteFileAsync() Partial

PBP (Binary Pattern)

Python .NET Status
PBP.fromBytes() PixelblazeBinaryPattern.FromBytes() Ported
PBP.fromFile() PixelblazeBinaryPattern.FromFileAsync() Ported
PBP.fromIpAddress() - Not ported
PBP.fromPixelblaze() PixelblazeBinaryPattern.FromPixelblazeAsync() Ported
PBP.toFile() PixelblazeBinaryPattern.ToFileAsync() Ported
PBP.toIpAddress() - Not ported
PBP.toPixelblaze() PixelblazeBinaryPattern.ToPixelblazeAsync() Ported
PBP.toEPE() PixelblazeBinaryPattern.ToEpe() Ported
PBP.explode() PixelblazeBinaryPattern.ExplodeAsync() Ported
Properties: id, name, jpeg, byteCode, sourceCode Same Ported

EPE (Electromage Pattern Export)

Python .NET Status
EPE.fromBytes() ElectromagePatternExport.FromBytes() Ported
EPE.fromFile() ElectromagePatternExport.LoadAsync() Ported
EPE.toFile() ElectromagePatternExport.SaveAsync() Ported
EPE.explode() ElectromagePatternExport.ExplodeAsync() Ported
Properties: patternId, patternName, sourceCode, previewImage Id, Name, SourceCode, PreviewImage Ported

Not Ported

The following Python features are not yet available in .NET:

Methods

  • getPeers() - Peer discovery (blocked - Pixelblaze firmware doesn't implement yet)

Enums

  • sequencerModes - Use strings: "off", "shuffleAll", "playlist"
  • ledTypes - Use strings
  • colorOrders - Use strings
  • cpuSpeeds - Use strings: "80", "160", "240"
  • updateStates - Returns dynamic

Features

  • Time synchronization
  • Continuous enumeration (legacy PixelblazeEnumerator)
  • WebSocket event subscriptions

Code Migration Examples

Python

from pixelblaze import Pixelblaze

pb = Pixelblaze("192.168.1.100")
patterns = pb.getPatternList()
pb.setActivePatternByName("Rainbow Melt")

.NET

using PixelBlazeDotNetClient;
using Microsoft.Extensions.Logging;

var loggerFactory = LoggerFactory.Create(b => b.AddConsole());
var client = new PixelblazeClient("192.168.1.100", loggerFactory);
await client.ConnectAsync();

var patterns = await client.Patterns.GetPatternListAsync();
await client.Patterns.SetActivePatternByNameAsync("Rainbow Melt");

await client.DisposeAsync();

Python Discovery

from pixelblaze import Pixelblaze

for pb in Pixelblaze.EnumerateDevices():
    print(f"Found: {pb.getDeviceName()}")

.NET Discovery

var loggerFactory = LoggerFactory.Create(b => b.AddConsole());
var discovery = new PixelblazeDiscovery(loggerFactory.CreateLogger<PixelblazeDiscovery>());

var devices = await discovery.DiscoverDevicesAsync();
foreach (var device in devices)
{
    var info = await discovery.GetDeviceInfoAsync(device.IpAddress);
    Console.WriteLine($"Found: {info?.Name}");
}

Known Issues

Jan 2026 Update: Sprints 1, 2, 4, and 5 complete. All major protocol and feature issues resolved.

Fixed in Sprint 5 ✓

WebSocket Frame Handling - FIXED ✓

  • Preview frame header size corrected (19→1 bytes)
  • WebSocket frame accumulation fixed (accumulate until EndOfMessage)
  • Color picker controls now return proper Dictionary<string, double> objects

Fixed in Sprint 1 & 2 ✓

Settings (17 getters) - FIXED ✓

All getter methods now use {"getConfig": true} and extract values from the cached response (5 second TTL).

Patterns - FIXED ✓

Method Status
SetActivePatternAsync() ✓ Uses {"activeProgramId": id}
GetPatternSourceCodeAsync() ✓ Uses {"getSources": id} with LZString decompression
DeletePatternAsync() ✓ Uses {"deleteProgram": id}
GetPatternListAsync() ✓ Handles binary response (type 7)
GetActivePatternAsync() ✓ Extracts from config cache

Sequencer - FIXED ✓

All commands now use correct protocol names: sequencerMode, runSequencer, nextProgram, sequenceTimer, getPlaylist.

Statistics - FIXED ✓

Statistics class now reads from broadcast stats cache. Unavailable methods throw NotSupportedException.

Infrastructure - FIXED ✓

  • ✓ Config cache (5 second TTL)
  • ✓ Binary message support (types 1-9)
  • ✓ Frame chunking for large transfers
  • ✓ LZString compression integrated

Remaining Issues (Sprint 3 - PARKED)

Deferred: Nice-to-have. Plugin has its own reconnect logic. Risk outweighs benefit.

  1. No Auto-Reconnect - Connections don't automatically recover from network issues
  2. No Connection Maintenance - May timeout after ~10 minutes of idle
  3. Missing saveToFlash parameter - Settings changes may revert on reboot

What Works

  • WebSocket connection (ws://ip:81)
  • HTTP file operations (/list, /edit, /delete?path=)
  • UDP beacon discovery (port 1889)
  • Config caching (5 second TTL)
  • Binary message types and frame chunking
  • LZString compression for pattern source code
  • All settings getters (extract from config)
  • All sequencer commands
  • Statistics from broadcast

File Operations

Device file operations (GetDeviceFileListAsync, GetDeviceFileAsync, PutDeviceFileAsync, DeleteDeviceFileAsync) use HTTP API and work correctly. Local cache operations (PutFile, DeleteFile) only update in-memory cache.

Duplicate Methods

Some settings methods exist in both PixelblazeSettings and PixelblazeAdvanced:

  • Brand name
  • Timezone
  • Auto-off settings
  • Network power save
  • UI modes

Both now use correct protocol commands. The Advanced versions use int for auto-off times (minutes since midnight); Settings uses "HH:MM" strings.


Recommendations for Python Users

  1. Ready for Testing - Sprint 1 & 2 fixes applied, library should work with real hardware
  2. Use async/await - All .NET methods are async
  3. Inject ILoggerFactory - Required for client construction
  4. Use IAsyncDisposable - await using or explicit DisposeAsync()
  5. Check return types - GetPatternListAsync() returns Dictionary<string, string>, not a list
  6. Prefer subsystem access - Use client.Patterns.* instead of client.* for pattern operations
  7. Statistics limitation - Some Python stats methods not available (throw NotSupportedException)