Skip to content

Commit

Permalink
v1.0.9
Browse files Browse the repository at this point in the history
- Fixed packet fragmentation issue (affecting RaZberry) and timeout errors occurring with some devices (eg. Fibaro in wall switches).
- Added WakeUp.SendToSleep command
- Added WakeUp on NodeInfo
  • Loading branch information
genemars committed Nov 29, 2015
1 parent 680e243 commit c38f092
Show file tree
Hide file tree
Showing 7 changed files with 145 additions and 60 deletions.
58 changes: 41 additions & 17 deletions ZWaveLib/CommandClasses/WakeUp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,22 +49,7 @@ public NodeEvent GetEvent(ZWaveNode node, byte[] message)
}
break;
case (byte)Command.WakeUpNotification:
// If node was marked as sleeping, reset the flag
var wakeUpStatus = node.GetData("WakeUpStatus");
if (wakeUpStatus != null && wakeUpStatus.Value != null && ((WakeUpStatus)wakeUpStatus.Value).IsSleeping)
{
((WakeUpStatus)wakeUpStatus.Value).IsSleeping = false;
var wakeEvent = new NodeEvent(node, EventParameter.WakeUpSleepingStatus, 0 /* 1 = sleeping, 0 = awake */, 0);
node.OnNodeUpdated(wakeEvent);
}
// Resend queued messages while node was asleep
var wakeUpResendQueue = GetResendQueueData(node);
for (int m = 0; m < wakeUpResendQueue.Count; m++)
{
Utility.logger.Trace("Sending message {0} {1}", m, BitConverter.ToString(wakeUpResendQueue[m]));
node.SendMessage(wakeUpResendQueue[m]);
}
wakeUpResendQueue.Clear();
WakeUpNode(node);
nodeEvent = new NodeEvent(node, EventParameter.WakeUpNotify, 1, 0);
break;
}
Expand All @@ -91,10 +76,49 @@ public static ZWaveMessage Set(ZWaveNode node, uint interval)
});
}

public static ZWaveMessage SendToSleep(ZWaveNode node)
{
ZWaveMessage msg = null;
var wakeUpStatus = (WakeUpStatus)node.GetData("WakeUpStatus", new WakeUpStatus()).Value;
if (!wakeUpStatus.IsSleeping)
{
// 0x01, 0x09, 0x00, 0x13, 0x2b, 0x02, 0x84, 0x08, 0x25, 0xee, 0x8b
msg = node.SendDataRequest(new byte[] {
(byte)CommandClass.WakeUp,
(byte)Command.WakeUpNoMoreInfo,
0x25
}).Wait();
wakeUpStatus.IsSleeping = true;
var nodeEvent = new NodeEvent(node, EventParameter.WakeUpSleepingStatus, 1 /* 1 = sleeping, 0 = awake */, 0);
node.OnNodeUpdated(nodeEvent);
}
return msg;
}

public static void WakeUpNode(ZWaveNode node)
{
// If node was marked as sleeping, reset the flag
var wakeUpStatus = node.GetData("WakeUpStatus");
if (wakeUpStatus != null && wakeUpStatus.Value != null && ((WakeUpStatus)wakeUpStatus.Value).IsSleeping)
{
((WakeUpStatus)wakeUpStatus.Value).IsSleeping = false;
var wakeEvent = new NodeEvent(node, EventParameter.WakeUpSleepingStatus, 0 /* 1 = sleeping, 0 = awake */, 0);
node.OnNodeUpdated(wakeEvent);
// Resend queued messages while node was asleep
var wakeUpResendQueue = GetResendQueueData(node);
for (int m = 0; m < wakeUpResendQueue.Count; m++)
{
Utility.logger.Trace("Sending message {0} {1}", m, BitConverter.ToString(wakeUpResendQueue[m]));
node.SendMessage(wakeUpResendQueue[m]);
}
wakeUpResendQueue.Clear();
}
}

