#include #include #include #include #include #include #include #include "ble/Bluetooth.h" #include "SoilMoisture.h" #include "trygvis/sensor.h" #include "trygvis/sensor/io.h" #include "apps.h" namespace trygvis { namespace apps { // I'm lazy using namespace std; using namespace std::chrono; using namespace trygvis::apps; using namespace trygvis::bluetooth; using namespace trygvis::sensor; using namespace trygvis::sensor::io; class sm_get_value : public app { public: sm_get_value() : app("sm-get-value") {} ~sm_get_value() = default; bool loop; sample_format_type format; unsigned int sleepTime; vector sensorIndexes; vector> sensors; KeyDictionary dict; const SampleKey *hostname_key = dict.indexOf("hostname"); const SampleKey *device_key = dict.indexOf("device"); const SampleKey *timestamp_key = dict.indexOf("timestamp_ms"); void add_options(po::options_description_easy_init &options) override { auto default_sleep = po::value<>(&sleepTime)->default_value(0); auto default_format = po::value(&format)->default_value(sample_format_type::KEY_VALUE); options("device", po::value()->required(), "MAC of device to poll"); options("sensor", po::value>(&sensorIndexes)->multitoken(), "Sensor to poll, defaults to all"); options("sleep", default_sleep, "How long to sleep in seconds between each poll. If not given, it will exit after first poll"); options("format", default_format, "Output format"); } int main(app_execution &execution) override { BluetoothSystem bluetoothSystem; auto desc = execution.desc; auto vm = execution.vm; try { if (!vm.count("device")) { cerr << "Missing required option: device" << endl; cerr << desc << "\n"; return EXIT_FAILURE; } auto mac_string = vm["device"].as(); Mac mac = Mac::parseMac(mac_string); auto adapter = bluetoothSystem.getAdapter("0"); auto device = adapter->getDevice(mac); loop = sleepTime > 0; do { try { LOG4CPLUS_INFO(execution.logger, "Connecting to device: " << device->getMac().str()); auto gatt = device->connectGatt(); withConnection(format, gatt); } catch (BluetoothException &e) { LOG4CPLUS_ERROR(execution.logger, "Bluetooth error: " << e.what()); } catch (runtime_error &e) { LOG4CPLUS_ERROR(execution.logger, "Exception: " << e.what()); } if (loop) { LOG4CPLUS_DEBUG(execution.logger, "Sleeping for " << std::to_string(sleepTime) << " seconds after failure."); auto targetTime = system_clock::now() + seconds(sleepTime); this_thread::sleep_until(targetTime); } } while (loop); return EXIT_SUCCESS; } catch (std::runtime_error &ex) { cout << "std::runtime_error: " << ex.what() << endl; return EXIT_FAILURE; } catch (std::exception &ex) { cout << "std::exception: " << ex.what() << endl; return EXIT_FAILURE; } } void read_sensors(SoilMoisture &soilMoisture, string mac) { auto epoch = system_clock::now().time_since_epoch(); auto timestamp = duration_cast(epoch).count(); auto unique_output_stream = open_sample_output_stream(shared_ptr(&cout, noop_deleter), dict, format); shared_ptr output_stream{std::move(unique_output_stream)}; auto sample = SampleRecord(dict) .set(hostname_key, get_hostname()) .set(device_key, mac) .set(timestamp_key, std::to_string(timestamp)); auto tempO = soilMoisture.readTemperature(); if (tempO) { sample.set(dict.indexOf("temperature"), std::to_string(tempO.value())); } int i = 0; for (auto s : sensors) { auto sensor = s.first; auto name = s.second; uint16_t value = soilMoisture.getValue((uint8_t) sensor); auto sensor_key = dict.indexOf("sensor" + std::to_string(i)); auto sensor_name_key = dict.indexOf("sensor_name" + std::to_string(i)); sample .set(sensor_key, std::to_string(value)) .set(sensor_name_key, name); i++; } output_stream->onSample(sample); } void withConnection(sample_format_type format, shared_ptr gatt) { SoilMoisture soilMoisture = SoilMoisture::create(gatt); if (sensors.empty()) { const int sensorCount = soilMoisture.getSensorCount(); if (sensorCount == 0) { throw runtime_error("Sensor count is 0"); } // If the user didn't specify any sensors, add all. if (sensors.empty()) { for (unsigned int i = 0; i < sensorCount; i++) { sensorIndexes.push_back(i); } } for_each(begin(sensorIndexes), end(sensorIndexes), [&](uint8_t i) { if (i >= sensorCount) { // Ignore invalid sensors return; } sensors.emplace_back(make_pair(i, soilMoisture.getName(i))); }); } auto mac = gatt->getDevice().getMac().str(); if (!loop) { read_sensors(soilMoisture, mac); } else { time_point targetTime; targetTime = system_clock::now(); do { read_sensors(soilMoisture, mac); do { targetTime = targetTime + seconds(sleepTime); } while (targetTime < system_clock::now()); this_thread::sleep_until(targetTime); } while (loop); } } }; } } int main(int argc, const char *argv[]) { using namespace trygvis::apps; return real_main(new sm_get_value(), argc, argv); }