private void ProcessSensorMessage(SensorMessage message)
        {
            Debug.WriteLine(message.ToString());

            bool isNodeMessage = message.NodeNo == 0 || message.SensorNo == 255;
            Node node = GetNode(message.NodeNo);
            Sensor sensor = GetSensor(message.NodeNo, message.SensorNo); // if message.SensorID == 255 it returns null

            switch (message.Type)
            {
                #region Presentation
                case SensorMessageType.Presentation: // sent by a nodes when they present attached sensors.
                    if (isNodeMessage)
                    {
                        if (node == null)
                        {
                            node = new Node
                            {
                                Id = Guid.NewGuid(),
                                NodeNo = message.NodeNo,
                                Type = (SensorType)message.SubType,
                                ProtocolVersion = message.Payload
                            };

                            Save(node);
                        }
                        else
                        {
                            node.Type = (SensorType)message.SubType;
                            node.ProtocolVersion = message.Payload;

                            SaveOrUpdate(node);
                        }

                        NotifyMessageReceivedForPlugins(message);
                        NotifyMessageReceivedForScripts(message);
                        NotifyForSignalR(new { MsgId = "NodePresentation", Data = BuildNodeRichWebModel(node) });
                    }
                    else // sensor message
                    {
                        if (node != null)
                        {
                            if (sensor == null)
                            {
                                sensor = new Sensor()
                                {
                                    Id = Guid.NewGuid(),
                                    NodeNo = node.NodeNo,
                                    SensorNo = message.SensorNo,
                                    Type = (SensorType)message.SubType,
                                    ProtocolVersion = message.Payload
                                };

                                Save(sensor);
                            }
                            else
                            {
                                sensor.Type = (SensorType)message.SubType;
                                sensor.ProtocolVersion = message.Payload;

                                SaveOrUpdate(sensor);
                            }

                            NotifyMessageReceivedForPlugins(message);
                            NotifyMessageReceivedForScripts(message);
                            NotifyForSignalR(new { MsgId = "SensorPresentation", Data = BuildSensorRichWebModel(sensor) });
                        }
                    }
                    break;
                #endregion

                #region Set
                case SensorMessageType.Set: // sent from or to a sensor when a sensor value should be updated
                    if (sensor != null)
                    {
                        NotifyMessageCalibrationForPlugins(message); // before saving to DB plugins may adjust the sensor value due to their calibration params
                        var sv = SaveSensorValueToDB(message);

                        NotifyForSignalR(new { MsgId = "MySensorsTileContent", Data = BuildTileContent() }); // update MySensors tile

                        NotifyMessageReceivedForPlugins(message);
                        NotifyMessageReceivedForScripts(message);
                        NotifyForSignalR(new { MsgId = "SensorValue", Data = sv }); // notify Web UI
                    }
                    break;
                #endregion

                #region Request
                case SensorMessageType.Request: // requests a variable value (usually from an actuator destined for controller)
                    break;
                #endregion

                #region Internal
                case SensorMessageType.Internal: // special internal message
                    InternalValueType ivt = (InternalValueType)message.SubType;

                    switch (ivt)
                    {
                        case InternalValueType.BatteryLevel: // int, in %
                            if (node != null)
                            {
                                var dtDB = DateTime.UtcNow;
                                var dt = DateTime.Now;

                                BatteryLevel bl = new BatteryLevel()
                                {
                                    Id = Guid.NewGuid(),
                                    NodeNo = message.NodeNo,
                                    TimeStamp = dtDB,
                                    Level = byte.Parse(message.Payload)
                                };

                                Save(bl);

                                bl.TimeStamp = dt;

                                NotifyMessageReceivedForPlugins(message);
                                NotifyMessageReceivedForScripts(message);
                                NotifyForSignalR(new { MsgId = "BatteryLevel", Data = bl });
                            }
                            break;
                        case InternalValueType.Time:
                            var result = Convert.ToInt64(DateTime.Now.Subtract(unixEpoch).TotalSeconds).ToString();
                            gatewayProxy.Send(new SensorMessage(message.NodeNo, message.SensorNo, SensorMessageType.Internal, false, (byte)InternalValueType.Time, result));
                            break;
                        case InternalValueType.Version:
                            break;
                        case InternalValueType.IDRequest:
                            GetNextAvailableNodeID();
                            break;
                        case InternalValueType.IDResponse:
                            break;
                        case InternalValueType.InclusionMode:
                            break;
                        case InternalValueType.Config:
                            gatewayProxy.Send(new SensorMessage(message.NodeNo, 255, SensorMessageType.Internal, false, (byte)InternalValueType.Config, GetSetting("UnitSystem").Value));
                            break;
                        case InternalValueType.FindParent:
                            break;
                        case InternalValueType.FindParentResponse:
                            break;
                        case InternalValueType.LogMessage:
                            break;
                        case InternalValueType.Children:
                            break;
                        case InternalValueType.SketchName:
                        case InternalValueType.SketchVersion:
                            if (node != null)
                            {
                                if (ivt == InternalValueType.SketchName)
                                    node.SketchName = message.Payload;
                                else
                                    node.SketchVersion = message.Payload;

                                SaveOrUpdate(node);

                                NotifyMessageReceivedForPlugins(message);
                                NotifyMessageReceivedForScripts(message);
                                NotifyForSignalR(new { MsgId = "NodePresentation", Data = BuildNodeRichWebModel(node) });
                            }
                            break;
                        case InternalValueType.Reboot:
                            break;
                        case InternalValueType.GatewayReady:
                            break;
                    }
                    break;
                #endregion

                #region Stream
                case SensorMessageType.Stream: //used for OTA firmware updates
                    switch ((StreamValueType)message.SubType)
                    {
                        case StreamValueType.FirmwareConfigRequest:
                            //var fwtype = pullWord(payload, 0);
                            //var fwversion = pullWord(payload, 2);
                            //sendFirmwareConfigResponse(sender, fwtype, fwversion, db, gw);
                            break;
                        case StreamValueType.FirmwareConfigResponse:
                            break;
                        case StreamValueType.FirmwareRequest:
                            break;
                        case StreamValueType.FirmwareResponse:
                            //var fwtype = pullWord(payload, 0);
                            //var fwversion = pullWord(payload, 2);
                            //var fwblock = pullWord(payload, 4);
                            //sendFirmwareResponse(sender, fwtype, fwversion, fwblock, db, gw);
                            break;
                        case StreamValueType.Sound:
                            break;
                        case StreamValueType.Image:
                            break;
                    }
                    break;
                #endregion
            }

            CheckRebootRequest(node);
        }
        public object BuildNodeRichWebModel(Node node)
        {
            if (node == null)
                return null;

            List<BatteryLevel> bls = GetBatteryLevels(node, 10);

            return new
            {
                Id = node.Id,
                Name = node.Name,
                NodeNo = node.NodeNo,
                TypeName = node.TypeName,
                ProtocolVersion = node.ProtocolVersion,
                SketchName = node.SketchName,
                SketchVersion = node.SketchVersion,
                BatteryLevels = bls.ToArray(),
                BatteryLevelLevel = bls.Count == 0 ? (byte?)null : bls.Last().Level,
                BatteryLevelTimeStamp = bls.Count == 0 ? (DateTime?)null : bls.Last().TimeStamp
            };
        }
 private void CheckRebootRequest(Node node)
 {
     if (node != null && node.Reboot)
         RebootNode(node);
 }
        private void GetNextAvailableNodeID()
        {
            using (var session = Context.OpenSession())
            {
                var nds = session.Query<Node>().OrderBy(node => node.NodeNo).ToList();

                byte id = 1;
                for (byte i = 0; i < nds.Count; i++)
                    if (nds[i].NodeNo > i + 1)
                    {
                        id = (byte)(i + 1);
                        break;
                    }
                    else
                        id++;

                if (id < 255)
                {
                    Node node = new Node { Id = Guid.NewGuid(), NodeNo = id };
                    Save(node);

                    gatewayProxy.Send(new SensorMessage(255, 255, SensorMessageType.Internal, false, (byte)InternalValueType.IDResponse, id.ToString()));
                }
            }
        }
 //public List<BatteryLevel> GetBatteryLevels(Node node, int hours, int count)
 //{
 //    return node != null ? GetBatteryLevels(node.NodeNo, hours, count) : new List<BatteryLevel>();
 //}
 public List<BatteryLevel> GetBatteryLevels(Node node, int count)
 {
     return node != null ? GetBatteryLevels(node.NodeNo, count) : new List<BatteryLevel>();
 }
 public BatteryLevel GetLastBatteryLevel(Node node)
 {
     return node != null ? GetLastBatteryLevel(node.NodeNo) : null;
 }
 public void RebootNode(Node node)
 {
     if (gatewayProxy != null && node != null)
         gatewayProxy.Send(new SensorMessage(node.NodeNo, 255, SensorMessageType.Internal, false, (byte)InternalValueType.Reboot, ""));
 }