From a606aa9eeee430ab0322995618da29e0d7acd90f Mon Sep 17 00:00:00 2001 From: Mark Crossley <1196094+mcrossley@users.noreply.github.com> Date: Sun, 23 Jan 2022 16:24:29 +0000 Subject: [PATCH 1/8] Fix real time processing running before data is received. Add missing THSW decode to Davis WLL --- CumulusMX/Cumulus.cs | 6 ++++++ CumulusMX/DavisWllStation.cs | 5 +++++ CumulusMX/HttpStationEcowitt.cs | 2 +- CumulusMX/Properties/AssemblyInfo.cs | 6 +++--- 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/CumulusMX/Cumulus.cs b/CumulusMX/Cumulus.cs index c7a8a15a..1ce02e8c 100644 --- a/CumulusMX/Cumulus.cs +++ b/CumulusMX/Cumulus.cs @@ -2720,6 +2720,12 @@ internal void RealtimeTimerTick(object sender, ElapsedEventArgs elapsedEventArgs return; } + if ((!station.PressReadyToPlot || station.TempReadyToPlot || station.WindReadyToPlot) && !StationOptions.NoSensorCheck) + { + // not all the data is ready and NoSensorCheck is not enabled + return; + } + LogDebugMessage($"Realtime[{cycle}]: Start cycle"); try { diff --git a/CumulusMX/DavisWllStation.cs b/CumulusMX/DavisWllStation.cs index 8582fc29..de9029c3 100644 --- a/CumulusMX/DavisWllStation.cs +++ b/CumulusMX/DavisWllStation.cs @@ -708,6 +708,11 @@ private void DecodeCurrent(string currentJson) DoWindChill(ConvertTempFToUser(data1.wind_chill.Value), dateTime); } + if (data1.thsw_index.HasValue) + { + THSWIndex = ConvertTempFToUser(data1.thsw_index.Value); + } + //TODO: Wet Bulb? rec["wet_bulb"] - No, we already have humidity //TODO: Heat Index? rec["heat_index"] - No, Cumulus always calculates HI } diff --git a/CumulusMX/HttpStationEcowitt.cs b/CumulusMX/HttpStationEcowitt.cs index 0c112e01..ba2cf5aa 100644 --- a/CumulusMX/HttpStationEcowitt.cs +++ b/CumulusMX/HttpStationEcowitt.cs @@ -173,7 +173,7 @@ POST Parameters - all fields are URL escaped var gustVal = ConvertWindMPHToUser(Convert.ToDouble(gust, invNum)); var dirVal = Convert.ToInt32(dir, invNum); var spdVal = ConvertWindMPHToUser(Convert.ToDouble(spd, invNum)); - //DoWind(gustVal, dirVal, spdVal, recDate); + // The protocol does not provide an average value // so feed in current MX average DoWind(spdVal, dirVal, WindAverage / cumulus.Calib.WindSpeed.Mult, recDate); diff --git a/CumulusMX/Properties/AssemblyInfo.cs b/CumulusMX/Properties/AssemblyInfo.cs index 2dd31f44..b77fc2b7 100644 --- a/CumulusMX/Properties/AssemblyInfo.cs +++ b/CumulusMX/Properties/AssemblyInfo.cs @@ -6,7 +6,7 @@ // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("Cumulus MX")] -[assembly: AssemblyDescription("Version 3.14.2 - Build 3162")] +[assembly: AssemblyDescription("Version 3.14.3 - Build 3163")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Cumulus MX")] @@ -32,5 +32,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("3.14.2.3162")] -[assembly: AssemblyFileVersion("3.14.2.3162")] +[assembly: AssemblyVersion("3.14.3.3163")] +[assembly: AssemblyFileVersion("3.14.3.3163")] From 4c1b9854a4ec26eee3240d4f8b55df264f87de19 Mon Sep 17 00:00:00 2001 From: Mark Crossley <1196094+mcrossley@users.noreply.github.com> Date: Sun, 23 Jan 2022 16:27:03 +0000 Subject: [PATCH 2/8] Updates.txt update --- Updates.txt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Updates.txt b/Updates.txt index 7297932e..1ed9b709 100644 --- a/Updates.txt +++ b/Updates.txt @@ -1,3 +1,10 @@ +3.14.3 - b3163 +—————————————— +- Fix: Prevent real time processing occurring before first data has been received +- Fix: Davis WLL: Add missing decode of THSW from current data + + + 3.14.2 - b3162 —————————————— - Fix: Dayfile viewer/editor header for log date changed year format to correct value = "dd/mm/yy" From bf8e24bfbef87c0f588310095e5faf88d8faa2ca Mon Sep 17 00:00:00 2001 From: Mark Crossley <1196094+mcrossley@users.noreply.github.com> Date: Tue, 25 Jan 2022 09:57:01 +0000 Subject: [PATCH 3/8] Add Annual ET total web tag #AnnualET Fix incorrect time being logged for daily high humidex --- CumulusMX/WeatherStation.cs | 2 +- CumulusMX/webtags.cs | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CumulusMX/WeatherStation.cs b/CumulusMX/WeatherStation.cs index 3d37ea60..11a603a7 100644 --- a/CumulusMX/WeatherStation.cs +++ b/CumulusMX/WeatherStation.cs @@ -5665,7 +5665,7 @@ private async void DoDayfile(DateTime timestamp) queryString.Append(HiLoToday.LowFeelsLike.ToString(cumulus.TempFormat, InvC) + ","); queryString.Append(HiLoToday.LowFeelsLikeTime.ToString("\\'HH:mm\\'") + ","); queryString.Append(HiLoToday.HighHumidex.ToString(cumulus.TempFormat, InvC) + ","); - queryString.Append(HiLoToday.HighFeelsLikeTime.ToString("\\'HH:mm\\'")); + queryString.Append(HiLoToday.HighHumidexTime.ToString("\\'HH:mm\\'")); queryString.Append(")"); diff --git a/CumulusMX/webtags.cs b/CumulusMX/webtags.cs index 14720475..5266099a 100644 --- a/CumulusMX/webtags.cs +++ b/CumulusMX/webtags.cs @@ -3214,6 +3214,11 @@ private string TagEt(Dictionary tagParams) return CheckRcDp(station.ET, tagParams, cumulus.RainDPlaces + 1); } + private string TagAnnualEt(Dictionary tagParams) + { + return CheckRcDp(station.AnnualETTotal, tagParams, cumulus.RainDPlaces + 1); + } + private string TagLight(Dictionary tagParams) { return CheckRcDp(station.LightValue, tagParams, 1); @@ -5612,6 +5617,7 @@ public void InitialiseWebtags() { "RCapptempTH", TagRCapptempTh }, { "RCapptempTL", TagRCapptempTl }, { "ET", TagEt }, + { "AnnualET", TagAnnualEt }, { "UV", TagUv }, { "SolarRad", TagSolarRad }, { "Light", TagLight }, From 5335be409c7d1a9400e05afbffe34a256424aa0f Mon Sep 17 00:00:00 2001 From: Mark Crossley <1196094+mcrossley@users.noreply.github.com> Date: Fri, 28 Jan 2022 18:33:20 +0000 Subject: [PATCH 4/8] First beta of Ecowitt catch-up --- CumulusMX/Cumulus.cs | 17 + CumulusMX/CumulusMX.csproj | 1 + CumulusMX/DavisWllStation.cs | 4 +- CumulusMX/EcowittApi.cs | 355 ++++++++++ CumulusMX/GW1000Station.cs | 974 +++++++++++++++++++++++++-- CumulusMX/HttpStationEcowitt.cs | 696 ++++++++++++++++++- CumulusMX/Properties/AssemblyInfo.cs | 10 +- CumulusMX/StationSettings.cs | 34 + CumulusMX/webtags.cs | 16 +- Updates.txt | 10 +- 10 files changed, 2018 insertions(+), 99 deletions(-) create mode 100644 CumulusMX/EcowittApi.cs diff --git a/CumulusMX/Cumulus.cs b/CumulusMX/Cumulus.cs index 1ce02e8c..4206d849 100644 --- a/CumulusMX/Cumulus.cs +++ b/CumulusMX/Cumulus.cs @@ -4209,6 +4209,14 @@ private void ReadIniFile() EcowittExtraUseLightning = ini.GetValue("GW1000", "ExtraSensorUseLightning", true); EcowittExtraUseLeak= ini.GetValue("GW1000", "ExtraSensorUseLeak", true); + EcowittApplicationKey = ini.GetValue("GW1000", "EcowittAppKey", ""); + EcowittUserApiKey = ini.GetValue("GW1000", "EcowittUserKey", ""); + EcowittMacAddress = ini.GetValue("Gw1000", "EcowittMacAddress", ""); + if (string.IsNullOrEmpty(EcowittMacAddress) && !string.IsNullOrEmpty(Gw1000MacAddress)) + { + EcowittMacAddress = Gw1000MacAddress; + } + // Ambient settings AmbientExtraEnabled = ini.GetValue("Ambient", "ExtraSensorDataEnabled", false); AmbientExtraUseSolar = ini.GetValue("Ambient", "ExtraSensorUseSolar", true); @@ -5327,6 +5335,10 @@ internal void WriteIniFile() ini.SetValue("GW1000", "ExtraSensorUseLightning", EcowittExtraUseLightning); ini.SetValue("GW1000", "ExtraSensorUseLeak", EcowittExtraUseLeak); + ini.SetValue("GW1000", "EcowittAppKey", EcowittApplicationKey); + ini.SetValue("GW1000", "EcowittUserKey", EcowittUserApiKey); + ini.SetValue("GW1000", "EcowittMacAddress", EcowittMacAddress); + // Ambient settings ini.SetValue("Ambient", "ExtraSensorDataEnabled", AmbientExtraEnabled); ini.SetValue("Ambient", "ExtraSensorUseSolar", AmbientExtraUseSolar); @@ -6266,9 +6278,11 @@ private void ReadStringsFile() //public int VPClosedownTime { get; set; } public string AirLinkInIPAddr { get; set; } + public string AirLinkOutIPAddr { get; set; } public bool AirLinkInEnabled { get; set; } + public bool AirLinkOutEnabled { get; set; } public bool EcowittExtraEnabled { get; set; } @@ -6283,6 +6297,9 @@ private void ReadStringsFile() public bool EcowittExtraUseCo2 { get; set; } public bool EcowittExtraUseLightning { get; set; } public bool EcowittExtraUseLeak { get; set; } + public string EcowittApplicationKey { get; set; } + public string EcowittUserApiKey { get; set; } + public string EcowittMacAddress { get; set; } public bool AmbientExtraEnabled { get; set; } public bool AmbientExtraUseSolar { get; set; } diff --git a/CumulusMX/CumulusMX.csproj b/CumulusMX/CumulusMX.csproj index db625377..98819905 100644 --- a/CumulusMX/CumulusMX.csproj +++ b/CumulusMX/CumulusMX.csproj @@ -204,6 +204,7 @@ + diff --git a/CumulusMX/DavisWllStation.cs b/CumulusMX/DavisWllStation.cs index de9029c3..c3432e82 100644 --- a/CumulusMX/DavisWllStation.cs +++ b/CumulusMX/DavisWllStation.cs @@ -250,10 +250,10 @@ public override void Start() tmrRealtime.AutoReset = true; tmrRealtime.Start(); - // Create a current conditions thread to poll readings every 30 seconds + // Create a current conditions thread to poll readings every 10 seconds as temperature updates every 10 seconds GetWllCurrent(null, null); tmrCurrent.Elapsed += GetWllCurrent; - tmrCurrent.Interval = 30 * 1000; // Every 30 seconds + tmrCurrent.Interval = 10 * 1000; // Every 10 seconds tmrCurrent.AutoReset = true; tmrCurrent.Start(); diff --git a/CumulusMX/EcowittApi.cs b/CumulusMX/EcowittApi.cs new file mode 100644 index 00000000..90cb0a4a --- /dev/null +++ b/CumulusMX/EcowittApi.cs @@ -0,0 +1,355 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Runtime.Serialization; +using System.Text; +using System.Threading.Tasks; +using ServiceStack; +using ServiceStack.Text; + +namespace CumulusMX +{ + internal class EcowittApi + { + private Cumulus cumulus; + private readonly NumberFormatInfo invNum = CultureInfo.InvariantCulture.NumberFormat; + + private static readonly HttpClientHandler httpHandler = new HttpClientHandler(); + private readonly HttpClient httpClient = new HttpClient(httpHandler); + + private static string historyUrl = "https://api.ecowitt.net/api/v3/device/history?"; + + public EcowittApi(Cumulus cuml) + { + cumulus = cuml; + + // Configure a web proxy if required + if (!string.IsNullOrEmpty(cumulus.HTTPProxyName)) + { + httpHandler.Proxy = new WebProxy(cumulus.HTTPProxyName, cumulus.HTTPProxyPort); + httpHandler.UseProxy = true; + if (!string.IsNullOrEmpty(cumulus.HTTPProxyUser)) + { + httpHandler.Credentials = new NetworkCredential(cumulus.HTTPProxyUser, cumulus.HTTPProxyPassword); + } + } + } + + + internal bool GetHistoricData(DateTime startTime, DateTime endTime, string[] callbacks, out EcowittHistoricData data) + { + // Let's decode the Unix ts to DateTime + JsConfig.DateHandler = DateHandler.UnixTime; + + data = new EcowittHistoricData(); + + cumulus.LogMessage("API.GetHistoricData: Get Ecowitt Historic Data"); + + if (string.IsNullOrEmpty(cumulus.EcowittApplicationKey) || string.IsNullOrEmpty(cumulus.EcowittUserApiKey) || string.IsNullOrEmpty(cumulus.EcowittMacAddress)) + { + cumulus.LogMessage("API.GetHistoricData: Missing Ecowitt API data in the configuration, aborting!"); + cumulus.LastUpdateTime = DateTime.Now; + return false; + } + + var sb = new StringBuilder(historyUrl); + + sb.Append($"application_key={cumulus.EcowittApplicationKey}"); + sb.Append($"&api_key={cumulus.EcowittUserApiKey}"); + sb.Append($"&mac={cumulus.EcowittMacAddress}&"); + sb.Append($"&start_date={startTime.ToString("yyyy-MM-dd'%20'HH:mm:ss")}"); + sb.Append($"&end_date={endTime.ToString("yyyy-MM-dd'%20'HH:mm:ss")}"); + + // available callbacks: + // outdoor, indoor, solar_and_uvi, rainfall, wind, pressure, lightning + // indoor_co2, co2_aqi_combo, pm25_aqi_combo, pm10_aqi_combo, temp_and_humidity_aqi_combo + // pm25_ch[1-4] + // temp_and_humidity_ch[1-8] + // soil_ch[1-8] + // temp_ch[1-8] + // leaf_ch[1-8] + // batt + sb.Append("&call_back="); + foreach (var cb in callbacks) + sb.Append(cb + ","); + sb.Length--; + + //TODO: match time to logging interval + // available times 5min, 30min, 4hour, 1day + sb.Append($"&cycle_type=5min"); + + var url = sb.ToString(); + + var logUrl = url.Replace(cumulus.EcowittApplicationKey, "<>").Replace(cumulus.EcowittUserApiKey, "<>"); + cumulus.LogDebugMessage($"Ecowitt URL = {logUrl}"); + + try + { + string responseBody; + int responseCode; + + // we want to do this synchronously, so .Result + using (HttpResponseMessage response = httpClient.GetAsync(url).Result) + { + responseBody = response.Content.ReadAsStringAsync().Result; + responseCode = (int)response.StatusCode; + cumulus.LogDebugMessage($"API.GetHistoricData: Ecowitt API Historic Response code: {responseCode}"); + cumulus.LogDataMessage($"API.GetHistoricData: Ecowitt API Historic Response: {responseBody}"); + } + + if (responseCode != 200) + { + var historyError = responseBody.FromJson(); + cumulus.LogMessage($"API.GetHistoricData: Ecowitt API Historic Error: {historyError.code}, {historyError.msg}"); + cumulus.LogConsoleMessage($" - Error {historyError.code}: {historyError.msg}", ConsoleColor.Red); + cumulus.LastUpdateTime = endTime; + return false; + } + + + if (responseBody == "{}") + { + cumulus.LogMessage("API.GetHistoricData: Ecowitt API Historic: No data was returned."); + cumulus.LogConsoleMessage(" - No historic data available"); + cumulus.LastUpdateTime = endTime; + return false; + } + else if (responseBody.StartsWith("{\"code\":")) // sanity check + { + // get the sensor data + var histObj = responseBody.FromJson(); + data = histObj.data; + return true; + } + else // No idea what we got, dump it to the log + { + cumulus.LogMessage("API.GetHistoricData: Invalid historic message received"); + cumulus.LogDataMessage("API.GetHistoricData: Received: " + responseBody); + cumulus.LastUpdateTime = endTime; + return false; + } + } + catch (Exception ex) + { + cumulus.LogMessage("API.GetHistoricData: Exception:"); + cumulus.LastUpdateTime = endTime; + return false; + } + + } + + + private string ErrorCode(int code) + { + switch (code) + { + case -1: return "System is busy"; + case 0: return "Success!"; + case 40000: return "Illegal parameter"; + case 40010: return "Illegal Application_Key Parameter"; + case 40011: return "Illegal Api_Key Parameter"; + case 40012: return "Illegal MAC/IMEI Parameter"; + case 40013: return "Illegal start_date Parameter"; + case 40014: return "Illegal end_date Parameter"; + case 40015: return "Illegal cycle_type Parameter"; + case 40016: return "Illegal call_back Parameter"; + case 40017: return "Missing Application_Key Parameter"; + case 40018: return "Missing Api_Key Parameter"; + case 40019: return "Missing MAC Parameter"; + case 40020: return "Missing start_date Parameter"; + case 40021: return "Missing end_date Parameter"; + case 40022: return "Illegal Voucher type"; + case 43001: return "Needs other service support"; + case 44001: return "Media file or data packet is null"; + case 45001: return "Over the limit or other error"; + case 46001: return "No existing request"; + case 47001: return "Parse JSON/XML contents error"; + case 48001: return "Privilege Problem"; + default: return "Unknown error code"; + } + } + + private class EcowitHistErrorResp + { + public int code { get; set; } + public string msg { get; set; } + public DateTime time { get; set; } + public object data { get; set; } + } + + internal class EcowittHistoricResp + { + public int code { get; set; } + public string msg { get; set; } + public DateTime time { get; set; } + public EcowittHistoricData data { get; set; } + } + + internal class EcowittHistoricData + { + public EcowittHistoricTempHum indoor { get; set; } + public EcowittHistoricDataPressure pressure { get; set; } + public EcowittHistoricOutdoor outdoor { get; set; } + public EcowittHistoricDataWind wind { get; set; } + public EcowittHistoricDataSolar solar_and_uvi { get; set; } + public EcowittHistoricDataRainfall rainfall { get; set; } + public EcowittHistoricTempHum temp_and_humidity_ch1 { get; set; } + public EcowittHistoricTempHum temp_and_humidity_ch2 { get; set; } + public EcowittHistoricTempHum temp_and_humidity_ch3 { get; set; } + public EcowittHistoricTempHum temp_and_humidity_ch4 { get; set; } + public EcowittHistoricTempHum temp_and_humidity_ch5 { get; set; } + public EcowittHistoricTempHum temp_and_humidity_ch6 { get; set; } + public EcowittHistoricTempHum temp_and_humidity_ch7 { get; set; } + public EcowittHistoricTempHum temp_and_humidity_ch8 { get; set; } + public EcowittHistoricDataSoil soil_ch1 { get; set; } + public EcowittHistoricDataSoil soil_ch2 { get; set; } + public EcowittHistoricDataSoil soil_ch3 { get; set; } + public EcowittHistoricDataSoil soil_ch4 { get; set; } + public EcowittHistoricDataSoil soil_ch5 { get; set; } + public EcowittHistoricDataSoil soil_ch6 { get; set; } + public EcowittHistoricDataSoil soil_ch7 { get; set; } + public EcowittHistoricDataSoil soil_ch8 { get; set; } + public EcowittHistoricDataTemp temp_ch1 { get; set; } + public EcowittHistoricDataTemp temp_ch2 { get; set; } + public EcowittHistoricDataTemp temp_ch3 { get; set; } + public EcowittHistoricDataTemp temp_ch4 { get; set; } + public EcowittHistoricDataTemp temp_ch5 { get; set; } + public EcowittHistoricDataTemp temp_ch6 { get; set; } + public EcowittHistoricDataTemp temp_ch7 { get; set; } + public EcowittHistoricDataTemp temp_ch8 { get; set; } + public EcowittHistoricDataLeaf leaf_ch1 { get; set; } + public EcowittHistoricDataLeaf leaf_ch2 { get; set; } + public EcowittHistoricDataLeaf leaf_ch3 { get; set; } + public EcowittHistoricDataLeaf leaf_ch4 { get; set; } + public EcowittHistoricDataLeaf leaf_ch5 { get; set; } + public EcowittHistoricDataLeaf leaf_ch6 { get; set; } + public EcowittHistoricDataLeaf leaf_ch7 { get; set; } + public EcowittHistoricDataLeaf leaf_ch8 { get; set; } + public EcowittHistoricDataLightning lightning { get; set; } + public EcowittHistoricDataCo2 indoor_co2 { get; set; } + + } + + internal class EcowittHistoricDataTypeInt + { + public string unit { get; set; } + public Dictionary list { get; set; } + } + + internal class EcowittHistoricDataTypeDbl + { + public string unit { get; set; } + public Dictionary list { get; set; } + } + + internal class EcowittHistoricTempHum + { + public EcowittHistoricDataTypeDbl temperature { get; set; } + public EcowittHistoricDataTypeInt humidity { get; set; } + } + + internal class EcowittHistoricOutdoor : EcowittHistoricTempHum + { + public EcowittHistoricDataTypeDbl dew_point { get; set; } + } + + internal class EcowittHistoricDataPressure + { + public EcowittHistoricDataTypeDbl relative { get; set; } + } + + internal class EcowittHistoricDataWind + { + public EcowittHistoricDataTypeInt wind_direction { get; set; } + public EcowittHistoricDataTypeDbl wind_speed { get; set; } + public EcowittHistoricDataTypeDbl wind_gust { get; set; } + } + + internal class EcowittHistoricDataSolar + { + public EcowittHistoricDataTypeDbl solar { get; set; } + public EcowittHistoricDataTypeDbl uvi { get; set; } + } + + internal class EcowittHistoricDataRainfall + { + public EcowittHistoricDataTypeDbl rain_rate { get; set; } + public EcowittHistoricDataTypeDbl yearly { get; set; } + } + + internal class EcowittHistoricDataSoil + { + public EcowittHistoricDataTypeInt soilmoisture { get; set; } + } + internal class EcowittHistoricDataTemp + { + public EcowittHistoricDataTypeDbl temperature { get; set; } + } + + internal class EcowittHistoricDataLeaf + { + public EcowittHistoricDataTypeInt leaf_wetness { get; set; } + } + + internal class EcowittHistoricDataLightning + { + public EcowittHistoricDataTypeDbl distance { get; set; } + public EcowittHistoricDataTypeInt count { get; set; } + } + + [DataContract] + internal class EcowittHistoricDataCo2 + { + public EcowittHistoricDataTypeInt co2 { get; set; } + [DataMember(Name= "24_hours_average")] + public EcowittHistoricDataTypeInt average24h { get; set; } + } + + internal class HistoricData + { + public decimal? IndoorTemp { get; set; } + public int? IndoorHum { get; set; } + public decimal? Temp { get; set; } + public decimal? DewPoint { get; set; } + public decimal? FeelsLike { get; set; } + public int? Humidity { get; set; } + public decimal? RainRate { get; set; } + public decimal? RainYear { get; set; } + public decimal? WindSpd { get; set; } + public decimal? WindGust { get; set; } + public int? WindDir { get; set; } + public decimal? Pressure { get; set; } + public int? Solar { get; set; } + public decimal? UVI { get; set; } + public decimal? LightningDist { get; set; } + public int? LightningCount { get; set; } + public decimal?[] ExtraTemp { get; set; } + public int?[] ExtraHumidity { get; set; } + public int?[] SoilMoist { get; set; } + public decimal?[] UserTemp { get; set; } + public int?[] LeafWetness { get; set; } + public decimal? pm25 { get; set; } + public decimal? AqiComboPm25 { get; set; } + public decimal? AqiComboPm10 { get; set; } + public decimal? AqiComboTemp { get; set; } + public int? AqiComboHum { get; set; } + public int? Co2 { get; set; } + public int? Co2hr24 { get; set; } + public int? IndoorCo2 { get; set; } + public int? IndoorCo2hr24 { get; set; } + + public HistoricData() + { + ExtraTemp = new decimal?[8]; + ExtraHumidity = new int?[8]; + SoilMoist = new int?[8]; + UserTemp = new decimal?[8]; + LeafWetness = new int?[8]; + } + } + + } +} diff --git a/CumulusMX/GW1000Station.cs b/CumulusMX/GW1000Station.cs index 5e13cdc2..9a96425a 100644 --- a/CumulusMX/GW1000Station.cs +++ b/CumulusMX/GW1000Station.cs @@ -8,6 +8,8 @@ using System.Timers; using System.Collections.Generic; using System.Linq; +using System.Globalization; +using System.Threading.Tasks; namespace CumulusMX { @@ -20,6 +22,9 @@ internal class GW1000Station : WeatherStation private int lastMinute; private bool tenMinuteChanged = true; + private EcowittApi api; + private int maxArchiveRuns = 1; + private TcpClient socket; private NetworkStream stream; private bool connectedOk = false; @@ -28,6 +33,8 @@ internal class GW1000Station : WeatherStation private readonly System.Timers.Timer tmrDataWatchdog; private bool stop = false; + private readonly NumberFormatInfo invNum = CultureInfo.InvariantCulture.NumberFormat; + private Version fwVersion; private string mainSensor; @@ -264,62 +271,50 @@ public GW1000Station(Cumulus cumulus) : base(cumulus) ipaddr = cumulus.Gw1000IpAddress; macaddr = cumulus.Gw1000MacAddress; - if (!DoDiscovery()) + if (DoDiscovery()) { - return; - } + cumulus.LogMessage("Using IP address = " + ipaddr + " Port = " + AtPort); + socket = OpenTcpPort(); - cumulus.LogMessage("Using IP address = " + ipaddr + " Port = " + AtPort); - socket = OpenTcpPort(); + connectedOk = socket != null; - connectedOk = socket != null; - - if (connectedOk) - { - cumulus.LogMessage("Connected OK"); - cumulus.LogConsoleMessage("Connected to station"); - } - else - { - cumulus.LogMessage("Not Connected"); - cumulus.LogConsoleMessage("Unable to connect to station", ConsoleColor.Red); - } + if (connectedOk) + { + cumulus.LogMessage("Connected OK"); + cumulus.LogConsoleMessage("Connected to station"); + } + else + { + cumulus.LogMessage("Not Connected"); + cumulus.LogConsoleMessage("Unable to connect to station", ConsoleColor.Red); + } - if (connectedOk) - { - // Get the firmware version as check we are communicating - GW1000FirmwareVersion = GetFirmwareVersion(); - cumulus.LogMessage($"GW1000 firmware version: {GW1000FirmwareVersion}"); - if (GW1000FirmwareVersion != "???") + if (connectedOk) { - var fwString = GW1000FirmwareVersion.Split(new string[] { "_V" }, StringSplitOptions.None); - if (fwString.Length > 1) - { - fwVersion = new Version(fwString[1]); - } - else + // Get the firmware version as check we are communicating + GW1000FirmwareVersion = GetFirmwareVersion(); + cumulus.LogMessage($"GW1000 firmware version: {GW1000FirmwareVersion}"); + if (GW1000FirmwareVersion != "???") { - // failed to get the version, lets assume it's fairly new - fwVersion = new Version("1.6.5"); + var fwString = GW1000FirmwareVersion.Split(new string[] { "_V" }, StringSplitOptions.None); + if (fwString.Length > 1) + { + fwVersion = new Version(fwString[1]); + } + else + { + // failed to get the version, lets assume it's fairly new + fwVersion = new Version("1.6.5"); + } } - } - GetSystemInfo(); + GetSystemInfo(); - GetSensorIdsNew(); + GetSensorIdsNew(); + } } - timerStartNeeded = true; - LoadLastHoursFromDataLogs(cumulus.LastUpdateTime); - DoTrendValues(DateTime.Now); - - // WLL does not provide a forecast string, so use the Cumulus forecast - cumulus.UseCumulusForecast = true; - - cumulus.LogMessage("Starting GW1000"); - - StartLoop(); - bw = new BackgroundWorker(); + Task.Run(getAndProcessHistoryData); } private TcpClient OpenTcpPort() @@ -372,16 +367,6 @@ private TcpClient OpenTcpPort() public override void Start() { - // Wait for the lock - cumulus.LogDebugMessage("Lock: Station waiting for lock"); - Cumulus.syncInit.Wait(); - cumulus.LogDebugMessage("Lock: Station has the lock"); - - cumulus.LogMessage("Start normal reading loop"); - - cumulus.LogDebugMessage("Lock: Station releasing lock"); - Cumulus.syncInit.Release(); - tenMinuteChanged = true; lastMinute = DateTime.Now.Minute; @@ -391,6 +376,22 @@ public override void Start() tmrDataWatchdog.AutoReset = true; tmrDataWatchdog.Start(); + LoadLastHoursFromDataLogs(cumulus.LastUpdateTime); + DoTrendValues(DateTime.Now); + + // WLL does not provide a forecast string, so use the Cumulus forecast + cumulus.UseCumulusForecast = true; + + // just incase we did not catch-up any history + DoDayResetIfNeeded(); + + cumulus.LogMessage("Starting GW1000"); + + cumulus.StartTimersAndSensors(); + + StartLoop(); + bw = new BackgroundWorker(); + try { while (!stop) @@ -468,6 +469,861 @@ private void bw_DoStart(object sender, DoWorkEventArgs e) Cumulus.syncInit.Release(); } + public override void getAndProcessHistoryData() + { + if (string.IsNullOrEmpty(cumulus.EcowittApplicationKey) || string.IsNullOrEmpty(cumulus.EcowittUserApiKey) || string.IsNullOrEmpty(cumulus.EcowittMacAddress)) + { + cumulus.LogMessage("API.GetHistoricData: Missing Ecowitt API data in the configuration, aborting!"); + cumulus.LastUpdateTime = DateTime.Now; + Start(); + return; + } + + int archiveRun = 0; + cumulus.LogDebugMessage("Lock: Station waiting for the lock"); + Cumulus.syncInit.Wait(); + cumulus.LogDebugMessage("Lock: Station has the lock"); + + try + { + + api = new EcowittApi(cumulus); + + do + { + GetHistoricData(); + archiveRun++; + } while (archiveRun < maxArchiveRuns); + } + catch (Exception ex) + { + cumulus.LogMessage("Exception occurred reading archive data: " + ex.Message); + } + + cumulus.LogDebugMessage("Lock: Station releasing the lock"); + _ = Cumulus.syncInit.Release(); + + Start(); + } + + private void GetHistoricData() + { + cumulus.LogMessage("GetHistoricData: Starting Historic Data Process"); + + var startTime = cumulus.LastUpdateTime; + var endTime = DateTime.Now; + + // The API call is limited to fetching 24 hours of data + if ((endTime - startTime).TotalHours > 24.0) + { + // only fetch 24 hours worth of data, and schedule another run to fetch the rest + endTime = startTime.AddHours(24); + maxArchiveRuns++; + } + + EcowittApi.EcowittHistoricData dat; + + // only fetch the data we are interested in... + var sb = new StringBuilder("indoor,outdoor,wind,pressure"); + if (cumulus.EcowittExtraUseSolar || cumulus.EcowittExtraUseUv) + sb.Append(",solar_and_uvi"); + if (cumulus.EcowittExtraUseTempHum) + sb.Append(",temp_and_humidity_ch1,temp_and_humidity_ch2,temp_and_humidity_ch3,temp_and_humidity_ch4,temp_and_humidity_ch5,temp_and_humidity_ch6,temp_and_humidity_ch7,temp_and_humidity_ch8"); + if (cumulus.EcowittExtraUseSoilMoist) + sb.Append(",soil_ch1,soil_ch2,soil_ch3,soil_ch4,soil_ch5,soil_ch6,soil_ch7,soil_ch8"); + if (cumulus.EcowittExtraUseUserTemp) + sb.Append(",temp_ch1,temp_ch2,temp_ch3,temp_ch4,temp_ch5,temp_ch6,temp_ch7,temp_ch8"); + if (cumulus.EcowittExtraUseLeafWet) + sb.Append(",leaf_ch1,leaf_ch2,leaf_ch3,leaf_ch4,leaf_ch5,leaf_ch6,leaf_ch7,leaf_ch8"); + + var res = api.GetHistoricData(startTime, endTime, new string[] { sb.ToString() }, out dat); + + if (res) + { + ProcessHistoryData(dat); + } + } + + private void ProcessHistoryData(EcowittApi.EcowittHistoricData data) + { + // allocate a dictionary of data objects, keyed on the timestamp + var buffer = new SortedDictionary(); + + // process each sensor type, and store them for adding to the system later + // Indoor Data + if (data.indoor != null) + { + // do the temperature + if (data.indoor.temperature.list != null) + { + foreach (var item in data.indoor.temperature.list) + { + // not present value = 140 + if (!item.Value.HasValue || item.Value == 140) + continue; + + var newItem = new EcowittApi.HistoricData(); + newItem.IndoorTemp = item.Value; + buffer.Add(item.Key, newItem); + } + } + // do the humidity + if (data.indoor.humidity.list != null) + { + foreach (var item in data.indoor.humidity.list) + { + if (!item.Value.HasValue) + continue; + + if (buffer.ContainsKey(item.Key)) + { + buffer[item.Key].IndoorHum = item.Value; + } + else + { + var newItem = new EcowittApi.HistoricData(); + newItem.IndoorHum = item.Value; + buffer.Add(item.Key, newItem); + } + } + } + } + // Outdoor Data + if (data.outdoor != null) + { + // Temperature + if (data.outdoor.temperature.list != null) + { + foreach (var item in data.outdoor.temperature.list) + { + // not present value = 140 + if (!item.Value.HasValue || item.Value == 140) + continue; + + if (buffer.ContainsKey(item.Key)) + { + buffer[item.Key].Temp = item.Value; + } + else + { + var newItem = new EcowittApi.HistoricData(); + newItem.Temp = item.Value; + buffer.Add(item.Key, newItem); + } + } + } + + // Humidity + if (data.outdoor.humidity.list != null) + { + foreach (var item in data.outdoor.humidity.list) + { + if (!item.Value.HasValue) + continue; + + if (buffer.ContainsKey(item.Key)) + { + buffer[item.Key].Humidity = item.Value; + } + else + { + var newItem = new EcowittApi.HistoricData(); + newItem.Humidity = item.Value; + buffer.Add(item.Key, newItem); + } + } + } + // Dewpoint + if (data.outdoor.dew_point.list != null) + { + foreach (var item in data.outdoor.dew_point.list) + { + if (!item.Value.HasValue) + continue; + + if (buffer.ContainsKey(item.Key)) + { + buffer[item.Key].DewPoint = item.Value; + } + else + { + var newItem = new EcowittApi.HistoricData(); + newItem.DewPoint = item.Value; + buffer.Add(item.Key, newItem); + } + } + } + } + // Wind Data + if (data.wind != null) + { + // Speed + if (data.wind.wind_speed.list != null) + { + foreach (var item in data.wind.wind_speed.list) + { + if (!item.Value.HasValue) + continue; + + if (buffer.ContainsKey(item.Key)) + { + buffer[item.Key].WindSpd = item.Value; + } + else + { + var newItem = new EcowittApi.HistoricData(); + newItem.WindSpd = item.Value; + buffer.Add(item.Key, newItem); + } + } + } + + // Gust + if (data.wind.wind_gust.list != null) + { + foreach (var item in data.wind.wind_gust.list) + { + if (!item.Value.HasValue) + continue; + + if (buffer.ContainsKey(item.Key)) + { + buffer[item.Key].WindGust = item.Value; + } + else + { + var newItem = new EcowittApi.HistoricData(); + newItem.WindGust = item.Value; + buffer.Add(item.Key, newItem); + } + } + } + // Direction + if (data.wind.wind_direction.list != null) + { + foreach (var item in data.wind.wind_direction.list) + { + if (!item.Value.HasValue) + continue; + + if (buffer.ContainsKey(item.Key)) + { + buffer[item.Key].WindDir = item.Value; + } + else + { + var newItem = new EcowittApi.HistoricData(); + newItem.WindDir = item.Value; + buffer.Add(item.Key, newItem); + } + } + } + } + // Pressure Data + if (data.pressure != null) + { + // relative + if (data.pressure.relative.list != null) + { + foreach (var item in data.pressure.relative.list) + { + if (!item.Value.HasValue) + continue; + + if (buffer.ContainsKey(item.Key)) + { + buffer[item.Key].Pressure = item.Value; + } + else + { + var newItem = new EcowittApi.HistoricData(); + newItem.Pressure = item.Value; + buffer.Add(item.Key, newItem); + } + } + } + } + // Solar Data + if (data.solar_and_uvi != null) + { + // solar + if (cumulus.EcowittExtraUseSolar && data.solar_and_uvi.solar.list != null) + { + foreach (var item in data.solar_and_uvi.solar.list) + { + if (!item.Value.HasValue) + continue; + + if (buffer.ContainsKey(item.Key)) + { + buffer[item.Key].Solar = (int)item.Value; + } + else + { + var newItem = new EcowittApi.HistoricData(); + newItem.Solar = (int)item.Value; + buffer.Add(item.Key, newItem); + } + } + } + // uvi + if (cumulus.EcowittExtraUseUv && data.solar_and_uvi.uvi.list != null) + { + foreach (var item in data.solar_and_uvi.uvi.list) + { + if (!item.Value.HasValue) + continue; + + if (buffer.ContainsKey(item.Key)) + { + buffer[item.Key].UVI = (int)item.Value; + } + else + { + var newItem = new EcowittApi.HistoricData(); + newItem.UVI = (int)item.Value; + buffer.Add(item.Key, newItem); + } + } + } + } + // Extra 8 channel sensors + for (var i = 1; i <= 8; i++) + { + EcowittApi.EcowittHistoricTempHum srcTH = null; + EcowittApi.EcowittHistoricDataSoil srcSoil = null; + EcowittApi.EcowittHistoricDataTemp srcTemp = null; + EcowittApi.EcowittHistoricDataLeaf srcLeaf = null; + switch (i) + { + case 1: + srcTH = data.temp_and_humidity_ch1; + srcSoil = data.soil_ch1; + srcTemp = data.temp_ch1; + srcLeaf = data.leaf_ch1; + break; + case 2: + srcTH = data.temp_and_humidity_ch2; + srcSoil = data.soil_ch2; + srcTemp = data.temp_ch2; + srcLeaf = data.leaf_ch2; + break; + case 3: + srcTH = data.temp_and_humidity_ch3; + srcSoil = data.soil_ch3; + srcTemp = data.temp_ch3; + srcLeaf = data.leaf_ch3; + break; + case 4: + srcTH = data.temp_and_humidity_ch4; + srcSoil = data.soil_ch4; + srcTemp = data.temp_ch4; + srcLeaf = data.leaf_ch4; + break; + case 5: + srcTH = data.temp_and_humidity_ch5; + srcSoil = data.soil_ch5; + srcTemp = data.temp_ch5; + srcLeaf = data.leaf_ch5; + break; + case 6: + srcTH = data.temp_and_humidity_ch6; + srcSoil = data.soil_ch6; + srcTemp = data.temp_ch6; + srcLeaf = data.leaf_ch6; + break; + case 7: + srcTH = data.temp_and_humidity_ch7; + srcSoil = data.soil_ch7; + srcTemp = data.temp_ch7; + srcLeaf = data.leaf_ch7; + break; + case 8: + srcTH = data.temp_and_humidity_ch8; + srcSoil = data.soil_ch8; + srcTemp = data.temp_ch8; + srcLeaf = data.leaf_ch8; + break; + } + + // Extra Temp/Hum Data + if (cumulus.EcowittExtraUseTempHum && srcTH != null) + { + // temperature + if (srcTH.temperature.list != null) + { + foreach (var item in srcTH.temperature.list) + { + if (!item.Value.HasValue) + continue; + + if (buffer.ContainsKey(item.Key)) + { + buffer[item.Key].ExtraTemp[i - 1] = item.Value; + } + else + { + var newItem = new EcowittApi.HistoricData(); + newItem.ExtraTemp[i - 1] = item.Value; + buffer.Add(item.Key, newItem); + } + } + } + // humidity + if (srcTH.humidity.list != null) + { + foreach (var item in srcTH.humidity.list) + { + if (!item.Value.HasValue) + continue; + + if (buffer.ContainsKey(item.Key)) + { + buffer[item.Key].ExtraHumidity[i - 1] = item.Value; + } + else + { + var newItem = new EcowittApi.HistoricData(); + newItem.ExtraHumidity[i - 1] = item.Value; + buffer.Add(item.Key, newItem); + } + } + } + } + // Extra Soil Moisture Data + if (cumulus.EcowittExtraUseSoilMoist && srcSoil != null && srcSoil.soilmoisture.list != null) + { + // moisture + foreach (var item in srcSoil.soilmoisture.list) + { + if (!item.Value.HasValue) + continue; + + if (buffer.ContainsKey(item.Key)) + { + buffer[item.Key].SoilMoist[i - 1] = item.Value; + } + else + { + var newItem = new EcowittApi.HistoricData(); + newItem.SoilMoist[i - 1] = item.Value; + buffer.Add(item.Key, newItem); + } + } + } + // User Temp Data + if (cumulus.EcowittExtraUseUserTemp && srcTemp != null && srcTemp.temperature.list != null) + { + // temperature + foreach (var item in srcTemp.temperature.list) + { + if (!item.Value.HasValue) + continue; + + if (buffer.ContainsKey(item.Key)) + { + buffer[item.Key].UserTemp[i - 1] = item.Value; + } + else + { + var newItem = new EcowittApi.HistoricData(); + newItem.UserTemp[i - 1] = item.Value; + buffer.Add(item.Key, newItem); + } + } + } + // Leaf Wetness Data + if (cumulus.EcowittExtraUseLeafWet && srcLeaf != null && srcLeaf.leaf_wetness.list != null) + { + // wetness + foreach (var item in srcLeaf.leaf_wetness.list) + { + if (!item.Value.HasValue) + continue; + + if (buffer.ContainsKey(item.Key)) + { + buffer[item.Key].LeafWetness[i - 1] = item.Value; + } + else + { + var newItem = new EcowittApi.HistoricData(); + newItem.LeafWetness[i - 1] = item.Value; + buffer.Add(item.Key, newItem); + } + } + } + } + + + // now we have all the data for this period, for each record create the string expected by ProcessData and get it processed + var rollHour = Math.Abs(cumulus.GetHourInc()); + var luhour = cumulus.LastUpdateTime.Hour; + var rolloverdone = luhour == rollHour; + var midnightraindone = luhour == 0; + + foreach (var rec in buffer) + { + cumulus.LogMessage("Processing data for " + rec.Key); + + var h = rec.Key.Hour; + + // if outside rollover hour, rollover yet to be done + if (h != rollHour) rolloverdone = false; + + // In rollover hour and rollover not yet done + if (h == rollHour && !rolloverdone) + { + // do rollover + cumulus.LogMessage("Day rollover " + rec.Key.ToShortTimeString()); + DayReset(rec.Key); + + rolloverdone = true; + } + + // Not in midnight hour, midnight rain yet to be done + if (h != 0) midnightraindone = false; + + // In midnight hour and midnight rain (and sun) not yet done + if (h == 0 && !midnightraindone) + { + ResetMidnightRain(rec.Key); + ResetSunshineHours(); + midnightraindone = true; + } + + // finally apply this data + ApplyHistoricData(rec); + + // add in archive period worth of sunshine, if sunny + if (SolarRad > CurrentSolarMax * cumulus.SolarOptions.SunThreshold / 100 && + SolarRad >= cumulus.SolarOptions.SolarMinimum) + SunshineHours += 5 / 60.0; + + + + // add in 'following interval' minutes worth of wind speed to windrun + cumulus.LogMessage("Windrun: " + WindAverage.ToString(cumulus.WindFormat) + cumulus.Units.WindText + " for " + 5 + " minutes = " + + (WindAverage * WindRunHourMult[cumulus.Units.Wind] * 5 / 60.0).ToString(cumulus.WindRunFormat) + cumulus.Units.WindRunText); + + WindRunToday += WindAverage * WindRunHourMult[cumulus.Units.Wind] * 5 / 60.0; + + // update heating/cooling degree days + UpdateDegreeDays(5); + + // update dominant wind bearing + CalculateDominantWindBearing(Bearing, WindAverage, 5); + + CheckForWindrunHighLow(rec.Key); + + //bw?.ReportProgress((totalentries - datalist.Count) * 100 / totalentries, "processing"); + + //UpdateDatabase(timestamp.ToUniversalTime(), historydata.interval, false); + + cumulus.DoLogFile(rec.Key, false); + if (cumulus.StationOptions.LogExtraSensors) cumulus.DoExtraLogFile(rec.Key); + + //AddRecentDataEntry(timestamp, WindAverage, RecentMaxGust, WindLatest, Bearing, AvgBearing, + // OutdoorTemperature, WindChill, OutdoorDewpoint, HeatIndex, + // OutdoorHumidity, Pressure, RainToday, SolarRad, UV, Raincounter, FeelsLike, Humidex); + + AddRecentDataWithAq(rec.Key, WindAverage, RecentMaxGust, WindLatest, Bearing, AvgBearing, OutdoorTemperature, WindChill, OutdoorDewpoint, HeatIndex, + OutdoorHumidity, Pressure, RainToday, SolarRad, UV, Raincounter, FeelsLike, Humidex, ApparentTemperature, IndoorTemperature, IndoorHumidity, CurrentSolarMax, RainRate); + + if (cumulus.StationOptions.CalculatedET && rec.Key.Minute == 0) + { + // Start of a new hour, and we want to calculate ET in Cumulus + CalculateEvaoptranspiration(rec.Key); + } + + DoTrendValues(rec.Key); + UpdatePressureTrendString(); + UpdateStatusPanel(rec.Key); + cumulus.AddToWebServiceLists(rec.Key); + + } + } + + + private void ApplyHistoricData(KeyValuePair rec) + { + // === Wind == + try + { + if (rec.Value.WindGust.HasValue && rec.Value.WindSpd.HasValue && rec.Value.WindDir.HasValue) + { + var gustVal = ConvertWindMPHToUser((double)rec.Value.WindGust); + var spdVal = ConvertWindMPHToUser((double)rec.Value.WindSpd); + var dirVal = rec.Value.WindDir.Value; + + // The protocol does not provide an average value + // so feed in current MX average + DoWind(spdVal, dirVal, WindAverage / cumulus.Calib.WindSpeed.Mult, rec.Key); + + var gustLastCal = gustVal * cumulus.Calib.WindGust.Mult; + if (gustLastCal > RecentMaxGust) + { + cumulus.LogDebugMessage("Setting max gust from current value: " + gustLastCal.ToString(cumulus.WindFormat)); + CheckHighGust(gustLastCal, dirVal, rec.Key); + + // add to recent values so normal calculation includes this value + WindRecent[nextwind].Gust = gustVal; // use uncalibrated value + WindRecent[nextwind].Speed = WindAverage / cumulus.Calib.WindSpeed.Mult; + WindRecent[nextwind].Timestamp = rec.Key; + nextwind = (nextwind + 1) % MaxWindRecent; + + RecentMaxGust = gustLastCal; + } + } + } + catch (Exception ex) + { + cumulus.LogMessage("ApplyHistoricData: Error in Wind data - " + ex.Message); + } + + // === Humidity === + try + { + if (rec.Value.IndoorHum.HasValue) + { + DoIndoorHumidity(rec.Value.IndoorHum.Value); + } + + if (rec.Value.Humidity.HasValue) + { + DoOutdoorHumidity(rec.Value.Humidity.Value, rec.Key); + } + } + catch (Exception ex) + { + cumulus.LogMessage("ApplyHistoricData: Error in Humidity data - " + ex.Message); + } + + // === Pressure === + try + { + if (rec.Value.Pressure.HasValue) + { + var pressVal = ConvertPressINHGToUser((double)rec.Value.Pressure); + DoPressure(pressVal, rec.Key); + UpdatePressureTrendString(); + } + } + catch (Exception ex) + { + cumulus.LogMessage("ApplyHistoricData: Error in Pressure data - " + ex.Message); + } + + // === Indoor temp === + try + { + if (rec.Value.IndoorTemp.HasValue) + { + var tempVal = ConvertTempFToUser((double)rec.Value.IndoorTemp); + DoIndoorTemp(tempVal); + } + } + catch (Exception ex) + { + cumulus.LogMessage("ApplyHistoricData: Error in Indoor temp data - " + ex.Message); + } + + // === Outdoor temp === + try + { + if (rec.Value.Temp.HasValue) + { + var tempVal = ConvertTempFToUser((double)rec.Value.Temp); + DoOutdoorTemp(tempVal, rec.Key); + } + } + catch (Exception ex) + { + cumulus.LogMessage("ApplyHistoricData: Error in Outdoor temp data - " + ex.Message); + } + + // === Rain === + try + { + double rRate = 0; + if (rec.Value.RainRate.HasValue) + { + // we have a rain rate, so we will NOT calculate it + calculaterainrate = false; + rRate = (double)rec.Value.RainRate; + } + else + { + // No rain rate, so we will calculate it + calculaterainrate = true; + } + + if (rec.Value.RainYear.HasValue) + { + var rainVal = ConvertRainINToUser((double)rec.Value.RainYear); + var rateVal = ConvertRainINToUser(rRate); + DoRain(rainVal, rateVal, rec.Key); + } + } + catch (Exception ex) + { + cumulus.LogMessage("ApplyHistoricData: Error in Rain data - " + ex.Message); + } + + // === Dewpoint === + try + { + if (cumulus.StationOptions.CalculatedDP) + { + DoOutdoorDewpoint(0, rec.Key); + } + else if (rec.Value.DewPoint.HasValue) + { + var val = ConvertTempFToUser((double)rec.Value.DewPoint); + DoOutdoorDewpoint(val, rec.Key); + } + } + catch (Exception ex) + { + cumulus.LogMessage("ApplyHistoricData: Error in Dew point data - " + ex.Message); + } + + // === Wind Chill === + try + { + if (cumulus.StationOptions.CalculatedWC && rec.Value.Temp.HasValue && rec.Value.WindSpd.HasValue) + { + DoWindChill(0, rec.Key); + } + else + { + // historic API does not provide Wind Chill so force calculation + cumulus.StationOptions.CalculatedWC = true; + DoWindChill(0, rec.Key); + cumulus.StationOptions.CalculatedWC = false; + } + } + catch (Exception ex) + { + cumulus.LogMessage("ApplyHistoricData: Error in Dew point data - " + ex.Message); + } + + // === Humidex === + if (rec.Value.Temp.HasValue && rec.Value.Humidity.HasValue) + { + try + { + DoHumidex(rec.Key); + + // === Apparent & Feels Like === - requires temp, hum, and windspeed + if (rec.Value.WindSpd.HasValue) + { + DoApparentTemp(rec.Key); + DoFeelsLike(rec.Key); + } + else + { + cumulus.LogMessage("ApplyHistoricData: Insufficient data to calculate Apparent/Feels Like temps"); + } + } + catch (Exception ex) + { + cumulus.LogMessage("ApplyHistoricData: Error in Humidex/Apparant/Feels Like - " + ex.Message); + } + } + else + { + cumulus.LogMessage("ApplyHistoricData: Insufficient data to calculate Humidex and Apparent/Feels Like temps"); + } + + // === Solar === + try + { + if (cumulus.EcowittExtraUseSolar && rec.Value.Solar.HasValue) + { + DoSolarRad(rec.Value.Solar.Value, rec.Key); + } + } + catch (Exception ex) + { + cumulus.LogMessage("ApplyHistoricData: Error in Solar data - " + ex.Message); + } + + // === UVI === + try + { + if (cumulus.EcowittExtraUseUv && rec.Value.UVI.HasValue) + { + DoUV((double)rec.Value.UVI, rec.Key); + } + } + catch (Exception ex) + { + cumulus.LogMessage("ApplyHistoricData: Error in Solar data - " + ex.Message); + } + + // === Extra Sensors === + for (var i = 1; i <= 8; i++) + { + if (cumulus.EcowittExtraUseTempHum) + { + // === Extra Temperature === + try + { + if (rec.Value.ExtraTemp[i-1].HasValue) + { + DoExtraTemp(ConvertTempFToUser((double)rec.Value.ExtraTemp[i - 1]), i); + } + } + catch (Exception ex) + { + cumulus.LogMessage($"ApplyHistoricData: Error in extra temperature data - {ex.Message}"); + } + // === Extra Humidity === + try + { + if (rec.Value.ExtraHumidity[i-1].HasValue) + { + DoExtraHum(rec.Value.ExtraHumidity[i - 1].Value, i); + } + } + catch (Exception ex) + { + cumulus.LogMessage($"ApplyHistoricData: Error in extra humidity data - {ex.Message}"); + } + + } + + // === User Temperature === + try + { + if (cumulus.EcowittExtraUseUserTemp && rec.Value.UserTemp[i - 1].HasValue) + { + DoUserTemp(ConvertTempFToUser((double)rec.Value.UserTemp[i - 1]), i); + } + } + catch (Exception ex) + { + cumulus.LogMessage($"ApplyHistoricData: Error in extra user temperature data - {ex.Message}"); + } + + // === Soil Moisture === + try + { + if (cumulus.EcowittExtraUseSoilMoist && rec.Value.SoilMoist[i - 1].HasValue) + { + DoSoilMoisture((double)rec.Value.SoilMoist[i-1], i); + } + } + catch (Exception ex) + { + cumulus.LogMessage($"ApplyHistoricData: Error in soil moisture data - {ex.Message}"); + } + } + + } + + private Discovery DiscoverGW1000() { // We only want unique IP addresses @@ -851,6 +1707,12 @@ private bool PrintSensorInfoNew(byte[] data, int idx) case string wh34 when wh34.StartsWith("WH34"): // ch 1-8 case string wh35 when wh35.StartsWith("WH35"): // ch 1-8 case "WH90": + // if a WS90 is connected, it has a 4.75 second update rate, so reduce the MX update rate from the default 10 seconds + if (updateRate > 4000) + { + cumulus.LogMessage($"PrintSensorInfoNew: WS90 sensor detected, changing the update rate from {updateRate / 1000} seconds to 4 seconds"); + updateRate = 4000; + } battV = data[battPos] * 0.02; batt = $"{battV:f1}V ({TestBattery10(data[battPos])})"; // volts/10, low = 1.2V break; @@ -1722,12 +2584,12 @@ private static string TestBattery10(byte value) { return value / 10.0 > 1.2 ? "OK" : "Low"; } + private static double TestBattery10V(byte value) { return value / 10.0; } - private static string TestBatteryPct(byte value) { return value >= 20 ? "OK" : "Low"; diff --git a/CumulusMX/HttpStationEcowitt.cs b/CumulusMX/HttpStationEcowitt.cs index ba2cf5aa..dce3e17e 100644 --- a/CumulusMX/HttpStationEcowitt.cs +++ b/CumulusMX/HttpStationEcowitt.cs @@ -4,6 +4,13 @@ using System.Web; using System.Globalization; using System.Collections.Specialized; +using System.Reflection; +using ServiceStack.Text; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; +using System.Threading.Tasks; namespace CumulusMX { @@ -15,6 +22,9 @@ class HttpStationEcowitt : WeatherStation private bool stopping = false; private readonly NumberFormatInfo invNum = CultureInfo.InvariantCulture.NumberFormat; private bool reportStationType = true; + private EcowittApi api; + private int maxArchiveRuns = 1; + public HttpStationEcowitt(Cumulus cumulus, WeatherStation station = null) : base(cumulus) { @@ -63,7 +73,7 @@ public HttpStationEcowitt(Cumulus cumulus, WeatherStation station = null) : base // Only perform the Start-up if we are a proper station, not a Extra Sensor if (station == null) { - Start(); + Task.Run(getAndProcessHistoryData); } else { @@ -77,7 +87,7 @@ public override void Start() { cumulus.LogMessage("Starting HTTP Station (Ecowitt)"); DoDayResetIfNeeded(); - timerStartNeeded = true; + cumulus.StartTimersAndSensors(); } else { @@ -95,7 +105,609 @@ public override void Stop() } } - public string ProcessData(IHttpContext context, bool main) + public override void getAndProcessHistoryData() + { + if (string.IsNullOrEmpty(cumulus.EcowittApplicationKey) || string.IsNullOrEmpty(cumulus.EcowittUserApiKey) || string.IsNullOrEmpty(cumulus.EcowittMacAddress)) + { + cumulus.LogMessage("API.GetHistoricData: Missing Ecowitt API data in the configuration, aborting!"); + cumulus.LastUpdateTime = DateTime.Now; + Start(); + return; + } + + int archiveRun = 0; + cumulus.LogDebugMessage("Lock: Station waiting for the lock"); + Cumulus.syncInit.Wait(); + cumulus.LogDebugMessage("Lock: Station has the lock"); + + try + { + + api = new EcowittApi(cumulus); + + do + { + GetHistoricData(); + archiveRun++; + } while (archiveRun < maxArchiveRuns); + } + catch (Exception ex) + { + cumulus.LogMessage("Exception occurred reading archive data: " + ex.Message); + } + + cumulus.LogDebugMessage("Lock: Station releasing the lock"); + _ = Cumulus.syncInit.Release(); + + Start(); + } + + private void GetHistoricData() + { + cumulus.LogMessage("GetHistoricData: Starting Historic Data Process"); + + var startTime = cumulus.LastUpdateTime; + var endTime = DateTime.Now; + + // The API call is limited to fetching 24 hours of data + if ((endTime - startTime).TotalHours > 24.0) + { + // only fetch 24 hours worth of data, and schedule another run to fetch the rest + endTime = startTime.AddHours(24); + maxArchiveRuns++; + } + + EcowittApi.EcowittHistoricData dat; + + // only fetch the data we are interested in... + var sb = new StringBuilder("indoor,outdoor,wind,pressure"); + if (cumulus.EcowittExtraUseSolar || cumulus.EcowittExtraUseUv) + sb.Append(",solar_and_uvi"); + if (cumulus.EcowittExtraUseTempHum) + sb.Append(",temp_and_humidity_ch1,temp_and_humidity_ch2,temp_and_humidity_ch3,temp_and_humidity_ch4,temp_and_humidity_ch5,temp_and_humidity_ch6,temp_and_humidity_ch7,temp_and_humidity_ch8"); + if (cumulus.EcowittExtraUseSoilMoist) + sb.Append(",soil_ch1,soil_ch2,soil_ch3,soil_ch4,soil_ch5,soil_ch6,soil_ch7,soil_ch8"); + if (cumulus.EcowittExtraUseUserTemp) + sb.Append(",temp_ch1,temp_ch2,temp_ch3,temp_ch4,temp_ch5,temp_ch6,temp_ch7,temp_ch8"); + if (cumulus.EcowittExtraUseLeafWet) + sb.Append(",leaf_ch1,leaf_ch2,leaf_ch3,leaf_ch4,leaf_ch5,leaf_ch6,leaf_ch7,leaf_ch8"); + + var res = api.GetHistoricData(startTime, endTime, new string[] { sb.ToString() }, out dat); + + if (res) + { + + ProcessHistoryData(dat); + + } + } + + private void ProcessHistoryData(EcowittApi.EcowittHistoricData data) + { + // allocate a dictionary of data objects, keyed on the timestamp + var buffer = new SortedDictionary(); + + // process each sensor type, and store them for adding to the system later + // Indoor Data + if (data.indoor != null) + { + // do the temperature + if (data.indoor.temperature.list != null) + { + foreach (var item in data.indoor.temperature.list) + { + // not present value = 140 + if (!item.Value.HasValue || item.Value == 140) + continue; + + var newItem = new EcowittApi.HistoricData(); + newItem.IndoorTemp = item.Value; + buffer.Add(item.Key, newItem); + } + } + // do the humidity + if (data.indoor.humidity.list != null) + { + foreach (var item in data.indoor.humidity.list) + { + if (!item.Value.HasValue) + continue; + + if (buffer.ContainsKey(item.Key)) + { + buffer[item.Key].IndoorHum = item.Value; + } + else + { + var newItem = new EcowittApi.HistoricData(); + newItem.IndoorHum = item.Value; + buffer.Add(item.Key, newItem); + } + } + } + } + // Outdoor Data + if (data.outdoor != null) + { + // Temperature + if (data.outdoor.temperature.list != null) + { + foreach (var item in data.outdoor.temperature.list) + { + // not present value = 140 + if (!item.Value.HasValue || item.Value == 140) + continue; + + if (buffer.ContainsKey(item.Key)) + { + buffer[item.Key].Temp = item.Value; + } + else + { + var newItem = new EcowittApi.HistoricData(); + newItem.Temp = item.Value; + buffer.Add(item.Key, newItem); + } + } + } + + // Humidity + if (data.outdoor.humidity.list != null) + { + foreach (var item in data.outdoor.humidity.list) + { + if (!item.Value.HasValue) + continue; + + if (buffer.ContainsKey(item.Key)) + { + buffer[item.Key].Humidity = item.Value; + } + else + { + var newItem = new EcowittApi.HistoricData(); + newItem.Humidity = item.Value; + buffer.Add(item.Key, newItem); + } + } + } + // Dewpoint + if (data.outdoor.dew_point.list != null) + { + foreach (var item in data.outdoor.dew_point.list) + { + if (!item.Value.HasValue) + continue; + + if (buffer.ContainsKey(item.Key)) + { + buffer[item.Key].DewPoint = item.Value; + } + else + { + var newItem = new EcowittApi.HistoricData(); + newItem.DewPoint = item.Value; + buffer.Add(item.Key, newItem); + } + } + } + } + // Wind Data + if (data.wind != null) + { + // Speed + if (data.wind.wind_speed.list != null) + { + foreach (var item in data.wind.wind_speed.list) + { + if (!item.Value.HasValue) + continue; + + if (buffer.ContainsKey(item.Key)) + { + buffer[item.Key].WindSpd = item.Value; + } + else + { + var newItem = new EcowittApi.HistoricData(); + newItem.WindSpd = item.Value; + buffer.Add(item.Key, newItem); + } + } + } + + // Gust + if (data.wind.wind_gust.list != null) + { + foreach (var item in data.wind.wind_gust.list) + { + if (!item.Value.HasValue) + continue; + + if (buffer.ContainsKey(item.Key)) + { + buffer[item.Key].WindGust = item.Value; + } + else + { + var newItem = new EcowittApi.HistoricData(); + newItem.WindGust = item.Value; + buffer.Add(item.Key, newItem); + } + } + } + // Direction + if (data.wind.wind_direction.list != null) + { + foreach (var item in data.wind.wind_direction.list) + { + if (!item.Value.HasValue) + continue; + + if (buffer.ContainsKey(item.Key)) + { + buffer[item.Key].WindDir = item.Value; + } + else + { + var newItem = new EcowittApi.HistoricData(); + newItem.WindDir = item.Value; + buffer.Add(item.Key, newItem); + } + } + } + } + // Pressure Data + if (data.pressure != null) + { + // relative + if (data.pressure.relative.list != null) + { + foreach (var item in data.pressure.relative.list) + { + if (!item.Value.HasValue) + continue; + + if (buffer.ContainsKey(item.Key)) + { + buffer[item.Key].Pressure = item.Value; + } + else + { + var newItem = new EcowittApi.HistoricData(); + newItem.Pressure = item.Value; + buffer.Add(item.Key, newItem); + } + } + } + } + // Solar Data + if (data.solar_and_uvi != null) + { + // solar + if (cumulus.EcowittExtraUseSolar && data.solar_and_uvi.solar.list != null) + { + foreach (var item in data.solar_and_uvi.solar.list) + { + if (!item.Value.HasValue) + continue; + + if (buffer.ContainsKey(item.Key)) + { + buffer[item.Key].Solar = (int)item.Value; + } + else + { + var newItem = new EcowittApi.HistoricData(); + newItem.Solar = (int)item.Value; + buffer.Add(item.Key, newItem); + } + } + } + // uvi + if (cumulus.EcowittExtraUseUv && data.solar_and_uvi.uvi.list != null) + { + foreach (var item in data.solar_and_uvi.uvi.list) + { + if (!item.Value.HasValue) + continue; + + if (buffer.ContainsKey(item.Key)) + { + buffer[item.Key].UVI = (int)item.Value; + } + else + { + var newItem = new EcowittApi.HistoricData(); + newItem.UVI = (int)item.Value; + buffer.Add(item.Key, newItem); + } + } + } + } + // Extra 8 channel sensors + for (var i = 1; i <= 8; i++) + { + EcowittApi.EcowittHistoricTempHum srcTH = null; + EcowittApi.EcowittHistoricDataSoil srcSoil = null; + EcowittApi.EcowittHistoricDataTemp srcTemp = null; + EcowittApi.EcowittHistoricDataLeaf srcLeaf = null; + switch (i) + { + case 1: + srcTH = data.temp_and_humidity_ch1; + srcSoil = data.soil_ch1; + srcTemp = data.temp_ch1; + srcLeaf = data.leaf_ch1; + break; + case 2: + srcTH = data.temp_and_humidity_ch2; + srcSoil = data.soil_ch2; + srcTemp = data.temp_ch2; + srcLeaf = data.leaf_ch2; + break; + case 3: + srcTH = data.temp_and_humidity_ch3; + srcSoil = data.soil_ch3; + srcTemp = data.temp_ch3; + srcLeaf = data.leaf_ch3; + break; + case 4: + srcTH = data.temp_and_humidity_ch4; + srcSoil = data.soil_ch4; + srcTemp = data.temp_ch4; + srcLeaf = data.leaf_ch4; + break; + case 5: + srcTH = data.temp_and_humidity_ch5; + srcSoil = data.soil_ch5; + srcTemp = data.temp_ch5; + srcLeaf = data.leaf_ch5; + break; + case 6: + srcTH = data.temp_and_humidity_ch6; + srcSoil = data.soil_ch6; + srcTemp = data.temp_ch6; + srcLeaf = data.leaf_ch6; + break; + case 7: + srcTH = data.temp_and_humidity_ch7; + srcSoil = data.soil_ch7; + srcTemp = data.temp_ch7; + srcLeaf = data.leaf_ch7; + break; + case 8: + srcTH = data.temp_and_humidity_ch8; + srcSoil = data.soil_ch8; + srcTemp = data.temp_ch8; + srcLeaf = data.leaf_ch8; + break; + } + + // Extra Temp/Hum Data + if (cumulus.EcowittExtraUseTempHum && srcTH != null) + { + // temperature + if (srcTH.temperature.list != null) + { + foreach (var item in srcTH.temperature.list) + { + if (!item.Value.HasValue) + continue; + + if (buffer.ContainsKey(item.Key)) + { + buffer[item.Key].ExtraTemp[i - 1] = item.Value; + } + else + { + var newItem = new EcowittApi.HistoricData(); + newItem.ExtraTemp[i - 1] = item.Value; + buffer.Add(item.Key, newItem); + } + } + } + // humidity + if (srcTH.humidity.list != null) + { + foreach (var item in srcTH.humidity.list) + { + if (!item.Value.HasValue) + continue; + + if (buffer.ContainsKey(item.Key)) + { + buffer[item.Key].ExtraHumidity[i - 1] = item.Value; + } + else + { + var newItem = new EcowittApi.HistoricData(); + newItem.ExtraHumidity[i - 1] = item.Value; + buffer.Add(item.Key, newItem); + } + } + } + } + // Extra Soil Moisture Data + if (cumulus.EcowittExtraUseSoilMoist && srcSoil != null && srcSoil.soilmoisture.list != null) + { + // moisture + foreach (var item in srcSoil.soilmoisture.list) + { + if (!item.Value.HasValue) + continue; + + if (buffer.ContainsKey(item.Key)) + { + buffer[item.Key].SoilMoist[i - 1] = item.Value; + } + else + { + var newItem = new EcowittApi.HistoricData(); + newItem.SoilMoist[i - 1] = item.Value; + buffer.Add(item.Key, newItem); + } + } + } + // User Temp Data + if (cumulus.EcowittExtraUseUserTemp && srcTemp != null && srcTemp.temperature.list != null) + { + // temperature + foreach (var item in srcTemp.temperature.list) + { + if (!item.Value.HasValue) + continue; + + if (buffer.ContainsKey(item.Key)) + { + buffer[item.Key].UserTemp[i - 1] = item.Value; + } + else + { + var newItem = new EcowittApi.HistoricData(); + newItem.UserTemp[i - 1] = item.Value; + buffer.Add(item.Key, newItem); + } + } + } + // Leaf Wetness Data + if (cumulus.EcowittExtraUseLeafWet && srcLeaf != null && srcLeaf.leaf_wetness.list != null) + { + // wetness + foreach (var item in srcLeaf.leaf_wetness.list) + { + if (!item.Value.HasValue) + continue; + + if (buffer.ContainsKey(item.Key)) + { + buffer[item.Key].LeafWetness[i - 1] = item.Value; + } + else + { + var newItem = new EcowittApi.HistoricData(); + newItem.LeafWetness[i - 1] = item.Value; + buffer.Add(item.Key, newItem); + } + } + } + } + + + // now we have all the data for this period, for each record create the string expected by ProcessData and get it processed + var rollHour = Math.Abs(cumulus.GetHourInc()); + var luhour = cumulus.LastUpdateTime.Hour; + var rolloverdone = luhour == rollHour; + var midnightraindone = luhour == 0; + + foreach (var rec in buffer) + { + var sb = new StringBuilder(); + if (rec.Value.IndoorTemp.HasValue) sb.Append("tempinf=" + rec.Value.IndoorTemp); + if (rec.Value.IndoorHum.HasValue) sb.Append("&humidityin=" + rec.Value.IndoorHum); + if (rec.Value.Temp.HasValue) sb.Append("&tempf=" + rec.Value.Temp); + if (rec.Value.Humidity.HasValue) sb.Append("&humidity=" + rec.Value.Humidity); + if (rec.Value.DewPoint.HasValue) sb.Append("&dewptf" + rec.Value.DewPoint); + if (rec.Value.WindDir.HasValue) sb.Append("&winddir=" + rec.Value.WindDir); + if (rec.Value.WindSpd.HasValue) sb.Append("&windspeedmph=" + rec.Value.WindSpd); + if (rec.Value.WindGust.HasValue) sb.Append("&windgustmph=" + rec.Value.WindGust); + if (rec.Value.Pressure.HasValue) sb.Append("&baromrelin=" + rec.Value.Pressure); + if (rec.Value.Solar.HasValue) sb.Append("&solarradiation=" + rec.Value.Solar); + if (rec.Value.UVI.HasValue) sb.Append("&uv=" + rec.Value.UVI); + for (var i = 1; i <= 8; i++) + { + if (rec.Value.ExtraTemp[i - 1].HasValue) sb.Append($"&temp{i}f={rec.Value.ExtraTemp[i - 1]}"); + if (rec.Value.ExtraHumidity[i - 1].HasValue) sb.Append($"&humidity{i}={rec.Value.ExtraHumidity[i - 1]}"); + if (rec.Value.UserTemp[i - 1].HasValue) sb.Append($"tf_ch{i}={rec.Value.UserTemp[i - 1]}"); + if (rec.Value.SoilMoist[i - 1].HasValue) sb.Append($"soilmoisture{i}={rec.Value.SoilMoist[i - 1]}"); + if (rec.Value.LeafWetness[i - 1].HasValue) + if (i==1) + sb.Append($"leafwetness={rec.Value.LeafWetness[i - 1]}"); + else + sb.Append($"leafwetness{i}={rec.Value.LeafWetness[i - 1]}"); + } + + cumulus.LogMessage("Processing data for " + rec.Key); + + var h = rec.Key.Hour; + + // if outside rollover hour, rollover yet to be done + if (h != rollHour) rolloverdone = false; + + // In rollover hour and rollover not yet done + if (h == rollHour && !rolloverdone) + { + // do rollover + cumulus.LogMessage("Day rollover " + rec.Key.ToShortTimeString()); + DayReset(rec.Key); + + rolloverdone = true; + } + + // Not in midnight hour, midnight rain yet to be done + if (h != 0) midnightraindone = false; + + // In midnight hour and midnight rain (and sun) not yet done + if (h == 0 && !midnightraindone) + { + ResetMidnightRain(rec.Key); + ResetSunshineHours(); + midnightraindone = true; + } + + // finally apply this data + ApplyData(sb.ToString(), station == null, rec.Key); + + // add in archive period worth of sunshine, if sunny + if (SolarRad > CurrentSolarMax * cumulus.SolarOptions.SunThreshold / 100 && + SolarRad >= cumulus.SolarOptions.SolarMinimum) + SunshineHours += 5 / 60.0; + + + + // add in 'following interval' minutes worth of wind speed to windrun + cumulus.LogMessage("Windrun: " + WindAverage.ToString(cumulus.WindFormat) + cumulus.Units.WindText + " for " + 5 + " minutes = " + + (WindAverage * WindRunHourMult[cumulus.Units.Wind] * 5 / 60.0).ToString(cumulus.WindRunFormat) + cumulus.Units.WindRunText); + + WindRunToday += WindAverage * WindRunHourMult[cumulus.Units.Wind] * 5 / 60.0; + + // update heating/cooling degree days + UpdateDegreeDays(5); + + // update dominant wind bearing + CalculateDominantWindBearing(Bearing, WindAverage, 5); + + CheckForWindrunHighLow(rec.Key); + + //bw?.ReportProgress((totalentries - datalist.Count) * 100 / totalentries, "processing"); + + //UpdateDatabase(timestamp.ToUniversalTime(), historydata.interval, false); + + cumulus.DoLogFile(rec.Key, false); + if (cumulus.StationOptions.LogExtraSensors) cumulus.DoExtraLogFile(rec.Key); + + //AddRecentDataEntry(timestamp, WindAverage, RecentMaxGust, WindLatest, Bearing, AvgBearing, + // OutdoorTemperature, WindChill, OutdoorDewpoint, HeatIndex, + // OutdoorHumidity, Pressure, RainToday, SolarRad, UV, Raincounter, FeelsLike, Humidex); + + AddRecentDataWithAq(rec.Key, WindAverage, RecentMaxGust, WindLatest, Bearing, AvgBearing, OutdoorTemperature, WindChill, OutdoorDewpoint, HeatIndex, + OutdoorHumidity, Pressure, RainToday, SolarRad, UV, Raincounter, FeelsLike, Humidex, ApparentTemperature, IndoorTemperature, IndoorHumidity, CurrentSolarMax, RainRate); + + if (cumulus.StationOptions.CalculatedET && rec.Key.Minute == 0) + { + // Start of a new hour, and we want to calculate ET in Cumulus + CalculateEvaoptranspiration(rec.Key); + } + + DoTrendValues(rec.Key); + UpdatePressureTrendString(); + UpdateStatusPanel(rec.Key); + cumulus.AddToWebServiceLists(rec.Key); + + } + } + + public string ProcessData(IHttpContext context, bool main, DateTime? ts = null) { /* * Ecowitt doc: @@ -107,11 +719,7 @@ POST Parameters - all fields are URL escaped */ - DateTime recDate; - var procName = main ? "ProcessData" : "ProcessExtraData"; - var thisStation = main ? this : station; - if (starting || stopping) { @@ -132,13 +740,51 @@ POST Parameters - all fields are URL escaped cumulus.LogDataMessage($"{procName}: Payload = {text}"); - var data = HttpUtility.ParseQueryString(text); + // force the wind chill calculation as it is not present in historic data + var chillSave = cumulus.StationOptions.CalculatedWC; + cumulus.StationOptions.CalculatedWC = true; + + var retVal = ApplyData(text, main, ts); + + // restore wind chill setting + cumulus.StationOptions.CalculatedWC = chillSave; + + if (retVal != "") + { + context.Response.StatusCode = 500; + return retVal; + } + } + catch (Exception ex) + { + cumulus.LogMessage($"{procName}: Error - {ex.Message}"); + context.Response.StatusCode = 500; + return "Failed: General error - " + ex.Message; + } + + cumulus.LogDebugMessage($"{procName}: Complete"); + + context.Response.StatusCode = 200; + return "success"; + } + + public string ApplyData(string dataString, bool main, DateTime? ts = null) + { + var procName = main ? "ApplyData" : "ApplyExtraData"; + var thisStation = main ? this : station; + + try + { + DateTime recDate; + - // We will ignore the dateutc field, this is "live" data so just use "now" to avoid any clock issues - recDate = DateTime.Now; + var data = HttpUtility.ParseQueryString(dataString); + + // We will ignore the dateutc field if this "live" data to avoid any clock issues + recDate = ts.HasValue ? ts.Value : DateTime.Now; // we only really want to do this once - if (reportStationType) + if (reportStationType && !ts.HasValue) { cumulus.LogDebugMessage($"{procName}: StationType = {data["stationtype"]}, Model = {data["model"]}, Frequency = {data["freq"]}Hz"); reportStationType = false; @@ -197,7 +843,6 @@ POST Parameters - all fields are URL escaped catch (Exception ex) { cumulus.LogMessage("ProcessData: Error in Wind data - " + ex.Message); - context.Response.StatusCode = 500; return "Failed: Error in wind data - " + ex.Message; } @@ -235,7 +880,6 @@ POST Parameters - all fields are URL escaped catch (Exception ex) { cumulus.LogMessage("ProcessData: Error in Humidity data - " + ex.Message); - context.Response.StatusCode = 500; return "Failed: Error in humidity data - " + ex.Message; } @@ -262,7 +906,6 @@ POST Parameters - all fields are URL escaped catch (Exception ex) { cumulus.LogMessage("ProcessData: Error in Pressure data - " + ex.Message); - context.Response.StatusCode = 500; return "Failed: Error in baro pressure data - " + ex.Message; } @@ -287,7 +930,6 @@ POST Parameters - all fields are URL escaped catch (Exception ex) { cumulus.LogMessage("ProcessData: Error in Indoor temp data - " + ex.Message); - context.Response.StatusCode = 500; return "Failed: Error in indoor temp data - " + ex.Message; } @@ -312,7 +954,6 @@ POST Parameters - all fields are URL escaped catch (Exception ex) { cumulus.LogMessage("ProcessData: Error in Outdoor temp data - " + ex.Message); - context.Response.StatusCode = 500; return "Failed: Error in outdoor temp data - " + ex.Message; } @@ -360,7 +1001,6 @@ POST Parameters - all fields are URL escaped catch (Exception ex) { cumulus.LogMessage("ProcessData: Error in Rain data - " + ex.Message); - context.Response.StatusCode = 500; return "Failed: Error in rainfall data - " + ex.Message; } @@ -389,7 +1029,6 @@ POST Parameters - all fields are URL escaped catch (Exception ex) { cumulus.LogMessage("ProcessData: Error in Dew point data - " + ex.Message); - context.Response.StatusCode = 500; return "Failed: Error in dew point data - " + ex.Message; } @@ -420,7 +1059,6 @@ POST Parameters - all fields are URL escaped catch (Exception ex) { cumulus.LogMessage("ProcessData: Error in Dew point data - " + ex.Message); - context.Response.StatusCode = 500; return "Failed: Error in dew point data - " + ex.Message; } @@ -650,6 +1288,7 @@ POST Parameters - all fields are URL escaped wh65batt wh68batt wh80batt + wh90batt batt[1-8] (wh31) soilbatt[1-8] (wh51) pm25batt[1-4] (wh41/wh43) @@ -681,10 +1320,15 @@ POST Parameters - all fields are URL escaped // === Firmware Version === try { - var fwString = data["stationtype"].Split(new string[] { "_V" }, StringSplitOptions.None); - if (fwString.Length > 1) + if (data["stationtype"] != null) { - GW1000FirmwareVersion = fwString[1]; + var fwString = data["stationtype"].Split(new string[] { "_V" }, StringSplitOptions.None); + if (fwString.Length > 1) + { + // bug fix for WS90 which sends "stationtype=GW2000A_V2.1.0, runtime=253500" + var str = fwString[1].Split(new string[] { ", " }, StringSplitOptions.None)[0]; + GW1000FirmwareVersion = str; + } } } catch (Exception ex) @@ -701,14 +1345,10 @@ POST Parameters - all fields are URL escaped catch (Exception ex) { cumulus.LogMessage($"{procName}: Error - {ex.Message}"); - context.Response.StatusCode = 500; return "Failed: General error - " + ex.Message; } - cumulus.LogDebugMessage($"{procName}: Complete"); - - context.Response.StatusCode = 200; - return "success"; + return ""; } private void ProcessExtraTemps(NameValueCollection data, WeatherStation station) @@ -926,6 +1566,7 @@ private void ProcessBatteries(NameValueCollection data) lowBatt = lowBatt || (data["wh65batt"] != null && data["wh65batt"] == "1"); lowBatt = lowBatt || (data["wh68batt"] != null && Convert.ToDouble(data["wh68batt"], invNum) <= 1.2); lowBatt = lowBatt || (data["wh80batt"] != null && Convert.ToDouble(data["wh80batt"], invNum) <= 1.2); + lowBatt = lowBatt || (data["wh90batt"] != null && Convert.ToDouble(data["wh90batt"], invNum) <= 2.4); for (var i = 1; i < 5; i++) { lowBatt = lowBatt || (data["batt" + i] != null && data["batt" + i] == "1"); @@ -957,5 +1598,6 @@ private void ProcessExtraDewPoint(NameValueCollection data, WeatherStation stati } } } + } } diff --git a/CumulusMX/Properties/AssemblyInfo.cs b/CumulusMX/Properties/AssemblyInfo.cs index b77fc2b7..1b93e37f 100644 --- a/CumulusMX/Properties/AssemblyInfo.cs +++ b/CumulusMX/Properties/AssemblyInfo.cs @@ -5,11 +5,11 @@ // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. -[assembly: AssemblyTitle("Cumulus MX")] -[assembly: AssemblyDescription("Version 3.14.3 - Build 3163")] +[assembly: AssemblyTitle("Cumulus MX BETA")] +[assembly: AssemblyDescription("Version 3.15.0 - Build 3164")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("Cumulus MX")] +[assembly: AssemblyProduct("Cumulus MX BETA")] [assembly: AssemblyCopyright("Copyright © 2015-2022 Cumulus MX")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -32,5 +32,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("3.14.3.3163")] -[assembly: AssemblyFileVersion("3.14.3.3163")] +[assembly: AssemblyVersion("3.15.0.3164")] +[assembly: AssemblyFileVersion("3.15.0.3164")] diff --git a/CumulusMX/StationSettings.cs b/CumulusMX/StationSettings.cs index aa2d1292..9ca7602f 100644 --- a/CumulusMX/StationSettings.cs +++ b/CumulusMX/StationSettings.cs @@ -131,6 +131,13 @@ internal string GetAlpacaFormData() macaddress = cumulus.Gw1000MacAddress }; + var ecowittapi = new JsonStationSettingsEcowittApi() + { + applicationkey = cumulus.EcowittApplicationKey, + userkey = cumulus.EcowittUserApiKey, + mac = cumulus.EcowittMacAddress + }; + var logrollover = new JsonStationSettingsLogRollover() { time = cumulus.RolloverHour == 9 ? "9am" : "midnight", @@ -429,6 +436,7 @@ internal string GetAlpacaFormData() easyw = easyweather, imet = imet, wmr928 = wmr928, + ecowittapi = ecowittapi, Options = options, Forecast = forecast, Solar = solar, @@ -1038,6 +1046,24 @@ internal string UpdateConfig(IHttpContext context) context.Response.StatusCode = 500; } + // Ecowitt API + try + { + if (settings.ecowittapi != null) + { + cumulus.EcowittApplicationKey = settings.ecowittapi.applicationkey; + cumulus.EcowittUserApiKey = settings.ecowittapi.userkey; + cumulus.EcowittMacAddress = settings.ecowittapi.mac; + } + } + catch (Exception ex) + { + var msg = "Error processing Ecowitt API settings: " + ex.Message; + cumulus.LogMessage(msg); + errorMsg += msg + "\n\n"; + context.Response.StatusCode = 500; + } + // Units try { @@ -1332,6 +1358,7 @@ internal class JsonStationSettingsData public JsonStationSettingsEasyWeather easyw { get; set; } public JsonStationSettingsImet imet { get; set; } public JsonStationSettingsWMR928 wmr928 { get; set; } + public JsonStationSettingsEcowittApi ecowittapi { get; set; } public JsonStationSettingsOptions Options { get; set; } public JsonStationSettingsForecast Forecast { get; set; } public JsonStationSettingsSolar Solar { get; set; } @@ -1492,6 +1519,13 @@ internal class JSonStationSettingsGw1000Conn public string macaddress { get; set; } } + internal class JsonStationSettingsEcowittApi + { + public string applicationkey { get; set; } + public string userkey { get; set; } + public string mac { get; set; } + } + internal class JsonStationSettingsWMR928 { public string comportname { get; set; } diff --git a/CumulusMX/webtags.cs b/CumulusMX/webtags.cs index 5266099a..e6c8b263 100644 --- a/CumulusMX/webtags.cs +++ b/CumulusMX/webtags.cs @@ -3875,42 +3875,42 @@ private string TagLightningStrikesToday(Dictionary tagParams) private string TagLeafWetness1(Dictionary tagParams) { - return station.LeafWetness1.ToString(cumulus.LeafWetFormat); + return CheckRcDp(station.LeafWetness1, tagParams, 1); } private string TagLeafWetness2(Dictionary tagParams) { - return station.LeafWetness2.ToString(cumulus.LeafWetFormat); + return CheckRcDp(station.LeafWetness2, tagParams, 1); } private string TagLeafWetness3(Dictionary tagParams) { - return station.LeafWetness3.ToString(cumulus.LeafWetFormat); + return CheckRcDp(station.LeafWetness3, tagParams, 1); } private string TagLeafWetness4(Dictionary tagParams) { - return station.LeafWetness4.ToString(cumulus.LeafWetFormat); + return CheckRcDp(station.LeafWetness4, tagParams, 1); } private string TagLeafWetness5(Dictionary tagParams) { - return station.LeafWetness5.ToString(cumulus.LeafWetFormat); + return CheckRcDp(station.LeafWetness5, tagParams, 1); } private string TagLeafWetness6(Dictionary tagParams) { - return station.LeafWetness6.ToString(cumulus.LeafWetFormat); + return CheckRcDp(station.LeafWetness6, tagParams, 1); } private string TagLeafWetness7(Dictionary tagParams) { - return station.LeafWetness7.ToString(cumulus.LeafWetFormat); + return CheckRcDp(station.LeafWetness7, tagParams, 1); } private string TagLeafWetness8(Dictionary tagParams) { - return station.LeafWetness8.ToString(cumulus.LeafWetFormat); + return CheckRcDp(station.LeafWetness8, tagParams, 1); } diff --git a/Updates.txt b/Updates.txt index 1ed9b709..f40cd4ca 100644 --- a/Updates.txt +++ b/Updates.txt @@ -1,7 +1,15 @@ -3.14.3 - b3163 +3.15.0 - b3164 - BETA —————————————— - Fix: Prevent real time processing occurring before first data has been received - Fix: Davis WLL: Add missing decode of THSW from current data +- Fix: Daily high humidex time being logged as high apparent temp time + +- Change: Leaf wetness web tags <#LeafWetness[1-8]> now accept the rc and dp parameters + +- New: Adds experimental support for Ecowitt stations (GW1000 & HTTP) historic catch-up +- New: HTTP (Ecowitt) station: adds support for WS990 battery state decoding + +- New: Additional web tag for annual ET total <#AnnualET> From 251116b44f699b994a628359aa0f7fcaa8e54abd Mon Sep 17 00:00:00 2001 From: Mark Crossley <1196094+mcrossley@users.noreply.github.com> Date: Fri, 28 Jan 2022 23:46:44 +0000 Subject: [PATCH 5/8] Beta 2 Fix GW1000 start-up issue! Fix possible duplication of the last log entry from the previous run of MX --- CumulusMX/GW1000Station.cs | 129 ++++++++++++--------------- CumulusMX/HttpStationEcowitt.cs | 3 +- CumulusMX/Properties/AssemblyInfo.cs | 6 +- Updates.txt | 6 +- 4 files changed, 69 insertions(+), 75 deletions(-) diff --git a/CumulusMX/GW1000Station.cs b/CumulusMX/GW1000Station.cs index 9a96425a..5fc83f70 100644 --- a/CumulusMX/GW1000Station.cs +++ b/CumulusMX/GW1000Station.cs @@ -314,6 +314,8 @@ public GW1000Station(Cumulus cumulus) : base(cumulus) } } + LoadLastHoursFromDataLogs(cumulus.LastUpdateTime); + Task.Run(getAndProcessHistoryData); } @@ -365,6 +367,7 @@ private TcpClient OpenTcpPort() return client; } + public override void Start() { tenMinuteChanged = true; @@ -376,67 +379,64 @@ public override void Start() tmrDataWatchdog.AutoReset = true; tmrDataWatchdog.Start(); - LoadLastHoursFromDataLogs(cumulus.LastUpdateTime); - DoTrendValues(DateTime.Now); // WLL does not provide a forecast string, so use the Cumulus forecast cumulus.UseCumulusForecast = true; // just incase we did not catch-up any history DoDayResetIfNeeded(); + DoTrendValues(DateTime.Now); cumulus.LogMessage("Starting GW1000"); - - cumulus.StartTimersAndSensors(); - StartLoop(); - bw = new BackgroundWorker(); + cumulus.StartTimersAndSensors(); - try + Task.Run(() => { - while (!stop) + try { - if (connectedOk) + while (!stop) { - GetLiveData(); - - // at the start of every 10 minutes to trigger battery status check - var minute = DateTime.Now.Minute; - if (minute != lastMinute) + if (connectedOk) { - lastMinute = minute; - if ((minute % 10) == 0) + GetLiveData(); + + // at the start of every 10 minutes to trigger battery status check + var minute = DateTime.Now.Minute; + if (minute != lastMinute) { - GetSensorIdsNew(); + lastMinute = minute; + if ((minute % 10) == 0) + { + GetSensorIdsNew(); + } } } - } - else - { - cumulus.LogMessage("Attempting to reconnect to GW1000..."); - socket = OpenTcpPort(); - connectedOk = socket != null; - if (connectedOk) + else { - cumulus.LogMessage("Reconnected to GW1000"); - GetLiveData(); + cumulus.LogMessage("Attempting to reconnect to GW1000..."); + socket = OpenTcpPort(); + connectedOk = socket != null; + if (connectedOk) + { + cumulus.LogMessage("Reconnected to GW1000"); + GetLiveData(); + } } + Thread.Sleep(updateRate); } - Thread.Sleep(updateRate); } - } - // Catch the ThreadAbortException - catch (ThreadAbortException) - { - } - finally - { - if (socket != null) + // Catch the ThreadAbortException + catch (ThreadAbortException) {} + finally { - socket.GetStream().WriteByte(10); - socket.Close(); + if (socket != null) + { + socket.GetStream().WriteByte(10); + socket.Close(); + } } - } + }); } public override void Stop() @@ -455,62 +455,51 @@ public override void Stop() } } - private void bw_DoStart(object sender, DoWorkEventArgs e) + + public override void getAndProcessHistoryData() { - cumulus.LogDebugMessage("Lock: Station waiting for lock"); + cumulus.LogDebugMessage("Lock: Station waiting for the lock"); Cumulus.syncInit.Wait(); cumulus.LogDebugMessage("Lock: Station has the lock"); - // Wait a short while for Cumulus initialisation to complete - Thread.Sleep(500); - StartLoop(); - - cumulus.LogDebugMessage("Lock: Station releasing lock"); - Cumulus.syncInit.Release(); - } - - public override void getAndProcessHistoryData() - { if (string.IsNullOrEmpty(cumulus.EcowittApplicationKey) || string.IsNullOrEmpty(cumulus.EcowittUserApiKey) || string.IsNullOrEmpty(cumulus.EcowittMacAddress)) { cumulus.LogMessage("API.GetHistoricData: Missing Ecowitt API data in the configuration, aborting!"); cumulus.LastUpdateTime = DateTime.Now; - Start(); - return; } - - int archiveRun = 0; - cumulus.LogDebugMessage("Lock: Station waiting for the lock"); - Cumulus.syncInit.Wait(); - cumulus.LogDebugMessage("Lock: Station has the lock"); - - try + else { + int archiveRun = 0; + + try + { - api = new EcowittApi(cumulus); + api = new EcowittApi(cumulus); - do + do + { + GetHistoricData(); + archiveRun++; + } while (archiveRun < maxArchiveRuns); + } + catch (Exception ex) { - GetHistoricData(); - archiveRun++; - } while (archiveRun < maxArchiveRuns); - } - catch (Exception ex) - { - cumulus.LogMessage("Exception occurred reading archive data: " + ex.Message); + cumulus.LogMessage("Exception occurred reading archive data: " + ex.Message); + } } cumulus.LogDebugMessage("Lock: Station releasing the lock"); _ = Cumulus.syncInit.Release(); - Start(); + StartLoop(); } private void GetHistoricData() { cumulus.LogMessage("GetHistoricData: Starting Historic Data Process"); - var startTime = cumulus.LastUpdateTime; + // add one minute to the time to avoid duplicating the last log entry + var startTime = cumulus.LastUpdateTime.AddMinutes(1); var endTime = DateTime.Now; // The API call is limited to fetching 24 hours of data diff --git a/CumulusMX/HttpStationEcowitt.cs b/CumulusMX/HttpStationEcowitt.cs index dce3e17e..dde41c69 100644 --- a/CumulusMX/HttpStationEcowitt.cs +++ b/CumulusMX/HttpStationEcowitt.cs @@ -146,7 +146,8 @@ private void GetHistoricData() { cumulus.LogMessage("GetHistoricData: Starting Historic Data Process"); - var startTime = cumulus.LastUpdateTime; + // add one minute to avoid duplicating the last log entry + var startTime = cumulus.LastUpdateTime.AddMinutes(1); var endTime = DateTime.Now; // The API call is limited to fetching 24 hours of data diff --git a/CumulusMX/Properties/AssemblyInfo.cs b/CumulusMX/Properties/AssemblyInfo.cs index 1b93e37f..a29a57ab 100644 --- a/CumulusMX/Properties/AssemblyInfo.cs +++ b/CumulusMX/Properties/AssemblyInfo.cs @@ -6,7 +6,7 @@ // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("Cumulus MX BETA")] -[assembly: AssemblyDescription("Version 3.15.0 - Build 3164")] +[assembly: AssemblyDescription("Version 3.15.0 - Build 3165")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Cumulus MX BETA")] @@ -32,5 +32,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("3.15.0.3164")] -[assembly: AssemblyFileVersion("3.15.0.3164")] +[assembly: AssemblyVersion("3.15.0.3165")] +[assembly: AssemblyFileVersion("3.15.0.3165")] diff --git a/Updates.txt b/Updates.txt index f40cd4ca..a59931ef 100644 --- a/Updates.txt +++ b/Updates.txt @@ -1,5 +1,9 @@ -3.15.0 - b3164 - BETA +3.15.0 - b3165 - BETA —————————————— +- 3165: Fix GW1000 start-up issue! +- 3165: Fix possible duplication of the last log entry from the previous run of MX + + - Fix: Prevent real time processing occurring before first data has been received - Fix: Davis WLL: Add missing decode of THSW from current data - Fix: Daily high humidex time being logged as high apparent temp time From 77bc4f2e35445eab8aa8545f7767c9bf43fa57a6 Mon Sep 17 00:00:00 2001 From: Mark Crossley <1196094+mcrossley@users.noreply.github.com> Date: Sat, 29 Jan 2022 00:13:11 +0000 Subject: [PATCH 6/8] Ecowitt Start code tweaks Ecowitt Check each historic record is later than the last update time --- CumulusMX/GW1000Station.cs | 32 ++++++------- CumulusMX/HttpStationEcowitt.cs | 71 ++++++++++++++-------------- CumulusMX/Properties/AssemblyInfo.cs | 6 +-- 3 files changed, 55 insertions(+), 54 deletions(-) diff --git a/CumulusMX/GW1000Station.cs b/CumulusMX/GW1000Station.cs index 5fc83f70..1ff259c6 100644 --- a/CumulusMX/GW1000Station.cs +++ b/CumulusMX/GW1000Station.cs @@ -548,7 +548,7 @@ private void ProcessHistoryData(EcowittApi.EcowittHistoricData data) foreach (var item in data.indoor.temperature.list) { // not present value = 140 - if (!item.Value.HasValue || item.Value == 140) + if (!item.Value.HasValue || item.Value == 140 || item.Key <= cumulus.LastUpdateTime) continue; var newItem = new EcowittApi.HistoricData(); @@ -561,7 +561,7 @@ private void ProcessHistoryData(EcowittApi.EcowittHistoricData data) { foreach (var item in data.indoor.humidity.list) { - if (!item.Value.HasValue) + if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) continue; if (buffer.ContainsKey(item.Key)) @@ -586,7 +586,7 @@ private void ProcessHistoryData(EcowittApi.EcowittHistoricData data) foreach (var item in data.outdoor.temperature.list) { // not present value = 140 - if (!item.Value.HasValue || item.Value == 140) + if (!item.Value.HasValue || item.Value == 140 || item.Key <= cumulus.LastUpdateTime) continue; if (buffer.ContainsKey(item.Key)) @@ -607,7 +607,7 @@ private void ProcessHistoryData(EcowittApi.EcowittHistoricData data) { foreach (var item in data.outdoor.humidity.list) { - if (!item.Value.HasValue) + if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) continue; if (buffer.ContainsKey(item.Key)) @@ -627,7 +627,7 @@ private void ProcessHistoryData(EcowittApi.EcowittHistoricData data) { foreach (var item in data.outdoor.dew_point.list) { - if (!item.Value.HasValue) + if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) continue; if (buffer.ContainsKey(item.Key)) @@ -651,7 +651,7 @@ private void ProcessHistoryData(EcowittApi.EcowittHistoricData data) { foreach (var item in data.wind.wind_speed.list) { - if (!item.Value.HasValue) + if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) continue; if (buffer.ContainsKey(item.Key)) @@ -672,7 +672,7 @@ private void ProcessHistoryData(EcowittApi.EcowittHistoricData data) { foreach (var item in data.wind.wind_gust.list) { - if (!item.Value.HasValue) + if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) continue; if (buffer.ContainsKey(item.Key)) @@ -692,7 +692,7 @@ private void ProcessHistoryData(EcowittApi.EcowittHistoricData data) { foreach (var item in data.wind.wind_direction.list) { - if (!item.Value.HasValue) + if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) continue; if (buffer.ContainsKey(item.Key)) @@ -716,7 +716,7 @@ private void ProcessHistoryData(EcowittApi.EcowittHistoricData data) { foreach (var item in data.pressure.relative.list) { - if (!item.Value.HasValue) + if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) continue; if (buffer.ContainsKey(item.Key)) @@ -740,7 +740,7 @@ private void ProcessHistoryData(EcowittApi.EcowittHistoricData data) { foreach (var item in data.solar_and_uvi.solar.list) { - if (!item.Value.HasValue) + if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) continue; if (buffer.ContainsKey(item.Key)) @@ -760,7 +760,7 @@ private void ProcessHistoryData(EcowittApi.EcowittHistoricData data) { foreach (var item in data.solar_and_uvi.uvi.list) { - if (!item.Value.HasValue) + if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) continue; if (buffer.ContainsKey(item.Key)) @@ -843,7 +843,7 @@ private void ProcessHistoryData(EcowittApi.EcowittHistoricData data) { foreach (var item in srcTH.temperature.list) { - if (!item.Value.HasValue) + if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) continue; if (buffer.ContainsKey(item.Key)) @@ -863,7 +863,7 @@ private void ProcessHistoryData(EcowittApi.EcowittHistoricData data) { foreach (var item in srcTH.humidity.list) { - if (!item.Value.HasValue) + if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) continue; if (buffer.ContainsKey(item.Key)) @@ -885,7 +885,7 @@ private void ProcessHistoryData(EcowittApi.EcowittHistoricData data) // moisture foreach (var item in srcSoil.soilmoisture.list) { - if (!item.Value.HasValue) + if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) continue; if (buffer.ContainsKey(item.Key)) @@ -906,7 +906,7 @@ private void ProcessHistoryData(EcowittApi.EcowittHistoricData data) // temperature foreach (var item in srcTemp.temperature.list) { - if (!item.Value.HasValue) + if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) continue; if (buffer.ContainsKey(item.Key)) @@ -927,7 +927,7 @@ private void ProcessHistoryData(EcowittApi.EcowittHistoricData data) // wetness foreach (var item in srcLeaf.leaf_wetness.list) { - if (!item.Value.HasValue) + if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) continue; if (buffer.ContainsKey(item.Key)) diff --git a/CumulusMX/HttpStationEcowitt.cs b/CumulusMX/HttpStationEcowitt.cs index dde41c69..15caeb0f 100644 --- a/CumulusMX/HttpStationEcowitt.cs +++ b/CumulusMX/HttpStationEcowitt.cs @@ -107,39 +107,40 @@ public override void Stop() public override void getAndProcessHistoryData() { + cumulus.LogDebugMessage("Lock: Station waiting for the lock"); + Cumulus.syncInit.Wait(); + cumulus.LogDebugMessage("Lock: Station has the lock"); + if (string.IsNullOrEmpty(cumulus.EcowittApplicationKey) || string.IsNullOrEmpty(cumulus.EcowittUserApiKey) || string.IsNullOrEmpty(cumulus.EcowittMacAddress)) { cumulus.LogMessage("API.GetHistoricData: Missing Ecowitt API data in the configuration, aborting!"); cumulus.LastUpdateTime = DateTime.Now; - Start(); - return; } - - int archiveRun = 0; - cumulus.LogDebugMessage("Lock: Station waiting for the lock"); - Cumulus.syncInit.Wait(); - cumulus.LogDebugMessage("Lock: Station has the lock"); - - try + else { + int archiveRun = 0; - api = new EcowittApi(cumulus); + try + { + + api = new EcowittApi(cumulus); - do + do + { + GetHistoricData(); + archiveRun++; + } while (archiveRun < maxArchiveRuns); + } + catch (Exception ex) { - GetHistoricData(); - archiveRun++; - } while (archiveRun < maxArchiveRuns); - } - catch (Exception ex) - { - cumulus.LogMessage("Exception occurred reading archive data: " + ex.Message); + cumulus.LogMessage("Exception occurred reading archive data: " + ex.Message); + } } cumulus.LogDebugMessage("Lock: Station releasing the lock"); _ = Cumulus.syncInit.Release(); - Start(); + StartLoop(); } private void GetHistoricData() @@ -198,7 +199,7 @@ private void ProcessHistoryData(EcowittApi.EcowittHistoricData data) foreach (var item in data.indoor.temperature.list) { // not present value = 140 - if (!item.Value.HasValue || item.Value == 140) + if (!item.Value.HasValue || item.Value == 140 || item.Key <= cumulus.LastUpdateTime) continue; var newItem = new EcowittApi.HistoricData(); @@ -211,7 +212,7 @@ private void ProcessHistoryData(EcowittApi.EcowittHistoricData data) { foreach (var item in data.indoor.humidity.list) { - if (!item.Value.HasValue) + if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) continue; if (buffer.ContainsKey(item.Key)) @@ -236,7 +237,7 @@ private void ProcessHistoryData(EcowittApi.EcowittHistoricData data) foreach (var item in data.outdoor.temperature.list) { // not present value = 140 - if (!item.Value.HasValue || item.Value == 140) + if (!item.Value.HasValue || item.Value == 140 || item.Key <= cumulus.LastUpdateTime) continue; if (buffer.ContainsKey(item.Key)) @@ -257,7 +258,7 @@ private void ProcessHistoryData(EcowittApi.EcowittHistoricData data) { foreach (var item in data.outdoor.humidity.list) { - if (!item.Value.HasValue) + if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) continue; if (buffer.ContainsKey(item.Key)) @@ -277,7 +278,7 @@ private void ProcessHistoryData(EcowittApi.EcowittHistoricData data) { foreach (var item in data.outdoor.dew_point.list) { - if (!item.Value.HasValue) + if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) continue; if (buffer.ContainsKey(item.Key)) @@ -301,7 +302,7 @@ private void ProcessHistoryData(EcowittApi.EcowittHistoricData data) { foreach (var item in data.wind.wind_speed.list) { - if (!item.Value.HasValue) + if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) continue; if (buffer.ContainsKey(item.Key)) @@ -322,7 +323,7 @@ private void ProcessHistoryData(EcowittApi.EcowittHistoricData data) { foreach (var item in data.wind.wind_gust.list) { - if (!item.Value.HasValue) + if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) continue; if (buffer.ContainsKey(item.Key)) @@ -342,7 +343,7 @@ private void ProcessHistoryData(EcowittApi.EcowittHistoricData data) { foreach (var item in data.wind.wind_direction.list) { - if (!item.Value.HasValue) + if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) continue; if (buffer.ContainsKey(item.Key)) @@ -366,7 +367,7 @@ private void ProcessHistoryData(EcowittApi.EcowittHistoricData data) { foreach (var item in data.pressure.relative.list) { - if (!item.Value.HasValue) + if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) continue; if (buffer.ContainsKey(item.Key)) @@ -390,7 +391,7 @@ private void ProcessHistoryData(EcowittApi.EcowittHistoricData data) { foreach (var item in data.solar_and_uvi.solar.list) { - if (!item.Value.HasValue) + if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) continue; if (buffer.ContainsKey(item.Key)) @@ -410,7 +411,7 @@ private void ProcessHistoryData(EcowittApi.EcowittHistoricData data) { foreach (var item in data.solar_and_uvi.uvi.list) { - if (!item.Value.HasValue) + if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) continue; if (buffer.ContainsKey(item.Key)) @@ -493,7 +494,7 @@ private void ProcessHistoryData(EcowittApi.EcowittHistoricData data) { foreach (var item in srcTH.temperature.list) { - if (!item.Value.HasValue) + if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) continue; if (buffer.ContainsKey(item.Key)) @@ -513,7 +514,7 @@ private void ProcessHistoryData(EcowittApi.EcowittHistoricData data) { foreach (var item in srcTH.humidity.list) { - if (!item.Value.HasValue) + if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) continue; if (buffer.ContainsKey(item.Key)) @@ -535,7 +536,7 @@ private void ProcessHistoryData(EcowittApi.EcowittHistoricData data) // moisture foreach (var item in srcSoil.soilmoisture.list) { - if (!item.Value.HasValue) + if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) continue; if (buffer.ContainsKey(item.Key)) @@ -556,7 +557,7 @@ private void ProcessHistoryData(EcowittApi.EcowittHistoricData data) // temperature foreach (var item in srcTemp.temperature.list) { - if (!item.Value.HasValue) + if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) continue; if (buffer.ContainsKey(item.Key)) @@ -577,7 +578,7 @@ private void ProcessHistoryData(EcowittApi.EcowittHistoricData data) // wetness foreach (var item in srcLeaf.leaf_wetness.list) { - if (!item.Value.HasValue) + if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) continue; if (buffer.ContainsKey(item.Key)) diff --git a/CumulusMX/Properties/AssemblyInfo.cs b/CumulusMX/Properties/AssemblyInfo.cs index a29a57ab..4da48e97 100644 --- a/CumulusMX/Properties/AssemblyInfo.cs +++ b/CumulusMX/Properties/AssemblyInfo.cs @@ -6,7 +6,7 @@ // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("Cumulus MX BETA")] -[assembly: AssemblyDescription("Version 3.15.0 - Build 3165")] +[assembly: AssemblyDescription("Version 3.15.0 - Build 3166")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Cumulus MX BETA")] @@ -32,5 +32,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("3.15.0.3165")] -[assembly: AssemblyFileVersion("3.15.0.3165")] +[assembly: AssemblyVersion("3.15.0.3166")] +[assembly: AssemblyFileVersion("3.15.0.3166")] From f43381330468e90007c5e088d32bf3e6fc74426f Mon Sep 17 00:00:00 2001 From: Mark Crossley <1196094+mcrossley@users.noreply.github.com> Date: Sun, 30 Jan 2022 20:26:20 +0000 Subject: [PATCH 7/8] final v3.15.0 updates --- CumulusMX/Cumulus.cs | 4 +- CumulusMX/EcowittApi.cs | 1229 +++++++++++++++++++++++++- CumulusMX/GW1000Station.cs | 875 +----------------- CumulusMX/HttpStationEcowitt.cs | 550 +----------- CumulusMX/Properties/AssemblyInfo.cs | 10 +- CumulusMX/WeatherStation.cs | 2 +- Updates.txt | 7 +- 7 files changed, 1202 insertions(+), 1475 deletions(-) diff --git a/CumulusMX/Cumulus.cs b/CumulusMX/Cumulus.cs index 4206d849..1130056c 100644 --- a/CumulusMX/Cumulus.cs +++ b/CumulusMX/Cumulus.cs @@ -4208,10 +4208,10 @@ private void ReadIniFile() EcowittExtraUseCo2= ini.GetValue("GW1000", "ExtraSensorUseCo2", true); EcowittExtraUseLightning = ini.GetValue("GW1000", "ExtraSensorUseLightning", true); EcowittExtraUseLeak= ini.GetValue("GW1000", "ExtraSensorUseLeak", true); - + // api EcowittApplicationKey = ini.GetValue("GW1000", "EcowittAppKey", ""); EcowittUserApiKey = ini.GetValue("GW1000", "EcowittUserKey", ""); - EcowittMacAddress = ini.GetValue("Gw1000", "EcowittMacAddress", ""); + EcowittMacAddress = ini.GetValue("GW1000", "EcowittMacAddress", ""); if (string.IsNullOrEmpty(EcowittMacAddress) && !string.IsNullOrEmpty(Gw1000MacAddress)) { EcowittMacAddress = Gw1000MacAddress; diff --git a/CumulusMX/EcowittApi.cs b/CumulusMX/EcowittApi.cs index 90cb0a4a..1d20cb7f 100644 --- a/CumulusMX/EcowittApi.cs +++ b/CumulusMX/EcowittApi.cs @@ -15,6 +15,7 @@ namespace CumulusMX internal class EcowittApi { private Cumulus cumulus; + private WeatherStation station; private readonly NumberFormatInfo invNum = CultureInfo.InvariantCulture.NumberFormat; private static readonly HttpClientHandler httpHandler = new HttpClientHandler(); @@ -22,9 +23,10 @@ internal class EcowittApi private static string historyUrl = "https://api.ecowitt.net/api/v3/device/history?"; - public EcowittApi(Cumulus cuml) + public EcowittApi(Cumulus cuml, WeatherStation stn) { cumulus = cuml; + station = stn; // Configure a web proxy if required if (!string.IsNullOrEmpty(cumulus.HTTPProxyName)) @@ -36,15 +38,33 @@ public EcowittApi(Cumulus cuml) httpHandler.Credentials = new NetworkCredential(cumulus.HTTPProxyUser, cumulus.HTTPProxyPassword); } } + + // Let's decode the Unix ts to DateTime + JsConfig.Init(new Config { + DateHandler = DateHandler.UnixTime + }); + + // override the default deserializer which returns a UTC time to return a local time + JsConfig.DeSerializeFn = datetimeStr => + { + if (string.IsNullOrWhiteSpace(datetimeStr)) + { + return DateTime.MinValue; + } + + if (long.TryParse(datetimeStr, out var date)) + { + return Utils.FromUnixTime(date); + } + + return DateTime.MinValue; + }; } - internal bool GetHistoricData(DateTime startTime, DateTime endTime, string[] callbacks, out EcowittHistoricData data) + internal bool GetHistoricData(DateTime startTime, DateTime endTime) { - // Let's decode the Unix ts to DateTime - JsConfig.DateHandler = DateHandler.UnixTime; - - data = new EcowittHistoricData(); + var data = new EcowittHistoricData(); cumulus.LogMessage("API.GetHistoricData: Get Ecowitt Historic Data"); @@ -59,7 +79,7 @@ internal bool GetHistoricData(DateTime startTime, DateTime endTime, string[] cal sb.Append($"application_key={cumulus.EcowittApplicationKey}"); sb.Append($"&api_key={cumulus.EcowittUserApiKey}"); - sb.Append($"&mac={cumulus.EcowittMacAddress}&"); + sb.Append($"&mac={cumulus.EcowittMacAddress}"); sb.Append($"&start_date={startTime.ToString("yyyy-MM-dd'%20'HH:mm:ss")}"); sb.Append($"&end_date={endTime.ToString("yyyy-MM-dd'%20'HH:mm:ss")}"); @@ -72,6 +92,53 @@ internal bool GetHistoricData(DateTime startTime, DateTime endTime, string[] cal // temp_ch[1-8] // leaf_ch[1-8] // batt + var callbacks = new string[] + { + "indoor", + "outdoor", + "wind", + "pressure", + "solar_and_uvi", + "temp_and_humidity_ch1", + "temp_and_humidity_ch2", + "temp_and_humidity_ch3", + "temp_and_humidity_ch4", + "temp_and_humidity_ch5", + "temp_and_humidity_ch6", + "temp_and_humidity_ch7", + "temp_and_humidity_ch8", + "soil_ch1", + "soil_ch2", + "soil_ch3", + "soil_ch4", + "soil_ch5", + "soil_ch6", + "soil_ch7", + "soil_ch8", + "temp_ch1", + "temp_ch2", + "temp_ch3", + "temp_ch4", + "temp_ch5", + "temp_ch6", + "temp_ch7", + "temp_ch8", + "leaf_ch1", + "leaf_ch2", + "leaf_ch3", + "leaf_ch4", + "leaf_ch5", + "leaf_ch6", + "leaf_ch7", + "leaf_ch8", + "indoor_co2", + "co2_aqi_combo", + "pm25_ch1", + "pm25_ch2", + "pm25_ch3", + "pm25_ch4" + }; + sb.Append("&call_back="); foreach (var cb in callbacks) sb.Append(cb + ","); @@ -83,65 +150,1127 @@ internal bool GetHistoricData(DateTime startTime, DateTime endTime, string[] cal var url = sb.ToString(); - var logUrl = url.Replace(cumulus.EcowittApplicationKey, "<>").Replace(cumulus.EcowittUserApiKey, "<>"); + var logUrl = url.Replace(cumulus.EcowittApplicationKey, "<>").Replace(cumulus.EcowittUserApiKey, "<>"); cumulus.LogDebugMessage($"Ecowitt URL = {logUrl}"); try { string responseBody; int responseCode; + int retries = 3; + bool success = false; + do + { + // we want to do this synchronously, so .Result + using (HttpResponseMessage response = httpClient.GetAsync(url).Result) + { + responseBody = response.Content.ReadAsStringAsync().Result; + responseCode = (int)response.StatusCode; + cumulus.LogDebugMessage($"API.GetHistoricData: Ecowitt API Historic Response code: {responseCode}"); + cumulus.LogDataMessage($"API.GetHistoricData: Ecowitt API Historic Response: {responseBody}"); + } + + if (responseCode != 200) + { + var historyError = responseBody.FromJson(); + cumulus.LogMessage($"API.GetHistoricData: Ecowitt API Historic Error: {historyError.code}, {historyError.msg}"); + cumulus.LogConsoleMessage($" - Error {historyError.code}: {historyError.msg}", ConsoleColor.Red); + cumulus.LastUpdateTime = endTime; + return false; + } + + + if (responseBody == "{}") + { + cumulus.LogMessage("API.GetHistoricData: Ecowitt API Historic: No data was returned."); + cumulus.LogConsoleMessage(" - No historic data available"); + cumulus.LastUpdateTime = endTime; + return false; + } + else if (responseBody.StartsWith("{\"code\":")) // sanity check + { + // get the sensor data + var histObj = responseBody.FromJson(); + + if (histObj != null) + { + // success + if (histObj.code == 0) + { + try + { + data = histObj.data; + success = true; + } + catch (Exception ex) + { + cumulus.LogMessage("API.GetHistoricData: Error decoding the response - " + ex.Message); + return false; + } + } + else if (histObj.code == -1 || histObj.code == 45001) + { + // -1 = system busy, 45001 = rate limited + + // have we reached the retry limit? + if (--retries <= 0) + return false; + + cumulus.LogMessage("API.GetHistoricData: System Busy or Rate Limited, waiting before retry..."); + System.Threading.Thread.Sleep(1500); + } + else + { + return false; + } + } + else + { + return false; + } + + } + else // No idea what we got, dump it to the log + { + cumulus.LogMessage("API.GetHistoricData: Invalid historic message received"); + cumulus.LogDataMessage("API.GetHistoricData: Received: " + responseBody); + cumulus.LastUpdateTime = endTime; + return false; + } + + // + } while (!success); + + ProcessHistoryData(data); + + return true; + } + catch (Exception ex) + { + cumulus.LogMessage("API.GetHistoricData: Exception:"); + cumulus.LastUpdateTime = endTime; + return false; + } + + } + + private void ProcessHistoryData(EcowittApi.EcowittHistoricData data) + { + // allocate a dictionary of data objects, keyed on the timestamp + var buffer = new SortedDictionary(); + + // process each sensor type, and store them for adding to the system later + // Indoor Data + if (data.indoor != null) + { + // do the temperature + if (data.indoor.temperature.list != null) + { + foreach (var item in data.indoor.temperature.list) + { + // not present value = 140 + if (!item.Value.HasValue || item.Value == 140 || item.Key <= cumulus.LastUpdateTime) + continue; + + var newItem = new EcowittApi.HistoricData(); + newItem.IndoorTemp = item.Value; + buffer.Add(item.Key, newItem); + } + } + // do the humidity + if (data.indoor.humidity.list != null) + { + foreach (var item in data.indoor.humidity.list) + { + if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) + continue; + + if (buffer.ContainsKey(item.Key)) + { + buffer[item.Key].IndoorHum = item.Value; + } + else + { + var newItem = new EcowittApi.HistoricData(); + newItem.IndoorHum = item.Value; + buffer.Add(item.Key, newItem); + } + } + } + } + // Outdoor Data + if (data.outdoor != null) + { + // Temperature + if (data.outdoor.temperature.list != null) + { + foreach (var item in data.outdoor.temperature.list) + { + // not present value = 140 + if (!item.Value.HasValue || item.Value == 140 || item.Key <= cumulus.LastUpdateTime) + continue; + + if (buffer.ContainsKey(item.Key)) + { + buffer[item.Key].Temp = item.Value; + } + else + { + var newItem = new EcowittApi.HistoricData(); + newItem.Temp = item.Value; + buffer.Add(item.Key, newItem); + } + } + } - // we want to do this synchronously, so .Result - using (HttpResponseMessage response = httpClient.GetAsync(url).Result) + // Humidity + if (data.outdoor.humidity.list != null) { - responseBody = response.Content.ReadAsStringAsync().Result; - responseCode = (int)response.StatusCode; - cumulus.LogDebugMessage($"API.GetHistoricData: Ecowitt API Historic Response code: {responseCode}"); - cumulus.LogDataMessage($"API.GetHistoricData: Ecowitt API Historic Response: {responseBody}"); + foreach (var item in data.outdoor.humidity.list) + { + if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) + continue; + + if (buffer.ContainsKey(item.Key)) + { + buffer[item.Key].Humidity = item.Value; + } + else + { + var newItem = new EcowittApi.HistoricData(); + newItem.Humidity = item.Value; + buffer.Add(item.Key, newItem); + } + } } + // Dewpoint + if (data.outdoor.dew_point.list != null) + { + foreach (var item in data.outdoor.dew_point.list) + { + if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) + continue; - if (responseCode != 200) + if (buffer.ContainsKey(item.Key)) + { + buffer[item.Key].DewPoint = item.Value; + } + else + { + var newItem = new EcowittApi.HistoricData(); + newItem.DewPoint = item.Value; + buffer.Add(item.Key, newItem); + } + } + } + } + // Wind Data + if (data.wind != null) + { + // Speed + if (data.wind.wind_speed.list != null) { - var historyError = responseBody.FromJson(); - cumulus.LogMessage($"API.GetHistoricData: Ecowitt API Historic Error: {historyError.code}, {historyError.msg}"); - cumulus.LogConsoleMessage($" - Error {historyError.code}: {historyError.msg}", ConsoleColor.Red); - cumulus.LastUpdateTime = endTime; - return false; + foreach (var item in data.wind.wind_speed.list) + { + if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) + continue; + + if (buffer.ContainsKey(item.Key)) + { + buffer[item.Key].WindSpd = item.Value; + } + else + { + var newItem = new EcowittApi.HistoricData(); + newItem.WindSpd = item.Value; + buffer.Add(item.Key, newItem); + } + } } + // Gust + if (data.wind.wind_gust.list != null) + { + foreach (var item in data.wind.wind_gust.list) + { + if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) + continue; - if (responseBody == "{}") + if (buffer.ContainsKey(item.Key)) + { + buffer[item.Key].WindGust = item.Value; + } + else + { + var newItem = new EcowittApi.HistoricData(); + newItem.WindGust = item.Value; + buffer.Add(item.Key, newItem); + } + } + } + // Direction + if (data.wind.wind_direction.list != null) { - cumulus.LogMessage("API.GetHistoricData: Ecowitt API Historic: No data was returned."); - cumulus.LogConsoleMessage(" - No historic data available"); - cumulus.LastUpdateTime = endTime; - return false; + foreach (var item in data.wind.wind_direction.list) + { + if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) + continue; + + if (buffer.ContainsKey(item.Key)) + { + buffer[item.Key].WindDir = item.Value; + } + else + { + var newItem = new EcowittApi.HistoricData(); + newItem.WindDir = item.Value; + buffer.Add(item.Key, newItem); + } + } } - else if (responseBody.StartsWith("{\"code\":")) // sanity check + } + // Pressure Data + if (data.pressure != null) + { + // relative + if (data.pressure.relative.list != null) { - // get the sensor data - var histObj = responseBody.FromJson(); - data = histObj.data; - return true; + foreach (var item in data.pressure.relative.list) + { + if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) + continue; + + if (buffer.ContainsKey(item.Key)) + { + buffer[item.Key].Pressure = item.Value; + } + else + { + var newItem = new EcowittApi.HistoricData(); + newItem.Pressure = item.Value; + buffer.Add(item.Key, newItem); + } + } } - else // No idea what we got, dump it to the log + } + // Solar Data + if (data.solar_and_uvi != null) + { + // solar + if (data.solar_and_uvi.solar.list != null) { - cumulus.LogMessage("API.GetHistoricData: Invalid historic message received"); - cumulus.LogDataMessage("API.GetHistoricData: Received: " + responseBody); - cumulus.LastUpdateTime = endTime; - return false; + foreach (var item in data.solar_and_uvi.solar.list) + { + if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) + continue; + + if (buffer.ContainsKey(item.Key)) + { + buffer[item.Key].Solar = (int)item.Value; + } + else + { + var newItem = new EcowittApi.HistoricData(); + newItem.Solar = (int)item.Value; + buffer.Add(item.Key, newItem); + } + } + } + // uvi + if (data.solar_and_uvi.uvi.list != null) + { + foreach (var item in data.solar_and_uvi.uvi.list) + { + if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) + continue; + + if (buffer.ContainsKey(item.Key)) + { + buffer[item.Key].UVI = (int)item.Value; + } + else + { + var newItem = new EcowittApi.HistoricData(); + newItem.UVI = (int)item.Value; + buffer.Add(item.Key, newItem); + } + } + } + } + // Extra 8 channel sensors + for (var i = 1; i <= 8; i++) + { + EcowittApi.EcowittHistoricTempHum srcTH = null; + EcowittApi.EcowittHistoricDataSoil srcSoil = null; + EcowittApi.EcowittHistoricDataTemp srcTemp = null; + EcowittApi.EcowittHistoricDataLeaf srcLeaf = null; + switch (i) + { + case 1: + srcTH = data.temp_and_humidity_ch1; + srcSoil = data.soil_ch1; + srcTemp = data.temp_ch1; + srcLeaf = data.leaf_ch1; + break; + case 2: + srcTH = data.temp_and_humidity_ch2; + srcSoil = data.soil_ch2; + srcTemp = data.temp_ch2; + srcLeaf = data.leaf_ch2; + break; + case 3: + srcTH = data.temp_and_humidity_ch3; + srcSoil = data.soil_ch3; + srcTemp = data.temp_ch3; + srcLeaf = data.leaf_ch3; + break; + case 4: + srcTH = data.temp_and_humidity_ch4; + srcSoil = data.soil_ch4; + srcTemp = data.temp_ch4; + srcLeaf = data.leaf_ch4; + break; + case 5: + srcTH = data.temp_and_humidity_ch5; + srcSoil = data.soil_ch5; + srcTemp = data.temp_ch5; + srcLeaf = data.leaf_ch5; + break; + case 6: + srcTH = data.temp_and_humidity_ch6; + srcSoil = data.soil_ch6; + srcTemp = data.temp_ch6; + srcLeaf = data.leaf_ch6; + break; + case 7: + srcTH = data.temp_and_humidity_ch7; + srcSoil = data.soil_ch7; + srcTemp = data.temp_ch7; + srcLeaf = data.leaf_ch7; + break; + case 8: + srcTH = data.temp_and_humidity_ch8; + srcSoil = data.soil_ch8; + srcTemp = data.temp_ch8; + srcLeaf = data.leaf_ch8; + break; + } + + // Extra Temp/Hum Data + if (srcTH != null) + { + // temperature + if (srcTH.temperature.list != null) + { + foreach (var item in srcTH.temperature.list) + { + if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) + continue; + + if (buffer.ContainsKey(item.Key)) + { + buffer[item.Key].ExtraTemp[i - 1] = item.Value; + } + else + { + var newItem = new EcowittApi.HistoricData(); + newItem.ExtraTemp[i - 1] = item.Value; + buffer.Add(item.Key, newItem); + } + } + } + // humidity + if (srcTH.humidity.list != null) + { + foreach (var item in srcTH.humidity.list) + { + if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) + continue; + + if (buffer.ContainsKey(item.Key)) + { + buffer[item.Key].ExtraHumidity[i - 1] = item.Value; + } + else + { + var newItem = new EcowittApi.HistoricData(); + newItem.ExtraHumidity[i - 1] = item.Value; + buffer.Add(item.Key, newItem); + } + } + } + } + // Extra Soil Moisture Data + if (srcSoil != null && srcSoil.soilmoisture.list != null) + { + // moisture + foreach (var item in srcSoil.soilmoisture.list) + { + if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) + continue; + + if (buffer.ContainsKey(item.Key)) + { + buffer[item.Key].SoilMoist[i - 1] = item.Value; + } + else + { + var newItem = new EcowittApi.HistoricData(); + newItem.SoilMoist[i - 1] = item.Value; + buffer.Add(item.Key, newItem); + } + } + } + // User Temp Data + if (srcTemp != null && srcTemp.temperature.list != null) + { + // temperature + foreach (var item in srcTemp.temperature.list) + { + if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) + continue; + + if (buffer.ContainsKey(item.Key)) + { + buffer[item.Key].UserTemp[i - 1] = item.Value; + } + else + { + var newItem = new EcowittApi.HistoricData(); + newItem.UserTemp[i - 1] = item.Value; + buffer.Add(item.Key, newItem); + } + } + } + // Leaf Wetness Data + if (srcLeaf != null && srcLeaf.leaf_wetness.list != null) + { + // wetness + foreach (var item in srcLeaf.leaf_wetness.list) + { + if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) + continue; + + if (buffer.ContainsKey(item.Key)) + { + buffer[item.Key].LeafWetness[i - 1] = item.Value; + } + else + { + var newItem = new EcowittApi.HistoricData(); + newItem.LeafWetness[i - 1] = item.Value; + buffer.Add(item.Key, newItem); + } + } + } + } + // Indoor CO2 + if (data.indoor_co2 != null) + { + // CO2 + if (data.indoor_co2.co2.list != null) + { + foreach (var item in data.indoor_co2.co2.list) + { + if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) + continue; + + if (buffer.ContainsKey(item.Key)) + { + buffer[item.Key].IndoorCo2 = item.Value; + } + else + { + var newItem = new EcowittApi.HistoricData(); + newItem.IndoorCo2 = item.Value; + buffer.Add(item.Key, newItem); + } + } + } + // 24 Avg + if (data.indoor_co2.average24h.list != null) + { + foreach (var item in data.indoor_co2.average24h.list) + { + if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) + continue; + + if (buffer.ContainsKey(item.Key)) + { + buffer[item.Key].IndoorCo2hr24 = item.Value; + } + else + { + var newItem = new EcowittApi.HistoricData(); + newItem.IndoorCo2hr24 = item.Value; + buffer.Add(item.Key, newItem); + } + } + } + } + // CO2 Combi + if (data.co2_aqi_combo != null) + { + // CO2 + if (data.co2_aqi_combo.co2.list != null) + { + foreach (var item in data.co2_aqi_combo.co2.list) + { + if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) + continue; + + if (buffer.ContainsKey(item.Key)) + { + buffer[item.Key].CO2pm2p5 = item.Value; + } + else + { + var newItem = new EcowittApi.HistoricData(); + newItem.CO2pm2p5 = item.Value; + buffer.Add(item.Key, newItem); + } + } + } + // 24 Avg + if (data.co2_aqi_combo.average24h.list != null) + { + foreach (var item in data.co2_aqi_combo.average24h.list) + { + if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) + continue; + + if (buffer.ContainsKey(item.Key)) + { + buffer[item.Key].CO2pm2p5hr24 = item.Value; + } + else + { + var newItem = new EcowittApi.HistoricData(); + newItem.CO2pm2p5hr24 = item.Value; + buffer.Add(item.Key, newItem); + } + } + } + } + // pm2.5 Combi + if (data.pm25_aqi_combo != null) + { + if (data.pm25_aqi_combo.pm25.list != null) + { + foreach (var item in data.pm25_aqi_combo.pm25.list) + { + if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) + continue; + + if (buffer.ContainsKey(item.Key)) + { + buffer[item.Key].AqiComboPm25 = item.Value; + } + else + { + var newItem = new EcowittApi.HistoricData(); + newItem.AqiComboPm25 = item.Value; + buffer.Add(item.Key, newItem); + } + } + } + } + // pm10 Combi + if (data.pm10_aqi_combo != null) + { + if (data.pm10_aqi_combo.pm10.list != null) + { + foreach (var item in data.pm10_aqi_combo.pm10.list) + { + if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) + continue; + + if (buffer.ContainsKey(item.Key)) + { + buffer[item.Key].AqiComboPm10 = item.Value; + } + else + { + var newItem = new EcowittApi.HistoricData(); + newItem.AqiComboPm10 = item.Value; + buffer.Add(item.Key, newItem); + } + } + } + } + // 4 channel PM 2.5 sensors + for (var i = 1; i <= 4 ; i++) + { + EcowittHistoricDataPm25Aqi sensor = null; + switch (i) + { + case 1: + sensor = data.pm25_ch1; + break; + case 2: + sensor = data.pm25_ch2; + break; + case 3: + sensor = data.pm25_ch3; + break; + case 4: + sensor = data.pm25_ch4; + break; + } + + if (sensor != null && sensor.pm25.list != null) + { + foreach (var item in sensor.pm25.list) + { + if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) + continue; + + if (buffer.ContainsKey(item.Key)) + { + buffer[item.Key].pm25[i -1] = item.Value; + } + else + { + var newItem = new EcowittApi.HistoricData(); + newItem.pm25[i - 1] = item.Value; + buffer.Add(item.Key, newItem); + } + } + } + } + + // now we have all the data for this period, for each record create the string expected by ProcessData and get it processed + var rollHour = Math.Abs(cumulus.GetHourInc()); + var luhour = cumulus.LastUpdateTime.Hour; + var rolloverdone = luhour == rollHour; + var midnightraindone = luhour == 0; + + foreach (var rec in buffer) + { + cumulus.LogMessage("Processing data for " + rec.Key); + + var h = rec.Key.Hour; + + // if outside rollover hour, rollover yet to be done + if (h != rollHour) rolloverdone = false; + + // In rollover hour and rollover not yet done + if (h == rollHour && !rolloverdone) + { + // do rollover + cumulus.LogMessage("Day rollover " + rec.Key.ToShortTimeString()); + station.DayReset(rec.Key); + + rolloverdone = true; + } + + // Not in midnight hour, midnight rain yet to be done + if (h != 0) midnightraindone = false; + + // In midnight hour and midnight rain (and sun) not yet done + if (h == 0 && !midnightraindone) + { + station.ResetMidnightRain(rec.Key); + station.ResetSunshineHours(); + midnightraindone = true; + } + + // finally apply this data + ApplyHistoricData(rec); + + // add in archive period worth of sunshine, if sunny + if (station.SolarRad > station.CurrentSolarMax * cumulus.SolarOptions.SunThreshold / 100 && + station.SolarRad >= cumulus.SolarOptions.SolarMinimum) + station.SunshineHours += 5 / 60.0; + + + + // add in 'following interval' minutes worth of wind speed to windrun + cumulus.LogMessage("Windrun: " + station.WindAverage.ToString(cumulus.WindFormat) + cumulus.Units.WindText + " for " + 5 + " minutes = " + + (station.WindAverage * station.WindRunHourMult[cumulus.Units.Wind] * 5 / 60.0).ToString(cumulus.WindRunFormat) + cumulus.Units.WindRunText); + + station.WindRunToday += station.WindAverage * station.WindRunHourMult[cumulus.Units.Wind] * 5 / 60.0; + + // update heating/cooling degree days + station.UpdateDegreeDays(5); + + // update dominant wind bearing + station.CalculateDominantWindBearing(station.Bearing, station.WindAverage, 5); + + station.CheckForWindrunHighLow(rec.Key); + + //bw?.ReportProgress((totalentries - datalist.Count) * 100 / totalentries, "processing"); + + //UpdateDatabase(timestamp.ToUniversalTime(), historydata.interval, false); + + cumulus.DoLogFile(rec.Key, false); + if (cumulus.StationOptions.LogExtraSensors) cumulus.DoExtraLogFile(rec.Key); + + //AddRecentDataEntry(timestamp, WindAverage, RecentMaxGust, WindLatest, Bearing, AvgBearing, + // OutdoorTemperature, WindChill, OutdoorDewpoint, HeatIndex, + // OutdoorHumidity, Pressure, RainToday, SolarRad, UV, Raincounter, FeelsLike, Humidex); + + station.AddRecentDataWithAq(rec.Key, station.WindAverage, station.RecentMaxGust, station.WindLatest, station.Bearing, station.AvgBearing, station.OutdoorTemperature, station.WindChill, station.OutdoorDewpoint, station.HeatIndex, + station.OutdoorHumidity, station.Pressure, station.RainToday, station.SolarRad, station.UV, station.Raincounter, station.FeelsLike, station.Humidex, station.ApparentTemperature, station.IndoorTemperature, station.IndoorHumidity, station.CurrentSolarMax, station.RainRate); + + if (cumulus.StationOptions.CalculatedET && rec.Key.Minute == 0) + { + // Start of a new hour, and we want to calculate ET in Cumulus + station.CalculateEvaoptranspiration(rec.Key); + } + + station.DoTrendValues(rec.Key); + station.UpdatePressureTrendString(); + station.UpdateStatusPanel(rec.Key); + cumulus.AddToWebServiceLists(rec.Key); + + } + } + + private void ApplyHistoricData(KeyValuePair rec) + { + // === Wind == + try + { + if (rec.Value.WindGust.HasValue && rec.Value.WindSpd.HasValue && rec.Value.WindDir.HasValue) + { + var gustVal = station.ConvertWindMPHToUser((double)rec.Value.WindGust); + var spdVal = station.ConvertWindMPHToUser((double)rec.Value.WindSpd); + var dirVal = rec.Value.WindDir.Value; + + // The protocol does not provide an average value + // so feed in current MX average + station.DoWind(spdVal, dirVal, station.WindAverage / cumulus.Calib.WindSpeed.Mult, rec.Key); + + var gustLastCal = gustVal * cumulus.Calib.WindGust.Mult; + if (gustLastCal > station.RecentMaxGust) + { + cumulus.LogDebugMessage("Setting max gust from current value: " + gustLastCal.ToString(cumulus.WindFormat)); + station.CheckHighGust(gustLastCal, dirVal, rec.Key); + + // add to recent values so normal calculation includes this value + station.WindRecent[station.nextwind].Gust = gustVal; // use uncalibrated value + station.WindRecent[station.nextwind].Speed = station.WindAverage / cumulus.Calib.WindSpeed.Mult; + station.WindRecent[station.nextwind].Timestamp = rec.Key; + station.nextwind = (station.nextwind + 1) % WeatherStation.MaxWindRecent; + + station.RecentMaxGust = gustLastCal; + } } } catch (Exception ex) { - cumulus.LogMessage("API.GetHistoricData: Exception:"); - cumulus.LastUpdateTime = endTime; - return false; + cumulus.LogMessage("ApplyHistoricData: Error in Wind data - " + ex.Message); } + // === Humidity === + try + { + if (rec.Value.IndoorHum.HasValue) + { + station.DoIndoorHumidity(rec.Value.IndoorHum.Value); + } + + if (rec.Value.Humidity.HasValue) + { + station.DoOutdoorHumidity(rec.Value.Humidity.Value, rec.Key); + } + } + catch (Exception ex) + { + cumulus.LogMessage("ApplyHistoricData: Error in Humidity data - " + ex.Message); + } + + // === Pressure === + try + { + if (rec.Value.Pressure.HasValue) + { + var pressVal = station.ConvertPressINHGToUser((double)rec.Value.Pressure); + station.DoPressure(pressVal, rec.Key); + station.UpdatePressureTrendString(); + } + } + catch (Exception ex) + { + cumulus.LogMessage("ApplyHistoricData: Error in Pressure data - " + ex.Message); + } + + // === Indoor temp === + try + { + if (rec.Value.IndoorTemp.HasValue) + { + var tempVal = station.ConvertTempFToUser((double)rec.Value.IndoorTemp); + station.DoIndoorTemp(tempVal); + } + } + catch (Exception ex) + { + cumulus.LogMessage("ApplyHistoricData: Error in Indoor temp data - " + ex.Message); + } + + // === Outdoor temp === + try + { + if (rec.Value.Temp.HasValue) + { + var tempVal = station.ConvertTempFToUser((double)rec.Value.Temp); + station.DoOutdoorTemp(tempVal, rec.Key); + } + } + catch (Exception ex) + { + cumulus.LogMessage("ApplyHistoricData: Error in Outdoor temp data - " + ex.Message); + } + + // === Rain === + try + { + double rRate = 0; + if (rec.Value.RainRate.HasValue) + { + // we have a rain rate, so we will NOT calculate it + station.calculaterainrate = false; + rRate = (double)rec.Value.RainRate; + } + else + { + // No rain rate, so we will calculate it + station.calculaterainrate = true; + } + + if (rec.Value.RainYear.HasValue) + { + var rainVal = station.ConvertRainINToUser((double)rec.Value.RainYear); + var rateVal = station.ConvertRainINToUser(rRate); + station.DoRain(rainVal, rateVal, rec.Key); + } + } + catch (Exception ex) + { + cumulus.LogMessage("ApplyHistoricData: Error in Rain data - " + ex.Message); + } + + // === Dewpoint === + try + { + if (cumulus.StationOptions.CalculatedDP) + { + station.DoOutdoorDewpoint(0, rec.Key); + } + else if (rec.Value.DewPoint.HasValue) + { + var val = station.ConvertTempFToUser((double)rec.Value.DewPoint); + station.DoOutdoorDewpoint(val, rec.Key); + } + } + catch (Exception ex) + { + cumulus.LogMessage("ApplyHistoricData: Error in Dew point data - " + ex.Message); + } + + // === Wind Chill === + try + { + if (cumulus.StationOptions.CalculatedWC && rec.Value.Temp.HasValue && rec.Value.WindSpd.HasValue) + { + station.DoWindChill(0, rec.Key); + } + else + { + // historic API does not provide Wind Chill so force calculation + cumulus.StationOptions.CalculatedWC = true; + station.DoWindChill(0, rec.Key); + cumulus.StationOptions.CalculatedWC = false; + } + } + catch (Exception ex) + { + cumulus.LogMessage("ApplyHistoricData: Error in Dew point data - " + ex.Message); + } + + // === Humidex === + if (rec.Value.Temp.HasValue && rec.Value.Humidity.HasValue) + { + try + { + station.DoHumidex(rec.Key); + + // === Apparent & Feels Like === - requires temp, hum, and windspeed + if (rec.Value.WindSpd.HasValue) + { + station.DoApparentTemp(rec.Key); + station.DoFeelsLike(rec.Key); + } + else + { + cumulus.LogMessage("ApplyHistoricData: Insufficient data to calculate Apparent/Feels Like temps"); + } + } + catch (Exception ex) + { + cumulus.LogMessage("ApplyHistoricData: Error in Humidex/Apparant/Feels Like - " + ex.Message); + } + } + else + { + cumulus.LogMessage("ApplyHistoricData: Insufficient data to calculate Humidex and Apparent/Feels Like temps"); + } + + // === Solar === + try + { + if (rec.Value.Solar.HasValue) + { + station.DoSolarRad(rec.Value.Solar.Value, rec.Key); + } + } + catch (Exception ex) + { + cumulus.LogMessage("ApplyHistoricData: Error in Solar data - " + ex.Message); + } + + // === UVI === + try + { + if (rec.Value.UVI.HasValue) + { + station.DoUV((double)rec.Value.UVI, rec.Key); + } + } + catch (Exception ex) + { + cumulus.LogMessage("ApplyHistoricData: Error in Solar data - " + ex.Message); + } + + // === Extra Sensors === + for (var i = 1; i <= 8; i++) + { + // === Extra Temperature === + try + { + if (rec.Value.ExtraTemp[i - 1].HasValue) + { + station.DoExtraTemp(station.ConvertTempFToUser((double)rec.Value.ExtraTemp[i - 1]), i); + } + } + catch (Exception ex) + { + cumulus.LogMessage($"ApplyHistoricData: Error in extra temperature data - {ex.Message}"); + } + // === Extra Humidity === + try + { + if (rec.Value.ExtraHumidity[i - 1].HasValue) + { + station.DoExtraHum(rec.Value.ExtraHumidity[i - 1].Value, i); + } + } + catch (Exception ex) + { + cumulus.LogMessage($"ApplyHistoricData: Error in extra humidity data - {ex.Message}"); + } + + + // === User Temperature === + try + { + if (rec.Value.UserTemp[i - 1].HasValue) + { + station.DoUserTemp(station.ConvertTempFToUser((double)rec.Value.UserTemp[i - 1]), i); + } + } + catch (Exception ex) + { + cumulus.LogMessage($"ApplyHistoricData: Error in extra user temperature data - {ex.Message}"); + } + + // === Soil Moisture === + try + { + if (rec.Value.SoilMoist[i - 1].HasValue) + { + station.DoSoilMoisture((double)rec.Value.SoilMoist[i - 1], i); + } + } + catch (Exception ex) + { + cumulus.LogMessage($"ApplyHistoricData: Error in soil moisture data - {ex.Message}"); + } + } + + // === Indoor CO2 === + try + { + if (rec.Value.IndoorCo2.HasValue) + { + station.CO2_pm2p5 = rec.Value.IndoorCo2.Value; + } + } + catch (Exception ex) + { + cumulus.LogMessage("ApplyHistoricData: Error in CO2 data - " + ex.Message); + } + + // === Indoor CO2 24hr avg === + try + { + if (rec.Value.IndoorCo2hr24.HasValue) + { + station.CO2_pm2p5_24h = rec.Value.CO2pm2p5hr24.Value; + } + } + catch (Exception ex) + { + cumulus.LogMessage("ApplyHistoricData: Error in CO2 24hr avg data - " + ex.Message); + } + + // === PM 2.5 Combo + try + { + if (rec.Value.AqiComboPm25.HasValue) + { + station.CO2_pm2p5 = (double)rec.Value.AqiComboPm25.Value; + } + } + catch (Exception ex) + { + cumulus.LogMessage("ApplyHistoricData: Error in AQI Combo pm2.5 data - " + ex.Message); + } + + // === PM 10 Combo + try + { + if (rec.Value.AqiComboPm10.HasValue) + { + station.CO2_pm10 = (double)rec.Value.AqiComboPm10.Value; + } + } + catch (Exception ex) + { + cumulus.LogMessage("ApplyHistoricData: Error in AQI Combo pm10 data - " + ex.Message); + } + + // === 4 channel pm 2.5 === + for (var i = 1; i <= 4; i++) + { + try + { + if (rec.Value.pm25[i - 1].HasValue) + { + station.DoAirQuality((double)rec.Value.pm25[i - 1].Value, i); + } + } + catch (Exception ex) + { + cumulus.LogMessage($"ApplyHistoricData: Error in extra temperature data - {ex.Message}"); + } + } } + private string ErrorCode(int code) { switch (code) @@ -230,6 +1359,13 @@ internal class EcowittHistoricData public EcowittHistoricDataLeaf leaf_ch8 { get; set; } public EcowittHistoricDataLightning lightning { get; set; } public EcowittHistoricDataCo2 indoor_co2 { get; set; } + public EcowittHistoricDataCo2 co2_aqi_combo { get; set; } + public EcowittHistoricDataPm25Aqi pm25_aqi_combo { get; set; } + public EcowittHistoricDataPm10Aqi pm10_aqi_combo { get; set; } + public EcowittHistoricDataPm25Aqi pm25_ch1 { get; set; } + public EcowittHistoricDataPm25Aqi pm25_ch2 { get; set; } + public EcowittHistoricDataPm25Aqi pm25_ch3 { get; set; } + public EcowittHistoricDataPm25Aqi pm25_ch4 { get; set; } } @@ -308,6 +1444,16 @@ internal class EcowittHistoricDataCo2 public EcowittHistoricDataTypeInt average24h { get; set; } } + internal class EcowittHistoricDataPm25Aqi + { + public EcowittHistoricDataTypeDbl pm25 { get; set; } + } + + internal class EcowittHistoricDataPm10Aqi + { + public EcowittHistoricDataTypeDbl pm10 { get; set; } + } + internal class HistoricData { public decimal? IndoorTemp { get; set; } @@ -331,18 +1477,19 @@ internal class HistoricData public int?[] SoilMoist { get; set; } public decimal?[] UserTemp { get; set; } public int?[] LeafWetness { get; set; } - public decimal? pm25 { get; set; } + public decimal?[] pm25 { get; set; } public decimal? AqiComboPm25 { get; set; } public decimal? AqiComboPm10 { get; set; } public decimal? AqiComboTemp { get; set; } public int? AqiComboHum { get; set; } - public int? Co2 { get; set; } - public int? Co2hr24 { get; set; } + public int? CO2pm2p5 { get; set; } + public int? CO2pm2p5hr24 { get; set; } public int? IndoorCo2 { get; set; } public int? IndoorCo2hr24 { get; set; } public HistoricData() { + pm25 = new decimal?[4]; ExtraTemp = new decimal?[8]; ExtraHumidity = new int?[8]; SoilMoist = new int?[8]; diff --git a/CumulusMX/GW1000Station.cs b/CumulusMX/GW1000Station.cs index 1ff259c6..396bbab8 100644 --- a/CumulusMX/GW1000Station.cs +++ b/CumulusMX/GW1000Station.cs @@ -474,7 +474,7 @@ public override void getAndProcessHistoryData() try { - api = new EcowittApi(cumulus); + api = new EcowittApi(cumulus, this); do { @@ -510,809 +510,9 @@ private void GetHistoricData() maxArchiveRuns++; } - EcowittApi.EcowittHistoricData dat; - - // only fetch the data we are interested in... - var sb = new StringBuilder("indoor,outdoor,wind,pressure"); - if (cumulus.EcowittExtraUseSolar || cumulus.EcowittExtraUseUv) - sb.Append(",solar_and_uvi"); - if (cumulus.EcowittExtraUseTempHum) - sb.Append(",temp_and_humidity_ch1,temp_and_humidity_ch2,temp_and_humidity_ch3,temp_and_humidity_ch4,temp_and_humidity_ch5,temp_and_humidity_ch6,temp_and_humidity_ch7,temp_and_humidity_ch8"); - if (cumulus.EcowittExtraUseSoilMoist) - sb.Append(",soil_ch1,soil_ch2,soil_ch3,soil_ch4,soil_ch5,soil_ch6,soil_ch7,soil_ch8"); - if (cumulus.EcowittExtraUseUserTemp) - sb.Append(",temp_ch1,temp_ch2,temp_ch3,temp_ch4,temp_ch5,temp_ch6,temp_ch7,temp_ch8"); - if (cumulus.EcowittExtraUseLeafWet) - sb.Append(",leaf_ch1,leaf_ch2,leaf_ch3,leaf_ch4,leaf_ch5,leaf_ch6,leaf_ch7,leaf_ch8"); - - var res = api.GetHistoricData(startTime, endTime, new string[] { sb.ToString() }, out dat); - - if (res) - { - ProcessHistoryData(dat); - } + api.GetHistoricData(startTime, endTime); } - private void ProcessHistoryData(EcowittApi.EcowittHistoricData data) - { - // allocate a dictionary of data objects, keyed on the timestamp - var buffer = new SortedDictionary(); - - // process each sensor type, and store them for adding to the system later - // Indoor Data - if (data.indoor != null) - { - // do the temperature - if (data.indoor.temperature.list != null) - { - foreach (var item in data.indoor.temperature.list) - { - // not present value = 140 - if (!item.Value.HasValue || item.Value == 140 || item.Key <= cumulus.LastUpdateTime) - continue; - - var newItem = new EcowittApi.HistoricData(); - newItem.IndoorTemp = item.Value; - buffer.Add(item.Key, newItem); - } - } - // do the humidity - if (data.indoor.humidity.list != null) - { - foreach (var item in data.indoor.humidity.list) - { - if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) - continue; - - if (buffer.ContainsKey(item.Key)) - { - buffer[item.Key].IndoorHum = item.Value; - } - else - { - var newItem = new EcowittApi.HistoricData(); - newItem.IndoorHum = item.Value; - buffer.Add(item.Key, newItem); - } - } - } - } - // Outdoor Data - if (data.outdoor != null) - { - // Temperature - if (data.outdoor.temperature.list != null) - { - foreach (var item in data.outdoor.temperature.list) - { - // not present value = 140 - if (!item.Value.HasValue || item.Value == 140 || item.Key <= cumulus.LastUpdateTime) - continue; - - if (buffer.ContainsKey(item.Key)) - { - buffer[item.Key].Temp = item.Value; - } - else - { - var newItem = new EcowittApi.HistoricData(); - newItem.Temp = item.Value; - buffer.Add(item.Key, newItem); - } - } - } - - // Humidity - if (data.outdoor.humidity.list != null) - { - foreach (var item in data.outdoor.humidity.list) - { - if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) - continue; - - if (buffer.ContainsKey(item.Key)) - { - buffer[item.Key].Humidity = item.Value; - } - else - { - var newItem = new EcowittApi.HistoricData(); - newItem.Humidity = item.Value; - buffer.Add(item.Key, newItem); - } - } - } - // Dewpoint - if (data.outdoor.dew_point.list != null) - { - foreach (var item in data.outdoor.dew_point.list) - { - if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) - continue; - - if (buffer.ContainsKey(item.Key)) - { - buffer[item.Key].DewPoint = item.Value; - } - else - { - var newItem = new EcowittApi.HistoricData(); - newItem.DewPoint = item.Value; - buffer.Add(item.Key, newItem); - } - } - } - } - // Wind Data - if (data.wind != null) - { - // Speed - if (data.wind.wind_speed.list != null) - { - foreach (var item in data.wind.wind_speed.list) - { - if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) - continue; - - if (buffer.ContainsKey(item.Key)) - { - buffer[item.Key].WindSpd = item.Value; - } - else - { - var newItem = new EcowittApi.HistoricData(); - newItem.WindSpd = item.Value; - buffer.Add(item.Key, newItem); - } - } - } - - // Gust - if (data.wind.wind_gust.list != null) - { - foreach (var item in data.wind.wind_gust.list) - { - if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) - continue; - - if (buffer.ContainsKey(item.Key)) - { - buffer[item.Key].WindGust = item.Value; - } - else - { - var newItem = new EcowittApi.HistoricData(); - newItem.WindGust = item.Value; - buffer.Add(item.Key, newItem); - } - } - } - // Direction - if (data.wind.wind_direction.list != null) - { - foreach (var item in data.wind.wind_direction.list) - { - if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) - continue; - - if (buffer.ContainsKey(item.Key)) - { - buffer[item.Key].WindDir = item.Value; - } - else - { - var newItem = new EcowittApi.HistoricData(); - newItem.WindDir = item.Value; - buffer.Add(item.Key, newItem); - } - } - } - } - // Pressure Data - if (data.pressure != null) - { - // relative - if (data.pressure.relative.list != null) - { - foreach (var item in data.pressure.relative.list) - { - if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) - continue; - - if (buffer.ContainsKey(item.Key)) - { - buffer[item.Key].Pressure = item.Value; - } - else - { - var newItem = new EcowittApi.HistoricData(); - newItem.Pressure = item.Value; - buffer.Add(item.Key, newItem); - } - } - } - } - // Solar Data - if (data.solar_and_uvi != null) - { - // solar - if (cumulus.EcowittExtraUseSolar && data.solar_and_uvi.solar.list != null) - { - foreach (var item in data.solar_and_uvi.solar.list) - { - if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) - continue; - - if (buffer.ContainsKey(item.Key)) - { - buffer[item.Key].Solar = (int)item.Value; - } - else - { - var newItem = new EcowittApi.HistoricData(); - newItem.Solar = (int)item.Value; - buffer.Add(item.Key, newItem); - } - } - } - // uvi - if (cumulus.EcowittExtraUseUv && data.solar_and_uvi.uvi.list != null) - { - foreach (var item in data.solar_and_uvi.uvi.list) - { - if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) - continue; - - if (buffer.ContainsKey(item.Key)) - { - buffer[item.Key].UVI = (int)item.Value; - } - else - { - var newItem = new EcowittApi.HistoricData(); - newItem.UVI = (int)item.Value; - buffer.Add(item.Key, newItem); - } - } - } - } - // Extra 8 channel sensors - for (var i = 1; i <= 8; i++) - { - EcowittApi.EcowittHistoricTempHum srcTH = null; - EcowittApi.EcowittHistoricDataSoil srcSoil = null; - EcowittApi.EcowittHistoricDataTemp srcTemp = null; - EcowittApi.EcowittHistoricDataLeaf srcLeaf = null; - switch (i) - { - case 1: - srcTH = data.temp_and_humidity_ch1; - srcSoil = data.soil_ch1; - srcTemp = data.temp_ch1; - srcLeaf = data.leaf_ch1; - break; - case 2: - srcTH = data.temp_and_humidity_ch2; - srcSoil = data.soil_ch2; - srcTemp = data.temp_ch2; - srcLeaf = data.leaf_ch2; - break; - case 3: - srcTH = data.temp_and_humidity_ch3; - srcSoil = data.soil_ch3; - srcTemp = data.temp_ch3; - srcLeaf = data.leaf_ch3; - break; - case 4: - srcTH = data.temp_and_humidity_ch4; - srcSoil = data.soil_ch4; - srcTemp = data.temp_ch4; - srcLeaf = data.leaf_ch4; - break; - case 5: - srcTH = data.temp_and_humidity_ch5; - srcSoil = data.soil_ch5; - srcTemp = data.temp_ch5; - srcLeaf = data.leaf_ch5; - break; - case 6: - srcTH = data.temp_and_humidity_ch6; - srcSoil = data.soil_ch6; - srcTemp = data.temp_ch6; - srcLeaf = data.leaf_ch6; - break; - case 7: - srcTH = data.temp_and_humidity_ch7; - srcSoil = data.soil_ch7; - srcTemp = data.temp_ch7; - srcLeaf = data.leaf_ch7; - break; - case 8: - srcTH = data.temp_and_humidity_ch8; - srcSoil = data.soil_ch8; - srcTemp = data.temp_ch8; - srcLeaf = data.leaf_ch8; - break; - } - - // Extra Temp/Hum Data - if (cumulus.EcowittExtraUseTempHum && srcTH != null) - { - // temperature - if (srcTH.temperature.list != null) - { - foreach (var item in srcTH.temperature.list) - { - if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) - continue; - - if (buffer.ContainsKey(item.Key)) - { - buffer[item.Key].ExtraTemp[i - 1] = item.Value; - } - else - { - var newItem = new EcowittApi.HistoricData(); - newItem.ExtraTemp[i - 1] = item.Value; - buffer.Add(item.Key, newItem); - } - } - } - // humidity - if (srcTH.humidity.list != null) - { - foreach (var item in srcTH.humidity.list) - { - if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) - continue; - - if (buffer.ContainsKey(item.Key)) - { - buffer[item.Key].ExtraHumidity[i - 1] = item.Value; - } - else - { - var newItem = new EcowittApi.HistoricData(); - newItem.ExtraHumidity[i - 1] = item.Value; - buffer.Add(item.Key, newItem); - } - } - } - } - // Extra Soil Moisture Data - if (cumulus.EcowittExtraUseSoilMoist && srcSoil != null && srcSoil.soilmoisture.list != null) - { - // moisture - foreach (var item in srcSoil.soilmoisture.list) - { - if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) - continue; - - if (buffer.ContainsKey(item.Key)) - { - buffer[item.Key].SoilMoist[i - 1] = item.Value; - } - else - { - var newItem = new EcowittApi.HistoricData(); - newItem.SoilMoist[i - 1] = item.Value; - buffer.Add(item.Key, newItem); - } - } - } - // User Temp Data - if (cumulus.EcowittExtraUseUserTemp && srcTemp != null && srcTemp.temperature.list != null) - { - // temperature - foreach (var item in srcTemp.temperature.list) - { - if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) - continue; - - if (buffer.ContainsKey(item.Key)) - { - buffer[item.Key].UserTemp[i - 1] = item.Value; - } - else - { - var newItem = new EcowittApi.HistoricData(); - newItem.UserTemp[i - 1] = item.Value; - buffer.Add(item.Key, newItem); - } - } - } - // Leaf Wetness Data - if (cumulus.EcowittExtraUseLeafWet && srcLeaf != null && srcLeaf.leaf_wetness.list != null) - { - // wetness - foreach (var item in srcLeaf.leaf_wetness.list) - { - if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) - continue; - - if (buffer.ContainsKey(item.Key)) - { - buffer[item.Key].LeafWetness[i - 1] = item.Value; - } - else - { - var newItem = new EcowittApi.HistoricData(); - newItem.LeafWetness[i - 1] = item.Value; - buffer.Add(item.Key, newItem); - } - } - } - } - - - // now we have all the data for this period, for each record create the string expected by ProcessData and get it processed - var rollHour = Math.Abs(cumulus.GetHourInc()); - var luhour = cumulus.LastUpdateTime.Hour; - var rolloverdone = luhour == rollHour; - var midnightraindone = luhour == 0; - - foreach (var rec in buffer) - { - cumulus.LogMessage("Processing data for " + rec.Key); - - var h = rec.Key.Hour; - - // if outside rollover hour, rollover yet to be done - if (h != rollHour) rolloverdone = false; - - // In rollover hour and rollover not yet done - if (h == rollHour && !rolloverdone) - { - // do rollover - cumulus.LogMessage("Day rollover " + rec.Key.ToShortTimeString()); - DayReset(rec.Key); - - rolloverdone = true; - } - - // Not in midnight hour, midnight rain yet to be done - if (h != 0) midnightraindone = false; - - // In midnight hour and midnight rain (and sun) not yet done - if (h == 0 && !midnightraindone) - { - ResetMidnightRain(rec.Key); - ResetSunshineHours(); - midnightraindone = true; - } - - // finally apply this data - ApplyHistoricData(rec); - - // add in archive period worth of sunshine, if sunny - if (SolarRad > CurrentSolarMax * cumulus.SolarOptions.SunThreshold / 100 && - SolarRad >= cumulus.SolarOptions.SolarMinimum) - SunshineHours += 5 / 60.0; - - - - // add in 'following interval' minutes worth of wind speed to windrun - cumulus.LogMessage("Windrun: " + WindAverage.ToString(cumulus.WindFormat) + cumulus.Units.WindText + " for " + 5 + " minutes = " + - (WindAverage * WindRunHourMult[cumulus.Units.Wind] * 5 / 60.0).ToString(cumulus.WindRunFormat) + cumulus.Units.WindRunText); - - WindRunToday += WindAverage * WindRunHourMult[cumulus.Units.Wind] * 5 / 60.0; - - // update heating/cooling degree days - UpdateDegreeDays(5); - - // update dominant wind bearing - CalculateDominantWindBearing(Bearing, WindAverage, 5); - - CheckForWindrunHighLow(rec.Key); - - //bw?.ReportProgress((totalentries - datalist.Count) * 100 / totalentries, "processing"); - - //UpdateDatabase(timestamp.ToUniversalTime(), historydata.interval, false); - - cumulus.DoLogFile(rec.Key, false); - if (cumulus.StationOptions.LogExtraSensors) cumulus.DoExtraLogFile(rec.Key); - - //AddRecentDataEntry(timestamp, WindAverage, RecentMaxGust, WindLatest, Bearing, AvgBearing, - // OutdoorTemperature, WindChill, OutdoorDewpoint, HeatIndex, - // OutdoorHumidity, Pressure, RainToday, SolarRad, UV, Raincounter, FeelsLike, Humidex); - - AddRecentDataWithAq(rec.Key, WindAverage, RecentMaxGust, WindLatest, Bearing, AvgBearing, OutdoorTemperature, WindChill, OutdoorDewpoint, HeatIndex, - OutdoorHumidity, Pressure, RainToday, SolarRad, UV, Raincounter, FeelsLike, Humidex, ApparentTemperature, IndoorTemperature, IndoorHumidity, CurrentSolarMax, RainRate); - - if (cumulus.StationOptions.CalculatedET && rec.Key.Minute == 0) - { - // Start of a new hour, and we want to calculate ET in Cumulus - CalculateEvaoptranspiration(rec.Key); - } - - DoTrendValues(rec.Key); - UpdatePressureTrendString(); - UpdateStatusPanel(rec.Key); - cumulus.AddToWebServiceLists(rec.Key); - - } - } - - - private void ApplyHistoricData(KeyValuePair rec) - { - // === Wind == - try - { - if (rec.Value.WindGust.HasValue && rec.Value.WindSpd.HasValue && rec.Value.WindDir.HasValue) - { - var gustVal = ConvertWindMPHToUser((double)rec.Value.WindGust); - var spdVal = ConvertWindMPHToUser((double)rec.Value.WindSpd); - var dirVal = rec.Value.WindDir.Value; - - // The protocol does not provide an average value - // so feed in current MX average - DoWind(spdVal, dirVal, WindAverage / cumulus.Calib.WindSpeed.Mult, rec.Key); - - var gustLastCal = gustVal * cumulus.Calib.WindGust.Mult; - if (gustLastCal > RecentMaxGust) - { - cumulus.LogDebugMessage("Setting max gust from current value: " + gustLastCal.ToString(cumulus.WindFormat)); - CheckHighGust(gustLastCal, dirVal, rec.Key); - - // add to recent values so normal calculation includes this value - WindRecent[nextwind].Gust = gustVal; // use uncalibrated value - WindRecent[nextwind].Speed = WindAverage / cumulus.Calib.WindSpeed.Mult; - WindRecent[nextwind].Timestamp = rec.Key; - nextwind = (nextwind + 1) % MaxWindRecent; - - RecentMaxGust = gustLastCal; - } - } - } - catch (Exception ex) - { - cumulus.LogMessage("ApplyHistoricData: Error in Wind data - " + ex.Message); - } - - // === Humidity === - try - { - if (rec.Value.IndoorHum.HasValue) - { - DoIndoorHumidity(rec.Value.IndoorHum.Value); - } - - if (rec.Value.Humidity.HasValue) - { - DoOutdoorHumidity(rec.Value.Humidity.Value, rec.Key); - } - } - catch (Exception ex) - { - cumulus.LogMessage("ApplyHistoricData: Error in Humidity data - " + ex.Message); - } - - // === Pressure === - try - { - if (rec.Value.Pressure.HasValue) - { - var pressVal = ConvertPressINHGToUser((double)rec.Value.Pressure); - DoPressure(pressVal, rec.Key); - UpdatePressureTrendString(); - } - } - catch (Exception ex) - { - cumulus.LogMessage("ApplyHistoricData: Error in Pressure data - " + ex.Message); - } - - // === Indoor temp === - try - { - if (rec.Value.IndoorTemp.HasValue) - { - var tempVal = ConvertTempFToUser((double)rec.Value.IndoorTemp); - DoIndoorTemp(tempVal); - } - } - catch (Exception ex) - { - cumulus.LogMessage("ApplyHistoricData: Error in Indoor temp data - " + ex.Message); - } - - // === Outdoor temp === - try - { - if (rec.Value.Temp.HasValue) - { - var tempVal = ConvertTempFToUser((double)rec.Value.Temp); - DoOutdoorTemp(tempVal, rec.Key); - } - } - catch (Exception ex) - { - cumulus.LogMessage("ApplyHistoricData: Error in Outdoor temp data - " + ex.Message); - } - - // === Rain === - try - { - double rRate = 0; - if (rec.Value.RainRate.HasValue) - { - // we have a rain rate, so we will NOT calculate it - calculaterainrate = false; - rRate = (double)rec.Value.RainRate; - } - else - { - // No rain rate, so we will calculate it - calculaterainrate = true; - } - - if (rec.Value.RainYear.HasValue) - { - var rainVal = ConvertRainINToUser((double)rec.Value.RainYear); - var rateVal = ConvertRainINToUser(rRate); - DoRain(rainVal, rateVal, rec.Key); - } - } - catch (Exception ex) - { - cumulus.LogMessage("ApplyHistoricData: Error in Rain data - " + ex.Message); - } - - // === Dewpoint === - try - { - if (cumulus.StationOptions.CalculatedDP) - { - DoOutdoorDewpoint(0, rec.Key); - } - else if (rec.Value.DewPoint.HasValue) - { - var val = ConvertTempFToUser((double)rec.Value.DewPoint); - DoOutdoorDewpoint(val, rec.Key); - } - } - catch (Exception ex) - { - cumulus.LogMessage("ApplyHistoricData: Error in Dew point data - " + ex.Message); - } - - // === Wind Chill === - try - { - if (cumulus.StationOptions.CalculatedWC && rec.Value.Temp.HasValue && rec.Value.WindSpd.HasValue) - { - DoWindChill(0, rec.Key); - } - else - { - // historic API does not provide Wind Chill so force calculation - cumulus.StationOptions.CalculatedWC = true; - DoWindChill(0, rec.Key); - cumulus.StationOptions.CalculatedWC = false; - } - } - catch (Exception ex) - { - cumulus.LogMessage("ApplyHistoricData: Error in Dew point data - " + ex.Message); - } - - // === Humidex === - if (rec.Value.Temp.HasValue && rec.Value.Humidity.HasValue) - { - try - { - DoHumidex(rec.Key); - - // === Apparent & Feels Like === - requires temp, hum, and windspeed - if (rec.Value.WindSpd.HasValue) - { - DoApparentTemp(rec.Key); - DoFeelsLike(rec.Key); - } - else - { - cumulus.LogMessage("ApplyHistoricData: Insufficient data to calculate Apparent/Feels Like temps"); - } - } - catch (Exception ex) - { - cumulus.LogMessage("ApplyHistoricData: Error in Humidex/Apparant/Feels Like - " + ex.Message); - } - } - else - { - cumulus.LogMessage("ApplyHistoricData: Insufficient data to calculate Humidex and Apparent/Feels Like temps"); - } - - // === Solar === - try - { - if (cumulus.EcowittExtraUseSolar && rec.Value.Solar.HasValue) - { - DoSolarRad(rec.Value.Solar.Value, rec.Key); - } - } - catch (Exception ex) - { - cumulus.LogMessage("ApplyHistoricData: Error in Solar data - " + ex.Message); - } - - // === UVI === - try - { - if (cumulus.EcowittExtraUseUv && rec.Value.UVI.HasValue) - { - DoUV((double)rec.Value.UVI, rec.Key); - } - } - catch (Exception ex) - { - cumulus.LogMessage("ApplyHistoricData: Error in Solar data - " + ex.Message); - } - - // === Extra Sensors === - for (var i = 1; i <= 8; i++) - { - if (cumulus.EcowittExtraUseTempHum) - { - // === Extra Temperature === - try - { - if (rec.Value.ExtraTemp[i-1].HasValue) - { - DoExtraTemp(ConvertTempFToUser((double)rec.Value.ExtraTemp[i - 1]), i); - } - } - catch (Exception ex) - { - cumulus.LogMessage($"ApplyHistoricData: Error in extra temperature data - {ex.Message}"); - } - // === Extra Humidity === - try - { - if (rec.Value.ExtraHumidity[i-1].HasValue) - { - DoExtraHum(rec.Value.ExtraHumidity[i - 1].Value, i); - } - } - catch (Exception ex) - { - cumulus.LogMessage($"ApplyHistoricData: Error in extra humidity data - {ex.Message}"); - } - - } - - // === User Temperature === - try - { - if (cumulus.EcowittExtraUseUserTemp && rec.Value.UserTemp[i - 1].HasValue) - { - DoUserTemp(ConvertTempFToUser((double)rec.Value.UserTemp[i - 1]), i); - } - } - catch (Exception ex) - { - cumulus.LogMessage($"ApplyHistoricData: Error in extra user temperature data - {ex.Message}"); - } - - // === Soil Moisture === - try - { - if (cumulus.EcowittExtraUseSoilMoist && rec.Value.SoilMoist[i - 1].HasValue) - { - DoSoilMoisture((double)rec.Value.SoilMoist[i-1], i); - } - } - catch (Exception ex) - { - cumulus.LogMessage($"ApplyHistoricData: Error in soil moisture data - {ex.Message}"); - } - } - - } - - private Discovery DiscoverGW1000() { // We only want unique IP addresses @@ -1509,77 +709,6 @@ private string GetFirmwareVersion() return response; } - /* - private bool GetSensorIds() - { - cumulus.LogMessage("Reading sensor ids"); - - var data = DoCommand(Commands.CMD_READ_SENSOR_ID); - - // expected response - // 0 - 0xff - header - // 1 - 0xff - header - // 2 - 0x3A - sensor id command - // 3 - 0x?? - size of response - // 4 - wh65 - // 5-8 - wh65 id - // 9 - wh65 signal - // 10 - wh65 battery - // 11 - wh68 - // ... etc - // (??) - 0x?? - checksum - - if (null != data && data.Length > 200) - { - for (int i = 4; i < data[3]; i += 7) - { - PrintSensorInfo(data, i); - } - return true; - } - else - { - return false; - } - } - - private void PrintSensorInfo(byte[] data, int idx) - { - // expected response - // 0 - 0xff - header - // 1 - 0xff - header - // 2 - 0x3A - sensor id command - // 3 - 0x?? - size of response - // 4 - wh65 - // 5-8 - wh65 id - // 9 - wh65 signal - // 10 - wh65 battery - // 11 - wh68 - // ... etc - // (??) - 0x?? - checksum - - var id = ConvertBigEndianUInt32(data, idx + 1); - var type = Enum.GetName(typeof(SensorIds), data[idx]).ToUpper(); - - if (string.IsNullOrEmpty(type)) - { - type = $"unknown type = {id}"; - } - switch (id) - { - case 0xFFFFFFFE: - cumulus.LogDebugMessage($" - {type} sensor = disabled"); - break; - case 0xFFFFFFFF: - cumulus.LogDebugMessage($" - {type} sensor = registering"); - break; - default: - cumulus.LogDebugMessage($" - {type} sensor id = {id} signal = {data[idx+5]} battery = {data[idx+6]}"); - break; - } - } - */ - private bool GetSensorIdsNew() { cumulus.LogMessage("Reading sensor ids"); diff --git a/CumulusMX/HttpStationEcowitt.cs b/CumulusMX/HttpStationEcowitt.cs index 15caeb0f..a643ff29 100644 --- a/CumulusMX/HttpStationEcowitt.cs +++ b/CumulusMX/HttpStationEcowitt.cs @@ -123,7 +123,7 @@ public override void getAndProcessHistoryData() try { - api = new EcowittApi(cumulus); + api = new EcowittApi(cumulus, this); do { @@ -159,554 +159,8 @@ private void GetHistoricData() maxArchiveRuns++; } - EcowittApi.EcowittHistoricData dat; + api.GetHistoricData(startTime, endTime); - // only fetch the data we are interested in... - var sb = new StringBuilder("indoor,outdoor,wind,pressure"); - if (cumulus.EcowittExtraUseSolar || cumulus.EcowittExtraUseUv) - sb.Append(",solar_and_uvi"); - if (cumulus.EcowittExtraUseTempHum) - sb.Append(",temp_and_humidity_ch1,temp_and_humidity_ch2,temp_and_humidity_ch3,temp_and_humidity_ch4,temp_and_humidity_ch5,temp_and_humidity_ch6,temp_and_humidity_ch7,temp_and_humidity_ch8"); - if (cumulus.EcowittExtraUseSoilMoist) - sb.Append(",soil_ch1,soil_ch2,soil_ch3,soil_ch4,soil_ch5,soil_ch6,soil_ch7,soil_ch8"); - if (cumulus.EcowittExtraUseUserTemp) - sb.Append(",temp_ch1,temp_ch2,temp_ch3,temp_ch4,temp_ch5,temp_ch6,temp_ch7,temp_ch8"); - if (cumulus.EcowittExtraUseLeafWet) - sb.Append(",leaf_ch1,leaf_ch2,leaf_ch3,leaf_ch4,leaf_ch5,leaf_ch6,leaf_ch7,leaf_ch8"); - - var res = api.GetHistoricData(startTime, endTime, new string[] { sb.ToString() }, out dat); - - if (res) - { - - ProcessHistoryData(dat); - - } - } - - private void ProcessHistoryData(EcowittApi.EcowittHistoricData data) - { - // allocate a dictionary of data objects, keyed on the timestamp - var buffer = new SortedDictionary(); - - // process each sensor type, and store them for adding to the system later - // Indoor Data - if (data.indoor != null) - { - // do the temperature - if (data.indoor.temperature.list != null) - { - foreach (var item in data.indoor.temperature.list) - { - // not present value = 140 - if (!item.Value.HasValue || item.Value == 140 || item.Key <= cumulus.LastUpdateTime) - continue; - - var newItem = new EcowittApi.HistoricData(); - newItem.IndoorTemp = item.Value; - buffer.Add(item.Key, newItem); - } - } - // do the humidity - if (data.indoor.humidity.list != null) - { - foreach (var item in data.indoor.humidity.list) - { - if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) - continue; - - if (buffer.ContainsKey(item.Key)) - { - buffer[item.Key].IndoorHum = item.Value; - } - else - { - var newItem = new EcowittApi.HistoricData(); - newItem.IndoorHum = item.Value; - buffer.Add(item.Key, newItem); - } - } - } - } - // Outdoor Data - if (data.outdoor != null) - { - // Temperature - if (data.outdoor.temperature.list != null) - { - foreach (var item in data.outdoor.temperature.list) - { - // not present value = 140 - if (!item.Value.HasValue || item.Value == 140 || item.Key <= cumulus.LastUpdateTime) - continue; - - if (buffer.ContainsKey(item.Key)) - { - buffer[item.Key].Temp = item.Value; - } - else - { - var newItem = new EcowittApi.HistoricData(); - newItem.Temp = item.Value; - buffer.Add(item.Key, newItem); - } - } - } - - // Humidity - if (data.outdoor.humidity.list != null) - { - foreach (var item in data.outdoor.humidity.list) - { - if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) - continue; - - if (buffer.ContainsKey(item.Key)) - { - buffer[item.Key].Humidity = item.Value; - } - else - { - var newItem = new EcowittApi.HistoricData(); - newItem.Humidity = item.Value; - buffer.Add(item.Key, newItem); - } - } - } - // Dewpoint - if (data.outdoor.dew_point.list != null) - { - foreach (var item in data.outdoor.dew_point.list) - { - if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) - continue; - - if (buffer.ContainsKey(item.Key)) - { - buffer[item.Key].DewPoint = item.Value; - } - else - { - var newItem = new EcowittApi.HistoricData(); - newItem.DewPoint = item.Value; - buffer.Add(item.Key, newItem); - } - } - } - } - // Wind Data - if (data.wind != null) - { - // Speed - if (data.wind.wind_speed.list != null) - { - foreach (var item in data.wind.wind_speed.list) - { - if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) - continue; - - if (buffer.ContainsKey(item.Key)) - { - buffer[item.Key].WindSpd = item.Value; - } - else - { - var newItem = new EcowittApi.HistoricData(); - newItem.WindSpd = item.Value; - buffer.Add(item.Key, newItem); - } - } - } - - // Gust - if (data.wind.wind_gust.list != null) - { - foreach (var item in data.wind.wind_gust.list) - { - if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) - continue; - - if (buffer.ContainsKey(item.Key)) - { - buffer[item.Key].WindGust = item.Value; - } - else - { - var newItem = new EcowittApi.HistoricData(); - newItem.WindGust = item.Value; - buffer.Add(item.Key, newItem); - } - } - } - // Direction - if (data.wind.wind_direction.list != null) - { - foreach (var item in data.wind.wind_direction.list) - { - if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) - continue; - - if (buffer.ContainsKey(item.Key)) - { - buffer[item.Key].WindDir = item.Value; - } - else - { - var newItem = new EcowittApi.HistoricData(); - newItem.WindDir = item.Value; - buffer.Add(item.Key, newItem); - } - } - } - } - // Pressure Data - if (data.pressure != null) - { - // relative - if (data.pressure.relative.list != null) - { - foreach (var item in data.pressure.relative.list) - { - if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) - continue; - - if (buffer.ContainsKey(item.Key)) - { - buffer[item.Key].Pressure = item.Value; - } - else - { - var newItem = new EcowittApi.HistoricData(); - newItem.Pressure = item.Value; - buffer.Add(item.Key, newItem); - } - } - } - } - // Solar Data - if (data.solar_and_uvi != null) - { - // solar - if (cumulus.EcowittExtraUseSolar && data.solar_and_uvi.solar.list != null) - { - foreach (var item in data.solar_and_uvi.solar.list) - { - if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) - continue; - - if (buffer.ContainsKey(item.Key)) - { - buffer[item.Key].Solar = (int)item.Value; - } - else - { - var newItem = new EcowittApi.HistoricData(); - newItem.Solar = (int)item.Value; - buffer.Add(item.Key, newItem); - } - } - } - // uvi - if (cumulus.EcowittExtraUseUv && data.solar_and_uvi.uvi.list != null) - { - foreach (var item in data.solar_and_uvi.uvi.list) - { - if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) - continue; - - if (buffer.ContainsKey(item.Key)) - { - buffer[item.Key].UVI = (int)item.Value; - } - else - { - var newItem = new EcowittApi.HistoricData(); - newItem.UVI = (int)item.Value; - buffer.Add(item.Key, newItem); - } - } - } - } - // Extra 8 channel sensors - for (var i = 1; i <= 8; i++) - { - EcowittApi.EcowittHistoricTempHum srcTH = null; - EcowittApi.EcowittHistoricDataSoil srcSoil = null; - EcowittApi.EcowittHistoricDataTemp srcTemp = null; - EcowittApi.EcowittHistoricDataLeaf srcLeaf = null; - switch (i) - { - case 1: - srcTH = data.temp_and_humidity_ch1; - srcSoil = data.soil_ch1; - srcTemp = data.temp_ch1; - srcLeaf = data.leaf_ch1; - break; - case 2: - srcTH = data.temp_and_humidity_ch2; - srcSoil = data.soil_ch2; - srcTemp = data.temp_ch2; - srcLeaf = data.leaf_ch2; - break; - case 3: - srcTH = data.temp_and_humidity_ch3; - srcSoil = data.soil_ch3; - srcTemp = data.temp_ch3; - srcLeaf = data.leaf_ch3; - break; - case 4: - srcTH = data.temp_and_humidity_ch4; - srcSoil = data.soil_ch4; - srcTemp = data.temp_ch4; - srcLeaf = data.leaf_ch4; - break; - case 5: - srcTH = data.temp_and_humidity_ch5; - srcSoil = data.soil_ch5; - srcTemp = data.temp_ch5; - srcLeaf = data.leaf_ch5; - break; - case 6: - srcTH = data.temp_and_humidity_ch6; - srcSoil = data.soil_ch6; - srcTemp = data.temp_ch6; - srcLeaf = data.leaf_ch6; - break; - case 7: - srcTH = data.temp_and_humidity_ch7; - srcSoil = data.soil_ch7; - srcTemp = data.temp_ch7; - srcLeaf = data.leaf_ch7; - break; - case 8: - srcTH = data.temp_and_humidity_ch8; - srcSoil = data.soil_ch8; - srcTemp = data.temp_ch8; - srcLeaf = data.leaf_ch8; - break; - } - - // Extra Temp/Hum Data - if (cumulus.EcowittExtraUseTempHum && srcTH != null) - { - // temperature - if (srcTH.temperature.list != null) - { - foreach (var item in srcTH.temperature.list) - { - if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) - continue; - - if (buffer.ContainsKey(item.Key)) - { - buffer[item.Key].ExtraTemp[i - 1] = item.Value; - } - else - { - var newItem = new EcowittApi.HistoricData(); - newItem.ExtraTemp[i - 1] = item.Value; - buffer.Add(item.Key, newItem); - } - } - } - // humidity - if (srcTH.humidity.list != null) - { - foreach (var item in srcTH.humidity.list) - { - if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) - continue; - - if (buffer.ContainsKey(item.Key)) - { - buffer[item.Key].ExtraHumidity[i - 1] = item.Value; - } - else - { - var newItem = new EcowittApi.HistoricData(); - newItem.ExtraHumidity[i - 1] = item.Value; - buffer.Add(item.Key, newItem); - } - } - } - } - // Extra Soil Moisture Data - if (cumulus.EcowittExtraUseSoilMoist && srcSoil != null && srcSoil.soilmoisture.list != null) - { - // moisture - foreach (var item in srcSoil.soilmoisture.list) - { - if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) - continue; - - if (buffer.ContainsKey(item.Key)) - { - buffer[item.Key].SoilMoist[i - 1] = item.Value; - } - else - { - var newItem = new EcowittApi.HistoricData(); - newItem.SoilMoist[i - 1] = item.Value; - buffer.Add(item.Key, newItem); - } - } - } - // User Temp Data - if (cumulus.EcowittExtraUseUserTemp && srcTemp != null && srcTemp.temperature.list != null) - { - // temperature - foreach (var item in srcTemp.temperature.list) - { - if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) - continue; - - if (buffer.ContainsKey(item.Key)) - { - buffer[item.Key].UserTemp[i - 1] = item.Value; - } - else - { - var newItem = new EcowittApi.HistoricData(); - newItem.UserTemp[i - 1] = item.Value; - buffer.Add(item.Key, newItem); - } - } - } - // Leaf Wetness Data - if (cumulus.EcowittExtraUseLeafWet && srcLeaf != null && srcLeaf.leaf_wetness.list != null) - { - // wetness - foreach (var item in srcLeaf.leaf_wetness.list) - { - if (!item.Value.HasValue || item.Key <= cumulus.LastUpdateTime) - continue; - - if (buffer.ContainsKey(item.Key)) - { - buffer[item.Key].LeafWetness[i - 1] = item.Value; - } - else - { - var newItem = new EcowittApi.HistoricData(); - newItem.LeafWetness[i - 1] = item.Value; - buffer.Add(item.Key, newItem); - } - } - } - } - - - // now we have all the data for this period, for each record create the string expected by ProcessData and get it processed - var rollHour = Math.Abs(cumulus.GetHourInc()); - var luhour = cumulus.LastUpdateTime.Hour; - var rolloverdone = luhour == rollHour; - var midnightraindone = luhour == 0; - - foreach (var rec in buffer) - { - var sb = new StringBuilder(); - if (rec.Value.IndoorTemp.HasValue) sb.Append("tempinf=" + rec.Value.IndoorTemp); - if (rec.Value.IndoorHum.HasValue) sb.Append("&humidityin=" + rec.Value.IndoorHum); - if (rec.Value.Temp.HasValue) sb.Append("&tempf=" + rec.Value.Temp); - if (rec.Value.Humidity.HasValue) sb.Append("&humidity=" + rec.Value.Humidity); - if (rec.Value.DewPoint.HasValue) sb.Append("&dewptf" + rec.Value.DewPoint); - if (rec.Value.WindDir.HasValue) sb.Append("&winddir=" + rec.Value.WindDir); - if (rec.Value.WindSpd.HasValue) sb.Append("&windspeedmph=" + rec.Value.WindSpd); - if (rec.Value.WindGust.HasValue) sb.Append("&windgustmph=" + rec.Value.WindGust); - if (rec.Value.Pressure.HasValue) sb.Append("&baromrelin=" + rec.Value.Pressure); - if (rec.Value.Solar.HasValue) sb.Append("&solarradiation=" + rec.Value.Solar); - if (rec.Value.UVI.HasValue) sb.Append("&uv=" + rec.Value.UVI); - for (var i = 1; i <= 8; i++) - { - if (rec.Value.ExtraTemp[i - 1].HasValue) sb.Append($"&temp{i}f={rec.Value.ExtraTemp[i - 1]}"); - if (rec.Value.ExtraHumidity[i - 1].HasValue) sb.Append($"&humidity{i}={rec.Value.ExtraHumidity[i - 1]}"); - if (rec.Value.UserTemp[i - 1].HasValue) sb.Append($"tf_ch{i}={rec.Value.UserTemp[i - 1]}"); - if (rec.Value.SoilMoist[i - 1].HasValue) sb.Append($"soilmoisture{i}={rec.Value.SoilMoist[i - 1]}"); - if (rec.Value.LeafWetness[i - 1].HasValue) - if (i==1) - sb.Append($"leafwetness={rec.Value.LeafWetness[i - 1]}"); - else - sb.Append($"leafwetness{i}={rec.Value.LeafWetness[i - 1]}"); - } - - cumulus.LogMessage("Processing data for " + rec.Key); - - var h = rec.Key.Hour; - - // if outside rollover hour, rollover yet to be done - if (h != rollHour) rolloverdone = false; - - // In rollover hour and rollover not yet done - if (h == rollHour && !rolloverdone) - { - // do rollover - cumulus.LogMessage("Day rollover " + rec.Key.ToShortTimeString()); - DayReset(rec.Key); - - rolloverdone = true; - } - - // Not in midnight hour, midnight rain yet to be done - if (h != 0) midnightraindone = false; - - // In midnight hour and midnight rain (and sun) not yet done - if (h == 0 && !midnightraindone) - { - ResetMidnightRain(rec.Key); - ResetSunshineHours(); - midnightraindone = true; - } - - // finally apply this data - ApplyData(sb.ToString(), station == null, rec.Key); - - // add in archive period worth of sunshine, if sunny - if (SolarRad > CurrentSolarMax * cumulus.SolarOptions.SunThreshold / 100 && - SolarRad >= cumulus.SolarOptions.SolarMinimum) - SunshineHours += 5 / 60.0; - - - - // add in 'following interval' minutes worth of wind speed to windrun - cumulus.LogMessage("Windrun: " + WindAverage.ToString(cumulus.WindFormat) + cumulus.Units.WindText + " for " + 5 + " minutes = " + - (WindAverage * WindRunHourMult[cumulus.Units.Wind] * 5 / 60.0).ToString(cumulus.WindRunFormat) + cumulus.Units.WindRunText); - - WindRunToday += WindAverage * WindRunHourMult[cumulus.Units.Wind] * 5 / 60.0; - - // update heating/cooling degree days - UpdateDegreeDays(5); - - // update dominant wind bearing - CalculateDominantWindBearing(Bearing, WindAverage, 5); - - CheckForWindrunHighLow(rec.Key); - - //bw?.ReportProgress((totalentries - datalist.Count) * 100 / totalentries, "processing"); - - //UpdateDatabase(timestamp.ToUniversalTime(), historydata.interval, false); - - cumulus.DoLogFile(rec.Key, false); - if (cumulus.StationOptions.LogExtraSensors) cumulus.DoExtraLogFile(rec.Key); - - //AddRecentDataEntry(timestamp, WindAverage, RecentMaxGust, WindLatest, Bearing, AvgBearing, - // OutdoorTemperature, WindChill, OutdoorDewpoint, HeatIndex, - // OutdoorHumidity, Pressure, RainToday, SolarRad, UV, Raincounter, FeelsLike, Humidex); - - AddRecentDataWithAq(rec.Key, WindAverage, RecentMaxGust, WindLatest, Bearing, AvgBearing, OutdoorTemperature, WindChill, OutdoorDewpoint, HeatIndex, - OutdoorHumidity, Pressure, RainToday, SolarRad, UV, Raincounter, FeelsLike, Humidex, ApparentTemperature, IndoorTemperature, IndoorHumidity, CurrentSolarMax, RainRate); - - if (cumulus.StationOptions.CalculatedET && rec.Key.Minute == 0) - { - // Start of a new hour, and we want to calculate ET in Cumulus - CalculateEvaoptranspiration(rec.Key); - } - - DoTrendValues(rec.Key); - UpdatePressureTrendString(); - UpdateStatusPanel(rec.Key); - cumulus.AddToWebServiceLists(rec.Key); - - } } public string ProcessData(IHttpContext context, bool main, DateTime? ts = null) diff --git a/CumulusMX/Properties/AssemblyInfo.cs b/CumulusMX/Properties/AssemblyInfo.cs index 4da48e97..238f387d 100644 --- a/CumulusMX/Properties/AssemblyInfo.cs +++ b/CumulusMX/Properties/AssemblyInfo.cs @@ -5,11 +5,11 @@ // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. -[assembly: AssemblyTitle("Cumulus MX BETA")] -[assembly: AssemblyDescription("Version 3.15.0 - Build 3166")] +[assembly: AssemblyTitle("Cumulus MX")] +[assembly: AssemblyDescription("Version 3.15.0 - Build 3167")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("Cumulus MX BETA")] +[assembly: AssemblyProduct("Cumulus MX")] [assembly: AssemblyCopyright("Copyright © 2015-2022 Cumulus MX")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -32,5 +32,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("3.15.0.3166")] -[assembly: AssemblyFileVersion("3.15.0.3166")] +[assembly: AssemblyVersion("3.15.0.3167")] +[assembly: AssemblyFileVersion("3.15.0.3167")] diff --git a/CumulusMX/WeatherStation.cs b/CumulusMX/WeatherStation.cs index 11a603a7..f60dac9c 100644 --- a/CumulusMX/WeatherStation.cs +++ b/CumulusMX/WeatherStation.cs @@ -7117,7 +7117,7 @@ public logfilerec ParseLogFileRec(string data, bool minMax) } } - protected void UpdateStatusPanel(DateTime timestamp) + internal void UpdateStatusPanel(DateTime timestamp) { LastDataReadTimestamp = timestamp; } diff --git a/Updates.txt b/Updates.txt index a59931ef..6f56ae80 100644 --- a/Updates.txt +++ b/Updates.txt @@ -1,14 +1,11 @@ -3.15.0 - b3165 - BETA +3.15.0 - b3167 —————————————— -- 3165: Fix GW1000 start-up issue! -- 3165: Fix possible duplication of the last log entry from the previous run of MX - - - Fix: Prevent real time processing occurring before first data has been received - Fix: Davis WLL: Add missing decode of THSW from current data - Fix: Daily high humidex time being logged as high apparent temp time - Change: Leaf wetness web tags <#LeafWetness[1-8]> now accept the rc and dp parameters +- Change: Davis WLL: Now fetches temperature data every 10 seconds instead of 60 seconds - New: Adds experimental support for Ecowitt stations (GW1000 & HTTP) historic catch-up - New: HTTP (Ecowitt) station: adds support for WS990 battery state decoding From f2888f342efe89f22a6406e0264df1ec1d1efb02 Mon Sep 17 00:00:00 2001 From: Mark Crossley <1196094+mcrossley@users.noreply.github.com> Date: Mon, 31 Jan 2022 13:44:46 +0000 Subject: [PATCH 8/8] Fix real time not running correctly Fix caught exception in Ecowitt API when there is no data returned --- CumulusMX/Cumulus.cs | 3 ++- CumulusMX/EcowittApi.cs | 14 ++++++++++++-- CumulusMX/Properties/AssemblyInfo.cs | 6 +++--- Updates.txt | 2 +- 4 files changed, 18 insertions(+), 7 deletions(-) diff --git a/CumulusMX/Cumulus.cs b/CumulusMX/Cumulus.cs index 1130056c..aec7e7ff 100644 --- a/CumulusMX/Cumulus.cs +++ b/CumulusMX/Cumulus.cs @@ -2720,9 +2720,10 @@ internal void RealtimeTimerTick(object sender, ElapsedEventArgs elapsedEventArgs return; } - if ((!station.PressReadyToPlot || station.TempReadyToPlot || station.WindReadyToPlot) && !StationOptions.NoSensorCheck) + if ((!station.PressReadyToPlot || !station.TempReadyToPlot || !station.WindReadyToPlot) && !StationOptions.NoSensorCheck) { // not all the data is ready and NoSensorCheck is not enabled + LogMessage($"Realtime[{cycle}]: Not all data is ready, aborting process"); return; } diff --git a/CumulusMX/EcowittApi.cs b/CumulusMX/EcowittApi.cs index 1d20cb7f..4a28653e 100644 --- a/CumulusMX/EcowittApi.cs +++ b/CumulusMX/EcowittApi.cs @@ -199,8 +199,16 @@ internal bool GetHistoricData(DateTime startTime, DateTime endTime) { try { - data = histObj.data; - success = true; + if (histObj.data != null) + { + data = histObj.data; + success = true; + } + else + { + // There was no data returned. + return false; + } } catch (Exception ex) { @@ -1317,6 +1325,8 @@ internal class EcowittHistoricResp public EcowittHistoricData data { get; set; } } + //TODO: OK this works, but ouch! + // refactor data as a dictionary object and parse each item indivually internal class EcowittHistoricData { public EcowittHistoricTempHum indoor { get; set; } diff --git a/CumulusMX/Properties/AssemblyInfo.cs b/CumulusMX/Properties/AssemblyInfo.cs index 238f387d..cc35a2d3 100644 --- a/CumulusMX/Properties/AssemblyInfo.cs +++ b/CumulusMX/Properties/AssemblyInfo.cs @@ -6,7 +6,7 @@ // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("Cumulus MX")] -[assembly: AssemblyDescription("Version 3.15.0 - Build 3167")] +[assembly: AssemblyDescription("Version 3.15.0 - Build 3169")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Cumulus MX")] @@ -32,5 +32,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("3.15.0.3167")] -[assembly: AssemblyFileVersion("3.15.0.3167")] +[assembly: AssemblyVersion("3.15.0.3169")] +[assembly: AssemblyFileVersion("3.15.0.3169")] diff --git a/Updates.txt b/Updates.txt index 6f56ae80..ba5b1cba 100644 --- a/Updates.txt +++ b/Updates.txt @@ -1,4 +1,4 @@ -3.15.0 - b3167 +3.15.0 - b3169 —————————————— - Fix: Prevent real time processing occurring before first data has been received - Fix: Davis WLL: Add missing decode of THSW from current data