Skip to content

Commit

Permalink
V3.1.0 (zwave-js#20)
Browse files Browse the repository at this point in the history
* Key check and Options access

* It bothered me

* Arch

* Update README.md

* Update server.js

* Update server.js

* WS Error Handle

* Update Driver.cs

* Alpha

* Update CHANGELOG.md

* DownloadPSI

* optimsie Unprovision

* Final

* Update Program.cs
  • Loading branch information
marcus-j-davies authored Jun 14, 2022
1 parent 23318a7 commit 7174690
Show file tree
Hide file tree
Showing 13 changed files with 312 additions and 101 deletions.
6 changes: 5 additions & 1 deletion .github/workflows/BuildPSI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,17 @@ jobs:
strategy:
matrix:
node-version: [16]
os: [windows-latest, macos-latest, ubuntu-latest]
os: [windows-latest, macos-latest, ubuntu-latest, self-hosted]
include:
# Define the binary names to use for uploads
- os: ubuntu-latest
file: server
asset_name: server-ubuntu.psi
asset_body: "Ubuntu Server Binary"
- os: self-hosted
file: server
asset_name: server-debian-arm.psi
asset_body: "Debian ARM Server Binary"
- os: windows-latest
file: server.exe
asset_name: server-win.psi
Expand Down
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,22 @@
- v3.1.0

- Versions
- ZWave JS Driver Version: 9.3.0
- ZWave JS Server Version: 1.17.0 (Schema Version 17)

- Internal changes
- The **CFGLogConfig** and **CFGStorage** classes can now be set exclusively.
- The child classes of **ZWaveOptions** are now instanciated with default values when calling their constructors.
- The **DownloadPSI** method now pulls down version locked binaries, to remove the potential for incompatible Binary/library combinations

- New Fearures
- Added ARM prebuilt binary (Debian, RPi)
- The **DownloadPSI** method - now has an optional override, allwowing the PSI to be focibly downloaded, i.e to ensure you have the correct version.

- Fixes
- Any inclusion or replace node method now checks that Security Keys are present if needed.
- Handle unexpected WS Disconnects.

- v3.0.0

- Versions
Expand Down
34 changes: 24 additions & 10 deletions PSI/server.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
const { Driver, ZWaveError, ZWaveErrorCodes } = require("zwave-js");
const { Driver } = require("zwave-js");
const { ZwavejsServer } = require("@zwave-js/server");

console.log("ZWaveJS.NET: Preparing server...");

const serialPort = process.env.SERIAL_PORT;
const wsPort = parseInt(process.env.WS_PORT);
const driverOptions = JSON.parse(process.env.CONFIG);
let ServerStarted = false;
let DriverStarted = false;

console.log(`ZWaveJS.NET: Serial Port: ${serialPort}, WSPort: ${wsPort}`);

Expand All @@ -21,19 +23,31 @@ if (driverOptions.securityKeys) {
console.log("ZWaveJS.NET: Instantiating driver...");
const driver = new Driver(serialPort, driverOptions);
const server = new ZwavejsServer(driver, { port: wsPort, host: "localhost" });
driver.on("error", (e) => {
/*
if (e instanceof ZWaveError && e.code === ZWaveErrorCodes.Driver_Failed) {
process.stderr.write("2\n");
}
*/
});
server.on("listening",() =>{
ServerStarted = true;
})
driver.on("error", (e) => {});

driver.on("driver ready", () => {
server.start();
ServerStarted = true;
});

console.log("ZWaveJS.NET: Starting driver...");
driver.start().catch((e) => {
driver.start()
.then(() =>{
DriverStarted = true;
process.stdin.on("data",HandleInput)
})
.catch((e) => {
process.stderr.write("1\n");
});
})

const HandleInput = async (Data) =>{
if(Data.toString().trim() === "KILL"){
console.log("ZWaveJS.NET: Cleaning up...");
if(ServerStarted) await server.destroy();
if(DriverStarted) await driver.destroy();
process.exit(0);
}
}
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,19 @@ The library will connect to an already running instance of [zwave-js-server](htt
The library will host its own zwave-js instance.
You might ask, if in this mode, **nodejs** and **npm** is needed on the host system - it is not!

This is all possible with an accompanying file - **server.psi**.
This is all possible with an accompanying file - **server.psi**. (Platform Support Image)

Its an executable that is running silently/hidden,
and it contains everything necessary for .NET to work with zwave-js.

**server.psi** files are platform specific, but the assembly isn't - it will run on windows, OSX and Linux, and the platform specifics i.e **node** are contained in **server.psi**.

## Prebuilt PSI's
- Windows x64
- MacOS x64 (Should support Apple Silicon via Rosetta2)
- Ubuntu Linux x64
- Debian Arm

## Building yor own platform specific binary.

To build an image for your platform:
Expand Down
29 changes: 10 additions & 19 deletions Visual Studio Projects/ZWaveJS.NET/Scratch Pad/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,12 @@ class Program
static void Main(string[] args)
{

ZWaveJS.NET.Helpers.DownloadPSI().ContinueWith((R) => {


var B = "sadsdsa";


ZWaveOptions ZWO = new ZWaveOptions();
_Driver = new Driver("COM3", ZWO);
_Driver.DriverReady += _Driver_DriverReady;

_Driver.Start();

Console.ReadLine();
});
}

private static void _Driver_StartupErrorEvent(string Message)
Expand All @@ -32,18 +28,13 @@ private static void _Driver_StartupErrorEvent(string Message)

private static void _Driver_DriverReady()
{
var ddd = new InclusionOptions();
ddd.strategy = Enums.InclusionStrategy.Security_S0;
_Driver.Controller.ReplaceFailedNode(4, ddd).ContinueWith((R) => {

VirtualNode VN = _Driver.Controller.GetMulticastGroup(new int[] { 2,3,4,6,7});
VN.GetDefinedValueIDs();
VN.GetEndpointCount();








var Res = R;

});
}

private static void Program_NodeDead(ZWaveNode Node)
Expand Down
132 changes: 99 additions & 33 deletions Visual Studio Projects/ZWaveJS.NET/ZWaveJS.NET/Controller.cs
Original file line number Diff line number Diff line change
Expand Up @@ -271,12 +271,12 @@ public Task<CMDResult> RestoreNVM(byte[] NVMData, ConvertRestoreNVMProgress Conv
Driver.Callbacks.Add(ID, (JO) =>
{
CMDResult Res = new CMDResult(JO);
Result.SetResult(Res);
if (Res.Success)
{
_Driver.Restart();
}

Result.SetResult(Res);

});

Dictionary<string, object> Request = new Dictionary<string, object>();
Expand Down Expand Up @@ -327,35 +327,56 @@ public Task<CMDResult> ReplaceFailedNode(int NodeID, InclusionOptions Options)

Guid ID = Guid.NewGuid();
TaskCompletionSource<CMDResult> Result = new TaskCompletionSource<CMDResult>();

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.Default)
}

if (Options.strategy == Enums.InclusionStrategy.Security_S2)
{
if (ValidateDSKAndEnterPINSub == null || GrantSecurityClassesSub == null || AbortSub == null)
{
CMDResult Res = new CMDResult("ZWJS.NET.ERR.002", "Invalid Strategy for 'ReplaceFailedNode' Valid Strategies are : [Insecure, Security_S0, Security_S2]", false);
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_S2)
if(Options.strategy == Enums.InclusionStrategy.Security_S0)
{
if(ValidateDSKAndEnterPINSub == null || GrantSecurityClassesSub == null || AbortSub == null)
if (_Driver.Options != null && _Driver.Options.MissingKeys(false, true))
{
CMDResult Res = new CMDResult("ZWJS.NET.ERR.001", "S2 Security require userCallbacks to be provided [validateDSKAndEnterPIN, grantSecurityClasses, abort]", false);
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) =>
{
Expand Down Expand Up @@ -497,18 +518,62 @@ public Task<CMDResult> BeginInclusion(InclusionOptions Options)
AbortSub = Options.userCallbacks?.abort ?? null;
break;
}

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 (Options.strategy == Enums.InclusionStrategy.Default || Options.strategy == Enums.InclusionStrategy.Security_S2)
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("ZWJS.NET.ERR.001", "S2 Security require userCallbacks to be provided [validateDSKAndEnterPIN, grantSecurityClasses, abort]", false);
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);
Expand Down Expand Up @@ -550,31 +615,18 @@ public Task<CMDResult> StopInclusion()

return Result.Task;
}

public Task<CMDResult> UnprovisionSmartStartNode(int NodeID)
{
Guid ID = Guid.NewGuid();
TaskCompletionSource<CMDResult> Result = new TaskCompletionSource<CMDResult>();

Driver.Callbacks.Add(ID, (JO) =>
{
CMDResult Res = new CMDResult(JO);
Result.SetResult(Res);
});

Dictionary<string, object> Request = new Dictionary<string, object>();

Request.Add("messageId", ID);
Request.Add("command", Enums.Commands.UnprovisionSmartStartNode);
Request.Add("dskOrNodeId", NodeID);

string RequestPL = Newtonsoft.Json.JsonConvert.SerializeObject(Request);
Driver.Client.SendAsync(RequestPL);

return Result.Task;
return _UnprovisionSmartStartNode(NodeID);
}

public Task<CMDResult> UnprovisionSmartStartNode(string DSK)
{
return _UnprovisionSmartStartNode(DSK);
}

private Task<CMDResult> _UnprovisionSmartStartNode(object dskOrNodeId)
{
Guid ID = Guid.NewGuid();
TaskCompletionSource<CMDResult> Result = new TaskCompletionSource<CMDResult>();
Expand All @@ -589,7 +641,7 @@ public Task<CMDResult> UnprovisionSmartStartNode(string DSK)

Request.Add("messageId", ID);
Request.Add("command", Enums.Commands.UnprovisionSmartStartNode);
Request.Add("dskOrNodeId", DSK);
Request.Add("dskOrNodeId", dskOrNodeId);

string RequestPL = Newtonsoft.Json.JsonConvert.SerializeObject(Request);
Driver.Client.SendAsync(RequestPL);
Expand All @@ -603,6 +655,20 @@ public Task<CMDResult> ProvisionSmartStartNode(string QRCode)
Guid ID = Guid.NewGuid();
TaskCompletionSource<CMDResult> Result = new TaskCompletionSource<CMDResult>();

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 (_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);
Expand Down
Loading

0 comments on commit 7174690

Please sign in to comment.