Restore Companion boot display and default WiFi to opt-in
This commit is contained in:
1 parent
5ef97727c7
commit
8505361ebe
11 files changed
+276
-11
No files matched your search
@@ -206,6 +206,23 @@ image over USB once when moving a device from the legacy partition layout.
|
||||
|
||||
## WiFi companion setup
|
||||
|
||||
Full and Bluetooth/wired-capable Companions default to infrastructure WiFi off.
|
||||
Bluetooth starts where the build supports it, and saved WiFi enablement is
|
||||
preserved across upgrades. Before using the setup AP or station, enable WiFi
|
||||
through the USB or Bluetooth CLI:
|
||||
|
||||
```text
|
||||
get wifi.enabled
|
||||
set wifi.enabled 1
|
||||
```
|
||||
|
||||
ESP32 simultaneous-transport builds apply the change without a reboot. SenseCAP
|
||||
Indicator exclusive-transport builds and Pico W require a reboot. WiFi-only
|
||||
compatibility builds without any other CLI transport retain WiFi on so they are
|
||||
not left unreachable. Saving an SSID/password alone does not enable WiFi on an
|
||||
opt-in build. Use `set wifi.enabled 0` to save it off again. This controls
|
||||
infrastructure WiFi, not a primary ESP-NOW mesh radio.
|
||||
|
||||
A `*_companion_radio_wifi` build replaces the BLE or USB companion link with
|
||||
the MeshCore companion protocol over TCP port 5000. The phone or computer must
|
||||
be able to reach the device on the same LAN.
|
||||
|
||||
@@ -363,6 +363,15 @@ new address; USB password input is masked. Binary Companion clients can use
|
||||
USB, BLE, or TCP port 5000 without the terminal-start token: send command
|
||||
`0x42` (`CMD_RUN_CLI_COMMAND`) followed by the same CLI text, such as
|
||||
`get wifi.powersave`, `get wifi.pwd`, `stats-core`, or `set wifi.powersave min`.
|
||||
Full and Bluetooth/wired-capable Companions default to infrastructure WiFi off
|
||||
and Bluetooth on where supported. Legacy WiFi-only builds without another CLI
|
||||
transport retain WiFi on so they are not left unreachable.
|
||||
Use `get wifi.enabled` and `set wifi.enabled 1` to inspect or persistently enable
|
||||
WiFi; `set wifi.enabled 0` disables it. Saved enablement survives upgrades, and
|
||||
credentials alone do not turn it on. ESP32 simultaneous-transport builds apply
|
||||
the change from the main loop; exclusive-transport builds and Pico W apply it
|
||||
after reboot. This does not disable a primary ESP-NOW mesh radio or add Bluetooth
|
||||
to a WiFi-only build.
|
||||
See [local maintenance commands](terminal_chat_cli.md#local-maintenance-commands)
|
||||
for the complete access rules. WiFi-only Companions accept
|
||||
all three modes. A Full Companion that runs BLE and infrastructure WiFi
|
||||
@@ -900,6 +909,13 @@ saved settings. The WebConfig **Display** card exposes the same controls:
|
||||
Modes are `off`, `on`, `button`, `pairing`, `button-pairing`, and `automatic`.
|
||||
Pairing modes are available only on builds supporting BLE pairing.
|
||||
|
||||
All display-equipped Companion builds show the screen at boot in the default
|
||||
`button` or `button-pairing` mode, on battery or USB power. It stays on for the
|
||||
selected profile's timeout (15 seconds by default); button input restarts that
|
||||
timer. This also applies to previously saved button modes. Saved `off` and
|
||||
pairing-only modes still suppress the boot screen. Repeaters, room servers,
|
||||
and sensors do not gain this Companion-only boot wake.
|
||||
|
||||
- `off`: stays dark, including boot and pairing.
|
||||
- `on`: stays on while the device is awake.
|
||||
- `button`: a physical button, keyboard/navigation input, or supported touch
|
||||
|
||||
@@ -18,6 +18,13 @@ updating other nodes. Most targets can run those transports simultaneously;
|
||||
the SenseCAP Indicator exception selects one secondary wireless transport per
|
||||
boot as described below.
|
||||
|
||||
Fresh Full Companion installs default to Bluetooth on and infrastructure WiFi off.
|
||||
WiFi remains compiled in: use `set wifi.enabled 1` through the USB or Bluetooth
|
||||
CLI to enable it, or `set wifi.enabled 0` to disable it persistently. Existing
|
||||
saved WiFi enablement is honored after an upgrade. The SenseCAP Indicator
|
||||
requires a reboot to apply its exclusive transport selection; primary ESP-NOW
|
||||
mesh operation is unaffected by the infrastructure-WiFi default.
|
||||
|
||||
The full Companion is deliberately not a LoRa OTA destination: it has no
|
||||
firmware staging store, refuses `ota install`, and never advertises its own
|
||||
firmware as an mOTA image.
|
||||
|
||||
@@ -2300,7 +2300,14 @@ MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMe
|
||||
_prefs.rx_ps_sleep_us = RX_POWERSAVING_DEFAULT_SLEEP_US;
|
||||
_prefs.powersaving_enabled = 1;
|
||||
_prefs.powersaving_policy_version = 0;
|
||||
// Keep WiFi-only compatibility builds reachable when they have no other
|
||||
// CLI transport. Full and Bluetooth/wired-capable Companions are opt-in.
|
||||
#if defined(WIFI_SSID) && !defined(BLE_PIN_CODE) && !defined(ENABLE_USB_INTERFACE) \
|
||||
&& !defined(SERIAL_RX) && !defined(ETHERNET_ENABLED)
|
||||
_prefs.wifi_enabled = 1;
|
||||
#else
|
||||
_prefs.wifi_enabled = 0;
|
||||
#endif
|
||||
memset(_prefs.bluetooth_name, 0, sizeof(_prefs.bluetooth_name));
|
||||
_prefs.display_rotation_degrees = 0;
|
||||
_prefs.cad_enabled = DEFAULT_CAD_ENABLED ? 1 : 0;
|
||||
@@ -2398,10 +2405,11 @@ void MyMesh::begin(bool has_display, bool radio_available) {
|
||||
|
||||
mesh::ui::loadDisplayPowerSettings(_store->getPrimaryFS(),
|
||||
#ifdef BLE_PIN_CODE
|
||||
true
|
||||
true,
|
||||
#else
|
||||
false
|
||||
false,
|
||||
#endif
|
||||
true // All Companion transports show startup unless the saved mode forbids it.
|
||||
);
|
||||
|
||||
#if COMPANION_FEATURE_NOTIFICATIONS
|
||||
|
||||
@@ -1273,7 +1273,7 @@ void halt() {
|
||||
static char configured_wifi_ssid[32];
|
||||
static char configured_wifi_password[65];
|
||||
static bool companion_wifi_has_credentials = false;
|
||||
static bool companion_wifi_requested = true;
|
||||
static bool companion_wifi_requested = false;
|
||||
static bool companion_wifi_active = false;
|
||||
static bool companion_wifi_disable_in_progress = false;
|
||||
static bool companion_wifi_services_stopped = false;
|
||||
@@ -1291,7 +1291,7 @@ void halt() {
|
||||
|
||||
#if defined(COMPANION_EXCLUSIVE_WIFI_BLE)
|
||||
static CompanionTransportMode companion_transport_boot_mode =
|
||||
CompanionTransportMode::WiFi;
|
||||
CompanionTransportMode::Bluetooth;
|
||||
static bool companion_transport_boot_mode_loaded = false;
|
||||
|
||||
CompanionTransportMode getCompanionTransportMode() {
|
||||
@@ -3074,7 +3074,7 @@ void setup() {
|
||||
} else {
|
||||
stopCompanionInfrastructureWiFi();
|
||||
board.setInhibitSleep(false);
|
||||
WIFI_DEBUG_PRINTLN("WiFi remains off from the saved BOOT/GPIO 0 setting");
|
||||
WIFI_DEBUG_PRINTLN("WiFi remains off; use set wifi.enabled 1 to enable it");
|
||||
}
|
||||
#if defined(COMPANION_EXCLUSIVE_WIFI_BLE)
|
||||
}
|
||||
|
||||
@@ -25,6 +25,8 @@ struct DisplayPowerPrefs {
|
||||
DisplayPowerProfile battery;
|
||||
DisplayPowerProfile usb;
|
||||
DisplayInboxMode inbox = DisplayInboxMode::History;
|
||||
// Runtime role policy, not serialized with the user's display settings.
|
||||
bool wake_on_boot = false;
|
||||
};
|
||||
|
||||
inline DisplayPowerPrefs& displayPowerPrefs() {
|
||||
@@ -70,7 +72,8 @@ public:
|
||||
void update(const DisplayPowerPrefs& prefs, bool usb, bool connected,
|
||||
bool pairing, uint32_t now) {
|
||||
const auto& selected = usb ? prefs.usb : prefs.battery;
|
||||
const bool reset = !_initialized || _usb != usb || _profile.mode != selected.mode;
|
||||
const bool startup = !_initialized;
|
||||
const bool reset = startup || _usb != usb || _profile.mode != selected.mode;
|
||||
const bool timeout_changed = _profile.seconds != selected.seconds;
|
||||
const bool just_connected = connected && !_connected;
|
||||
_profile = selected;
|
||||
@@ -80,7 +83,14 @@ public:
|
||||
if (reset) {
|
||||
_timed = false;
|
||||
_initialized = true;
|
||||
wake(DisplayWake::Boot, now);
|
||||
// Companions show startup for the normal button timeout. Only the first
|
||||
// initialization counts; changing profiles must not synthesize input.
|
||||
if (startup && prefs.wake_on_boot && (_profile.mode == DisplayMode::Button
|
||||
|| _profile.mode == DisplayMode::ButtonPairing)) {
|
||||
wake(DisplayWake::Button, now);
|
||||
} else {
|
||||
wake(DisplayWake::Boot, now);
|
||||
}
|
||||
} else if (timeout_changed && _timed) {
|
||||
_deadline = now + uint32_t(_profile.seconds) * 1000;
|
||||
}
|
||||
|
||||
@@ -80,10 +80,11 @@ bool savePrefs(const DisplayPowerPrefs& prefs) {
|
||||
}
|
||||
}
|
||||
|
||||
bool loadDisplayPowerSettings(FILESYSTEM* fs, bool pairing_supported) {
|
||||
bool loadDisplayPowerSettings(FILESYSTEM* fs, bool pairing_supported, bool wake_on_boot) {
|
||||
settings_fs = fs;
|
||||
pairing_available = pairing_supported;
|
||||
DisplayPowerPrefs prefs;
|
||||
prefs.wake_on_boot = wake_on_boot;
|
||||
if (pairing_supported) prefs.battery.mode = prefs.usb.mode = DisplayMode::ButtonPairing;
|
||||
displayPowerPrefs() = prefs;
|
||||
if (!fs) return false;
|
||||
|
||||
@@ -7,7 +7,9 @@ namespace mesh { namespace ui {
|
||||
|
||||
// Separate, versioned file: existing node and MQTT preference layouts stay
|
||||
// readable by older firmware. Returns true when a saved profile was loaded.
|
||||
bool loadDisplayPowerSettings(FILESYSTEM* fs, bool pairing_supported);
|
||||
// Companion startup enables a timed boot screen without changing saved modes.
|
||||
bool loadDisplayPowerSettings(FILESYSTEM* fs, bool pairing_supported,
|
||||
bool wake_on_boot = false);
|
||||
bool handleDisplayPowerCommand(const char* command, char* reply, size_t reply_size,
|
||||
bool inbox_supported = false);
|
||||
bool displayPairingSupported();
|
||||
|
||||
@@ -128,6 +128,7 @@ int main(int argc,char** argv){
|
||||
}
|
||||
DataStore fresh;CompanionNodePrefs defaults;double lat=0,lon=0;
|
||||
assert(fresh.loadPrefs(defaults,lat,lon));
|
||||
assert(defaults.wifi_enabled==0); // Fresh installs never enable WiFi implicitly.
|
||||
assert(fresh.savePrefs(defaults,lat,lon));
|
||||
} else if(scenario==2){
|
||||
#if defined(ESP32_PLATFORM) || defined(RP2040_PLATFORM)
|
||||
@@ -198,6 +199,23 @@ int main(int argc,char** argv){
|
||||
assert(legacy.loadPrefs(loaded,lat,lon));
|
||||
assert(loaded.flood_retry_advert_enabled==0);
|
||||
assert(loaded.one_key_dm_enabled==0);
|
||||
} else if(scenario==8){
|
||||
for(uint8_t enabled : {0,1}){
|
||||
CompanionNodePrefs saved=original;saved.wifi_enabled=enabled;
|
||||
assert(store.savePrefs(saved,47.1,-122.2));
|
||||
CompanionNodePrefs loaded;double lat=0,lon=0;
|
||||
loaded.wifi_enabled=1-enabled;
|
||||
assert(store.loadPrefs(loaded,lat,lon));
|
||||
assert(loaded.wifi_enabled==enabled); // Saved choices override the default.
|
||||
assert(store.fs.files["/new_prefs"][156]==enabled); // Established byte offset.
|
||||
}
|
||||
for(unsigned size : {84,144,155,156}){
|
||||
DataStore legacy;legacy.fs.files["/new_prefs"]=store.fs.files["/new_prefs"];
|
||||
legacy.fs.files["/new_prefs"].resize(size);
|
||||
CompanionNodePrefs loaded;double lat=0,lon=0;
|
||||
assert(legacy.loadPrefs(loaded,lat,lon));
|
||||
assert(loaded.wifi_enabled==0); // Old files with no WiFi field use opt-in.
|
||||
}
|
||||
} else if(scenario==4){
|
||||
DataStore legacy;legacy.fs.files["/node_prefs"]=disk;
|
||||
legacy.fs.fail_write_after=17;
|
||||
@@ -285,7 +303,7 @@ inline char* utoa(unsigned int value,char* output,int base){
|
||||
str(ROOT / 'src/helpers/TxtDataHelpers.cpp'),
|
||||
'-o', str(binary)], capture_output=True, text=True)
|
||||
self.assertEqual(build.returncode, 0, build.stdout + build.stderr)
|
||||
for scenario in range(8):
|
||||
for scenario in range(9):
|
||||
with self.subTest(scenario=scenario):
|
||||
run = subprocess.run([str(binary), str(scenario)], capture_output=True, text=True)
|
||||
self.assertEqual(run.returncode, 0, run.stdout + run.stderr)
|
||||
|
||||
@@ -170,6 +170,43 @@ int main() {
|
||||
|
||||
|
||||
class CompanionUsbDefaultTest(unittest.TestCase):
|
||||
def test_wifi_defaults_preserve_a_reachable_cli(self):
|
||||
mesh = (ROOT / "examples/companion_radio/MyMesh.cpp").read_text()
|
||||
start = mesh.index(" // Keep WiFi-only compatibility builds reachable")
|
||||
end = mesh.index(" memset(_prefs.bluetooth_name", start)
|
||||
defaults = mesh[start:end]
|
||||
profiles = {
|
||||
"esp32-full": (["ESP32=1", "WIFI_SSID=1", "BLE_PIN_CODE=123456",
|
||||
"ENABLE_USB_INTERFACE=1", "COMPANION_RADIO_FULL=1"], 0),
|
||||
"esp32-ble-wifi": (["ESP32=1", "WIFI_SSID=1", "BLE_PIN_CODE=123456"], 0),
|
||||
"esp32-usb-wifi": (["ESP32=1", "WIFI_SSID=1", "ENABLE_USB_INTERFACE=1"], 0),
|
||||
"esp32-wifi-only": (["ESP32=1", "WIFI_SSID=1"], 1),
|
||||
"pico-usb-ble-wifi": (["RP2040_PLATFORM=1", "WIFI_SSID=1",
|
||||
"ENABLE_USB_INTERFACE=1", "BLE_PIN_CODE=123456"], 0),
|
||||
"pico-wifi-only": (["RP2040_PLATFORM=1", "WIFI_SSID=1"], 1),
|
||||
"ethernet-wifi": (["ESP32=1", "WIFI_SSID=1", "ETHERNET_ENABLED=1"], 0),
|
||||
"hardware-serial-wifi": (["ESP32=1", "WIFI_SSID=1", "SERIAL_RX=1"], 0),
|
||||
"indicator-exclusive": (["ESP32=1", "WIFI_SSID=1", "BLE_PIN_CODE=123456",
|
||||
"ENABLE_USB_INTERFACE=1", "COMPANION_EXCLUSIVE_WIFI_BLE=1"], 0),
|
||||
"nrf52-ble": (["NRF52_PLATFORM=1", "BLE_PIN_CODE=123456"], 0),
|
||||
"usb-only": (["ENABLE_USB_INTERFACE=1"], 0),
|
||||
}
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
binary = Path(directory) / "wireless-defaults"
|
||||
for name, (flags, expected) in profiles.items():
|
||||
with self.subTest(profile=name):
|
||||
source = ("#include <cassert>\nstruct Prefs { unsigned wifi_enabled = 99; } _prefs;\n"
|
||||
"int main() {\n" + defaults
|
||||
+ f"assert(_prefs.wifi_enabled == {expected});\n}}\n")
|
||||
build = subprocess.run(["g++", "-std=c++17", "-x", "c++",
|
||||
*["-D" + flag for flag in flags],
|
||||
"-", "-o", str(binary)], input=source,
|
||||
text=True, capture_output=True)
|
||||
self.assertEqual(build.returncode, 0, build.stdout + build.stderr)
|
||||
subprocess.run([str(binary)], check=True)
|
||||
self.assertIn("static bool companion_wifi_requested = false;", MAIN)
|
||||
self.assertIn("companion_transport_boot_mode =\n CompanionTransportMode::Bluetooth;", MAIN)
|
||||
|
||||
def test_startup_and_reconnection_for_each_usb_transport(self):
|
||||
functions = "\n".join(function("static void " + name + "(") for name in (
|
||||
"resetUsbTerminalHostSession", "serviceUsbTerminalHostSessionReset",
|
||||
|
||||
+150
-1
@@ -1,5 +1,6 @@
|
||||
"""Exercise the shared display policy and real settings/CLI code on the host."""
|
||||
from pathlib import Path
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
@@ -109,6 +110,135 @@ void policyTests() {
|
||||
p.update(prefs, false, false, true, 1001);
|
||||
assert(!p.on()); // Off wins over a pending button wake and pairing.
|
||||
}
|
||||
void companionBootTests() {
|
||||
// The startup exception is role-wide, independent of transport, power
|
||||
// source, or whether an app has already connected by the first UI frame.
|
||||
for (bool usb : {false, true}) for (bool connected : {false, true}) {
|
||||
for (unsigned mode = 0; mode <= 5; ++mode) {
|
||||
DisplayPowerPrefs prefs;
|
||||
prefs.wake_on_boot = true;
|
||||
prefs.battery.mode = prefs.usb.mode = DisplayMode::Off;
|
||||
auto& profile = usb ? prefs.usb : prefs.battery;
|
||||
profile.mode = DisplayMode(mode);
|
||||
DisplayPowerPolicy p;
|
||||
p.update(prefs, usb, connected, false, 100);
|
||||
const bool button_mode = mode == 2 || mode == 4;
|
||||
assert(p.on() == (mode == 1 || button_mode || (mode == 5 && !connected)));
|
||||
p.update(prefs, usb, connected, false, 4100);
|
||||
assert(p.on() == (mode == 1 || button_mode)); // Normal 15s, not 4s.
|
||||
p.update(prefs, usb, connected, false, 15099);
|
||||
assert(p.on() == (mode == 1 || button_mode));
|
||||
p.update(prefs, usb, connected, false, 15100);
|
||||
assert(p.on() == (mode == 1));
|
||||
p.update(prefs, usb, connected, false, 30000);
|
||||
assert(p.on() == (mode == 1)); // Refresh cannot retrigger startup.
|
||||
}
|
||||
}
|
||||
for (DisplayMode mode : {DisplayMode::Button, DisplayMode::ButtonPairing}) {
|
||||
DisplayPowerPrefs prefs;
|
||||
prefs.wake_on_boot = true;
|
||||
prefs.battery = prefs.usb = {mode, 27};
|
||||
DisplayPowerPolicy p;
|
||||
p.update(prefs, false, false, false, 100);
|
||||
p.update(prefs, false, true, false, 101);
|
||||
assert(p.on()); // Connecting does not dismiss a button-mode boot screen.
|
||||
assert(!p.wake(DisplayWake::Message, 200));
|
||||
p.update(prefs, false, true, false, 27099);
|
||||
assert(p.on());
|
||||
assert(p.wake(DisplayWake::Button, 27099));
|
||||
p.update(prefs, false, true, false, 27100);
|
||||
assert(p.on()); // Real input extends the boot timeout.
|
||||
p.update(prefs, false, true, false, 54099);
|
||||
assert(!p.on());
|
||||
p.update(prefs, false, true, true, 54100);
|
||||
assert(p.on() == (mode == DisplayMode::ButtonPairing));
|
||||
p.update(prefs, false, true, true, 100000);
|
||||
assert(p.on() == (mode == DisplayMode::ButtonPairing));
|
||||
p.update(prefs, false, true, false, 100001);
|
||||
assert(!p.on());
|
||||
|
||||
DisplayPowerPolicy switched;
|
||||
switched.update(prefs, false, false, false, 0);
|
||||
assert(switched.on());
|
||||
switched.update(prefs, true, false, false, 1);
|
||||
assert(!switched.on()); // No synthetic boot when the power source changes.
|
||||
switched.update(prefs, false, false, false, 2);
|
||||
assert(!switched.on());
|
||||
prefs.battery.mode = DisplayMode::Off;
|
||||
switched.update(prefs, false, false, false, 3);
|
||||
prefs.battery.mode = mode;
|
||||
switched.update(prefs, false, false, false, 4);
|
||||
assert(!switched.on()); // No synthetic boot when the mode changes.
|
||||
|
||||
DisplayPowerPolicy dismissed;
|
||||
dismissed.update(prefs, false, false, false, 0);
|
||||
dismissed.dismiss();
|
||||
dismissed.update(prefs, false, false, false, 1);
|
||||
assert(!dismissed.on());
|
||||
|
||||
DisplayPowerPolicy rollover;
|
||||
rollover.update(prefs, false, false, false, UINT32_MAX - 500);
|
||||
rollover.update(prefs, false, false, false, 26498);
|
||||
assert(rollover.on());
|
||||
rollover.update(prefs, false, false, false, 26499);
|
||||
assert(!rollover.on());
|
||||
}
|
||||
}
|
||||
void companionSettingsTests() {
|
||||
for (bool ble : {false, true}) {
|
||||
FakeFS fs;
|
||||
assert(!loadDisplayPowerSettings(&fs, ble, true));
|
||||
assert(displayPowerPrefs().wake_on_boot);
|
||||
const DisplayMode mode = ble ? DisplayMode::ButtonPairing : DisplayMode::Button;
|
||||
assert(displayPowerPrefs().battery.mode == mode);
|
||||
assert(displayPowerPrefs().usb.mode == mode);
|
||||
char reply[160];
|
||||
for (const char* command : {"set display.timeout 27", "set display.usb.timeout 123"}) {
|
||||
assert(handleDisplayPowerCommand(command, reply, sizeof(reply)));
|
||||
assert(std::string(reply) == "OK");
|
||||
}
|
||||
assert(fs.files["/display_prefs"].size() == 12); // No on-device format change.
|
||||
assert(fs.files["/display_prefs"][10] == 0);
|
||||
assert(loadDisplayPowerSettings(&fs, ble, true));
|
||||
assert(displayPowerPrefs().wake_on_boot);
|
||||
for (bool usb : {false, true}) {
|
||||
DisplayPowerPolicy p;
|
||||
p.update(displayPowerPrefs(), usb, false, false, 0);
|
||||
assert(p.on()); // Previously saved button modes also show startup.
|
||||
const uint32_t timeout = usb ? 123000 : 27000;
|
||||
p.update(displayPowerPrefs(), usb, false, false, timeout - 1);
|
||||
assert(p.on());
|
||||
p.update(displayPowerPrefs(), usb, false, false, timeout);
|
||||
assert(!p.on());
|
||||
}
|
||||
for (const char* command : {"set display.mode off", "set display.usb.mode off"}) {
|
||||
assert(handleDisplayPowerCommand(command, reply, sizeof(reply)));
|
||||
assert(std::string(reply) == "OK");
|
||||
}
|
||||
for (unsigned version : {1, 2}) {
|
||||
auto& saved = fs.files["/display_prefs"];
|
||||
saved[2] = version; saved[11] = 0;
|
||||
for (unsigned i = 0; i < 11; ++i) saved[11] ^= saved[i];
|
||||
assert(loadDisplayPowerSettings(&fs, ble, true));
|
||||
assert(displayPowerPrefs().wake_on_boot);
|
||||
for (bool usb : {false, true}) {
|
||||
DisplayPowerPolicy p;
|
||||
p.update(displayPowerPrefs(), usb, false, true, 0);
|
||||
assert(!p.on()); // Saved off wins over startup and pairing.
|
||||
assert(!p.wake(DisplayWake::Button, 1));
|
||||
}
|
||||
}
|
||||
assert(loadDisplayPowerSettings(&fs, ble));
|
||||
assert(!displayPowerPrefs().wake_on_boot); // Infrastructure does not opt in.
|
||||
assert(!loadDisplayPowerSettings(nullptr, ble, true));
|
||||
assert(displayPowerPrefs().wake_on_boot); // Defaults still work without flash.
|
||||
DisplayPowerPolicy p;
|
||||
p.update(displayPowerPrefs(), false, false, false, 0);
|
||||
assert(p.on());
|
||||
assert(!loadDisplayPowerSettings(nullptr, ble));
|
||||
assert(!displayPowerPrefs().wake_on_boot);
|
||||
}
|
||||
}
|
||||
void commandTests() {
|
||||
FakeFS fs;
|
||||
assert(!loadDisplayPowerSettings(&fs, true));
|
||||
@@ -190,11 +320,30 @@ void commandTests() {
|
||||
run("get display.timeout", "> 42");
|
||||
run("get display.usb.timeout", "> 42");
|
||||
}
|
||||
int main() { policyTests(); commandTests(); }
|
||||
int main() {
|
||||
policyTests(); companionBootTests(); companionSettingsTests(); commandTests();
|
||||
}
|
||||
'''
|
||||
|
||||
|
||||
class DisplayPowerTest(unittest.TestCase):
|
||||
def test_companion_boot_wake_is_transport_and_layout_independent(self):
|
||||
mesh = (ROOT / "examples/companion_radio/MyMesh.cpp").read_text()
|
||||
self.assertRegex(mesh, re.compile(
|
||||
r"loadDisplayPowerSettings\(_store->getPrimaryFS\(\),\s*"
|
||||
r"#ifdef BLE_PIN_CODE\s*true,\s*#else\s*false,\s*#endif\s*true\b"))
|
||||
main = (ROOT / "examples/companion_radio/main.cpp").read_text()
|
||||
setup = main[main.index("void setup()"):main.index("\nvoid loop()")]
|
||||
self.assertLess(setup.index("the_mesh.begin("), setup.index("ui_task.begin("))
|
||||
for layout in ("ui-new", "ui-orig", "ui-tiny"):
|
||||
with self.subTest(layout=layout):
|
||||
ui = (ROOT / "examples/companion_radio" / layout / "UITask.cpp").read_text()
|
||||
start = ui.index("void UITask::begin(")
|
||||
end = ui.index("\n}", start)
|
||||
self.assertIn("_display->servicePower(", ui[start:end])
|
||||
infrastructure = (ROOT / "src/helpers/CommonCLI.cpp").read_text()
|
||||
self.assertIn("loadDisplayPowerSettings(fs, false);", infrastructure)
|
||||
|
||||
def test_policy_and_persistent_commands(self):
|
||||
compiler = shutil.which("g++") or shutil.which("clang++")
|
||||
self.assertIsNotNone(compiler, "host C++ compiler required")
|
||||
|
||||
Reference in new issue
Block a user