Skip to content
Merged
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
9 changes: 5 additions & 4 deletions src/Backend/VanDaemon.Core/Enums/ControlType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ namespace VanDaemon.Core.Enums;
/// </summary>
public enum ControlType
{
Toggle, // On/Off switch
Momentary, // Push button
Dimmer, // Variable control (0-100)
Selector // Multi-position selector
Toggle, // On/Off switch
Momentary, // Push button
Dimmer, // Variable control (0-100)
Selector, // Multi-position selector
ColorChooser // RGB color picker
}
31 changes: 24 additions & 7 deletions src/Frontend/VanDaemon.Web/Pages/Controls.razor
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

<PageTitle>Controls - VanDaemon</PageTitle>

<div style="padding: 16px;">

<style>
.controls-header {
margin-bottom: 24px;
Expand Down Expand Up @@ -144,19 +146,26 @@
Label="Control Type"
Required="true"
Class="mb-4">
<MudSelectItem Value="@("Toggle")">Toggle Switch</MudSelectItem>
<MudSelectItem Value="@("Dimmer")">Dimmer / Slider</MudSelectItem>
<MudSelectItem Value="@("Toggle")">On/Off Switch</MudSelectItem>
<MudSelectItem Value="@("Dimmer")">Dimmable / Slider</MudSelectItem>
<MudSelectItem Value="@("ColorChooser")">Color Chooser (RGB)</MudSelectItem>
<MudSelectItem Value="@("Momentary")">Momentary Button</MudSelectItem>
<MudSelectItem Value="@("Selector")">Selector</MudSelectItem>
</MudSelect>

<MudSelect @bind-Value="editingControl.IconName"
Label="Icon"
Class="mb-4">
<MudSelectItem Value="@("lightbulb")">Lightbulb</MudSelectItem>
<MudSelectItem Value="@("light_mode")">Light Mode</MudSelectItem>
<MudSelectItem Value="@("water_pump")">Water Pump</MudSelectItem>
<MudSelectItem Value="@("thermostat")">Thermostat</MudSelectItem>
<MudSelectItem Value="@("lightbulb")">💡 Lightbulb</MudSelectItem>
<MudSelectItem Value="@("light_mode")">☀️ Light Mode</MudSelectItem>
<MudSelectItem Value="@("water_drop")">💧 Water Pump</MudSelectItem>
<MudSelectItem Value="@("thermostat")">🌡️ Thermostat</MudSelectItem>
<MudSelectItem Value="@("power")">⚡ Power</MudSelectItem>
<MudSelectItem Value="@("fan")">🌀 Fan/Ventilation</MudSelectItem>
<MudSelectItem Value="@("heating")">🔥 Heating</MudSelectItem>
<MudSelectItem Value="@("air_conditioner")">❄️ Air Conditioner</MudSelectItem>
<MudSelectItem Value="@("outlet")">🔌 Outlet/Socket</MudSelectItem>
<MudSelectItem Value="@("nightlight")">🌙 Night Light</MudSelectItem>
</MudSelect>

<MudDivider Class="my-4" />
Expand Down Expand Up @@ -214,6 +223,8 @@
</DialogActions>
</MudDialog>

</div>

@code {
private List<ControlDto>? controls;
private bool dialogVisible = false;
Expand Down Expand Up @@ -292,7 +303,7 @@
editingControl.ControlConfiguration?.ContainsKey("GpioPin") == true)
{
providerType = "GPIO";
if (editingControl.ControlConfiguration.TryGetValue("GpioPin", out var pin))

Check warning on line 306 in src/Frontend/VanDaemon.Web/Pages/Controls.razor

View workflow job for this annotation

GitHub Actions / build

Dereference of a possibly null reference.

Check warning on line 306 in src/Frontend/VanDaemon.Web/Pages/Controls.razor

View workflow job for this annotation

GitHub Actions / build

Dereference of a possibly null reference.
gpioPin = Convert.ToInt32(pin);
if (editingControl.ControlConfiguration.TryGetValue("ActiveLow", out var activeLow))
gpioActiveLow = Convert.ToBoolean(activeLow);
Expand All @@ -301,7 +312,7 @@
editingControl.ControlConfiguration?.ContainsKey("ModbusAddress") == true)
{
providerType = "Modbus";
if (editingControl.ControlConfiguration.TryGetValue("ModbusAddress", out var addr))

Check warning on line 315 in src/Frontend/VanDaemon.Web/Pages/Controls.razor

View workflow job for this annotation

GitHub Actions / build

Dereference of a possibly null reference.
modbusAddress = addr?.ToString() ?? "";
if (editingControl.ControlConfiguration.TryGetValue("Register", out var reg))
modbusRegister = Convert.ToInt32(reg);
Expand Down Expand Up @@ -464,8 +475,14 @@
{
"lightbulb" => Icons.Material.Filled.Lightbulb,
"light_mode" => Icons.Material.Filled.LightMode,
"water_pump" => Icons.Material.Filled.WaterDrop,
"water_drop" => Icons.Material.Filled.WaterDrop,
"thermostat" => Icons.Material.Filled.Thermostat,
"power" => Icons.Material.Filled.Power,
"fan" => Icons.Material.Filled.Air,
"heating" => Icons.Material.Filled.LocalFireDepartment,
"air_conditioner" => Icons.Material.Filled.AcUnit,
"outlet" => Icons.Material.Filled.PowerSettingsNew,
"nightlight" => Icons.Material.Filled.Nightlight,
_ => Icons.Material.Filled.SettingsInputComponent
};
}
Expand Down
140 changes: 127 additions & 13 deletions src/Frontend/VanDaemon.Web/Pages/Index.razor
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
@page "/"
@using MudBlazor.Utilities
@inject HttpClient Http
@inject TelemetryService Telemetry
@inject ISnackbar Snackbar
Expand Down Expand Up @@ -121,14 +122,46 @@
}
else if (overlay.Type == OverlayType.Control)
{
<MudTooltip Text="@overlay.Name">
<div style="background: var(--mud-palette-surface); padding: 4px; border-radius: 8px; box-shadow: var(--mud-elevation-4);">
<MudSwitch Value="@overlay.IsOn"
Color="Color.Primary"
Disabled="@isEditMode"
ValueChanged="@((bool val) => OnControlToggle(overlay, val))" />
<div class="overlay-content" style="text-align: center; background: var(--mud-palette-surface); padding: 12px 16px; border-radius: 12px; box-shadow: var(--mud-elevation-6); min-width: 100px;">
<div style="display: flex; flex-direction: column; align-items: center; gap: 8px;">
<MudIcon Icon="@GetControlIcon(overlay.IconName)"
Size="Size.Large"
Style="font-size: 48px !important;"
Color="@(overlay.IsOn ? Color.Primary : Color.Default)" />
<MudText Typo="Typo.body2" Style="font-weight: 600; white-space: nowrap;">@overlay.Name</MudText>

@if (overlay.ControlType == "Toggle" && !isEditMode)
{
<MudSwitch Value="@overlay.IsOn"
Color="Color.Success"
Size="Size.Small"
ValueChanged="@((bool val) => OnControlToggle(overlay, val))" />
}
else if (overlay.ControlType == "Dimmer" && !isEditMode)
{
<div style="width: 120px;">
<MudSlider Value="@overlay.DimmerValue"
Color="Color.Primary"
Min="0"
Max="100"
Step="5"
Size="Size.Small"
ValueChanged="@((int val) => OnDimmerChange(overlay, val))" />
<MudText Typo="Typo.caption" Align="Align.Center">@overlay.DimmerValue%</MudText>
</div>
}
else if (overlay.ControlType == "ColorChooser" && !isEditMode)
{
<MudColorPicker Value="@overlay.ColorValue"
ColorPickerMode="ColorPickerMode.RGB"
DisableAlpha="true"
PickerVariant="PickerVariant.Inline"
Style="width: 120px;"
ColorPickerView="ColorPickerView.Palette"
ValueChanged="@((MudColor val) => OnColorChange(overlay, val))" />
}
</div>
</MudTooltip>
</div>
}
</div>
}
Expand Down Expand Up @@ -210,7 +243,7 @@
Snackbar.Add($"{name} turned {(Convert.ToBoolean(state) ? "ON" : "OFF")}", Severity.Info);
}

