Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion BrickController2/BrickController2/App.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,22 @@
<Style TargetType="Switch">
<Setter Property="BackgroundColor" Value="{DynamicResource TransparentColor}"/>
<Setter Property="OnColor" Value="{DynamicResource SwitchOnColor}"/>
<Setter Property="ThumbColor" Value="{DynamicResource SwitchThumbColor}"/>
<Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="On">
<VisualState.Setters>
<Setter Property="ThumbColor" Value="{DynamicResource SwitchThumbColor}" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Off">
<VisualState.Setters>
<Setter Property="ThumbColor" Value="{DynamicResource SwitchThumbColorOff}" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</Setter>
</Style>

<Style TargetType="Picker">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace BrickController2.DeviceManagement.BuWizz;

public enum BuWizz2OutputLevels
{
Low,
Normal,
High,
Ludicrous,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace BrickController2.DeviceManagement.BuWizz;

public enum BuWizzOutputLevels
{
Low,
Normal,
High
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using BrickController2.Helpers;
using BrickController2.DeviceManagement.BuWizz;
using BrickController2.Helpers;
using BrickController2.PlatformServices.BluetoothLE;
using BrickController2.Settings;
using System;
using System.Collections.Generic;
using System.Linq;
Expand All @@ -21,10 +23,13 @@ internal class BuWizz2Device : BluetoothDevice

private static readonly TimeSpan VoltageMeasurementTimeout = TimeSpan.FromSeconds(5);

private const string SwapChannelsSettingName = "BuWizz2SwapChannels";
private const string DefaultOutputLevelName = "BuWizz2DefaultOutputLevel";
private const BuWizz2OutputLevels DefaultLevel = BuWizz2OutputLevels.Normal;

private readonly int[] _outputValues = new int[4];
private readonly int[] _lastOutputValues = new int[4];
private readonly object _outputLock = new object();
private readonly bool _swapChannels;

private DateTime _batteryMeasurementTimestamp;
private byte _batteryVoltageRaw;
Expand All @@ -37,18 +42,24 @@ internal class BuWizz2Device : BluetoothDevice
private IGattCharacteristic? _modelNumberCharacteristic;
private IGattCharacteristic? _firmwareRevisionCharacteristic;

public BuWizz2Device(string name, string address, byte[] deviceData, IDeviceRepository deviceRepository, IBluetoothLEService bleService)
public BuWizz2Device(string name, string address, byte[] deviceData, IEnumerable<NamedSetting> settings, IDeviceRepository deviceRepository, IBluetoothLEService bleService)
: base(name, address, deviceRepository, bleService)
{
// On BuWizz2 with manufacturer data 0x4e054257001e the ports are swapped
// (no normal BuWizz2es manufacturer data is 0x4e054257001b)
_swapChannels = deviceData != null && deviceData.Length >= 6 && deviceData[5] == 0x1E;
var swapChannels = deviceData != null && deviceData.Length >= 6 && deviceData[5] == 0x1E;

// apply values (if any) or default
SetSettingValue(SwapChannelsSettingName, settings, swapChannels);
SetSettingValue(DefaultOutputLevelName, settings, DefaultLevel);
// update output value again to apply settings
_outputLevel = DefaultOutputLevel;
}

public override DeviceType DeviceType => DeviceType.BuWizz2;
public override int NumberOfChannels => 4;
public override int NumberOfOutputLevels => 4;
public override int DefaultOutputLevel => 1;
public override int DefaultOutputLevel => (int)GetSettingValue(DefaultOutputLevelName, DefaultLevel);
protected override bool AutoConnectOnFirstConnect => false;

public override string BatteryVoltageSign => "V";
Expand Down Expand Up @@ -215,13 +226,15 @@ protected override async Task ProcessOutputsAsync(CancellationToken token)
}
}

private bool SwapChannels => base.GetSettingValue<bool>(SwapChannelsSettingName);

