/// <summary> /// It looks for the compatible devices (discovery + analysis). Then, it asks /// for the user to choose the device. The selected device is returned /// through a CompatibleEndPoint object. It contains the id, service and /// characteristic name. /// </summary> /// <returns>The complete device id (or CompatibleEndPoint with all fields set as null if none were selected)</returns> static async Task <CompatibleEndPoint> AutomaticScan() { BleUtility.Discovery bleDiscovery = new BleUtility.Discovery(); bleDiscovery.Start(); LogHelper.Overwrite(true); LogHelper.NewLine(false); while (!bleDiscovery.HasEnded()) { Thread.Sleep(50); } LogHelper.Overwrite(false); LogHelper.NewLine(true); LogHelper.Log(""); LogHelper.Ok("Devices:"); LogHelper.IncrementIndentLevel(); List <DeviceInformation> devices = bleDiscovery.GetDevices(); foreach (DeviceInformation device in devices) { LogHelper.Ok(device.Id + " " + device.Name); } LogHelper.DecrementIndentLevel(); List <CompatibleEndPoint> compatibleEndPoints = new List <CompatibleEndPoint>(); LogHelper.Pending("Checking compatibility of each device"); LogHelper.IncrementIndentLevel(); foreach (DeviceInformation device in devices) { LogHelper.Pending("Checking compatibility of device " + device.Id.Split("-").Last()); LogHelper.IncrementIndentLevel(); LogHelper.Pending("Connecting to device..."); BluetoothLEDevice connection = await BleUtility.Connect(device.Id); if (connection == null) { LogHelper.DecrementIndentLevel(); LogHelper.Warn("Failed to connect (doesn't mean that it's incompatible)"); continue; } LogHelper.Ok("Connected"); LogHelper.Pending("Looking for services..."); IReadOnlyList <GattDeviceService> services = await BleUtility.GetServices(connection); if (services == null) { LogHelper.DecrementIndentLevel(); LogHelper.Warn("Gatt communication failed"); continue; } else if (services.Count == 0) { LogHelper.DecrementIndentLevel(); LogHelper.Warn("No services found"); continue; } LogHelper.Ok(services.Count + " service(s) found"); LogHelper.Pending("Looking for characteristics for each service..."); LogHelper.IncrementIndentLevel(); foreach (GattDeviceService service in services) { LogHelper.Pending("Looking for characteristics of service " + DisplayHelpers.GetServiceName(service)); LogHelper.IncrementIndentLevel(); IReadOnlyList <GattCharacteristic> characteristics = await BleUtility.GetCharacteristics(service); if (characteristics == null) { LogHelper.DecrementIndentLevel(); LogHelper.Warn("Failed to retrieve characteristics"); service.Dispose(); continue; } else if (characteristics.Count == 0) { LogHelper.DecrementIndentLevel(); LogHelper.Warn("No characteristics found"); service.Dispose(); continue; } LogHelper.Ok(characteristics.Count + " characteristic(s) found"); LogHelper.Pending("Checking compatibility of each characteristic..."); LogHelper.IncrementIndentLevel(); int compatibleCpt = 0; foreach (GattCharacteristic characteristic in characteristics) { LogHelper.Pending("Checking characteristic " + DisplayHelpers.GetCharacteristicName(characteristic) + "..."); if (BleUtility.IsWriteableCharateristic(characteristic)) { CompatibleEndPoint endPoint = new CompatibleEndPoint( device.Id, device.Name, DisplayHelpers.GetServiceName(service), DisplayHelpers.GetCharacteristicName(characteristic) ); compatibleCpt++; compatibleEndPoints.Add(endPoint); LogHelper.Ok("Compatible!"); } else { LogHelper.Warn("Not compatible"); } } service.Dispose(); LogHelper.DecrementIndentLevel(); LogHelper.Ok(compatibleCpt + " compatible endpoint(s) found"); LogHelper.DecrementIndentLevel(); } LogHelper.Ok("Finished looking for characteristics"); LogHelper.DecrementIndentLevel(); LogHelper.Ok("Finished compatibility check of device " + device.Id.Split('-').Last()); LogHelper.DecrementIndentLevel(); } LogHelper.DecrementIndentLevel(); LogHelper.Ok("Finished analyzing devices"); if (compatibleEndPoints.Count == 0) { LogHelper.Error("No compatible device found"); LogHelper.Error("Make sure that you're not already connected to it"); return(new CompatibleEndPoint(null, null, null, null)); } else { LogHelper.Ok("Compatible device(s):"); LogHelper.IncrementIndentLevel(); string[] ids = new string[compatibleEndPoints.Count]; for (int i = 0; i < compatibleEndPoints.Count; i++) { CompatibleEndPoint compatibleEndPoint = compatibleEndPoints.ElementAt(i); LogHelper.Ok("name = '" + compatibleEndPoint.deviceName + "' id = '" + compatibleEndPoint.deviceId + "'"); ids[i] = compatibleEndPoint.deviceId + (compatibleEndPoint.deviceName != "" ? (" " + compatibleEndPoint.deviceName) : ""); } LogHelper.DecrementIndentLevel(); int choice = LogHelper.AskUserToChoose("Choose the device to use: ", ids); bleDiscovery.Dispose(); return(compatibleEndPoints.ElementAt(choice)); } }
/// <summary> /// The program Loop: CTRL+C to stop it properly /// </summary> /// <param name="characteristic"></param> static void Loop(GattCharacteristic characteristic) { SoundListener soundListener = new SoundListener(); Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e) { e.Cancel = true; keepRunning = false; }; LogHelper.Ok("Program running. Press CTRL+C to stop"); #region variables (prevents reallocation) // Rectangle in the middle of the screen Rectangle rect = new Rectangle(1920 / 4, 1080 / 4, 1920 / 4 * 2, 1080 / 4 * 2); String colorCode = ""; Color color; int soundLevel; String brightness; String textToWrite; #endregion #region configuration bool sensitiveToBass = Configuration.audioSensibility == Configuration.AudioSensibility.BASS_LEVEL; bool dynamicSmoothing = Configuration.smoothingMode == Configuration.SmoothingMode.DYNAMIC; bool valueSmoothing = Configuration.smoothingMode == Configuration.SmoothingMode.VALUE; double smoothness = Configuration.smoothingValue; #endregion int current = -1; // Smooth brightness variation int cpt = 0; // Used to know when to read the screen's pixels soundListener.ListenForBass(sensitiveToBass); while (keepRunning) { if (Configuration.audioSensibility != Configuration.AudioSensibility.NONE) { soundLevel = (int)((sensitiveToBass ? soundListener.GetBassLevel() : soundListener.GetSoundLevel()) * 100f); // Between 0.0f and 100.0f if (current == -1) { current = soundLevel; } if (dynamicSmoothing) { current = (current + soundLevel) / 2; } else if (current < soundLevel && valueSmoothing && smoothness > 0) { current = Math.Min((int)(current + 100 / smoothness), soundLevel); } else if (smoothness > 0) { current = Math.Max((int)(current - 100 / smoothness), soundLevel); } else { current = soundLevel; } // Format: 7e 00 01 brightness 00 00 00 00 ef // brightness: 0x00-0x64 (0-100) // So we need to convert the soundLevel to hex so that 100.0f is 0x64 and 0.0f is 0x00 brightness = (current).ToString("X"); textToWrite = "7e0001" + brightness + "00000000ef"; _ = BleUtility.WriteHex(textToWrite, characteristic); // we don't want it to be blocking } if (Configuration.colorSensibility != Configuration.ColorSensibility.NONE) { // We don't want to analyze pixels as fast as we check for the sound level cpt++; if (cpt == 10) { new Thread(() => { color = ScreenUtils.CalculateAverageScreenColorAt(rect); if (color.GetBrightness() >= 0.85f) { // white colorCode = "86"; } else if (color.GetHue() < 25 || color.GetHue() >= 330) { // red colorCode = "80"; } else if (color.GetHue() >= 25 && color.GetHue() < 65) { // yellow colorCode = "84"; } else if (color.GetHue() >= 65 && color.GetHue() < 180) { // green colorCode = "82"; } else if (color.GetHue() >= 180 && color.GetHue() < 200) { // cyan colorCode = "83"; } else if (color.GetHue() >= 200 && color.GetHue() < 250) { // blue colorCode = "81"; } else if (color.GetHue() >= 250 && color.GetHue() < 330) { // magenta colorCode = "85"; } _ = BleUtility.WriteHex("7e0003" + colorCode + "03000000ef", characteristic); }).Start(); cpt = 0; } } Thread.Sleep(50); } soundListener.Dispose(); }
/// <summary> /// The actual program, needs to be waited /// </summary> /// <param name="args"></param> /// <returns></returns> static async Task Run(string[] args) { LogHelper.PrintHeader(); LogHelper.PrintTitle("Configuration"); bool configSucceed = await Configure(args); if (!configSucceed) { LogHelper.Error("Something went wrong during configuration"); } else { #region connection LogHelper.PrintTitle("Connection"); LogHelper.Pending("Looking for BLE device of id " + Configuration.device.deviceId); device = await BleUtility.Connect(Configuration.device.deviceId); if (device == null) { LogHelper.Error("Failed to connect to device"); } #endregion #region services GattDeviceService targettedService = null; if (device != null) { LogHelper.PrintTitle("Services"); LogHelper.Pending("Looking for service " + Configuration.device.deviceServiceName + "..."); IReadOnlyList <GattDeviceService> services = await BleUtility.GetServices(device); if (services != null) { LogHelper.Ok(String.Format("Found {0} service(s)", services.Count)); foreach (var service in services) { if (DisplayHelpers.GetServiceName(service) == Configuration.device.deviceServiceName) { LogHelper.Ok("Found service " + Configuration.device.deviceServiceName); targettedService = service; break; } } if (targettedService == null) { LogHelper.Error("Couldn't find service " + Configuration.device.deviceServiceName); } } else { LogHelper.Error("Device unreachable"); } } #endregion #region caracteristics GattCharacteristic characteristic = null; if (targettedService != null) { LogHelper.PrintTitle("Caracteristics"); LogHelper.Pending("Looking for characteristic " + Configuration.device.deviceCharacteristicName + "..."); IReadOnlyList <GattCharacteristic> characteristics = await BleUtility.GetCharacteristics(targettedService); if (characteristics == null) { LogHelper.Error("Could not find characteristics of " + Configuration.device.deviceName); } else { foreach (var charact in characteristics) { if (DisplayHelpers.GetCharacteristicName(charact) == Configuration.device.deviceCharacteristicName) { LogHelper.Ok("Found characteristic"); characteristic = charact; } } if (characteristic == null) { LogHelper.Error("Could not find characteristic " + Configuration.device.deviceCharacteristicName); } } } #endregion #region show config LogHelper.PrintTitle("Save your config"); LogHelper.Ok("Using configuration:"); LogHelper.Log(Configuration.GetArgumentDetails()); LogHelper.Log("\"" + Configuration.ToArgument() + "\""); LogHelper.Ok("Copy the line below, and run the program with this argument to [...]"); LogHelper.Ok("[...] start the program automatically with the current configuration"); #endregion #region communication if (characteristic != null) { LogHelper.PrintTitle("Communication"); if (BleUtility.IsWriteableCharateristic(characteristic)) { Loop(characteristic); } else { LogHelper.Error("This characteristic does not have either the 'Write' or 'WriteWithoutResponse' properties"); } } #endregion } #region cleanup LogHelper.PrintTitle("Cleanup"); LogHelper.Pending("Exiting properly"); device?.Dispose(); LogHelper.Ok("Done. Type a key to exit"); Console.ReadKey(true); #endregion }