diff --git a/BrickController2/BrickController2.Tests/DeviceManagement/SBrickDeviceManagerTests.cs b/BrickController2/BrickController2.Tests/DeviceManagement/SBrickDeviceManagerTests.cs index 111abb20..2c72a1f2 100644 --- a/BrickController2/BrickController2.Tests/DeviceManagement/SBrickDeviceManagerTests.cs +++ b/BrickController2/BrickController2.Tests/DeviceManagement/SBrickDeviceManagerTests.cs @@ -1,5 +1,4 @@ using BrickController2.DeviceManagement; -using BrickController2.DeviceManagement.Lego; using FluentAssertions; using Xunit; @@ -8,9 +7,10 @@ namespace BrickController2.Tests.DeviceManagement; public class SBrickDeviceManagerTests : DeviceManagerTestBase { [Fact] - public void TryGetDevice_VengitManufacturerId_ReturnsSBrickDevice() + public void TryGetDevice_VengitManufacturerIdWithSBrickProductId_ReturnsSBrickDevice() { - byte[] manufacturerData = [0x98, 0x01]; + byte[] manufacturerData = [0x98, 0x01, + 0x02, 0x00, 0x00]; var scanResult = CreateScanResult(deviceName: default, manufacturerData: manufacturerData); var result = _manager.TryGetDevice(scanResult, out var device); @@ -25,6 +25,52 @@ public void TryGetDevice_VengitManufacturerId_ReturnsSBrickDevice() }); } + [Fact] + public void TryGetDevice_VengitManufacturerIdWithSBrickLightProductId_ReturnsSBrickLightDevice() + { + byte[] manufacturerData = [0x98, 0x01, + 0x02, 0x03, 0x00, + 0x06, 0x00, 0x01, 0x05, 0x00, 0x05, 0x19]; + var scanResult = CreateScanResult(deviceName: default, manufacturerData: manufacturerData); + + var result = _manager.TryGetDevice(scanResult, out var device); + + result.Should().BeTrue(); + device.Should().BeEquivalentTo(new FoundDevice() + { + DeviceAddress = scanResult.DeviceAddress, + DeviceName = scanResult.DeviceName, + DeviceType = DeviceType.SBrickLight, + ManufacturerData = manufacturerData + }); + } + + [Fact] + public void TryGetDevice_VengitManufacturerIdWithUnknownProductId_ReturnsFalse() + { + byte[] manufacturerData = [0x98, 0x01, + 0x06, 0x00, 0xAA, 0x00, 0x00, 0x00, 0x00]; + var scanResult = CreateScanResult(deviceName: default, manufacturerData: manufacturerData); + + var result = _manager.TryGetDevice(scanResult, out var device); + + result.Should().BeFalse(); + device.DeviceType.Should().Be(DeviceType.Unknown); + } + + [Fact] + public void TryGetDevice_VengitManufacturerIdWithMissingProductId_ReturnsFalse() + { + byte[] manufacturerData = [0x98, 0x01, + 0x02, 0x03, 0x00]; + var scanResult = CreateScanResult(deviceName: default, manufacturerData: manufacturerData); + + var result = _manager.TryGetDevice(scanResult, out var device); + + result.Should().BeFalse(); + device.DeviceType.Should().Be(DeviceType.Unknown); + } + [Fact] public void TryGetDevice_WrongManufacturerId_ReturnsFalse() { diff --git a/BrickController2/BrickController2/BrickController2.csproj b/BrickController2/BrickController2/BrickController2.csproj index 1c121d88..d8dcdfba 100644 --- a/BrickController2/BrickController2/BrickController2.csproj +++ b/BrickController2/BrickController2/BrickController2.csproj @@ -41,6 +41,8 @@ + + diff --git a/BrickController2/BrickController2/DeviceManagement/DI/DeviceManagementModule.cs b/BrickController2/BrickController2/DeviceManagement/DI/DeviceManagementModule.cs index f8fc1442..5147b95a 100644 --- a/BrickController2/BrickController2/DeviceManagement/DI/DeviceManagementModule.cs +++ b/BrickController2/BrickController2/DeviceManagement/DI/DeviceManagementModule.cs @@ -1,8 +1,8 @@ using Autofac; using BrickController2.DeviceManagement.BuWizz; using BrickController2.DeviceManagement.CaDA; -using BrickController2.DeviceManagement.Lego; using BrickController2.DeviceManagement.Vendors; +using BrickController2.DeviceManagement.Vengit; using BrickController2.Extensions; using BrickController2.PlatformServices.BluetoothLE; @@ -19,7 +19,6 @@ protected override void Load(ContainerBuilder builder) builder.RegisterType().As().SingleInstance(); builder.RegisterType().As().SingleInstance(); - builder.RegisterType().Keyed(DeviceType.SBrick); builder.RegisterType().Keyed(DeviceType.BuWizz); builder.RegisterType().Keyed(DeviceType.BuWizz2); builder.RegisterType().Keyed(DeviceType.BuWizz3); diff --git a/BrickController2/BrickController2/DeviceManagement/DeviceType.cs b/BrickController2/BrickController2/DeviceManagement/DeviceType.cs index e7f2afd2..ee535d3e 100644 --- a/BrickController2/BrickController2/DeviceManagement/DeviceType.cs +++ b/BrickController2/BrickController2/DeviceManagement/DeviceType.cs @@ -23,5 +23,6 @@ public enum DeviceType MK5, MK3_8, RemoteControl, + SBrickLight, } } diff --git a/BrickController2/BrickController2/DeviceManagement/SBrickDeviceManager.cs b/BrickController2/BrickController2/DeviceManagement/SBrickDeviceManager.cs index 4d44e6ee..5f99c6d4 100644 --- a/BrickController2/BrickController2/DeviceManagement/SBrickDeviceManager.cs +++ b/BrickController2/BrickController2/DeviceManagement/SBrickDeviceManager.cs @@ -1,6 +1,8 @@ using System; using BrickController2.PlatformServices.BluetoothLE; +using static BrickController2.DeviceManagement.Vengit.SBrickProtocol; + namespace BrickController2.DeviceManagement; /// @@ -15,11 +17,39 @@ public bool TryGetDevice(ScanResult scanResult, out FoundDevice device) // check if there are any data and it matches Vengit prefix 0x0198 if (scanResult.TryGetManufacturerData(out var manufacturerData) && manufacturerData.StartsWith(ManufacturerId)) { - device = new FoundDevice(scanResult, DeviceType.SBrick, manufacturerData); - return true; + var productId = GetProductId(manufacturerData); + + device = productId switch + { + PRODUCT_ID_SBRICK => new FoundDevice(scanResult, DeviceType.SBrick, manufacturerData), + PRODUCT_ID_SBRICK_LIGHT => new FoundDevice(scanResult, DeviceType.SBrickLight, manufacturerData), + + _ => default + }; + return device.DeviceType != DeviceType.Unknown; } device = default; return false; } + + private static byte GetProductId(ReadOnlySpan manufacturerData) + { + // walk through SBrick Data Records to look for 0x00 Product type + int length = 2; + ReadOnlySpan dataRecord = manufacturerData; + + while (length < dataRecord.Length) + { + dataRecord = dataRecord[length..]; + length = 1 + dataRecord[0]; + + if (length > 2 && dataRecord[1] == DATA_RECORD_PRODUCT_TYPE) + { + return dataRecord[2]; + } + } + + return PRODUCT_ID_UNKNOWN; + } } diff --git a/BrickController2/BrickController2/DeviceManagement/Vengit/SBrickLightDevice.cs b/BrickController2/BrickController2/DeviceManagement/Vengit/SBrickLightDevice.cs new file mode 100644 index 00000000..ffef2a3f --- /dev/null +++ b/BrickController2/BrickController2/DeviceManagement/Vengit/SBrickLightDevice.cs @@ -0,0 +1,164 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using BrickController2.DeviceManagement.IO; +using BrickController2.Helpers; +using BrickController2.PlatformServices.BluetoothLE; + +using static BrickController2.DeviceManagement.Vengit.SBrickProtocol; + +namespace BrickController2.DeviceManagement.Vengit; + +internal class SBrickLightDevice : BluetoothDevice +{ + private const int BANK_0_CHANNELS = 16; + private const int BANK_1_CHANNELS = 8; + + private readonly OutputValuesGroup _bankOutputs0 = new(BANK_0_CHANNELS); + private readonly OutputValuesGroup _bankOutputs1 = new(BANK_1_CHANNELS); + + private IGattCharacteristic? _firmwareRevisionCharacteristic; + private IGattCharacteristic? _hardwareRevisionCharacteristic; + private IGattCharacteristic? _remoteControlCharacteristic; + + public SBrickLightDevice(string name, string address, byte[] deviceData, IDeviceRepository deviceRepository, IBluetoothLEService bleService) + : base(name, address, deviceRepository, bleService) + { + } + + public override DeviceType DeviceType => DeviceType.SBrickLight; + public override string BatteryVoltageSign => "V"; + public override int NumberOfChannels => BANK_0_CHANNELS + BANK_1_CHANNELS; + protected override bool AutoConnectOnFirstConnect => false; + + public override void SetOutput(int channel, float value) + { + CheckChannel(channel); + value = CutOutputValue(value); + + // for lights use 0-255 range + var rawValue = (byte)(Math.Abs(value) * 255); + + if (channel >= BANK_0_CHANNELS) + { + int lightChannel = channel - BANK_0_CHANNELS; + _bankOutputs1.SetOutput(lightChannel, rawValue); + } + else + { + _bankOutputs0.SetOutput(channel, rawValue); + } + } + + protected override Task ValidateServicesAsync(IEnumerable? services, CancellationToken token) + { + var deviceInformationService = services?.FirstOrDefault(s => s.Uuid == GattProtocol.DeviceInformationServiceUuid); + _firmwareRevisionCharacteristic = deviceInformationService?.Characteristics?.FirstOrDefault(c => c.Uuid == GattProtocol.FirmwareRevisionCharacteristicUuid); + _hardwareRevisionCharacteristic = deviceInformationService?.Characteristics?.FirstOrDefault(c => c.Uuid == GattProtocol.HardwareRevisionCharacteristicUuid); + + var remoteControlService = services?.FirstOrDefault(s => s.Uuid == SBrickProtocol.ServiceUuid); + _remoteControlCharacteristic = remoteControlService?.Characteristics?.FirstOrDefault(c => c.Uuid == RemoteControlCharacteristicUuid); + + return Task.FromResult( + _firmwareRevisionCharacteristic is not null && + _hardwareRevisionCharacteristic is not null && + _remoteControlCharacteristic is not null); + } + + protected override async Task AfterConnectSetupAsync(bool requestDeviceInformation, CancellationToken token) + { + try + { + if (requestDeviceInformation) + { + await ReadDeviceInfo(token).ConfigureAwait(false); + } + } + catch { } + + return true; + } + + protected override async Task ProcessOutputsAsync(CancellationToken token) + { + try + { + // reset outputs + _bankOutputs0.Initialize(); + _bankOutputs1.Initialize(); + + while (!token.IsCancellationRequested) + { + // process first bank 0 + bool changed = await TryProcessChanges(_bankOutputs0, LIGHTS_FLAGS_APPLY | LIGHTS_FLAGS_BANK_0, token); + + // process additional bank 1 + if (await TryProcessChanges(_bankOutputs1, LIGHTS_FLAGS_APPLY | LIGHTS_FLAGS_BANK_1, token)) + { + changed = true; + } + + if (!changed) + { + await Task.Delay(10, token).ConfigureAwait(false); + } + } + } + catch + { + } + } + + private async Task TryProcessChanges(OutputValuesGroup valueBank, byte flags, CancellationToken token) + { + try + { + if (valueBank.TryGetValues(out var values)) + { + var command = BuildSetAllLights(flags, values); + var success = await _bleDevice!.WriteAsync(_remoteControlCharacteristic!, command, token).ConfigureAwait(false); + if (success) + { + // confirm successful sending + valueBank.Commmit(); + await Task.Delay(5, token).ConfigureAwait(false); + return true; + } + } + return false; + } + catch + { + return false; + } + } + + private async Task ReadDeviceInfo(CancellationToken token) + { + var firmwareData = await _bleDevice!.ReadAsync(_firmwareRevisionCharacteristic!, token); + var firmwareVersion = firmwareData?.ToAsciiStringSafe(); + if (!string.IsNullOrEmpty(firmwareVersion)) + { + FirmwareVersion = firmwareVersion; + } + + var hardwareData = await _bleDevice.ReadAsync(_hardwareRevisionCharacteristic!, token); + var hardwareVersion = hardwareData?.ToAsciiStringSafe(); + if (!string.IsNullOrEmpty(hardwareVersion)) + { + HardwareVersion = hardwareVersion; + } + + // 0x0F Query ADC | voltage on 0x08 + await _bleDevice.WriteAsync(_remoteControlCharacteristic!, [0x0f, 0x08], token); + var voltageBuffer = await _bleDevice!.ReadAsync(_remoteControlCharacteristic!, token); + if (voltageBuffer is not null && voltageBuffer.Length >= 2) + { + var rawVoltage = voltageBuffer[0] + (voltageBuffer[1] << 8); + var voltage = (rawVoltage * 0.42567F) / 2047; + BatteryVoltage = voltage.ToString("F2"); + } + } +} diff --git a/BrickController2/BrickController2/DeviceManagement/Vengit/SBrickProtocol.cs b/BrickController2/BrickController2/DeviceManagement/Vengit/SBrickProtocol.cs new file mode 100644 index 00000000..05215624 --- /dev/null +++ b/BrickController2/BrickController2/DeviceManagement/Vengit/SBrickProtocol.cs @@ -0,0 +1,43 @@ +using System; + +namespace BrickController2.DeviceManagement.Vengit; + +/// +/// Contains implementation of SBrick protocol +/// +internal static class SBrickProtocol +{ + /// + /// SBrick - Remote control service UUID + /// + public static readonly Guid ServiceUuid = new("4dc591b0-857c-41de-b5f1-15abda665b0c"); + /// + /// Remote control service - Remote control commands characteristic UUID + /// + public static readonly Guid RemoteControlCharacteristicUuid = new("02b8cbcc-0e25-4bda-8790-a15f53e6010f"); + + // Light flags + public const byte LIGHTS_FLAGS_BANK_0 = 0x00; + public const byte LIGHTS_FLAGS_BANK_1 = 0x01; + public const byte LIGHTS_FLAGS_APPLY = 0x80; + + // data records + public const byte DATA_RECORD_PRODUCT_TYPE = 0x00; + + public const byte PRODUCT_ID_SBRICK = 0x00; + public const byte PRODUCT_ID_SBRICK_LIGHT = 0x01; + public const byte PRODUCT_ID_UNKNOWN = 0xFF; + + // message builders + public static byte[] BuildSetAllLights(byte flags, ReadOnlySpan values) + { + // 0x36 Set all lights + var buffer = new byte[2 + values.Length]; + + buffer[0] = 0x36; + buffer[1] = flags; + values.CopyTo(buffer.AsSpan(2)); + + return buffer; + } +} diff --git a/BrickController2/BrickController2/DeviceManagement/Vengit/Vengit.cs b/BrickController2/BrickController2/DeviceManagement/Vengit/Vengit.cs new file mode 100644 index 00000000..34505ee7 --- /dev/null +++ b/BrickController2/BrickController2/DeviceManagement/Vengit/Vengit.cs @@ -0,0 +1,24 @@ +using BrickController2.DeviceManagement.DI; +using BrickController2.DeviceManagement.Vendors; +using BrickController2.Extensions; + +namespace BrickController2.DeviceManagement.Vengit; + +/// +/// Vendor: Vengit and all its device types: SBrick, SBrick Plus, SBrick Light and implementation of IBluetoothLEDeviceManager +/// +internal class Vengit : Vendor +{ + public override string VendorName => "Vengit"; + + protected override void Register(VendorBuilder builder) + { + // classic devices + builder.ContainerBuilder + .RegisterDevice(DeviceType.SBrick) + .RegisterDevice(DeviceType.SBrickLight); + + // device manager + builder.RegisterDeviceManager(); + } +} diff --git a/BrickController2/BrickController2/Protocols/GattProtocol.cs b/BrickController2/BrickController2/Protocols/GattProtocol.cs new file mode 100644 index 00000000..259e0ed1 --- /dev/null +++ b/BrickController2/BrickController2/Protocols/GattProtocol.cs @@ -0,0 +1,14 @@ +using System; +using System.Buffers.Binary; + +namespace BrickController2.DeviceManagement.Vengit; + +/// +/// Generic GATT protocol +/// +internal static class GattProtocol +{ + public static readonly Guid DeviceInformationServiceUuid = new("0000180a-0000-1000-8000-00805f9b34fb"); + public static readonly Guid FirmwareRevisionCharacteristicUuid = new("00002a26-0000-1000-8000-00805f9b34fb"); + public static readonly Guid HardwareRevisionCharacteristicUuid = new("00002a27-0000-1000-8000-00805f9b34fb"); +} diff --git a/BrickController2/BrickController2/UI/Controls/ChannelSelectorRadioButton.xaml.cs b/BrickController2/BrickController2/UI/Controls/ChannelSelectorRadioButton.xaml.cs index 054ed878..b737c10a 100644 --- a/BrickController2/BrickController2/UI/Controls/ChannelSelectorRadioButton.xaml.cs +++ b/BrickController2/BrickController2/UI/Controls/ChannelSelectorRadioButton.xaml.cs @@ -13,10 +13,10 @@ public ChannelSelectorRadioButton() InitializeComponent(); } - public static BindableProperty DeviceTypeProperty = BindableProperty.Create(nameof(DeviceType), typeof(DeviceType), typeof(ChannelSelectorRadioButton), default(DeviceType), BindingMode.OneWay, null, OnDeviceTypeChanged); - public static BindableProperty ChannelProperty = BindableProperty.Create(nameof(Channel), typeof(int), typeof(ChannelSelectorRadioButton), 0, BindingMode.OneWay, null, OnChannelChanged); - public static BindableProperty SelectedChannelProperty = BindableProperty.Create(nameof(SelectedChannel), typeof(int), typeof(ChannelSelectorRadioButton), 0, BindingMode.OneWay, null, OnSelectedChannelChanged); - public static BindableProperty CommandProperty = BindableProperty.Create(nameof(Command), typeof(ICommand), typeof(ChannelSelectorRadioButton), null, BindingMode.OneWay, null, OnCommandChanged); + public static readonly BindableProperty DeviceTypeProperty = BindableProperty.Create(nameof(DeviceType), typeof(DeviceType), typeof(ChannelSelectorRadioButton), default(DeviceType), BindingMode.OneWay, null, OnDeviceTypeChanged); + public static readonly BindableProperty ChannelProperty = BindableProperty.Create(nameof(Channel), typeof(int), typeof(ChannelSelectorRadioButton), 0, BindingMode.OneWay, null, OnChannelChanged); + public static readonly BindableProperty SelectedChannelProperty = BindableProperty.Create(nameof(SelectedChannel), typeof(int), typeof(ChannelSelectorRadioButton), 0, BindingMode.OneWay, null, OnSelectedChannelChanged); + public static readonly BindableProperty CommandProperty = BindableProperty.Create(nameof(Command), typeof(ICommand), typeof(ChannelSelectorRadioButton), null, BindingMode.OneWay, null, OnCommandChanged); public DeviceType DeviceType { diff --git a/BrickController2/BrickController2/UI/Controls/DeviceChannelLabel.cs b/BrickController2/BrickController2/UI/Controls/DeviceChannelLabel.cs index ed52b8a7..919cbaf7 100644 --- a/BrickController2/BrickController2/UI/Controls/DeviceChannelLabel.cs +++ b/BrickController2/BrickController2/UI/Controls/DeviceChannelLabel.cs @@ -14,6 +14,7 @@ public class DeviceChannelLabel : Label private readonly static string[] _buwizz3ChannelLetters = new[] { "1", "2", "3", "4", "A", "B" }; private readonly static string[] _mk5ChannelLetters = ["AB", "T", "C", "AB+T", "TL"]; private readonly static string[] _mk6ChannelLetters = new[] { "A", "B", "C", "D", "E", "F" }; + private readonly static char[] _sBrickLightChannelLetters = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H']; public static readonly BindableProperty DeviceTypeProperty = BindableProperty.Create(nameof(DeviceType), typeof(DeviceType), typeof(DeviceChannelLabel), default(DeviceType), BindingMode.OneWay, null, OnDeviceChanged); public static readonly BindableProperty ChannelProperty = BindableProperty.Create(nameof(Channel), typeof(int), typeof(DeviceChannelLabel), 0, BindingMode.OneWay, null, OnChannelChanged); @@ -93,6 +94,14 @@ private void SetChannelText() SetChannelText(_mk5ChannelLetters); break; + case DeviceType.SBrickLight: // e.g. C.3 + Text = $"{_sBrickLightChannelLetters[Channel / 3]}.{1 + Channel % 3}"; + break; + + case DeviceType.Unknown: + Text = ""; + break; + default: Text = $"{Channel + 1}"; break; diff --git a/BrickController2/BrickController2/UI/Controls/DeviceChannelSelector.xaml b/BrickController2/BrickController2/UI/Controls/DeviceChannelSelector.xaml index e1c4bc47..76ae73df 100644 --- a/BrickController2/BrickController2/UI/Controls/DeviceChannelSelector.xaml +++ b/BrickController2/BrickController2/UI/Controls/DeviceChannelSelector.xaml @@ -36,6 +36,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/BrickController2/BrickController2/UI/Controls/DeviceChannelSelector.xaml.cs b/BrickController2/BrickController2/UI/Controls/DeviceChannelSelector.xaml.cs index 66e5f31a..5d3436fb 100644 --- a/BrickController2/BrickController2/UI/Controls/DeviceChannelSelector.xaml.cs +++ b/BrickController2/BrickController2/UI/Controls/DeviceChannelSelector.xaml.cs @@ -17,6 +17,30 @@ public DeviceChannelSelector() SBrickChannel1.Command = new SafeCommand(() => SelectedChannel = 1); SBrickChannel2.Command = new SafeCommand(() => SelectedChannel = 2); SBrickChannel3.Command = new SafeCommand(() => SelectedChannel = 3); + SBrickLightChannelA1.Command = new SafeCommand(() => SelectedChannel = 0); + SBrickLightChannelA2.Command = new SafeCommand(() => SelectedChannel = 1); + SBrickLightChannelA3.Command = new SafeCommand(() => SelectedChannel = 2); + SBrickLightChannelB1.Command = new SafeCommand(() => SelectedChannel = 3); + SBrickLightChannelB2.Command = new SafeCommand(() => SelectedChannel = 4); + SBrickLightChannelB3.Command = new SafeCommand(() => SelectedChannel = 5); + SBrickLightChannelC1.Command = new SafeCommand(() => SelectedChannel = 6); + SBrickLightChannelC2.Command = new SafeCommand(() => SelectedChannel = 7); + SBrickLightChannelC3.Command = new SafeCommand(() => SelectedChannel = 8); + SBrickLightChannelD1.Command = new SafeCommand(() => SelectedChannel = 9); + SBrickLightChannelD2.Command = new SafeCommand(() => SelectedChannel = 10); + SBrickLightChannelD3.Command = new SafeCommand(() => SelectedChannel = 11); + SBrickLightChannelE1.Command = new SafeCommand(() => SelectedChannel = 12); + SBrickLightChannelE2.Command = new SafeCommand(() => SelectedChannel = 13); + SBrickLightChannelE3.Command = new SafeCommand(() => SelectedChannel = 14); + SBrickLightChannelF1.Command = new SafeCommand(() => SelectedChannel = 15); + SBrickLightChannelF2.Command = new SafeCommand(() => SelectedChannel = 16); + SBrickLightChannelF3.Command = new SafeCommand(() => SelectedChannel = 17); + SBrickLightChannelG1.Command = new SafeCommand(() => SelectedChannel = 18); + SBrickLightChannelG2.Command = new SafeCommand(() => SelectedChannel = 19); + SBrickLightChannelG3.Command = new SafeCommand(() => SelectedChannel = 20); + SBrickLightChannelH1.Command = new SafeCommand(() => SelectedChannel = 21); + SBrickLightChannelH2.Command = new SafeCommand(() => SelectedChannel = 22); + SBrickLightChannelH3.Command = new SafeCommand(() => SelectedChannel = 23); BuWizzChannel0.Command = new SafeCommand(() => SelectedChannel = 0); BuWizzChannel1.Command = new SafeCommand(() => SelectedChannel = 1); BuWizzChannel2.Command = new SafeCommand(() => SelectedChannel = 2); @@ -111,6 +135,7 @@ private static void OnDeviceChanged(BindableObject bindable, object oldValue, ob { var deviceType = device.DeviceType; dcs.SbrickSection.IsVisible = deviceType == DeviceType.SBrick; + dcs.SbrickLightSection.IsVisible = deviceType == DeviceType.SBrickLight; dcs.BuWizzSection.IsVisible = deviceType == DeviceType.BuWizz || deviceType == DeviceType.BuWizz2; dcs.BuWizz3Section.IsVisible = deviceType == DeviceType.BuWizz3; dcs.InfraredSection.IsVisible = deviceType == DeviceType.Infrared; @@ -145,6 +170,30 @@ private static void OnSelectedChannelChanged(BindableObject bindable, object old dcs.SBrickChannel1.SelectedChannel = selectedChannel; dcs.SBrickChannel2.SelectedChannel = selectedChannel; dcs.SBrickChannel3.SelectedChannel = selectedChannel; + dcs.SBrickLightChannelA1.SelectedChannel = selectedChannel; + dcs.SBrickLightChannelA2.SelectedChannel = selectedChannel; + dcs.SBrickLightChannelA3.SelectedChannel = selectedChannel; + dcs.SBrickLightChannelB1.SelectedChannel = selectedChannel; + dcs.SBrickLightChannelB2.SelectedChannel = selectedChannel; + dcs.SBrickLightChannelB3.SelectedChannel = selectedChannel; + dcs.SBrickLightChannelC1.SelectedChannel = selectedChannel; + dcs.SBrickLightChannelC2.SelectedChannel = selectedChannel; + dcs.SBrickLightChannelC3.SelectedChannel = selectedChannel; + dcs.SBrickLightChannelD1.SelectedChannel = selectedChannel; + dcs.SBrickLightChannelD2.SelectedChannel = selectedChannel; + dcs.SBrickLightChannelD3.SelectedChannel = selectedChannel; + dcs.SBrickLightChannelE1.SelectedChannel = selectedChannel; + dcs.SBrickLightChannelE2.SelectedChannel = selectedChannel; + dcs.SBrickLightChannelE3.SelectedChannel = selectedChannel; + dcs.SBrickLightChannelF1.SelectedChannel = selectedChannel; + dcs.SBrickLightChannelF2.SelectedChannel = selectedChannel; + dcs.SBrickLightChannelF3.SelectedChannel = selectedChannel; + dcs.SBrickLightChannelG1.SelectedChannel = selectedChannel; + dcs.SBrickLightChannelG2.SelectedChannel = selectedChannel; + dcs.SBrickLightChannelG3.SelectedChannel = selectedChannel; + dcs.SBrickLightChannelH1.SelectedChannel = selectedChannel; + dcs.SBrickLightChannelH2.SelectedChannel = selectedChannel; + dcs.SBrickLightChannelH3.SelectedChannel = selectedChannel; dcs.BuWizzChannel0.SelectedChannel = selectedChannel; dcs.BuWizzChannel1.SelectedChannel = selectedChannel; dcs.BuWizzChannel2.SelectedChannel = selectedChannel; diff --git a/BrickController2/BrickController2/UI/Converters/DeviceTypeToImageConverter.cs b/BrickController2/BrickController2/UI/Converters/DeviceTypeToImageConverter.cs index ec45aa58..2d4ddf1a 100644 --- a/BrickController2/BrickController2/UI/Converters/DeviceTypeToImageConverter.cs +++ b/BrickController2/BrickController2/UI/Converters/DeviceTypeToImageConverter.cs @@ -28,6 +28,9 @@ public class DeviceTypeToImageConverter : IValueConverter case DeviceType.SBrick: return ResourceHelper.GetImageResource("sbrick_image.png"); + case DeviceType.SBrickLight: + return ResourceHelper.GetImageResource("sbricklight_image.png"); + case DeviceType.Infrared: return ResourceHelper.GetImageResource("infra_image.png"); diff --git a/BrickController2/BrickController2/UI/Converters/DeviceTypeToSmallImageConverter.cs b/BrickController2/BrickController2/UI/Converters/DeviceTypeToSmallImageConverter.cs index 532a6965..ebaa0d2e 100644 --- a/BrickController2/BrickController2/UI/Converters/DeviceTypeToSmallImageConverter.cs +++ b/BrickController2/BrickController2/UI/Converters/DeviceTypeToSmallImageConverter.cs @@ -23,6 +23,9 @@ public class DeviceTypeToSmallImageConverter : IValueConverter case DeviceType.SBrick: return ResourceHelper.GetImageResource("sbrick_image_small.png"); + case DeviceType.SBrickLight: + return ResourceHelper.GetImageResource("sbricklight_image_small.png"); + case DeviceType.Infrared: return ResourceHelper.GetImageResource("infra_image_small.png"); diff --git a/BrickController2/BrickController2/UI/Images/sbricklight_image.png b/BrickController2/BrickController2/UI/Images/sbricklight_image.png new file mode 100644 index 00000000..7580bd9a Binary files /dev/null and b/BrickController2/BrickController2/UI/Images/sbricklight_image.png differ diff --git a/BrickController2/BrickController2/UI/Images/sbricklight_image_small.png b/BrickController2/BrickController2/UI/Images/sbricklight_image_small.png new file mode 100644 index 00000000..a7614dda Binary files /dev/null and b/BrickController2/BrickController2/UI/Images/sbricklight_image_small.png differ diff --git a/README.md b/README.md index 64954c3a..94beb5dd 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ Cross platform mobile application for controlling your creations using a bluetoo ## Supported receivers - SBrick - both normal and plus (output only) +- SBrick Light - BuWizz 1 - BuWizz 2 - BuWizz 3