/// <summary> /// Called when receiving a message from the server /// </summary> /// <param name="message">Message from server</param> /// <param name="reply">Pre-generated reply message template. Modify it to indicate the result.</param> internal virtual void MessageReceived(SinricMessage message, SinricMessage reply) { Debug.Print($"Sensor {Name} of type {Type} received action: {message.Payload.Action}"); // what action is being requested? ActionStateEnums.TryGetValue(message.Payload.Action, out var enumType); // handle a state change if a known/supported enum if (enumType != null) { BasicState.TryGetValue(message.Payload.Action, out var currentState); // state that the server is trying to set: var newState = message.Payload.GetValue <string>(SinricValue.State); var genericType = typeof(BasicStateChangeInfo <>); var boundType = genericType.MakeGenericType(enumType); var basicStateChangeInfo = (BasicStateChangeInfo)Activator.CreateInstance(boundType); //ActionType = actionStateEnum, basicStateChangeInfo.Action = message.Payload.Action; basicStateChangeInfo.Device = this; basicStateChangeInfo.OldState = currentState; basicStateChangeInfo.NewState = newState; basicStateChangeInfo.Success = true; // look up the enum value by the receive description attribute var enumValues = Enum.GetValues(enumType).Cast <object>(); var enumValue = enumValues.FirstOrDefault(e => SinricMessageAttribute.Get(e)?.ReceiveValue == newState); basicStateChangeInfo.ReceiveValue = newState; basicStateChangeInfo.SendValue = SinricMessageAttribute.Get(enumValue).SendValue; // set the basicStateChangeInfo<T>.NewStateEnum field basicStateChangeInfo.GetType() .GetProperty("NewStateEnum", BindingFlags.Public | BindingFlags.Instance) ?.SetValue(basicStateChangeInfo, enumValue, null); // check if general handler is registered. call the handler and return the result Handlers.TryGetValue(message.Payload.Action, out var delegateFunc); var method = delegateFunc?.GetType().GetMethod("Invoke"); method?.Invoke(delegateFunc, new object[] { basicStateChangeInfo }); // check if conditional handler is registered. call the handler and return the result Handlers.TryGetValue(message.Payload.Action + ":" + newState, out var delegateFuncConditional); method?.Invoke(delegateFuncConditional, new object[] { basicStateChangeInfo }); // reply with the result reply.Payload.SetState(basicStateChangeInfo.SendValue); reply.Payload.Success = basicStateChangeInfo.Success; Debug.Print($"Sensor {Name} of type {Type} reply was: {basicStateChangeInfo.NewState}, success: {basicStateChangeInfo.Success}"); } }
/// <summary> /// Enqueue message thread safe /// </summary> /// <param name="message"></param> internal void AddMessageToQueue(SinricMessage message) { var payloadJson = JsonConvert.SerializeObject(message.Payload); message.RawPayload = new JRaw(payloadJson); // compute the signature using our secret key so that the service can verify authenticity message.Signature.Hmac = HmacSignature.Signature(payloadJson, SecretKey); OutgoingMessages.Enqueue(message); Debug.Print("Queued websocket message for sending"); }
/// <summary> /// Send message immediately /// </summary> /// <param name="message"></param> private void SendMessage(SinricMessage message) { try { // serialize the message to json var json = JsonConvert.SerializeObject(message); WebSocket.Send(json); Debug.Print("Websocket message sent:\n" + json + "\n"); } catch (Exception ex) { Debug.Print("Websocket send exception: " + ex); } }
/// <summary> /// Creates a new message with base information filled in for contacting the server. /// The caller must add remaining info & sign the message for it to be valid. /// </summary> /// <returns>A newly generated message will be returned</returns> internal SinricMessage NewMessage(string messageType) { var message = new SinricMessage { TimestampUtc = DateTime.UtcNow, Payload = { DeviceId = DeviceId, CreatedAtUtc = DateTime.UtcNow, ReplyToken = Guid.NewGuid().ToString(), Type = messageType, Success = SinricPayload.Result.Success } }; return(message); }
internal static bool ValidateMessageSignature(SinricMessage message, string secretKey) { var payloadString = message.RawPayload?.Value as string; if (!string.IsNullOrEmpty(payloadString)) { // if the message contains a payload then we need to validate its signature // todo validate timestamp of message, must be within X seconds of local clock, and must be > than the last message time received to protect against replay attacks // compute a local signature from the raw payload using our secret key: var signature = HmacSignature.Signature(payloadString, secretKey); // compare the locally computed signature with the one supplied in the message: return(signature == message.Signature.Hmac); } return(true); }
/// <summary> /// Given a message, creates a valid response with predetermined defaults filled in. /// The caller must add remaining info & sign the message for it to be valid. /// </summary> /// <param name="message">The message being replied to</param> /// <param name="result"></param> /// <returns>A newly generated message containing the reply details will be returned</returns> internal static SinricMessage CreateReplyMessage(SinricMessage message, bool result = SinricPayload.Result.Fail) { var reply = new SinricMessage { TimestampUtc = DateTime.UtcNow, Payload = { CreatedAtUtc = DateTime.UtcNow, Type = SinricPayload.MessageType.Response, Message = SinricPayload.Messages.Ok, DeviceId = message.Payload.DeviceId, ReplyToken = message.Payload.ReplyToken, Action = message.Payload.Action, Success = result, } }; return(reply); }