From 8892044e51f61f7cb5fa96a473ddcfe0836a7c27 Mon Sep 17 00:00:00 2001 From: David Date: Fri, 10 Jan 2025 20:52:29 +0100 Subject: [PATCH] dodana podpora za temperaturni senzor addon, pm1 dodan nlog pinganje devicov --- .../Classes/Pm1GetStatusResponse.cs | 30 +++ .../Classes/TemperatureGetStatusResponse.cs | 16 ++ ShellyExporter/Metrics.cs | 189 +++++++++++++++--- ShellyExporter/Program.cs | 16 +- ShellyExporter/ShellyExporter.csproj | 6 +- ShellyExporter/appsettings.json | 17 ++ ShellyExporter/nlog.config | 21 ++ 7 files changed, 259 insertions(+), 36 deletions(-) create mode 100644 ShellyExporter/Classes/Pm1GetStatusResponse.cs create mode 100644 ShellyExporter/Classes/TemperatureGetStatusResponse.cs create mode 100644 ShellyExporter/nlog.config diff --git a/ShellyExporter/Classes/Pm1GetStatusResponse.cs b/ShellyExporter/Classes/Pm1GetStatusResponse.cs new file mode 100644 index 0000000..0f97ae9 --- /dev/null +++ b/ShellyExporter/Classes/Pm1GetStatusResponse.cs @@ -0,0 +1,30 @@ +using Newtonsoft.Json; + +namespace ShellyExporter.Classes +{ + internal class Pm1GetStatusResponse + { + public class Energy + { + [JsonProperty("total")] + internal string? Total { get; set; } + [JsonProperty("minute_ts")] + internal string? MinuteTimeStamp { get; set; } + } + + [JsonProperty("id")] + internal string? Id { get; set; } + [JsonProperty("voltage")] + internal string? Voltage { get; set; } + [JsonProperty("current")] + internal string? Current { get; set; } + [JsonProperty("apower")] + internal string? Apower { get; set; } + [JsonProperty("freq")] + internal string? Freq { get; set; } + + [JsonProperty("aenergy")] + internal Energy? Aenergy { get; set; } + + } +} diff --git a/ShellyExporter/Classes/TemperatureGetStatusResponse.cs b/ShellyExporter/Classes/TemperatureGetStatusResponse.cs new file mode 100644 index 0000000..d0090fb --- /dev/null +++ b/ShellyExporter/Classes/TemperatureGetStatusResponse.cs @@ -0,0 +1,16 @@ +using Newtonsoft.Json; + +namespace ShellyExporter.Classes +{ + internal class TemperatureGetStatusResponse + { + [JsonProperty("id")] + internal string? Id { get; set; } + + [JsonProperty("tC")] + internal string? TemperatureC { get; set; } + + [JsonProperty("tF")] + internal string? TemperatureF { get; set; } + } +} diff --git a/ShellyExporter/Metrics.cs b/ShellyExporter/Metrics.cs index ff1758f..43af331 100644 --- a/ShellyExporter/Metrics.cs +++ b/ShellyExporter/Metrics.cs @@ -1,22 +1,30 @@ using System.Globalization; +using System.Net.NetworkInformation; using System.Text; using Newtonsoft.Json; +using NLog; namespace ShellyExporter { public class Metrics { + private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); + public enum Type { PlugS = 1, - Em3 = 2 + Em3 = 2, + TemperatureAddon = 3, + Pm1 = 4 } public enum Version { Gen1 = 1, - Gen2 = 2 + Gen2 = 2, + Gen3 = 3 } + public class Data { public string? Power { get; set; } @@ -54,41 +62,97 @@ namespace ShellyExporter } - public static async Task Get(string path, string name, Version shellyVersion, Type type) + public static async Task Get(string ip, string name, Version shellyVersion, Type type, string id) { var sb = new StringBuilder(); - - Data data; - switch (type) + var online = true; + var error = false; + try { - case Type.PlugS: - switch (shellyVersion) - { - case Version.Gen1: - data = await GetPlugSGen1(path); - break; - case Version.Gen2: - data = await GetPlugSGen2(path); - break; - default: - throw new ArgumentOutOfRangeException(nameof(shellyVersion), shellyVersion, null); - } - break; - case Type.Em3: - switch (shellyVersion) - { - case Version.Gen1: - data = await Get3EmGen1(path); - break; - default: - throw new ArgumentOutOfRangeException(); - } - break; - default: - throw new ArgumentOutOfRangeException(nameof(type), type, null); + using var pinger = new Ping(); + var reply = pinger.Send(ip, TimeSpan.FromSeconds(2), null, null); + if (reply.Status != IPStatus.Success) + { + Logger.Error($"PingDevice:Status {reply.Status}"); + online = false; + } + } + catch (PingException e) + { + Logger.Error(e, "PingDevice"); + online = false; + error = true; } - + var path = $"http://{ip}"; + + Data data = null; + if (online) + { + try + { + switch (type) + { + case Type.PlugS: + switch (shellyVersion) + { + case Version.Gen1: + data = await GetPlugSGen1(path); + break; + case Version.Gen2: + data = await GetPlugSGen2(path); + break; + default: + throw new ArgumentOutOfRangeException(nameof(shellyVersion), shellyVersion, null); + } + break; + case Type.Em3: + switch (shellyVersion) + { + case Version.Gen1: + data = await Get3EmGen1(path); + break; + default: + throw new ArgumentOutOfRangeException(); + } + break; + case Type.TemperatureAddon: + switch (shellyVersion) + { + case Version.Gen2: + data = await GetTemperatureAddonGen2(path, id); + break; + default: + throw new ArgumentOutOfRangeException(); + } + break; + case Type.Pm1: + switch (shellyVersion) + { + case Version.Gen3: + data = await GetPm1Gen3(path); + break; + default: + throw new ArgumentOutOfRangeException(); + } + break; + default: + throw new ArgumentOutOfRangeException(nameof(type), type, null); + } + } + catch (Exception e) + { + Logger.Error(e, "GetDataFromDevice"); + error = true; + } + } + + sb.AppendLine($"device_online{{item=\"{name}\"}} {(online ? 1 : 0)}"); + sb.AppendLine($"device_error_retrieving_data{{item=\"{name}\"}} {(error ? 1 : 0)}"); + if (!online || error) + return sb; + + //Prestavljeno na sestevek vseh faz if (data.Power != null) sb.AppendLine($"meters_power_wats{{item=\"{name}\"}} {data.Power}"); if (data.Overpower != null) @@ -151,6 +215,18 @@ namespace ShellyExporter if (data.Emeters3TotalReturned != null) sb.AppendLine($"emeters_3_total_returned{{item=\"{name}\"}} " + data.Emeters3TotalReturned); + //Ce gre za 3PM + if (data.Power != null + && data is { Emeters1Power: not null, Emeters2Power: not null, Emeters3Power: not null }) + { + var emetersPowerTotal = 0d; + double.TryParse(data.Emeters1Power, CultureInfo.InvariantCulture, out var emeters1Power); + double.TryParse(data.Emeters2Power, CultureInfo.InvariantCulture, out var emeters2Power); + double.TryParse(data.Emeters3Power, CultureInfo.InvariantCulture, out var emeters3Power); + + sb.AppendLine($"meters_power_wats{{item=\"{name}\"}} {(emeters1Power + emeters2Power + emeters3Power)}"); + } + return sb; } @@ -269,5 +345,54 @@ namespace ShellyExporter return data; } + + private static async Task GetTemperatureAddonGen2(string path, string id) + { + var data = new Data(); + var client = new HttpClient(); + var response = await client.GetStringAsync(path + $"/rpc/Temperature.GetStatus?id={id}"); + + var responseObject = JsonConvert.DeserializeObject(response); + + if (responseObject == null) + return data; + + if (responseObject.TemperatureC != null) + data.Temperature = responseObject.TemperatureC; + + return data; + } + + private static async Task GetPm1Gen3(string path) + { + var data = new Data(); + var client = new HttpClient(); + var response = await client.GetStringAsync(path + "/rpc/PM1.GetStatus?id=0"); + + var responseObject = JsonConvert.DeserializeObject(response); + + if (responseObject == null) + return data; + + if (responseObject.Apower != null) + data.Power = responseObject.Apower; + + if (responseObject.Current != null) + data.Current = responseObject.Current; + + if (responseObject.Voltage != null) + data.Voltage = responseObject.Voltage; + + if (responseObject.Aenergy != null) + { + if (responseObject.Aenergy.Total != null) + data.Total = (Convert.ToDecimal(responseObject.Aenergy.Total) * (decimal)60).ToString(CultureInfo.InvariantCulture); + + if (responseObject.Aenergy.MinuteTimeStamp!= null) + data.Timestamp = responseObject.Aenergy.MinuteTimeStamp; + } + + return data; + } } } diff --git a/ShellyExporter/Program.cs b/ShellyExporter/Program.cs index d6c2975..1989ffb 100644 --- a/ShellyExporter/Program.cs +++ b/ShellyExporter/Program.cs @@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.Extensions.Hosting; using ShellyExporter; using System.Xml.Linq; +using NLog.Web; var builder = WebApplication.CreateBuilder(args); @@ -19,6 +20,12 @@ if (!builder.Environment.IsDevelopment()) builder.WebHost.UseUrls(builder.Configuration["Kst:Url"]); } +//Nlog +builder.Logging.ClearProviders(); +builder.Logging.SetMinimumLevel(LogLevel.Trace); +builder.Host.UseNLog(); + + var app = builder.Build(); // Configure the HTTP request pipeline. @@ -39,10 +46,15 @@ app.Map("/metrics", () => foreach (var host in builder.Configuration.GetSection("Hosts").GetChildren()) { - sb.Append(Metrics.Get($"http://{host["ip"]}", + var id = "0"; + if (host["id"] != "") + id = host["id"]; + + sb.Append(Metrics.Get(host["ip"], host.Path.Replace("Hosts:", ""), (Metrics.Version)Convert.ToInt16(host["version"]), - (Metrics.Type)Convert.ToInt16(host["type"])).Result); + (Metrics.Type)Convert.ToInt16(host["type"]), + id).Result); } return sb.ToString(); diff --git a/ShellyExporter/ShellyExporter.csproj b/ShellyExporter/ShellyExporter.csproj index 2468209..12edcef 100644 --- a/ShellyExporter/ShellyExporter.csproj +++ b/ShellyExporter/ShellyExporter.csproj @@ -10,9 +10,11 @@ - + - + + + diff --git a/ShellyExporter/appsettings.json b/ShellyExporter/appsettings.json index 347ccad..a386e6f 100644 --- a/ShellyExporter/appsettings.json +++ b/ShellyExporter/appsettings.json @@ -24,6 +24,23 @@ "ip": "192.168.1.64", "type": 2, "version": 1 + }, + "temperatura-zunaj": { + "ip": "192.168.1.75", + "type": 3, + "version": 2, + "id": "100" + }, + "temperatura-notraj": { + "ip": "192.168.1.75", + "type": 3, + "version": 2, + "id": "101" + }, + "tv-dnevna": { + "ip": "192.168.1.67", + "type": 4, + "version": 3 } } diff --git a/ShellyExporter/nlog.config b/ShellyExporter/nlog.config new file mode 100644 index 0000000..3a1797a --- /dev/null +++ b/ShellyExporter/nlog.config @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file