public void LightSetPower() { var expected = "2a 00 00 14 c0 65 74 0c d0 73 d5 28 b3 34 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 75 00 00 00 ff ff c8 00 00 00"; var message = LifxMessage.CreateTargeted(new LightSetPowerRequest(true, 200), 208954816, false, false, 1, new byte[] { 0xd0, 0x73, 0xd5, 0x28, 0xb3, 0x34, 0, 0 }, null); var actual = FormatBytes(GetBytes(message)); Assert.AreEqual(expected, actual); }
private byte[] GetBytes(LifxMessage message) { using (var memoryStream = new MemoryStream()) { message.WriteToStream(memoryStream); return(memoryStream.ToArray()); } }
public void GetService() { var expected = "24 00 00 34 4c e5 c1 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 00 00 00"; var message = LifxMessage.CreateBroadcast(new GetServiceRequest(), 113370444, false, false, 0); var actual = FormatBytes(GetBytes(message)); Assert.AreEqual(expected, actual); }
/// <summary> /// The listner clients message recieved event handler. /// This is where the message will be converted into a LifxMessage and will invoke the MessageRecieved event handler if anything is subscribed to it /// </summary> /// <param name="sender">The socket that recieved the packet</param> /// <param name="args">Where the packet information is contained</param> private void lifxCommunicatorClient_MessageReceived(DatagramSocket sender, DatagramSocketMessageReceivedEventArgs args) { if (IsDisposed) { return; } uint bufferArraySize = args.GetDataReader().UnconsumedBufferLength; Byte[] receiveBytes = new Byte[bufferArraySize]; args.GetDataReader().ReadBytes(receiveBytes); LifxDataPacket packet = new LifxDataPacket(receiveBytes); LifxMessage receivedMessage = LifxHelper.PacketToMessage(packet); if (receivedMessage != null) // Check to make sure the packet we recieved was sent from a LIFX bulb, others can be sending on the same port { // If the packet type is a pan gateway we need to handle this a bit differently if (receivedMessage.PacketType == MessagePacketType.PanGateway) { // This locks the variable and lets any senders will waiting for a pan gateway know that we have received the message and you can stop retrying lock (commandsAwaitingResponse) { // Ensure that there are senders awaiting a message if (commandsAwaitingResponse.ContainsKey(receivedMessage.PacketType)) { commandsAwaitingResponse[receivedMessage.PacketType] = receivedMessage; // Sets the reply (from bulb) message to this message messageResumer[receivedMessage.PacketType].Release(); // Stops the senders from retrying to ask for pan gateway messages } } // Sets up the pan handler class and assigns the bulb to it LifxPanController foundPanHandler = new LifxPanController() { MACAddress = LifxHelper.ByteArrayToString(receivedMessage.ReceivedData.PanControllerMac), IPAddress = args.RemoteAddress.DisplayName }; foundPanHandler.Bulbs.Add(new LifxBulb(foundPanHandler, args.RemoteAddress.DisplayName, LifxHelper.ByteArrayToString(receivedMessage.ReceivedData.PanControllerMac))); // Invoke the PanControllerFound event handler PanControllerFound.Invoke(this, foundPanHandler); return; } // If the incoming packet isn't a pan controller, just lock the variable // and lets any senders that may be waiting for a return message know that we have received the message and you can stop retrying lock (commandsAwaitingResponse) { // If there is someone waiting for a recieved message, i.e. the command is *meant* to receive something from the bulb // let it know we have found it (Release) and assign the message to the varaible // Otherwise we have received a LifxMessage we didn't ask for. Ensure that there is someone subscribed to the event handler and Invoke with the message if (commandsAwaitingResponse.ContainsKey(receivedMessage.PacketType)) { commandsAwaitingResponse[receivedMessage.PacketType] = receivedMessage; messageResumer[receivedMessage.PacketType].Release(); } else { if (MessageRecieved != null) { MessageRecieved.Invoke(this, receivedMessage); } } } } }
/// <summary> /// Sends a command to the specified IP address /// </summary> /// <param name="command">The command to send</param> /// <param name="bulbMacAddress">The MAC address of the bulb that will receive the command (used for mesh networking)</param> /// <param name="panControllerMacAddress">The pan controller MAC address that will receive the command (used for mesh networking)</param> /// <param name="remoteIPAddress">The IP address that will physically receive the command first</param> /// <returns>Returns a message if the command is expecting one, otherwise will return null <see cref="LifxCommand.ExpectedReturnMessagePacketType"/></returns> private async Task <LifxMessage> SendCommand(LifxCommand command, string bulbMacAddress, string panControllerMacAddress, string remoteIPAddress) { if (!IsInitialized) { throw new InvalidOperationException("The communicator needs to be initialized before sending a command."); } // If the command requires a reply message handle it accordingly (TCP over UDP essentially) otherwise just send the command if (command.ExpectedReturnMessagePacketType != MessagePacketType.Unknown) { // This locks and sets up the variable lock (commandsAwaitingResponse) { // If it contains a key already don't create a new one, use an old one // TODO: This is to ensure for example if a user spams a GetLightState command, if we receive one LightStateMessage all the commands that are waiting will be dropped // bar one, that returns the message if (!commandsAwaitingResponse.ContainsKey(command.ExpectedReturnMessagePacketType)) { commandsAwaitingResponse.Add(command.ExpectedReturnMessagePacketType, null); messageResumer.Add(command.ExpectedReturnMessagePacketType, new SemaphoreSlim(0)); } } do { LifxMessage returnedMessage = null; SemaphoreSlim resumerSemaphore = null; lock (commandsAwaitingResponse) { commandsAwaitingResponse.TryGetValue(command.ExpectedReturnMessagePacketType, out returnedMessage); messageResumer.TryGetValue(command.ExpectedReturnMessagePacketType, out resumerSemaphore); } // Send command then wait for reply. If Semephore times out then subtract one from retry count and send again // Otherwise if there is a response heard from the device remove the values from the varaibles and return the message received if (returnedMessage == null)// && resumerSemaphore != null) { if (await SendCommandRaw(command, bulbMacAddress, panControllerMacAddress, remoteIPAddress)) { // If a response has come in then the wait command will return true if (!resumerSemaphore.Wait(command.WaitTimeBetweenRetry)) { command.RetryCount--; } } // If the retry count reaches 0 and the command is required to, or want a reply and none is heard from then throw an exception if (command.RetryCount == 0 && command.NeedReplyMessage) { throw new TimeoutException("No response heard from light in required time"); } } else { commandsAwaitingResponse.Remove(command.ExpectedReturnMessagePacketType); messageResumer.Remove(command.ExpectedReturnMessagePacketType); return(returnedMessage); } } while (command.RetryCount > 0); // Continue to loop if no response is head and the reply count is not zero yet } else { await SendCommandRaw(command, bulbMacAddress, panControllerMacAddress, remoteIPAddress); return(null); } return(null); }