private async Task LoadSettings()

Check warning on line 246 in src/Frontend/VanDaemon.Web/Pages/Index.razor

View workflow job for this annotation

GitHub Actions / build

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.
{
try
{
Expand Down Expand Up @@ -271,11 +304,26 @@
foreach (var control in controls)
{
bool isOn = false;
if (control.State is bool boolState)
isOn = boolState;
else if (control.State is System.Text.Json.JsonElement jsonElement &&
jsonElement.ValueKind == System.Text.Json.JsonValueKind.True)
isOn = true;
int dimmerValue = 0;

if (control.Type == "Toggle" || control.Type == "Momentary")
{
if (control.State is bool boolState)
isOn = boolState;
else if (control.State is System.Text.Json.JsonElement jsonElement &&
jsonElement.ValueKind == System.Text.Json.JsonValueKind.True)
isOn = true;
}
else if (control.Type == "Dimmer")
{
if (control.State is int intState)
dimmerValue = intState;
else if (control.State is System.Text.Json.JsonElement jsonElement &&
jsonElement.ValueKind == System.Text.Json.JsonValueKind.Number)
dimmerValue = jsonElement.GetInt32();

isOn = dimmerValue > 0;
}

// Use saved position if available, otherwise use default
double x = 20 + (index * 15);
Expand All @@ -292,8 +340,12 @@
Id = control.Id,
Name = control.Name,
Type = OverlayType.Control,
Icon = "", // Not used for controls
Icon = GetControlIcon(control.IconName),
IconName = control.IconName,
ControlType = control.Type,
IsOn = isOn,
DimmerValue = dimmerValue,
ColorValue = new MudColor("#FFFFFF"),
X = x,
Y = y
});
Expand All @@ -314,6 +366,24 @@
};
}