private async Task<bool> SendOutputValuesAsync(int v0, int v1, int v2, int v3, CancellationToken token)
{
try
{
var sendOutputBuffer = new byte[] { 0x10, 0x00, 0x00, 0x00, 0x00, 0x00 };

if (_swapChannels)
if (SwapChannels)
{
sendOutputBuffer[1] = (byte)(v1 / 2);
sendOutputBuffer[2] = (byte)(v0 / 2);
Expand Down
15 changes: 12 additions & 3 deletions BrickController2/BrickController2/DeviceManagement/BuwizzDevice.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using BrickController2.PlatformServices.BluetoothLE;
using BrickController2.DeviceManagement.BuWizz;
using BrickController2.PlatformServices.BluetoothLE;
using BrickController2.Settings;
using System;
using System.Collections.Generic;
using System.Linq;
Expand All @@ -16,6 +18,9 @@ internal class BuWizzDevice : BluetoothDevice

private static readonly TimeSpan LastOutputTimeout = TimeSpan.FromMilliseconds(1500);

private const string DefaultOutputLevelName = "BuWizzDefaultOutputLevel";
private const BuWizzOutputLevels DefaultLevel = BuWizzOutputLevels.Normal;

private readonly int[] _outputValues = new int[4];
private readonly object _outputLock = new object();

Expand All @@ -24,15 +29,19 @@ internal class BuWizzDevice : BluetoothDevice

private IGattCharacteristic? _characteristic;

public BuWizzDevice(string name, string address, byte[] deviceData, IDeviceRepository deviceRepository, IBluetoothLEService bleService)
public BuWizzDevice(string name, string address, byte[] deviceData, IEnumerable<NamedSetting> settings, IDeviceRepository deviceRepository, IBluetoothLEService bleService)
: base(name, address, deviceRepository, bleService)
{
// apply values (if any) or default
SetSettingValue(DefaultOutputLevelName, settings, DefaultLevel);
// update output value again to apply settings
_outputLevel = DefaultOutputLevel;
}

public override DeviceType DeviceType => DeviceType.BuWizz;
public override int NumberOfChannels => 4;
public override int NumberOfOutputLevels => 3;
public override int DefaultOutputLevel => 1;
public override int DefaultOutputLevel => (int)GetSettingValue(DefaultOutputLevelName, DefaultLevel);
protected override bool AutoConnectOnFirstConnect => false;

public override void SetOutput(int channel, float value)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,11 @@ protected override void Load(ContainerBuilder builder)
builder.Register<DeviceFactory>(c =>
{
IComponentContext ctx = c.Resolve<IComponentContext>();
return (deviceType, name, address, deviceData) => ctx.ResolveKeyed<Device>(deviceType, new NamedParameter("name", name), new NamedParameter("address", address), new NamedParameter("deviceData", deviceData));
return (deviceType, name, address, deviceData, settings) => ctx.ResolveOptionalKeyed<Device>(deviceType,
new NamedParameter("name", name),
new NamedParameter("address", address),
new NamedParameter("deviceData", deviceData),
new NamedParameter("settings", settings));
});
}
}
Expand Down
48 changes: 48 additions & 0 deletions BrickController2/BrickController2/DeviceManagement/Device.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using BrickController2.Helpers;
using BrickController2.Settings;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

Expand All @@ -9,6 +11,7 @@ namespace BrickController2.DeviceManagement
public abstract class Device : NotifyPropertyChangedSource
{
private readonly IDeviceRepository _deviceRepository;
private readonly Dictionary<string, NamedSetting> _settings = [];
protected readonly AsyncLock _asyncLock = new AsyncLock();

private string _name;
Expand Down Expand Up @@ -111,6 +114,51 @@ public async Task RenameDeviceAsync(Device device, string newName)
}
}

public async Task UpdateDeviceSettingsAsync(IEnumerable<NamedSetting> settings)
{
using (await _asyncLock.LockAsync())
{
// update provided settings, but existing with matching type only
foreach (var setting in settings ?? [])
{
if (_settings.TryGetValue(setting.Name, out var storedSetting) && setting.Type == storedSetting.Type)
{
storedSetting.Value = setting.Value;
}
}

await _deviceRepository.UpdateDeviceAsync(DeviceType, Address, CurrentSettings);
}
}
public bool HasSettings => _settings.Values.Any();
public IReadOnlyCollection<NamedSetting> CurrentSettings => _settings.Values;

protected TValue GetSettingValue<TValue>(string settingName, TValue defaultValue = default!)
{
if (_settings.TryGetValue(settingName, out var setting) && setting.Value is TValue value)
{
return value;
}

return defaultValue;
}

protected void SetSettingValue<TValue>(string settingName, IEnumerable<NamedSetting>? settings, string group, TValue defaultValue)
where TValue: struct
{
var foundSetting = settings?.FirstOrDefault(s => s.Name == settingName);
_settings[settingName] = new NamedSetting
{
Name = settingName,
Value = foundSetting.GetValue(defaultValue),
Group = group,
DefaultValue = defaultValue
};
}
protected void SetSettingValue<TValue>(string settingName, IEnumerable<NamedSetting>? settings, TValue defaultValue)
where TValue : struct
=> SetSettingValue(settingName, settings, string.Empty, defaultValue);

