Skip to content

Commit aa502ff

Browse files
#11 - Added ability to customize both ID, and Name for measurements / sensors.
1 parent f40d908 commit aa502ff

File tree

8 files changed

+119
-33
lines changed

8 files changed

+119
-33
lines changed

rPDU2MQTT/Classes/PDU.cs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,9 @@ private void processChildDevice<T>(Dictionary<string, T> entities) where T : Nam
7171
{
7272
if (entity is Outlet o)
7373
{
74-
entity.Entity_Name = o.GetOverrideOrDefault(key, config.Overrides.OutletID, FormatName: true);
75-
entity.Entity_DisplayName = o.GetOverrideOrDefault(key, config.Overrides.OutletName, FormatName: false);
74+
int k = int.TryParse(key, out int s) ? s + 1 : 0; //Note- the plus one, is so the number aligns with what is seen on the GUI.
75+
entity.Entity_Name = o.GetOverrideOrDefault(k, config.Overrides.OutletID, FormatName: true);
76+
entity.Entity_DisplayName = o.GetOverrideOrDefault(k, config.Overrides.OutletName, FormatName: false);
7677
}
7778
else
7879
{
@@ -94,8 +95,10 @@ private void processMeasurements<T>(Dictionary<string, T> measurements) where T
9495
{
9596
// We want to override the default key here- to give a nice, readable key.
9697
entity.Record_Key = entity.Type;
97-
entity.Entity_Name = entity.GetEntityName();
98-
entity.Entity_DisplayName = entity.Type;
98+
99+
var suffix = entity.GetOverrideOrDefault(entity.Type, config.Overrides.MeasurementID, entity.Type, true);
100+
entity.Entity_Name = entity.GetEntityName(suffix);
101+
entity.Entity_DisplayName = entity.GetOverrideOrDefault(entity.Type, config.Overrides.MeasurementName, entity.Type, false);
99102
}
100103
}
101104
}

rPDU2MQTT/Extensions/EntityWithName_Overrides.cs

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ public static class EntityWithName_Overrides
1616
/// <param name="entity"></param>
1717
/// <param name="Key"></param>
1818
/// <param name="Overrides"></param>
19-
public static string GetOverrideOrDefault<T>(this T entity, string key, Dictionary<int, string> Overrides, string Default = null, bool FormatName = false)
19+
public static string GetOverrideOrDefault<T, Key>(this T entity, Key? key, Dictionary<Key, string> Overrides, string Default = null, bool FormatName = false)
20+
where Key : notnull
2021
where T : NamedEntity
2122
{
2223
string formatIfNeeded(string input) => FormatName switch
@@ -25,11 +26,8 @@ public static string GetOverrideOrDefault<T>(this T entity, string key, Dictiona
2526
false => input
2627
};
2728

28-
if (int.TryParse(key, out int num) && Overrides.ContainsKey(num + 1))
29-
{
30-
var value = Overrides[num + 1];
31-
return formatIfNeeded(value);
32-
}
29+
if (tryGetValue(Overrides, key, out string val))
30+
return formatIfNeeded(val);
3331

3432
if (Default is not null)
3533
return formatIfNeeded(Default);
@@ -40,4 +38,48 @@ public static string GetOverrideOrDefault<T>(this T entity, string key, Dictiona
4038
else
4139
throw new Exception("Unable to determine suitable name.");
4240
}
41+
42+
/// <summary>
43+
/// Multi-type lookup. Does case-insensitive compare for strings.
44+
/// </summary>
45+
/// <typeparam name="T"></typeparam>
46+
/// <param name="dictionary"></param>
47+
/// <param name="key"></param>
48+
/// <param name="Result"></param>
49+
/// <returns></returns>
50+
private static bool tryGetValue<T>(this Dictionary<T, string> dictionary, T? key, out string Result)
51+
where T : notnull
52+
{
53+
if (key is null)
54+
{
55+
Result = string.Empty;
56+
return false;
57+
}
58+
if (key is string sKey && dictionary is Dictionary<string, string> stringDictionary)
59+
return tryGetStringValue(stringDictionary, sKey, out Result);
60+
61+
if (dictionary.ContainsKey(key))
62+
{
63+
Result = dictionary[key];
64+
return true;
65+
}
66+
67+
Result = string.Empty;
68+
return false;
69+
}
70+
71+
72+
private static bool tryGetStringValue(this Dictionary<string, string> Dictionary, string Key, out string Result)
73+
{
74+
var match = Dictionary.Keys.FirstOrDefault(o => string.Equals(o, Key, StringComparison.OrdinalIgnoreCase));
75+
if (match is null)
76+
{
77+
Result = string.Empty;
78+
return false;
79+
}
80+
81+
Result = Dictionary[match];
82+
return !string.IsNullOrWhiteSpace(Result);
83+
}
84+
4385
}