private string GetControlIcon(string iconName)
{
return iconName switch
{
"lightbulb" => Icons.Material.Filled.Lightbulb,
"light_mode" => Icons.Material.Filled.LightMode,
"water_drop" => Icons.Material.Filled.WaterDrop,
"thermostat" => Icons.Material.Filled.Thermostat,
"power" => Icons.Material.Filled.Power,
"fan" => Icons.Material.Filled.Air,
"heating" => Icons.Material.Filled.LocalFireDepartment,
"air_conditioner" => Icons.Material.Filled.AcUnit,
"outlet" => Icons.Material.Filled.PowerSettingsNew,
"nightlight" => Icons.Material.Filled.Nightlight,
_ => Icons.Material.Filled.SettingsInputComponent
};
}

private void OnOverlayClick(OverlayItem overlay)
{
if (!isEditMode)
Expand Down Expand Up @@ -394,6 +464,46 @@
await InvokeAsync(StateHasChanged);
}

private async Task OnDimmerChange(OverlayItem overlay, int value)
{
try
{
var response = await Http.PostAsJsonAsync($"api/controls/{overlay.Id}/state", new { state = value });
if (response.IsSuccessStatusCode)
{
overlay.DimmerValue = value;
overlay.IsOn = value > 0;
}
}
catch (Exception ex)
{
Console.WriteLine($"Error controlling {overlay.Name}: {ex.Message}");
}

await InvokeAsync(StateHasChanged);
}

private async Task OnColorChange(OverlayItem overlay, MudColor value)
{
try
{
var colorHex = value.ToString();
var response = await Http.PostAsJsonAsync($"api/controls/{overlay.Id}/state", new { state = colorHex });
if (response.IsSuccessStatusCode)
{
overlay.ColorValue = value;
overlay.IsOn = true;
Snackbar.Add($"{overlay.Name} color set to {colorHex}", Severity.Success);
}
}
catch (Exception ex)
{
Console.WriteLine($"Error controlling {overlay.Name}: {ex.Message}");
}

await InvokeAsync(StateHasChanged);
}

private Color GetOverlayColor(OverlayItem overlay)
{
if (overlay.Type == OverlayType.Tank)
Expand Down Expand Up @@ -557,8 +667,12 @@
public string Name { get; set; } = string.Empty;
public OverlayType Type { get; set; }
public string Icon { get; set; } = string.Empty;
public string IconName { get; set; } = string.Empty;
public string ControlType { get; set; } = string.Empty;
public double Value { get; set; }
public bool IsOn { get; set; }
public int DimmerValue { get; set; }
public MudColor ColorValue { get; set; } = new MudColor("#FFFFFF");
public double X { get; set; } // Position as percentage
public double Y { get; set; } // Position as percentage
public string? ConfiguredAction { get; set; } // For controls
Expand Down
4 changes: 4 additions & 0 deletions src/Frontend/VanDaemon.Web/Pages/Settings.razor
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

<PageTitle>Settings - VanDaemon</PageTitle>

<div style="padding: 16px;">

<style>
.settings-header {
margin-bottom: 24px;
Expand Down Expand Up @@ -199,6 +201,8 @@ else
</MudGrid>
}

</div>

@code {
private SystemConfigurationDto? configuration;
private List<string>? availableDiagrams;
Expand Down
4 changes: 4 additions & 0 deletions src/Frontend/VanDaemon.Web/Pages/Tanks.razor
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

<PageTitle>Tanks - VanDaemon</PageTitle>

<div style="padding: 16px;">

<style>
.tanks-header {
margin-bottom: 24px;
Expand Down Expand Up @@ -234,6 +236,8 @@ else
</DialogActions>
</MudDialog>

</div>

@code {
private List<TankDto>? tanks;
private bool dialogVisible = false;
Expand Down
Loading