From c38f0920f188b61783da2b7822c46c2f7a303343 Mon Sep 17 00:00:00 2001 From: Generoso Martello Date: Sun, 29 Nov 2015 19:42:51 +0100 Subject: [PATCH] v1.0.9 - 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 --- ZWaveLib/CommandClasses/WakeUp.cs | 58 ++++++++++++++++++++-------- ZWaveLib/Enums/Command.cs | 1 + ZWaveLib/Enums/OperationStatus.cs | 7 ++++ ZWaveLib/Values/AlarmValue.cs | 49 ++++++++++++----------- ZWaveLib/ZWaveController.cs | 64 +++++++++++++++++++++++++------ ZWaveLib/ZWaveLib.nuspec | 6 ++- ZWaveLib/ZWaveMessage.cs | 20 +++++++--- 7 files changed, 145 insertions(+), 60 deletions(-) diff --git a/ZWaveLib/CommandClasses/WakeUp.cs b/ZWaveLib/CommandClasses/WakeUp.cs index ff550a1..f33ffb2 100644 --- a/ZWaveLib/CommandClasses/WakeUp.cs +++ b/ZWaveLib/CommandClasses/WakeUp.cs @@ -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; } @@ -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); diff --git a/ZWaveLib/Enums/Command.cs b/ZWaveLib/Enums/Command.cs index c97c942..ddfa09d 100644 --- a/ZWaveLib/Enums/Command.cs +++ b/ZWaveLib/Enums/Command.cs @@ -109,6 +109,7 @@ public enum Command : byte WakeUpIntervalGet = 0x05, WakeUpIntervalReport = 0x06, WakeUpNotification = 0x07, + WakeUpNoMoreInfo = 0x08, WakeUpIntervalCapabilitiesGet = 0x09, WakeUpIntervalCapabilitiesReport = 0x0A, // diff --git a/ZWaveLib/Enums/OperationStatus.cs b/ZWaveLib/Enums/OperationStatus.cs index 81aad60..235aeee 100644 --- a/ZWaveLib/Enums/OperationStatus.cs +++ b/ZWaveLib/Enums/OperationStatus.cs @@ -89,5 +89,12 @@ public enum NeighborsUpdateStatus : byte NeighborsUpdateFailed = 0x23 } + public enum ApplicationUpdateStatus : byte + { + None = 0x00, + RequestNodeInfoFailed = 0x81, + RequestNodeInfoSuccessful = 0x84 + } + } diff --git a/ZWaveLib/Values/AlarmValue.cs b/ZWaveLib/Values/AlarmValue.cs index 92eb68a..cba3eac 100644 --- a/ZWaveLib/Values/AlarmValue.cs +++ b/ZWaveLib/Values/AlarmValue.cs @@ -47,21 +47,23 @@ public enum ZWaveAlarmType /// an open door if the alarm AccessControl alarm type is set. /// /// - 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) @@ -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 { @@ -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; } } diff --git a/ZWaveLib/ZWaveController.cs b/ZWaveLib/ZWaveController.cs index 4424c5a..06b81bb 100644 --- a/ZWaveLib/ZWaveController.cs +++ b/ZWaveLib/ZWaveController.cs @@ -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); @@ -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; @@ -65,6 +67,7 @@ public class ZWaveController : IDisposable private List nodeList = new List(); + private byte[] serialBuffer = null; private byte[] lastMessage = null; private DateTime lastMessageTimestamp = DateTime.UtcNow; @@ -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(); @@ -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); @@ -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) @@ -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 { @@ -1191,10 +1208,8 @@ 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) @@ -1202,18 +1217,15 @@ private void ProcessMessage(ZWaveMessage zm) 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) { @@ -1294,6 +1306,16 @@ private void ZWave_NodeUpdated(object sender, NodeEvent eventData) /// raw bytes data. 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) @@ -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) @@ -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 @@ -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 @@ -1462,7 +1502,7 @@ private void NodeInformationFrameDone(ZWaveNode znode) /// Arguments. 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); } diff --git a/ZWaveLib/ZWaveLib.nuspec b/ZWaveLib/ZWaveLib.nuspec index f217010..90b1cee 100644 --- a/ZWaveLib/ZWaveLib.nuspec +++ b/ZWaveLib/ZWaveLib.nuspec @@ -2,7 +2,7 @@ ZWaveLib - 1.0.8 + 1.0.9 ZWaveLib Generoso Martello G-Labs @@ -21,7 +21,9 @@ Features [Source Code and Home Page] https://github.com/genielabs/zwave-lib-dotnet Z-Wave library for Home Automation (.NET / Mono) - Added configurable delay between commands and HealNetwork method to ZWaveController. + - 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 G-Labs zwave z-wave home automation diff --git a/ZWaveLib/ZWaveMessage.cs b/ZWaveLib/ZWaveMessage.cs index 2c3ff41..333269c 100644 --- a/ZWaveLib/ZWaveMessage.cs +++ b/ZWaveLib/ZWaveMessage.cs @@ -160,19 +160,19 @@ public ZWaveMessage(byte[] message, MessageDirection direction = MessageDirectio switch (Type) { case MessageType.Request: - if (Function == ZWaveFunction.SendData && message.Length == 8) - { - Enum.TryParse(message[6].ToString(), out CommandClass); - } - else if (Function == ZWaveFunction.SendData && message.Length == 6) + if (Function == ZWaveFunction.SendData && message.Length == 6) { Enum.TryParse(message[4].ToString(), out CallbackStatus); } - else if (Function == ZWaveFunction.SendData && message.Length == 7) + else if (Function == ZWaveFunction.SendData && (message.Length == 7 || message.Length == 9)) { CallbackId = message[4]; Enum.TryParse(message[5].ToString(), out CallbackStatus); } + else if (Function == ZWaveFunction.SendData && message.Length == 8) + { + Enum.TryParse(message[6].ToString(), out CommandClass); + } else if (Function == ZWaveFunction.SendData && message.Length > 6) { NodeId = message[4]; @@ -218,6 +218,14 @@ public ZWaveMessage(byte[] message, MessageDirection direction = MessageDirectio if (seqNumber == long.MaxValue) seqNumber = 0; Seq = ++seqNumber; + + Utility.logger.Debug("ZWaveMessage (RawData={0})", BitConverter.ToString(RawData)); + if (Direction == MessageDirection.Inbound) + Utility.logger.Debug("ZWaveMessage (Direction={0}, Header={1}, NodeId={2}, Type={3}, Function={4}, CommandClass={5})", + Direction, Header, NodeId, Type, Function, CommandClass); + else + Utility.logger.Debug("ZWaveMessage (Direction={0}, Header={1}, NodeId={2}, Type={3}, Function={4}, CommandClass={5}, CallbackId={6}, CallbackStatus={7})", + Direction, Header, NodeId, Type, Function, CommandClass, CallbackId, CallbackStatus); } ///