diff --git a/src/App/Components/Pages/MeterDetail.razor b/src/App/Components/Pages/MeterDetail.razor
index 1d00847..ec33cfa 100644
--- a/src/App/Components/Pages/MeterDetail.razor
+++ b/src/App/Components/Pages/MeterDetail.razor
@@ -214,20 +214,30 @@ else
@(_sourceEdit.Id == 0 ? "New source" : "Edit source")
-
+
@foreach (var type in Enum.GetValues())
{
@type
}
- @if (_sourceEdit.SourceType is SourceType.HomeAssistant or SourceType.Mqtt or SourceType.Tasmota)
+ @if (RequiredEndpointType(_sourceEdit.SourceType) is { } needed)
{
-
- @foreach (var e in _endpoints)
- {
- @e.Name (@e.Type)
- }
-
+ if (ConnectorsFor(needed).Count == 0)
+ {
+
+ No @needed connector yet — create one
+ (set it up once; every source then just picks it).
+
+ }
+ else
+ {
+
+ @foreach (var e in ConnectorsFor(needed))
+ {
+ @e.Name
+ }
+
+ }
}
@if (_sourceEdit.SourceType == SourceType.HomeAssistant)
{
@@ -305,6 +315,7 @@ else
if (source is null)
{
_sourceEdit = new SourceEdit();
+ OnSourceTypeChanged(_sourceEdit.SourceType);
}
else
{
@@ -332,6 +343,28 @@ else
private async Task SaveSourceAsync()
{
+ // A live source without a matching connector has no connection details and would silently
+ // never ingest, so refuse it here rather than letting it look configured.
+ if (RequiredEndpointType(_sourceEdit.SourceType) is { } needed)
+ {
+ var selected = _endpoints.FirstOrDefault(e => e.Id == _sourceEdit.EndpointId);
+ if (selected is null)
+ {
+ Snackbar.Add($"Pick a {needed} connector for this {_sourceEdit.SourceType} source.", Severity.Error);
+ return;
+ }
+
+ if (selected.Type != needed)
+ {
+ Snackbar.Add($"'{selected.Name}' is a {selected.Type} connector; a {_sourceEdit.SourceType} source needs {needed}.", Severity.Error);
+ return;
+ }
+ }
+ else
+ {
+ _sourceEdit.EndpointId = null;
+ }
+
var config = new SourceConfig
{
EntityId = Trim(_sourceEdit.EntityId),
@@ -394,6 +427,44 @@ else
private static string? Trim(string? value) => string.IsNullOrWhiteSpace(value) ? null : value.Trim();
+ ///
+ /// Which connector kind a source type needs, or null if it needs none (manual/import/virtual).
+ /// Tasmota has no endpoint kind of its own — it is served by an MQTT broker connector.
+ ///
+ private static EndpointType? RequiredEndpointType(SourceType sourceType) => sourceType switch
+ {
+ SourceType.HomeAssistant => EndpointType.HomeAssistant,
+ SourceType.Mqtt or SourceType.Tasmota => EndpointType.MqttBroker,
+ _ => null,
+ };
+
+ private List ConnectorsFor(EndpointType type) =>
+ _endpoints.Where(e => e.Type == type).ToList();
+
+ // Changing the source type can invalidate the chosen connector (an HA connector cannot serve an
+ // MQTT source), so drop a selection that no longer fits rather than saving a mismatched pair.
+ private void OnSourceTypeChanged(SourceType sourceType)
+ {
+ _sourceEdit.SourceType = sourceType;
+
+ var needed = RequiredEndpointType(sourceType);
+ var selected = _endpoints.FirstOrDefault(e => e.Id == _sourceEdit.EndpointId);
+ if (needed is null || (selected is not null && selected.Type != needed))
+ {
+ _sourceEdit.EndpointId = null;
+ }
+
+ // Sole candidate: preselect it, so the common single-broker / single-HA setup is one click.
+ if (needed is not null && _sourceEdit.EndpointId is null)
+ {
+ var candidates = _endpoints.Where(e => e.Type == needed).ToList();
+ if (candidates.Count == 1)
+ {
+ _sourceEdit.EndpointId = candidates[0].Id;
+ }
+ }
+ }
+
private sealed class SourceEdit
{
public int Id { get; set; }