rPDU2MQTT/Extensions/IMQTTKeyExtensions.cs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,26 +59,29 @@ public static string CreateChildIdentifier(this IMQTTKey Device, string ChildIde
5959
/// </summary>
6060
/// <param name="Device"></param>
6161
/// <returns></returns>
62-
public static string GetEntityName(this IMQTTKey Device)
62+
public static string GetEntityName(this IMQTTKey Device, string Suffix)
6363
{
6464
string getObjectID(IMQTTKey? cur)
6565
{
6666
//If- the current device is null- just return the unique identifier.
6767
if (cur is null)
68-
return Device.Entity_Identifier;
68+
return $"{Device.Entity_Identifier}_{Suffix}";
6969

7070
//If- this is a dummy entity used for organization, skip it, and recurse to the parent.
7171
if (cur is DummyEntity && cur.Record_Parent is not null)
7272
return getObjectID(cur.Record_Parent);
7373

7474
if (cur is NamedEntityWithMeasurements cd)
75-
return $"{cd.Entity_Name}_{Device.Record_Key}";
75+
return $"{cd.Entity_Name}_{Suffix}";
7676

7777
//Recurse up another level.
7878
return getObjectID(cur.Record_Parent);
7979
}
8080

81-
return getObjectID(Device);
81+
var result = getObjectID(Device);
82+
83+
84+
return result.FormatName();
8285
}
8386

8487
/// <summary>

rPDU2MQTT/Helpers/MeasurementHelper.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,12 @@ public static class MeasurementHelper
1717
{
1818
"currentcrestfactor" => null,
1919
"balance" => null,
20-
"apparentpower" => new SensorDTO(StateClass.Measurement, DeviceClass.ApparentPower, "apparentPower"),
21-
"realpower" => new SensorDTO(StateClass.Measurement, DeviceClass.Power, "power"),
22-
"energy" => new SensorDTO(StateClass.TotalIncreasing, DeviceClass.Energy, "energy"),
23-
"powerfactor" => new SensorDTO(StateClass.Measurement, DeviceClass.PowerFactor, "powerFactor"),
24-
"current" => new SensorDTO(StateClass.Measurement, DeviceClass.Current, "current"),
25-
"voltage" => new SensorDTO(StateClass.Measurement, DeviceClass.Voltage, "voltage"),
20+
"apparentpower" => new SensorDTO(StateClass.Measurement, DeviceClass.ApparentPower),
21+
"realpower" => new SensorDTO(StateClass.Measurement, DeviceClass.Power),
22+
"energy" => new SensorDTO(StateClass.TotalIncreasing, DeviceClass.Energy),
23+
"powerfactor" => new SensorDTO(StateClass.Measurement, DeviceClass.PowerFactor),
24+
"current" => new SensorDTO(StateClass.Measurement, DeviceClass.Current),
25+
"voltage" => new SensorDTO(StateClass.Measurement, DeviceClass.Voltage),
2626
_ => null
2727
};
2828
}

rPDU2MQTT/Models/Config/Overrides.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,14 @@ public class Overrides
2727
/// This maps to <see cref="Models.HomeAssistant.baseClasses.baseEntity.DisplayName"/>, ie, "name"
2828
/// </remarks>
2929
public Dictionary<int, string> OutletName { get; set; } = new();
30+
31+
/// <summary>
32+
/// Allows overriding the generated Entity ID for measurements.
33+
/// </summary>
34+
public Dictionary<string, string> MeasurementID { get; set; } = new();
35+
36+
/// <summary>
37+
/// Allows overriding the generated Entity Name for measurements.
38+
/// </summary>
39+
public Dictionary<string, string> MeasurementName { get; set; } = new();
3040
}

rPDU2MQTT/Models/HomeAssistant/ObjectDTOs/SensorDTO.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ namespace rPDU2MQTT.Models.HomeAssistant.ObjectDTOs;
55

