private UdpClient GetConnectedClient(LifxCommand command, IPEndPoint endPoint) { if (mSendCommandClient == null) { return(CreateClient(command, endPoint)); } else { if (command.IsBroadcastCommand) { if (mSendCommandClient.Client.EnableBroadcast) { return(mSendCommandClient); } else { mSendCommandClient.Close(); return(CreateClient(command, endPoint)); } } else { if (mSendCommandClient.Client.EnableBroadcast) { mSendCommandClient.Close(); return(CreateClient(command, endPoint)); } else { return(mSendCommandClient); } } } }
public LifxDataPacket(LifxCommand messageToPackage) { mPacketData = new byte[messageToPackage.GetRawMessage().Length + 36]; Size = (ushort)(messageToPackage.GetRawMessage().Length + 36); Protocol = STANDARD_PROTOCOL; PacketType = messageToPackage.PacketType; Payload = messageToPackage.GetRawMessage(); }
public LifxDataPacket(LifxCommand messageToPackage) { mPacketData = new byte[messageToPackage.GetRawMessage().Length + 36]; Size = (ushort)(messageToPackage.GetRawMessage().Length + 36); Protocol = STANDARD_PROTOCOL; PacketType = (ushort)messageToPackage.CommandPacketType; //PacketTimestamp = messageToPackage.TimeStamp; // breaks everything... maybe a fix in the firmware update? Payload = messageToPackage.GetRawMessage(); }
/// <summary> /// Sends the command to the device specified /// </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 false if the command couldn't be sent for whatever reason</returns> private async Task <bool> SendCommandRaw(LifxCommand command, string bulbMacAddress, string panControllerMacAddress, string remoteIPAddress) { try { LifxDataPacket packet = new LifxDataPacket(command); packet.TargetMac = LifxHelper.StringToByteArray(bulbMacAddress); packet.PanControllerMac = LifxHelper.StringToByteArray(panControllerMacAddress); using (var stream = await new DatagramSocket().GetOutputStreamAsync(new HostName(remoteIPAddress), LifxHelper.LIFX_PORT.ToString())) { using (var writer = new DataWriter(stream)) { writer.WriteBytes(packet.PacketData); await writer.StoreAsync(); } } return(true); } catch (Exception) { return(false); } }
private UdpClient CreateClient(LifxCommand command, IPEndPoint endPoint) { if (command.IsBroadcastCommand) { mSendCommandClient = new UdpClient(); mSendCommandClient.EnableBroadcast = true; mSendCommandClient.Client.SetSocketOption( SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); mSendCommandClient.Connect(new IPEndPoint(IPAddress.Broadcast, LIFX_PORT)); return(mSendCommandClient); } else { mSendCommandClient = new UdpClient(); mSendCommandClient.Client.SetSocketOption( SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); mSendCommandClient.Connect(endPoint); return(mSendCommandClient); } }
/// <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); }
/// <summary> /// Tells the comminucator to send a command to the pan controller specified /// </summary> /// <param name="command">The command to send to the pan controller</param> /// <param name="bulb">The pan controller to send the command to</param> /// <returns>Returns a message if the command is expecting one, otherwise will return null <see cref="LifxCommand.ExpectedReturnMessagePacketType"/></returns> public async Task <LifxMessage> SendCommand(LifxCommand command, LifxPanController panController) { return(await SendCommand(command, "", panController.MACAddress, panController.IPAddress)); }
/// <summary> /// Tells the comminucator to send a command to the bulb specified /// </summary> /// <param name="command">The command to send to the bulb</param> /// <param name="bulb">The bulb to send the command to</param> /// <returns>Returns a message if the command is expecting one, otherwise will return null <see cref="LifxCommand.ExpectedReturnMessagePacketType"/></returns> public async Task <LifxMessage> SendCommand(LifxCommand command, LifxBulb bulb) { return(await SendCommand(command, bulb.MACAddress, bulb.PanController.MACAddress, bulb.IPAddress)); }
/// <summary> /// Sends command to a bulb /// </summary> /// <param name="command"></param> /// <param name="bulb">The bulb to send the command to.</param> /// <returns>Returns the response message. If the command does not trigger a response it will reurn null. </returns> public LifxReceivedMessage SendCommand(LifxCommand command, string macAddress, string panController, IPEndPoint endPoint) { if (!IsInitialized) { throw new InvalidOperationException("The communicator needs to be initialized before sending a command."); } UdpClient client = GetConnectedClient(command, endPoint); LifxDataPacket packet = new LifxDataPacket(command); packet.TargetMac = LifxHelper.StringToByteArray(macAddress); packet.PanControllerMac = LifxHelper.StringToByteArray(panController); client.Send(packet.PacketData, packet.PacketData.Length); DateTime commandSentTime = DateTime.Now; if (command.ReturnMessage == null) { return(null); } while ((DateTime.Now - commandSentTime).TotalMilliseconds < mTimeoutMilliseconds) { if (mIncomingQueue.Count != 0) { IncomingMessage mess = mIncomingQueue.Dequeue(); LifxDataPacket receivedPacket = mess.Data; if (receivedPacket.PacketType == LifxPANGatewayStateMessage.PACKET_TYPE) { //Panhandler identified LifxPANGatewayStateMessage panGateway = new LifxPANGatewayStateMessage(); panGateway.ReceivedData = receivedPacket; AddDiscoveredPanHandler(new LifxPanController( LifxHelper.ByteArrayToString(receivedPacket.TargetMac), mess.BulbAddress)); } else if (receivedPacket.PacketType == LifxLightStatusMessage.PACKET_TYPE && command.IsDiscoveryCommand) { //Panhandler identified LifxLightStatusMessage panGateway = new LifxLightStatusMessage(); panGateway.ReceivedData = receivedPacket; AddDiscoveredBulb( LifxHelper.ByteArrayToString(receivedPacket.TargetMac), LifxHelper.ByteArrayToString(receivedPacket.PanControllerMac)); } else if (receivedPacket.PacketType == command.ReturnMessage.PacketType) { command.ReturnMessage.ReceivedData = receivedPacket; mIncomingQueue.Clear(); return(command.ReturnMessage); } } Thread.Sleep(30); } if (command.IsDiscoveryCommand) { return(null); } if (command.RetryCount > 0) { command.RetryCount -= 1; //Recurssion return(SendCommand(command, macAddress, panController, endPoint)); } else { throw new TimeoutException("Did not get a reply from bulb in a timely fashion"); } }
public LifxReceivedMessage SendCommand(LifxCommand command, LifxPanController panController) { return(SendCommand(command, "", panController.MacAddress, panController.IpEndpoint)); }
public LifxReceivedMessage SendCommand(LifxCommand command, LifxBulb bulb) { return(SendCommand(command, bulb.MacAddress, bulb.PanHandler, bulb.IpEndpoint)); }