diff --git a/build/Makefile b/build/Makefile index 2ac6c9b..cb0e5c6 100644 --- a/build/Makefile +++ b/build/Makefile @@ -72,6 +72,8 @@ $(PROJECT_PATH)/gui/gui_string_dialog.cpp \ $(PROJECT_PATH)/gui/gui_timers_dialog.cpp \ $(PROJECT_PATH)/gui/gui_wifi_setup.cpp \ +$(PROJECT_PATH)/gui/gui_wifi_add.cpp \ +$(PROJECT_PATH)/gui/gui_mult_wifi_setup.cpp \ $(PROJECT_PATH)/gui/scope_display.cpp @@ -81,9 +83,9 @@ CPPDEFS += _FILE_OFFSET_BITS=64 __WXGTK__ wxDEBUG_LEVEL=0 CFLAGS += -CXXFLAGS += -Wno-unused-parameter `wx-config --cxxflags` +CXXFLAGS += -std=c++20 -Wno-unused-parameter `wx-config --cxxflags` -LDFLAGS += -lstdc++ -lbluetooth -lm -ldl `wx-config --libs` +LDFLAGS += -lstdc++ -lbluetooth -lm -ldl `wx-config --libs` `pkg-config --libs dbus-1` LIBS += $(POWERMON_LIB_PATH)/powermon_lib.a diff --git a/build/set_perm.sh b/build/set_perm.sh deleted file mode 100755 index 6295b47..0000000 --- a/build/set_perm.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash -e - -setcap 'cap_net_raw,cap_net_admin+eip' bin/powermon-manager diff --git a/gui/gui_about.cpp b/gui/gui_about.cpp index 49ebfbb..231c963 100644 --- a/gui/gui_about.cpp +++ b/gui/gui_about.cpp @@ -53,7 +53,7 @@ str.Printf(wxT("Firmware: v%X.%02X"), version >> 8, version & 0xFF); main_sizer->Add(new wxStaticText(this, wxID_ANY, str), 0, wxALL | wxEXPAND, 5); - if (Powermon::hasWifi(mPowermon.getLastDeviceInfo().hardware_revision_bcd)) + if (Powermon::hasNetwork(mPowermon.getLastDeviceInfo().hardware_revision_bcd)) str = wxString(wxT("Device IP Address: ")) + Powermon::getIpAddressString(mPowermon.getLastDeviceInfo().address); else str = wxString(wxT("Device Bluetooth Address: ")) + Powermon::getMacAddressString(mPowermon.getLastDeviceInfo().address); diff --git a/gui/gui_device.cpp b/gui/gui_device.cpp index 660a8c8..905d329 100644 --- a/gui/gui_device.cpp +++ b/gui/gui_device.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -35,6 +36,8 @@ #include +#include + #define MONITOR_FONT wxFont(18, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL) #define MONITOR_FONT_BOLD wxFont(18, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD) @@ -202,7 +205,7 @@ if (scanner) scanner->startBleScan(); - mPowermon.requestGetInfo([this](uint16_t status, const Powermon::DeviceInfo &info) + mPowermon.requestGetInfo([this](Powermon::ResponseCode status, const Powermon::DeviceInfo &info) { wxQueueEvent(this, new GuiEvent([this, status, info](const GuiEvent &event) { @@ -268,7 +271,7 @@ //start the thread and connect SetStatusText(wxT("Connecting ...")); - if (Powermon::hasWifi(id.hardware_revision_bcd)) + if (Powermon::hasNetwork(id.hardware_revision_bcd)) { if (id.address) mPowermon.connectWifi(id.address); @@ -320,6 +323,12 @@ mSettingsMenu->Enable(ID_MENU_CALIBRATE_CURRENT, state); mSettingsMenu->Enable(ID_MENU_FORCE_FG_SYNC, state); + + const uint8_t hw = mPowermon.getLastDeviceInfo().hardware_revision_bcd; + if (Powermon::hasWifi(hw) == false) + { + mSettingsMenu->Remove(ID_MENU_WIFI_SETUP); + } } @@ -338,7 +347,7 @@ mLogFiles.clear(); - mPowermon.requestGetLogFileList([this](uint16_t status, const std::vector &list) + mPowermon.requestGetLogFileList([this](Powermon::ResponseCode status, const std::vector &list) { if (status == Powermon::RSP_SUCCESS) { @@ -422,10 +431,10 @@ void GuiDevice::readFileBlock(void) { - const uint32_t block_size = Powermon::hasWifi(mPowermon.getLastDeviceInfo().hardware_revision_bcd) ? 128 * 1024 : 4 * 1024; + const uint32_t block_size = Powermon::hasNetwork(mPowermon.getLastDeviceInfo().hardware_revision_bcd) ? 128 * 1024 : 4 * 1024; mPowermon.requestReadLogFile(mLogFiles.front().id, mFileSize, block_size, - [this](uint16_t status, const uint8_t* data, size_t size) + [this](Powermon::ResponseCode status, const uint8_t* data, size_t size) { if ((status == Powermon::RSP_SUCCESS) && size) { @@ -460,7 +469,7 @@ void GuiDevice::startDevice(void) { - if (Powermon::hasWifi(mPowermon.getLastDeviceInfo().hardware_revision_bcd)) + if (Powermon::hasNetwork(mPowermon.getLastDeviceInfo().hardware_revision_bcd)) mMonitorDataTimer->Start(mPowermon.isLocalConnection() ? 1000 : 2000); Model::getInstance().setDevice(mPowermon.getLastDeviceInfo().serial); @@ -474,8 +483,9 @@ { GuiEnterString dialog(this, wxID_ANY, wxT("Enter New Name"), false, false); - dialog.setStringLabel(wxT("Enter name")); - dialog.setMaxStringLength(Powermon::hasWifi(mPowermon.getLastDeviceInfo().hardware_revision_bcd) ? MAX_WIFI_NAME_LENGTH : MAX_BLE_NAME_LENGTH); + dialog.setStringLabel(wxT("New Name")); + dialog.setMaxStringLength(Powermon::hasNetwork(mPowermon.getLastDeviceInfo().hardware_revision_bcd) ? MAX_NAME_LENGTH : MAX_BLE_NAME_LENGTH); + dialog.setString(mPowermon.getLastDeviceInfo().name); dialog.SetFocus(); if (dialog.ShowModal() == wxID_OK) @@ -483,7 +493,7 @@ const wxString name = dialog.getString(); mPowermon.requestRename(name.mb_str(), - [this, name](uint16_t status) + [this, name](Powermon::ResponseCode status) { wxQueueEvent(this, new GuiEvent([this, status, name](const GuiEvent &event) { @@ -502,7 +512,7 @@ wxMessageDialog msg(this, wxT("Reset the energy meter ?"), wxT("Confirmation"), wxOK | wxCANCEL | wxICON_QUESTION | wxSTAY_ON_TOP | wxCENTRE); if (msg.ShowModal() == wxID_OK) { - mPowermon.requestResetEnergyMeter([this](uint16_t status) + mPowermon.requestResetEnergyMeter([this](Powermon::ResponseCode status) { wxQueueEvent(this, new GuiEvent([this, status](const GuiEvent& event) { @@ -518,7 +528,7 @@ wxMessageDialog msg(this, wxT("Reset the coulomb meter ?"), wxT("Confirmation"), wxOK | wxCANCEL | wxICON_QUESTION | wxSTAY_ON_TOP | wxCENTRE); if (msg.ShowModal() == wxID_OK) { - mPowermon.requestResetCoulombMeter([this](uint16_t status) + mPowermon.requestResetCoulombMeter([this](Powermon::ResponseCode status) { wxQueueEvent(this, new GuiEvent([this, status](const GuiEvent& event) { @@ -534,7 +544,7 @@ wxMessageDialog msg(this, wxT("Force the fuel gauge SoC synchronization ?"), wxT("Confirmation"), wxOK | wxCANCEL | wxICON_QUESTION | wxSTAY_ON_TOP | wxCENTRE); if (msg.ShowModal() == wxID_OK) { - mPowermon.requestFgSynchronize([this](uint16_t status) + mPowermon.requestFgSynchronize([this](Powermon::ResponseCode status) { wxQueueEvent(this, new GuiEvent([this, status](const GuiEvent &event) { @@ -556,7 +566,7 @@ void GuiDevice::OnMenuDeviceViewFgStats(wxCommandEvent &event) { - mPowermon.requestGetFgStatistics([this](uint16_t status, const Powermon::FuelgaugeStatistics &stats) + mPowermon.requestGetFgStatistics([this](Powermon::ResponseCode status, const Powermon::FuelgaugeStatistics &stats) { wxQueueEvent(this, new GuiEvent([this, status, stats](const GuiEvent &event) { @@ -577,7 +587,7 @@ wxMessageDialog msg(this, wxT("Calibrate the current offset ?"), wxT("Confirmation"), wxOK | wxCANCEL | wxICON_QUESTION | wxSTAY_ON_TOP | wxCENTRE); if (msg.ShowModal() == wxID_OK) { - mPowermon.requestZeroCurrentOffset([this](uint16_t status) + mPowermon.requestZeroCurrentOffset([this](Powermon::ResponseCode status) { wxQueueEvent(this, new GuiEvent([this, status](const GuiEvent &event) { @@ -594,7 +604,7 @@ if (dialog.ShowModal() == wxID_OK) { - mPowermon.requestCalibrateCurrent(dialog.getValue(), [this](uint16_t status) + mPowermon.requestCalibrateCurrent(dialog.getValue(), [this](Powermon::ResponseCode status) { wxQueueEvent(this, new GuiEvent([this, status](const GuiEvent &event) { @@ -611,7 +621,7 @@ msg.SetOKCancelLabels(wxT("ON"), wxT("OFF")); const bool power_state = (msg.ShowModal() == wxID_OK); - mPowermon.requestSetPowerState(power_state, [this](uint16_t status) + mPowermon.requestSetPowerState(power_state, [this](Powermon::ResponseCode status) { wxQueueEvent(this, new GuiEvent([this, status](const GuiEvent &event) { @@ -634,7 +644,7 @@ if (msg.ShowModal() != wxID_OK) return; - mPowermon.requestResetConfig([this](uint16_t status) + mPowermon.requestResetConfig([this](Powermon::ResponseCode status) { wxQueueEvent(this, new GuiEvent([this, status](const GuiEvent &event) { @@ -650,7 +660,7 @@ void GuiDevice::OnMenuSettingsConfiguration(wxCommandEvent &event) { - mPowermon.requestGetConfig([this](uint16_t status, const PowermonConfig &config) + mPowermon.requestGetConfig([this](Powermon::ResponseCode status, const PowermonConfig &config) { wxQueueEvent(this, new GuiEvent([this, status, config](const GuiEvent &event) { @@ -660,11 +670,16 @@ if (dialog.ShowModal() == wxID_OK) { - mPowermon.requestSetConfig(dialog.getConfiguration(), [this](uint16_t status) + mPowermon.requestSetConfig(dialog.getConfiguration(), [this](Powermon::ResponseCode status) { wxQueueEvent(this, new GuiEvent([this, status](const GuiEvent &event) { checkDeviceError(this, status); + + mPowermon.requestGetInfo([this](Powermon::ResponseCode status, const Powermon::DeviceInfo &info) + { + checkDeviceError(this, status); + }); })); }); @@ -688,8 +703,11 @@ wxMessageDialog msg(this, message_str, wxT("Confirmation"), wxOK | wxCANCEL | wxICON_QUESTION | wxSTAY_ON_TOP | wxCENTRE); if (msg.ShowModal() == wxID_OK) { - mPowermon.requestSetTime(wxGetLocalTime() + (dt.IsDST() ? 3600 : 0), - [this](uint16_t status) + const int8_t tzIndex = mPowermon.getLastDeviceInfo().timezone; + const uint32_t time = tzIndex >= 0 ? wxGetUTCTime() : wxGetLocalTime(); + + mPowermon.requestSetTime(time, + [this](Powermon::ResponseCode status) { wxQueueEvent(this, new GuiEvent([this, status](const GuiEvent &event) { @@ -702,7 +720,7 @@ void GuiDevice::OnMenuDeviceTimers(wxCommandEvent &event) { - mPowermon.requestGetSchedules([this](uint16_t status, const std::vector &schedules) + mPowermon.requestGetSchedules([this](Powermon::ResponseCode status, const std::vector &schedules) { wxQueueEvent(this, new GuiEvent([this, status, schedules](const GuiEvent &event) { @@ -714,7 +732,7 @@ { const std::vector new_schedules = dialog.getSchedules(); - mPowermon.requestClearSchedules([this, new_schedules](uint16_t status) + mPowermon.requestClearSchedules([this, new_schedules](Powermon::ResponseCode status) { wxQueueEvent(this, new GuiEvent([this, status, new_schedules](const GuiEvent &event) { @@ -722,7 +740,7 @@ { if (new_schedules.size()) { - mPowermon.requestAddSchedules(new_schedules, [this](uint16_t status) + mPowermon.requestAddSchedules(new_schedules, [this](Powermon::ResponseCode status) { wxQueueEvent(this, new GuiEvent([this, status](const GuiEvent &event) { @@ -731,7 +749,7 @@ }); } - mPowermon.requestCommitSchedules([this](uint16_t status) + mPowermon.requestCommitSchedules([this](Powermon::ResponseCode status) { wxQueueEvent(this, new GuiEvent([this, status](const GuiEvent &event) { @@ -753,7 +771,7 @@ stopSync(); Model::getInstance().deleteLogData(); - mPowermon.requestClearLog([this](uint16_t status) + mPowermon.requestClearLog([this](Powermon::ResponseCode status) { wxQueueEvent(this, new GuiEvent([this, status](const GuiEvent &event) { @@ -803,7 +821,7 @@ { const Powermon::AuthKey key = Powermon::getAuthKeyFromPassword(dialog.getString().mb_str()); - mPowermon.requestSetUserPasswordLock(key, [this](uint16_t status) + mPowermon.requestSetUserPasswordLock(key, [this](Powermon::ResponseCode status) { wxQueueEvent(this, new GuiEvent([this, status](const GuiEvent &event) { @@ -813,7 +831,7 @@ } else { - mPowermon.requestClearUserPasswordLock([this](uint16_t status) + mPowermon.requestClearUserPasswordLock([this](Powermon::ResponseCode status) { wxQueueEvent(this, new GuiEvent([this, status](const GuiEvent &event) { @@ -845,7 +863,7 @@ { const Powermon::AuthKey key = Powermon::getAuthKeyFromPassword(dialog.getString().mb_str()); - mPowermon.requestSetMasterPasswordLock(key, [this](uint16_t status) + mPowermon.requestSetMasterPasswordLock(key, [this](Powermon::ResponseCode status) { wxQueueEvent(this, new GuiEvent([this, status](const GuiEvent &event) { @@ -855,7 +873,7 @@ } else { - mPowermon.requestClearMasterPasswordLock([this](uint16_t status) + mPowermon.requestClearMasterPasswordLock([this](Powermon::ResponseCode status) { wxQueueEvent(this, new GuiEvent([this, status](const GuiEvent &event) { @@ -875,14 +893,22 @@ void GuiDevice::OnMenuSettingsWifiSetup(wxCommandEvent &event) { - GuiWifiSetup setup(this, mPowermon); - setup.ShowModal(); + if (mPowermon.getLastDeviceInfo().firmware_version_bcd >= 0x120) + { + GuiMultipleWifiSetup setup(this, mPowermon); + setup.ShowModal(); + } + else + { + GuiWifiSetup setup(this, mPowermon); + setup.ShowModal(); + } } void GuiDevice::OnMenuDevicePair(wxCommandEvent &event) { - mPowermon.requestGetAccessKeys([this](uint16_t status, const Powermon::WifiAccessKey &key) + mPowermon.requestGetAccessKeys([this](Powermon::ResponseCode status, const Powermon::WifiAccessKey &key) { wxQueueEvent(this, new GuiEvent([this, status, key](const GuiEvent &event) { @@ -974,7 +1000,7 @@ void GuiDevice::OnMonitorDataTimerEvent(wxTimerEvent &event) { - mPowermon.requestGetMonitorData([this](uint16_t status, const Powermon::MonitorData &data) + mPowermon.requestGetMonitorData([this](Powermon::ResponseCode status, const Powermon::MonitorData &data) { if (status == Powermon::RSP_SUCCESS) { @@ -1002,7 +1028,7 @@ { const Powermon::AuthKey key = Powermon::getAuthKeyFromPassword(dialog.getString().mb_str()); - mPowermon.requestUnlock(key, [this, disconnect_on_failure](uint16_t status) + mPowermon.requestUnlock(key, [this, disconnect_on_failure](Powermon::ResponseCode status) { wxQueueEvent(this, new GuiEvent([this, status, disconnect_on_failure](const GuiEvent &event) { @@ -1160,13 +1186,27 @@ if (mMonitorData.time) { - wxDateTime dt; - dt.Set((time_t)mMonitorData.time); - dt.MakeUTC(); + std::chrono::sys_seconds time = std::chrono::sys_seconds{std::chrono::seconds{mMonitorData.time}}; + std::chrono::zoned_time zt; - str = dt.Format(wxT("%I:%M %p")).mb_str(); + const int8_t tzIndex = mPowermon.getLastDeviceInfo().timezone; + + if (tzIndex >= 0) + { + const char* tzString = PowermonConfig::timeZones[tzIndex]; + const std::chrono::time_zone* tz = std::chrono::locate_zone(tzString); + zt = std::chrono::zoned_time{tz, time}; + } + else + { + const std::chrono::time_zone* tz = std::chrono::locate_zone("UTC"); + zt = std::chrono::zoned_time{tz, time}; + } + + std::string str = std::format("{:%I:%M %p}", zt); dc.DrawText(str, size.x - dc.GetTextExtent(str).x - RIGHT_BORDER, y); y += row_height; - str = dt.Format(wxT("%b %d, %Y")); + + str = std::format("{:%b %d, %Y}", zt); dc.DrawText(str, size.x - dc.GetTextExtent(str).x - RIGHT_BORDER, y); y += row_height; } else diff --git a/gui/gui_device_config.cpp b/gui/gui_device_config.cpp index 6f1e21a..1e47686 100644 --- a/gui/gui_device_config.cpp +++ b/gui/gui_device_config.cpp @@ -93,10 +93,43 @@ if (Powermon::hasWifi(mPowermon.getLastDeviceInfo().hardware_revision_bcd)) { - mWifiKeepApOnCheckBox = new wxCheckBox(panel, wxID_ANY, wxT("Keep Access Point ON"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); + mWifiKeepApOnCheckBox = new wxCheckBox(panel, wxID_ANY, wxT("Keep Access Point On"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); main_v_sizer->Add(mWifiKeepApOnCheckBox, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + mWifiDisablePowerSavingCheckBox = new wxCheckBox(panel, wxID_ANY, wxT("Disable WiFi Power Saving"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); + main_v_sizer->Add(mWifiDisablePowerSavingCheckBox, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + mWifiWatchdogEnableCheckBox = new wxCheckBox(panel, wxID_ANY, wxT("WiFi Watchdog"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); + main_v_sizer->Add(mWifiWatchdogEnableCheckBox, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); } + if (Powermon::hasEthernet(mPowermon.getLastDeviceInfo().hardware_revision_bcd)) + { + mEthKeepLinkLedOnCheckBox = new wxCheckBox(panel, wxID_ANY, wxT("Keep Ethernet Link LED On"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); + main_v_sizer->Add(mEthKeepLinkLedOnCheckBox, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + } + + if (Powermon::hasNetwork(mPowermon.getLastDeviceInfo().hardware_revision_bcd)) + { + mNoInternetEnableCheckBox = new wxCheckBox(panel, wxID_ANY, wxT("Use Without Internet"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); + main_v_sizer->Add(mNoInternetEnableCheckBox, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + mNtpEnableCheckBox = new wxCheckBox(panel, wxID_ANY, wxT("Set Time Automatically"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); + main_v_sizer->Add(mNtpEnableCheckBox, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); + + mTimezoneChoice = new wxChoice(panel, ID_CHOICE_SHUNT, wxDefaultPosition, CTRL_SIZE); + for(uint32_t i = 0; i < sizeof(PowermonConfig::timeZones) / sizeof(char*); i++) + { + std::string s = PowermonConfig::timeZones[i]; + std::replace(s.begin(), s.end(), '_', ' '); + mTimezoneChoice->Append(s); + } + + v_sizer = new wxBoxSizer(wxVERTICAL); + v_sizer->Add(new wxStaticText(panel, wxID_ANY, wxT("Device Timezone"), wxDefaultPosition, wxDefaultSize), 0, wxBOTTOM | wxALIGN_LEFT, GAP_SIZE); + v_sizer->Add(mTimezoneChoice, 0, wxALL, 0); + main_v_sizer->Add(v_sizer, 0, wxALL, BORDER_SIZE); + } if (Powermon::hasConfigurableShunt(mPowermon.getLastDeviceInfo().hardware_revision_bcd)) { @@ -117,14 +150,12 @@ v_sizer->Add(mMaxShuntCurrentText, 0, wxALL, 0); main_v_sizer->Add(v_sizer, 0, wxALL, BORDER_SIZE); - if (Powermon::hasConfigurableShunt(mPowermon.getLastDeviceInfo().hardware_revision_bcd)) { mAdcFlipCurrentSignCheckBox = new wxCheckBox(panel, wxID_ANY, wxT("Flip Current Sign"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); main_v_sizer->Add(mAdcFlipCurrentSignCheckBox, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); } - mAdcDisableV2CheckBox = new wxCheckBox(panel, ID_CHECKBOX_DISABLE_V2, wxT("Disable Voltage 2"), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); main_v_sizer->Add(mAdcDisableV2CheckBox, 0, wxALL | wxALIGN_LEFT, BORDER_SIZE); @@ -596,9 +627,25 @@ main_sizer->SetSizeHints(this); - if (Powermon::hasWifi(mPowermon.getLastDeviceInfo().hardware_revision_bcd)) + { mWifiKeepApOnCheckBox->SetValue(mConfig.getWiFiKeepAPOn()); + mWifiDisablePowerSavingCheckBox->SetValue(mConfig.getWiFiDisablePowerSaving()); + mWifiWatchdogEnableCheckBox->SetValue(mConfig.getWiFiWatchdogEnable()); + } + + if (Powermon::hasEthernet(mPowermon.getLastDeviceInfo().hardware_revision_bcd)) + { + mEthKeepLinkLedOnCheckBox->SetValue(mConfig.getEthKeepLinkLedOn()); + } + + if (Powermon::hasNetwork(mPowermon.getLastDeviceInfo().hardware_revision_bcd)) + { + mNoInternetEnableCheckBox->SetValue(mConfig.getNoInternetEnable()); + mNtpEnableCheckBox->SetValue(mConfig.getNtpEnable()); + + mTimezoneChoice->SetSelection(mConfig.getTimeZone()); + } if (Powermon::hasConfigurableShunt(mPowermon.getLastDeviceInfo().hardware_revision_bcd)) { @@ -995,7 +1042,28 @@ try { if (Powermon::hasWifi(mPowermon.getLastDeviceInfo().hardware_revision_bcd)) + { mConfig.setWiFiKeepAPOn(mWifiKeepApOnCheckBox->GetValue()); + mConfig.setWiFiDisablePowerSaving(mWifiDisablePowerSavingCheckBox->GetValue()); + mConfig.setWiFiWatchdogEnable(mWifiWatchdogEnableCheckBox->GetValue()); + } + + if (Powermon::hasEthernet(mPowermon.getLastDeviceInfo().hardware_revision_bcd)) + { + mConfig.setEthKeepLinkLedOn(mEthKeepLinkLedOnCheckBox->GetValue()); + } + + if (Powermon::hasNetwork(mPowermon.getLastDeviceInfo().hardware_revision_bcd)) + { + mConfig.setNoInternetEnable(mNoInternetEnableCheckBox->GetValue()); + mConfig.setNtpEnable(mNtpEnableCheckBox->GetValue()); + + const int32_t selected = mTimezoneChoice->GetCurrentSelection(); + if (selected < 0) + throw ParserException("Timezone not selected"); + + mConfig.setTimeZone((PowermonConfig::TimeZone)selected); + } if (Powermon::hasConfigurableShunt(mPowermon.getLastDeviceInfo().hardware_revision_bcd)) { diff --git a/gui/gui_device_config.h b/gui/gui_device_config.h index a2e66ac..2aa4e33 100644 --- a/gui/gui_device_config.h +++ b/gui/gui_device_config.h @@ -47,6 +47,7 @@ enum { ID_CHOICE_SHUNT = wxID_HIGHEST + 1, + ID_CHOICE_TIMEZONE, ID_CHECKBOX_DISABLE_V2, ID_CHECKBOX_ENABLE_FEATURE, ID_CHECKBOX_ENABLE_GENERATOR, @@ -67,6 +68,14 @@ wxListbook* mListBook; wxCheckBox* mWifiKeepApOnCheckBox; + wxCheckBox* mWifiDisablePowerSavingCheckBox; + wxCheckBox* mWifiWatchdogEnableCheckBox; + wxCheckBox* mNtpEnableCheckBox; + wxCheckBox* mNoInternetEnableCheckBox; + wxCheckBox* mEthKeepLinkLedOnCheckBox; + + wxChoice* mTimezoneChoice; + wxChoice* mShuntChoice; wxTextCtrl* mMaxShuntCurrentText; diff --git a/gui/gui_dfu_progress.cpp b/gui/gui_dfu_progress.cpp index 73804c2..b46382d 100644 --- a/gui/gui_dfu_progress.cpp +++ b/gui/gui_dfu_progress.cpp @@ -79,7 +79,7 @@ return true; }, - [this](uint16_t status) + [this](Powermon::ResponseCode status) { mInProgress = false; diff --git a/gui/gui_global.h b/gui/gui_global.h index debf52d..c3ff31c 100644 --- a/gui/gui_global.h +++ b/gui/gui_global.h @@ -70,7 +70,7 @@ }; -static inline bool checkDeviceError(wxWindow* window, uint16_t status) +static inline bool checkDeviceError(wxWindow* window, Powermon::ResponseCode status) { switch(status) { diff --git a/gui/gui_main.cpp b/gui/gui_main.cpp index 6ee110e..fc0296f 100644 --- a/gui/gui_main.cpp +++ b/gui/gui_main.cpp @@ -55,9 +55,10 @@ mDeviceMenu = new wxMenu; - mDeviceMenu->Append(ID_MENU_ADD_DEVICE, wxT("Add New Device")); - mDeviceMenu->Append(ID_MENU_DIRECT_CONNECT, wxT("WiFi Direct Connect")); + mDeviceMenu->Append(ID_MENU_ADD_DEVICE, wxT("Add Remote Device")); mDeviceMenu->SetHelpString(ID_MENU_ADD_DEVICE, wxT("Add New Remote Device")); + + mDeviceMenu->Append(ID_MENU_DIRECT_CONNECT, wxT("WiFi Direct Connect")); mDeviceMenu->SetHelpString(ID_MENU_DIRECT_CONNECT, wxT("Direct Connection to PowerMon-W Device")); mMenuBar->Append(mDeviceMenu, wxT("&Device")); @@ -117,7 +118,7 @@ mPowermonScanner = PowermonScanner::createInstance(); - mPowermonScanner->setCallback([this](const PowermonScanner::Advertisment &adv) + mPowermonScanner->setCallback([this](const PowermonScanner::Advertisement &adv) { wxQueueEvent(this, new GuiEvent([this, adv](const GuiEvent &event) mutable { @@ -172,7 +173,7 @@ } -void GuiMain::UpdateLocalDeviceList(int32_t index, const PowermonScanner::Advertisment &adv) +void GuiMain::UpdateLocalDeviceList(int32_t index, const PowermonScanner::Advertisement &adv) { wxString str; @@ -265,7 +266,7 @@ std::string strr = Powermon::getIpAddressString(adv.address); - if (Powermon::hasWifi(adv.hardware_revision_bcd)) + if (Powermon::hasNetwork(adv.hardware_revision_bcd)) str = Powermon::getIpAddressString(adv.address); else str = Powermon::getMacAddressString(adv.address); @@ -361,7 +362,7 @@ did.hardware_revision_bcd = device.adv.hardware_revision_bcd; did.address = device.adv.address; - PowermonScanner* scanner = Powermon::hasWifi(device.adv.hardware_revision_bcd) ? nullptr : mPowermonScanner; + PowermonScanner* scanner = Powermon::hasNetwork(device.adv.hardware_revision_bcd) ? nullptr : mPowermonScanner; GuiDevice* device_window = new GuiDevice(this, did, scanner); mDevices.push_back(device_window); diff --git a/gui/gui_main.h b/gui/gui_main.h index 3505b19..b959be6 100644 --- a/gui/gui_main.h +++ b/gui/gui_main.h @@ -52,7 +52,7 @@ struct DeviceListItem { int64_t timestamp; - PowermonScanner::Advertisment adv; + PowermonScanner::Advertisement adv; }; wxTimer *mScrubTimer; @@ -88,7 +88,7 @@ void startConnection(void); - void UpdateLocalDeviceList(int32_t index, const PowermonScanner::Advertisment &adv); + void UpdateLocalDeviceList(int32_t index, const PowermonScanner::Advertisement &adv); DECLARE_EVENT_TABLE() }; diff --git a/gui/gui_mult_wifi_setup.cpp b/gui/gui_mult_wifi_setup.cpp new file mode 100644 index 0000000..e5869e0 --- /dev/null +++ b/gui/gui_mult_wifi_setup.cpp @@ -0,0 +1,259 @@ +/* Copyright (C) 2020 - 2025, Thornwave Labs Inc + * Written by Razvan Turiac + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the “Software”), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * Attribution shall be given to Thornwave Labs Inc. and shall be made visible to the final user. + * + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include +#include + +#include + + +#include + + +GuiMultipleWifiSetup::GuiMultipleWifiSetup(wxWindow* parent, Powermon &powermon): wxDialog(parent, wxID_ANY, wxT("PowerMon WiFi Setup")), mPowermon(powermon) +{ + const size_t BORDER_SIZE = FromDIP(6); + + wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL); + SetSizer(main_sizer); + + mNetworkStatic = new wxStaticText(this, wxID_ANY, wxT("")); + mStatusStatic = new wxStaticText(this, wxID_ANY, wxT("")); + + wxBoxSizer* h_sizer = new wxBoxSizer(wxHORIZONTAL); + h_sizer->Add(new wxStaticText(this, wxID_ANY, wxT("WiFi Network: ")), 0, wxALL | wxEXPAND, BORDER_SIZE); + h_sizer->Add(mNetworkStatic, 0, wxALL | wxEXPAND, BORDER_SIZE); + main_sizer->Add(h_sizer, 0, wxALL | wxEXPAND, 0); + + h_sizer = new wxBoxSizer(wxHORIZONTAL); + h_sizer->Add(new wxStaticText(this, wxID_ANY, wxT("WiFi Status: ")), 0, wxALL | wxEXPAND, BORDER_SIZE); + h_sizer->Add(mStatusStatic, 0, wxALL | wxEXPAND, BORDER_SIZE); + main_sizer->Add(h_sizer, 0, wxDOWN | wxEXPAND, 20); + + + main_sizer->Add(new wxStaticText(this, wxID_ANY, wxT("Known WiFi Networks")), 0, wxBOTTOM | wxEXPAND, BORDER_SIZE); + + mNetworksListCtrl = new wxListCtrl(this, ID_LISTBOX_NETWORKS, wxDefaultPosition, wxDefaultSize, wxLC_SINGLE_SEL | wxLC_REPORT | wxLC_VRULES); + main_sizer->Add(mNetworksListCtrl, 0, wxALL | wxEXPAND, 0); + + mNetworksListCtrl->AppendColumn(wxT("Name"), wxLIST_FORMAT_LEFT, FromDIP(120)); + mNetworksListCtrl->AppendColumn(wxT("Properties"), wxLIST_FORMAT_LEFT, FromDIP(300)); + + h_sizer = new wxBoxSizer(wxHORIZONTAL); + mAddButton = new wxButton(this, ID_BUTTON_ADD, wxT("Add Network"), wxDefaultPosition, FromDIP(wxSize(120, 40))); + mAddButton->Enable(false); + h_sizer->Add(mAddButton, 0, wxALL | wxEXPAND, BORDER_SIZE); + + mRemoveButton = new wxButton(this, ID_BUTTON_REMOVE, wxT("Remove Network"), wxDefaultPosition, FromDIP(wxSize(160, 40))); + mRemoveButton->Enable(false); + h_sizer->Add(mRemoveButton, 0, wxALL | wxEXPAND, BORDER_SIZE); + main_sizer->Add(h_sizer, 0, wxDOWN | wxEXPAND, 20); + + main_sizer->SetSizeHints(this); + Centre(); + + + mTimer = new wxTimer(this, ID_TIMER); + mTimer->Start(500); + + UpdateInfo(); + UpdateNetworkList(); +} + + +GuiMultipleWifiSetup::~GuiMultipleWifiSetup() +{ +} + + +void GuiMultipleWifiSetup::OnClose(wxCloseEvent &event) +{ + mTimer->Stop(); + EndModal(wxID_CANCEL); +} + + +void GuiMultipleWifiSetup::OnGuiEvent(GuiEvent &event) +{ + event.process(); +} + + +void GuiMultipleWifiSetup::UpdateInfo(void) +{ + mPowermon.requestGetInfo([this](Powermon::ResponseCode status, const Powermon::DeviceInfo &info) + { + if (status == Powermon::RSP_SUCCESS) + { + debug_printf("\r\nINFO Status: %X", status); + debug_printf("\r\nSerial: %016lX, Name: %s %X", info.serial, info.name.c_str(), info.flags); + + wxQueueEvent(this, new GuiEvent([this, info](GuiEvent &event) + { + char cstr[MAX_WIFI_SSID_SIZE + 1]; + memcpy(cstr, info.ssid, info.ssid_length); + cstr[info.ssid_length] = 0; + + mNetworkStatic->SetLabel(wxString(cstr, wxConvUTF8)); + + if (info.isWifiFailed()) + { + mStatusStatic->SetLabel(wxT("Cannot connect")); + } + else if (info.isWifiConnecting()) + { + mStatusStatic->SetLabel(wxT("Connecting ...")); + } + else if (info.isWifiConnected()) + { + if (info.address) + { + wxString str; + str.Printf(wxT("Connected (%u.%u.%u.%u)"), (info.address >> 0) & 0xFF, + (info.address >> 8) & 0xFF, + (info.address >> 16) & 0xFF, + (info.address >> 24) & 0xFF); + mStatusStatic->SetLabel(str); + } + else + { + mStatusStatic->SetLabel(wxT("Connected (obtaining IP ...)")); + } + } + else + { + mNetworkStatic->SetLabel(wxT("")); + mStatusStatic->SetLabel(wxT("Not connected")); + } + + mNetworkStatic->Refresh(); + mStatusStatic->Refresh(); + })); + } + }); +} + + +void GuiMultipleWifiSetup::UpdateNetworkList(void) +{ + mRemoveButton->Enable(false); + + mPowermon.requestGetWifiNetworks([this](Powermon::ResponseCode status, const std::vector &networks) + { + wxQueueEvent(this, new GuiEvent([this, status, networks](GuiEvent &event) + { + if (checkDeviceError(this, status)) + { + mAddButton->Enable(networks.size() < 5); + + uint32_t index = 0; + for(const Powermon::WifiNetwork &net: networks) + { + std::string ssid(reinterpret_cast(net.ssid), net.ssid_length); + size_t count = mNetworksListCtrl->GetItemCount(); + + if (index >= count) + mNetworksListCtrl->InsertItem(count, wxString(ssid.c_str(), wxConvUTF8)); + else + mNetworksListCtrl->SetItem(index, 0, wxString(ssid.c_str(), wxConvUTF8)); + + wxString str; + + if (net.isPasswordProtected()) + str += wxT("password protected"); + + if (!str.IsEmpty()) + str += wxT(", "); + + if (net.isMetered()) + str += wxT("metered"); + else + str += wxT("non-metered"); + + if (net.isFailed()) + { + if (!str.IsEmpty()) + str += wxT(", "); + + str += wxT("FAILED"); + } + + mNetworksListCtrl->SetItem(index, 1, str); + mNetworksListCtrl->RefreshItem(index); + + index++; + } + + size_t count = mNetworksListCtrl->GetItemCount(); + while (count > index) + mNetworksListCtrl->DeleteItem(--count); + + mNetworksListCtrl->SetItemState(-1, 0, wxLIST_STATE_SELECTED); + } + })); + }); +} + + +void GuiMultipleWifiSetup::OnListSelection(wxListEvent &event) +{ + const int32_t selected = mNetworksListCtrl->GetNextItem(-1, wxLIST_NEXT_BELOW, wxLIST_STATE_SELECTED); + mRemoveButton->Enable(selected >= 0); +} + + +void GuiMultipleWifiSetup::OnButtonAddClicked(wxCommandEvent &event) +{ + GuiWifiAdd dialog(this, mPowermon); + dialog.ShowModal(); + UpdateNetworkList(); +} + + +void GuiMultipleWifiSetup::OnButtonRemoveClicked(wxCommandEvent &event) +{ + const int32_t selected = mNetworksListCtrl->GetNextItem(-1, wxLIST_NEXT_BELOW, wxLIST_STATE_SELECTED); + if (selected >= 0) + { + mPowermon.requestRemoveWifiNetwork(selected, [this](Powermon::ResponseCode status) + { + debug_printf("\r\nREMOVE WIFI Status: %X", status); + + wxQueueEvent(this, new GuiEvent([this, status](GuiEvent &event) + { + if (checkDeviceError(this, status)) + UpdateNetworkList(); + })); + }); + } +} + + +void GuiMultipleWifiSetup::OnTimerEvent(wxTimerEvent &event) +{ + UpdateInfo(); +} + + +BEGIN_EVENT_TABLE(GuiMultipleWifiSetup, wxDialog) +EVT_CLOSE(GuiMultipleWifiSetup::OnClose) +EVT_LIST_ITEM_SELECTED(ID_LISTBOX_NETWORKS, GuiMultipleWifiSetup::OnListSelection) +EVT_BUTTON(ID_BUTTON_ADD, GuiMultipleWifiSetup::OnButtonAddClicked) +EVT_BUTTON(ID_BUTTON_REMOVE, GuiMultipleWifiSetup::OnButtonRemoveClicked) +EVT_TIMER(ID_TIMER, GuiMultipleWifiSetup::OnTimerEvent) +GUI_EVT(GuiMultipleWifiSetup::OnGuiEvent) +END_EVENT_TABLE() diff --git a/gui/gui_mult_wifi_setup.h b/gui/gui_mult_wifi_setup.h new file mode 100644 index 0000000..e26ea40 --- /dev/null +++ b/gui/gui_mult_wifi_setup.h @@ -0,0 +1,74 @@ +/* Copyright (C) 2020 - 2024, Thornwave Labs Inc + * Written by Razvan Turiac + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the “Software”), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * Attribution shall be given to Thornwave Labs Inc. and shall be made visible to the final user. + * + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef _GUI_MULT_WIFI_SETUP_H +#define _GUI_MULT_WIFI_SETUP_H + + +#include +#include + +#include + +#include + + +class GuiMultipleWifiSetup: public wxDialog +{ +public: + GuiMultipleWifiSetup(wxWindow* parent, Powermon &powermon); + ~GuiMultipleWifiSetup(); + +private: + enum + { + ID_LISTBOX_NETWORKS = wxID_HIGHEST + 1, + ID_BUTTON_ADD, + ID_BUTTON_REMOVE, + ID_TIMER + }; + + Powermon &mPowermon; + + wxTimer* mTimer; + + wxStaticText* mNetworkStatic; + wxStaticText* mStatusStatic; + + wxListCtrl* mNetworksListCtrl; + + wxButton* mAddButton; + wxButton* mRemoveButton; + + //std::vector mNetworks; + + void OnTimerEvent(wxTimerEvent &event); + + void OnListSelection(wxListEvent &event); + void OnButtonAddClicked(wxCommandEvent &event); + void OnButtonRemoveClicked(wxCommandEvent &event); + + void OnGuiEvent(GuiEvent &event); + void OnClose(wxCloseEvent &event); + + void UpdateInfo(void); + void UpdateNetworkList(void); + + DECLARE_EVENT_TABLE() +}; + +#endif diff --git a/gui/gui_stats.cpp b/gui/gui_stats.cpp index a786d2d..24b67ef 100644 --- a/gui/gui_stats.cpp +++ b/gui/gui_stats.cpp @@ -135,7 +135,7 @@ wxMessageDialog msg(this, wxT("Reset the power meter statistics ?"), wxT("Confirmation"), wxOK | wxCANCEL | wxICON_QUESTION | wxSTAY_ON_TOP | wxCENTRE); if (msg.ShowModal() == wxID_OK) { - mPowermon.requestResetStatistics([this](uint16_t status) + mPowermon.requestResetStatistics([this](Powermon::ResponseCode status) { wxQueueEvent(this, new GuiEvent([this, status](const GuiEvent &event) { @@ -154,7 +154,7 @@ void GuiStats::updateStats(void) { - mPowermon.requestGetStatistics([this](uint16_t status, const Powermon::MonitorStatistics &stats) + mPowermon.requestGetStatistics([this](Powermon::ResponseCode status, const Powermon::MonitorStatistics &stats) { wxQueueEvent(this, new GuiEvent([this, status, stats](const GuiEvent &event) { diff --git a/gui/gui_string_dialog.h b/gui/gui_string_dialog.h index 7d62687..e65e778 100644 --- a/gui/gui_string_dialog.h +++ b/gui/gui_string_dialog.h @@ -26,7 +26,7 @@ { public: - GuiEnterString(wxWindow *parent, wxWindowID id, const wxString &title, bool password, bool confirm); + GuiEnterString(wxWindow* parent, wxWindowID id, const wxString &title, bool password, bool confirm); ~GuiEnterString(); void setStringLabel(const wxString &string) @@ -46,6 +46,12 @@ mMaxStringLength = max_length; } + + void setString(const wxString &str) + { + mStringText->SetValue(str); + } + wxString getString(void) const { return mStringText->GetValue(); diff --git a/gui/gui_wifi_add.cpp b/gui/gui_wifi_add.cpp new file mode 100644 index 0000000..ebc6a56 --- /dev/null +++ b/gui/gui_wifi_add.cpp @@ -0,0 +1,172 @@ +/* Copyright (C) 2020 - 2024, Thornwave Labs Inc + * Written by Razvan Turiac + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the “Software”), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * Attribution shall be given to Thornwave Labs Inc. and shall be made visible to the final user. + * + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include +#include + + +#include + + +GuiWifiAdd::GuiWifiAdd(wxWindow* parent, Powermon &powermon): wxDialog(parent, wxID_ANY, wxT("Add WiFi Network")), mPowermon(powermon) +{ + const size_t BORDER_SIZE = FromDIP(6); + + wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL); + SetSizer(main_sizer); + + main_sizer->Add(new wxStaticText(this, wxID_ANY, wxT("Wireless Network List")), 0, wxBOTTOM | wxEXPAND, BORDER_SIZE); + + mNetworkListCtrl = new wxListCtrl(this, ID_LISTBOX_NETWORK, wxDefaultPosition, FromDIP(wxSize(300, 420)), wxLC_SINGLE_SEL | wxLC_LIST | wxLC_VRULES); + main_sizer->Add(mNetworkListCtrl, 1, wxEXPAND | wxALL, 0); + + main_sizer->SetSizeHints(this); + Centre(); + + mPowermon.setOnWifiScanReportCallback([this](const Powermon::WifiScanResult* result) + { + if (result) + { + Powermon::WifiScanResult report = *result; + + wxQueueEvent(this, new GuiEvent([this, report](const GuiEvent &event) + { + if (std::find(mNetworkList.begin(), mNetworkList.end(), report) == mNetworkList.end()) + { + mNetworkList.push_back(report); + + char name[33]; + memset(name, 0, sizeof(name)); + + if (report.ssid_length) + { + memcpy(name, report.ssid, report.ssid_length); + + char* ptr = name; + for(uint32_t i = 0; i < report.ssid_length; i++) + { + if (isprint(*ptr) == false) + *ptr = '-'; + + ptr++; + } + } + else + { + strcpy(name, ""); + } + + mNetworkListCtrl->InsertItem(mNetworkListCtrl->GetItemCount(), wxString(name, wxConvUTF8)); + mNetworkListCtrl->Refresh(); + } + })); + } + }); + + mTimer = new wxTimer(this, ID_TIMER); + mTimer->Start(10000); + + mPowermon.requestStartWifiScan([this](Powermon::ResponseCode status) + { + }); +} + + +GuiWifiAdd::~GuiWifiAdd() +{ +} + + +void GuiWifiAdd::OnClose(wxCloseEvent &event) +{ + mTimer->Stop(); + mPowermon.setOnWifiScanReportCallback([this](const Powermon::WifiScanResult* result){}); + + EndModal(wxID_CANCEL); +} + + +void GuiWifiAdd::OnGuiEvent(GuiEvent &event) +{ + event.process(); +} + + +void GuiWifiAdd::OnTimerEvent(wxTimerEvent &event) +{ + mPowermon.requestStartWifiScan([this](Powermon::ResponseCode status) + { + }); +} + + +void GuiWifiAdd::OnListNetworkActivated(wxListEvent &event) +{ + const int32_t selected = mNetworkListCtrl->GetNextItem(-1, wxLIST_NEXT_BELOW, wxLIST_STATE_SELECTED); + + if (selected >= 0) + { + Powermon::WifiScanResult &scan = mNetworkList[selected]; + + Powermon::WifiNetwork network; + memset(&network, 0, sizeof(Powermon::WifiNetwork)); + + network.ssid_length = scan.ssid_length; + memcpy(network.ssid, scan.ssid, sizeof(network.ssid)); + + debug_printf("\r\nSelected: %.*s", scan.ssid_length, scan.ssid); + + if (scan.security.wpa2 || scan.security.wpa || scan.security.wep) + { + GuiEnterString dialog(this, wxID_ANY, wxT("Enter password for network"), true, false); + dialog.setMaxStringLength(64); + dialog.setStringLabel(wxT("Network Password")); + + if (dialog.ShowModal() != wxID_OK) + return; + + network.pass_length = dialog.getString().size(); + memcpy(network.pass, dialog.getString().mb_str(), network.pass_length); + } + + wxMessageDialog msg(this, wxT("Is this network metered ? (mobile hotspot or limited data)"), + wxT("Network Options"), wxYES | wxNO | wxICON_QUESTION | wxSTAY_ON_TOP | wxCENTRE); + network.setMetered(msg.ShowModal() == wxID_OK); + + mPowermon.requestAddWifiNetwork(network, [this](Powermon::ResponseCode status) + { + debug_printf("\r\nADD WIFI Status: %X", status); + + wxQueueEvent(this, new GuiEvent([this, status](GuiEvent &event) + { + checkDeviceError(this, status); + + mTimer->Stop(); + mPowermon.setOnWifiScanReportCallback([this](const Powermon::WifiScanResult* result){}); + + EndModal(status == Powermon::RSP_SUCCESS ? wxID_OK : wxID_CANCEL); + })); + }); + } +} + +BEGIN_EVENT_TABLE(GuiWifiAdd, wxDialog) +EVT_CLOSE(GuiWifiAdd::OnClose) +EVT_LIST_ITEM_ACTIVATED(ID_LISTBOX_NETWORK, GuiWifiAdd::OnListNetworkActivated) +GUI_EVT(GuiWifiAdd::OnGuiEvent) +EVT_TIMER(ID_TIMER, GuiWifiAdd::OnTimerEvent) +END_EVENT_TABLE() diff --git a/gui/gui_wifi_add.h b/gui/gui_wifi_add.h new file mode 100644 index 0000000..c11244e --- /dev/null +++ b/gui/gui_wifi_add.h @@ -0,0 +1,58 @@ +/* Copyright (C) 2020 - 2024, Thornwave Labs Inc + * Written by Razvan Turiac + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the “Software”), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * Attribution shall be given to Thornwave Labs Inc. and shall be made visible to the final user. + * + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef _GUI_WIFI_ADD_H +#define _GUI_WIFI_ADD_H + + +#include +#include + +#include + +#include + + +class GuiWifiAdd: public wxDialog +{ +public: + GuiWifiAdd(wxWindow* parent, Powermon &powermon); + ~GuiWifiAdd(); + +private: + enum + { + ID_LISTBOX_NETWORK = wxID_HIGHEST + 1, + ID_TIMER + }; + + Powermon &mPowermon; + + wxTimer* mTimer; + wxListCtrl* mNetworkListCtrl; + std::vector mNetworkList; + + void OnTimerEvent(wxTimerEvent &event); + void OnListNetworkActivated(wxListEvent &event); + + void OnGuiEvent(GuiEvent &event); + void OnClose(wxCloseEvent &event); + + DECLARE_EVENT_TABLE() +}; + +#endif diff --git a/gui/gui_wifi_setup.cpp b/gui/gui_wifi_setup.cpp index d4814f7..5bdeba9 100644 --- a/gui/gui_wifi_setup.cpp +++ b/gui/gui_wifi_setup.cpp @@ -95,7 +95,7 @@ UpdateInfo(); - mPowermon.requestStartWifiScan([this](uint16_t status) + mPowermon.requestStartWifiScan([this](Powermon::ResponseCode status) { }); } @@ -128,7 +128,7 @@ void GuiWifiSetup::UpdateInfo(void) { - mPowermon.requestGetInfo([this](uint16_t status, const Powermon::DeviceInfo &info) + mPowermon.requestGetInfo([this](Powermon::ResponseCode status, const Powermon::DeviceInfo &info) { if (status == Powermon::RSP_SUCCESS) { @@ -194,17 +194,17 @@ network.ssid_length = scan.ssid_length; memcpy(network.ssid, scan.ssid, sizeof(network.ssid)); - network.security = Powermon::WifiNetwork::SecurityMode::OPEN; + network.flags = Powermon::WifiNetwork::SecurityMode::OPEN; if (scan.security.wpa2) - network.security = Powermon::WifiNetwork::SecurityMode::WPA2_PSK; + network.flags = Powermon::WifiNetwork::SecurityMode::WPA2_PSK; else if (scan.security.wpa) - network.security = Powermon::WifiNetwork::SecurityMode::WPA2_WPA1_PSK; + network.flags = Powermon::WifiNetwork::SecurityMode::WPA2_WPA1_PSK; else if (scan.security.wep) - network.security = Powermon::WifiNetwork::SecurityMode::WEP; + network.flags = Powermon::WifiNetwork::SecurityMode::WEP; debug_printf("\r\nSelected: %.*s", scan.ssid_length, scan.ssid); - if (network.security) + if (network.flags) { GuiEnterString dialog(this, wxID_ANY, wxT("Enter password for network"), true, false); dialog.setMaxStringLength(64); @@ -219,7 +219,7 @@ debug_printf("\r\nPass: %.*s", network.pass_length, network.pass); } - mPowermon.requestWifiConfigure(network, [this](uint16_t status) + mPowermon.requestWifiConfigure(network, [this](Powermon::ResponseCode status) { debug_printf("\r\nSET WIFI Status: %X %X", status); diff --git a/lib/powermon/inc/powermon.h b/lib/powermon/inc/powermon.h index 53ecf71..67f2b3c 100644 --- a/lib/powermon/inc/powermon.h +++ b/lib/powermon/inc/powermon.h @@ -39,7 +39,7 @@ #define ENCRYPTION_KEY_SIZE 32 #define MAX_BLE_NAME_LENGTH 8 -#define MAX_WIFI_NAME_LENGTH 32 +#define MAX_NAME_LENGTH 32 #define MAX_TIMER_NAME_LENGTH 16 #define MAX_TIMER_COUNT 16 @@ -160,7 +160,7 @@ */ struct WifiNetwork { - enum SecurityMode: uint8_t + enum SecurityMode: uint16_t { OPEN = 0x0, ///< No security WEP = 0x1, ///< Use WEP @@ -175,7 +175,13 @@ uint8_t pass_length; uint8_t pass[MAX_WIFI_PASSWORD_SIZE]; - SecurityMode security; + uint16_t flags; + + bool isPasswordProtected(void) const; + bool isMetered(void) const; + bool isFailed(void) const; + + void setMetered(bool enabled); }; @@ -222,6 +228,7 @@ uint8_t ssid_length; uint8_t ssid[MAX_WIFI_SSID_SIZE]; uint8_t flags; + int8_t timezone; bool isUserPasswordSet(void) const; bool isMasterPasswordSet(void) const; @@ -612,6 +619,28 @@ * \param cb Lambda of type void(ResponseCode) that will be called to signal the result of the request */ virtual void requestWifiConfigure(const WifiNetwork &network, const std::function &cb) = 0; + + + /** + * \brief Returns a list of all WiFi networks known by the device. + * \param cb Lambda of type void(ResponseCode, const std::vector&) that will be called to signal the result of the request + */ + virtual void requestGetWifiNetworks(const std::function&)> &cb) = 0; + + /** + * \brief Add a new WiFi network + * The new credentials will be saved by the device whether PowerMon can or cannot connect to that specified network. + * \param network WiFi network credentials + * \param cb Lambda of type void(ResponseCode) that will be called to signal the result of the request + */ + virtual void requestAddWifiNetwork(const WifiNetwork& network, const std::function &cb) = 0; + + /** + * \brief Removes a stored WiFi network + * \param index WiFi network index in the list returned by requestGetWifiNetworks() + * \param cb Lambda of type void(ResponseCode) that will be called to signal the result of the request + */ + virtual void requestRemoveWifiNetwork(uint8_t index, const std::function &cb) = 0; /** @@ -730,6 +759,9 @@ virtual void requestEraseDebug(const std::function &cb) = 0; + virtual void requestReboot(const std::function &cb) = 0; + + /** * \brief Returns the IP address as string @@ -778,6 +810,12 @@ */ static bool hasConfigurableShunt(uint8_t bcd); + + /** + * \brief Returns true if the PowerMon described by the BCD hardware revision has an initegrated shunt + */ + static bool hasIntegratedShunt(uint8_t bcd); + /** * \brief Returns true if the PowerMon described by the BCD hardware revision has WiFi @@ -786,6 +824,18 @@ /** + * \brief Returns true if the PowerMon described by the BCD hardware revision has Ethernet + */ + static bool hasEthernet(uint8_t bcd); + + + /** + * \brief Returns true if the PowerMon described by the BCD hardware revision has Ethernet + */ + static bool hasNetwork(uint8_t bcd); + + + /** * \brief Returns true if the PowerMon described by the BCD hardware revision has Bluetooth */ static bool hasBluetooth(uint8_t bcd); diff --git a/lib/powermon/inc/powermon_config.h b/lib/powermon/inc/powermon_config.h index 8002e6c..bacf35c 100644 --- a/lib/powermon/inc/powermon_config.h +++ b/lib/powermon/inc/powermon_config.h @@ -62,12 +62,50 @@ }; + enum TimeZone: uint32_t + { + TZ_AMERICA_ATLANTIC = 0, + TZ_AMERICA_EASTERN = 1, + TZ_AMERICA_CENTRAL = 2, + TZ_AMERICA_MOUNTAIN = 3, + TZ_AMERICA_PACIFIC_NO_DST = 4, + TZ_AMERICA_PACIFIC = 5, + TZ_AMERICA_ALASKA = 6, + TZ_AMERICA_HAWAII = 7, + TZ_AMERICA_SAMOA = 8, + TZ_AMERICA_CHAMORO = 9, + TZ_EUROPE_WESTERN = 10, + TZ_EUROPE_CENTRAL = 11, + TZ_EUROPE_EASTERN = 12, + TZ_EUROPE_MOSCOW = 13 + }; + + static const char* timeZones[14]; + void setMfMode(MultiFunctionPinMode mode); MultiFunctionPinMode getMfMode(void) const; void setWiFiKeepAPOn(bool state); bool getWiFiKeepAPOn(void) const; + void setWiFiDisablePowerSaving(bool state); + bool getWiFiDisablePowerSaving(void) const; + + void setWiFiWatchdogEnable(bool state); + bool getWiFiWatchdogEnable(void) const; + + void setNoInternetEnable(bool state); + bool getNoInternetEnable(void) const; + + void setNtpEnable(bool state); + bool getNtpEnable(void) const; + + void setTimeZone(TimeZone tz); + TimeZone getTimeZone(void) const; + + void setEthKeepLinkLedOn(bool state); + bool getEthKeepLinkLedOn(void) const; + void setEngineShuntVdropNom(uint8_t mv); uint8_t getEngineShuntVdropNom(void) const; diff --git a/lib/powermon/inc/powermon_scanner.h b/lib/powermon/inc/powermon_scanner.h index 25025e6..3210489 100644 --- a/lib/powermon/inc/powermon_scanner.h +++ b/lib/powermon/inc/powermon_scanner.h @@ -27,15 +27,15 @@ /** - * \brief PowermonScanner offers support for scanning for PowerMon advertisments. Both BLE and WiFi PowerMon devices are supported. + * \brief PowermonScanner offers support for scanning for PowerMon advertisements. Both BLE and WiFi PowerMon devices are supported. */ class PowermonScanner { public: /** - * \brief Advertisment is a structure representing an advertisment packet received from the PowerMon device. + * \brief Advertisement is a structure representing an advertisement packet received from the PowerMon device. */ - struct Advertisment + struct Advertisement { uint64_t serial; uint64_t address; @@ -77,28 +77,28 @@ virtual ~PowermonScanner(); /** - * \brief Sets the callback to be called by the Powermon scanner when a new advertisment has been received - * \param cb Lambda of type `void(const Advertisment&)` + * \brief Sets the callback to be called by the Powermon scanner when a new advertisement has been received + * \param cb Lambda of type `void(const Advertisement&)` */ - virtual void setCallback(const std::function &cb) = 0; + virtual void setCallback(const std::function &cb) = 0; /** - * \brief Starts scanning for WiFi device advertisments + * \brief Starts scanning for WiFi device advertisements */ virtual void startWifiScan(void) = 0; /** - * \brief Stops scanning for WiFi device advertisments + * \brief Stops scanning for WiFi device advertisements */ virtual void stopWifiScan(void) = 0; /** - * Starts scanning for BLE device advertisments + * Starts scanning for BLE device advertisements */ virtual void startBleScan(void) = 0; /** - * Stops scanning for BLE device advertisments + * Stops scanning for BLE device advertisements */ virtual void stopBleScan(void) = 0; }; diff --git a/lib/powermon/powermon_lib.a b/lib/powermon/powermon_lib.a index 8c9ea4d..4e969a4 100644 --- a/lib/powermon/powermon_lib.a +++ b/lib/powermon/powermon_lib.a Binary files differ