public static void ResendOnWakeUp(ZWaveNode node, byte[] msg)
{
int minCommandLength = 8;
if (msg.Length >= minCommandLength)
if (msg.Length >= minCommandLength && (msg[6] != (byte)CommandClass.WakeUp && msg[7] != (byte)Command.WakeUpNoMoreInfo))
{
byte[] command = new byte[minCommandLength];
Array.Copy(msg, 0, command, 0, minCommandLength);
Expand Down
1 change: 1 addition & 0 deletions ZWaveLib/Enums/Command.cs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ public enum Command : byte
WakeUpIntervalGet = 0x05,
WakeUpIntervalReport = 0x06,
WakeUpNotification = 0x07,
WakeUpNoMoreInfo = 0x08,
WakeUpIntervalCapabilitiesGet = 0x09,
WakeUpIntervalCapabilitiesReport = 0x0A,
//
Expand Down
7 changes: 7 additions & 0 deletions ZWaveLib/Enums/OperationStatus.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,5 +89,12 @@ public enum NeighborsUpdateStatus : byte
NeighborsUpdateFailed = 0x23
}

public enum ApplicationUpdateStatus : byte
{
None = 0x00,
RequestNodeInfoFailed = 0x81,
RequestNodeInfoSuccessful = 0x84
}

}

49 changes: 26 additions & 23 deletions ZWaveLib/Values/AlarmValue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,21 +47,23 @@ public enum ZWaveAlarmType
/// an open door if the alarm AccessControl alarm type is set.
/// </summary>
///
public enum ZWaveAlarmDetailType
public enum ZWaveAlarmEvent
{
Generic = 0x00,
// Home security
HomeSecurityTamper = 0x03,
HomeSecurityMotion = 0x07, // Air?
HomeSecurityPir = 0x08,
// Access control
AccessDoorOpen = 0x16,
AccessDoorClosed = 0x17,
// Home security
HomeSecTamper = 0x03
}

public class AlarmValue
{
public EventParameter EventType = EventParameter.AlarmGeneric;
public ZWaveAlarmType Parameter = ZWaveAlarmType.Generic;
public ZWaveAlarmDetailType Detail = ZWaveAlarmDetailType.Generic;
public ZWaveAlarmEvent Event = ZWaveAlarmEvent.Generic;
public byte Value = 0x00;

public static AlarmValue Parse(byte[] message)
Expand All @@ -74,14 +76,15 @@ public static AlarmValue Parse(byte[] message)
alarm.Parameter = (ZWaveAlarmType)Enum.Parse(typeof(ZWaveAlarmType), message[3].ToString());
alarm.Value = message[4];
}
else
else // CommandClass.Alarm
{
// TODO: change this to --> if (node.GetCommandClass(CommandClass.Alarm).Version == 2) ...
if (message.Length > 7)
{
// Version 2
alarm.Detail = (ZWaveAlarmDetailType)Enum.Parse(typeof(ZWaveAlarmDetailType), message[7].ToString());
alarm.Event = (ZWaveAlarmEvent)Enum.Parse(typeof(ZWaveAlarmEvent), message[7].ToString());
alarm.Parameter = (ZWaveAlarmType)Enum.Parse(typeof(ZWaveAlarmType), message[6].ToString());
alarm.Value = message[7];
alarm.Value = message[5];
}
else
{
Expand Down Expand Up @@ -110,31 +113,31 @@ public static AlarmValue Parse(byte[] message)
break;
case ZWaveAlarmType.AccessControl:
alarm.EventType = EventParameter.AlarmDoorWindow;
if (alarm.Detail != ZWaveAlarmDetailType.Generic)
switch (alarm.Event)
{
if (alarm.Detail == ZWaveAlarmDetailType.AccessDoorOpen)
{
alarm.Value = 0x01;
}
if (alarm.Detail == ZWaveAlarmDetailType.AccessDoorClosed)
{
alarm.Value = 0x00;
}
case ZWaveAlarmEvent.AccessDoorOpen:
alarm.Value = 1;
break;
case ZWaveAlarmEvent.AccessDoorClosed:
alarm.Value = 0;
break;
}
break;
case ZWaveAlarmType.Burglar:
alarm.EventType = EventParameter.AlarmTampered;
if (alarm.Detail != ZWaveAlarmDetailType.Generic)
alarm.EventType = EventParameter.AlarmGeneric;
switch (alarm.Event)
{
if (alarm.Detail == ZWaveAlarmDetailType.HomeSecTamper)
{
alarm.Value = 0x01;
}
case ZWaveAlarmEvent.HomeSecurityTamper:
alarm.EventType = EventParameter.AlarmTampered;
break;
case ZWaveAlarmEvent.HomeSecurityMotion:
case ZWaveAlarmEvent.HomeSecurityPir:
alarm.EventType = EventParameter.SensorMotion;
break;
}
break;
}

//
return alarm;
}
}
Expand Down
64 changes: 52 additions & 12 deletions ZWaveLib/ZWaveController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ public class ZWaveController : IDisposable
private SerialPortInput serialPort;
private string portName = "";
private const int commandDelayMin = 100;
private const int commandRetryDelay = 200;
private int commandDelay = commandDelayMin;