public override string ToString()
{
return Name;
Expand Down
10 changes: 9 additions & 1 deletion BrickController2/BrickController2/DeviceManagement/DeviceDTO.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
using SQLite;
using BrickController2.Settings;
using SQLite;
using SQLiteNetExtensions.Attributes;
using System;
using System.Collections.ObjectModel;

namespace BrickController2.DeviceManagement
{
Expand All @@ -13,5 +16,10 @@ internal class DeviceDTO
public string Name { get; set; } = string.Empty;
public string Address { get; set; } = string.Empty;
public byte[] DeviceData { get; set; } = Array.Empty<byte>();

[TextBlob(nameof(SettingsBlobed))]
public ObservableCollection<NamedSetting> Settings { get; set; } = [];

public string? SettingsBlobed { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
namespace BrickController2.DeviceManagement
using BrickController2.Settings;
using System.Collections.Generic;

namespace BrickController2.DeviceManagement
{
internal delegate Device DeviceFactory(DeviceType deviceType, string name, string address, byte[] deviceData);
internal delegate Device? DeviceFactory(DeviceType deviceType, string name, string address, byte[] deviceData, IEnumerable<NamedSetting> settings);
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public async Task LoadDevicesAsync()
var deviceDTOs = await _deviceRepository.GetDevicesAsync();
foreach (var deviceDTO in deviceDTOs)
{
var device = _deviceFactory(deviceDTO.DeviceType, deviceDTO.Name, deviceDTO.Address, deviceDTO.DeviceData);
var device = _deviceFactory(deviceDTO.DeviceType, deviceDTO.Name, deviceDTO.Address, deviceDTO.DeviceData, deviceDTO.Settings);
if (device != null)
{
Devices.Add(device);
Expand Down Expand Up @@ -97,10 +97,10 @@ async Task FoundDevice(DeviceType deviceType, string deviceName, string deviceAd
return;
}

var device = _deviceFactory(deviceType, deviceName, deviceAddress, deviceData);
var device = _deviceFactory(deviceType, deviceName, deviceAddress, deviceData, []);
if (device != null)
{
await _deviceRepository.InsertDeviceAsync(device.DeviceType, device.Name, device.Address, deviceData);
await _deviceRepository.InsertDeviceAsync(device.DeviceType, device.Name, device.Address, deviceData, device.CurrentSettings);

await _uiThreadService.RunOnMainThread(() => Devices.Add(device));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
using BrickController2.Database;
using BrickController2.Helpers;
using BrickController2.Settings;
using SQLite;
using SQLiteNetExtensionsAsync.Extensions;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace BrickController2.DeviceManagement
Expand Down Expand Up @@ -34,25 +37,32 @@ public async Task<IEnumerable<DeviceDTO>> GetDevicesAsync()
using (await _lock.LockAsync())
{
await InitAsync();
return await _databaseConnection.Table<DeviceDTO>().ToListAsync();
return await _databaseConnection.GetAllWithChildrenAsync<DeviceDTO>();
}
}

public async Task InsertDeviceAsync(DeviceType type, string name, string address, byte[] devicedata)
public async Task InsertDeviceAsync(DeviceType type, string name, string address, byte[] devicedata, IEnumerable<NamedSetting> settings)
{
using (await _lock.LockAsync())
{
var device = new DeviceDTO { DeviceType = type, Address = address, Name = name, DeviceData = devicedata };
var device = new DeviceDTO
{
DeviceType = type,
Address = address,
Name = name,
DeviceData = devicedata,
Settings = new(settings)
};
await InitAsync();
await _databaseConnection.InsertAsync(device);
await _databaseConnection.InsertWithChildrenAsync(device);
}
}

public async Task DeleteDeviceAsync(DeviceType type, string address)
{
using (await _lock.LockAsync())
{
var device = await _databaseConnection.Table<DeviceDTO>().Where(d => d.DeviceType == type && d.Address == address).FirstOrDefaultAsync();
var device = await GetDevice(type, address);
if (device != null)
{
await _databaseConnection.DeleteAsync(device);
Expand All @@ -72,13 +82,30 @@ public async Task UpdateDeviceAsync(DeviceType type, string address, string newN
{
using (await _lock.LockAsync())
{
var device = await _databaseConnection.Table<DeviceDTO>().Where(d => d.DeviceType == type && d.Address == address).FirstOrDefaultAsync();
var device = await GetDevice(type, address);
if (device != null)
{
device.Name = newName;
await _databaseConnection.UpdateAsync(device);
await _databaseConnection.UpdateWithChildrenAsync(device);
}
}
}

public async Task UpdateDeviceAsync(DeviceType type, string address, IEnumerable<NamedSetting> settings)
{
using (await _lock.LockAsync())
{
var device = await GetDevice(type, address);
if (device != null)
{
device.Settings = new(settings);
await _databaseConnection.UpdateWithChildrenAsync(device);
}
}
}

private async Task<DeviceDTO?> GetDevice(DeviceType type, string address)
=> (await _databaseConnection.GetAllWithChildrenAsync<DeviceDTO>(d => d.DeviceType == type && d.Address == address))
.FirstOrDefault();
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
using System.Collections.Generic;
using BrickController2.Settings;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace BrickController2.DeviceManagement
{
internal interface IDeviceRepository
{
Task<IEnumerable<DeviceDTO>> GetDevicesAsync();
Task InsertDeviceAsync(DeviceType type, string name, string address, byte[] manufacturerData);
Task InsertDeviceAsync(DeviceType type, string name, string address, byte[] manufacturerData, IEnumerable<NamedSetting> settings);
Task DeleteDeviceAsync(DeviceType type, string address);
Task DeleteDevicesAsync();
Task UpdateDeviceAsync(DeviceType type, string address, string newName);
Task UpdateDeviceAsync(DeviceType type, string address, IEnumerable<NamedSetting> settings);
}
}
Loading