diff --git a/CMakeLists.txt b/CMakeLists.txt index b59dcf9..3d074ab 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,7 +17,7 @@ if (POLICY CMP0141) endif() project (GoveeBTTempLogger - VERSION 2.20230922.7 + VERSION 2.20230928.1 DESCRIPTION "Listen and log Govee Thermometer Bluetooth Low Energy Advertisments" HOMEPAGE_URL https://github.com/wcbonner/GoveeBTTempLogger ) diff --git a/goveebttemplogger.cpp b/goveebttemplogger.cpp index e0925c8..f021451 100644 --- a/goveebttemplogger.cpp +++ b/goveebttemplogger.cpp @@ -24,8 +24,8 @@ ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// -// GoveeBTTempLogger is designed as a project to run on a Raspberry Pi with -// Bluetooth Low Energy support. It listens for advertisments from Govee +// GoveeBTTempLogger is designed as a project to run on a Raspberry Pi with +// Bluetooth Low Energy support. It listens for advertisments from Govee // https://www.govee.com/product/thermometers-hygrometers/indoor-thermometers-hygrometers // Currently the H5074, GVH5075, and GVH5177 are decoded and logged. // Each unit has its data logged to it's own file, with a new file created daily. @@ -925,6 +925,8 @@ bool GenerateLogFile(std::map> &AddressTemperat bool rval = false; if (!LogDirectory.empty()) { + if (ConsoleVerbosity > 1) + std::cout << "[" << getTimeISO8601() << "] GenerateLogFile: " << LogDirectory << std::endl; for (auto it = AddressTemperatureMap.begin(); it != AddressTemperatureMap.end(); ++it) { if (!it->second.empty()) // Only open the log file if there are entries to add @@ -980,7 +982,7 @@ bool GenerateLogFile(std::map> &AddressTemperat } else { - // clear the queued data if LogDirectory not specified + // clear the queued data if LogDirectory not specified for (auto it = AddressTemperatureMap.begin(); it != AddressTemperatureMap.end(); ++it) { while (!it->second.empty()) @@ -1065,7 +1067,7 @@ void GetMRTGOutput(const std::string &TextAddress, const int Minutes) } ///////////////////////////////////////////////////////////////////////////// std::map> GoveeMRTGLogs; // memory map of BT addresses and vector structure similar to MRTG Log Files -std::map GoveeBluetoothTitles; +std::map GoveeBluetoothTitles; ///////////////////////////////////////////////////////////////////////////// std::filesystem::path GenerateCacheFileName(const bdaddr_t& a) { @@ -1073,9 +1075,9 @@ std::filesystem::path GenerateCacheFileName(const bdaddr_t& a) for (auto pos = btAddress.find(':'); pos != std::string::npos; pos = btAddress.find(':')) btAddress.erase(pos, 1); std::ostringstream OutputFilename; - OutputFilename << "gvh-cache-"; + OutputFilename << "gvh-"; OutputFilename << btAddress; - OutputFilename << ".txt"; + OutputFilename << "-cache.txt"; std::filesystem::path CacheFileName(CacheDirectory / OutputFilename.str()); return(CacheFileName); } @@ -1093,6 +1095,8 @@ bool GenerateCacheFile(const bdaddr_t& a, const std::vector& GoveeMR bCacheOldOrNonexistant = false; if (bCacheOldOrNonexistant) { + if (ConsoleVerbosity > 1) + std::cout << "[" << getTimeISO8601() << "] GenerateCacheFile: " << MRTGCacheFile << std::endl; std::ofstream CacheFile(MRTGCacheFile, std::ios_base::out | std::ios_base::trunc); if (CacheFile.is_open()) { @@ -1114,22 +1118,34 @@ bool GenerateCacheFile(const bdaddr_t& a, const std::vector& GoveeMR } return(rval); } +void GenerateCacheFile(std::map> &AddressTemperatureMap) +{ + if (!CacheDirectory.empty()) + { + if (ConsoleVerbosity > 1) + std::cout << "[" << getTimeISO8601() << "] GenerateCacheFile: " << CacheDirectory << std::endl; + for (auto it = AddressTemperatureMap.begin(); it != AddressTemperatureMap.end(); ++it) + GenerateCacheFile(it->first, it->second); + } +} void ReadCacheDirectory(void) { if (!CacheDirectory.empty()) { + if (ConsoleVerbosity > 1) + std::cout << "[" << getTimeISO8601() << "] ReadCacheDirectory: " << CacheDirectory << std::endl; std::deque files; for (auto const& dir_entry : std::filesystem::directory_iterator{ CacheDirectory }) if (dir_entry.is_regular_file()) if (dir_entry.path().extension() == ".txt") - if (dir_entry.path().stem().string().substr(0, 10) == "gvh-cache-") + if (dir_entry.path().stem().string().find("cache") != std::string::npos) files.push_back(dir_entry); if (!files.empty()) { sort(files.begin(), files.end()); while (!files.empty()) { - auto ssBTAddress = files.begin()->stem().string().substr(10, 12); // new filename format (2023-04-03) + auto ssBTAddress = files.begin()->stem().string().substr(4, 12); // new cache filename format (2023-09-28) for (auto index = ssBTAddress.length() - 2; index > 0; index -= 2) ssBTAddress.insert(index, ":"); bdaddr_t TheBlueToothAddress; @@ -1648,7 +1664,7 @@ void UpdateMRTGData(const bdaddr_t& TheAddress, Govee_Temp& TheValue) DaySampleFirst->Time = (DaySampleFirst + 1)->Time + DAY_SAMPLE; if (DaySampleFirst->GetTimeGranularity() == Govee_Temp::granularity::year) { - if (ConsoleVerbosity > 1) + if (ConsoleVerbosity > 2) std::cout << "[" << getTimeISO8601() << "] shuffling year " << timeToExcelLocal(DaySampleFirst->Time) << " > " << timeToExcelLocal(YearSampleFirst->Time) << std::endl; // shuffle all the year samples toward the end std::copy_backward(YearSampleFirst, YearSampleLast - 1, YearSampleLast); @@ -1659,7 +1675,7 @@ void UpdateMRTGData(const bdaddr_t& TheAddress, Govee_Temp& TheValue) if ((DaySampleFirst->GetTimeGranularity() == Govee_Temp::granularity::year) || (DaySampleFirst->GetTimeGranularity() == Govee_Temp::granularity::month)) { - if (ConsoleVerbosity > 1) + if (ConsoleVerbosity > 2) std::cout << "[" << getTimeISO8601() << "] shuffling month " << timeToExcelLocal(DaySampleFirst->Time) << std::endl; // shuffle all the month samples toward the end std::copy_backward(MonthSampleFirst, MonthSampleLast - 1, MonthSampleLast); @@ -1671,7 +1687,7 @@ void UpdateMRTGData(const bdaddr_t& TheAddress, Govee_Temp& TheValue) (DaySampleFirst->GetTimeGranularity() == Govee_Temp::granularity::month) || (DaySampleFirst->GetTimeGranularity() == Govee_Temp::granularity::week)) { - if (ConsoleVerbosity > 1) + if (ConsoleVerbosity > 2) std::cout << "[" << getTimeISO8601() << "] shuffling week " << timeToExcelLocal(DaySampleFirst->Time) << std::endl; // shuffle all the month samples toward the end std::copy_backward(WeekSampleFirst, WeekSampleLast - 1, WeekSampleLast); @@ -1722,6 +1738,9 @@ void ReadLoggedData(void) { if (!LogDirectory.empty()) { + ReadCacheDirectory(); // if cache directory is configured, read it before reading all the raw data + if (ConsoleVerbosity > 1) + std::cout << "[" << getTimeISO8601() << "] ReadLoggedData: " << LogDirectory << std::endl; std::deque files; for (auto const& dir_entry : std::filesystem::directory_iterator{ LogDirectory }) if (dir_entry.is_regular_file()) @@ -2193,11 +2212,11 @@ int bt_LEScan(int BlueToothDevice_Handle, const bool enable, const std::set 1) - { + { std::cout << "[" << getTimeISO8601() << "] [" << ba2string(GoveeBTAddress) << "] ==> BT_ATT_OP_WRITE_REQ Handle: "; std::cout << std::hex << std::setfill('0') << std::setw(4) << pkt.handle << " Value: "; for (auto index = 0; index < sizeof(pkt.buf) / sizeof(pkt.buf[0]); index++) @@ -3097,7 +3116,7 @@ int main(int argc, char **argv) SVGTitleMapFilename = TitleMapFilename.str(); } ReadTitleMap(SVGTitleMapFilename); - ReadLoggedData(); + ReadLoggedData(); // only read the logged data if creating SVG files WriteAllSVG(); } /////////////////////////////////////////////////////////////////////////////////////////////// @@ -3540,6 +3559,7 @@ int main(int argc, char **argv) signal(SIGINT, previousHandlerSIGINT); // Restore original Ctrl-C signal handler GenerateLogFile(GoveeTemperatures, GoveeLastDownload); // flush contents of accumulated map to logfiles + GenerateCacheFile(GoveeMRTGLogs); // flush FakeMRTG data to cache files if (ConsoleVerbosity > 0) {