private ManualResetEvent sendMessageAck = new ManualResetEvent(false);
Expand All @@ -56,6 +57,7 @@ public class ZWaveController : IDisposable

private ZWaveMessage pendingRequest;
private QueryStage currentStage;
private object readLock = new object();

private bool disposing = false;
private Thread queueManager;
Expand All @@ -65,6 +67,7 @@ public class ZWaveController : IDisposable

private List<ZWaveNode> nodeList = new List<ZWaveNode>();

private byte[] serialBuffer = null;
private byte[] lastMessage = null;
private DateTime lastMessageTimestamp = DateTime.UtcNow;

Expand Down Expand Up @@ -767,7 +770,7 @@ private void QueueManagerTask()
{
msg.ResendCount++;
Utility.logger.Warn("Could not deliver message to Node {0} (CallbackId={1}, Retry={2})", msg.NodeId, msg.CallbackId.ToString("X2"), msg.ResendCount);
Thread.Sleep(commandDelay);
Thread.Sleep(commandRetryDelay);
}
msg.sentAck.Set();

Expand All @@ -793,7 +796,8 @@ private void QueueManagerTask()
UpdateOperationProgress(msg.NodeId, NodeQueryStatus.Error);
}
// little breeze between each send
Thread.Sleep(commandDelay);
if (commandDelay > 0)
Thread.Sleep(commandDelay);
}
// TODO: get rid of this Sleep
Thread.Sleep(500);
Expand Down Expand Up @@ -1014,6 +1018,14 @@ private void ReceiveMessage(ZWaveMessage msg)

case ZWaveFunction.ApplicationUpdate:

// TODO: enable nodeInfoStatus byte check
/*
var applicationUpdateStatus = ApplicationUpdateStatus.None;
Enum.TryParse(rawData[4].ToString(), out nodeInfoStatus);
if (applicationUpdateStatus == ApplicationUpdateStatus.RequestNodeInfoSuccessful)
{
*/
int nifLength = (int)rawData[6];
var znode = GetNode(rawData[5]);
if (znode != null)
Expand All @@ -1032,6 +1044,11 @@ private void ReceiveMessage(ZWaveMessage msg)
NodeInformationFrameDone(znode);
SetQueryStage(QueryStage.Complete);
}
// if node supports WakeUp command class and is sleeping, then wake it up
if (znode.SupportCommandClass(CommandClass.WakeUp))
{
WakeUp.WakeUpNode(znode);
}
}
else
{
Expand Down Expand Up @@ -1191,29 +1208,24 @@ private void ProcessMessage(ZWaveMessage zm)
{
if (zm.Header == FrameHeader.SOF)
{

if (ZWaveMessage.VerifyChecksum(zm.RawData))
{

// Some replies do not include the Id of the node
// so we take it from the pending request message
if (pendingRequest != null && zm.NodeId == 0)
{
zm.NodeId = pendingRequest.NodeId;
zm.CallbackId = pendingRequest.CallbackId;
}

SendAck();
ReceiveMessage(zm);
UpdateQueryStage(zm);

}
else
{
SendNack();
Utility.logger.Warn("Bad message checksum");
}

}
else if (zm.Header == FrameHeader.CAN)
{
Expand Down Expand Up @@ -1294,6 +1306,16 @@ private void ZWave_NodeUpdated(object sender, NodeEvent eventData)
/// <param name="message">raw bytes data.</param>
private void ParseSerialData(byte[] message)
{
if (serialBuffer != null)
{
byte[] merged = new byte[serialBuffer.Length + message.Length];
Array.Copy(serialBuffer, 0, merged, 0, serialBuffer.Length);
Array.Copy(message, 0, merged, serialBuffer.Length, message.Length);
message = merged;
serialBuffer = null;
Utility.logger.Trace("Merged buffer to message: {0}", BitConverter.ToString(message));
}

// Extract Z-Wave frames from incoming serial port data
FrameHeader header = (FrameHeader)((int)message[0]);
if (header == FrameHeader.ACK)
Expand All @@ -1309,7 +1331,13 @@ private void ParseSerialData(byte[] message)

int msgLength = 0;
byte[] nextMessage = null;
if (message.Length > 1)
if (header == FrameHeader.CAN && message.Length > 1)
{
nextMessage = new byte[message.Length - 1];
Array.Copy(message, 1, nextMessage, 0, nextMessage.Length);
message = new byte[] { (byte)header };
}
else if (message.Length > 1)
{
msgLength = (int)message[1];
if (message.Length > msgLength + 2)
Expand All @@ -1320,6 +1348,13 @@ private void ParseSerialData(byte[] message)
Array.Copy(message, 0, tmpmsg, 0, msgLength + 2);
message = tmpmsg;
}
else if (header == FrameHeader.SOF && message.Length < msgLength + 2)
{
serialBuffer = new byte[message.Length];
Array.Copy(message, 0, serialBuffer, 0, serialBuffer.Length);
Utility.logger.Trace("Expected message length is {0}, currently received length is {1}", msgLength + 2, message.Length);
return;
}
}

try
Expand All @@ -1341,15 +1376,20 @@ private void ParseSerialData(byte[] message)
private void SerialPort_ConnectionStatusChanged(object sender, ConnectionStatusChangedEventArgs args)
{
var status = args.Connected ? ControllerStatus.Connected : ControllerStatus.Disconnected;
serialBuffer = null;
lastMessage = null;
Thread.Sleep(1000);
OnControllerStatusChanged(new ControllerStatusEventArgs(status));
}

private void SerialPort_MessageReceived(object sender, SerialPortLib.MessageReceivedEventArgs args)
{
busyReceiving = true;
ParseSerialData(args.Data);
busyReceiving = false;
lock (readLock)
{
busyReceiving = true;
ParseSerialData(args.Data);
busyReceiving = false;
}
}

#endregion
Expand Down Expand Up @@ -1462,7 +1502,7 @@ private void NodeInformationFrameDone(ZWaveNode znode)
/// <param name="args">Arguments.</param>
protected virtual void OnNodeUpdated(NodeUpdatedEventArgs args)
{
Utility.logger.Debug("{0} {1} {2}", args.NodeId, args.Event.Parameter, args.Event.Value);
Utility.logger.Debug("NodeUpdated (NodeId={0}, Parameter={1}, Value={2})", args.NodeId, args.Event.Parameter, args.Event.Value);
if (NodeUpdated != null)
NodeUpdated(this, args);
}
Expand Down
6 changes: 4 additions & 2 deletions ZWaveLib/ZWaveLib.nuspec
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata>
<id>ZWaveLib</id>
<version>1.0.8</version>
<version>1.0.9</version>
<title>ZWaveLib</title>
<authors>Generoso Martello</authors>
<owners>G-Labs</owners>
Expand All @@ -21,7 +21,9 @@ Features
[Source Code and Home Page]
https://github.com/genielabs/zwave-lib-dotnet</description>
<summary>Z-Wave library for Home Automation (.NET / Mono)</summary>
<releaseNotes>Added configurable delay between commands and HealNetwork method to ZWaveController.</releaseNotes>
<releaseNotes>- Fixed packet fragmentation issue (affecting RaZberry) and timeout errors occurring with some devices (eg. Fibaro in wall switches).
- Added WakeUp.SendToSleep command
- Added WakeUp on NodeInfo</releaseNotes>
<copyright>G-Labs</copyright>
<tags>zwave z-wave home automation</tags>
<dependencies>
Expand Down
Loading

0 comments on commit c38f092

Please sign in to comment.