From 56046711d0cd7204c6cbc41d8062ffbe91601c37 Mon Sep 17 00:00:00 2001 From: Marcus Davies <55892693+marcus-j-davies@users.noreply.github.com> Date: Sat, 29 Nov 2025 09:58:19 +0000 Subject: [PATCH 01/59] Migrate --- Debug Notes/Debug Notes.txt | 38 - Debug Notes/SomeDevice/SomeDevice.json | 37 - PSI/package.json | 14 +- PSI/server_source.js | 2 +- README.md | 3 +- .../.idea.ZWaveJS.NET/.idea/encodings.xml | 4 + .../.idea.ZWaveJS.NET/.idea/indexLayout.xml | 8 + .../.idea/projectSettingsUpdater.xml | 8 + .../.idea/.idea.ZWaveJS.NET/.idea/vcs.xml | 6 + .../.idea.ZWaveJS.NET/.idea/workspace.xml | 101 +++ .../ZWaveJS.NET/ZWaveJS.NET/Controller.cs | 102 ++- .../ZWaveJS.NET/ZWaveJS.NET/Driver.cs | 16 +- .../ZWaveJS.NET/ZWaveJS.NET/Enums.cs | 4 + .../ZWaveJS.NET/ZWaveJS.NET/Server.cs | 3 +- .../Exceptions/WebsocketBadInputException.cs | 25 - .../Exceptions/WebsocketException.cs | 26 - .../Websocket.Client/IWebsocketClient.cs | 182 ----- .../Models/DisconnectionInfo.cs | 72 -- .../Models/DisconnectionType.cs | 40 -- .../Models/ReconnectionInfo.cs | 30 - .../Models/ReconnectionType.cs | 39 -- .../Websocket.Client/ResponseMessage.cs | 62 -- .../Threading/WebsocketAsyncLock.cs | 74 -- .../Validations/Validations.cs | 109 --- .../WebsocketClient.Reconnecting.cs | 138 ---- .../WebsocketClient.Sending.cs | 239 ------- .../Websocket.Client/WebsocketClient.cs | 649 ------------------ .../ZWaveJS.NET/ZWaveJS.NET.csproj | 17 +- .../ZWaveJS.NET/ZWaveJS.NET/ZWaveNode.cs | 7 + 29 files changed, 250 insertions(+), 1805 deletions(-) delete mode 100644 Debug Notes/Debug Notes.txt delete mode 100644 Debug Notes/SomeDevice/SomeDevice.json create mode 100644 Visual Studio Projects/ZWaveJS.NET/.idea/.idea.ZWaveJS.NET/.idea/encodings.xml create mode 100644 Visual Studio Projects/ZWaveJS.NET/.idea/.idea.ZWaveJS.NET/.idea/indexLayout.xml create mode 100644 Visual Studio Projects/ZWaveJS.NET/.idea/.idea.ZWaveJS.NET/.idea/projectSettingsUpdater.xml create mode 100644 Visual Studio Projects/ZWaveJS.NET/.idea/.idea.ZWaveJS.NET/.idea/vcs.xml create mode 100644 Visual Studio Projects/ZWaveJS.NET/.idea/.idea.ZWaveJS.NET/.idea/workspace.xml delete mode 100644 Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Websocket.Client/Exceptions/WebsocketBadInputException.cs delete mode 100644 Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Websocket.Client/Exceptions/WebsocketException.cs delete mode 100644 Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Websocket.Client/IWebsocketClient.cs delete mode 100644 Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Websocket.Client/Models/DisconnectionInfo.cs delete mode 100644 Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Websocket.Client/Models/DisconnectionType.cs delete mode 100644 Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Websocket.Client/Models/ReconnectionInfo.cs delete mode 100644 Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Websocket.Client/Models/ReconnectionType.cs delete mode 100644 Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Websocket.Client/ResponseMessage.cs delete mode 100644 Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Websocket.Client/Threading/WebsocketAsyncLock.cs delete mode 100644 Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Websocket.Client/Validations/Validations.cs delete mode 100644 Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Websocket.Client/WebsocketClient.Reconnecting.cs delete mode 100644 Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Websocket.Client/WebsocketClient.Sending.cs delete mode 100644 Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Websocket.Client/WebsocketClient.cs diff --git a/Debug Notes/Debug Notes.txt b/Debug Notes/Debug Notes.txt deleted file mode 100644 index eaf0a89..0000000 --- a/Debug Notes/Debug Notes.txt +++ /dev/null @@ -1,38 +0,0 @@ -.env file (FWUS) -------------------------------------- -API_REQUIRE_KEY=false -ADMIN_SECRET=1234567890 - - -Wrangler -------------------------------------- -cache_persist = false - - -Build / Run -------------------------------------- -yarn -yarn build -yarn dev - - -Upload Firmware Test -------------------------------------- -set ADMIN_SECRET=1234567890 -set BASE_URL=http://localhost:8787 -yarn build:index -yarn upload - - -S2 Keys (DO NOT USE IN PRODUCTION) -------------------------------------- -0d0003d7bda539f90b5553297d043fdd -c7a2a891f6c786a679453608eb04ade5 -b959a89faedab01ce55fdf23d36e6361 -9142ddbbce0dc0cef42939acaac24f6e - - - - - - diff --git a/Debug Notes/SomeDevice/SomeDevice.json b/Debug Notes/SomeDevice/SomeDevice.json deleted file mode 100644 index 9e107d1..0000000 --- a/Debug Notes/SomeDevice/SomeDevice.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "devices": [ - { - "brand": "Some MFG", - "model": "Some Device", - "manufacturerId": "0x0346", - "productType": "0x0101", - "productId": "0x0202" - } - ], - "upgrades": [ - { - "version": "9.0", - "channel": "stable", - "changelog": "Bug Fixes:\n* Bug Fixes\n* More bug fixes", - "files": [ - { - "target": 0, - "url": "https://z-wave.freshdesk.com/helpdesk/attachments/80257130806", - "integrity": "sha256:f653271acaf3ed44563dd3bfb6d98cf8bdfe34d5d68d350cf099e95af1e9724b" - } - ] - }, - { - "version": "9.5", - "channel": "stable", - "changelog": "Bug Fixes:\n* More More Bug Fixes\n* More More More bug fixes", - "files": [ - { - "target": 0, - "url": "https://z-wave.freshdesk.com/helpdesk/attachments/80257130806", - "integrity": "sha256:f653271acaf3ed44563dd3bfb6d98cf8bdfe34d5d68d350cf099e95af1e9724b" - } - ] - } - ] -} diff --git a/PSI/package.json b/PSI/package.json index 2c91dba..c0f663e 100644 --- a/PSI/package.json +++ b/PSI/package.json @@ -1,16 +1,16 @@ { - "version": "4.0.0", + "version": "5.0.0", "name": "server", "bin": "./server.js", "dependencies": { - "@zwave-js/server": "^3.1.0", - "zwave-js": "^15.3.0" + "@zwave-js/server": "^3.5.0", + "zwave-js": "^15.17.1" }, "devDependencies": { - "@yao-pkg/pkg": "^6.5.1", - "esbuild": "^0.25.6", - "eslint": "^9.31.0", - "prettier": "^3.6.2" + "@yao-pkg/pkg": "^6.10.1", + "esbuild": "^0.27.0", + "eslint": "^9.39.1", + "prettier": "^3.7.2" }, "scripts": { "build": "npm run do_esbuild && npm run do_pkgbuild", diff --git a/PSI/server_source.js b/PSI/server_source.js index 60a3454..c02ec93 100755 --- a/PSI/server_source.js +++ b/PSI/server_source.js @@ -24,7 +24,7 @@ if (driverOptions.securityKeysLongRange) { } const driver = new Driver(serialPort, driverOptions); -const server = new ZwavejsServer(driver, { port: wsPort, host: 'localhost' }); +const server = new ZwavejsServer(driver, { port: wsPort, host: 'localhost', reconnect: false, 'disable-dns-sd' : true }); server.on('listening', () => { ServerStarted = true; }); diff --git a/README.md b/README.md index fcc430d..5baec2d 100644 --- a/README.md +++ b/README.md @@ -12,11 +12,10 @@ ZWaveJS.NET is a class library developed for the .NET framework family, that opens up the zwave-js Driver in .NET, allowing its full runtime to be used directly in .NET applications. ## Supported Targets - - NET 4.8 - NET 6.0 - NET 7.0 - NET 8.0 - - NET Standard 2.0 + - NET 9.0 - NET Standard 2.1 The library strictly follows the structure of the zwave-js API. diff --git a/Visual Studio Projects/ZWaveJS.NET/.idea/.idea.ZWaveJS.NET/.idea/encodings.xml b/Visual Studio Projects/ZWaveJS.NET/.idea/.idea.ZWaveJS.NET/.idea/encodings.xml new file mode 100644 index 0000000..df87cf9 --- /dev/null +++ b/Visual Studio Projects/ZWaveJS.NET/.idea/.idea.ZWaveJS.NET/.idea/encodings.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/Visual Studio Projects/ZWaveJS.NET/.idea/.idea.ZWaveJS.NET/.idea/indexLayout.xml b/Visual Studio Projects/ZWaveJS.NET/.idea/.idea.ZWaveJS.NET/.idea/indexLayout.xml new file mode 100644 index 0000000..7b08163 --- /dev/null +++ b/Visual Studio Projects/ZWaveJS.NET/.idea/.idea.ZWaveJS.NET/.idea/indexLayout.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/Visual Studio Projects/ZWaveJS.NET/.idea/.idea.ZWaveJS.NET/.idea/projectSettingsUpdater.xml b/Visual Studio Projects/ZWaveJS.NET/.idea/.idea.ZWaveJS.NET/.idea/projectSettingsUpdater.xml new file mode 100644 index 0000000..ef20cb0 --- /dev/null +++ b/Visual Studio Projects/ZWaveJS.NET/.idea/.idea.ZWaveJS.NET/.idea/projectSettingsUpdater.xml @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file diff --git a/Visual Studio Projects/ZWaveJS.NET/.idea/.idea.ZWaveJS.NET/.idea/vcs.xml b/Visual Studio Projects/ZWaveJS.NET/.idea/.idea.ZWaveJS.NET/.idea/vcs.xml new file mode 100644 index 0000000..b2bdec2 --- /dev/null +++ b/Visual Studio Projects/ZWaveJS.NET/.idea/.idea.ZWaveJS.NET/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Visual Studio Projects/ZWaveJS.NET/.idea/.idea.ZWaveJS.NET/.idea/workspace.xml b/Visual Studio Projects/ZWaveJS.NET/.idea/.idea.ZWaveJS.NET/.idea/workspace.xml new file mode 100644 index 0000000..17b9752 --- /dev/null +++ b/Visual Studio Projects/ZWaveJS.NET/.idea/.idea.ZWaveJS.NET/.idea/workspace.xml @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1760176866190 + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Controller.cs b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Controller.cs index 141c5df..2254342 100644 --- a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Controller.cs +++ b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Controller.cs @@ -144,12 +144,7 @@ internal void Trigger_FirmwareUpdateProgress(ControllerFirmwareUpdateProgressArg // CHECKED public Task GetAvailableFirmwareUpdates(int NodeID, bool IncludePrereleases, UsageEnvironment Environment, string APIKey = null) { - - - - Guid ID = Guid.NewGuid(); - TaskCompletionSource Result = new TaskCompletionSource(); if (Environment == UsageEnvironment.Commercial && string.IsNullOrEmpty(APIKey)) @@ -189,7 +184,6 @@ public Task GetAvailableFirmwareUpdates(int NodeID, bool IncludePrere public Task FirmwareUpdateOTA(int NodeID, FirmwareUpdateInfo Update) { Guid ID = Guid.NewGuid(); - TaskCompletionSource Result = new TaskCompletionSource(); _driver.Callbacks.Add(ID, (JO) => @@ -200,10 +194,7 @@ public Task FirmwareUpdateOTA(int NodeID, FirmwareUpdateInfo Update) NodeFirmwareUpdateResultArgs FUR = JO.SelectToken("result").ToObject(); Res.SetPayload(FUR); } - - Result.SetResult(Res); - }); Dictionary Request = new Dictionary(); @@ -222,13 +213,11 @@ public Task FirmwareUpdateOTA(int NodeID, FirmwareUpdateInfo Update) public Task GetRFRegion() { Guid ID = Guid.NewGuid(); - TaskCompletionSource Result = new TaskCompletionSource(); _driver.Callbacks.Add(ID, (JO) => { CMDResult Res = new CMDResult(JO); - if (Res.Success) { Enums.RFRegion Region = JO.SelectToken("result.region").ToObject(); @@ -252,7 +241,6 @@ public Task GetRFRegion() public Task SetRFRegion(Enums.RFRegion Region) { Guid ID = Guid.NewGuid(); - TaskCompletionSource Result = new TaskCompletionSource(); _driver.Callbacks.Add(ID, (JO) => @@ -272,12 +260,67 @@ public Task SetRFRegion(Enums.RFRegion Region) return Result.Task; } + + // CHECKED + public Task SetMaxLongRangePowerlevel(decimal Limit) + { + Guid ID = Guid.NewGuid(); + TaskCompletionSource Result = new TaskCompletionSource(); + + _driver.Callbacks.Add(ID, (JO) => + { + + CMDResult Res = new CMDResult(JO); + Result.SetResult(Res); + + }); + + Dictionary Request = new Dictionary(); + Request.Add("messageId", ID); + Request.Add("limit", Limit); + Request.Add("command", Enums.Commands.SetLRMaxPower); + + + string RequestPL = Newtonsoft.Json.JsonConvert.SerializeObject(Request); + _driver.ClientWebSocket.SendInstant(RequestPL); + + return Result.Task; + } // CHECKED - public Task GetPowerLevel() + public Task GetMaxLongRangePowerlevel() { Guid ID = Guid.NewGuid(); + TaskCompletionSource Result = new TaskCompletionSource(); + + _driver.Callbacks.Add(ID, (JO) => + { + CMDResult Res = new CMDResult(JO); + + if (Res.Success) + { + decimal Level = JO.SelectToken("result.limit").ToObject(); + Res.SetPayload(Level); + } + Result.SetResult(Res); + }); + + Dictionary Request = new Dictionary(); + Request.Add("messageId", ID); + Request.Add("command", Enums.Commands.GetLRMaxPower); + + + string RequestPL = Newtonsoft.Json.JsonConvert.SerializeObject(Request); + _driver.ClientWebSocket.SendInstant(RequestPL); + + return Result.Task; + } + + // CHECKED + public Task GetPowerLevel() + { + Guid ID = Guid.NewGuid(); TaskCompletionSource Result = new TaskCompletionSource(); _driver.Callbacks.Add(ID, (JO) => @@ -308,7 +351,6 @@ public Task GetPowerLevel() public Task SetPowerLevel(decimal PowerLevel, decimal Measured0dBm) { Guid ID = Guid.NewGuid(); - TaskCompletionSource Result = new TaskCompletionSource(); _driver.Callbacks.Add(ID, (JO) => @@ -340,7 +382,6 @@ public VirtualNode GetMulticastGroup(int[] Nodes) // CHECKED public Task FirmwareUpdateOTW(FirmwareUpdate Update) { - if (Update.firmwareTarget != null) { TaskCompletionSource Fail = new TaskCompletionSource(); @@ -351,7 +392,6 @@ public Task FirmwareUpdateOTW(FirmwareUpdate Update) } Guid ID = Guid.NewGuid(); - TaskCompletionSource Result = new TaskCompletionSource(); _driver.Callbacks.Add(ID, (JO) => @@ -408,6 +448,29 @@ public Task GetProvisioningEntries() return Result.Task; } + // Checked + public Task ToggleRF(bool Enabled) + { + Guid ID = Guid.NewGuid(); + TaskCompletionSource Result = new TaskCompletionSource(); + _driver.Callbacks.Add(ID, (JO) => + { + CMDResult Res = new CMDResult(JO); + Result.SetResult(Res); + + }); + + Dictionary Request = new Dictionary(); + Request.Add("messageId", ID); + Request.Add("command", Enums.Commands.ToggleRF); + Request.Add("enabled", Enabled); + + string RequestPL = Newtonsoft.Json.JsonConvert.SerializeObject(Request); + _driver.ClientWebSocket.SendInstant(RequestPL); + + return Result.Task; + } + // CHECKED public Task RemoveAssociations(AssociationAddress Source, int Group, AssociationAddress[] Targets) { @@ -754,7 +817,6 @@ public Task BeginRebuildingRoutes(RebuildRoutesOptions Options) // CHECKED public Task StopRebuildingRoutes() { - Guid ID = Guid.NewGuid(); TaskCompletionSource Result = new TaskCompletionSource(); @@ -845,9 +907,7 @@ public Task BeginInclusion(InclusionOptions Options) return Result.Task; } } - - - + if (_driver.Options != null && !_driver.Options.CheckKeyLength()) { CMDResult Res = new CMDResult(Enums.ErrorCodes.InvalidkeyLength, "Invalid Key length. All Security Keys must be a 32 character hexadecimal string (representing 16 bytes)", false); @@ -994,7 +1054,6 @@ public Task ProvisionSmartStartNode(SmartStartProvisioningEntry Provi public Task BeginExclusion(ExclusionOptions Options) { Guid ID = Guid.NewGuid(); - TaskCompletionSource Result = new TaskCompletionSource(); _driver.Callbacks.Add(ID, (JO) => @@ -1019,7 +1078,6 @@ public Task BeginExclusion(ExclusionOptions Options) public Task StopExclusion() { Guid ID = Guid.NewGuid(); - TaskCompletionSource Result = new TaskCompletionSource(); _driver.Callbacks.Add(ID, (JO) => diff --git a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Driver.cs b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Driver.cs index ca1fa40..e8c3289 100644 --- a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Driver.cs +++ b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Driver.cs @@ -14,7 +14,7 @@ public class Driver // Global List of Socket Ports that are registered internal static List UsedPorts = new List(); - internal Websocket.Client.WebsocketClient ClientWebSocket; + internal WebsocketClient ClientWebSocket; internal Dictionary> Callbacks; internal bool Inited = false; internal ZWaveOptions Options; @@ -23,7 +23,7 @@ public class Driver private Dictionary> NodeEventMap; private Dictionary> ControllerEventMap; private Dictionary> DriverEventMap; - private int _schemaVersion = 43; + private int _schemaVersion = 44; private string SerialPort; private bool RequestedExit = false; private JsonSerializer _jsonSerializer; @@ -81,6 +81,18 @@ internal void Trigger_LoggingEvent(LoggingEventArgs args) private void MapNodeEvents() { + + NodeEventMap.Add("node info received", (JO) => + { + int NID = JO.SelectToken("event.nodeId").ToObject(); + ZWaveNode N = this.Controller.Nodes.Get(NID); + + Task.Run(() => + { + N.Trigger_NodeInfo(); + }); + }); + NodeEventMap.Add("check lifeline health progress", (JO) => { int NID = JO.SelectToken("event.nodeId").ToObject(); diff --git a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Enums.cs b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Enums.cs index a49c145..ec217df 100644 --- a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Enums.cs +++ b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Enums.cs @@ -138,6 +138,7 @@ internal class Commands public const string RefreshCCValues = "node.refresh_cc_values"; public const string GetDefinedValueIDs = "node.get_defined_value_ids"; public const string GetValueMetadata = "node.get_value_metadata"; + public const string ToggleRF = "controller.toggle_rf"; public const string BeginInclusion = "controller.begin_inclusion"; public const string StopInclusion = "controller.stop_inclusion"; public const string BeginExclusion = "controller.begin_exclusion"; @@ -188,6 +189,9 @@ internal class Commands public const string LookupDevice = "config_manager.lookup_device"; public const string LoadManufacturers = "config_manager.load_manufacturers"; public const string LoadDeviceIndex = "config_manager.load_device_index"; + public const string GetLRMaxPower = "controller.get_max_long_range_powerlevel"; + public const string SetLRMaxPower = "controller.set_max_long_range_powerlevel"; + } diff --git a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Server.cs b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Server.cs index eb648b4..a23c5a4 100644 --- a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Server.cs +++ b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Server.cs @@ -30,8 +30,7 @@ internal void Terminate() internal void Start(string SerialPort, ZWaveOptions Config, int WSPort) { - - + string ProcessName = string.Format("server.{0}.psi", WSPort); Process[] Zombies = Process.GetProcessesByName(ProcessName); diff --git a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Websocket.Client/Exceptions/WebsocketBadInputException.cs b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Websocket.Client/Exceptions/WebsocketBadInputException.cs deleted file mode 100644 index 1e8fc12..0000000 --- a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Websocket.Client/Exceptions/WebsocketBadInputException.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; - -namespace Websocket.Client.Exceptions -{ - /// - /// Custom exception that indicates bad user/client input - /// - public class WebsocketBadInputException : WebsocketException - { - /// - public WebsocketBadInputException() - { - } - - /// - public WebsocketBadInputException(string message) : base(message) - { - } - - /// - public WebsocketBadInputException(string message, Exception innerException) : base(message, innerException) - { - } - } -} diff --git a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Websocket.Client/Exceptions/WebsocketException.cs b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Websocket.Client/Exceptions/WebsocketException.cs deleted file mode 100644 index 327a27b..0000000 --- a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Websocket.Client/Exceptions/WebsocketException.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; - -namespace Websocket.Client.Exceptions -{ - /// - /// Custom exception related to WebsocketClient - /// - public class WebsocketException : Exception - { - /// - public WebsocketException() - { - } - - /// - public WebsocketException(string message) - : base(message) - { - } - - /// - public WebsocketException(string message, Exception innerException) : base(message, innerException) - { - } - } -} diff --git a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Websocket.Client/IWebsocketClient.cs b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Websocket.Client/IWebsocketClient.cs deleted file mode 100644 index b1c9da9..0000000 --- a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Websocket.Client/IWebsocketClient.cs +++ /dev/null @@ -1,182 +0,0 @@ -#nullable enable -using System; -using System.Net.WebSockets; -using System.Text; -using System.Threading.Tasks; - -namespace Websocket.Client -{ - /// - /// A simple websocket client with built-in reconnection and error handling - /// - public interface IWebsocketClient : IDisposable - { - /// - /// Get or set target websocket url - /// - Uri Url { get; set; } - - /// - /// Stream with received message (raw format) - /// - IObservable MessageReceived { get; } - - /// - /// Stream for reconnection event (triggered after the new connection) - /// - IObservable ReconnectionHappened { get; } - - /// - /// Stream for disconnection event (triggered after the connection was lost) - /// - IObservable DisconnectionHappened { get; } - - /// - /// Time range for how long to wait before reconnecting if no message comes from server. - /// Set null to disable this feature. - /// Default: 1 minute. - /// - TimeSpan? ReconnectTimeout { get; set; } - - /// - /// Time range for how long to wait before reconnecting if last reconnection failed. - /// Set null to disable this feature. - /// Default: 1 minute. - /// - TimeSpan? ErrorReconnectTimeout { get; set; } - - /// - /// Time range for how long to wait before reconnecting if connection is lost with a transient error. - /// Set null to disable this feature. - /// Default: null/disabled (immediately) - /// - TimeSpan? LostReconnectTimeout { get; set; } - - /// - /// Get or set the name of the current websocket client instance. - /// For logging purpose (in case you use more parallel websocket clients and want to distinguish between them) - /// - string? Name { get; set; } - - /// - /// Returns true if Start() method was called at least once. False if not started or disposed - /// - bool IsStarted { get; } - - /// - /// Returns true if client is running and connected to the server - /// - bool IsRunning { get; } - - /// - /// Enable or disable reconnection functionality (enabled by default) - /// - bool IsReconnectionEnabled { get; set; } - - /// - /// Enable or disable text message conversion from binary to string (via 'MessageEncoding' property). - /// Default: true - /// - bool IsTextMessageConversionEnabled { get; set; } - - /// - /// Returns currently used native websocket client. - /// Use with caution, on every reconnection there will be a new instance. - /// - ClientWebSocket? NativeClient { get; } - - /// - /// Sets used encoding for sending and receiving text messages. - /// Default: UTF8 - /// - Encoding? MessageEncoding { get; set; } - - /// - /// Start listening to the websocket stream on the background thread. - /// In case of connection error it doesn't throw an exception. - /// Only streams a message via 'DisconnectionHappened' and logs it. - /// - Task Start(); - - /// - /// Start listening to the websocket stream on the background thread. - /// In case of connection error it throws an exception. - /// Fail fast approach. - /// - Task StartOrFail(); - - /// - /// Stop/close websocket connection with custom close code. - /// Method doesn't throw exception, only logs it and mark client as closed. - /// - /// Returns true if close was initiated successfully - Task Stop(WebSocketCloseStatus status, string statusDescription); - - /// - /// Stop/close websocket connection with custom close code. - /// Method could throw exceptions, but client is marked as closed anyway. - /// - /// Returns true if close was initiated successfully - Task StopOrFail(WebSocketCloseStatus status, string statusDescription); - - /// - /// Send message to the websocket channel. - /// It inserts the message to the queue and actual sending is done on an other thread - /// - /// Message to be sent - void Send(string message); - - /// - /// Send binary message to the websocket channel. - /// It inserts the message to the queue and actual sending is done on an other thread - /// - /// Binary message to be sent - void Send(byte[] message); - - /// - /// Send binary message to the websocket channel. - /// It inserts the message to the queue and actual sending is done on an other thread - /// - /// Binary message to be sent - void Send(ArraySegment message); - - /// - /// Send message to the websocket channel. - /// It doesn't use a sending queue, - /// beware of issue while sending two messages in the exact same time - /// on the full .NET Framework platform - /// - /// Message to be sent - Task SendInstant(string message); - - /// - /// Send binary message to the websocket channel. - /// It doesn't use a sending queue, - /// beware of issue while sending two messages in the exact same time - /// on the full .NET Framework platform - /// - /// Message to be sent - Task SendInstant(byte[] message); - - /// - /// Force reconnection. - /// Closes current websocket stream and perform a new connection to the server. - /// In case of connection error it doesn't throw an exception, but tries to reconnect indefinitely. - /// - Task Reconnect(); - - /// - /// Force reconnection. - /// Closes current websocket stream and perform a new connection to the server. - /// In case of connection error it throws an exception and doesn't perform any other reconnection try. - /// - Task ReconnectOrFail(); - - /// - /// Stream/publish fake message (via 'MessageReceived' observable). - /// Use for testing purposes to simulate a server message. - /// - /// Message to be stream - void StreamFakeMessage(ResponseMessage message); - } -} \ No newline at end of file diff --git a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Websocket.Client/Models/DisconnectionInfo.cs b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Websocket.Client/Models/DisconnectionInfo.cs deleted file mode 100644 index 3f6e8b4..0000000 --- a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Websocket.Client/Models/DisconnectionInfo.cs +++ /dev/null @@ -1,72 +0,0 @@ -#nullable enable -using System; -using System.Net.WebSockets; - -// ReSharper disable once CheckNamespace -namespace Websocket.Client -{ - /// - /// Info about happened disconnection - /// - public class DisconnectionInfo - { - /// - /// Info about happened disconnection - /// - public DisconnectionInfo(DisconnectionType type, WebSocketCloseStatus? closeStatus, - string? closeStatusDescription, string? subProtocol, Exception? exception) - { - Type = type; - CloseStatus = closeStatus; - CloseStatusDescription = closeStatusDescription; - SubProtocol = subProtocol; - Exception = exception; - } - - /// - /// Disconnection reason - /// - public DisconnectionType Type { get; } - - /// - /// Indicates the reason why the remote endpoint initiated the close handshake - /// - public WebSocketCloseStatus? CloseStatus { get; } - - /// - /// Allows the remote endpoint to describe the reason why the connection was closed - /// - public string? CloseStatusDescription { get; } - - /// - /// The subprotocol that was negotiated during the opening handshake - /// - public string? SubProtocol { get; } - - /// - /// Exception that cause disconnection, can be null - /// - public Exception? Exception { get; } - - - /// - /// Set to true if you want to cancel ongoing reconnection - /// - public bool CancelReconnection { get; set; } - - /// - /// Set to true if you want to cancel ongoing connection close (only when Type = ByServer) - /// - public bool CancelClosing { get; set; } - - - /// - /// Simple factory method - /// - public static DisconnectionInfo Create(DisconnectionType type, WebSocket? client, Exception? exception) - { - return new DisconnectionInfo(type, client?.CloseStatus, client?.CloseStatusDescription, - client?.SubProtocol, exception); - } - } -} diff --git a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Websocket.Client/Models/DisconnectionType.cs b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Websocket.Client/Models/DisconnectionType.cs deleted file mode 100644 index c6bfb2f..0000000 --- a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Websocket.Client/Models/DisconnectionType.cs +++ /dev/null @@ -1,40 +0,0 @@ -// ReSharper disable once CheckNamespace -namespace Websocket.Client -{ - /// - /// Type that specify happened disconnection - /// - public enum DisconnectionType - { - /// - /// Type used for exit event, disposing of the websocket client - /// - Exit = 0, - - /// - /// Type used when connection to websocket was lost in meantime - /// - Lost = 1, - - /// - /// Type used when connection to websocket was lost by not receiving any message in given time-range - /// - NoMessageReceived = 2, - - /// - /// Type used when connection or reconnection returned error - /// - Error = 3, - - /// - /// Type used when disconnection was requested by user - /// - ByUser = 4, - - - /// - /// Type used when disconnection was requested by server - /// - ByServer = 5 - } -} diff --git a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Websocket.Client/Models/ReconnectionInfo.cs b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Websocket.Client/Models/ReconnectionInfo.cs deleted file mode 100644 index c3a76ec..0000000 --- a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Websocket.Client/Models/ReconnectionInfo.cs +++ /dev/null @@ -1,30 +0,0 @@ -// ReSharper disable once CheckNamespace -namespace Websocket.Client -{ - /// - /// Info about happened reconnection - /// - public class ReconnectionInfo - { - /// - /// Info about happened reconnection - /// - public ReconnectionInfo(ReconnectionType type) - { - Type = type; - } - - /// - /// Reconnection reason - /// - public ReconnectionType Type { get; } - - /// - /// Simple factory method - /// - public static ReconnectionInfo Create(ReconnectionType type) - { - return new ReconnectionInfo(type); - } - } -} diff --git a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Websocket.Client/Models/ReconnectionType.cs b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Websocket.Client/Models/ReconnectionType.cs deleted file mode 100644 index 356743c..0000000 --- a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Websocket.Client/Models/ReconnectionType.cs +++ /dev/null @@ -1,39 +0,0 @@ -// ReSharper disable once CheckNamespace -namespace Websocket.Client -{ - /// - /// Type that specify happened reconnection - /// - public enum ReconnectionType - { - /// - /// Type used for initial connection to websocket stream - /// - Initial = 0, - - /// - /// Type used when connection to websocket was lost in meantime - /// - Lost = 1, - - /// - /// Type used when connection to websocket was lost by not receiving any message in given time-range - /// - NoMessageReceived = 2, - - /// - /// Type used after unsuccessful previous reconnection - /// - Error = 3, - - /// - /// Type used when reconnection was requested by user - /// - ByUser = 4, - - /// - /// Type used when reconnection was requested by server - /// - ByServer = 5 - } -} diff --git a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Websocket.Client/ResponseMessage.cs b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Websocket.Client/ResponseMessage.cs deleted file mode 100644 index ad7e9b1..0000000 --- a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Websocket.Client/ResponseMessage.cs +++ /dev/null @@ -1,62 +0,0 @@ -#nullable enable -using System.Net.WebSockets; - -namespace Websocket.Client -{ - /// - /// Received message, could be Text or Binary - /// - public class ResponseMessage - { - private ResponseMessage(byte[]? binary, string? text, WebSocketMessageType messageType) - { - Binary = binary; - Text = text; - MessageType = messageType; - } - - /// - /// Received text message (only if type = WebSocketMessageType.Text) - /// - public string? Text { get; } - - /// - /// Received text message (only if type = WebSocketMessageType.Binary) - /// - public byte[]? Binary { get; } - - /// - /// Current message type (Text or Binary) - /// - public WebSocketMessageType MessageType { get; } - - /// - /// Return string info about the message - /// - public override string ToString() - { - if (MessageType == WebSocketMessageType.Text) - { - return Text ?? string.Empty; - } - - return $"Type binary, length: {Binary?.Length}"; - } - - /// - /// Create text response message - /// - public static ResponseMessage TextMessage(string? data) - { - return new ResponseMessage(null, data, WebSocketMessageType.Text); - } - - /// - /// Create binary response message - /// - public static ResponseMessage BinaryMessage(byte[]? data) - { - return new ResponseMessage(data, null, WebSocketMessageType.Binary); - } - } -} \ No newline at end of file diff --git a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Websocket.Client/Threading/WebsocketAsyncLock.cs b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Websocket.Client/Threading/WebsocketAsyncLock.cs deleted file mode 100644 index e443909..0000000 --- a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Websocket.Client/Threading/WebsocketAsyncLock.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; - -namespace Websocket.Client.Threading -{ - /// - /// Class that wraps SemaphoreSlim and enables to use locking inside 'using' blocks easily - /// Don't need to bother with releasing and handling SemaphoreSlim correctly - /// Example: - /// - /// using(await _asyncLock.LockAsync()) - /// { - /// // do your synchronized work - /// } - /// - /// - public class WebsocketAsyncLock - { - private readonly Task _releaserTask; - private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1); - private readonly IDisposable _releaser; - - /// - /// Class that wraps SemaphoreSlim and enables to use locking inside 'using' blocks easily - /// Don't need to bother with releasing and handling SemaphoreSlim correctly - /// - public WebsocketAsyncLock() - { - _releaser = new Releaser(_semaphore); - _releaserTask = Task.FromResult(_releaser); - } - - /// - /// Use inside 'using' block - /// - public IDisposable Lock() - { - _semaphore.Wait(); - return _releaser; - } - - /// - /// Use inside 'using' block with await - /// - public Task LockAsync() - { - var waitTask = _semaphore.WaitAsync(); - return waitTask.IsCompleted - ? _releaserTask - : waitTask.ContinueWith( - (_, releaser) => (IDisposable)releaser!, - _releaser, - CancellationToken.None, - TaskContinuationOptions.ExecuteSynchronously, - TaskScheduler.Default); - } - - private class Releaser : IDisposable - { - private readonly SemaphoreSlim _semaphore; - - public Releaser(SemaphoreSlim semaphore) - { - _semaphore = semaphore; - } - - public void Dispose() - { - _semaphore.Release(); - } - } - } -} diff --git a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Websocket.Client/Validations/Validations.cs b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Websocket.Client/Validations/Validations.cs deleted file mode 100644 index d2ab633..0000000 --- a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Websocket.Client/Validations/Validations.cs +++ /dev/null @@ -1,109 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using Websocket.Client.Exceptions; - -namespace Websocket.Client.Validations -{ - internal static class Validations - { - /// - /// It throws if value is null or empty/white spaces - /// - /// The value to be validated - /// Input parameter name - public static void ValidateInput(string value, string name) - { - if (string.IsNullOrEmpty(value)) - { - throw new WebsocketBadInputException($"Input string parameter '{name}' is null or empty. Please correct it."); - } - } - - /// - /// It throws if value is null - /// - /// The value to be validated - /// Input parameter name - public static void ValidateInput(T value, string name) - { - if (Equals(value, default(T))) - { - throw new WebsocketBadInputException($"Input parameter '{name}' is null. Please correct it."); - } - } - - /// - /// It throws if collection is null or collection is empty - /// - /// The collection to be validated - /// Input parameter name - public static void ValidateInputCollection(IEnumerable collection, string name) - { - // ReSharper disable once PossibleMultipleEnumeration - ValidateInput(collection, name); - - // ReSharper disable once PossibleMultipleEnumeration - if (!collection.Any()) - { - throw new WebsocketBadInputException($"Input collection '{name}' is empty. Please correct it."); - } - } - - /// - /// It throws if value is not in specified range - /// - /// The value to be validated - /// Input parameter name - /// Minimal value of input - /// Maximum value of input - public static void ValidateInput(int value, string name, int minValue = int.MinValue, int maxValue = int.MaxValue) - { - if (value < minValue) - { - throw new WebsocketBadInputException($"Input parameter '{name}' is lower than {minValue}. Please correct it."); - } - if (value > maxValue) - { - throw new WebsocketBadInputException($"Input parameter '{name}' is higher than {maxValue}. Please correct it."); - } - } - - /// - /// It throws if value is not in specified range - /// - /// The value to be validated - /// Input parameter name - /// Minimal value of input - /// Maximum value of input - public static void ValidateInput(long value, string name, long minValue = long.MinValue, long maxValue = long.MaxValue) - { - if (value < minValue) - { - throw new WebsocketBadInputException($"Input parameter '{name}' is lower than {minValue}. Please correct it."); - } - if (value > maxValue) - { - throw new WebsocketBadInputException($"Input parameter '{name}' is higher than {maxValue}. Please correct it."); - } - } - - /// - /// It throws if value is not in specified range - /// - /// The value to be validated - /// Input parameter name - /// Minimal value of input - /// Maximum value of input - public static void ValidateInput(double value, string name, double minValue = double.MinValue, double maxValue = double.MaxValue) - { - if (value < minValue) - { - throw new WebsocketBadInputException($"Input parameter '{name}' is lower than {minValue}. Please correct it."); - } - if (value > maxValue) - { - throw new WebsocketBadInputException($"Input parameter '{name}' is higher than {maxValue}. Please correct it."); - } - } - } -} diff --git a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Websocket.Client/WebsocketClient.Reconnecting.cs b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Websocket.Client/WebsocketClient.Reconnecting.cs deleted file mode 100644 index eabafc4..0000000 --- a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Websocket.Client/WebsocketClient.Reconnecting.cs +++ /dev/null @@ -1,138 +0,0 @@ -#nullable enable -using System; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.Logging; - -namespace Websocket.Client -{ - public partial class WebsocketClient - { - - /// - /// Force reconnection. - /// Closes current websocket stream and perform a new connection to the server. - /// In case of connection error it doesn't throw an exception, but tries to reconnect indefinitely. - /// - public Task Reconnect() - { - return ReconnectInternal(false); - } - - /// - /// Force reconnection. - /// Closes current websocket stream and perform a new connection to the server. - /// In case of connection error it throws an exception and doesn't perform any other reconnection try. - /// - public Task ReconnectOrFail() - { - return ReconnectInternal(true); - } - - private async Task ReconnectInternal(bool failFast) - { - if (!IsStarted) - { - _logger.LogDebug(L("Client not started, ignoring reconnection.."), Name); - return; - } - - try - { - await ReconnectSynchronized(ReconnectionType.ByUser, failFast, null).ConfigureAwait(false); - } - finally - { - _reconnecting = false; - } - } - - private async Task ReconnectSynchronized(ReconnectionType type, bool failFast, Exception? causedException) - { - using (await _locker.LockAsync()) - { - await Reconnect(type, failFast, causedException); - } - } - - private async Task Reconnect(ReconnectionType type, bool failFast, Exception? causedException) - { - IsRunning = false; - if (_disposing || !IsStarted) - { - // client already disposed or stopped manually - return; - } - - _reconnecting = true; - - var disType = TranslateTypeToDisconnection(type); - var disInfo = DisconnectionInfo.Create(disType, _client, causedException); - if (type != ReconnectionType.Error) - { - _disconnectedSubject.OnNext(disInfo); - if (disInfo.CancelReconnection) - { - // reconnection canceled by user, do nothing - _logger.LogInformation(L("Reconnecting canceled by user, exiting."), Name); - } - } - - _cancellation?.Cancel(); - try - { - _client?.Abort(); - } - catch (Exception e) - { - _logger.LogError(e, L("Exception while aborting client. Error: '{error}'"), Name, e.Message); - } - _client?.Dispose(); - - if (!IsReconnectionEnabled || disInfo.CancelReconnection) - { - // reconnection disabled, do nothing - IsStarted = false; - _reconnecting = false; - return; - } - - _logger.LogDebug(L("Reconnecting..."), Name); - _cancellation = new CancellationTokenSource(); - await StartClient(_url, _cancellation.Token, type, failFast).ConfigureAwait(false); - _reconnecting = false; - } - - private void ActivateLastChance() - { - var timerMs = 1000 * 1; - _lastChanceTimer = new Timer(LastChance, null, timerMs, timerMs); - } - - private void DeactivateLastChance() - { - _lastChanceTimer?.Dispose(); - _lastChanceTimer = null; - } - - private void LastChance(object? state) - { - if (!IsReconnectionEnabled || ReconnectTimeout == null) - { - // reconnection disabled, do nothing - DeactivateLastChance(); - return; - } - - var timeoutMs = Math.Abs(ReconnectTimeout.Value.TotalMilliseconds); - var diffMs = Math.Abs(DateTime.UtcNow.Subtract(_lastReceivedMsg).TotalMilliseconds); - if (diffMs > timeoutMs) - { - _logger.LogDebug(L("Last message received more than {timeoutMs} ms ago. Hard restart.."), Name, timeoutMs.ToString("F")); - - DeactivateLastChance(); - _ = ReconnectSynchronized(ReconnectionType.NoMessageReceived, false, null); - } - } - } -} diff --git a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Websocket.Client/WebsocketClient.Sending.cs b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Websocket.Client/WebsocketClient.Sending.cs deleted file mode 100644 index 1967941..0000000 --- a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Websocket.Client/WebsocketClient.Sending.cs +++ /dev/null @@ -1,239 +0,0 @@ -#nullable enable -using System; -using System.Net.WebSockets; -using System.Threading; -using System.Threading.Channels; -using System.Threading.Tasks; -using Microsoft.Extensions.Logging; - -namespace Websocket.Client -{ - public partial class WebsocketClient - { - private readonly Channel _messagesTextToSendQueue = Channel.CreateUnbounded(new UnboundedChannelOptions() - { - SingleReader = true, - SingleWriter = false - }); - private readonly Channel> _messagesBinaryToSendQueue = Channel.CreateUnbounded>(new UnboundedChannelOptions() - { - SingleReader = true, - SingleWriter = false - }); - - - /// - /// Send text message to the websocket channel. - /// It inserts the message to the queue and actual sending is done on an other thread - /// - /// Text message to be sent - public void Send(string message) - { - Validations.Validations.ValidateInput(message, nameof(message)); - - _messagesTextToSendQueue.Writer.TryWrite(message); - } - - /// - /// Send binary message to the websocket channel. - /// It inserts the message to the queue and actual sending is done on an other thread - /// - /// Binary message to be sent - public void Send(byte[] message) - { - Validations.Validations.ValidateInput(message, nameof(message)); - - _messagesBinaryToSendQueue.Writer.TryWrite(new ArraySegment(message)); - } - - /// - /// Send binary message to the websocket channel. - /// It inserts the message to the queue and actual sending is done on an other thread - /// - /// Binary message to be sent - public void Send(ArraySegment message) - { - Validations.Validations.ValidateInput(message, nameof(message)); - - _messagesBinaryToSendQueue.Writer.TryWrite(message); - } - - /// - /// Send text message to the websocket channel. - /// It doesn't use a sending queue, - /// beware of issue while sending two messages in the exact same time - /// on the full .NET Framework platform - /// - /// Message to be sent - public Task SendInstant(string message) - { - Validations.Validations.ValidateInput(message, nameof(message)); - - return SendInternalSynchronized(message); - } - - /// - /// Send binary message to the websocket channel. - /// It doesn't use a sending queue, - /// beware of issue while sending two messages in the exact same time - /// on the full .NET Framework platform - /// - /// Message to be sent - public Task SendInstant(byte[] message) - { - return SendInternalSynchronized(new ArraySegment(message)); - } - - /// - /// Stream/publish fake message (via 'MessageReceived' observable). - /// Use for testing purposes to simulate a server message. - /// - /// Message to be stream - public void StreamFakeMessage(ResponseMessage message) - { - Validations.Validations.ValidateInput(message, nameof(message)); - - _messageReceivedSubject.OnNext(message); - } - - - private async Task SendTextFromQueue() - { - try - { - while (await _messagesTextToSendQueue.Reader.WaitToReadAsync()) - { - while (_messagesTextToSendQueue.Reader.TryRead(out var message)) - { - try - { - await SendInternalSynchronized(message).ConfigureAwait(false); - } - catch (Exception e) - { - _logger.LogError(e, L("Failed to send text message: '{message}'. Error: {error}"), Name, message, e.Message); - } - } - } - } - catch (TaskCanceledException) - { - // task was canceled, ignore - } - catch (OperationCanceledException) - { - // operation was canceled, ignore - } - catch (Exception e) - { - if (_cancellationTotal?.IsCancellationRequested == true || _disposing) - { - // disposing/canceling, do nothing and exit - return; - } - - _logger.LogTrace(L("Sending text thread failed, error: {error}. Creating a new sending thread."), Name, e.Message); - StartBackgroundThreadForSendingText(); - } - - } - - private async Task SendBinaryFromQueue() - { - try - { - while (await _messagesBinaryToSendQueue.Reader.WaitToReadAsync()) - { - while (_messagesBinaryToSendQueue.Reader.TryRead(out var message)) - { - try - { - await SendInternalSynchronized(message).ConfigureAwait(false); - } - catch (Exception e) - { - _logger.LogError(e, L("Failed to send binary message: '{message}'. Error: {error}"), Name, message, e.Message); - } - } - } - } - catch (TaskCanceledException) - { - // task was canceled, ignore - } - catch (OperationCanceledException) - { - // operation was canceled, ignore - } - catch (Exception e) - { - if (_cancellationTotal?.IsCancellationRequested == true || _disposing) - { - // disposing/canceling, do nothing and exit - return; - } - - _logger.LogTrace(L("Sending binary thread failed, error: {error}. Creating a new sending thread."), Name, e.Message); - StartBackgroundThreadForSendingBinary(); - } - - } - - private void StartBackgroundThreadForSendingText() - { - _ = Task.Factory.StartNew(_ => SendTextFromQueue(), TaskCreationOptions.LongRunning, _cancellationTotal?.Token ?? CancellationToken.None); - } - - private void StartBackgroundThreadForSendingBinary() - { - _ = Task.Factory.StartNew(_ => SendBinaryFromQueue(), TaskCreationOptions.LongRunning, _cancellationTotal?.Token ?? CancellationToken.None); - } - - private async Task SendInternalSynchronized(string message) - { - using (await _locker.LockAsync()) - { - await SendInternal(message); - } - } - - private async Task SendInternal(string message) - { - if (!IsClientConnected()) - { - _logger.LogDebug(L("Client is not connected to server, cannot send: {message}"), Name, message); - return; - } - - _logger.LogTrace(L("Sending: {message}"), Name, message); - var buffer = GetEncoding().GetBytes(message); - var messageSegment = new ArraySegment(buffer); - await _client! - .SendAsync(messageSegment, WebSocketMessageType.Text, true, _cancellation?.Token ?? CancellationToken.None) - .ConfigureAwait(false); - } - - private async Task SendInternalSynchronized(ArraySegment message) - { - using (await _locker.LockAsync()) - { - await SendInternal(message); - } - } - - private async Task SendInternal(ArraySegment message) - { - if (!IsClientConnected()) - { - _logger.LogDebug(L("Client is not connected to server, cannot send binary, length: {length}"), Name, message.Count); - return; - } - - _logger.LogTrace(L("Sending binary, length: {length}"), Name, message.Count); - - await _client! - .SendAsync(message, WebSocketMessageType.Binary, true, _cancellation?.Token ?? CancellationToken.None) - .ConfigureAwait(false); - } - } -} diff --git a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Websocket.Client/WebsocketClient.cs b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Websocket.Client/WebsocketClient.cs deleted file mode 100644 index d4b6fbd..0000000 --- a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Websocket.Client/WebsocketClient.cs +++ /dev/null @@ -1,649 +0,0 @@ -#nullable enable -using Microsoft.Extensions.Logging; -using System; -using System.IO; -using System.Linq; -using System.Net.WebSockets; -using System.Reactive.Linq; -using System.Reactive.Subjects; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.Logging.Abstractions; -using Websocket.Client.Exceptions; -using Websocket.Client.Threading; - -namespace Websocket.Client -{ - /// - /// A simple websocket client with built-in reconnection and error handling - /// - public partial class WebsocketClient : IWebsocketClient - { - private readonly ILogger _logger; - private readonly WebsocketAsyncLock _locker = new WebsocketAsyncLock(); - private readonly Func> _connectionFactory; - - private Uri _url; - private Timer? _lastChanceTimer; - private DateTime _lastReceivedMsg = DateTime.UtcNow; - - private Timer? _errorReconnectTimer; - - private bool _disposing; - private bool _reconnecting; - private bool _stopping; - private bool _isReconnectionEnabled = true; - private WebSocket? _client; - private CancellationTokenSource? _cancellation; - private CancellationTokenSource? _cancellationTotal; - - private readonly Subject _messageReceivedSubject = new Subject(); - private readonly Subject _reconnectionSubject = new Subject(); - private readonly Subject _disconnectedSubject = new Subject(); - - /// - /// A simple websocket client with built-in reconnection and error handling - /// - /// Target websocket url (wss://) - /// Optional factory for native ClientWebSocket, use it whenever you need some custom features (proxy, settings, etc) - public WebsocketClient(Uri url, Func? clientFactory = null) - : this(url, null, GetClientFactory(clientFactory)) - { - } - - /// - /// A simple websocket client with built-in reconnection and error handling - /// - /// Target websocket url (wss://) - /// Logger instance, can be null - /// Optional factory for native ClientWebSocket, use it whenever you need some custom features (proxy, settings, etc) - public WebsocketClient(Uri url, ILogger? logger, Func? clientFactory = null) - : this(url, logger, GetClientFactory(clientFactory)) - { - } - - /// - /// A simple websocket client with built-in reconnection and error handling - /// - /// Target websocket url (wss://) - /// Logger instance, can be null - /// Optional factory for native creating and connecting to a websocket. The method should return a which is connected. Use it whenever you need some custom features (proxy, settings, etc) - public WebsocketClient(Uri url, ILogger? logger, Func>? connectionFactory) - { - Validations.Validations.ValidateInput(url, nameof(url)); - - _logger = logger ?? NullLogger.Instance; - _url = url; - _connectionFactory = connectionFactory ?? (async (uri, token) => - { - //var client = new ClientWebSocket - //{ - // Options = { KeepAliveInterval = new TimeSpan(0, 0, 5, 0) } - //}; - var client = new ClientWebSocket(); - await client.ConnectAsync(uri, token).ConfigureAwait(false); - return client; - }); - } - - /// - public Uri Url - { - get => _url; - set - { - Validations.Validations.ValidateInput(value, nameof(Url)); - _url = value; - } - } - - /// - /// Stream with received message (raw format) - /// - public IObservable MessageReceived => _messageReceivedSubject.AsObservable(); - - /// - /// Stream for reconnection event (triggered after the new connection) - /// - public IObservable ReconnectionHappened => _reconnectionSubject.AsObservable(); - - /// - /// Stream for disconnection event (triggered after the connection was lost) - /// - public IObservable DisconnectionHappened => _disconnectedSubject.AsObservable(); - - /// - /// Time range for how long to wait before reconnecting if no message comes from server. - /// Set null to disable this feature. - /// Default: 1 minute - /// - public TimeSpan? ReconnectTimeout { get; set; } = TimeSpan.FromMinutes(1); - - /// - /// Time range for how long to wait before reconnecting if last reconnection failed. - /// Set null to disable this feature. - /// Default: 1 minute - /// - public TimeSpan? ErrorReconnectTimeout { get; set; } = TimeSpan.FromMinutes(1); - - /// - /// Time range for how long to wait before reconnecting if connection is lost with a transient error. - /// Set null to disable this feature. - /// Default: null/disabled (immediately) - /// - public TimeSpan? LostReconnectTimeout { get; set; } - - /// - /// Enable or disable reconnection functionality (enabled by default) - /// - public bool IsReconnectionEnabled - { - get => _isReconnectionEnabled; - set - { - _isReconnectionEnabled = value; - - if (IsStarted) - { - if (_isReconnectionEnabled) - { - ActivateLastChance(); - } - else - { - DeactivateLastChance(); - } - } - } - } - - /// - /// Get or set the name of the current websocket client instance. - /// For logging purpose (in case you use more parallel websocket clients and want to distinguish between them) - /// - public string? Name { get; set; } - - /// - /// Returns true if Start() method was called at least once. False if not started or disposed - /// - public bool IsStarted { get; private set; } - - /// - /// Returns true if client is running and connected to the server - /// - public bool IsRunning { get; private set; } - - /// - /// Enable or disable text message conversion from binary to string (via 'MessageEncoding' property). - /// Default: true - /// - public bool IsTextMessageConversionEnabled { get; set; } = true; - - /// - public Encoding? MessageEncoding { get; set; } - - /// - public ClientWebSocket? NativeClient => GetSpecificOrThrow(_client); - - /// - /// Terminate the websocket connection and cleanup everything - /// - public void Dispose() - { - _disposing = true; - _logger.LogDebug(L("Disposing.."), Name); - try - { - _messagesTextToSendQueue.Writer.Complete(); - _messagesBinaryToSendQueue.Writer.Complete(); - _lastChanceTimer?.Dispose(); - _errorReconnectTimer?.Dispose(); - _cancellation?.Cancel(); - _cancellationTotal?.Cancel(); - _client?.Abort(); - _client?.Dispose(); - _cancellation?.Dispose(); - _cancellationTotal?.Dispose(); - _messageReceivedSubject.OnCompleted(); - _reconnectionSubject.OnCompleted(); - } - catch (Exception e) - { - _logger.LogError(e, L("Failed to dispose client, error: {error}"), Name, e.Message); - } - - if (IsRunning) - { - _disconnectedSubject.OnNext(DisconnectionInfo.Create(DisconnectionType.Exit, _client, null)); - } - - IsRunning = false; - IsStarted = false; - _disconnectedSubject.OnCompleted(); - } - - /// - /// Start listening to the websocket stream on the background thread. - /// In case of connection error it doesn't throw an exception. - /// Only streams a message via 'DisconnectionHappened' and logs it. - /// - public Task Start() - { - return StartInternal(false); - } - - /// - /// Start listening to the websocket stream on the background thread. - /// In case of connection error it throws an exception. - /// Fail fast approach. - /// - public Task StartOrFail() - { - return StartInternal(true); - } - - /// - /// Stop/close websocket connection with custom close code. - /// Method doesn't throw exception, only logs it and mark client as closed. - /// - /// Returns true if close was initiated successfully - public async Task Stop(WebSocketCloseStatus status, string statusDescription) - { - var result = await StopInternal( - _client, - status, - statusDescription, - null, - false, - false).ConfigureAwait(false); - _disconnectedSubject.OnNext(DisconnectionInfo.Create(DisconnectionType.ByUser, _client, null)); - return result; - } - - /// - /// Stop/close websocket connection with custom close code. - /// Method could throw exceptions, but client is marked as closed anyway. - /// - /// Returns true if close was initiated successfully - public async Task StopOrFail(WebSocketCloseStatus status, string statusDescription) - { - var result = await StopInternal( - _client, - status, - statusDescription, - null, - true, - false).ConfigureAwait(false); - _disconnectedSubject.OnNext(DisconnectionInfo.Create(DisconnectionType.ByUser, _client, null)); - return result; - } - - private static Func>? GetClientFactory(Func? clientFactory) - { - if (clientFactory == null) - return null; - - return (async (uri, token) => - { - var client = clientFactory(); - await client.ConnectAsync(uri, token).ConfigureAwait(false); - return client; - }); - } - - private async Task StartInternal(bool failFast) - { - if (_disposing) - { - throw new WebsocketException($"Client {Name} is already disposed, starting not possible"); - } - - if (IsStarted) - { - _logger.LogDebug(L("Client already started, ignoring.."), Name); - return; - } - - IsStarted = true; - - _logger.LogDebug(L("Starting.."), Name); - _cancellation = new CancellationTokenSource(); - _cancellationTotal = new CancellationTokenSource(); - - await StartClient(_url, _cancellation.Token, ReconnectionType.Initial, failFast).ConfigureAwait(false); - - StartBackgroundThreadForSendingText(); - StartBackgroundThreadForSendingBinary(); - } - - private async Task StopInternal(WebSocket? client, WebSocketCloseStatus status, string statusDescription, - CancellationToken? cancellation, bool failFast, bool byServer) - { - if (_disposing) - { - throw new WebsocketException($"Client {Name} is already disposed, stopping not possible"); - } - - DeactivateLastChance(); - - if (client == null) - { - IsStarted = false; - IsRunning = false; - return false; - } - - if (!IsRunning) - { - _logger.LogInformation(L("Client is already stopped"), Name); - IsStarted = false; - return false; - } - - var result = false; - try - { - var cancellationToken = cancellation ?? CancellationToken.None; - _stopping = true; - if (byServer) - await client.CloseOutputAsync(status, statusDescription, cancellationToken); - else - await client.CloseAsync(status, statusDescription, cancellationToken); - result = true; - } - catch (Exception e) - { - _logger.LogError(e, L("Error while stopping client, message: '{error}'"), Name, e.Message); - - if (failFast) - { - // fail fast, propagate exception - throw new WebsocketException($"Failed to stop Websocket client {Name}, error: '{e.Message}'", e); - } - } - finally - { - IsRunning = false; - _stopping = false; - - if (!byServer || !IsReconnectionEnabled) - { - // stopped manually or no reconnection, mark client as non-started - IsStarted = false; - } - } - - return result; - } - - private async Task StartClient(Uri uri, CancellationToken token, ReconnectionType type, bool failFast) - { - DeactivateLastChance(); - - try - { - _client = await _connectionFactory(uri, token).ConfigureAwait(false); - _ = Listen(_client, token); - IsRunning = true; - IsStarted = true; - _reconnectionSubject.OnNext(ReconnectionInfo.Create(type)); - _lastReceivedMsg = DateTime.UtcNow; - ActivateLastChance(); - } - catch (Exception e) - { - var info = DisconnectionInfo.Create(DisconnectionType.Error, _client, e); - _disconnectedSubject.OnNext(info); - - if (info.CancelReconnection) - { - // reconnection canceled by user, do nothing - _logger.LogError(e, L("Exception while connecting. " + - "Reconnecting canceled by user, exiting. Error: '{error}'"), Name, e.Message); - return; - } - - if (failFast) - { - // fail fast, propagate exception - // do not reconnect - throw new WebsocketException($"Failed to start Websocket client {Name}, error: '{e.Message}'", e); - } - - if (ErrorReconnectTimeout == null) - { - _logger.LogError(e, L("Exception while connecting. " + - "Reconnecting disabled, exiting. Error: '{error}'"), Name, e.Message); - return; - } - - var timeout = ErrorReconnectTimeout.Value; - _logger.LogError(e, L("Exception while connecting. " + - "Waiting {timeout} sec before next reconnection try. Error: '{error}'"), Name, timeout.TotalSeconds, e.Message); - _errorReconnectTimer?.Dispose(); - _errorReconnectTimer = new Timer(ReconnectOnError, e, timeout, Timeout.InfiniteTimeSpan); - } - } - - private void ReconnectOnError(object? state) - { - // await Task.Delay(timeout, token).ConfigureAwait(false); - _ = Reconnect(ReconnectionType.Error, false, state as Exception).ConfigureAwait(false); - } - - private bool IsClientConnected() - { - return _client?.State == WebSocketState.Open; - } - - private async Task Listen(WebSocket client, CancellationToken token) - { - Exception? causedException = null; - try - { - // define buffer here and reuse, to avoid more allocation - const int chunkSize = 1024 * 4; - var buffer = new ArraySegment(new byte[chunkSize]); - - do - { - WebSocketReceiveResult result; - byte[]? resultArrayWithTrailing = null; - var resultArraySize = 0; - var isResultArrayCloned = false; - MemoryStream? ms = null; - - while (true) - { - result = await client.ReceiveAsync(buffer, token); - var currentChunk = buffer.Array; - var currentChunkSize = result.Count; - - var isFirstChunk = resultArrayWithTrailing == null; - if (isFirstChunk) - { - // first chunk, use buffer as reference, do not allocate anything - resultArraySize += currentChunkSize; - resultArrayWithTrailing = currentChunk; - isResultArrayCloned = false; - } - else if (currentChunk == null) - { - // weird chunk, do nothing - } - else - { - // received more chunks, lets merge them via memory stream - if (ms == null) - { - // create memory stream and insert first chunk - ms = new MemoryStream(); - ms.Write(resultArrayWithTrailing!, 0, resultArraySize); - } - - // insert current chunk - ms.Write(currentChunk, buffer.Offset, currentChunkSize); - } - - if (result.EndOfMessage) - { - break; - } - - if (isResultArrayCloned) - continue; - - // we got more chunks incoming, need to clone first chunk - resultArrayWithTrailing = resultArrayWithTrailing?.ToArray(); - isResultArrayCloned = true; - } - - ms?.Seek(0, SeekOrigin.Begin); - - ResponseMessage message; - if (result.MessageType == WebSocketMessageType.Text && IsTextMessageConversionEnabled) - { - var data = ms != null ? - GetEncoding().GetString(ms.ToArray()) : - resultArrayWithTrailing != null ? - GetEncoding().GetString(resultArrayWithTrailing, 0, resultArraySize) : - null; - - message = ResponseMessage.TextMessage(data); - } - else if (result.MessageType == WebSocketMessageType.Close) - { - _logger.LogTrace(L("Received close message"), Name); - - if (!IsStarted || _stopping) - { - return; - } - - var info = DisconnectionInfo.Create(DisconnectionType.ByServer, client, null); - _disconnectedSubject.OnNext(info); - - if (info.CancelClosing) - { - // closing canceled, reconnect if enabled - if (IsReconnectionEnabled) - { - throw new OperationCanceledException($"Websocket connection was closed by server (client: {Name})"); - } - - continue; - } - - await StopInternal(client, WebSocketCloseStatus.NormalClosure, "Closing", - token, false, true); - - // reconnect if enabled - if (IsReconnectionEnabled && !ShouldIgnoreReconnection(client)) - { - _ = ReconnectSynchronized(ReconnectionType.Lost, false, null); - } - - return; - } - else - { - if (ms != null) - { - message = ResponseMessage.BinaryMessage(ms.ToArray()); - } - else - { - Array.Resize(ref resultArrayWithTrailing, resultArraySize); - message = ResponseMessage.BinaryMessage(resultArrayWithTrailing); - } - } - - ms?.Dispose(); - - _logger.LogTrace(L("Received: {message}"), Name, message); - _lastReceivedMsg = DateTime.UtcNow; - _messageReceivedSubject.OnNext(message); - - } while (client.State == WebSocketState.Open && !token.IsCancellationRequested); - } - catch (TaskCanceledException e) - { - // task was canceled, ignore - causedException = e; - } - catch (OperationCanceledException e) - { - // operation was canceled, ignore - causedException = e; - } - catch (ObjectDisposedException e) - { - // client was disposed, ignore - causedException = e; - } - catch (Exception e) - { - _logger.LogError(e, L("Error while listening to websocket stream, error: '{error}'"), Name, e.Message); - causedException = e; - } - - if (ShouldIgnoreReconnection(client) || !IsStarted) - { - // reconnection already in progress or client stopped/disposed, do nothing - return; - } - - if (LostReconnectTimeout.HasValue) - { - var timeout = LostReconnectTimeout.Value; - _logger.LogWarning(L("Listening websocket stream is lost. " + - "Waiting {timeout} sec before next reconnection try."), Name, timeout.TotalSeconds); - await Task.Delay(timeout, token).ConfigureAwait(false); - } - - // listening thread is lost, we have to reconnect - _ = ReconnectSynchronized(ReconnectionType.Lost, false, causedException); - } - - private bool ShouldIgnoreReconnection(WebSocket client) - { - // reconnection already in progress or client stopped/ disposed, - var inProgress = _disposing || _reconnecting || _stopping; - - // already reconnected - var differentClient = client != _client; - - return inProgress || differentClient; - } - - private Encoding GetEncoding() - { - if (MessageEncoding == null) - MessageEncoding = Encoding.UTF8; - return MessageEncoding; - } - - private ClientWebSocket? GetSpecificOrThrow(WebSocket? client) - { - if (client == null) - return null; - var specific = client as ClientWebSocket; - if (specific == null) - throw new WebsocketException("Cannot cast 'WebSocket' client to 'ClientWebSocket', " + - "provide correct type via factory or don't use this property at all."); - return specific; - } - - private string L(string msg) - { - return $"[WEBSOCKET {{name}}] {msg}"; - } - - private DisconnectionType TranslateTypeToDisconnection(ReconnectionType type) - { - // beware enum indexes must correspond to each other - return (DisconnectionType)type; - } - } -} diff --git a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/ZWaveJS.NET.csproj b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/ZWaveJS.NET.csproj index 1c5b285..205dd0e 100644 --- a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/ZWaveJS.NET.csproj +++ b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/ZWaveJS.NET.csproj @@ -1,8 +1,8 @@ - net48;net6.0;net7.0;net8.0;netstandard2.0;netstandard2.1 - 4.0.0 + net6.0;net7.0;net8.0;netstandard2.1;net9.0 + 5.0.0 Marcus Davies true ZWave JS @@ -17,13 +17,6 @@ Debug;Release 8.0 - - - - - - - @@ -32,4 +25,10 @@ + + + + + + diff --git a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/ZWaveNode.cs b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/ZWaveNode.cs index de4b440..042ab2a 100644 --- a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/ZWaveNode.cs +++ b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/ZWaveNode.cs @@ -86,6 +86,13 @@ internal void Trigger_Notification(int CCID, JObject Args) { Notification?.Invoke(this, CCID, Args); } + + public delegate void NodeInfoEvent(ZWaveNode Node); + public event NodeInfoEvent NodeInfo; + internal void Trigger_NodeInfo() + { + NodeInfo?.Invoke(this); + } public delegate void NodeAliveEvent(ZWaveNode Node); public event NodeAliveEvent NodeAlive; From bf45b27c0d8e5be0d68f6fd277286e80af76226c Mon Sep 17 00:00:00 2001 From: Marcus Davies <55892693+marcus-j-davies@users.noreply.github.com> Date: Sun, 30 Nov 2025 12:10:09 +0000 Subject: [PATCH 02/59] fix --- PSI/package.json | 2 +- .../.idea.ZWaveJS.NET/.idea/workspace.xml | 68 ++++---- .../ZWaveJS.NET.sln.DotSettings.user | 2 + .../ZWaveJS.NET/ZWaveJS.NET/CMDResult.cs | 7 + .../ZWaveJS.NET/ZWaveJS.NET/Controller.cs | 164 ++++++++---------- .../ZWaveJS.NET/ZWaveJS.NET/Endpoint.cs | 4 +- .../ZWaveJS.NET/ZWaveJS.NET/Enums.cs | 82 +++++---- .../ZWaveJS.NET/ZWaveJS.NET/Structures.cs | 2 + .../ZWaveJS.NET/ZWaveJS.NET/Test Messages.txt | 62 +++++++ .../ZWaveJS.NET/ZWaveJS.NET/VirtualNode.cs | 13 +- .../ZWaveJS.NET/ZWaveJS.NET/ZWaveNode.cs | 22 +-- 11 files changed, 251 insertions(+), 177 deletions(-) create mode 100644 Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET.sln.DotSettings.user create mode 100644 Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Test Messages.txt diff --git a/PSI/package.json b/PSI/package.json index c0f663e..f382dab 100644 --- a/PSI/package.json +++ b/PSI/package.json @@ -16,7 +16,7 @@ "build": "npm run do_esbuild && npm run do_pkgbuild", "do_esbuild": "node esb.js", "do_pkgbuild": "pkg ./build/package.json --compress gzip -t host --targets node20 --output ./build/dist/server", - "test": "export WS_PORT=7070 SERIAL_PORT=/dev/tty.usbmodem114201 CONFIG=$(<./test_config.json) && ./build/dist/server", + "test": "export WS_PORT=7070 SERIAL_PORT=/dev/tty.usbmodem1101 CONFIG=$(<./test_config.json) && ./build/dist/server", "lint": "eslint --ext .js .", "lint:fix": "eslint --fix --ext .js . && prettier -w ." } diff --git a/Visual Studio Projects/ZWaveJS.NET/.idea/.idea.ZWaveJS.NET/.idea/workspace.xml b/Visual Studio Projects/ZWaveJS.NET/.idea/.idea.ZWaveJS.NET/.idea/workspace.xml index 17b9752..04ccf6b 100644 --- a/Visual Studio Projects/ZWaveJS.NET/.idea/.idea.ZWaveJS.NET/.idea/workspace.xml +++ b/Visual Studio Projects/ZWaveJS.NET/.idea/.idea.ZWaveJS.NET/.idea/workspace.xml @@ -5,27 +5,14 @@ - - - + + + - + - - - - - - - - - - - - - - - + + - - - - - - { - "keyToString": { - "ModuleVcsDetector.initialDetectionPerformed": "true", - "RunOnceActivity.ShowReadmeOnStart": "true", - "RunOnceActivity.git.unshallow": "true", - "git-widget-placeholder": "modern", - "node.js.detected.package.eslint": "true", - "node.js.detected.package.tslint": "true", - "node.js.selected.package.eslint": "(autodetect)", - "node.js.selected.package.tslint": "(autodetect)", - "nodejs_package_manager_path": "npm", - "settings.editor.selected.configurable": "preferences.pluginManager", - "vue.rearranger.settings.migration": "true" + +}]]> @@ -62,6 +57,7 @@ + diff --git a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/CMDResult.cs b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/CMDResult.cs index 049beb5..583f7ef 100644 --- a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/CMDResult.cs +++ b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/CMDResult.cs @@ -30,6 +30,11 @@ internal CMDResult(JObject Res) this.Message = Res.Value("zwaveErrorMessage"); } + public T ResultPayloadAs() + { + return ResultPayload is T value ? value : default!; + } + internal void SetPayload(object Payload) { this.ResultPayload = Payload; diff --git a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Controller.cs b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Controller.cs index ffa3c56..3ae7094 100644 --- a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Controller.cs +++ b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Controller.cs @@ -157,6 +157,7 @@ public Task GetAvailableFirmwareUpdates(int NodeID, bool IncludePrere _driver.Callbacks.Add(ID, (JO) => { CMDResult Res = new CMDResult(JO); + if (Res.Success) { @@ -776,7 +777,7 @@ public Task BeginRebuildingRoutes(RebuildRoutesOptions Options) _driver.Callbacks.Add(ID, (JO) => { CMDResult Res = new CMDResult(JO); - if (Res.Success && Res.ResultPayload is bool b && b) + if (Res.Success && Res.ResultPayloadAs()) { this.isRebuildingRoutes = true; } @@ -804,7 +805,7 @@ public Task StopRebuildingRoutes() _driver.Callbacks.Add(ID, (JO) => { CMDResult Res = new CMDResult(JO); - if (Res.Success && Res.ResultPayload is bool b && b) + if (Res.Success && Res.ResultPayloadAs()) { this.isRebuildingRoutes = false; } @@ -939,7 +940,7 @@ public Task StopInclusion() return Result.Task; } - // MMMM + // Checked as of : 3.5.0 public Task ProvisionSmartStartNode(SmartStartProvisioningEntry ProvisioningInformation) { Guid ID = Guid.NewGuid(); diff --git a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/ZWaveNode.cs b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/ZWaveNode.cs index e6cd1c7..d9f67a3 100644 --- a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/ZWaveNode.cs +++ b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/ZWaveNode.cs @@ -398,7 +398,7 @@ public Task SetValue(ValueID ValueID, object Value, SetValueAPIOption return Result.Task; } - // CHECKED + // Checked as of : 3.5.0 public Task PollValue(ValueID ValueID) { Guid ID = Guid.NewGuid(); From 5270b0e1ad451cfac686a5e126d0082a48876332 Mon Sep 17 00:00:00 2001 From: Marcus Davies <55892693+marcus-j-davies@users.noreply.github.com> Date: Sun, 18 Jan 2026 08:55:53 +0000 Subject: [PATCH 04/59] Bump, check --- .gitignore | 2 - PSI/package-lock.json | 4182 +++++++++++++++++ PSI/package.json | 10 +- .../.idea.ZWaveJS.NET/.idea/encodings.xml | 4 - .../.idea.ZWaveJS.NET/.idea/indexLayout.xml | 8 - .../.idea/projectSettingsUpdater.xml | 8 - .../.idea/.idea.ZWaveJS.NET/.idea/vcs.xml | 6 - .../.idea.ZWaveJS.NET/.idea/workspace.xml | 89 - .../ZWaveJS.NET/ZWaveJS.NET/ConfigManager.cs | 4 +- .../ZWaveJS.NET/ZWaveJS.NET/Utils.cs | 2 +- .../ZWaveJS.NET/ZWaveJS.NET/ZWaveNode.cs | 44 +- 11 files changed, 4213 insertions(+), 146 deletions(-) create mode 100644 PSI/package-lock.json delete mode 100644 Visual Studio Projects/ZWaveJS.NET/.idea/.idea.ZWaveJS.NET/.idea/encodings.xml delete mode 100644 Visual Studio Projects/ZWaveJS.NET/.idea/.idea.ZWaveJS.NET/.idea/indexLayout.xml delete mode 100644 Visual Studio Projects/ZWaveJS.NET/.idea/.idea.ZWaveJS.NET/.idea/projectSettingsUpdater.xml delete mode 100644 Visual Studio Projects/ZWaveJS.NET/.idea/.idea.ZWaveJS.NET/.idea/vcs.xml delete mode 100644 Visual Studio Projects/ZWaveJS.NET/.idea/.idea.ZWaveJS.NET/.idea/workspace.xml diff --git a/.gitignore b/.gitignore index 403454a..c94c2ce 100644 --- a/.gitignore +++ b/.gitignore @@ -20,5 +20,3 @@ **/.pnp.* PSI/server.js -PSI/package-lock.json -package-lock.json diff --git a/PSI/package-lock.json b/PSI/package-lock.json new file mode 100644 index 0000000..da0f9fe --- /dev/null +++ b/PSI/package-lock.json @@ -0,0 +1,4182 @@ +{ + "name": "server", + "version": "5.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "server", + "version": "5.0.0", + "dependencies": { + "@zwave-js/server": "^3.5.0", + "zwave-js": "^15.20.0" + }, + "bin": { + "server": "server.js" + }, + "devDependencies": { + "@yao-pkg/pkg": "^6.12.0", + "esbuild": "^0.27.2", + "eslint": "^9.39.2", + "prettier": "^3.8.0" + } + }, + "node_modules/@alcalzone/jsonl-db": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@alcalzone/jsonl-db/-/jsonl-db-4.0.2.tgz", + "integrity": "sha512-GUs0Q2UYdOw3mtWD4NOePxRAvCEp6yMjpzRkbAOPxlBn3YQ0tuUzUe70RW0CIEy0Q6glLR2flyLFZPnPJyZIoQ==", + "license": "MIT", + "dependencies": { + "@alcalzone/proper-lockfile": "^4.1.3-0", + "alcalzone-shared": "^5.0.0" + }, + "engines": { + "node": ">=22" + } + }, + "node_modules/@alcalzone/proper-lockfile": { + "version": "4.1.3-0", + "resolved": "https://registry.npmjs.org/@alcalzone/proper-lockfile/-/proper-lockfile-4.1.3-0.tgz", + "integrity": "sha512-8mlX3l5Xc+pYyiK9G156NyMosNuvvukL+TtNMqw7ti2zgVpz+WqPMPb2J1WU8I03Jbm4cXF+Q0D53hWvQqLQ0Q==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "retry": "^0.12.0", + "signal-exit": "^3.0.2" + } + }, + "node_modules/@andrewbranch/untar.js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@andrewbranch/untar.js/-/untar.js-1.0.3.tgz", + "integrity": "sha512-Jh15/qVmrLGhkKJBdXlK1+9tY4lZruYjsgkDFj08ZmDiWVBLJcqkok7Z0/R0In+i1rScBpJlSvrTS2Lm41Pbnw==" + }, + "node_modules/@babel/generator": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.6.tgz", + "integrity": "sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.6.tgz", + "integrity": "sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.6" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.6.tgz", + "integrity": "sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@colors/colors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "license": "MIT", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@dabh/diagnostics": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.8.tgz", + "integrity": "sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q==", + "license": "MIT", + "dependencies": { + "@so-ric/colorspace": "^1.1.6", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz", + "integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.2.tgz", + "integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz", + "integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.2.tgz", + "integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz", + "integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz", + "integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz", + "integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz", + "integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz", + "integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz", + "integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz", + "integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz", + "integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz", + "integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz", + "integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz", + "integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz", + "integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz", + "integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz", + "integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz", + "integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz", + "integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz", + "integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz", + "integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz", + "integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz", + "integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz", + "integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz", + "integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", + "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz", + "integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==", + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.1", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "9.39.2", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz", + "integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==", + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@homebridge/ciao": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@homebridge/ciao/-/ciao-1.3.4.tgz", + "integrity": "sha512-qK6ZgGx0wwOubq/MY6eTbhApQHBUQCvCOsTYpQE01uLvfA2/Prm6egySHlZouKaina1RPuDwfLhCmsRCxwHj3Q==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.1", + "fast-deep-equal": "^3.1.3", + "source-map-support": "^0.5.21", + "tslib": "^2.8.1" + }, + "bin": { + "ciao-bcs": "lib/bonjour-conformance-testing.js" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@leichtgewicht/ip-codec": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", + "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==", + "license": "MIT" + }, + "node_modules/@serialport/binding-mock": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@serialport/binding-mock/-/binding-mock-10.2.2.tgz", + "integrity": "sha512-HAFzGhk9OuFMpuor7aT5G1ChPgn5qSsklTFOTUX72Rl6p0xwcSVsRtG/xaGp6bxpN7fI9D/S8THLBWbBgS6ldw==", + "license": "MIT", + "dependencies": { + "@serialport/bindings-interface": "^1.2.1", + "debug": "^4.3.3" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@serialport/bindings-cpp": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@serialport/bindings-cpp/-/bindings-cpp-13.0.0.tgz", + "integrity": "sha512-r25o4Bk/vaO1LyUfY/ulR6hCg/aWiN6Wo2ljVlb4Pj5bqWGcSRC4Vse4a9AcapuAu/FeBzHCbKMvRQeCuKjzIQ==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@serialport/bindings-interface": "1.2.2", + "@serialport/parser-readline": "12.0.0", + "debug": "4.4.0", + "node-addon-api": "8.3.0", + "node-gyp-build": "4.8.4" + }, + "engines": { + "node": ">=18.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/bindings-cpp/node_modules/@serialport/parser-delimiter": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-delimiter/-/parser-delimiter-12.0.0.tgz", + "integrity": "sha512-gu26tVt5lQoybhorLTPsH2j2LnX3AOP2x/34+DUSTNaUTzu2fBXw+isVjQJpUBFWu6aeQRZw5bJol5X9Gxjblw==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/bindings-cpp/node_modules/@serialport/parser-readline": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-readline/-/parser-readline-12.0.0.tgz", + "integrity": "sha512-O7cywCWC8PiOMvo/gglEBfAkLjp/SENEML46BXDykfKP5mTPM46XMaX1L0waWU6DXJpBgjaL7+yX6VriVPbN4w==", + "license": "MIT", + "dependencies": { + "@serialport/parser-delimiter": "12.0.0" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/bindings-cpp/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@serialport/bindings-interface": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@serialport/bindings-interface/-/bindings-interface-1.2.2.tgz", + "integrity": "sha512-CJaUd5bLvtM9c5dmO9rPBHPXTa9R2UwpkJ0wdh9JCYcbrPWsKz+ErvR0hBLeo7NPeiFdjFO4sonRljiw4d2XiA==", + "license": "MIT", + "engines": { + "node": "^12.22 || ^14.13 || >=16" + } + }, + "node_modules/@serialport/parser-byte-length": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-byte-length/-/parser-byte-length-13.0.0.tgz", + "integrity": "sha512-32yvqeTAqJzAEtX5zCrN1Mej56GJ5h/cVFsCDPbF9S1ZSC9FWjOqNAgtByseHfFTSTs/4ZBQZZcZBpolt8sUng==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/parser-cctalk": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-cctalk/-/parser-cctalk-13.0.0.tgz", + "integrity": "sha512-RErAe57g9gvnlieVYGIn1xymb1bzNXb2QtUQd14FpmbQQYlcrmuRnJwKa1BgTCujoCkhtaTtgHlbBWOxm8U2uA==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/parser-delimiter": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-delimiter/-/parser-delimiter-13.0.0.tgz", + "integrity": "sha512-Qqyb0FX1avs3XabQqNaZSivyVbl/yl0jywImp7ePvfZKLwx7jBZjvL+Hawt9wIG6tfq6zbFM24vzCCK7REMUig==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/parser-inter-byte-timeout": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-inter-byte-timeout/-/parser-inter-byte-timeout-13.0.0.tgz", + "integrity": "sha512-a0w0WecTW7bD2YHWrpTz1uyiWA2fDNym0kjmPeNSwZ2XCP+JbirZt31l43m2ey6qXItTYVuQBthm75sPVeHnGA==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/parser-packet-length": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-packet-length/-/parser-packet-length-13.0.0.tgz", + "integrity": "sha512-60ZDDIqYRi0Xs2SPZUo4Jr5LLIjtb+rvzPKMJCohrO6tAqSDponcNpcB1O4W21mKTxYjqInSz+eMrtk0LLfZIg==", + "license": "MIT", + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/@serialport/parser-readline": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-readline/-/parser-readline-13.0.0.tgz", + "integrity": "sha512-dov3zYoyf0dt1Sudd1q42VVYQ4WlliF0MYvAMA3MOyiU1IeG4hl0J6buBA2w4gl3DOCC05tGgLDN/3yIL81gsA==", + "license": "MIT", + "dependencies": { + "@serialport/parser-delimiter": "13.0.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/parser-ready": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-ready/-/parser-ready-13.0.0.tgz", + "integrity": "sha512-JNUQA+y2Rfs4bU+cGYNqOPnNMAcayhhW+XJZihSLQXOHcZsFnOa2F9YtMg9VXRWIcnHldHYtisp62Etjlw24bw==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/parser-regex": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-regex/-/parser-regex-13.0.0.tgz", + "integrity": "sha512-m7HpIf56G5XcuDdA3DB34Z0pJiwxNRakThEHjSa4mG05OnWYv0IG8l2oUyYfuGMowQWaVnQ+8r+brlPxGVH+eA==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/parser-slip-encoder": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-slip-encoder/-/parser-slip-encoder-13.0.0.tgz", + "integrity": "sha512-fUHZEExm6izJ7rg0A1yjXwu4sOzeBkPAjDZPfb+XQoqgtKAk+s+HfICiYn7N2QU9gyaeCO8VKgWwi+b/DowYOg==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/parser-spacepacket": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-spacepacket/-/parser-spacepacket-13.0.0.tgz", + "integrity": "sha512-DoXJ3mFYmyD8X/8931agJvrBPxqTaYDsPoly9/cwQSeh/q4EjQND9ySXBxpWz5WcpyCU4jOuusqCSAPsbB30Eg==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/stream": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@serialport/stream/-/stream-13.0.0.tgz", + "integrity": "sha512-F7xLJKsjGo2WuEWMSEO1SimRcOA+WtWICsY13r0ahx8s2SecPQH06338g28OT7cW7uRXI7oEQAk62qh5gHJW3g==", + "license": "MIT", + "dependencies": { + "@serialport/bindings-interface": "1.2.2", + "debug": "4.4.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/stream/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@so-ric/colorspace": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@so-ric/colorspace/-/colorspace-1.1.6.tgz", + "integrity": "sha512-/KiKkpHNOBgkFJwu9sh48LkHSMYGyuTcSFK/qMBdnOAlrRJzRSXAOFB5qwzaVQuDl8wAvHVMkaASQDReTahxuw==", + "license": "MIT", + "dependencies": { + "color": "^5.0.2", + "text-hex": "1.0.x" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "license": "MIT" + }, + "node_modules/@types/triple-beam": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", + "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", + "license": "MIT" + }, + "node_modules/@yao-pkg/pkg": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@yao-pkg/pkg/-/pkg-6.12.0.tgz", + "integrity": "sha512-yXdr5XTnEUm+AuBWPvMdv1z6dCcuKLUPYGZKPwb0pS8YE+P/Jspb47QjutcjfA31tIkGU6JTsOhlGxDxrO/A2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/generator": "^7.23.0", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", + "@yao-pkg/pkg-fetch": "3.5.32", + "into-stream": "^6.0.0", + "minimist": "^1.2.6", + "multistream": "^4.1.0", + "picocolors": "^1.1.0", + "picomatch": "^4.0.2", + "prebuild-install": "^7.1.1", + "resolve": "^1.22.10", + "stream-meter": "^1.0.4", + "tar": "^7.4.3", + "tinyglobby": "^0.2.11", + "unzipper": "^0.12.3" + }, + "bin": { + "pkg": "lib-es5/bin.js" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@yao-pkg/pkg-fetch": { + "version": "3.5.32", + "resolved": "https://registry.npmjs.org/@yao-pkg/pkg-fetch/-/pkg-fetch-3.5.32.tgz", + "integrity": "sha512-hS8zzze5VVyVAciZoNX4q3ZmCdn0gi34P2SElk4PFbzkA4LPs7qBJ3LhUKb4d4KGw1HVkFJJOMgGomH7cMqQ5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "https-proxy-agent": "^5.0.0", + "node-fetch": "^2.6.6", + "picocolors": "^1.1.0", + "progress": "^2.0.3", + "semver": "^7.3.5", + "tar-fs": "^3.1.1", + "yargs": "^16.2.0" + }, + "bin": { + "pkg-fetch": "lib-es5/bin.js" + } + }, + "node_modules/@zwave-js/cc": { + "version": "15.20.0", + "resolved": "https://registry.npmjs.org/@zwave-js/cc/-/cc-15.20.0.tgz", + "integrity": "sha512-ZBuq+GPt02oPeND+25JXicSrRp0S601zD3YwTDlJoVNwyZHK8y/77J4ciuD0aZal17Xs9sjQLbaq7vaNvMZxpw==", + "license": "MIT", + "dependencies": { + "@zwave-js/core": "15.20.0", + "@zwave-js/host": "15.20.0", + "@zwave-js/shared": "15.20.0", + "alcalzone-shared": "^5.0.0", + "ansi-colors": "^4.1.3", + "reflect-metadata": "^0.2.2" + }, + "engines": { + "node": ">= 20" + }, + "funding": { + "url": "https://github.com/sponsors/AlCalzone/" + } + }, + "node_modules/@zwave-js/config": { + "version": "15.20.0", + "resolved": "https://registry.npmjs.org/@zwave-js/config/-/config-15.20.0.tgz", + "integrity": "sha512-42QN01vlNgN6uCO0TXaZfZURAuiLt6WB4HZ0ZGO2bTabolvaQ+6yM2wg0JTWNx2AhY73fbxIoway0J5Y1rWdFg==", + "license": "MIT", + "dependencies": { + "@zwave-js/core": "15.20.0", + "@zwave-js/shared": "15.20.0", + "alcalzone-shared": "^5.0.0", + "ansi-colors": "^4.1.3", + "eslint": "^9.39.2", + "json-logic-js": "^2.0.5", + "json5": "^2.2.3", + "pathe": "^2.0.3", + "semver": "^7.7.2", + "winston": "^3.18.3" + }, + "engines": { + "node": ">= 20" + }, + "funding": { + "url": "https://github.com/sponsors/AlCalzone/" + } + }, + "node_modules/@zwave-js/core": { + "version": "15.20.0", + "resolved": "https://registry.npmjs.org/@zwave-js/core/-/core-15.20.0.tgz", + "integrity": "sha512-cmTe2/yZw3qS+d07wVU1VvZec9GVQR9KMzB0f5YBeWanu7ppnpQNvmZTfrum14pbENEcbByDK0NIDD4F+2FiVQ==", + "license": "MIT", + "dependencies": { + "@alcalzone/jsonl-db": "^4.0.2", + "@zwave-js/shared": "15.20.0", + "alcalzone-shared": "^5.0.0", + "ansi-colors": "^4.1.3", + "dayjs": "^1.11.18", + "fflate": "0.8.2", + "logform": "^2.7.0", + "nrf-intel-hex": "^1.4.0", + "pathe": "^2.0.3", + "reflect-metadata": "^0.2.2", + "semver": "^7.7.2", + "triple-beam": "*", + "winston": "^3.18.3", + "winston-daily-rotate-file": "^5.0.0", + "winston-transport": "^4.9.0" + }, + "engines": { + "node": ">= 20" + }, + "funding": { + "url": "https://github.com/sponsors/AlCalzone/" + } + }, + "node_modules/@zwave-js/host": { + "version": "15.20.0", + "resolved": "https://registry.npmjs.org/@zwave-js/host/-/host-15.20.0.tgz", + "integrity": "sha512-R1dKIPTDs+nyW5Ge/OoTyPJ/mGoDFWWzjISh2tzOfjTXTvMcTPuIvq6p3AsJ6P68CboQD7ZPRUbq8jZe+m3OcQ==", + "license": "MIT", + "dependencies": { + "@zwave-js/config": "15.20.0", + "@zwave-js/core": "15.20.0", + "@zwave-js/shared": "15.20.0", + "alcalzone-shared": "^5.0.0" + }, + "engines": { + "node": ">= 20" + }, + "funding": { + "url": "https://github.com/sponsors/AlCalzone/" + } + }, + "node_modules/@zwave-js/nvmedit": { + "version": "15.20.0", + "resolved": "https://registry.npmjs.org/@zwave-js/nvmedit/-/nvmedit-15.20.0.tgz", + "integrity": "sha512-0nhRIN/7cptFoTAtvwu7M0QR8zI0WUrLrGQ5aVfwXHO+FTv4UBlb6VzlRajWw82AKWYbnAXqjJNdkERVRSm7Zw==", + "license": "MIT", + "dependencies": { + "@zwave-js/core": "15.20.0", + "@zwave-js/shared": "15.20.0", + "alcalzone-shared": "^5.0.0", + "reflect-metadata": "^0.2.2", + "semver": "^7.7.2", + "yargs": "^18.0.0" + }, + "bin": { + "nvmedit": "bin/nvmedit.mjs" + }, + "engines": { + "node": ">= 20" + }, + "funding": { + "url": "https://github.com/sponsors/AlCalzone/" + } + }, + "node_modules/@zwave-js/nvmedit/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@zwave-js/nvmedit/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@zwave-js/nvmedit/node_modules/cliui": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", + "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", + "license": "ISC", + "dependencies": { + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@zwave-js/nvmedit/node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "license": "MIT" + }, + "node_modules/@zwave-js/nvmedit/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@zwave-js/nvmedit/node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@zwave-js/nvmedit/node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@zwave-js/nvmedit/node_modules/yargs": { + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.0.0.tgz", + "integrity": "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==", + "license": "MIT", + "dependencies": { + "cliui": "^9.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "string-width": "^7.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^22.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/@zwave-js/nvmedit/node_modules/yargs-parser": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", + "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", + "license": "ISC", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/@zwave-js/serial": { + "version": "15.20.0", + "resolved": "https://registry.npmjs.org/@zwave-js/serial/-/serial-15.20.0.tgz", + "integrity": "sha512-y0hqWxJchuNgedayRISpSPHCcBg7PBpEaTEFFP3YpMm2uBiL57gBs6RM15WmxyMRPAR0kdULaFK/IefR3KQoWg==", + "license": "MIT", + "dependencies": { + "@serialport/stream": "^13.0.0", + "@zwave-js/cc": "15.20.0", + "@zwave-js/core": "15.20.0", + "@zwave-js/host": "15.20.0", + "@zwave-js/shared": "15.20.0", + "alcalzone-shared": "^5.0.0", + "serialport": "^13.0.0", + "winston": "^3.18.3" + }, + "engines": { + "node": ">= 20" + }, + "funding": { + "url": "https://github.com/sponsors/AlCalzone/" + } + }, + "node_modules/@zwave-js/server": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/@zwave-js/server/-/server-3.5.0.tgz", + "integrity": "sha512-sEZuJSHvDxB5FS8A9NrDHlTFKe3L6Aauc/QjqgTpQwKzEwQshFD1n9B+W0jQJJ6K6Wk0SPgFIepQhEdbxOYe3A==", + "license": "Apache-2.0", + "dependencies": { + "@homebridge/ciao": "^1.1.7", + "minimist": "^1.2.8", + "ws": "^8.18.0" + }, + "bin": { + "zwave-client": "dist-esm/bin/client.js", + "zwave-server": "dist-esm/bin/server.js" + }, + "engines": { + "node": ">= 20" + }, + "peerDependencies": { + "zwave-js": "^15.14.0" + } + }, + "node_modules/@zwave-js/shared": { + "version": "15.20.0", + "resolved": "https://registry.npmjs.org/@zwave-js/shared/-/shared-15.20.0.tgz", + "integrity": "sha512-8NQcK1we/LShxEuRlF70Q3Qb42xQ5eo4QMd2mTTVXldZZT2U6wC58VIKKyQyfnpv4vXKgVl63PokOHSGtbK84Q==", + "license": "MIT", + "dependencies": { + "alcalzone-shared": "^5.0.0", + "pathe": "^2.0.3" + }, + "engines": { + "node": ">= 20" + }, + "funding": { + "url": "https://github.com/sponsors/AlCalzone/" + } + }, + "node_modules/@zwave-js/testing": { + "version": "15.20.0", + "resolved": "https://registry.npmjs.org/@zwave-js/testing/-/testing-15.20.0.tgz", + "integrity": "sha512-yWXAGZ0MHXv38+ULM5zAR+AQB12OsvjO91oAmlGTMR1iYFW4Us/5o5iMblZIsn/1QaOoWUdCPKacMaPYEudmEQ==", + "license": "MIT", + "dependencies": { + "@zwave-js/core": "15.20.0", + "@zwave-js/host": "15.20.0", + "@zwave-js/serial": "15.20.0", + "@zwave-js/shared": "15.20.0", + "alcalzone-shared": "^5.0.0", + "ansi-colors": "^4.1.3" + }, + "engines": { + "node": ">= 20" + }, + "funding": { + "url": "https://github.com/sponsors/AlCalzone/" + } + }, + "node_modules/@zwave-js/waddle": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@zwave-js/waddle/-/waddle-1.2.1.tgz", + "integrity": "sha512-E1kJiWBbiCm6LF23fm0Wupr0MbMiZxaqL7lJMA7BJdFmqKw4SXcv645+tzGsS004qg9mbc5GfvCntqD3AOw4HQ==", + "license": "MIT", + "dependencies": { + "alcalzone-shared": "^5.0.0" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "license": "MIT", + "peer": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/alcalzone-shared": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/alcalzone-shared/-/alcalzone-shared-5.0.0.tgz", + "integrity": "sha512-X73hgVWcrIKUUB6jZgHj5flRbTft8AAoJ2MqRKEcAX1whW3OeGkxsQ6ol4nd4/rKxd1eoCRXUGW3cIhXrXU4Sg==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "license": "MIT" + }, + "node_modules/b4a": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.7.3.tgz", + "integrity": "sha512-5Q2mfq2WfGuFp3uS//0s6baOJLMoVduPYVeNmDYxu5OUA1/cBfvr2RIS7vi62LdNj/urk1hfmj867I3qt6uZ7Q==", + "dev": true, + "license": "Apache-2.0", + "peerDependencies": { + "react-native-b4a": "*" + }, + "peerDependenciesMeta": { + "react-native-b4a": { + "optional": true + } + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/bare-events": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.8.2.tgz", + "integrity": "sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==", + "dev": true, + "license": "Apache-2.0", + "peerDependencies": { + "bare-abort-controller": "*" + }, + "peerDependenciesMeta": { + "bare-abort-controller": { + "optional": true + } + } + }, + "node_modules/bare-fs": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.5.2.tgz", + "integrity": "sha512-veTnRzkb6aPHOvSKIOy60KzURfBdUflr5VReI+NSaPL6xf+XLdONQgZgpYvUuZLVQ8dCqxpBAudaOM1+KpAUxw==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "bare-events": "^2.5.4", + "bare-path": "^3.0.0", + "bare-stream": "^2.6.4", + "bare-url": "^2.2.2", + "fast-fifo": "^1.3.2" + }, + "engines": { + "bare": ">=1.16.0" + }, + "peerDependencies": { + "bare-buffer": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + } + } + }, + "node_modules/bare-os": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.6.2.tgz", + "integrity": "sha512-T+V1+1srU2qYNBmJCXZkUY5vQ0B4FSlL3QDROnKQYOqeiQR8UbjNHlPa+TIbM4cuidiN9GaTaOZgSEgsvPbh5A==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "engines": { + "bare": ">=1.14.0" + } + }, + "node_modules/bare-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.0.0.tgz", + "integrity": "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "bare-os": "^3.0.1" + } + }, + "node_modules/bare-stream": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.7.0.tgz", + "integrity": "sha512-oyXQNicV1y8nc2aKffH+BUHFRXmx6VrPzlnaEvMhram0nPBrKcEdcyBg5r08D0i8VxngHFAiVyn1QKXpSG0B8A==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "streamx": "^2.21.0" + }, + "peerDependencies": { + "bare-buffer": "*", + "bare-events": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + }, + "bare-events": { + "optional": true + } + } + }, + "node_modules/bare-url": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.3.2.tgz", + "integrity": "sha512-ZMq4gd9ngV5aTMa5p9+UfY0b3skwhHELaDkhEHetMdX0LRkW9kzaym4oo/Eh+Ghm0CCDuMTsRIGM/ytUc1ZYmw==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "bare-path": "^3.0.0" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bl/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "license": "MIT" + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/color": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/color/-/color-5.0.3.tgz", + "integrity": "sha512-ezmVcLR3xAVp8kYOm4GS45ZLLgIE6SPAFoduLr6hTDajwb3KZ2F46gulK3XpcwRFb5KKGCSezCBAY4Dw4HsyXA==", + "license": "MIT", + "dependencies": { + "color-convert": "^3.1.3", + "color-string": "^2.1.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/color-string": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-2.1.4.tgz", + "integrity": "sha512-Bb6Cq8oq0IjDOe8wJmi4JeNn763Xs9cfrBcaylK1tPypWzyoy2G3l90v9k64kjphl/ZJjPIShFztenRomi8WTg==", + "license": "MIT", + "dependencies": { + "color-name": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/color-string/node_modules/color-name": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz", + "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==", + "license": "MIT", + "engines": { + "node": ">=12.20" + } + }, + "node_modules/color/node_modules/color-convert": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-3.1.3.tgz", + "integrity": "sha512-fasDH2ont2GqF5HpyO4w0+BcewlhHEZOFn9c1ckZdHpJ56Qb7MHhH/IcJZbBGgvdtwdwNbLvxiBEdg336iA9Sg==", + "license": "MIT", + "dependencies": { + "color-name": "^2.0.0" + }, + "engines": { + "node": ">=14.6" + } + }, + "node_modules/color/node_modules/color-name": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz", + "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==", + "license": "MIT", + "engines": { + "node": ">=12.20" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "license": "MIT" + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/dayjs": { + "version": "1.11.19", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.19.tgz", + "integrity": "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/dns-packet": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", + "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", + "license": "MIT", + "dependencies": { + "@leichtgewicht/ip-codec": "^2.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "readable-stream": "^2.0.2" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==", + "license": "MIT" + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/esbuild": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz", + "integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.2", + "@esbuild/android-arm": "0.27.2", + "@esbuild/android-arm64": "0.27.2", + "@esbuild/android-x64": "0.27.2", + "@esbuild/darwin-arm64": "0.27.2", + "@esbuild/darwin-x64": "0.27.2", + "@esbuild/freebsd-arm64": "0.27.2", + "@esbuild/freebsd-x64": "0.27.2", + "@esbuild/linux-arm": "0.27.2", + "@esbuild/linux-arm64": "0.27.2", + "@esbuild/linux-ia32": "0.27.2", + "@esbuild/linux-loong64": "0.27.2", + "@esbuild/linux-mips64el": "0.27.2", + "@esbuild/linux-ppc64": "0.27.2", + "@esbuild/linux-riscv64": "0.27.2", + "@esbuild/linux-s390x": "0.27.2", + "@esbuild/linux-x64": "0.27.2", + "@esbuild/netbsd-arm64": "0.27.2", + "@esbuild/netbsd-x64": "0.27.2", + "@esbuild/openbsd-arm64": "0.27.2", + "@esbuild/openbsd-x64": "0.27.2", + "@esbuild/openharmony-arm64": "0.27.2", + "@esbuild/sunos-x64": "0.27.2", + "@esbuild/win32-arm64": "0.27.2", + "@esbuild/win32-ia32": "0.27.2", + "@esbuild/win32-x64": "0.27.2" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.39.2", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.2.tgz", + "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.1", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.39.2", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "license": "MIT" + }, + "node_modules/events-universal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", + "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.7.0" + } + }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "dev": true, + "license": "(MIT OR WTFPL)", + "engines": { + "node": ">=6" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", + "license": "MIT" + }, + "node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "license": "MIT" + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/file-stream-rotator": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/file-stream-rotator/-/file-stream-rotator-0.6.1.tgz", + "integrity": "sha512-u+dBid4PvZw17PmDeRcNOtCP9CCK/9lRN2w+r1xIS7yOL9JFrIBKTvrYsxT4P0pGtThYTn++QS5ChHaUov3+zQ==", + "license": "MIT", + "dependencies": { + "moment": "^2.29.1" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "license": "ISC" + }, + "node_modules/fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==", + "license": "MIT" + }, + "node_modules/from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true, + "license": "MIT" + }, + "node_modules/fs-extra": { + "version": "11.3.3", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.3.tgz", + "integrity": "sha512-VWSRii4t0AFm6ixFFmLLx1t7wS1gh+ckoa84aOeapGum0h+EZd1EhEumSB+ZdDLnEPuucsVB9oB7cxJHap6Afg==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz", + "integrity": "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "dev": true, + "license": "MIT" + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "license": "ISC" + }, + "node_modules/into-stream": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-6.0.0.tgz", + "integrity": "sha512-XHbaOAvP+uFKUFsOgoNPRjLkwB+I22JFPFe5OjTkQ0nwgj6+pSjb4NmB6VMxaPshLiOf+zcpOCBQuLwC1KHhZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "from2": "^2.3.0", + "p-is-promise": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "license": "MIT" + }, + "node_modules/json-logic-js": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/json-logic-js/-/json-logic-js-2.0.5.tgz", + "integrity": "sha512-rTT2+lqcuUmj4DgWfmzupZqQDA64AdmYqizzMPWj3DxGdfFNsxPpcNVSaTj4l8W2tG/+hg7/mQhxjU3aPacO6g==", + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==", + "license": "MIT" + }, + "node_modules/ky": { + "version": "1.14.2", + "resolved": "https://registry.npmjs.org/ky/-/ky-1.14.2.tgz", + "integrity": "sha512-q3RBbsO5A5zrPhB6CaCS8ZUv+NWCXv6JJT4Em0i264G9W0fdPB8YRfnnEi7Dm7X7omAkBIPojzYJ2D1oHTHqug==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sindresorhus/ky?sponsor=1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "license": "MIT" + }, + "node_modules/logform": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz", + "integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==", + "license": "MIT", + "dependencies": { + "@colors/colors": "1.6.0", + "@types/triple-beam": "^1.3.2", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/mdns-server": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/mdns-server/-/mdns-server-1.0.11.tgz", + "integrity": "sha512-luzHnhQYmxaqrvYb8akwH2V/lSSyAumDoAK1zmogvpsj7sd4l04AF/LchBYH44NQVm7oo/KBVVFg3AbsJXr12w==", + "license": "MIT", + "dependencies": { + "dns-packet": "^5.2.2" + } + }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minizlib": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "dev": true, + "license": "MIT" + }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/multistream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/multistream/-/multistream-4.1.0.tgz", + "integrity": "sha512-J1XDiAmmNpRCBfIWJv+n0ymC4ABcf/Pl+5YvC5B/D2f/2+8PtHvCNxMPKiQcZyi922Hq69J2YOpb1pTywfifyw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "once": "^1.4.0", + "readable-stream": "^3.6.0" + } + }, + "node_modules/multistream/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/napi-build-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "license": "MIT" + }, + "node_modules/node-abi": { + "version": "3.86.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.86.0.tgz", + "integrity": "sha512-sn9Et4N3ynsetj3spsZR729DVlGH6iBG4RiDMV7HEp3guyOW6W3S0unGpLDxT50mXortGUMax/ykUNQXdqc/Xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-addon-api": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.3.0.tgz", + "integrity": "sha512-8VOpLHFrOQlAH+qA0ZzuGRlALRA6/LVh8QJldbrC4DY0hXoMP0l4Acq8TzFC018HztWiRqyCEj2aTWY2UvnJUg==", + "license": "MIT", + "engines": { + "node": "^18 || ^20 || >= 21" + } + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-gyp-build": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", + "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", + "license": "MIT", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/nrf-intel-hex": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/nrf-intel-hex/-/nrf-intel-hex-1.4.0.tgz", + "integrity": "sha512-q3+GGRIpe0VvCjUP1zaqW5rk6IpCZzhD0lu7Sguo1bgWwFcA9kZRjsaKUb0jBQMnefyOl5o0BBGAxvqMqYx8Sg==", + "license": "BSD", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "license": "MIT", + "dependencies": { + "fn.name": "1.x.x" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-is-promise": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-3.0.0.tgz", + "integrity": "sha512-Wo8VsW4IRQSKVXsJCn7TomUaVtyfjVDn3nUP7kE967BQk0CwFpdbZs0X0uk5sW9mkBa9eNM7hCMaG93WUAwxYQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-queue": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-8.1.1.tgz", + "integrity": "sha512-aNZ+VfjobsWryoiPnEApGGmf5WmNsCo9xu8dfaYamG5qaLP7ClhLN6NgsFe6SwJ2UbLEBK5dv9x8Mn5+RVhMWQ==", + "license": "MIT", + "dependencies": { + "eventemitter3": "^5.0.1", + "p-timeout": "^6.1.2" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-timeout": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-6.1.4.tgz", + "integrity": "sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg==", + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/prebuild-install": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^2.0.0", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/prebuild-install/node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true, + "license": "ISC" + }, + "node_modules/prebuild-install/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/prebuild-install/node_modules/tar-fs": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", + "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/prebuild-install/node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.0.tgz", + "integrity": "sha512-yEPsovQfpxYfgWNhCfECjG5AQaO+K3dp6XERmOepyPDVqcJm+bjyCVO3pmU+nAPe0N5dDvekfGezt/EIiRe1TA==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/pump": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", + "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/reflect-metadata": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", + "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", + "license": "Apache-2.0" + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/serialport": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/serialport/-/serialport-13.0.0.tgz", + "integrity": "sha512-PHpnTd8isMGPfFTZNCzOZp9m4mAJSNWle9Jxu6BPTcWq7YXl5qN7tp8Sgn0h+WIGcD6JFz5QDgixC2s4VW7vzg==", + "license": "MIT", + "dependencies": { + "@serialport/binding-mock": "10.2.2", + "@serialport/bindings-cpp": "13.0.0", + "@serialport/parser-byte-length": "13.0.0", + "@serialport/parser-cctalk": "13.0.0", + "@serialport/parser-delimiter": "13.0.0", + "@serialport/parser-inter-byte-timeout": "13.0.0", + "@serialport/parser-packet-length": "13.0.0", + "@serialport/parser-readline": "13.0.0", + "@serialport/parser-ready": "13.0.0", + "@serialport/parser-regex": "13.0.0", + "@serialport/parser-slip-encoder": "13.0.0", + "@serialport/parser-spacepacket": "13.0.0", + "@serialport/stream": "13.0.0", + "debug": "4.4.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/serialport/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC" + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/stream-meter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/stream-meter/-/stream-meter-1.0.4.tgz", + "integrity": "sha512-4sOEtrbgFotXwnEuzzsQBYEV1elAeFSO8rSGeTwabuX1RRn/kEq9JVH7I0MRBhKVRR0sJkr0M0QCH7yOLf9fhQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "^2.1.4" + } + }, + "node_modules/streamx": { + "version": "2.23.0", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.23.0.tgz", + "integrity": "sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "events-universal": "^1.0.0", + "fast-fifo": "^1.3.2", + "text-decoder": "^1.1.0" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tar": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.3.tgz", + "integrity": "sha512-ENg5JUHUm2rDD7IvKNFGzyElLXNjachNLp6RaGf4+JOgxXHkqA+gq81ZAMCUmtMtqBsoU62lcp6S27g1LCYGGQ==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.1.0", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/tar-fs": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.1.tgz", + "integrity": "sha512-LZA0oaPOc2fVo82Txf3gw+AkEd38szODlptMYejQUhndHMLQ9M059uXR+AfS7DNo0NpINvSqDsvyaCrBVkptWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0", + "tar-stream": "^3.1.5" + }, + "optionalDependencies": { + "bare-fs": "^4.0.1", + "bare-path": "^3.0.0" + } + }, + "node_modules/tar-stream": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", + "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, + "node_modules/text-decoder": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz", + "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.6.4" + } + }, + "node_modules/text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true, + "license": "MIT" + }, + "node_modules/triple-beam": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", + "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", + "license": "MIT", + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unzipper": { + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.12.3.tgz", + "integrity": "sha512-PZ8hTS+AqcGxsaQntl3IRBw65QrBI6lxzqDEL7IAo/XCEqRTKGfOX56Vea5TH9SZczRVxuzk1re04z/YjuYCJA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bluebird": "~3.7.2", + "duplexer2": "~0.1.4", + "fs-extra": "^11.2.0", + "graceful-fs": "^4.2.2", + "node-int64": "^0.4.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/winston": { + "version": "3.19.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.19.0.tgz", + "integrity": "sha512-LZNJgPzfKR+/J3cHkxcpHKpKKvGfDZVPS4hfJCc4cCG0CgYzvlD6yE/S3CIL/Yt91ak327YCpiF/0MyeZHEHKA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@colors/colors": "^1.6.0", + "@dabh/diagnostics": "^2.0.8", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.7.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.9.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston-daily-rotate-file": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/winston-daily-rotate-file/-/winston-daily-rotate-file-5.0.0.tgz", + "integrity": "sha512-JDjiXXkM5qvwY06733vf09I2wnMXpZEhxEVOSPenZMii+g7pcDcTBt2MRugnoi8BwVSuCT2jfRXBUy+n1Zz/Yw==", + "license": "MIT", + "dependencies": { + "file-stream-rotator": "^0.6.1", + "object-hash": "^3.0.0", + "triple-beam": "^1.4.1", + "winston-transport": "^4.7.0" + }, + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "winston": "^3" + } + }, + "node_modules/winston-transport": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz", + "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==", + "license": "MIT", + "dependencies": { + "logform": "^2.7.0", + "readable-stream": "^3.6.2", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston-transport/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/winston/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/ws": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", + "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zwave-js": { + "version": "15.20.0", + "resolved": "https://registry.npmjs.org/zwave-js/-/zwave-js-15.20.0.tgz", + "integrity": "sha512-1Kn5Kd0hxNl0h24RVbGOeuFxMr1KHFiD5b90A9YLyG9tmRSGHig4ISF0T13e0Q7AczTSHVluLib51aLDQg9SRw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@alcalzone/jsonl-db": "^4.0.2", + "@andrewbranch/untar.js": "^1.0.3", + "@homebridge/ciao": "^1.3.4", + "@zwave-js/cc": "15.20.0", + "@zwave-js/config": "15.20.0", + "@zwave-js/core": "15.20.0", + "@zwave-js/host": "15.20.0", + "@zwave-js/nvmedit": "15.20.0", + "@zwave-js/serial": "15.20.0", + "@zwave-js/shared": "15.20.0", + "@zwave-js/testing": "15.20.0", + "@zwave-js/waddle": "^1.2.1", + "alcalzone-shared": "^5.0.0", + "ansi-colors": "^4.1.3", + "ky": "^1.8.1", + "mdns-server": "^1.0.11", + "p-queue": "^8.1.0", + "pathe": "^2.0.3", + "reflect-metadata": "^0.2.2", + "semver": "^7.7.2", + "serialport": "^13.0.0", + "source-map-support": "^0.5.21", + "winston": "^3.18.3" + }, + "bin": { + "mock-server": "bin/mock-server.cjs" + }, + "engines": { + "node": ">= 20" + }, + "funding": { + "url": "https://github.com/sponsors/AlCalzone/" + } + } + } +} diff --git a/PSI/package.json b/PSI/package.json index f382dab..660428c 100644 --- a/PSI/package.json +++ b/PSI/package.json @@ -4,13 +4,13 @@ "bin": "./server.js", "dependencies": { "@zwave-js/server": "^3.5.0", - "zwave-js": "^15.17.1" + "zwave-js": "^15.20.0" }, "devDependencies": { - "@yao-pkg/pkg": "^6.10.1", - "esbuild": "^0.27.0", - "eslint": "^9.39.1", - "prettier": "^3.7.2" + "@yao-pkg/pkg": "^6.12.0", + "esbuild": "^0.27.2", + "eslint": "^9.39.2", + "prettier": "^3.8.0" }, "scripts": { "build": "npm run do_esbuild && npm run do_pkgbuild", diff --git a/Visual Studio Projects/ZWaveJS.NET/.idea/.idea.ZWaveJS.NET/.idea/encodings.xml b/Visual Studio Projects/ZWaveJS.NET/.idea/.idea.ZWaveJS.NET/.idea/encodings.xml deleted file mode 100644 index df87cf9..0000000 --- a/Visual Studio Projects/ZWaveJS.NET/.idea/.idea.ZWaveJS.NET/.idea/encodings.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/Visual Studio Projects/ZWaveJS.NET/.idea/.idea.ZWaveJS.NET/.idea/indexLayout.xml b/Visual Studio Projects/ZWaveJS.NET/.idea/.idea.ZWaveJS.NET/.idea/indexLayout.xml deleted file mode 100644 index 7b08163..0000000 --- a/Visual Studio Projects/ZWaveJS.NET/.idea/.idea.ZWaveJS.NET/.idea/indexLayout.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/Visual Studio Projects/ZWaveJS.NET/.idea/.idea.ZWaveJS.NET/.idea/projectSettingsUpdater.xml b/Visual Studio Projects/ZWaveJS.NET/.idea/.idea.ZWaveJS.NET/.idea/projectSettingsUpdater.xml deleted file mode 100644 index ef20cb0..0000000 --- a/Visual Studio Projects/ZWaveJS.NET/.idea/.idea.ZWaveJS.NET/.idea/projectSettingsUpdater.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - \ No newline at end of file diff --git a/Visual Studio Projects/ZWaveJS.NET/.idea/.idea.ZWaveJS.NET/.idea/vcs.xml b/Visual Studio Projects/ZWaveJS.NET/.idea/.idea.ZWaveJS.NET/.idea/vcs.xml deleted file mode 100644 index b2bdec2..0000000 --- a/Visual Studio Projects/ZWaveJS.NET/.idea/.idea.ZWaveJS.NET/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/Visual Studio Projects/ZWaveJS.NET/.idea/.idea.ZWaveJS.NET/.idea/workspace.xml b/Visual Studio Projects/ZWaveJS.NET/.idea/.idea.ZWaveJS.NET/.idea/workspace.xml deleted file mode 100644 index 1c806b9..0000000 --- a/Visual Studio Projects/ZWaveJS.NET/.idea/.idea.ZWaveJS.NET/.idea/workspace.xml +++ /dev/null @@ -1,89 +0,0 @@ - - - - - - - - - - - - - - - - - - - { - "customColor": "", - "associatedIndex": 5 -} - - - - - - - - 1760176866190 - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/ConfigManager.cs b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/ConfigManager.cs index 734e932..edd39f9 100644 --- a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/ConfigManager.cs +++ b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/ConfigManager.cs @@ -35,7 +35,7 @@ internal ConfigManager(Driver Driver) } - // CHECKED + // Checked as of : 3.5.0 public Task LookupDevice(int ManufacturerID, int ProductTypeID, int ProductId) { @@ -65,7 +65,7 @@ public Task LookupDevice(int ManufacturerID, int ProductTypeID, int P return Result.Task; } - // CHECKED + // Checked as of : 3.5.0 public Task LookupManufacturer(int ManufacturerID) { Guid ID = Guid.NewGuid(); diff --git a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Utils.cs b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Utils.cs index 61eb1f1..8d9f4b7 100644 --- a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Utils.cs +++ b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Utils.cs @@ -16,7 +16,7 @@ internal Utils(Driver Driver) _driver = Driver; } - // CHECKED + // Checked as of : 3.5.0 public Task ParseQRCodeString(string QR) { Guid ID = Guid.NewGuid(); diff --git a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/ZWaveNode.cs b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/ZWaveNode.cs index d9f67a3..d53ed43 100644 --- a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/ZWaveNode.cs +++ b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/ZWaveNode.cs @@ -427,7 +427,7 @@ public Task PollValue(ValueID ValueID) return Result.Task; } - // CHECKED - Variant 1: Normal parameter, defined in a config file + // Checked as of : 3.5.0 - Variant 1: Normal parameter, defined in a config file public Task ZWJSS_SetRawConfigParameterValue(int Parameter, int Value) { Guid ID = Guid.NewGuid(); @@ -453,7 +453,7 @@ public Task ZWJSS_SetRawConfigParameterValue(int Parameter, int Value return Result.Task; } - // CHECKED - Variant 2: Normal parameter, not defined in a config file + // Checked as of : 3.5.0 - Variant 2: Normal parameter, not defined in a config file public Task ZWJSS_SetRawConfigParameterValue(int Parameter, int Value, int ValueSize, Enums.ConfigValueFormat ValueFormat) { Guid ID = Guid.NewGuid(); @@ -481,7 +481,7 @@ public Task ZWJSS_SetRawConfigParameterValue(int Parameter, int Value return Result.Task; } - // CHECKED - Variant 3: Partial parameter, must be defined in a config file + // Checked as of : 3.5.0 - Variant 3: Partial parameter, must be defined in a config file public Task ZWJSS_SetRawConfigParameterValue(int Parameter, int Bitmask, int Value) { Guid ID = Guid.NewGuid(); @@ -508,7 +508,7 @@ public Task ZWJSS_SetRawConfigParameterValue(int Parameter, int Bitma return Result.Task; } - // CHECKED + // Checked as of : 3.5.0 public Task RefreshValues() { Guid ID = Guid.NewGuid(); @@ -532,7 +532,7 @@ public Task RefreshValues() return Result.Task; } - // CHECKED + // Checked as of : 3.5.0 public Task RefreshCCValues(int CommandClass) { Guid ID = Guid.NewGuid(); @@ -557,7 +557,7 @@ public Task RefreshCCValues(int CommandClass) return Result.Task; } - // CHEKCED + // Checked as of : 3.5.0 public Task GetDefinedValueIDs() { Guid ID = Guid.NewGuid(); @@ -586,7 +586,7 @@ public Task GetDefinedValueIDs() return Result.Task; } - // CHECKED + // Checked as of : 3.5.0 public Task GetValueMetadata(ValueID VID) { Guid ID = Guid.NewGuid(); @@ -617,7 +617,7 @@ public Task GetValueMetadata(ValueID VID) return Result.Task; } - // CHECKED + // Checked as of : 3.5.0 public Task SupportsCCAPI(int CommandClass) { Guid ID = Guid.NewGuid(); @@ -646,7 +646,7 @@ public Task SupportsCCAPI(int CommandClass) return Result.Task; } - // CHECKED + // Checked as of : 3.5.0 public Task InvokeCCAPI(int CommandClass, string Method, params object[] Params) { Guid ID = Guid.NewGuid(); @@ -677,14 +677,9 @@ public Task InvokeCCAPI(int CommandClass, string Method, params objec return Result.Task; } - // LOCAL - public Endpoint GetEndpoint(int Index) - { - Endpoint EP = this.endpoints.FirstOrDefault((E) => E.index.Equals(Index)); - return EP; - } + - // CHECKED + // Checked as of : 3.5.0 public Task GetEndpointCount() { Guid ID = Guid.NewGuid(); @@ -712,7 +707,7 @@ public Task GetEndpointCount() return Result.Task; } - // CHECKED + // Checked as of : 3.5.0 public Task GetHighestSecurityClass() { Guid ID = Guid.NewGuid(); @@ -743,7 +738,7 @@ public Task GetHighestSecurityClass() return Result.Task; } - // CHECKED + // Checked as of : 3.5.0 public Task HasSecurityClass(Enums.SecurityClass Class) { Guid ID = Guid.NewGuid(); @@ -773,7 +768,7 @@ public Task HasSecurityClass(Enums.SecurityClass Class) return Result.Task; } - // CHECKED + // Checked as of : 3.5.0 public Task WaitForWakeup() { Guid ID = Guid.NewGuid(); @@ -798,7 +793,7 @@ public Task WaitForWakeup() return Result.Task; } - // CHECKED + // Checked as of : 3.5.0 public Task ManuallyIdleNotificationValue(ValueID VID) { Guid ID = Guid.NewGuid(); @@ -824,7 +819,7 @@ public Task ManuallyIdleNotificationValue(ValueID VID) return Result.Task; } - // CHECKED + // Checked as of : 3.5.0 public Task ManuallyIdleNotificationValue(int notificationType, int prevValue, int? endpointIndex = null) { Guid ID = Guid.NewGuid(); @@ -855,6 +850,13 @@ public Task ManuallyIdleNotificationValue(int notificationType, int p return Result.Task; } + // LOCAL + public Endpoint GetEndpoint(int Index) + { + Endpoint EP = this.endpoints.FirstOrDefault((E) => E.index.Equals(Index)); + return EP; + } + [Newtonsoft.Json.JsonProperty] public Endpoint[] endpoints { get; internal set; } [Newtonsoft.Json.JsonProperty] From 963d08dfa448b21dbcf6eeee5854bd721348ecb0 Mon Sep 17 00:00:00 2001 From: Marcus Davies <55892693+marcus-j-davies@users.noreply.github.com> Date: Tue, 20 Jan 2026 20:01:47 +0000 Subject: [PATCH 05/59] Remove semver --- .../ZWaveJS.NET/ZWaveJS.NET/ZWaveJS.NET.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/ZWaveJS.NET.csproj b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/ZWaveJS.NET.csproj index 205dd0e..ec4d190 100644 --- a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/ZWaveJS.NET.csproj +++ b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/ZWaveJS.NET.csproj @@ -27,7 +27,6 @@ - From e6cebf706263b9111c385a2bceaf3dfaedca2545 Mon Sep 17 00:00:00 2001 From: Marcus Davies <55892693+marcus-j-davies@users.noreply.github.com> Date: Tue, 20 Jan 2026 20:24:26 +0000 Subject: [PATCH 06/59] Cleanup re-connect logic --- .../ZWaveJS.NET/ZWaveJS.NET/Driver.cs | 21 +++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Driver.cs b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Driver.cs index e8c3289..0496f40 100644 --- a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Driver.cs +++ b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Driver.cs @@ -660,26 +660,21 @@ private void InternalPrep() { if (!RequestedExit) { - if (LastError == DateTime.MinValue || (DateTime.Now - LastError).TotalMilliseconds > ServerErrorThrottleTime) + DateTime now = DateTime.UtcNow; + if (LastError == DateTime.MinValue || (now - LastError).TotalMilliseconds > ServerErrorThrottleTime) { - LastError = DateTime.Now; - + LastError = now; + string message = DisconnectionInfo?.Exception?.Message ?? "Unknown error"; if (!Inited) - { - StartUpError?.Invoke($"Could not connect to the server, Connection will continue to try: {DisconnectionInfo?.Exception?.Message}"); - } + StartUpError?.Invoke($"Could not connect to the server. Connection will continue to try: {message}"); else - { - ConnectionLost?.Invoke($"Connection to the server was lost. Connection will attempt to be restored: {DisconnectionInfo?.Exception?.Message}"); - } + ConnectionLost?.Invoke($"Connection to the server was lost. Attempting to restore: {message}"); } } - - }); - ClientWebSocket.ReconnectTimeout = null; - ClientWebSocket.ErrorReconnectTimeout = TimeSpan.FromSeconds(1); + ClientWebSocket.ReconnectTimeout = null; // Dont attempt to reconnect when quite + ClientWebSocket.ErrorReconnectTimeout = TimeSpan.FromSeconds(3); } From fe983fd88c0972d6157f4a1416b57fc3e2179606 Mon Sep 17 00:00:00 2001 From: Marcus Davies <55892693+marcus-j-davies@users.noreply.github.com> Date: Tue, 20 Jan 2026 21:13:44 +0000 Subject: [PATCH 07/59] Further clean up (fixes) --- .../ZWaveJS.NET/ZWaveJS.NET/Driver.cs | 13 +++++++------ .../ZWaveJS.NET/ZWaveJS.NET/Server.cs | 2 +- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Driver.cs b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Driver.cs index 0496f40..8f4995e 100644 --- a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Driver.cs +++ b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Driver.cs @@ -678,11 +678,9 @@ private void InternalPrep() } - // OBSOLETE - // Server Process Exit + // Server Process Exit Unexpected private void Server_Exited() { - if (!RequestedExit) { Inited = false; @@ -690,8 +688,7 @@ private void Server_Exited() Controller = null; DestroySocket(); - SettleCallbacksError(); - + if(UnexpectedHostExit != null) { if (UnexpectedHostExit.Invoke()) @@ -699,6 +696,10 @@ private void Server_Exited() Restart(); } } + else + { + SettleCallbacksError(); + } } @@ -791,7 +792,7 @@ private void Server_FatalError() Controller = null; DestroySocket(); DestroyServer(); - StartUpError?.Invoke("Fatal ZWave Server Error."); + StartUpError?.Invoke("Fatal ZWaveJS Server (OR Driver) Error."); } private void SetAPIVersionCB(JObject JO) diff --git a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Server.cs b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Server.cs index a23c5a4..09689e6 100644 --- a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Server.cs +++ b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Server.cs @@ -79,7 +79,7 @@ internal void Start(string SerialPort, ZWaveOptions Config, int WSPort) private void ServerProcess_Exited(object sender, EventArgs e) { - // Exited?.Invoke(); I think this will be indirectly handled by the socket client now + Exited?.Invoke(); ServerProcess.Dispose(); } From 9d945d14b9aa21da9a62eb32b4b4330089c9842b Mon Sep 17 00:00:00 2001 From: Marcus Davies <55892693+marcus-j-davies@users.noreply.github.com> Date: Wed, 21 Jan 2026 19:47:24 +0000 Subject: [PATCH 08/59] Re-engineer Error Handling --- .../ZWaveJS.NET/ZWaveJS.NET/Driver.cs | 74 +++++++++---------- .../ZWaveJS.NET/ZWaveJS.NET/Enums.cs | 20 ++--- 2 files changed, 48 insertions(+), 46 deletions(-) diff --git a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Driver.cs b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Driver.cs index 8f4995e..2575eae 100644 --- a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Driver.cs +++ b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Driver.cs @@ -53,7 +53,6 @@ public string ZWJSS_ServerVersion public int ServerCommunicationPort { get; private set; } public int ServerErrorThrottleTime { get; private set; } - private DateTime LastError; public Controller Controller { get; internal set; } public Utils Utils { get; internal set; } @@ -62,15 +61,9 @@ public string ZWJSS_ServerVersion public delegate void DriverReadyEvent(); public event DriverReadyEvent DriverReady; - public delegate void StartupErrorEvent(string Message); - public event StartupErrorEvent StartUpError; - - public delegate void ConnectionLostEvent(string Message); - public event ConnectionLostEvent ConnectionLost; - - - public delegate bool UnexpectedHostExitEvent(); - public event UnexpectedHostExitEvent UnexpectedHostExit; + // NEW + public delegate void ServerConnectionErrorEvent(string ErrorCode, string message, Action Retry); + public event ServerConnectionErrorEvent ServerConnectionError; public delegate void LoggingEventDelegate(LoggingEventArgs args); public event LoggingEventDelegate ZWJSS_LoggingEvent; @@ -645,10 +638,14 @@ private void InternalPrep() var Factory = new Func(() => new ClientWebSocket { - Options = { KeepAliveInterval = TimeSpan.FromSeconds(5) } + Options = { + KeepAliveInterval = TimeSpan.FromSeconds(5), + RemoteCertificateValidationCallback = (sender, cert, chain, errors) => true + } }); ClientWebSocket = new Websocket.Client.WebsocketClient(this.WSAddress, Factory); + ClientWebSocket.ConnectTimeout = TimeSpan.FromSeconds(15); ClientWebSocket.MessageReceived.Subscribe((Message) => @@ -658,23 +655,24 @@ private void InternalPrep() ClientWebSocket.DisconnectionHappened.Subscribe((DisconnectionInfo) => { - if (!RequestedExit) + if (!RequestedExit && DisconnectionInfo.Type == DisconnectionType.Error) { - DateTime now = DateTime.UtcNow; - if (LastError == DateTime.MinValue || (now - LastError).TotalMilliseconds > ServerErrorThrottleTime) - { - LastError = now; - string message = DisconnectionInfo?.Exception?.Message ?? "Unknown error"; - if (!Inited) - StartUpError?.Invoke($"Could not connect to the server. Connection will continue to try: {message}"); - else - ConnectionLost?.Invoke($"Connection to the server was lost. Attempting to restore: {message}"); - } + ServerConnectionError?.Invoke(Enums.ErrorCodes.WSConnectionTimout,"Could not connect to the ZWaveJS Websocket (timeout)", (retry, timeout) => + { + if (retry) + { + if (timeout.HasValue && timeout.Value > 0) + { + ClientWebSocket.ConnectTimeout = TimeSpan.FromSeconds(timeout.Value); + } + ClientWebSocket.Reconnect(); + } + }); } }); ClientWebSocket.ReconnectTimeout = null; // Dont attempt to reconnect when quite - ClientWebSocket.ErrorReconnectTimeout = TimeSpan.FromSeconds(3); + ClientWebSocket.ErrorReconnectTimeout = null; } @@ -688,19 +686,21 @@ private void Server_Exited() Controller = null; DestroySocket(); - - if(UnexpectedHostExit != null) - { - if (UnexpectedHostExit.Invoke()) - { - Restart(); - } - } - else + + ServerConnectionError?.Invoke(Enums.ErrorCodes.Unknown,"The Server process unexpectedly terminted.", (retry, timeout) => { - SettleCallbacksError(); - } + SettleCallbacksError(); + + if (retry) + { + if (timeout.HasValue && timeout.Value > 0) + { + ClientWebSocket.ConnectTimeout = TimeSpan.FromSeconds(timeout.Value); + } + Restart(); + } + }); } } @@ -767,7 +767,7 @@ private void SettleCallbacksError() { JObject JO = new JObject(); JO.Add("success", false); - JO.Add("zwaveErrorCode", Enums.ErrorCodes.WSConnectionError); + JO.Add("zwaveErrorCode", Enums.ErrorCodes.Unknown); JO.Add("zwaveErrorMessage", "The Server process unexpectedly terminted. It is unknown if the command was successfull, assuming false. Subscribe to the 'UnexpectedHostExit' event of the driver to restart the Driver Runtime"); // Guard against race condition @@ -792,7 +792,7 @@ private void Server_FatalError() Controller = null; DestroySocket(); DestroyServer(); - StartUpError?.Invoke("Fatal ZWaveJS Server (OR Driver) Error."); + ServerConnectionError?.Invoke(Enums.ErrorCodes.StartUpError,"Fatal ZWaveJS Server (OR Driver) Error.", null); } private void SetAPIVersionCB(JObject JO) @@ -819,7 +819,7 @@ private void SetAPIVersionCB(JObject JO) RequestedExit = true; DestroySocket(); DestroyServer(); - StartUpError?.Invoke("Client and Server schema mismatch"); + ServerConnectionError?.Invoke(Enums.ErrorCodes.SchemaMisMatch,"Client and Server schema mismatch",null); break; } diff --git a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Enums.cs b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Enums.cs index 67060bc..2c6ebf7 100644 --- a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Enums.cs +++ b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Enums.cs @@ -105,17 +105,19 @@ public enum SecurityBootstrapFailure Unknown } - - internal class ErrorCodes { - public const string MissingS2Callbacks = "ZWJS.NET.ERR.001"; - public const string InvalidStrategy = "ZWJS.NET.ERR.002"; - public const string MissingKeys = "ZWJS.NET.ERR.003"; - public const string InvalidkeyLength = "ZWJS.NET.ERR.004"; - public const string WSConnectionError = "ZWJS.NET.ERR.005"; - public const string CommercialAPIKey = "ZWJS.NET.ERR.006"; - public const string WrongOverride = "ZWJS.NET.ERR.007"; + public const string Unknown = "ZWDNET-ER-00"; + public const string WSConnectionTimout = "ZWDNET-ER-01"; + public const string SchemaMisMatch = "ZWDNET-ER-02"; + public const string StartUpError = "ZWDNET-ER-03"; + + public const string MissingS2Callbacks = "ZWDNET-ER-04"; + public const string InvalidStrategy = "ZWDNET-ER-05"; + public const string MissingKeys = "ZWDNET-ER-06"; + public const string InvalidkeyLength = "ZWDNET-ER-07"; + public const string CommercialAPIKey = "ZWDNET-ER-08"; + public const string WrongOverride = "ZWDNET-ER-09"; } internal class Commands From 38c6dc2c746ab4816cadb1dd844d1f46d01e83a3 Mon Sep 17 00:00:00 2001 From: Marcus Davies <55892693+marcus-j-davies@users.noreply.github.com> Date: Wed, 21 Jan 2026 21:27:33 +0000 Subject: [PATCH 09/59] Threading --- CHANGELOG.md | 58 ++++- .../ZWaveJS.NET/ZWaveJS.NET/Driver.cs | 241 +++++++++--------- .../ZWaveJS.NET/ZWaveJS.NET.csproj | 2 +- 3 files changed, 176 insertions(+), 125 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f13ec4..db06aa8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,62 @@ +- v5.0.0 + + - Versions + - ZWave JS Driver Version: 15.20.0 + - ZWave JS Server Version: 3.5.0 (Schema Version 44) + + - Breaking Changes + - Dropped frameworks, the following frameworks are now as follows: + - net6.0 + - net7.0 + - net8.0 + - net9.0 + - netstandard2.1 + + - Driver class init signature changes + - ```public Driver(string SerialPort, ZWaveOptions Options, int ServerCommunicationPort = 50001)``` + - ```public Driver(Uri Server, int SchemaVersion = 0)``` + + - Re-engineered error/connection handling. + All Driver/Server error handling, is now handled through the **ServerConnectionError** event. + This event has the following signature: + + ```csharp + ServerConnectionError(string ErrorCode, string Message, Action Retry) + ``` + + The possible error codes via this event are as follows: + + ```csharp + ZWDNET-ER-00 : Unknown Error (Supports Retry) + ZWDNET-ER-01 : Connection Timeout (Supports Retry) + ZWDNET-ER-02 : Schema Mismatch + ZWDNET-ER-03 : Fatal Error During Server Start up + ``` + + Where the error code, supports a retry, ```Retry``` will not be ```null``` + Arguments: **Should Retry**, **New Timeout Value** (if <1, defaults to 15s) + + - All interaction error codes have been updated + ```csharp + ZWDNET-ER-04 : S2 Call backs missing + ZWDNET-ER-05 : Invalid Strategy + ZWDNET-ER-06 : Missing Security Keys + ZWDNET-ER-07 : Invalid Key Length + ZWDNET-ER-08 : Missing API key (Commercial use) + ZWDNET-ER-09 : Use of incorrect override + ``` + + - Internal Chnages. + - All responses to method calls are now dispatched asynchronously on the thread pool, so user code triggered by these responses cannot block the WebSocket message handler. + + + + - v4.0.0 - Versions - - ZWave JS Driver Version: 12.2.1 - - ZWave JS Server Version: 1.33.0 (Schema Version 33) + - ZWave JS Driver Version: 15.20.0 + - ZWave JS Server Version: 3.5.0 (Schema Version 44) - Breaking Changes - Removed support for **NET45** diff --git a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Driver.cs b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Driver.cs index 2575eae..3fb948e 100644 --- a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Driver.cs +++ b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Driver.cs @@ -32,7 +32,7 @@ public class Driver private Uri WSAddress; private bool Host = true; private Server _server; - + private string _ZWaveJSDriverVersion; public string ZWJSS_DriverVersion { @@ -52,7 +52,6 @@ public string ZWJSS_ServerVersion } public int ServerCommunicationPort { get; private set; } - public int ServerErrorThrottleTime { get; private set; } public Controller Controller { get; internal set; } public Utils Utils { get; internal set; } @@ -61,10 +60,9 @@ public string ZWJSS_ServerVersion public delegate void DriverReadyEvent(); public event DriverReadyEvent DriverReady; - // NEW - public delegate void ServerConnectionErrorEvent(string ErrorCode, string message, Action Retry); + public delegate void ServerConnectionErrorEvent(string ErrorCode, string Message, Action Retry); public event ServerConnectionErrorEvent ServerConnectionError; - + public delegate void LoggingEventDelegate(LoggingEventArgs args); public event LoggingEventDelegate ZWJSS_LoggingEvent; internal void Trigger_LoggingEvent(LoggingEventArgs args) @@ -74,18 +72,18 @@ internal void Trigger_LoggingEvent(LoggingEventArgs args) private void MapNodeEvents() { - + NodeEventMap.Add("node info received", (JO) => { int NID = JO.SelectToken("event.nodeId").ToObject(); ZWaveNode N = this.Controller.Nodes.Get(NID); - Task.Run(() => + _ = Task.Run(() => { N.Trigger_NodeInfo(); }); }); - + NodeEventMap.Add("check lifeline health progress", (JO) => { int NID = JO.SelectToken("event.nodeId").ToObject(); @@ -95,7 +93,7 @@ private void MapNodeEvents() ZWaveNode N = this.Controller.Nodes.Get(NID); - Task.Run(() => + _ = Task.Run(() => { N.Trigger_LifelineHealthCheckProgress(Round, Total, LastRating); }); @@ -107,7 +105,7 @@ private void MapNodeEvents() NodeStatisticsUpdatedArgs NS = JO.SelectToken("event.statistics").ToObject(); ZWaveNode N = this.Controller.Nodes.Get(NID); - Task.Run(() => + _ = Task.Run(() => { N.Trigger_StatisticsUpdated(NS); }); @@ -119,7 +117,7 @@ private void MapNodeEvents() NodeFirmwareUpdateResultArgs Result = JO.SelectToken("event.result").ToObject(); ZWaveNode N = this.Controller.Nodes.Get(NID); - Task.Run(() => + _ = Task.Run(() => { N.Trigger_FirmwareUpdateFinished(Result); }); @@ -130,10 +128,10 @@ private void MapNodeEvents() NodeEventMap.Add("firmware update progress", (JO) => { int NID = JO.SelectToken("event.nodeId").Value(); - NodeFirmwareUpdateProgressArgs Progress = JO.SelectToken("event.progress").ToObject(); + NodeFirmwareUpdateProgressArgs Progress = JO.SelectToken("event.progress").ToObject(); ZWaveNode N = this.Controller.Nodes.Get(NID); - Task.Run(() => + _ = Task.Run(() => { N.Trigger_FirmwareUpdateProgress(Progress); }); @@ -145,7 +143,7 @@ private void MapNodeEvents() ValueUpdatedArgs Args = JO.SelectToken("event.args").ToObject(); ZWaveNode N = this.Controller.Nodes.Get(NID); - Task.Run(() => + _ = Task.Run(() => { N.Trigger_ValueUpdated(Args); }); @@ -157,7 +155,7 @@ private void MapNodeEvents() ValueAddedArgs Args = JO.SelectToken("event.args").ToObject(); ZWaveNode N = this.Controller.Nodes.Get(NID); - Task.Run(() => + _ = Task.Run(() => { N.Trigger_ValueAdded(Args); }); @@ -169,7 +167,7 @@ private void MapNodeEvents() ValueRemovedArgs Args = JO.SelectToken("event.args").ToObject(); ZWaveNode N = this.Controller.Nodes.Get(NID); - Task.Run(() => + _ = Task.Run(() => { N.Trigger_ValueRemoved(Args); }); @@ -181,12 +179,12 @@ private void MapNodeEvents() ValueNotificationArgs Args = JO.SelectToken("event.args").ToObject(); ZWaveNode N = this.Controller.Nodes.Get(NID); - Task.Run(() => + _ = Task.Run(() => { N.Trigger_ValueNotification(Args); }); }); - + NodeEventMap.Add("notification", (JO) => { int NID = JO.SelectToken("event.nodeId").ToObject(); @@ -194,7 +192,7 @@ private void MapNodeEvents() JObject IJO = JO.SelectToken("event.args").ToObject(); ZWaveNode N = this.Controller.Nodes.Get(NID); - Task.Run(() => + _ = Task.Run(() => { N.Trigger_Notification(CCID, IJO); }); @@ -205,7 +203,7 @@ private void MapNodeEvents() int NID = JO.SelectToken("event.nodeId").ToObject(); ZWaveNode N = this.Controller.Nodes.Get(NID); - Task.Run(() => + _ = Task.Run(() => { N.Trigger_NodeAlive(); }); @@ -216,7 +214,7 @@ private void MapNodeEvents() int NID = JO.SelectToken("event.nodeId").ToObject(); ZWaveNode N = this.Controller.Nodes.Get(NID); - Task.Run(() => + _ = Task.Run(() => { N.Trigger_NodeDead(); }); @@ -227,7 +225,7 @@ private void MapNodeEvents() int NID = JO.SelectToken("event.nodeId").ToObject(); ZWaveNode N = this.Controller.Nodes.Get(NID); - Task.Run(() => + _ = Task.Run(() => { N.Trigger_NodeAwake(); }); @@ -238,7 +236,7 @@ private void MapNodeEvents() int NID = JO.SelectToken("event.nodeId").ToObject(); ZWaveNode N = this.Controller.Nodes.Get(NID); - Task.Run(() => + _ = Task.Run(() => { N.Trigger_NodeAsleep(); }); @@ -252,7 +250,7 @@ private void MapNodeEvents() ZWaveNode N = this.Controller.Nodes.Get(NID); this.Controller.Nodes.ReplaceInformation(NNI, N); - Task.Run(() => + _ = Task.Run(() => { N.Trigger_NodeReady(); }); @@ -263,7 +261,7 @@ private void MapNodeEvents() int NID = JO.SelectToken("event.nodeId").ToObject(); ZWaveNode N = this.Controller.Nodes.Get(NID); - Task.Run(() => + _ = Task.Run(() => { N.Trigger_NodeInterviewStarted(); }); @@ -274,7 +272,7 @@ private void MapNodeEvents() int NID = JO.SelectToken("event.nodeId").ToObject(); ZWaveNode N = this.Controller.Nodes.Get(NID); - Task.Run(() => + _ = Task.Run(() => { N.Trigger_NodeInterviewCompleted(); }); @@ -286,23 +284,23 @@ private void MapNodeEvents() NodeInterviewFailedEventArgs FII = JO.SelectToken("event.args").ToObject(); ZWaveNode N = this.Controller.Nodes.Get(NID); - Task.Run(() => + _ = Task.Run(() => { N.Trigger_NodeInterviewFailed(FII); }); }); - NodeEventMap.Add("metadata updated", (JO) => - { - int NID = JO.SelectToken("event.nodeId").Value(); - MetadataUpdatedArgs Args = JO.SelectToken("event.args").ToObject(); - ZWaveNode N = this.Controller.Nodes.Get(NID); + NodeEventMap.Add("metadata updated", (JO) => + { + int NID = JO.SelectToken("event.nodeId").Value(); + MetadataUpdatedArgs Args = JO.SelectToken("event.args").ToObject(); + ZWaveNode N = this.Controller.Nodes.Get(NID); - Task.Run(() => - { - N.Trigger_MetadataUpdated(Args); - }); - }); + _ = Task.Run(() => + { + N.Trigger_MetadataUpdated(Args); + }); + }); } private void MapControllerEvents() @@ -310,8 +308,8 @@ private void MapControllerEvents() ControllerEventMap.Add("firmware update finished", (JO) => { ControllerFirmwareUpdateResultArgs Result = JO.SelectToken("event.result").ToObject(); - - Task.Run(() => + + _ = Task.Run(() => { this.Controller.Trigger_FirmwareUpdateFinished(Result); }); @@ -322,8 +320,8 @@ private void MapControllerEvents() ControllerEventMap.Add("firmware update progress", (JO) => { ControllerFirmwareUpdateProgressArgs Progress = JO.SelectToken("event.progress").ToObject(); - - Task.Run(() => + + _ = Task.Run(() => { this.Controller.Trigger_FirmwareUpdateProgress(Progress); }); @@ -333,8 +331,8 @@ private void MapControllerEvents() { int Read = JO.SelectToken("event.bytesRead").ToObject(); int Total = JO.SelectToken("event.total").ToObject(); - - Task.Run(() => + + _ = Task.Run(() => { this.Controller.Trigger_BackupNVMProgress(Read, Total); }); @@ -344,8 +342,8 @@ private void MapControllerEvents() { int Read = JO.SelectToken("event.bytesRead").ToObject(); int Total = JO.SelectToken("event.total").ToObject(); - - Task.Run(() => + + _ = Task.Run(() => { this.Controller.Trigger_ConvertRestoreNVMProgress(Read, Total); }); @@ -355,8 +353,8 @@ private void MapControllerEvents() { int Written = JO.SelectToken("event.bytesWritten").ToObject(); int Total = JO.SelectToken("event.total").ToObject(); - - Task.Run(() => + + _ = Task.Run(() => { this.Controller.Trigger_RestoreNVMProgressSub(Written, Total); }); @@ -365,8 +363,8 @@ private void MapControllerEvents() ControllerEventMap.Add("statistics updated", (JO) => { ControllerStatisticsUpdatedArgs CS = JO.SelectToken("event.statistics").ToObject(); - - Task.Run(() => + + _ = Task.Run(() => { this.Controller.Trigger_StatisticsUpdated(CS); }); @@ -374,7 +372,7 @@ private void MapControllerEvents() ControllerEventMap.Add("inclusion aborted", (JO) => { - Task.Run(() => + _ = Task.Run(() => { this.Controller.Trigger_InclusionAborted(); }); @@ -383,8 +381,8 @@ private void MapControllerEvents() ControllerEventMap.Add("inclusion started", (JO) => { bool Secure = (JO.SelectToken("event.strategy").ToObject() != Enums.InclusionStrategy.Insecure); - - Task.Run(() => + + _ = Task.Run(() => { this.Controller.Trigger_InclusionStarted(Secure); }); @@ -392,7 +390,7 @@ private void MapControllerEvents() ControllerEventMap.Add("inclusion stopped", (JO) => { - Task.Run(() => + _ = Task.Run(() => { this.Controller.Trigger_InclusionStopped(); }); @@ -400,7 +398,7 @@ private void MapControllerEvents() ControllerEventMap.Add("exclusion started", (JO) => { - Task.Run(() => + _ = Task.Run(() => { this.Controller.Trigger_ExclusionStarted(); }); @@ -408,7 +406,7 @@ private void MapControllerEvents() ControllerEventMap.Add("exclusion stopped", (JO) => { - Task.Run(() => + _ = Task.Run(() => { this.Controller.Trigger_ExclusionStopped(); }); @@ -418,16 +416,16 @@ private void MapControllerEvents() { int NID = JO.SelectToken("event.node.nodeId").ToObject(); Enums.RemoveNodeReason Reason = JO.SelectToken("event.reason").ToObject(); - + ZWaveNode N = this.Controller.Nodes.Get(NID); this.Controller.Nodes.RemoveNodeFromCollection(NID); - - Task.Run(() => + + _ = Task.Run(() => { this.Controller.Trigger_NodeRemoved(N, Reason); }); - + }); ControllerEventMap.Add("node added", (JO) => @@ -439,19 +437,19 @@ private void MapControllerEvents() NN.id = NID; this.Controller.Nodes.AddNodeToCollection(NN); - - Task.Run(() => + + _ = Task.Run(() => { this.Controller.Trigger_NodeAdded(NN, IR); }); - + }); ControllerEventMap.Add("node found", (JO) => { int NID = JO.SelectToken("event.node.nodeId").ToObject(); - Task.Run(() => + _ = Task.Run(() => { this.Controller.Trigger_NodeFound(NID); }); @@ -460,7 +458,7 @@ private void MapControllerEvents() ControllerEventMap.Add("grant security classes", (JO) => { - Task.Run(() => + _ = Task.Run(() => { InclusionGrant RIG = JO.SelectToken("event.requested").ToObject(); InclusionGrant SIG = this.Controller.Trigger_GrantSecurityClasses(RIG); @@ -478,7 +476,7 @@ private void MapControllerEvents() ControllerEventMap.Add("validate dsk and enter pin", (JO) => { - Task.Run(() => + _ = Task.Run(() => { string DSK = this.Controller.Trigger_ValidateDSK(JO.SelectToken("event.dsk").ToObject()); @@ -507,7 +505,7 @@ private void MapControllerEvents() Args.SkippedNodes = Skipped.Select(x => Convert.ToInt32(x.Key)).ToArray(); Args.PendingNodes = Pending.Select(x => Convert.ToInt32(x.Key)).ToArray(); - Task.Run(() => + _ = Task.Run(() => { this.Controller.Trigger_RebuildRoutesProgress(Args); }); @@ -526,7 +524,7 @@ private void MapControllerEvents() Args.FailedNodes = Failed.Select(x => Convert.ToInt32(x.Key)).ToArray(); Args.SkippedNodes = Skipped.Select(x => Convert.ToInt32(x.Key)).ToArray(); - Task.Run(() => + _ = Task.Run(() => { this.Controller.Trigger_RebuildRoutesDone(Args); }); @@ -540,7 +538,7 @@ private void MapServerEvents() { LoggingEventArgs Args = JO.SelectToken("event").ToObject(); - Task.Run(() => + _ = Task.Run(() => { Trigger_LoggingEvent(Args); }); @@ -558,9 +556,9 @@ private void MapEvents() DriverEventMap = new Dictionary>(); MapServerEvents(); } - + // Client Mode - public Driver(Uri Server, int SchemaVersion = 0, int ServerErrorThrottleTime = 10000) + public Driver(Uri Server, int SchemaVersion = 0) { Newtonsoft.Json.JsonConvert.DefaultSettings = () => new JsonSerializerSettings { @@ -581,25 +579,24 @@ public Driver(Uri Server, int SchemaVersion = 0, int ServerErrorThrottleTime = 1 Callbacks = new Dictionary>(); MapEvents(); - + this.WSAddress = Server; this.Host = false; - this.ServerErrorThrottleTime = ServerErrorThrottleTime; InternalPrep(); } // Host Mode - public Driver(string SerialPort, ZWaveOptions Options, int ServerCommunicationPort = 50001, int ServerErrorThrottleTime = 10000) + public Driver(string SerialPort, ZWaveOptions Options, int ServerCommunicationPort = 50001) { if (UsedPorts.Contains(ServerCommunicationPort)) { - throw new Exception(string.Format("Web Socket Port: {0} already in use", ServerCommunicationPort)); + throw new Exception(string.Format("Web Socket Port: {0} already in use by a driver instance.", ServerCommunicationPort)); } UsedPorts.Add(ServerCommunicationPort); - + Newtonsoft.Json.JsonConvert.DefaultSettings = () => new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore @@ -614,13 +611,12 @@ public Driver(string SerialPort, ZWaveOptions Options, int ServerCommunicationPo Callbacks = new Dictionary>(); MapEvents(); - + this.SerialPort = SerialPort; this.Options = Options; this.ServerCommunicationPort = ServerCommunicationPort; this.WSAddress = new Uri("ws://localhost:" + ServerCommunicationPort); this.Host = true; - this.ServerErrorThrottleTime = ServerErrorThrottleTime; this._server = new Server(); InternalPrep(); @@ -640,13 +636,13 @@ private void InternalPrep() { Options = { KeepAliveInterval = TimeSpan.FromSeconds(5), - RemoteCertificateValidationCallback = (sender, cert, chain, errors) => true + RemoteCertificateValidationCallback = (sender, cert, chain, errors) => true } }); ClientWebSocket = new Websocket.Client.WebsocketClient(this.WSAddress, Factory); ClientWebSocket.ConnectTimeout = TimeSpan.FromSeconds(15); - + ClientWebSocket.MessageReceived.Subscribe((Message) => { @@ -657,17 +653,17 @@ private void InternalPrep() { if (!RequestedExit && DisconnectionInfo.Type == DisconnectionType.Error) { - ServerConnectionError?.Invoke(Enums.ErrorCodes.WSConnectionTimout,"Could not connect to the ZWaveJS Websocket (timeout)", (retry, timeout) => - { - if (retry) - { - if (timeout.HasValue && timeout.Value > 0) - { - ClientWebSocket.ConnectTimeout = TimeSpan.FromSeconds(timeout.Value); - } - ClientWebSocket.Reconnect(); - } - }); + ServerConnectionError?.Invoke(Enums.ErrorCodes.WSConnectionTimout, "Could not connect to the ZWaveJS Websocket (timeout)", (retry, timeout) => + { + if (retry) + { + if (timeout.HasValue && timeout.Value > 0) + { + ClientWebSocket.ConnectTimeout = TimeSpan.FromSeconds(timeout.Value); + } + ClientWebSocket.Reconnect(); + } + }); } }); @@ -687,24 +683,24 @@ private void Server_Exited() DestroySocket(); - ServerConnectionError?.Invoke(Enums.ErrorCodes.Unknown,"The Server process unexpectedly terminted.", (retry, timeout) => + ServerConnectionError?.Invoke(Enums.ErrorCodes.Unknown, "The Server process unexpectedly terminted.", (retry, timeout) => { SettleCallbacksError(); - if (retry) - { - if (timeout.HasValue && timeout.Value > 0) - { - ClientWebSocket.ConnectTimeout = TimeSpan.FromSeconds(timeout.Value); - } + if (retry) + { + if (timeout.HasValue && timeout.Value > 0) + { + ClientWebSocket.ConnectTimeout = TimeSpan.FromSeconds(timeout.Value); + } - Restart(); - } + Restart(); + } }); - + } } - + // Start Driver public void Start() { @@ -740,14 +736,14 @@ public void Destroy() { RequestedExit = true; Inited = false; - if(Controller != null) + if (Controller != null) { Controller.Nodes = null; // Is this necessary? Controller = null; } - + DestroySocket(); - DestroyServer(); + DestroyServer(); } async internal void Restart() @@ -768,7 +764,7 @@ private void SettleCallbacksError() JObject JO = new JObject(); JO.Add("success", false); JO.Add("zwaveErrorCode", Enums.ErrorCodes.Unknown); - JO.Add("zwaveErrorMessage", "The Server process unexpectedly terminted. It is unknown if the command was successfull, assuming false. Subscribe to the 'UnexpectedHostExit' event of the driver to restart the Driver Runtime"); + JO.Add("zwaveErrorMessage", "The Server process unexpectedly terminted. It is unknown if the command was successfull, assuming false. Subscribe to the 'ServerConnectionError' event of the driver to restart the Driver Runtime"); // Guard against race condition try @@ -783,7 +779,7 @@ private void SettleCallbacksError() } } - + private void Server_FatalError() { RequestedExit = true; @@ -792,9 +788,9 @@ private void Server_FatalError() Controller = null; DestroySocket(); DestroyServer(); - ServerConnectionError?.Invoke(Enums.ErrorCodes.StartUpError,"Fatal ZWaveJS Server (OR Driver) Error.", null); + ServerConnectionError?.Invoke(Enums.ErrorCodes.StartUpError, "Fatal ZWaveJS Server (OR Driver) Error.", null); } - + private void SetAPIVersionCB(JObject JO) { if (JO.Value("success")) @@ -819,7 +815,7 @@ private void SetAPIVersionCB(JObject JO) RequestedExit = true; DestroySocket(); DestroyServer(); - ServerConnectionError?.Invoke(Enums.ErrorCodes.SchemaMisMatch,"Client and Server schema mismatch",null); + ServerConnectionError?.Invoke(Enums.ErrorCodes.SchemaMisMatch, "Client and Server schema mismatch", null); break; } @@ -835,16 +831,16 @@ private void StartListetningCB(JObject JO) { Controller C = JO.SelectToken("result.state.controller").ToObject(_jsonSerializer); ZWaveNode[] Nodes = JO.SelectToken("result.state.nodes").ToObject(_jsonSerializer); - + C.deviceConfig = Nodes.FirstOrDefault((N) => N.isControllerNode).deviceConfig; Nodes = Nodes.Where((N) => !N.isControllerNode).ToArray(); this.Controller = C; this.Controller.Nodes = new NodesCollection(Nodes); - + this.Utils = new Utils(this); this.ConfigManager = new ConfigManager(this); - + Inited = true; DriverReady?.Invoke(); @@ -949,7 +945,7 @@ public Task SoftReset() // Proces Message private void WebsocketClient_MessageReceived(object sender, ResponseMessage Message) { - + if (System.Diagnostics.Debugger.IsAttached) { System.Diagnostics.Debug.WriteLine(Message.Text); @@ -966,18 +962,19 @@ private void WebsocketClient_MessageReceived(object sender, ResponseMessage Mess if (MessageID != Guid.Empty) { - if (Callbacks.ContainsKey(MessageID)) + if (Callbacks.TryGetValue(MessageID, out var callback)) { - // Guard against race condition - try + Callbacks.Remove(MessageID); + _ = Task.Run(() => { - Callbacks[MessageID].Invoke(JO); - Callbacks.Remove(MessageID); - } - catch (Exception) { } - + try + { + callback(JO); + } + catch {} + }); + return; } - return; } diff --git a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/ZWaveJS.NET.csproj b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/ZWaveJS.NET.csproj index ec4d190..6fbdbe9 100644 --- a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/ZWaveJS.NET.csproj +++ b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/ZWaveJS.NET.csproj @@ -1,7 +1,7 @@ - net6.0;net7.0;net8.0;netstandard2.1;net9.0 + net6.0;net7.0;net8.0;net9.0;netstandard2.1 5.0.0 Marcus Davies true From 0315f075bded278071fd2147c4f4d5e26ec43b02 Mon Sep 17 00:00:00 2001 From: Marcus Davies <55892693+marcus-j-davies@users.noreply.github.com> Date: Thu, 22 Jan 2026 18:21:07 +0000 Subject: [PATCH 10/59] More threading changes --- CHANGELOG.md | 3 + .../ZWaveJS.NET/ZWaveJS.NET/Driver.cs | 406 ++++++++---------- 2 files changed, 192 insertions(+), 217 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index db06aa8..985b5ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,8 @@ Where the error code, supports a retry, ```Retry``` will not be ```null``` Arguments: **Should Retry**, **New Timeout Value** (if <1, defaults to 15s) + Effectively, the host application, is now respoabile for reconnection attempts + - All interaction error codes have been updated ```csharp ZWDNET-ER-04 : S2 Call backs missing @@ -48,6 +50,7 @@ - Internal Chnages. - All responses to method calls are now dispatched asynchronously on the thread pool, so user code triggered by these responses cannot block the WebSocket message handler. + - Previously, the node, controller, and driver callbacks each created their own task after completing their prep work. Now the task is created upfront, and both the prep work and the callback execute inside that single task. diff --git a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Driver.cs b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Driver.cs index 3fb948e..dcdbf4d 100644 --- a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Driver.cs +++ b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Driver.cs @@ -19,6 +19,7 @@ public class Driver internal bool Inited = false; internal ZWaveOptions Options; internal const string FWUSAPIKey = "921f8000486fcc2744721cfc747aab2db8fc025b5d487cbf2eba76e88ff6f79a064644bf"; + internal DateTime ConnectStart; private Dictionary> NodeEventMap; private Dictionary> ControllerEventMap; @@ -78,10 +79,7 @@ private void MapNodeEvents() int NID = JO.SelectToken("event.nodeId").ToObject(); ZWaveNode N = this.Controller.Nodes.Get(NID); - _ = Task.Run(() => - { - N.Trigger_NodeInfo(); - }); + N.Trigger_NodeInfo(); }); NodeEventMap.Add("check lifeline health progress", (JO) => @@ -92,11 +90,7 @@ private void MapNodeEvents() int LastRating = JO.SelectToken("event.lastRating").ToObject(); ZWaveNode N = this.Controller.Nodes.Get(NID); - - _ = Task.Run(() => - { - N.Trigger_LifelineHealthCheckProgress(Round, Total, LastRating); - }); + N.Trigger_LifelineHealthCheckProgress(Round, Total, LastRating); }); NodeEventMap.Add("statistics updated", (JO) => @@ -105,10 +99,7 @@ private void MapNodeEvents() NodeStatisticsUpdatedArgs NS = JO.SelectToken("event.statistics").ToObject(); ZWaveNode N = this.Controller.Nodes.Get(NID); - _ = Task.Run(() => - { - N.Trigger_StatisticsUpdated(NS); - }); + N.Trigger_StatisticsUpdated(NS); }); NodeEventMap.Add("firmware update finished", (JO) => @@ -117,11 +108,7 @@ private void MapNodeEvents() NodeFirmwareUpdateResultArgs Result = JO.SelectToken("event.result").ToObject(); ZWaveNode N = this.Controller.Nodes.Get(NID); - _ = Task.Run(() => - { - N.Trigger_FirmwareUpdateFinished(Result); - }); - + N.Trigger_FirmwareUpdateFinished(Result); }); @@ -131,10 +118,7 @@ private void MapNodeEvents() NodeFirmwareUpdateProgressArgs Progress = JO.SelectToken("event.progress").ToObject(); ZWaveNode N = this.Controller.Nodes.Get(NID); - _ = Task.Run(() => - { - N.Trigger_FirmwareUpdateProgress(Progress); - }); + N.Trigger_FirmwareUpdateProgress(Progress); }); NodeEventMap.Add("value updated", (JO) => @@ -143,10 +127,8 @@ private void MapNodeEvents() ValueUpdatedArgs Args = JO.SelectToken("event.args").ToObject(); ZWaveNode N = this.Controller.Nodes.Get(NID); - _ = Task.Run(() => - { - N.Trigger_ValueUpdated(Args); - }); + N.Trigger_ValueUpdated(Args); + }); NodeEventMap.Add("value added", (JO) => @@ -155,10 +137,8 @@ private void MapNodeEvents() ValueAddedArgs Args = JO.SelectToken("event.args").ToObject(); ZWaveNode N = this.Controller.Nodes.Get(NID); - _ = Task.Run(() => - { - N.Trigger_ValueAdded(Args); - }); + N.Trigger_ValueAdded(Args); + }); NodeEventMap.Add("value removed", (JO) => @@ -167,10 +147,8 @@ private void MapNodeEvents() ValueRemovedArgs Args = JO.SelectToken("event.args").ToObject(); ZWaveNode N = this.Controller.Nodes.Get(NID); - _ = Task.Run(() => - { - N.Trigger_ValueRemoved(Args); - }); + N.Trigger_ValueRemoved(Args); + }); NodeEventMap.Add("value notification", (JO) => @@ -179,10 +157,8 @@ private void MapNodeEvents() ValueNotificationArgs Args = JO.SelectToken("event.args").ToObject(); ZWaveNode N = this.Controller.Nodes.Get(NID); - _ = Task.Run(() => - { - N.Trigger_ValueNotification(Args); - }); + N.Trigger_ValueNotification(Args); + }); NodeEventMap.Add("notification", (JO) => @@ -192,10 +168,8 @@ private void MapNodeEvents() JObject IJO = JO.SelectToken("event.args").ToObject(); ZWaveNode N = this.Controller.Nodes.Get(NID); - _ = Task.Run(() => - { - N.Trigger_Notification(CCID, IJO); - }); + N.Trigger_Notification(CCID, IJO); + }); NodeEventMap.Add("alive", (JO) => @@ -203,10 +177,8 @@ private void MapNodeEvents() int NID = JO.SelectToken("event.nodeId").ToObject(); ZWaveNode N = this.Controller.Nodes.Get(NID); - _ = Task.Run(() => - { - N.Trigger_NodeAlive(); - }); + N.Trigger_NodeAlive(); + }); NodeEventMap.Add("dead", (JO) => @@ -214,10 +186,8 @@ private void MapNodeEvents() int NID = JO.SelectToken("event.nodeId").ToObject(); ZWaveNode N = this.Controller.Nodes.Get(NID); - _ = Task.Run(() => - { - N.Trigger_NodeDead(); - }); + N.Trigger_NodeDead(); + }); NodeEventMap.Add("wake up", (JO) => @@ -225,10 +195,8 @@ private void MapNodeEvents() int NID = JO.SelectToken("event.nodeId").ToObject(); ZWaveNode N = this.Controller.Nodes.Get(NID); - _ = Task.Run(() => - { - N.Trigger_NodeAwake(); - }); + N.Trigger_NodeAwake(); + }); NodeEventMap.Add("sleep", (JO) => @@ -236,10 +204,8 @@ private void MapNodeEvents() int NID = JO.SelectToken("event.nodeId").ToObject(); ZWaveNode N = this.Controller.Nodes.Get(NID); - _ = Task.Run(() => - { - N.Trigger_NodeAsleep(); - }); + N.Trigger_NodeAsleep(); + }); NodeEventMap.Add("ready", (JO) => @@ -250,10 +216,8 @@ private void MapNodeEvents() ZWaveNode N = this.Controller.Nodes.Get(NID); this.Controller.Nodes.ReplaceInformation(NNI, N); - _ = Task.Run(() => - { - N.Trigger_NodeReady(); - }); + N.Trigger_NodeReady(); + }); NodeEventMap.Add("interview started", (JO) => @@ -261,10 +225,8 @@ private void MapNodeEvents() int NID = JO.SelectToken("event.nodeId").ToObject(); ZWaveNode N = this.Controller.Nodes.Get(NID); - _ = Task.Run(() => - { - N.Trigger_NodeInterviewStarted(); - }); + N.Trigger_NodeInterviewStarted(); + }); NodeEventMap.Add("interview completed", (JO) => @@ -272,10 +234,8 @@ private void MapNodeEvents() int NID = JO.SelectToken("event.nodeId").ToObject(); ZWaveNode N = this.Controller.Nodes.Get(NID); - _ = Task.Run(() => - { - N.Trigger_NodeInterviewCompleted(); - }); + N.Trigger_NodeInterviewCompleted(); + }); NodeEventMap.Add("interview failed", (JO) => @@ -284,10 +244,8 @@ private void MapNodeEvents() NodeInterviewFailedEventArgs FII = JO.SelectToken("event.args").ToObject(); ZWaveNode N = this.Controller.Nodes.Get(NID); - _ = Task.Run(() => - { - N.Trigger_NodeInterviewFailed(FII); - }); + N.Trigger_NodeInterviewFailed(FII); + }); NodeEventMap.Add("metadata updated", (JO) => @@ -296,10 +254,8 @@ private void MapNodeEvents() MetadataUpdatedArgs Args = JO.SelectToken("event.args").ToObject(); ZWaveNode N = this.Controller.Nodes.Get(NID); - _ = Task.Run(() => - { - N.Trigger_MetadataUpdated(Args); - }); + N.Trigger_MetadataUpdated(Args); + }); } @@ -309,10 +265,8 @@ private void MapControllerEvents() { ControllerFirmwareUpdateResultArgs Result = JO.SelectToken("event.result").ToObject(); - _ = Task.Run(() => - { - this.Controller.Trigger_FirmwareUpdateFinished(Result); - }); + this.Controller.Trigger_FirmwareUpdateFinished(Result); + }); @@ -321,10 +275,8 @@ private void MapControllerEvents() { ControllerFirmwareUpdateProgressArgs Progress = JO.SelectToken("event.progress").ToObject(); - _ = Task.Run(() => - { - this.Controller.Trigger_FirmwareUpdateProgress(Progress); - }); + this.Controller.Trigger_FirmwareUpdateProgress(Progress); + }); ControllerEventMap.Add("nvm backup progress", (JO) => @@ -332,10 +284,8 @@ private void MapControllerEvents() int Read = JO.SelectToken("event.bytesRead").ToObject(); int Total = JO.SelectToken("event.total").ToObject(); - _ = Task.Run(() => - { - this.Controller.Trigger_BackupNVMProgress(Read, Total); - }); + this.Controller.Trigger_BackupNVMProgress(Read, Total); + }); ControllerEventMap.Add("nvm convert progress", (JO) => @@ -343,10 +293,8 @@ private void MapControllerEvents() int Read = JO.SelectToken("event.bytesRead").ToObject(); int Total = JO.SelectToken("event.total").ToObject(); - _ = Task.Run(() => - { - this.Controller.Trigger_ConvertRestoreNVMProgress(Read, Total); - }); + this.Controller.Trigger_ConvertRestoreNVMProgress(Read, Total); + }); ControllerEventMap.Add("nvm restore progress", (JO) => @@ -354,62 +302,48 @@ private void MapControllerEvents() int Written = JO.SelectToken("event.bytesWritten").ToObject(); int Total = JO.SelectToken("event.total").ToObject(); - _ = Task.Run(() => - { - this.Controller.Trigger_RestoreNVMProgressSub(Written, Total); - }); + this.Controller.Trigger_RestoreNVMProgressSub(Written, Total); + }); ControllerEventMap.Add("statistics updated", (JO) => { ControllerStatisticsUpdatedArgs CS = JO.SelectToken("event.statistics").ToObject(); - _ = Task.Run(() => - { - this.Controller.Trigger_StatisticsUpdated(CS); - }); + this.Controller.Trigger_StatisticsUpdated(CS); + }); ControllerEventMap.Add("inclusion aborted", (JO) => { - _ = Task.Run(() => - { - this.Controller.Trigger_InclusionAborted(); - }); + this.Controller.Trigger_InclusionAborted(); + }); ControllerEventMap.Add("inclusion started", (JO) => { bool Secure = (JO.SelectToken("event.strategy").ToObject() != Enums.InclusionStrategy.Insecure); - _ = Task.Run(() => - { - this.Controller.Trigger_InclusionStarted(Secure); - }); + this.Controller.Trigger_InclusionStarted(Secure); + }); ControllerEventMap.Add("inclusion stopped", (JO) => { - _ = Task.Run(() => - { - this.Controller.Trigger_InclusionStopped(); - }); + this.Controller.Trigger_InclusionStopped(); + }); ControllerEventMap.Add("exclusion started", (JO) => { - _ = Task.Run(() => - { - this.Controller.Trigger_ExclusionStarted(); - }); + this.Controller.Trigger_ExclusionStarted(); + }); ControllerEventMap.Add("exclusion stopped", (JO) => { - _ = Task.Run(() => - { - this.Controller.Trigger_ExclusionStopped(); - }); + this.Controller.Trigger_ExclusionStopped(); + }); ControllerEventMap.Add("node removed", (JO) => @@ -420,11 +354,7 @@ private void MapControllerEvents() ZWaveNode N = this.Controller.Nodes.Get(NID); this.Controller.Nodes.RemoveNodeFromCollection(NID); - _ = Task.Run(() => - { - this.Controller.Trigger_NodeRemoved(N, Reason); - }); - + this.Controller.Trigger_NodeRemoved(N, Reason); }); @@ -438,10 +368,8 @@ private void MapControllerEvents() this.Controller.Nodes.AddNodeToCollection(NN); - _ = Task.Run(() => - { - this.Controller.Trigger_NodeAdded(NN, IR); - }); + this.Controller.Trigger_NodeAdded(NN, IR); + }); @@ -449,45 +377,41 @@ private void MapControllerEvents() { int NID = JO.SelectToken("event.node.nodeId").ToObject(); - _ = Task.Run(() => - { - this.Controller.Trigger_NodeFound(NID); - }); + this.Controller.Trigger_NodeFound(NID); + }); ControllerEventMap.Add("grant security classes", (JO) => { - _ = Task.Run(() => - { - InclusionGrant RIG = JO.SelectToken("event.requested").ToObject(); - InclusionGrant SIG = this.Controller.Trigger_GrantSecurityClasses(RIG); - Dictionary Request = new Dictionary(); - Request.Add("messageId", Guid.NewGuid().ToString()); - Request.Add("command", Enums.Commands.GrantSecurityClasses); - Request.Add("inclusionGrant", SIG); + InclusionGrant RIG = JO.SelectToken("event.requested").ToObject(); + InclusionGrant SIG = this.Controller.Trigger_GrantSecurityClasses(RIG); + + Dictionary Request = new Dictionary(); + Request.Add("messageId", Guid.NewGuid().ToString()); + Request.Add("command", Enums.Commands.GrantSecurityClasses); + Request.Add("inclusionGrant", SIG); + + string RequestPL = Newtonsoft.Json.JsonConvert.SerializeObject(Request); + ClientWebSocket.SendInstant(RequestPL); - string RequestPL = Newtonsoft.Json.JsonConvert.SerializeObject(Request); - ClientWebSocket.SendInstant(RequestPL); - }); }); ControllerEventMap.Add("validate dsk and enter pin", (JO) => { - _ = Task.Run(() => - { - string DSK = this.Controller.Trigger_ValidateDSK(JO.SelectToken("event.dsk").ToObject()); - Dictionary Request = new Dictionary(); - Request.Add("messageId", Guid.NewGuid().ToString()); - Request.Add("command", Enums.Commands.ValidateDSK); - Request.Add("pin", DSK); + string DSK = this.Controller.Trigger_ValidateDSK(JO.SelectToken("event.dsk").ToObject()); + + Dictionary Request = new Dictionary(); + Request.Add("messageId", Guid.NewGuid().ToString()); + Request.Add("command", Enums.Commands.ValidateDSK); + Request.Add("pin", DSK); + + string RequestPL = Newtonsoft.Json.JsonConvert.SerializeObject(Request); + ClientWebSocket.SendInstant(RequestPL); - string RequestPL = Newtonsoft.Json.JsonConvert.SerializeObject(Request); - ClientWebSocket.SendInstant(RequestPL); - }); }); ControllerEventMap.Add("rebuild routes progress", (JO) => @@ -505,10 +429,9 @@ private void MapControllerEvents() Args.SkippedNodes = Skipped.Select(x => Convert.ToInt32(x.Key)).ToArray(); Args.PendingNodes = Pending.Select(x => Convert.ToInt32(x.Key)).ToArray(); - _ = Task.Run(() => - { - this.Controller.Trigger_RebuildRoutesProgress(Args); - }); + + this.Controller.Trigger_RebuildRoutesProgress(Args); + }); ControllerEventMap.Add("rebuild routes done", (JO) => @@ -524,10 +447,9 @@ private void MapControllerEvents() Args.FailedNodes = Failed.Select(x => Convert.ToInt32(x.Key)).ToArray(); Args.SkippedNodes = Skipped.Select(x => Convert.ToInt32(x.Key)).ToArray(); - _ = Task.Run(() => - { - this.Controller.Trigger_RebuildRoutesDone(Args); - }); + + this.Controller.Trigger_RebuildRoutesDone(Args); + }); } @@ -538,10 +460,9 @@ private void MapServerEvents() { LoggingEventArgs Args = JO.SelectToken("event").ToObject(); - _ = Task.Run(() => - { - Trigger_LoggingEvent(Args); - }); + + Trigger_LoggingEvent(Args); + }); } @@ -649,11 +570,35 @@ private void InternalPrep() WebsocketClient_MessageReceived(ClientWebSocket, Message); }); - ClientWebSocket.DisconnectionHappened.Subscribe((DisconnectionInfo) => + ClientWebSocket.DisconnectionHappened.Subscribe((info) => { - if (!RequestedExit && DisconnectionInfo.Type == DisconnectionType.Error) - { - ServerConnectionError?.Invoke(Enums.ErrorCodes.WSConnectionTimout, "Could not connect to the ZWaveJS Websocket (timeout)", (retry, timeout) => + _ = HandleDisconnectAsync(info); + }); + + + ClientWebSocket.ReconnectTimeout = null; // Dont attempt to reconnect when quite + ClientWebSocket.ErrorReconnectTimeout = null; // disable automatic + + } + + + private async Task HandleDisconnectAsync(DisconnectionInfo info) + { + TimeSpan elapsed = DateTime.UtcNow - ConnectStart; + + if (elapsed < ClientWebSocket.ConnectTimeout) + { + await Task.Delay(1000); + ClientWebSocket.Reconnect(); + return; + } + + if (!RequestedExit && info.Type == DisconnectionType.Error) + { + ServerConnectionError?.Invoke( + Enums.ErrorCodes.WSConnectionTimout, + "Could not connect to the ZWaveJS Websocket (timeout)", + (retry, timeout) => { if (retry) { @@ -661,17 +606,18 @@ private void InternalPrep() { ClientWebSocket.ConnectTimeout = TimeSpan.FromSeconds(timeout.Value); } + else + { + ClientWebSocket.ConnectTimeout = TimeSpan.FromSeconds(15); + } + ConnectStart = DateTime.UtcNow; ClientWebSocket.Reconnect(); } }); - } - }); - - ClientWebSocket.ReconnectTimeout = null; // Dont attempt to reconnect when quite - ClientWebSocket.ErrorReconnectTimeout = null; - + } } + // Server Process Exit Unexpected private void Server_Exited() { @@ -693,7 +639,10 @@ private void Server_Exited() { ClientWebSocket.ConnectTimeout = TimeSpan.FromSeconds(timeout.Value); } - + else + { + ClientWebSocket.ConnectTimeout = TimeSpan.FromSeconds(15); + } Restart(); } }); @@ -705,6 +654,7 @@ private void Server_Exited() public void Start() { RequestedExit = false; + ConnectStart = DateTime.UtcNow; ClientWebSocket.Start(); } @@ -791,37 +741,6 @@ private void Server_FatalError() ServerConnectionError?.Invoke(Enums.ErrorCodes.StartUpError, "Fatal ZWaveJS Server (OR Driver) Error.", null); } - private void SetAPIVersionCB(JObject JO) - { - if (JO.Value("success")) - { - Guid CBID = Guid.NewGuid(); - Callbacks.Add(CBID, StartListetningCB); - - Dictionary Request = new Dictionary(); - Request.Add("messageId", CBID.ToString()); - Request.Add("command", Enums.Commands.StartListetning); - - string RequestPL = Newtonsoft.Json.JsonConvert.SerializeObject(Request); - - ClientWebSocket.SendInstant(RequestPL); - } - else - { - string ErrorCode = JO.Value("errorCode"); - switch (ErrorCode) - { - case "schema_incompatible": - RequestedExit = true; - DestroySocket(); - DestroyServer(); - ServerConnectionError?.Invoke(Enums.ErrorCodes.SchemaMisMatch, "Client and Server schema mismatch", null); - break; - - } - } - - } private void StartListetningCB(JObject JO) { @@ -942,6 +861,38 @@ public Task SoftReset() return Result.Task; } + private void SetAPIVersionCB(JObject JO) + { + if (JO.Value("success")) + { + Guid CBID = Guid.NewGuid(); + Callbacks.Add(CBID, StartListetningCB); + + Dictionary Request = new Dictionary(); + Request.Add("messageId", CBID.ToString()); + Request.Add("command", Enums.Commands.StartListetning); + + string RequestPL = Newtonsoft.Json.JsonConvert.SerializeObject(Request); + + ClientWebSocket.SendInstant(RequestPL); + } + else + { + string ErrorCode = JO.Value("errorCode"); + switch (ErrorCode) + { + case "schema_incompatible": + RequestedExit = true; + DestroySocket(); + DestroyServer(); + ServerConnectionError?.Invoke(Enums.ErrorCodes.SchemaMisMatch, "Client and Server schema mismatch", null); + break; + + } + } + } + + // Proces Message private void WebsocketClient_MessageReceived(object sender, ResponseMessage Message) { @@ -971,7 +922,7 @@ private void WebsocketClient_MessageReceived(object sender, ResponseMessage Mess { callback(JO); } - catch {} + catch { } }); return; } @@ -1006,23 +957,44 @@ private void WebsocketClient_MessageReceived(object sender, ResponseMessage Mess switch (_Source) { case "node": - if (NodeEventMap.ContainsKey(_Event)) + if (NodeEventMap.TryGetValue(_Event, out var NodeCB)) { - NodeEventMap[_Event].Invoke(JO); + _ = Task.Run(() => + { + try + { + NodeCB(JO); + } + catch { } + }); } break; case "controller": - if (ControllerEventMap.ContainsKey(_Event)) + if (ControllerEventMap.TryGetValue(_Event, out var ControllerCB)) { - ControllerEventMap[_Event].Invoke(JO); + _ = Task.Run(() => + { + try + { + ControllerCB(JO); + } + catch { } + }); } break; case "driver": - if (DriverEventMap.ContainsKey(_Event)) + if (DriverEventMap.TryGetValue(_Event, out var DriverCB)) { - DriverEventMap[_Event].Invoke(JO); + _ = Task.Run(() => + { + try + { + DriverCB(JO); + } + catch { } + }); } break; From e14eb459a9f4590e8cd50a206ea6dd571bf299a3 Mon Sep 17 00:00:00 2001 From: Marcus Davies <55892693+marcus-j-davies@users.noreply.github.com> Date: Thu, 22 Jan 2026 19:36:10 +0000 Subject: [PATCH 11/59] Handle more WS exit codes + null check in terminated events --- .../ZWaveJS.NET/ZWaveJS.NET/Driver.cs | 36 +++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Driver.cs b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Driver.cs index dcdbf4d..1452efa 100644 --- a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Driver.cs +++ b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Driver.cs @@ -584,6 +584,35 @@ private void InternalPrep() private async Task HandleDisconnectAsync(DisconnectionInfo info) { + + if (info.Type == DisconnectionType.ByUser) + { + return; // this is us + } + + if (info.Type == DisconnectionType.Lost) + { + ServerConnectionError?.Invoke(Enums.ErrorCodes.Unknown, "Unexpectedly lost connection to the server.", (retry, timeout) => + { + SettleCallbacksError(); + + if (retry) + { + if (timeout.HasValue && timeout.Value > 0) + { + ClientWebSocket.ConnectTimeout = TimeSpan.FromSeconds(timeout.Value); + } + else + { + ClientWebSocket.ConnectTimeout = TimeSpan.FromSeconds(15); + } + Restart(); + } + }); + + return; + } + TimeSpan elapsed = DateTime.UtcNow - ConnectStart; if (elapsed < ClientWebSocket.ConnectTimeout) @@ -734,8 +763,11 @@ private void Server_FatalError() { RequestedExit = true; Inited = false; - Controller.Nodes = null; - Controller = null; + if (Controller != null) + { + Controller.Nodes = null; + Controller = null; + } DestroySocket(); DestroyServer(); ServerConnectionError?.Invoke(Enums.ErrorCodes.StartUpError, "Fatal ZWaveJS Server (OR Driver) Error.", null); From 020f6f25410ffc4aff40bfab416fa0f86a622053 Mon Sep 17 00:00:00 2001 From: Marcus Davies <55892693+marcus-j-davies@users.noreply.github.com> Date: Fri, 23 Jan 2026 19:48:00 +0000 Subject: [PATCH 12/59] Update CHANGELOG.md --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 985b5ba..faf32c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -48,9 +48,9 @@ ZWDNET-ER-09 : Use of incorrect override ``` - - Internal Chnages. + - Internal Changes. - All responses to method calls are now dispatched asynchronously on the thread pool, so user code triggered by these responses cannot block the WebSocket message handler. - - Previously, the node, controller, and driver callbacks each created their own task after completing their prep work. Now the task is created upfront, and both the prep work and the callback execute inside that single task. + - Whilst the various methods already return Task instances, task completion is now performed asynchronously to prevent blocking the WebSocket message handler. From 945ec53ab2b62fc38ac397d67c8050301c3525ba Mon Sep 17 00:00:00 2001 From: Marcus Davies <55892693+marcus-j-davies@users.noreply.github.com> Date: Sat, 24 Jan 2026 14:18:25 +0000 Subject: [PATCH 13/59] Optimisations --- CHANGELOG.md | 8 +- .../ZWaveJS.NET/ZWaveJS.NET/Controller.cs | 204 ++++++++++-------- .../ZWaveJS.NET/ZWaveJS.NET/Driver.cs | 59 ++--- .../ZWaveJS.NET/ZWaveJS.NET/ZWaveNode.cs | 142 +++++------- 4 files changed, 203 insertions(+), 210 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index faf32c9..2ffaed2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,7 +16,7 @@ - ```public Driver(string SerialPort, ZWaveOptions Options, int ServerCommunicationPort = 50001)``` - ```public Driver(Uri Server, int SchemaVersion = 0)``` - - Re-engineered error/connection handling. + - Re-engineered error/connection handling. All Driver/Server error handling, is now handled through the **ServerConnectionError** event. This event has the following signature: @@ -48,9 +48,15 @@ ZWDNET-ER-09 : Use of incorrect override ``` + - The following methods have been renamed + - ```ZWJSS_SetRawConfigParameterValue``` -> ```SetRawConfigParameterValue``` + - ```ZWJSS_StartListeningLogs``` -> ```StartListeningLogs``` + - ```ZWJSS_StopListeningLogs``` -> ```StopListeningLogs``` + - Internal Changes. - All responses to method calls are now dispatched asynchronously on the thread pool, so user code triggered by these responses cannot block the WebSocket message handler. - Whilst the various methods already return Task instances, task completion is now performed asynchronously to prevent blocking the WebSocket message handler. + - Various optimisations to the code base for easiyer maintenance. diff --git a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Controller.cs b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Controller.cs index 3ae7094..e0357fe 100644 --- a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Controller.cs +++ b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Controller.cs @@ -17,7 +17,7 @@ internal Controller(Driver driver) } public delegate void BackupNVMProgress(int BytesRead, int Total); - private BackupNVMProgress BackupNVMProgressSub; + private BackupNVMProgress BackupNVMProgressSub; internal void Trigger_BackupNVMProgress(int BytesRead, int Total) { BackupNVMProgressSub?.Invoke(BytesRead, Total); @@ -49,7 +49,7 @@ internal void Trigger_StatisticsUpdated(ControllerStatisticsUpdatedArgs Args) public event RebuildRoutesProgressEvent RebuildRoutesProgress; internal void Trigger_RebuildRoutesProgress(RebuildRoutesProgressArgs Args) { - RebuildRoutesProgress?.Invoke(Args); + RebuildRoutesProgress?.Invoke(Args); } public delegate void RebuildRoutesDoneEvent(RebuildRoutesDoneArgs Args); @@ -133,7 +133,7 @@ internal void Trigger_FirmwareUpdateFinished(ControllerFirmwareUpdateResultArgs { FirmwareUpdateFinished?.Invoke(Args); } - + public delegate void FirmwareUpdateProgressEvent(ControllerFirmwareUpdateProgressArgs Args); public event FirmwareUpdateProgressEvent FirmwareUpdateProgress; internal void Trigger_FirmwareUpdateProgress(ControllerFirmwareUpdateProgressArgs Args) @@ -141,11 +141,43 @@ internal void Trigger_FirmwareUpdateProgress(ControllerFirmwareUpdateProgressArg FirmwareUpdateProgress?.Invoke(Args); } + private void ResetInclusionCallbacks() + { + ValidateDSKAndEnterPINSub = null; + GrantSecurityClassesSub = null; + AbortSub = null; + } + + private void ExtractInclusionCallbacks(InclusionOptions options) + { + if (options.strategy == Enums.InclusionStrategy.Default || options.strategy == Enums.InclusionStrategy.Security_S2) + { + ValidateDSKAndEnterPINSub = options.userCallbacks?.validateDSKAndEnterPIN; + GrantSecurityClassesSub = options.userCallbacks?.grantSecurityClasses; + AbortSub = options.userCallbacks?.abort; + } + } + + private CMDResult ValidateStrategy(Enums.InclusionStrategy strategy) + { + bool requiresCallbacks = + strategy == Enums.InclusionStrategy.Default || + strategy == Enums.InclusionStrategy.Security_S2; + + if (requiresCallbacks && (ValidateDSKAndEnterPINSub == null || GrantSecurityClassesSub == null || AbortSub == null)) + { + return new CMDResult(Enums.ErrorCodes.MissingS2Callbacks,"S2 Security requires userCallbacks [validateDSKAndEnterPIN, grantSecurityClasses, abort]",false); + } + + return null; + } + + // Checked as of : 3.5.0 public Task GetAvailableFirmwareUpdates(int NodeID, bool IncludePrereleases, UsageEnvironment Environment, string APIKey = null) { - Guid ID = Guid.NewGuid(); - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); if (Environment == UsageEnvironment.Commercial && string.IsNullOrEmpty(APIKey)) { @@ -157,7 +189,6 @@ public Task GetAvailableFirmwareUpdates(int NodeID, bool IncludePrere _driver.Callbacks.Add(ID, (JO) => { CMDResult Res = new CMDResult(JO); - if (Res.Success) { @@ -184,8 +215,8 @@ public Task GetAvailableFirmwareUpdates(int NodeID, bool IncludePrere // Checked as of : 3.5.0 public Task FirmwareUpdateOTA(int NodeID, FirmwareUpdateInfo Update) { - Guid ID = Guid.NewGuid(); - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); _driver.Callbacks.Add(ID, (JO) => { @@ -213,8 +244,8 @@ public Task FirmwareUpdateOTA(int NodeID, FirmwareUpdateInfo Update) // Checked as of : 3.5.0 public Task GetRFRegion() { - Guid ID = Guid.NewGuid(); - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); _driver.Callbacks.Add(ID, (JO) => { @@ -227,11 +258,11 @@ public Task GetRFRegion() Result.SetResult(Res); }); - + Dictionary Request = new Dictionary(); Request.Add("messageId", ID); Request.Add("command", Enums.Commands.GetRFRegion); - + string RequestPL = Newtonsoft.Json.JsonConvert.SerializeObject(Request); _driver.ClientWebSocket.SendInstant(RequestPL); @@ -241,15 +272,15 @@ public Task GetRFRegion() // Checked as of : 3.5.0 public Task SetRFRegion(Enums.RFRegion Region) { - Guid ID = Guid.NewGuid(); - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); _driver.Callbacks.Add(ID, (JO) => { CMDResult Res = new CMDResult(JO); Result.SetResult(Res); }); - + Dictionary Request = new Dictionary(); Request.Add("messageId", ID); Request.Add("command", Enums.Commands.SetRFRegion); @@ -260,12 +291,12 @@ public Task SetRFRegion(Enums.RFRegion Region) return Result.Task; } - + // Checked as of : 3.5.0 public Task SetMaxLongRangePowerlevel(decimal Limit) { - Guid ID = Guid.NewGuid(); - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); _driver.Callbacks.Add(ID, (JO) => { @@ -288,8 +319,8 @@ public Task SetMaxLongRangePowerlevel(decimal Limit) // Checked as of : 3.5.0 public Task GetMaxLongRangePowerlevel() { - Guid ID = Guid.NewGuid(); - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); _driver.Callbacks.Add(ID, (JO) => { @@ -318,8 +349,8 @@ public Task GetMaxLongRangePowerlevel() // Checked as of : 3.5.0 public Task GetPowerLevel() { - Guid ID = Guid.NewGuid(); - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); _driver.Callbacks.Add(ID, (JO) => { @@ -348,8 +379,8 @@ public Task GetPowerLevel() // Checked as of : 3.5.0 public Task SetPowerLevel(decimal PowerLevel, decimal Measured0dBm) { - Guid ID = Guid.NewGuid(); - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); _driver.Callbacks.Add(ID, (JO) => { @@ -370,7 +401,7 @@ public Task SetPowerLevel(decimal PowerLevel, decimal Measured0dBm) return Result.Task; } - + // Checked as of : 3.5.0 public Task FirmwareUpdateOTW(FirmwareUpdate Update) @@ -384,8 +415,8 @@ public Task FirmwareUpdateOTW(FirmwareUpdate Update) return Fail.Task; } - Guid ID = Guid.NewGuid(); - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); _driver.Callbacks.Add(ID, (JO) => { @@ -406,7 +437,7 @@ public Task FirmwareUpdateOTW(FirmwareUpdate Update) Request.Add("command", Enums.Commands.FirmwareUpdateOTW); Request.Add("file", Update.data); Request.Add("filename", Update.filename); - + string RequestPL = Newtonsoft.Json.JsonConvert.SerializeObject(Request); _driver.ClientWebSocket.SendInstant(RequestPL); @@ -416,8 +447,8 @@ public Task FirmwareUpdateOTW(FirmwareUpdate Update) // Checked as of : 3.5.0 public Task GetProvisioningEntries() { - Guid ID = Guid.NewGuid(); - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); _driver.Callbacks.Add(ID, (JO) => { CMDResult Res = new CMDResult(JO); @@ -444,8 +475,8 @@ public Task GetProvisioningEntries() // Checked as of : 3.5.0 public Task ToggleRF(bool Enabled) { - Guid ID = Guid.NewGuid(); - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); _driver.Callbacks.Add(ID, (JO) => { CMDResult Res = new CMDResult(JO); @@ -457,7 +488,7 @@ public Task ToggleRF(bool Enabled) Request.Add("messageId", ID); Request.Add("command", Enums.Commands.ToggleRF); Request.Add("enabled", Enabled); - + string RequestPL = Newtonsoft.Json.JsonConvert.SerializeObject(Request); _driver.ClientWebSocket.SendInstant(RequestPL); @@ -467,8 +498,8 @@ public Task ToggleRF(bool Enabled) // Checked as of : 3.5.0 public Task RemoveAssociations(AssociationAddress Source, int Group, AssociationAddress[] Targets) { - Guid ID = Guid.NewGuid(); - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); _driver.Callbacks.Add(ID, (JO) => { CMDResult Res = new CMDResult(JO); @@ -494,14 +525,14 @@ public Task RemoveAssociations(AssociationAddress Source, int Group, // Checked as of : 3.5.0 public Task AddAssociations(AssociationAddress Source, int Group, AssociationAddress[] Targets) { - Guid ID = Guid.NewGuid(); - TaskCompletionSource Result = new TaskCompletionSource(); - _driver.Callbacks.Add(ID, (JO) => - { - CMDResult Res = new CMDResult(JO); - Result.SetResult(Res); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); + _driver.Callbacks.Add(ID, (JO) => + { + CMDResult Res = new CMDResult(JO); + Result.SetResult(Res); - }); + }); Dictionary Request = new Dictionary(); Request.Add("messageId", ID); @@ -513,7 +544,7 @@ public Task AddAssociations(AssociationAddress Source, int Group, Ass string RequestPL = Newtonsoft.Json.JsonConvert.SerializeObject(Request); - _driver.ClientWebSocket.SendInstant(RequestPL); + _driver.ClientWebSocket.SendInstant(RequestPL); return Result.Task; } @@ -521,8 +552,8 @@ public Task AddAssociations(AssociationAddress Source, int Group, Ass // Checked as of : 3.5.0 public Task GetAssociations(int Node, int Endpoint) { - Guid ID = Guid.NewGuid(); - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); _driver.Callbacks.Add(ID, (JO) => { CMDResult Res = new CMDResult(JO); @@ -551,8 +582,8 @@ public Task GetAssociations(int Node, int Endpoint) // Checked as of : 3.5.0 public Task GetAssociationGroups(int Node, int Endpoint) { - Guid ID = Guid.NewGuid(); - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); _driver.Callbacks.Add(ID, (JO) => { CMDResult Res = new CMDResult(JO); @@ -585,8 +616,8 @@ public Task RestoreNVM(byte[] NVMData, ConvertRestoreNVMProgress Conv ConvertRestoreNVMProgressSub = ConvertProgress; RestoreNVMProgressSub = RestoreProgress; - Guid ID = Guid.NewGuid(); - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); _driver.Callbacks.Add(ID, (JO) => { CMDResult Res = new CMDResult(JO); @@ -614,8 +645,8 @@ public Task BackupNVMRaw(BackupNVMProgress OnProgress = null) { BackupNVMProgressSub = OnProgress; - Guid ID = Guid.NewGuid(); - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); _driver.Callbacks.Add(ID, (JO) => { CMDResult Res = new CMDResult(JO); @@ -646,8 +677,8 @@ public Task ReplaceFailedNode(int NodeID, InclusionOptions Options) GrantSecurityClassesSub = null; AbortSub = null; - Guid ID = Guid.NewGuid(); - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); switch (Options.strategy) { @@ -723,8 +754,8 @@ public Task ReplaceFailedNode(int NodeID, InclusionOptions Options) // Checked as of : 3.5.0 public Task RemoveFailedNode(int NodeID) { - Guid ID = Guid.NewGuid(); - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); _driver.Callbacks.Add(ID, (JO) => { @@ -747,8 +778,8 @@ public Task RemoveFailedNode(int NodeID) // Checked as of : 3.5.0 public Task RebuildNodeRoutes(int NodeID) { - Guid ID = Guid.NewGuid(); - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); _driver.Callbacks.Add(ID, (JO) => { @@ -771,8 +802,8 @@ public Task RebuildNodeRoutes(int NodeID) // Checked as of : 3.5.0 public Task BeginRebuildingRoutes(RebuildRoutesOptions Options) { - Guid ID = Guid.NewGuid(); - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); _driver.Callbacks.Add(ID, (JO) => { @@ -799,8 +830,8 @@ public Task BeginRebuildingRoutes(RebuildRoutesOptions Options) // Checked as of : 3.5.0 public Task StopRebuildingRoutes() { - Guid ID = Guid.NewGuid(); - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); _driver.Callbacks.Add(ID, (JO) => { @@ -827,21 +858,18 @@ public Task StopRebuildingRoutes() // Checked as of : 3.5.0 public Task BeginInclusion(InclusionOptions Options) { - ValidateDSKAndEnterPINSub = null; - GrantSecurityClassesSub = null; - AbortSub = null; + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); - Guid ID = Guid.NewGuid(); - TaskCompletionSource Result = new TaskCompletionSource(); + ResetInclusionCallbacks(); + ExtractInclusionCallbacks(Options); - switch (Options.strategy) + CMDResult Error = ValidateStrategy(Options.strategy); + + if(Error != null) { - case Enums.InclusionStrategy.Default: - case Enums.InclusionStrategy.Security_S2: - ValidateDSKAndEnterPINSub = Options.userCallbacks?.validateDSKAndEnterPIN ?? null; - GrantSecurityClassesSub = Options.userCallbacks?.grantSecurityClasses ?? null; - AbortSub = Options.userCallbacks?.abort ?? null; - break; + Result.SetResult(Error); + return Result.Task; } if (Options.strategy == Enums.InclusionStrategy.Default) @@ -889,7 +917,7 @@ public Task BeginInclusion(InclusionOptions Options) return Result.Task; } } - + if (_driver.Options != null && !_driver.Options.CheckKeyLength()) { CMDResult Res = new CMDResult(Enums.ErrorCodes.InvalidkeyLength, "Invalid Key length. All Security Keys must be a 32 character hexadecimal string (representing 16 bytes)", false); @@ -921,8 +949,8 @@ public Task BeginInclusion(InclusionOptions Options) // Checked as of : 3.5.0 public Task StopInclusion() { - Guid ID = Guid.NewGuid(); - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); _driver.Callbacks.Add(ID, (JO) => { @@ -939,12 +967,12 @@ public Task StopInclusion() return Result.Task; } - + // Checked as of : 3.5.0 public Task ProvisionSmartStartNode(SmartStartProvisioningEntry ProvisioningInformation) { - Guid ID = Guid.NewGuid(); - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); if (_driver.Options != null && _driver.Options.MissingKeys(true, true)) { @@ -999,8 +1027,8 @@ public Task ProvisionSmartStartNode(SmartStartProvisioningEntry Provi // Checked as of : 3.5.0 public Task BeginExclusion(ExclusionOptions Options) { - Guid ID = Guid.NewGuid(); - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); _driver.Callbacks.Add(ID, (JO) => { @@ -1023,8 +1051,8 @@ public Task BeginExclusion(ExclusionOptions Options) // Checked as of : 3.5.0 public Task StopExclusion() { - Guid ID = Guid.NewGuid(); - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); _driver.Callbacks.Add(ID, (JO) => { @@ -1042,12 +1070,12 @@ public Task StopExclusion() return Result.Task; } - + // Checked as of : 3.5.0 private Task _UnprovisionSmartStartNode(object dskOrNodeId) { - Guid ID = Guid.NewGuid(); - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); _driver.Callbacks.Add(ID, (JO) => { @@ -1066,7 +1094,7 @@ private Task _UnprovisionSmartStartNode(object dskOrNodeId) return Result.Task; } - + // LOCAL public Task UnprovisionSmartStartNode(int NodeID) { @@ -1078,7 +1106,7 @@ public Task UnprovisionSmartStartNode(string DSK) { return _UnprovisionSmartStartNode(DSK); } - + // LOCAL public VirtualNode GetMulticastGroup(int[] Nodes) { diff --git a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Driver.cs b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Driver.cs index 1452efa..cd1379e 100644 --- a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Driver.cs +++ b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Driver.cs @@ -21,6 +21,14 @@ public class Driver internal const string FWUSAPIKey = "921f8000486fcc2744721cfc747aab2db8fc025b5d487cbf2eba76e88ff6f79a064644bf"; internal DateTime ConnectStart; + internal TaskCompletionSource GetNewTaskCompletionSource(out Guid ID) + { + ID = Guid.NewGuid(); + TaskCompletionSource Result = new TaskCompletionSource(); + + return Result; + } + private Dictionary> NodeEventMap; private Dictionary> ControllerEventMap; private Dictionary> DriverEventMap; @@ -28,34 +36,17 @@ public class Driver private string SerialPort; private bool RequestedExit = false; private JsonSerializer _jsonSerializer; - - private Uri WSAddress; private bool Host = true; private Server _server; - private string _ZWaveJSDriverVersion; - public string ZWJSS_DriverVersion - { - get - { - return _ZWaveJSDriverVersion; - } - } - - private string _ZWaveJSServerVersion; - public string ZWJSS_ServerVersion - { - get - { - return _ZWaveJSServerVersion; - } - } - + public string ZWaveJSDriverVersion {get; internal set;} + public string ZWaveJSServerVersion {get; internal set;} public int ServerCommunicationPort { get; private set; } public Controller Controller { get; internal set; } public Utils Utils { get; internal set; } + public ConfigManager ConfigManager { get; internal set; } public delegate void DriverReadyEvent(); @@ -799,11 +790,10 @@ private void StartListetningCB(JObject JO) } } - public Task ZWJSS_StartListeningLogs() + public Task StartListeningLogs() { - Guid ID = Guid.NewGuid(); - - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = GetNewTaskCompletionSource(out ID); Callbacks.Add(ID, (JO) => { @@ -822,11 +812,10 @@ public Task ZWJSS_StartListeningLogs() return Result.Task; } - public Task ZWJSS_StopListeningLogs() + public Task StopListeningLogs() { - Guid ID = Guid.NewGuid(); - - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = GetNewTaskCompletionSource(out ID); Callbacks.Add(ID, (JO) => { @@ -847,9 +836,8 @@ public Task ZWJSS_StopListeningLogs() public Task HardReset() { - Guid ID = Guid.NewGuid(); - - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = GetNewTaskCompletionSource(out ID); Callbacks.Add(ID, (JO) => { @@ -872,9 +860,8 @@ public Task HardReset() public Task SoftReset() { - Guid ID = Guid.NewGuid(); - - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = GetNewTaskCompletionSource(out ID); Callbacks.Add(ID, (JO) => { @@ -963,8 +950,8 @@ private void WebsocketClient_MessageReceived(object sender, ResponseMessage Mess if (Type == "version") { - _ZWaveJSDriverVersion = JO.Value("driverVersion"); - _ZWaveJSServerVersion = JO.Value("serverVersion"); + ZWaveJSDriverVersion = JO.Value("driverVersion"); + ZWaveJSServerVersion = JO.Value("serverVersion"); Guid CBID = Guid.NewGuid(); Callbacks.Add(CBID, SetAPIVersionCB); diff --git a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/ZWaveNode.cs b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/ZWaveNode.cs index d53ed43..5b5b91f 100644 --- a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/ZWaveNode.cs +++ b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/ZWaveNode.cs @@ -158,9 +158,8 @@ internal void Trigger_NodeInterviewFailed(NodeInterviewFailedEventArgs Args) // Checked as of : 3.5.0 public Task Ping() { - Guid ID = Guid.NewGuid(); - - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); _driver.Callbacks.Add(ID, (JO) => { CMDResult Res = new CMDResult(JO); @@ -177,7 +176,6 @@ public Task Ping() Request.Add("command", Enums.Commands.Ping); Request.Add("nodeId", this.id); - string RequestPL = Newtonsoft.Json.JsonConvert.SerializeObject(Request); _driver.ClientWebSocket.SendInstant(RequestPL); @@ -187,9 +185,8 @@ public Task Ping() // Checked as of : 3.5.0 public Task Interview() { - Guid ID = Guid.NewGuid(); - - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); _driver.Callbacks.Add(ID, (JO) => { CMDResult Res = new CMDResult(JO); @@ -213,9 +210,8 @@ public Task CheckLifelineHealth(int Rounds, LifelineHealthCheckProgre { LifelineHealthCheckProgressSub = OnProgress; - Guid ID = Guid.NewGuid(); - - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); _driver.Callbacks.Add(ID, (JO) => { CMDResult Res = new CMDResult(JO); @@ -244,9 +240,8 @@ public Task CheckLifelineHealth(int Rounds, LifelineHealthCheckProgre // Checked as of : 3.5.0 public Task AbortFirmwareUpdate() { - Guid ID = Guid.NewGuid(); - - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); _driver.Callbacks.Add(ID, (JO) => { CMDResult Res = new CMDResult(JO); @@ -281,9 +276,8 @@ public Task UpdateFirmware(FirmwareUpdate[] Updates) } } - Guid ID = Guid.NewGuid(); - - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); _driver.Callbacks.Add(ID, (JO) => { CMDResult Res = new CMDResult(JO); @@ -310,9 +304,8 @@ public Task UpdateFirmware(FirmwareUpdate[] Updates) // Checked as of : 3.5.0 public Task RefreshInfo(RefreshInfoOptions Options = null) { - Guid ID = Guid.NewGuid(); - - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); _driver.Callbacks.Add(ID, (JO) => { CMDResult Res = new CMDResult(JO); @@ -336,9 +329,8 @@ public Task RefreshInfo(RefreshInfoOptions Options = null) // Checked as of : 3.5.0 public Task GetValue(ValueID ValueID) { - Guid ID = Guid.NewGuid(); - - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); _driver.Callbacks.Add(ID, (JO) => { CMDResult Res = new CMDResult(JO); @@ -366,9 +358,8 @@ public Task GetValue(ValueID ValueID) // Checked as of : 3.5.0 public Task SetValue(ValueID ValueID, object Value, SetValueAPIOptions Options = null) { - Guid ID = Guid.NewGuid(); - - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); _driver.Callbacks.Add(ID, (JO) => { CMDResult Res = new CMDResult(JO); @@ -401,9 +392,8 @@ public Task SetValue(ValueID ValueID, object Value, SetValueAPIOption // Checked as of : 3.5.0 public Task PollValue(ValueID ValueID) { - Guid ID = Guid.NewGuid(); - - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); _driver.Callbacks.Add(ID, (JO) => { CMDResult Res = new CMDResult(JO); @@ -428,11 +418,10 @@ public Task PollValue(ValueID ValueID) } // Checked as of : 3.5.0 - Variant 1: Normal parameter, defined in a config file - public Task ZWJSS_SetRawConfigParameterValue(int Parameter, int Value) + public Task SetRawConfigParameterValue(int Parameter, int Value) { - Guid ID = Guid.NewGuid(); - - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); _driver.Callbacks.Add(ID, (JO) => { CMDResult Res = new CMDResult(JO); @@ -454,11 +443,10 @@ public Task ZWJSS_SetRawConfigParameterValue(int Parameter, int Value } // Checked as of : 3.5.0 - Variant 2: Normal parameter, not defined in a config file - public Task ZWJSS_SetRawConfigParameterValue(int Parameter, int Value, int ValueSize, Enums.ConfigValueFormat ValueFormat) + public Task SetRawConfigParameterValue(int Parameter, int Value, int ValueSize, Enums.ConfigValueFormat ValueFormat) { - Guid ID = Guid.NewGuid(); - - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); _driver.Callbacks.Add(ID, (JO) => { CMDResult Res = new CMDResult(JO); @@ -482,11 +470,10 @@ public Task ZWJSS_SetRawConfigParameterValue(int Parameter, int Value } // Checked as of : 3.5.0 - Variant 3: Partial parameter, must be defined in a config file - public Task ZWJSS_SetRawConfigParameterValue(int Parameter, int Bitmask, int Value) + public Task SetRawConfigParameterValue(int Parameter, int Bitmask, int Value) { - Guid ID = Guid.NewGuid(); - - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); _driver.Callbacks.Add(ID, (JO) => { CMDResult Res = new CMDResult(JO); @@ -511,9 +498,8 @@ public Task ZWJSS_SetRawConfigParameterValue(int Parameter, int Bitma // Checked as of : 3.5.0 public Task RefreshValues() { - Guid ID = Guid.NewGuid(); - - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); _driver.Callbacks.Add(ID, (JO) => { CMDResult Res = new CMDResult(JO); @@ -535,9 +521,8 @@ public Task RefreshValues() // Checked as of : 3.5.0 public Task RefreshCCValues(int CommandClass) { - Guid ID = Guid.NewGuid(); - - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); _driver.Callbacks.Add(ID, (JO) => { CMDResult Res = new CMDResult(JO); @@ -560,9 +545,8 @@ public Task RefreshCCValues(int CommandClass) // Checked as of : 3.5.0 public Task GetDefinedValueIDs() { - Guid ID = Guid.NewGuid(); - - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); _driver.Callbacks.Add(ID, (JO) => { CMDResult Res = new CMDResult(JO); @@ -589,9 +573,8 @@ public Task GetDefinedValueIDs() // Checked as of : 3.5.0 public Task GetValueMetadata(ValueID VID) { - Guid ID = Guid.NewGuid(); - - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); _driver.Callbacks.Add(ID, (JO) => { CMDResult Res = new CMDResult(JO); @@ -620,9 +603,8 @@ public Task GetValueMetadata(ValueID VID) // Checked as of : 3.5.0 public Task SupportsCCAPI(int CommandClass) { - Guid ID = Guid.NewGuid(); - - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); _driver.Callbacks.Add(ID, (JO) => { CMDResult Res = new CMDResult(JO); @@ -649,9 +631,8 @@ public Task SupportsCCAPI(int CommandClass) // Checked as of : 3.5.0 public Task InvokeCCAPI(int CommandClass, string Method, params object[] Params) { - Guid ID = Guid.NewGuid(); - - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); _driver.Callbacks.Add(ID, (JO) => { CMDResult Res = new CMDResult(JO); @@ -682,9 +663,8 @@ public Task InvokeCCAPI(int CommandClass, string Method, params objec // Checked as of : 3.5.0 public Task GetEndpointCount() { - Guid ID = Guid.NewGuid(); - - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); _driver.Callbacks.Add(ID, (JO) => { @@ -710,9 +690,8 @@ public Task GetEndpointCount() // Checked as of : 3.5.0 public Task GetHighestSecurityClass() { - Guid ID = Guid.NewGuid(); - - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); _driver.Callbacks.Add(ID, (JO) => { @@ -741,9 +720,8 @@ public Task GetHighestSecurityClass() // Checked as of : 3.5.0 public Task HasSecurityClass(Enums.SecurityClass Class) { - Guid ID = Guid.NewGuid(); - - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); _driver.Callbacks.Add(ID, (JO) => { @@ -771,9 +749,8 @@ public Task HasSecurityClass(Enums.SecurityClass Class) // Checked as of : 3.5.0 public Task WaitForWakeup() { - Guid ID = Guid.NewGuid(); - - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); _driver.Callbacks.Add(ID, (JO) => { @@ -796,9 +773,8 @@ public Task WaitForWakeup() // Checked as of : 3.5.0 public Task ManuallyIdleNotificationValue(ValueID VID) { - Guid ID = Guid.NewGuid(); - - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); _driver.Callbacks.Add(ID, (JO) => { @@ -822,9 +798,8 @@ public Task ManuallyIdleNotificationValue(ValueID VID) // Checked as of : 3.5.0 public Task ManuallyIdleNotificationValue(int notificationType, int prevValue, int? endpointIndex = null) { - Guid ID = Guid.NewGuid(); - - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); _driver.Callbacks.Add(ID, (JO) => { @@ -927,9 +902,8 @@ public Endpoint GetEndpoint(int Index) public bool keepAwake { get; internal set; } public Task SetKeepAwake(bool Option) { - Guid ID = Guid.NewGuid(); - - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); _driver.Callbacks.Add(ID, (JO) => { @@ -958,9 +932,8 @@ public Task SetKeepAwake(bool Option) public string name { get; internal set; } public Task SetName(string Name, bool UpdateCC = true) { - Guid ID = Guid.NewGuid(); - - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); _driver.Callbacks.Add(ID, (JO) => { @@ -990,9 +963,8 @@ public Task SetName(string Name, bool UpdateCC = true) public string location { get; internal set; } public Task SetLocation(string Location, bool UpdateCC = true) { - Guid ID = Guid.NewGuid(); - - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); _driver.Callbacks.Add(ID, (JO) => { From 78ae2d14b1d727d15ae60cbf3e95774665d679fa Mon Sep 17 00:00:00 2001 From: Marcus Davies <55892693+marcus-j-davies@users.noreply.github.com> Date: Sat, 24 Jan 2026 14:22:29 +0000 Subject: [PATCH 14/59] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ffaed2..4257fe8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -55,7 +55,7 @@ - Internal Changes. - All responses to method calls are now dispatched asynchronously on the thread pool, so user code triggered by these responses cannot block the WebSocket message handler. - - Whilst the various methods already return Task instances, task completion is now performed asynchronously to prevent blocking the WebSocket message handler. + - Previously, the node, controller, and driver callbacks each created their own task after completing their prep work. Now the task is created upfront, and both the prep work and the callback execute inside that single task - Various optimisations to the code base for easiyer maintenance. From 9b4c820833e5ed475f626f6f31113c90f4bfa579 Mon Sep 17 00:00:00 2001 From: Marcus Davies <55892693+marcus-j-davies@users.noreply.github.com> Date: Sat, 24 Jan 2026 14:36:28 +0000 Subject: [PATCH 15/59] Further TCS's to be created from the single method --- .../ZWaveJS.NET/ZWaveJS.NET/ConfigManager.cs | 10 ++++---- .../ZWaveJS.NET/ZWaveJS.NET/Endpoint.cs | 10 ++++---- .../ZWaveJS.NET/ZWaveJS.NET/Utils.cs | 5 ++-- .../ZWaveJS.NET/ZWaveJS.NET/VirtualNode.cs | 23 +++++++++---------- 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/ConfigManager.cs b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/ConfigManager.cs index edd39f9..3bdc6fd 100644 --- a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/ConfigManager.cs +++ b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/ConfigManager.cs @@ -39,8 +39,9 @@ internal ConfigManager(Driver Driver) public Task LookupDevice(int ManufacturerID, int ProductTypeID, int ProductId) { - Guid ID = Guid.NewGuid(); - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); + _driver.Callbacks.Add(ID, (JO) => { CMDResult Res = new CMDResult(JO); @@ -68,8 +69,9 @@ public Task LookupDevice(int ManufacturerID, int ProductTypeID, int P // Checked as of : 3.5.0 public Task LookupManufacturer(int ManufacturerID) { - Guid ID = Guid.NewGuid(); - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); + _driver.Callbacks.Add(ID, (JO) => { CMDResult Res = new CMDResult(JO); diff --git a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Endpoint.cs b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Endpoint.cs index 18a403e..744b039 100644 --- a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Endpoint.cs +++ b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Endpoint.cs @@ -17,9 +17,9 @@ internal Endpoint(Driver driver = null) // Checked as of : 3.5.0 public Task SupportsCCAPI(int CommandClass) { - Guid ID = Guid.NewGuid(); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); - TaskCompletionSource Result = new TaskCompletionSource(); _driver.Callbacks.Add(ID, (JO) => { CMDResult Res = new CMDResult(JO); @@ -49,9 +49,9 @@ public Task SupportsCCAPI(int CommandClass) // Checked as of : 3.5.0 public Task InvokeCCAPI(int CommandClass, string Method, params object[] Params) { - Guid ID = Guid.NewGuid(); - - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); + _driver.Callbacks.Add(ID, (JO) => { CMDResult Res = new CMDResult(JO); diff --git a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Utils.cs b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Utils.cs index 8d9f4b7..5e638a6 100644 --- a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Utils.cs +++ b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Utils.cs @@ -19,9 +19,8 @@ internal Utils(Driver Driver) // Checked as of : 3.5.0 public Task ParseQRCodeString(string QR) { - Guid ID = Guid.NewGuid(); - - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); _driver.Callbacks.Add(ID, (JO) => { diff --git a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/VirtualNode.cs b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/VirtualNode.cs index aa67019..ffbb8b4 100644 --- a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/VirtualNode.cs +++ b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/VirtualNode.cs @@ -17,9 +17,8 @@ internal VirtualNode(Driver driver, int[] Nodes) // Checked as of : 3.5.0 public Task GetEndpointCount() { - Guid ID = Guid.NewGuid(); - - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); _driver.Callbacks.Add(ID, (JO) => { @@ -45,9 +44,9 @@ public Task GetEndpointCount() // Checked as of : 3.5.0 public Task SetValue(ValueID ValueID, object Value, SetValueAPIOptions Options = null) { - Guid ID = Guid.NewGuid(); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); - TaskCompletionSource Result = new TaskCompletionSource(); _driver.Callbacks.Add(ID, (JO) => { CMDResult Res = new CMDResult(JO); @@ -79,9 +78,9 @@ public Task SetValue(ValueID ValueID, object Value, SetValueAPIOption // Checked as of : 3.5.0 public Task GetDefinedValueIDs() { - Guid ID = Guid.NewGuid(); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); - TaskCompletionSource Result = new TaskCompletionSource(); _driver.Callbacks.Add(ID, (JO) => { CMDResult Res = new CMDResult(JO); @@ -108,9 +107,9 @@ public Task GetDefinedValueIDs() // Checked as of : 3.5.0 public Task SupportsCCAPI(int CommandClass) { - Guid ID = Guid.NewGuid(); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); - TaskCompletionSource Result = new TaskCompletionSource(); _driver.Callbacks.Add(ID, (JO) => { CMDResult Res = new CMDResult(JO); @@ -137,9 +136,9 @@ public Task SupportsCCAPI(int CommandClass) // Checked as of : 3.5.0 public Task InvokeCCAPI(int CommandClass, string Method, params object[] Params) { - Guid ID = Guid.NewGuid(); - - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); + _driver.Callbacks.Add(ID, (JO) => { CMDResult Res = new CMDResult(JO); From ef2d3f12d41334512dbb022b3c296e310df08170 Mon Sep 17 00:00:00 2001 From: Marcus Davies <55892693+marcus-j-davies@users.noreply.github.com> Date: Sat, 24 Jan 2026 15:42:43 +0000 Subject: [PATCH 16/59] Optimise all inclusion & Replace requirements --- .../ZWaveJS.NET/ZWaveJS.NET/Controller.cs | 189 ++++++------------ .../ZWaveJS.NET/ZWaveJS.NET/ZWaveOptions.cs | 4 +- 2 files changed, 62 insertions(+), 131 deletions(-) diff --git a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Controller.cs b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Controller.cs index e0357fe..4532e56 100644 --- a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Controller.cs +++ b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Controller.cs @@ -166,13 +166,57 @@ private CMDResult ValidateStrategy(Enums.InclusionStrategy strategy) if (requiresCallbacks && (ValidateDSKAndEnterPINSub == null || GrantSecurityClassesSub == null || AbortSub == null)) { - return new CMDResult(Enums.ErrorCodes.MissingS2Callbacks,"S2 Security requires userCallbacks [validateDSKAndEnterPIN, grantSecurityClasses, abort]",false); + return new CMDResult(Enums.ErrorCodes.MissingS2Callbacks, "S2 Security requires userCallbacks [validateDSKAndEnterPIN, grantSecurityClasses, abort]", false); } + return null; + } + + private CMDResult ValidateLRKeys() + { + if (_driver.Options == null) + return null; + + return _driver.Options.MissingLRKeys() ? new CMDResult(Enums.ErrorCodes.MissingKeys, "Missing Security Keys in Options (LongRange)", false) : null; + } + + + private CMDResult ValidateKeys(Enums.InclusionStrategy strategy) + { + if (_driver.Options == null) + return null; + + return strategy switch + { + Enums.InclusionStrategy.Default => + _driver.Options.MissingKeys(true, true) + ? new CMDResult(Enums.ErrorCodes.MissingKeys, "Missing Security Keys in Options (SO, S2)", false) + : null, + + Enums.InclusionStrategy.Security_S2 => + _driver.Options.MissingKeys(true, false) + ? new CMDResult(Enums.ErrorCodes.MissingKeys, "Missing Security Keys in Options (S2)", false) + : null, + + Enums.InclusionStrategy.Security_S0 => + _driver.Options.MissingKeys(false, true) + ? new CMDResult(Enums.ErrorCodes.MissingKeys, "Missing Security Keys in Options (SO)", false) + : null, + + _ => null + }; + } + private CMDResult ValidateKeyLength() + { + if (_driver.Options != null && !_driver.Options.CheckKeyLength()) + { + CMDResult Res = new CMDResult(Enums.ErrorCodes.InvalidkeyLength, "Invalid Key length. All Security Keys must be a 32 character hexadecimal string (representing 16 bytes)", false); + return Res; + } return null; } - + // Checked as of : 3.5.0 public Task GetAvailableFirmwareUpdates(int NodeID, bool IncludePrereleases, UsageEnvironment Environment, string APIKey = null) { @@ -673,63 +717,27 @@ public Task BackupNVMRaw(BackupNVMProgress OnProgress = null) // Checked as of : 3.5.0 public Task ReplaceFailedNode(int NodeID, InclusionOptions Options) { - ValidateDSKAndEnterPINSub = null; - GrantSecurityClassesSub = null; - AbortSub = null; Guid ID; TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); - switch (Options.strategy) - { - case Enums.InclusionStrategy.Default: - CMDResult Res = new CMDResult(Enums.ErrorCodes.InvalidStrategy, "Invalid Strategy for 'ReplaceFailedNode' Valid Strategies are : [Insecure, Security_S0, Security_S2]", false); - Result.SetResult(Res); - return Result.Task; - - case Enums.InclusionStrategy.Security_S2: - ValidateDSKAndEnterPINSub = Options.userCallbacks?.validateDSKAndEnterPIN ?? null; - GrantSecurityClassesSub = Options.userCallbacks?.grantSecurityClasses ?? null; - AbortSub = Options.userCallbacks?.abort ?? null; - break; - - } - - if (Options.strategy == Enums.InclusionStrategy.Security_S2) - { - if (ValidateDSKAndEnterPINSub == null || GrantSecurityClassesSub == null || AbortSub == null) - { - CMDResult Res = new CMDResult(Enums.ErrorCodes.MissingS2Callbacks, "S2 Security require userCallbacks to be provided [validateDSKAndEnterPIN, grantSecurityClasses, abort]", false); - Result.SetResult(Res); - return Result.Task; - } - - if (_driver.Options != null && _driver.Options.MissingKeys(true, false)) - { - CMDResult Res = new CMDResult(Enums.ErrorCodes.MissingKeys, "Missing Security Keys in Options", false); - Result.SetResult(Res); - return Result.Task; - } - } + ResetInclusionCallbacks(); + ExtractInclusionCallbacks(Options); - if (Options.strategy == Enums.InclusionStrategy.Security_S0) + CMDResult Error = ValidateStrategy(Options.strategy) ?? ValidateKeys(Options.strategy) ?? ValidateKeyLength(); + if (Error != null) { - if (_driver.Options != null && _driver.Options.MissingKeys(false, true)) - { - CMDResult Res = new CMDResult(Enums.ErrorCodes.MissingKeys, "Missing Security Keys in Options", false); - Result.SetResult(Res); - return Result.Task; - } + Result.SetResult(Error); + return Result.Task; } - if (_driver.Options != null && !_driver.Options.CheckKeyLength()) + if (Options.strategy == InclusionStrategy.Default) { - CMDResult Res = new CMDResult(Enums.ErrorCodes.InvalidkeyLength, "Invalid Key length. All Security Keys must be a 32 character hexadecimal string (representing 16 bytes)", false); + CMDResult Res = new CMDResult(Enums.ErrorCodes.InvalidStrategy, "Invalid Strategy for 'ReplaceFailedNode' Valid Strategies are : [Insecure, Security_S0, Security_S2]", false); Result.SetResult(Res); return Result.Task; } - _driver.Callbacks.Add(ID, (JO) => { CMDResult Res = new CMDResult(JO); @@ -864,67 +872,13 @@ public Task BeginInclusion(InclusionOptions Options) ResetInclusionCallbacks(); ExtractInclusionCallbacks(Options); - CMDResult Error = ValidateStrategy(Options.strategy); - - if(Error != null) + CMDResult Error = ValidateStrategy(Options.strategy) ?? ValidateKeys(Options.strategy) ?? ValidateKeyLength(); + if (Error != null) { Result.SetResult(Error); return Result.Task; } - if (Options.strategy == Enums.InclusionStrategy.Default) - { - - if (ValidateDSKAndEnterPINSub == null || GrantSecurityClassesSub == null || AbortSub == null) - { - CMDResult Res = new CMDResult(Enums.ErrorCodes.MissingS2Callbacks, "S2 Security require userCallbacks to be provided [validateDSKAndEnterPIN, grantSecurityClasses, abort]", false); - Result.SetResult(Res); - return Result.Task; - } - - if (_driver.Options != null && _driver.Options.MissingKeys(true, true)) - { - CMDResult Res = new CMDResult(Enums.ErrorCodes.MissingKeys, "Missing Security Keys in Options", false); - Result.SetResult(Res); - return Result.Task; - } - } - - if (Options.strategy == Enums.InclusionStrategy.Security_S2) - { - - if (ValidateDSKAndEnterPINSub == null || GrantSecurityClassesSub == null || AbortSub == null) - { - CMDResult Res = new CMDResult(Enums.ErrorCodes.MissingS2Callbacks, "S2 Security require userCallbacks to be provided [validateDSKAndEnterPIN, grantSecurityClasses, abort]", false); - Result.SetResult(Res); - return Result.Task; - } - - if (_driver.Options != null && _driver.Options.MissingKeys(true, false)) - { - CMDResult Res = new CMDResult(Enums.ErrorCodes.MissingKeys, "Missing Security Keys in Options", false); - Result.SetResult(Res); - return Result.Task; - } - } - - if (Options.strategy == Enums.InclusionStrategy.Security_S0) - { - if (_driver.Options != null && _driver.Options.MissingKeys(false, true)) - { - CMDResult Res = new CMDResult(Enums.ErrorCodes.MissingKeys, "Missing Security Keys in Options", false); - Result.SetResult(Res); - return Result.Task; - } - } - - if (_driver.Options != null && !_driver.Options.CheckKeyLength()) - { - CMDResult Res = new CMDResult(Enums.ErrorCodes.InvalidkeyLength, "Invalid Key length. All Security Keys must be a 32 character hexadecimal string (representing 16 bytes)", false); - Result.SetResult(Res); - return Result.Task; - } - _driver.Callbacks.Add(ID, (JO) => { CMDResult Res = new CMDResult(JO); @@ -974,38 +928,15 @@ public Task ProvisionSmartStartNode(SmartStartProvisioningEntry Provi Guid ID; TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); - if (_driver.Options != null && _driver.Options.MissingKeys(true, true)) - { - CMDResult Res = new CMDResult(Enums.ErrorCodes.MissingKeys, "Missing Security Keys in Options", false); - Result.SetResult(Res); - return Result.Task; - } + CMDResult KeysCheckType = ProvisioningInformation.protocol == Protocols.ZWave ? ValidateKeys(InclusionStrategy.Security_S2) : ValidateLRKeys(); - if (_driver.Options != null && !_driver.Options.CheckKeyLength()) + CMDResult Error = KeysCheckType ?? ValidateKeyLength(); + if (Error != null) { - CMDResult Res = new CMDResult(Enums.ErrorCodes.InvalidkeyLength, "Invalid Key length. All Security Keys must be a 32 character hexadecimal string (representing 16 bytes)", false); - Result.SetResult(Res); + Result.SetResult(Error); return Result.Task; } - if (ProvisioningInformation.protocol == Protocols.ZWaveLongRange) - { - if (_driver.Options != null && _driver.Options.MissingLRKeys()) - { - CMDResult Res = new CMDResult(Enums.ErrorCodes.MissingKeys, "Missing LR Security Keys in Options", false); - Result.SetResult(Res); - return Result.Task; - } - - - if (_driver.Options != null && !_driver.Options.CheckKeyLengthLR()) - { - CMDResult Res = new CMDResult(Enums.ErrorCodes.InvalidkeyLength, "Invalid Key length. All Security Keys must be a 32 character hexadecimal string (representing 16 bytes)", false); - Result.SetResult(Res); - return Result.Task; - } - } - _driver.Callbacks.Add(ID, (JO) => { CMDResult Res = new CMDResult(JO); diff --git a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/ZWaveOptions.cs b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/ZWaveOptions.cs index 12ec11d..a3baf5a 100644 --- a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/ZWaveOptions.cs +++ b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/ZWaveOptions.cs @@ -168,7 +168,7 @@ internal bool MissingKeys(bool IncludeS2, bool IncludeS0) } - internal bool CheckKeyLengthLR() + private bool CheckKeyLengthLR() { if (this.securityKeysLongRange != null && this.securityKeysLongRange.S2_AccessControl != null && this.securityKeysLongRange.S2_AccessControl.Length != 32) return false; @@ -194,7 +194,7 @@ internal bool CheckKeyLength() if (this.securityKeys != null && this.securityKeys.S2_Unauthenticated != null && this.securityKeys.S2_Unauthenticated.Length != 32) return false; - return true; + return CheckKeyLengthLR(); } } From 40664a9a3e209d59aa523336ad7361f2a963dcde Mon Sep 17 00:00:00 2001 From: Marcus Davies <55892693+marcus-j-davies@users.noreply.github.com> Date: Sat, 24 Jan 2026 15:53:23 +0000 Subject: [PATCH 17/59] NET10 --- CHANGELOG.md | 1 + .../ZWaveJS.NET/ZWaveJS.NET/ZWaveJS.NET.csproj | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4257fe8..8cb05fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ - net7.0 - net8.0 - net9.0 + - net10.0 - netstandard2.1 - Driver class init signature changes diff --git a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/ZWaveJS.NET.csproj b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/ZWaveJS.NET.csproj index 6fbdbe9..f110971 100644 --- a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/ZWaveJS.NET.csproj +++ b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/ZWaveJS.NET.csproj @@ -1,7 +1,7 @@ - net6.0;net7.0;net8.0;net9.0;netstandard2.1 + net6.0;net7.0;net8.0;net9.0;net10.0;netstandard2.1 5.0.0 Marcus Davies true From 099955b0f7574c0415250b2edd3a248051e56589 Mon Sep 17 00:00:00 2001 From: Marcus Davies <55892693+marcus-j-davies@users.noreply.github.com> Date: Sat, 24 Jan 2026 16:03:23 +0000 Subject: [PATCH 18/59] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8cb05fc..baf1566 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -58,6 +58,7 @@ - All responses to method calls are now dispatched asynchronously on the thread pool, so user code triggered by these responses cannot block the WebSocket message handler. - Previously, the node, controller, and driver callbacks each created their own task after completing their prep work. Now the task is created upfront, and both the prep work and the callback execute inside that single task - Various optimisations to the code base for easiyer maintenance. + - Reverted to using the actively maintained Websocket.Client library. Previously, we were relying on an outdated source‑based code copy From b018ad4e9b0a934af4744fdeca92bdb5970c90b5 Mon Sep 17 00:00:00 2001 From: Marcus Davies <55892693+marcus-j-davies@users.noreply.github.com> Date: Sat, 24 Jan 2026 16:38:19 +0000 Subject: [PATCH 19/59] Address conflicts --- CHANGELOG.md | 3 +- .../ZWaveJS.NET/ZWaveJS.NET/Controller.cs | 51 +------------------ .../ZWaveJS.NET/ZWaveJS.NET/Driver.cs | 4 +- .../ZWaveJS.NET/ZWaveJS.NET/MethodFactory.cs | 15 +++--- 4 files changed, 11 insertions(+), 62 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index baf1566..de66856 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,10 +49,11 @@ ZWDNET-ER-09 : Use of incorrect override ``` - - The following methods have been renamed + - The following methods/events have been renamed - ```ZWJSS_SetRawConfigParameterValue``` -> ```SetRawConfigParameterValue``` - ```ZWJSS_StartListeningLogs``` -> ```StartListeningLogs``` - ```ZWJSS_StopListeningLogs``` -> ```StopListeningLogs``` + - ```ZWJSS_LoggingEvent``` -> ```LoggingEvent``` - Internal Changes. - All responses to method calls are now dispatched asynchronously on the thread pool, so user code triggered by these responses cannot block the WebSocket message handler. diff --git a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Controller.cs b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Controller.cs index d5e2988..93f6feb 100644 --- a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Controller.cs +++ b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Controller.cs @@ -233,7 +233,7 @@ public Task GetAvailableFirmwareUpdates(int NodeID, bool IncludePrere _driver.Callbacks.Add(ID, (JO) => { CMDResult Res = new CMDResult(JO); - + if (Res.Success) { @@ -336,12 +336,6 @@ public Task SetRFRegion(Enums.RFRegion Region) return Result.Task; } - - // Checked as of : 3.5.0 - public Task SetMaxLongRangePowerlevel(decimal Limit) - { - Guid ID = Guid.NewGuid(); - TaskCompletionSource Result = new TaskCompletionSource(); // Checked as of : 3.5.0 public Task SetMaxLongRangePowerlevel(decimal Limit) @@ -1008,49 +1002,6 @@ public Task StopExclusion() return Result.Task; } - - // Checked as of : 3.5.0 - private Task _UnprovisionSmartStartNode(object dskOrNodeId) - { - Guid ID = Guid.NewGuid(); - TaskCompletionSource Result = new TaskCompletionSource(); - - _driver.Callbacks.Add(ID, (JO) => - { - CMDResult Res = new CMDResult(JO); - Result.SetResult(Res); - }); - - Dictionary Request = new Dictionary(); - - Request.Add("messageId", ID); - Request.Add("command", Enums.Commands.UnprovisionSmartStartNode); - Request.Add("dskOrNodeId", dskOrNodeId); - - string RequestPL = Newtonsoft.Json.JsonConvert.SerializeObject(Request); - _driver.ClientWebSocket.SendInstant(RequestPL); - - return Result.Task; - } - - // LOCAL - public Task UnprovisionSmartStartNode(int NodeID) - { - return _UnprovisionSmartStartNode(NodeID); - } - - // LOCAL - public Task UnprovisionSmartStartNode(string DSK) - { - return _UnprovisionSmartStartNode(DSK); - } - - // LOCAL - public VirtualNode GetMulticastGroup(int[] Nodes) - { - VirtualNode VN = new VirtualNode(_driver, Nodes); - return VN; - } // Checked as of : 3.5.0 private Task _UnprovisionSmartStartNode(object dskOrNodeId) diff --git a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Driver.cs b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Driver.cs index cd1379e..01bb2c6 100644 --- a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Driver.cs +++ b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Driver.cs @@ -56,10 +56,10 @@ internal TaskCompletionSource GetNewTaskCompletionSource(out Guid ID) public event ServerConnectionErrorEvent ServerConnectionError; public delegate void LoggingEventDelegate(LoggingEventArgs args); - public event LoggingEventDelegate ZWJSS_LoggingEvent; + public event LoggingEventDelegate LoggingEvent; internal void Trigger_LoggingEvent(LoggingEventArgs args) { - ZWJSS_LoggingEvent?.Invoke(args); + LoggingEvent?.Invoke(args); } private void MapNodeEvents() diff --git a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/MethodFactory.cs b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/MethodFactory.cs index 378d7ce..b767146 100644 --- a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/MethodFactory.cs +++ b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/MethodFactory.cs @@ -27,9 +27,8 @@ public static Func, Task> CreateCLASS(Driv private static Task Execute(Driver Runtime, string ServerMethod, Dictionary Args, string ObjectPath) { - Guid ID = Guid.NewGuid(); - - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = Runtime.GetNewTaskCompletionSource(out ID); Runtime.Callbacks.Add(ID, (JO) => { @@ -57,9 +56,8 @@ private static Task Execute(Driver Runtime, string ServerMethod, Dict private static Task Execute(Driver Runtime, string ServerMethod, Dictionary Args, Type MappedClass, string ObjectPath) { - Guid ID = Guid.NewGuid(); - - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = Runtime.GetNewTaskCompletionSource(out ID); Runtime.Callbacks.Add(ID, (JO) => { @@ -87,9 +85,8 @@ private static Task Execute(Driver Runtime, string ServerMethod, Dict private static Task Execute(Driver Runtime, string ServerMethod, Dictionary Args) { - Guid ID = Guid.NewGuid(); - - TaskCompletionSource Result = new TaskCompletionSource(); + Guid ID; + TaskCompletionSource Result = Runtime.GetNewTaskCompletionSource(out ID); Runtime.Callbacks.Add(ID, (JO) => { From 1a146c9f87e0f3c2dc87a696320a72dd926674d8 Mon Sep 17 00:00:00 2001 From: Marcus Davies <55892693+marcus-j-davies@users.noreply.github.com> Date: Sat, 24 Jan 2026 21:11:21 +0000 Subject: [PATCH 20/59] Options --- CHANGELOG.md | 6 +++ .../ZWaveJS.NET/ZWaveJS.NET/ZWaveOptions.cs | 46 +++++++++++++++---- 2 files changed, 42 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index de66856..b69a46a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -61,6 +61,12 @@ - Various optimisations to the code base for easiyer maintenance. - Reverted to using the actively maintained Websocket.Client library. Previously, we were relying on an outdated source‑based code copy + - New Features. + - Exposed further Zwave Options + - ```preferences``` + - ```attempts.smartStartInclusion``` + - ```attempts.firmwareUpdateOTW``` + diff --git a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/ZWaveOptions.cs b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/ZWaveOptions.cs index a3baf5a..2a322b4 100644 --- a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/ZWaveOptions.cs +++ b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/ZWaveOptions.cs @@ -17,6 +17,7 @@ public ZWaveOptions() this.disableOptimisticValueUpdate = false; this.emitValueUpdateAfterSetValue = false; this.features = new CFGFeatures(); + this.preferences = new Preferences(); } @@ -40,7 +41,28 @@ public static ZWaveOptions FromSerialized(string JSON) public bool disableOptimisticValueUpdate { get; set; } public bool emitValueUpdateAfterSetValue { get; set; } public CFGFeatures features { get; set; } + public Preferences preferences { get; set; } + public class ZWOptionScales + { + public ZWOptionScales() + { + this.humidity = 0x00; + this.temperature = 0x00; + } + public int temperature { get; set; } + public int humidity { get; set; } + } + + public class Preferences + { + public Preferences() + { + this.scales = new ZWOptionScales(); + } + + public ZWOptionScales scales { get; set; } + } public class CFGTimeouts { @@ -55,15 +77,17 @@ public class CFGTimeouts public int? sendToSleep { get; set; } public int? serialAPIStarted { get; set; } } - + public class CFGAttempts { public int? controller { get; set; } public int? sendData { get; set; } public int? sendDataJammed { get; set; } public int? nodeInterview { get; set; } + public int? smartStartInclusion { get; set; } + public int? firmwareUpdateOTW { get; set; } } - + public class CFGLogConfig { public CFGLogConfig() @@ -80,19 +104,21 @@ public CFGLogConfig() public int[] nodeFilter { get; set; } public string filename { get; set; } } - + public class CFGInterview { public CFGInterview() { this.queryAllUserCodes = false; this.disableOnNodeAdded = false; + this.applyRecommendedConfigParamValues = false; } public bool queryAllUserCodes { get; set; } public bool disableOnNodeAdded { get; set; } + public bool applyRecommendedConfigParamValues { get; set; } } - + public class CFGStorage { public CFGStorage() @@ -106,7 +132,7 @@ public CFGStorage() public string throttle { get; set; } public string deviceConfigExternalDir { get; set; } } - + public class CFGSecurityKeys { public string S2_Unauthenticated { get; set; } @@ -114,7 +140,7 @@ public class CFGSecurityKeys public string S2_AccessControl { get; set; } public string S0_Legacy { get; set; } } - + public class CFGSecurityKeysLR { public string S2_Authenticated { get; set; } @@ -128,7 +154,7 @@ public CFGFeatures() this.softReset = true; this.unresponsiveControllerRecovery = true; } - + public bool softReset { get; set; } public bool unresponsiveControllerRecovery { get; set; } } @@ -143,13 +169,13 @@ internal bool MissingLRKeys() if (this.securityKeysLongRange.S2_Authenticated == null) return true; - + return false; } internal bool MissingKeys(bool IncludeS2, bool IncludeS0) { - if(this.securityKeys == null) + if (this.securityKeys == null) return true; if (this.securityKeys.S0_Legacy == null && IncludeS0) @@ -198,5 +224,5 @@ internal bool CheckKeyLength() } } - + } From 2dd604cf7c94cd6192df69319837bcc5e0ebde93 Mon Sep 17 00:00:00 2001 From: Marcus Davies <55892693+marcus-j-davies@users.noreply.github.com> Date: Sat, 24 Jan 2026 21:20:44 +0000 Subject: [PATCH 21/59] Update ZWaveOptions.cs --- .../ZWaveJS.NET/ZWaveJS.NET/ZWaveOptions.cs | 29 ++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/ZWaveOptions.cs b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/ZWaveOptions.cs index 2a322b4..1221df7 100644 --- a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/ZWaveOptions.cs +++ b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/ZWaveOptions.cs @@ -17,7 +17,7 @@ public ZWaveOptions() this.disableOptimisticValueUpdate = false; this.emitValueUpdateAfterSetValue = false; this.features = new CFGFeatures(); - this.preferences = new Preferences(); + this.preferences = new CFGPreferences(); } @@ -41,27 +41,30 @@ public static ZWaveOptions FromSerialized(string JSON) public bool disableOptimisticValueUpdate { get; set; } public bool emitValueUpdateAfterSetValue { get; set; } public CFGFeatures features { get; set; } - public Preferences preferences { get; set; } + public CFGPreferences preferences { get; set; } - public class ZWOptionScales + + public class CFGPreferences { - public ZWOptionScales() + public CFGPreferences() { - this.humidity = 0x00; - this.temperature = 0x00; + this.scales = new CFGScales(); + this.lookupUserIdInNotificationEvents = false; } - public int temperature { get; set; } - public int humidity { get; set; } + + public CFGScales scales { get; set; } + public bool lookupUserIdInNotificationEvents { get; set; } } - public class Preferences + public class CFGScales { - public Preferences() + public CFGScales() { - this.scales = new ZWOptionScales(); + this.humidity = 0x00; + this.temperature = 0x00; } - - public ZWOptionScales scales { get; set; } + public int temperature { get; set; } + public int humidity { get; set; } } public class CFGTimeouts From 53d47a9c2d950c71ba3470cb6e6cdee157a9146d Mon Sep 17 00:00:00 2001 From: Marcus Davies <55892693+marcus-j-davies@users.noreply.github.com> Date: Sun, 25 Jan 2026 09:43:12 +0000 Subject: [PATCH 22/59] Move S2 --- CHANGELOG.md | 9 ++- .../ZWaveJS.NET/ZWaveJS.NET/Controller.cs | 47 +--------------- .../ZWaveJS.NET/ZWaveJS.NET/Driver.cs | 28 +++++++--- .../ZWaveJS.NET/ZWaveJS.NET/Structures.cs | 55 +++++++++---------- .../ZWaveJS.NET/ZWaveJS.NET/ZWaveOptions.cs | 14 +++-- 5 files changed, 67 insertions(+), 86 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b69a46a..f305870 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,9 +13,16 @@ - net10.0 - netstandard2.1 + - Moved the S2 callbacks to the ```ZWaveOptions``` class + This falls inline with the Driver API Settings object, So ```InclusionOptions``` no longer has these properties. + Additionally, running the library in Client mode, now requires the Driver construct take an argument of the callbacks object. + this also applies to ```ZWaveOptions.FromSerialized``` + + A null check is executed during any inclusion that require these callbacks. + - Driver class init signature changes - ```public Driver(string SerialPort, ZWaveOptions Options, int ServerCommunicationPort = 50001)``` - - ```public Driver(Uri Server, int SchemaVersion = 0)``` + - ```public Driver(Uri Server, InclusionUserCallbacks S2Callbacks, int SchemaVersion = 0)``` - Re-engineered error/connection handling. All Driver/Server error handling, is now handled through the **ServerConnectionError** event. diff --git a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Controller.cs b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Controller.cs index 93f6feb..42fceba 100644 --- a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Controller.cs +++ b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Controller.cs @@ -59,25 +59,7 @@ internal void Trigger_RebuildRoutesDone(RebuildRoutesDoneArgs Args) this.isRebuildingRoutes = false; RebuildRoutesDone?.Invoke(Args); } - - private Abort AbortSub; - internal void Trigger_InclusionAborted() - { - AbortSub?.Invoke(); - } - - private ValidateDSKAndEnterPIN ValidateDSKAndEnterPINSub; - internal string Trigger_ValidateDSK(string DSK) - { - return ValidateDSKAndEnterPINSub?.Invoke(DSK); - } - - private GrantSecurityClasses GrantSecurityClassesSub; - internal InclusionGrant Trigger_GrantSecurityClasses(InclusionGrant Requested) - { - return GrantSecurityClassesSub?.Invoke(Requested); - } - + public delegate void InclusionStartedEvent(bool Secure); public event InclusionStartedEvent InclusionStarted; internal void Trigger_InclusionStarted(bool Secure) @@ -140,31 +122,14 @@ internal void Trigger_FirmwareUpdateProgress(ControllerFirmwareUpdateProgressArg { FirmwareUpdateProgress?.Invoke(Args); } - - private void ResetInclusionCallbacks() - { - ValidateDSKAndEnterPINSub = null; - GrantSecurityClassesSub = null; - AbortSub = null; - } - - private void ExtractInclusionCallbacks(InclusionOptions options) - { - if (options.strategy == Enums.InclusionStrategy.Default || options.strategy == Enums.InclusionStrategy.Security_S2) - { - ValidateDSKAndEnterPINSub = options.userCallbacks?.validateDSKAndEnterPIN; - GrantSecurityClassesSub = options.userCallbacks?.grantSecurityClasses; - AbortSub = options.userCallbacks?.abort; - } - } - + private CMDResult ValidateStrategy(Enums.InclusionStrategy strategy) { bool requiresCallbacks = strategy == Enums.InclusionStrategy.Default || strategy == Enums.InclusionStrategy.Security_S2; - if (requiresCallbacks && (ValidateDSKAndEnterPINSub == null || GrantSecurityClassesSub == null || AbortSub == null)) + if (requiresCallbacks && (_driver.S2Callbacks?.validateDSKAndEnterPIN == null || _driver.S2Callbacks?.grantSecurityClasses == null || _driver.S2Callbacks?.abort == null)) { return new CMDResult(Enums.ErrorCodes.MissingS2Callbacks, "S2 Security requires userCallbacks [validateDSKAndEnterPIN, grantSecurityClasses, abort]", false); } @@ -722,9 +687,6 @@ public Task ReplaceFailedNode(int NodeID, InclusionOptions Options) Guid ID; TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); - ResetInclusionCallbacks(); - ExtractInclusionCallbacks(Options); - CMDResult Error = ValidateStrategy(Options.strategy) ?? ValidateKeys(Options.strategy) ?? ValidateKeyLength(); if (Error != null) { @@ -870,9 +832,6 @@ public Task BeginInclusion(InclusionOptions Options) Guid ID; TaskCompletionSource Result = _driver.GetNewTaskCompletionSource(out ID); - ResetInclusionCallbacks(); - ExtractInclusionCallbacks(Options); - CMDResult Error = ValidateStrategy(Options.strategy) ?? ValidateKeys(Options.strategy) ?? ValidateKeyLength(); if (Error != null) { diff --git a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Driver.cs b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Driver.cs index 01bb2c6..b39db8b 100644 --- a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Driver.cs +++ b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Driver.cs @@ -16,11 +16,9 @@ public class Driver internal WebsocketClient ClientWebSocket; internal Dictionary> Callbacks; - internal bool Inited = false; internal ZWaveOptions Options; internal const string FWUSAPIKey = "921f8000486fcc2744721cfc747aab2db8fc025b5d487cbf2eba76e88ff6f79a064644bf"; - internal DateTime ConnectStart; - + internal InclusionUserCallbacks S2Callbacks; internal TaskCompletionSource GetNewTaskCompletionSource(out Guid ID) { ID = Guid.NewGuid(); @@ -39,9 +37,12 @@ internal TaskCompletionSource GetNewTaskCompletionSource(out Guid ID) private Uri WSAddress; private bool Host = true; private Server _server; + private DateTime ConnectStart; + private bool Inited = false; + - public string ZWaveJSDriverVersion {get; internal set;} - public string ZWaveJSServerVersion {get; internal set;} + public string ZWaveJSDriverVersion { get; internal set; } + public string ZWaveJSServerVersion { get; internal set; } public int ServerCommunicationPort { get; private set; } public Controller Controller { get; internal set; } @@ -307,7 +308,7 @@ private void MapControllerEvents() ControllerEventMap.Add("inclusion aborted", (JO) => { - this.Controller.Trigger_InclusionAborted(); + S2Callbacks?.abort?.Invoke(); }); @@ -377,7 +378,11 @@ private void MapControllerEvents() { InclusionGrant RIG = JO.SelectToken("event.requested").ToObject(); - InclusionGrant SIG = this.Controller.Trigger_GrantSecurityClasses(RIG); + InclusionGrant SIG = S2Callbacks?.grantSecurityClasses?.Invoke(RIG); + + if (SIG == null) + return; + Dictionary Request = new Dictionary(); Request.Add("messageId", Guid.NewGuid().ToString()); @@ -393,7 +398,10 @@ private void MapControllerEvents() ControllerEventMap.Add("validate dsk and enter pin", (JO) => { - string DSK = this.Controller.Trigger_ValidateDSK(JO.SelectToken("event.dsk").ToObject()); + string DSK = S2Callbacks?.validateDSKAndEnterPIN?.Invoke(JO.SelectToken("event.dsk").ToObject()); + + if (DSK == null) + return; Dictionary Request = new Dictionary(); Request.Add("messageId", Guid.NewGuid().ToString()); @@ -470,7 +478,7 @@ private void MapEvents() } // Client Mode - public Driver(Uri Server, int SchemaVersion = 0) + public Driver(Uri Server, InclusionUserCallbacks S2Callbacks, int SchemaVersion = 0) { Newtonsoft.Json.JsonConvert.DefaultSettings = () => new JsonSerializerSettings { @@ -492,6 +500,7 @@ public Driver(Uri Server, int SchemaVersion = 0) Callbacks = new Dictionary>(); MapEvents(); + this.S2Callbacks = S2Callbacks; this.WSAddress = Server; this.Host = false; @@ -526,6 +535,7 @@ public Driver(string SerialPort, ZWaveOptions Options, int ServerCommunicationPo this.SerialPort = SerialPort; this.Options = Options; + this.S2Callbacks = Options.inclusionUserCallbacks; this.ServerCommunicationPort = ServerCommunicationPort; this.WSAddress = new Uri("ws://localhost:" + ServerCommunicationPort); this.Host = true; diff --git a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Structures.cs b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Structures.cs index 73b555c..3503b83 100644 --- a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Structures.cs +++ b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Structures.cs @@ -8,6 +8,17 @@ namespace ZWaveJS.NET { + public delegate string ValidateDSKAndEnterPIN(string dsk); + public delegate InclusionGrant GrantSecurityClasses(InclusionGrant requested); + public delegate void Abort(); + + public class InclusionUserCallbacks + { + public ValidateDSKAndEnterPIN validateDSKAndEnterPIN { get; set; } + public GrantSecurityClasses grantSecurityClasses { get; set; } + public Abort abort { get; set; } + } + public class QRProvisioningInformation { internal QRProvisioningInformation() { } @@ -65,8 +76,8 @@ internal FirmwareUpdateFileInfo() { } [Newtonsoft.Json.JsonProperty] public string integrity { get; internal set; } } - - + + public class FirmwareUpdateDeviceID { @@ -134,7 +145,7 @@ internal ControllerFirmwareUpdateResultArgs() { } [Newtonsoft.Json.JsonProperty] public bool success { get; internal set; } [Newtonsoft.Json.JsonProperty] - public Enums.ControllerFirmwareUpdateStatus status { get; internal set; } + public Enums.ControllerFirmwareUpdateStatus status { get; internal set; } } @@ -164,7 +175,7 @@ internal NodeFirmwareUpdateProgressArgs() { } [Newtonsoft.Json.JsonProperty] public int currentFile { get; internal set; } [Newtonsoft.Json.JsonProperty] - public int totalFiles { get; internal set; } + public int totalFiles { get; internal set; } } public class SmartStartProvisioningEntry @@ -173,11 +184,11 @@ internal SmartStartProvisioningEntry() { } public SmartStartProvisioningEntry(QRProvisioningInformation ProvisioningInformation, Protocols protocol = Protocols.ZWave, ProvisioningEntryStatus status = ProvisioningEntryStatus.Active) { - if(!ProvisioningInformation.supportedProtocols.Contains(protocol)) + if (!ProvisioningInformation.supportedProtocols.Contains(protocol)) { throw new NotSupportedException("The provided protocol is not supported by this device."); } - + this.dsk = ProvisioningInformation.dsk; this.securityClasses = ProvisioningInformation.securityClasses; this.requestedSecurityClasses = ProvisioningInformation.securityClasses; @@ -205,7 +216,7 @@ public SmartStartProvisioningEntry(string dsk, SecurityClass[] securityClasses, [Newtonsoft.Json.JsonProperty] public Protocols[] supportedProtocols { get; internal set; } [Newtonsoft.Json.JsonProperty] - public SecurityClass[] securityClasses { get; set; } + public SecurityClass[] securityClasses { get; set; } [Newtonsoft.Json.JsonProperty] public SecurityClass[] requestedSecurityClasses { get; internal set; } [Newtonsoft.Json.JsonProperty] @@ -221,7 +232,7 @@ public class RebuildRoutesOptions public class AssociationAddress { public int nodeId { get; set; } - public int? endpoint { get; set; } + public int? endpoint { get; set; } } public class RebuildRouteStats @@ -263,7 +274,7 @@ public class InclusionGrant public Enums.SecurityClass[] securityClasses { get; set; } public bool clientSideAuth { get; set; } } - + public class ValueMetadata { internal ValueMetadata() { } @@ -307,7 +318,7 @@ public class DeviceClass internal DeviceClass() { } [Newtonsoft.Json.JsonProperty] - public DeviceClassType basic { get; internal set; } + public DeviceClassType basic { get; internal set; } [Newtonsoft.Json.JsonProperty] public DeviceClassType generic { get; internal set; } [Newtonsoft.Json.JsonProperty] @@ -333,15 +344,15 @@ public class DeviceConfig internal DeviceConfig() { } [Newtonsoft.Json.JsonProperty] - public string filename { get;internal set; } + public string filename { get; internal set; } [Newtonsoft.Json.JsonProperty] - public bool isEmbedded { get;internal set; } + public bool isEmbedded { get; internal set; } [Newtonsoft.Json.JsonProperty] public string manufacturer { get; internal set; } [Newtonsoft.Json.JsonProperty] public string label { get; internal set; } [Newtonsoft.Json.JsonProperty] - public string description { get; internal set; } + public string description { get; internal set; } [Newtonsoft.Json.JsonProperty] public Device[] devices { get; internal set; } [Newtonsoft.Json.JsonProperty] @@ -382,7 +393,7 @@ internal Device() { } public int productId { get; internal set; } } - public class SetValueAPIOptions + public class SetValueAPIOptions { public string transitionDuration { get; set; } public int volume { get; set; } @@ -407,7 +418,7 @@ public class LifelineHealthCheckSummary internal LifelineHealthCheckSummary() { } [Newtonsoft.Json.JsonProperty] - public LifelineHealthCheckResult[] results { get; internal set; } + public LifelineHealthCheckResult[] results { get; internal set; } [Newtonsoft.Json.JsonProperty] public int rating { get; internal set; } } @@ -444,10 +455,6 @@ internal InclusionResultArgs() { } public Enums.SecurityBootstrapFailure lowSecurityReason { get; internal set; } } - public delegate string ValidateDSKAndEnterPIN(string dsk); - public delegate InclusionGrant GrantSecurityClasses(InclusionGrant requested); - public delegate void Abort(); - public class ExclusionOptions { public Enums.ExclusionStrategy strategy { get; set; } @@ -457,14 +464,6 @@ public class InclusionOptions { public Enums.InclusionStrategy strategy { get; set; } public bool forceSecurity { get; set; } - public InclusionUserCallbacks userCallbacks { get; set; } - } - - public class InclusionUserCallbacks - { - public ValidateDSKAndEnterPIN validateDSKAndEnterPIN { get; set; } - public GrantSecurityClasses grantSecurityClasses { get; set; } - public Abort abort { get; set; } } public class LoggingEventArgs @@ -575,7 +574,7 @@ public static FirmwareUpdate Create(string Filename, int Target) } internal FirmwareUpdate() { } - + [Newtonsoft.Json.JsonProperty(PropertyName = "file")] public byte[] data { get; internal set; } public string filename { get; internal set; } diff --git a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/ZWaveOptions.cs b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/ZWaveOptions.cs index 1221df7..4b09abe 100644 --- a/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/ZWaveOptions.cs +++ b/Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/ZWaveOptions.cs @@ -3,6 +3,8 @@ namespace ZWaveJS.NET { + + public class ZWaveOptions { public ZWaveOptions() @@ -18,6 +20,7 @@ public ZWaveOptions() this.emitValueUpdateAfterSetValue = false; this.features = new CFGFeatures(); this.preferences = new CFGPreferences(); + this.inclusionUserCallbacks = new InclusionUserCallbacks(); } @@ -26,9 +29,11 @@ public string Serialize() return Newtonsoft.Json.JsonConvert.SerializeObject(this, Newtonsoft.Json.Formatting.Indented); } - public static ZWaveOptions FromSerialized(string JSON) + public static ZWaveOptions FromSerialized(string JSON,InclusionUserCallbacks S2Callbacks) { - return Newtonsoft.Json.JsonConvert.DeserializeObject(JSON); + ZWaveOptions Op = Newtonsoft.Json.JsonConvert.DeserializeObject(JSON); + Op.inclusionUserCallbacks = S2Callbacks; + return Op; } public CFGTimeouts timeouts { get; set; } @@ -42,8 +47,9 @@ public static ZWaveOptions FromSerialized(string JSON) public bool emitValueUpdateAfterSetValue { get; set; } public CFGFeatures features { get; set; } public CFGPreferences preferences { get; set; } - - + [Newtonsoft.Json.JsonIgnore] + public InclusionUserCallbacks inclusionUserCallbacks { get; set; } + public class CFGPreferences { public CFGPreferences() From db80439ef69d3f1c97cf47259483c859c3a2fa8b Mon Sep 17 00:00:00 2001 From: Marcus Davies <55892693+marcus-j-davies@users.noreply.github.com> Date: Sun, 25 Jan 2026 15:54:13 +0000 Subject: [PATCH 23/59] Demo app --- .vscode/launch.json | 7 + CHANGELOG.md | 4 + .../ZWaveJS.NET/Demo Application/App.xaml | 14 + .../ZWaveJS.NET/Demo Application/App.xaml.cs | 99 ++++ .../Demo Application/AppShell.xaml | 14 + .../Demo Application/AppShell.xaml.cs | 9 + .../Demo Application/Demo Application.csproj | 71 +++ .../Demo Application.csproj.user | 6 + .../Demo Application/Information.xaml | 98 ++++ .../Demo Application/Information.xaml.cs | 10 + .../Demo Application/MainAppShell.xaml | 31 ++ .../Demo Application/MainAppShell.xaml.cs | 9 + .../Demo Application/MainPage.xaml | 92 ++++ .../Demo Application/MainPage.xaml.cs | 29 ++ .../Demo Application/MauiProgram.cs | 25 + .../Platforms/MacCatalyst/AppDelegate.cs | 9 + .../Platforms/MacCatalyst/Entitlements.plist | 11 + .../Platforms/MacCatalyst/Info.plist | 40 ++ .../Platforms/MacCatalyst/Program.cs | 15 + .../Platforms/Windows/App.xaml | 8 + .../Platforms/Windows/App.xaml.cs | 24 + .../Platforms/Windows/Package.appxmanifest | 46 ++ .../Platforms/Windows/app.manifest | 17 + .../Properties/launchSettings.json | 8 + .../Resources/AppIcon/appicon.svg | 4 + .../Resources/AppIcon/appiconfg.svg | 8 + .../Resources/Fonts/OpenSans-Regular.ttf | Bin 0 -> 107280 bytes .../Resources/Fonts/OpenSans-Semibold.ttf | Bin 0 -> 111208 bytes .../Resources/Images/zwavejs_dark.svg | 1 + .../Resources/Raw/AboutAssets.txt | 15 + .../Resources/Splash/splash.svg | 8 + .../Resources/Styles/Colors.xaml | 49 ++ .../Resources/Styles/Styles.xaml | 434 ++++++++++++++++++ .../ZWaveJS.NET/ZWaveJS.NET.sln | 26 ++ .../ZWaveJS.NET/ZWaveJS.NET/Driver.cs | 6 +- .../ZWaveJS.NET/ZWaveJS.NET/Server.cs | 20 +- 36 files changed, 1259 insertions(+), 8 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 Visual Studio Projects/ZWaveJS.NET/Demo Application/App.xaml create mode 100644 Visual Studio Projects/ZWaveJS.NET/Demo Application/App.xaml.cs create mode 100644 Visual Studio Projects/ZWaveJS.NET/Demo Application/AppShell.xaml create mode 100644 Visual Studio Projects/ZWaveJS.NET/Demo Application/AppShell.xaml.cs create mode 100644 Visual Studio Projects/ZWaveJS.NET/Demo Application/Demo Application.csproj create mode 100644 Visual Studio Projects/ZWaveJS.NET/Demo Application/Demo Application.csproj.user create mode 100644 Visual Studio Projects/ZWaveJS.NET/Demo Application/Information.xaml create mode 100644 Visual Studio Projects/ZWaveJS.NET/Demo Application/Information.xaml.cs create mode 100644 Visual Studio Projects/ZWaveJS.NET/Demo Application/MainAppShell.xaml create mode 100644 Visual Studio Projects/ZWaveJS.NET/Demo Application/MainAppShell.xaml.cs create mode 100644 Visual Studio Projects/ZWaveJS.NET/Demo Application/MainPage.xaml create mode 100644 Visual Studio Projects/ZWaveJS.NET/Demo Application/MainPage.xaml.cs create mode 100644 Visual Studio Projects/ZWaveJS.NET/Demo Application/MauiProgram.cs create mode 100644 Visual Studio Projects/ZWaveJS.NET/Demo Application/Platforms/MacCatalyst/AppDelegate.cs create mode 100644 Visual Studio Projects/ZWaveJS.NET/Demo Application/Platforms/MacCatalyst/Entitlements.plist create mode 100644 Visual Studio Projects/ZWaveJS.NET/Demo Application/Platforms/MacCatalyst/Info.plist create mode 100644 Visual Studio Projects/ZWaveJS.NET/Demo Application/Platforms/MacCatalyst/Program.cs create mode 100644 Visual Studio Projects/ZWaveJS.NET/Demo Application/Platforms/Windows/App.xaml create mode 100644 Visual Studio Projects/ZWaveJS.NET/Demo Application/Platforms/Windows/App.xaml.cs create mode 100644 Visual Studio Projects/ZWaveJS.NET/Demo Application/Platforms/Windows/Package.appxmanifest create mode 100644 Visual Studio Projects/ZWaveJS.NET/Demo Application/Platforms/Windows/app.manifest create mode 100644 Visual Studio Projects/ZWaveJS.NET/Demo Application/Properties/launchSettings.json create mode 100644 Visual Studio Projects/ZWaveJS.NET/Demo Application/Resources/AppIcon/appicon.svg create mode 100644 Visual Studio Projects/ZWaveJS.NET/Demo Application/Resources/AppIcon/appiconfg.svg create mode 100644 Visual Studio Projects/ZWaveJS.NET/Demo Application/Resources/Fonts/OpenSans-Regular.ttf create mode 100644 Visual Studio Projects/ZWaveJS.NET/Demo Application/Resources/Fonts/OpenSans-Semibold.ttf create mode 100644 Visual Studio Projects/ZWaveJS.NET/Demo Application/Resources/Images/zwavejs_dark.svg create mode 100644 Visual Studio Projects/ZWaveJS.NET/Demo Application/Resources/Raw/AboutAssets.txt create mode 100644 Visual Studio Projects/ZWaveJS.NET/Demo Application/Resources/Splash/splash.svg create mode 100644 Visual Studio Projects/ZWaveJS.NET/Demo Application/Resources/Styles/Colors.xaml create mode 100644 Visual Studio Projects/ZWaveJS.NET/Demo Application/Resources/Styles/Styles.xaml diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..5c7247b --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,7 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [] +} \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index f305870..d2cc109 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -69,10 +69,14 @@ - Reverted to using the actively maintained Websocket.Client library. Previously, we were relying on an outdated source‑based code copy - New Features. + - Added the ability to set the PSI root folder via ```Server.PSIRoot```, this is to address some OSX quirks, with App Bundles. + it should only be the folder path, and not the executable name. - Exposed further Zwave Options - ```preferences``` - ```attempts.smartStartInclusion``` - ```attempts.firmwareUpdateOTW``` + - Added ```Driver.IsHostedMode``` property + - Added ```Driver.ServerSchemaVersiondMode``` property diff --git a/Visual Studio Projects/ZWaveJS.NET/Demo Application/App.xaml b/Visual Studio Projects/ZWaveJS.NET/Demo Application/App.xaml new file mode 100644 index 0000000..b79a35f --- /dev/null +++ b/Visual Studio Projects/ZWaveJS.NET/Demo Application/App.xaml @@ -0,0 +1,14 @@ + + + + + + + + + + + diff --git a/Visual Studio Projects/ZWaveJS.NET/Demo Application/App.xaml.cs b/Visual Studio Projects/ZWaveJS.NET/Demo Application/App.xaml.cs new file mode 100644 index 0000000..e74802d --- /dev/null +++ b/Visual Studio Projects/ZWaveJS.NET/Demo Application/App.xaml.cs @@ -0,0 +1,99 @@ +using Microsoft.Extensions.DependencyInjection; +using ZWaveJS.NET; + +namespace Demo_Application; + +public partial class App : Application +{ + internal Driver _Driver; + internal static App _Instance; + internal static Driver Driver => _Instance._Driver; // xmal support + + public App() + { + InitializeComponent(); + _Instance = this; + } + + private void Quit() + { +#if MACCATALYST + UIKit.UIApplication.SharedApplication.PerformSelector(new ObjCRuntime.Selector("terminate:"), null, 0); +#elif WINDOWS + System.Diagnostics.Process.GetCurrentProcess().Kill(); +#endif + } + + + internal void StartDriver(bool Host, string PoretOrURI) + { + string DocumentsPath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); + Server.PSIRoot = DocumentsPath; + switch (Host) + { + case true: + ZWaveOptions Options = new ZWaveOptions(); + Options.features.softReset = false; + Options.storage.cacheDir = Path.Join(DocumentsPath,"zwave-js-cache"); + _Driver = new Driver(PoretOrURI.ToString(), Options); + _Driver.DriverReady += HandleReady; + _Driver.ServerConnectionError += HandleError; + break; + + default: + _Driver = new Driver(new Uri(PoretOrURI.ToString()), null); + _Driver.DriverReady += HandleReady; + _Driver.ServerConnectionError += HandleError; + break; + + } + + _Driver.Start(); + + } + + private async void HandleError(string ErrorCode, string Message, Action Retry) + { + if (Retry == null) + { + _ = MainThread.InvokeOnMainThreadAsync(async () => + { + await Current.Windows[0].Page.DisplayAlertAsync($"Error Code : {ErrorCode}", Message, "Quit"); + Quit(); + + }); + + } + else + { + _ = MainThread.InvokeOnMainThreadAsync(async () => + { + bool Reset = await Current.Windows[0].Page.DisplayAlertAsync($"Error Code : {ErrorCode}", Message, "Retry", "Quit"); + if (Reset) + { + Retry(true, null); + } + else + { + Retry(false, null); + Quit(); + } + }); + } + } + + private void HandleReady() + { + _ = MainThread.InvokeOnMainThreadAsync(async () => + { + + Window window = Application.Current.Windows[0]; + window.Page = new MainAppShell(); + }); + } + + protected override Window CreateWindow(IActivationState? activationState) + { + return new Window(new AppShell()); + } +} \ No newline at end of file diff --git a/Visual Studio Projects/ZWaveJS.NET/Demo Application/AppShell.xaml b/Visual Studio Projects/ZWaveJS.NET/Demo Application/AppShell.xaml new file mode 100644 index 0000000..44739ad --- /dev/null +++ b/Visual Studio Projects/ZWaveJS.NET/Demo Application/AppShell.xaml @@ -0,0 +1,14 @@ + + + + + + diff --git a/Visual Studio Projects/ZWaveJS.NET/Demo Application/AppShell.xaml.cs b/Visual Studio Projects/ZWaveJS.NET/Demo Application/AppShell.xaml.cs new file mode 100644 index 0000000..f92503c --- /dev/null +++ b/Visual Studio Projects/ZWaveJS.NET/Demo Application/AppShell.xaml.cs @@ -0,0 +1,9 @@ +namespace Demo_Application; + +public partial class AppShell : Shell +{ + public AppShell() + { + InitializeComponent(); + } +} diff --git a/Visual Studio Projects/ZWaveJS.NET/Demo Application/Demo Application.csproj b/Visual Studio Projects/ZWaveJS.NET/Demo Application/Demo Application.csproj new file mode 100644 index 0000000..479bbd2 --- /dev/null +++ b/Visual Studio Projects/ZWaveJS.NET/Demo Application/Demo Application.csproj @@ -0,0 +1,71 @@ + + + + net10.0-maccatalyst + $(TargetFrameworks);net10.0-windows10.0.19041.0 + + + + + Exe + Demo_Application + true + true + enable + enable + + + Demo Application + + + com.companyname.demoapplication + + + 1.0 + 1 + + + None + + 15.0 + 10.0.17763.0 + 10.0.17763.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + MainAppShell.xaml + + + + diff --git a/Visual Studio Projects/ZWaveJS.NET/Demo Application/Demo Application.csproj.user b/Visual Studio Projects/ZWaveJS.NET/Demo Application/Demo Application.csproj.user new file mode 100644 index 0000000..137b32c --- /dev/null +++ b/Visual Studio Projects/ZWaveJS.NET/Demo Application/Demo Application.csproj.user @@ -0,0 +1,6 @@ + + + + net10.0-maccatalyst + + \ No newline at end of file diff --git a/Visual Studio Projects/ZWaveJS.NET/Demo Application/Information.xaml b/Visual Studio Projects/ZWaveJS.NET/Demo Application/Information.xaml new file mode 100644 index 0000000..3024829 --- /dev/null +++ b/Visual Studio Projects/ZWaveJS.NET/Demo Application/Information.xaml @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Visual Studio Projects/ZWaveJS.NET/Demo Application/Information.xaml.cs b/Visual Studio Projects/ZWaveJS.NET/Demo Application/Information.xaml.cs new file mode 100644 index 0000000..41c87f0 --- /dev/null +++ b/Visual Studio Projects/ZWaveJS.NET/Demo Application/Information.xaml.cs @@ -0,0 +1,10 @@ +namespace Demo_Application; + +public partial class Information : ContentPage +{ + public Information() + { + InitializeComponent(); + + } +} \ No newline at end of file diff --git a/Visual Studio Projects/ZWaveJS.NET/Demo Application/MainAppShell.xaml b/Visual Studio Projects/ZWaveJS.NET/Demo Application/MainAppShell.xaml new file mode 100644 index 0000000..6baa004 --- /dev/null +++ b/Visual Studio Projects/ZWaveJS.NET/Demo Application/MainAppShell.xaml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + diff --git a/Visual Studio Projects/ZWaveJS.NET/Demo Application/MainAppShell.xaml.cs b/Visual Studio Projects/ZWaveJS.NET/Demo Application/MainAppShell.xaml.cs new file mode 100644 index 0000000..b2c62e8 --- /dev/null +++ b/Visual Studio Projects/ZWaveJS.NET/Demo Application/MainAppShell.xaml.cs @@ -0,0 +1,9 @@ +namespace Demo_Application; + +public partial class MainAppShell : Shell +{ + public MainAppShell() + { + InitializeComponent(); + } +} diff --git a/Visual Studio Projects/ZWaveJS.NET/Demo Application/MainPage.xaml b/Visual Studio Projects/ZWaveJS.NET/Demo Application/MainPage.xaml new file mode 100644 index 0000000..33b0f83 --- /dev/null +++ b/Visual Studio Projects/ZWaveJS.NET/Demo Application/MainPage.xaml @@ -0,0 +1,92 @@ + + + + + + + +