diff --git a/TeslaLogger/ElectricityMeterBase.cs b/TeslaLogger/ElectricityMeterBase.cs index f97762194..c9ccd36ef 100644 --- a/TeslaLogger/ElectricityMeterBase.cs +++ b/TeslaLogger/ElectricityMeterBase.cs @@ -37,6 +37,8 @@ public static ElectricityMeterBase Instance(string type, string host, string par { if (type == "openwb") return new ElectricityMeterOpenWB(host, paramater); + else if (type == "openwb2") + return new ElectricityMeterOpenWB2(host, paramater); else if (type == "cfos") return new ElectricityMeterCFos(host, paramater); else if (type == "go-e") diff --git a/TeslaLogger/ElectricityMeterOpenWB2.cs b/TeslaLogger/ElectricityMeterOpenWB2.cs new file mode 100644 index 000000000..3bec2d7be --- /dev/null +++ b/TeslaLogger/ElectricityMeterOpenWB2.cs @@ -0,0 +1,340 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Runtime.Caching; +using Exceptionless; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace TeslaLogger +{ + [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1031:Keine allgemeinen Ausnahmetypen abfangen", Justification = "")] + class ElectricityMeterOpenWB2 : ElectricityMeterBase + { + string host; + string parameter; + string chargepointid; + string gridmeterid; + internal string api_state; + + internal string mockup_version; + internal string mockup_charge_state; + internal string mockup_charge_point; + internal string mockup_grid; + internal string mockup_hierarchy; + + Guid guid; // defaults to new Guid(); + static WebClient client; + + public ElectricityMeterOpenWB2(string host, string parameter) + { + if (client == null) + { + client = new WebClient(); + } + + this.host = host; + this.parameter = parameter; + + + /* + Example parameters: + "": grid id will be assumed from hierarchy, first charge point id will be taken from hierarchy + "CP:26": chargepoind id = 26, grid id will be assumed from hierarchy + "G:7|CP:27": Grid meter with id = 7 and chargepoind id = 26 + */ + + var args = parameter.Split('|'); + foreach (var p in args) + { + if (p.StartsWith("CP", StringComparison.InvariantCultureIgnoreCase)) + { + string[] parts = p.Split(':'); + if (!string.IsNullOrEmpty(parts[1])) + chargepointid = parts[1]; + } + if (p.StartsWith("G", StringComparison.InvariantCultureIgnoreCase)) + { + string[] parts = p.Split(':'); + if (!string.IsNullOrEmpty(parts[1])) + gridmeterid = parts[1]; + } + } + } + + string GetCurrentData(string topic) + { + try + { + string cacheKey = "owb2_" + topic + guid.ToString(); + object o = MemoryCache.Default.Get(cacheKey); + + if (o != null) + return (string)o; + + string url = host + "/v1/?topic=" + topic; + string lastJSON = client.DownloadString(url); + + MemoryCache.Default.Add(cacheKey, lastJSON, DateTime.Now.AddSeconds(10)); + return lastJSON; + } + catch (Exception ex) + { + if (ex is WebException wx) + { + if ((wx.Response as HttpWebResponse)?.StatusCode == HttpStatusCode.NotFound) + { + Logfile.Log(wx.Message); + return ""; + } + + } + if (!WebHelper.FilterNetworkoutage(ex)) + ex.ToExceptionless().FirstCarUserID().Submit(); + + Logfile.Log(ex.ToString()); + } + + return ""; + } + + public override double? GetUtilityMeterReading_kWh() + { + string h = null; + string j = null; + try + { + if (string.IsNullOrEmpty(gridmeterid)) + { + //get grid meter id via hierarchy: + if (mockup_hierarchy != null) + { + h = mockup_hierarchy; + } + else + { + h = GetCurrentData("openWB/counter/get/hierarchy"); + } + + JArray jsonArray = JArray.Parse(h); + JToken firstCounter = jsonArray + .FirstOrDefault(item => (string)item["type"] == "counter"); + + if (string.IsNullOrEmpty(firstCounter["id"].ToString())) + return null; + + gridmeterid = firstCounter["id"].ToString(); + } + + if (mockup_grid != null) + { + j = mockup_grid; + } + else + { + j = GetCurrentData("openWB/counter/" + gridmeterid + "/get/imported"); + } + + dynamic jsonResult = JsonConvert.DeserializeObject(j); + if (jsonResult == null) + return null; + + if (!Tools.IsPropertyExist(jsonResult, "status")) + return null; + + Dictionary r1 = jsonResult.ToObject>(); + + r1.TryGetValue("status", out object status); + if (status.ToString() != "success") + return null; + + if (r1.ContainsKey("message")) + { + double.TryParse(r1["message"].ToString(), out double value); + return value; + } + else + { + return double.NaN; + } + + + } + catch (Exception ex) + { + ex.ToExceptionless().FirstCarUserID().Submit(); + Logfile.ExceptionWriter(ex, j); + } + + return null; + } + + public override double? GetVehicleMeterReading_kWh() + { + string h = null; + string j = null; + try + { + if (string.IsNullOrEmpty(chargepointid)) + { + //no charge point id provided, get first charge point id via hierarchy: + if (mockup_hierarchy != null) + { + h = mockup_hierarchy; + } + else + { + h = GetCurrentData("openWB/counter/get/hierarchy"); + } + + JArray jsonArray = JArray.Parse(h); + JObject firstItem = (JObject)jsonArray[0]; + JArray childrenArray = (JArray)firstItem["children"]; + JToken cpEntry = childrenArray + .Where(child => (string)child["type"] == "counter" && child["children"] != null) + .SelectMany(child => child["children"]) + .Where(grandchild => (string)grandchild["type"] == "cp") + .FirstOrDefault(); + + if (cpEntry == null) + { + return null; + } + + chargepointid = cpEntry["id"].ToString(); + } + + if (mockup_charge_point != null) + { + j = mockup_charge_point; + } + else + { + j = GetCurrentData("openWB/chargepoint/" + chargepointid + "/get/imported"); + } + + dynamic jsonResult = JsonConvert.DeserializeObject(j); + if (jsonResult == null) + return null; + + if (!Tools.IsPropertyExist(jsonResult, "status")) + return null; + + Dictionary r1 = jsonResult.ToObject>(); + + r1.TryGetValue("status", out object status); + if (status.ToString() != "success") + return null; + + if (r1.ContainsKey("message")) + { + double.TryParse(r1["message"].ToString(), out double value); + return value; + } + else + { + return double.NaN; + } + + } + catch (Exception ex) + { + ex.ToExceptionless().FirstCarUserID().Submit(); + Logfile.ExceptionWriter(ex, j); + } + + return null; + } + + public override bool? IsCharging() + { + string j = null; + try + { + if (mockup_charge_state != null) + { + j = mockup_charge_state; + } + else + { + j = GetCurrentData("openWB/chargepoint/" + chargepointid + "/get/charge_state"); + } + + dynamic jsonResult = JsonConvert.DeserializeObject(j); + if (jsonResult == null) + return null; + + if (!Tools.IsPropertyExist(jsonResult, "status")) + return null; + + Dictionary r1 = jsonResult.ToObject>(); + + r1.TryGetValue("status", out object status); + if (status.ToString() != "success") + return null; + + r1.TryGetValue("message", out object version); + if (version.ToString() == "true") + { + return true; + } + else + { + return false; + } + + } + catch (Exception ex) + { + ex.ToExceptionless().FirstCarUserID().Submit(); + Logfile.ExceptionWriter(ex, j); + } + + return null; + } + + public override string GetVersion() + { + string j = null; + try + { + if (mockup_version != null) + { + j = mockup_version; + } + else + { + j = GetCurrentData("openWB/system/version"); + } + + dynamic jsonResult = JsonConvert.DeserializeObject(j); + if (jsonResult == null) + return null; + + if (!Tools.IsPropertyExist(jsonResult, "status")) + return null; + + Dictionary r1 = jsonResult.ToObject>(); + + r1.TryGetValue("status", out object status); + if (status.ToString() != "success") + return null; + + r1.TryGetValue("message", out object version); + return version.ToString(); + } + catch (Exception ex) + { + if (!WebHelper.FilterNetworkoutage(ex)) + ex.ToExceptionless().FirstCarUserID().Submit(); + + Logfile.ExceptionWriter(ex, j); + } + + return ""; + } + + } + +} diff --git a/TeslaLogger/TeslaLogger.csproj b/TeslaLogger/TeslaLogger.csproj index 311f0f25d..c677eca10 100644 --- a/TeslaLogger/TeslaLogger.csproj +++ b/TeslaLogger/TeslaLogger.csproj @@ -148,6 +148,7 @@ + diff --git a/TeslaLogger/www/admin/wallbox.php b/TeslaLogger/www/admin/wallbox.php index 212715f25..69d96b858 100644 --- a/TeslaLogger/www/admin/wallbox.php +++ b/TeslaLogger/www/admin/wallbox.php @@ -119,6 +119,7 @@ function btn_save_click() + diff --git a/UnitTestsTeslalogger/UnitTestWallbox.cs b/UnitTestsTeslalogger/UnitTestWallbox.cs index 82099aac2..3a293e86d 100644 --- a/UnitTestsTeslalogger/UnitTestWallbox.cs +++ b/UnitTestsTeslalogger/UnitTestWallbox.cs @@ -50,6 +50,30 @@ public void OpenWBMeterConstructor() Console.WriteLine(ret); } + [TestMethod] + public void OpenWB2() + { + var v = new ElectricityMeterOpenWB2("", ""); + + v.mockup_version = System.IO.File.ReadAllText(@"..\..\testdata\openwb2_version.txt"); + v.mockup_charge_state = System.IO.File.ReadAllText(@"..\..\testdata\openwb2_charge_state.txt"); + v.mockup_charge_point = System.IO.File.ReadAllText(@"..\..\testdata\openwb2_cp.txt"); + v.mockup_grid = System.IO.File.ReadAllText(@"..\..\testdata\openwb2_grid.txt"); + v.mockup_hierarchy = System.IO.File.ReadAllText(@"..\..\testdata\openwb2_hierarchy.txt"); + + double? kwh = v.GetVehicleMeterReading_kWh(); + var charging = v.IsCharging(); + var utility_meter_kwh = v.GetUtilityMeterReading_kWh(); + var version = v.GetVersion(); + string ret = v.ToString(); + Console.WriteLine(ret); + Assert.AreEqual(441759, kwh); + Assert.AreEqual(false, charging); + Assert.AreEqual(123456789, utility_meter_kwh); + Assert.AreEqual("2.1.7-Beta.2", version); + } + + [TestMethod] public void GoEMeter() { diff --git a/UnitTestsTeslalogger/testdata/openwb2_charge_state.txt b/UnitTestsTeslalogger/testdata/openwb2_charge_state.txt new file mode 100644 index 000000000..bf6d80d4c --- /dev/null +++ b/UnitTestsTeslalogger/testdata/openwb2_charge_state.txt @@ -0,0 +1,5 @@ +{ + "status": "success", + "topic": "openWB/chargepoint/27/get/charge_state", + "message": false +} \ No newline at end of file diff --git a/UnitTestsTeslalogger/testdata/openwb2_cp.txt b/UnitTestsTeslalogger/testdata/openwb2_cp.txt new file mode 100644 index 000000000..4ddd7ab5f --- /dev/null +++ b/UnitTestsTeslalogger/testdata/openwb2_cp.txt @@ -0,0 +1,5 @@ +{ + "status": "success", + "topic": "openWB/chargepoint/27/get/imported", + "message": 441759 +} \ No newline at end of file diff --git a/UnitTestsTeslalogger/testdata/openwb2_grid.txt b/UnitTestsTeslalogger/testdata/openwb2_grid.txt new file mode 100644 index 000000000..6036443c0 --- /dev/null +++ b/UnitTestsTeslalogger/testdata/openwb2_grid.txt @@ -0,0 +1,5 @@ +{ + "status": "success", + "topic": "openWB/counter/7/get/imported", + "message": 123456789 +} \ No newline at end of file diff --git a/UnitTestsTeslalogger/testdata/openwb2_hierarchy.txt b/UnitTestsTeslalogger/testdata/openwb2_hierarchy.txt new file mode 100644 index 000000000..c22212182 --- /dev/null +++ b/UnitTestsTeslalogger/testdata/openwb2_hierarchy.txt @@ -0,0 +1,60 @@ +[ + { + "id": 7, + "type": "counter", + "children": [ + { + "id": 23, + "type": "inverter", + "children": [] + }, + { + "id": 22, + "type": "inverter", + "children": [] + }, + { + "id": 14, + "type": "counter", + "children": [] + }, + { + "id": 24, + "type": "inverter", + "children": [ + { + "id": 25, + "type": "bat", + "children": [] + } + ] + }, + { + "id": 28, + "type": "counter", + "children": [ + { + "id": 26, + "type": "cp", + "children": [] + }, + { + "id": 27, + "type": "cp", + "children": [] + } + ] + }, + { + "id": 34, + "type": "counter", + "children": [] + }, + { + "id": 35, + "type": "counter", + "children": [] + } + ] + } +] \ No newline at end of file diff --git a/UnitTestsTeslalogger/testdata/openwb2_version.txt b/UnitTestsTeslalogger/testdata/openwb2_version.txt new file mode 100644 index 000000000..ab99aec31 --- /dev/null +++ b/UnitTestsTeslalogger/testdata/openwb2_version.txt @@ -0,0 +1,5 @@ +{ + "status": "success", + "topic": "openWB/system/version", + "message": "2.1.7-Beta.2" +} \ No newline at end of file