66
public record EntityDTO(string EntitySuffix);
77

8-
public record SensorDTO(StateClass StateClass, DeviceClass SensorClass, string EntitySuffix);
8+
public record SensorDTO(StateClass StateClass, DeviceClass SensorClass);

rPDU2MQTT/Services/HomeAssistantDiscoveryService.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ protected override async Task Execute(CancellationToken cancellationToken)
3737
{
3838
//If we are unable to parse this measurement as valid, skip to the next.
3939
var dto = measurement.TryParseValue();
40-
if (dto is null || dto.EntitySuffix is null)
40+
if (dto is null)
4141
continue;
4242

4343
Sensors.Add(CreateSensorDiscovery(measurement, ParentDevice, dto));
@@ -55,13 +55,12 @@ protected override async Task Execute(CancellationToken cancellationToken)
5555
{
5656
//If we are unable to parse this measurement as valid, skip to the next.
5757
var dto = measurement.TryParseValue();
58-
if (dto is null || dto.EntitySuffix is null)
58+
if (dto is null)
5959
continue;
6060

6161
Sensors.Add(CreateSensorDiscovery(measurement, childDevice, dto));
6262
}
6363
}
64-
6564
}
6665

6766
log.LogInformation("Publishing discovery messages");

rPDU2MQTT/appsettings.json

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@
2525
"PollInterval": 5
2626
},
2727
// This section allows overriding the generated entity_id, and names for various objects.
28-
// Note- ID / entity_id, is only factored when the initial discovery runs.
29-
// After created- Home assistant will not update it.
30-
// However, Display Name / Name can be updated after creation.
28+
// Note- ID / entity_id, will only be set when the entity is created.
29+
// Do not touch or change it throug here after it has been created! Otherwise, you can end up with duplicates, or other issues.
30+
// "Name" can be freely updated, and home assistant will reflect updated names instantly (after the discovery runs)
3131
"Overrides": {
3232
// Allows overriding the generated identifier for the PDU. Leave blank to disable.
3333
// Warning- Do not change after creation! This is the root, unique identifier used, and should not change!
@@ -43,17 +43,46 @@
4343
// By default, if not specified, an entity ID will be automatically generated from the labels configured via the PDU.
4444
// Note- don't change this after initial discovery. Will cause problems.
4545
"OutletID": {
46-
"1": "kube02",
47-
"2": "dell_md1220",
48-
"3": "outlet_3"
46+
//"1": "kube02",
47+
//"2": "dell_md1220",
48+
//"3": "outlet_3"
49+
//..
4950
},
5051
// The "name" can be overridden, and customized for each outlet.
5152
// This, represents the "display name", which is the human-readable, pretty version.
5253
// By default, if not specified, this will be automatically generated from the labels configured via the PDU.
5354
"OutletName": {
54-
"1": "Proxmox: Kube02",
55-
"2": "Dell: MD1220",
56-
"3": "Outlet 3"
55+
//"1": "Proxmox: Kube02",
56+
//"2": "Dell: MD1220",
57+
//"3": "Outlet 3 Friendly Name"
58+
//..
59+
},
60+
61+
// This allows customizing the entity IDs generated for metrics.
62+
// All measurement entity_ids will start with the generated ID for the device they belong to.
63+
// ie, {device_name}_power, device_energy
64+
// Output Format will be {EntityID}_{MeasurementID}
65+
// Both measurement ID, and Name, is based on the data type presented from the PDU.
66+
// This will affect ALL devices and entities, using measurements.
67+
// Note- changing the ID only customizes the suffix.
68+
"MeasurementID": {
69+
"apparentpower": null,
70+
"realpower": "power",
71+
"energy": null,
72+
"powerFactor": null,
73+
"current": null,
74+
"voltage": null
75+
},
76+
77+
//Customize the names for measurements.
78+
//Output Format will be {Entity Name} {Measurement Name}
79+
"MeasurementName": {
80+
"apparentpower": "Apparent Power",
81+
"realpower": "Power",
82+
"energy": "Energy",
83+
"powerFactor": "Power Factor",
84+
"current": "Current",
85+
"voltage": "Voltage"
5786
}
5887
},
5988
"Actions": {

0 commit comments

Comments
 (0)