Give Bluetooth settings a bit of a refresh

It's now a bit more responsive to stuff happening, gives you more information, and remembers your previously paired devices for faster switching between them.
custom
jacqueline 9 months ago
parent a3eb2dd9dc
commit f78de39a75
  1. 42
      lua/settings.lua
  2. 7
      lua/widgets.lua
  3. 22
      src/drivers/bluetooth.cpp
  4. 2
      src/drivers/include/drivers/nvs.hpp
  5. 2
      src/drivers/nvs.cpp
  6. 5
      src/tangara/lua/property.cpp

@ -46,6 +46,7 @@ local BluetoothPairing = SettingsScreen:new {
for _, dev in pairs(devs) do for _, dev in pairs(devs) do
devices:add_btn(nil, dev.name):onClicked(function() devices:add_btn(nil, dev.name):onClicked(function()
bluetooth.paired_device:set(dev) bluetooth.paired_device:set(dev)
backstack.pop()
end) end)
end end
end) end)
@ -101,16 +102,15 @@ local BluetoothSettings = SettingsScreen:new {
theme.set_style(paired_label, "settings_title") theme.set_style(paired_label, "settings_title")
self.bindings = self.bindings + { self.bindings = self.bindings + {
bluetooth.connected:bind(function(conn) bluetooth.connecting:bind(function(conn)
if conn then if conn then
paired_label:set { text = "Connecting to:" }
else
if bluetooth.connected:get() then
paired_label:set { text = "Connected to:" } paired_label:set { text = "Connected to:" }
else else
paired_label:set { text = "Paired with:" } paired_label:set { text = "Paired with:" }
end end
end),
bluetooth.connecting:bind(function(conn)
if conn then
paired_label:set { text = "Connecting to:" }
end end
end), end),
} }
@ -159,22 +159,42 @@ local BluetoothSettings = SettingsScreen:new {
h = lvgl.SIZE_CONTENT, h = lvgl.SIZE_CONTENT,
} }
-- 'Pair new device' button that goes to the discovery screen.
local button_container = self.content:Object {
w = lvgl.PCT(100),
h = lvgl.SIZE_CONTENT,
flex = {
flex_direction = "row",
justify_content = "center",
align_items = "space-evenly",
align_content = "center",
},
pad_top = 4,
pad_column = 4,
}
button_container:add_style(styles.list_item)
local pair_new = button_container:Button {}
pair_new:Label { text = "Pair new device" }
pair_new:onClicked(function()
backstack.push(BluetoothPairing:new())
end)
self.bindings = self.bindings + { self.bindings = self.bindings + {
bluetooth.known_devices:bind(function(devs) bluetooth.known_devices:bind(function(devs)
local group = lvgl.group.get_default()
group.remove_obj(pair_new)
devices:clean() devices:clean()
for _, dev in pairs(devs) do for _, dev in pairs(devs) do
devices:add_btn(nil, dev.name):onClicked(function() devices:add_btn(nil, dev.name):onClicked(function()
bluetooth.paired_device:set(dev) bluetooth.paired_device:set(dev)
end) end)
end end
group:add_obj(pair_new)
end) end)
} }
local pair_new = self.content:Button {}
pair_new:Label { text = "Pair new device" }
pair_new:onClicked(function()
backstack.push(BluetoothPairing:new())
end)
end end
} }

@ -79,11 +79,10 @@ function widgets.Row(parent, left, right)
} }
end end
local bindings_meta = { local bindings_meta = {}
__add = function(a, b) bindings_meta["__add"] = function(a, b)
return table.move(a, 1, #a, #b + 1, b) return setmetatable(table.move(a, 1, #a, #b + 1, b), bindings_meta)
end end
}
function widgets.StatusBar(parent, opts) function widgets.StatusBar(parent, opts)
local root = parent.root:Object { local root = parent.root:Object {

@ -603,17 +603,22 @@ void Disabled::react(const events::Enable&) {
void Idle::entry() { void Idle::entry() {
ESP_LOGI(kTag, "bt is idle"); ESP_LOGI(kTag, "bt is idle");
std::invoke(sEventHandler_, SimpleEvent::kConnectionStateChanged);
} }
void Idle::exit() {} void Idle::exit() {
std::invoke(sEventHandler_, SimpleEvent::kConnectionStateChanged);
}
void Idle::react(const events::Disable& ev) { void Idle::react(const events::Disable& ev) {
transit<Disabled>(); transit<Disabled>();
} }
void Idle::react(const events::PairedDeviceChanged& ev) { void Idle::react(const events::PairedDeviceChanged& ev) {
if (sPairedWith_) {
connect(*sPairedWith_); connect(*sPairedWith_);
} }
}
void Idle::react(events::internal::Gap ev) { void Idle::react(events::internal::Gap ev) {
sScanner_->HandleGapEvent(ev); sScanner_->HandleGapEvent(ev);
@ -633,18 +638,10 @@ void Connecting::entry() {
sTimeoutTimer = xTimerCreate("bt_timeout", pdMS_TO_TICKS(15000), false, NULL, sTimeoutTimer = xTimerCreate("bt_timeout", pdMS_TO_TICKS(15000), false, NULL,
timeoutCallback); timeoutCallback);
xTimerStart(sTimeoutTimer, portMAX_DELAY); xTimerStart(sTimeoutTimer, portMAX_DELAY);
if (sEventHandler_) {
std::invoke(sEventHandler_, SimpleEvent::kConnectionStateChanged);
}
} }
void Connecting::exit() { void Connecting::exit() {
xTimerDelete(sTimeoutTimer, portMAX_DELAY); xTimerDelete(sTimeoutTimer, portMAX_DELAY);
if (sEventHandler_) {
std::invoke(sEventHandler_, SimpleEvent::kConnectionStateChanged);
}
} }
void Connecting::react(const events::ConnectTimedOut& ev) { void Connecting::react(const events::ConnectTimedOut& ev) {
@ -751,12 +748,16 @@ void Connected::entry() {
sStorage_->PreferredBluetoothDevice(sPairedWith_); sStorage_->PreferredBluetoothDevice(sPairedWith_);
} }
std::invoke(sEventHandler_, SimpleEvent::kConnectionStateChanged);
// TODO: if we already have a source, immediately start playing // TODO: if we already have a source, immediately start playing
} }
void Connected::exit() { void Connected::exit() {
ESP_LOGI(kTag, "exiting connected state"); ESP_LOGI(kTag, "exiting connected state");
esp_a2d_source_disconnect(connected_to_.data()); esp_a2d_source_disconnect(connected_to_.data());
std::invoke(sEventHandler_, SimpleEvent::kConnectionStateChanged);
} }
void Connected::react(const events::Disable& ev) { void Connected::react(const events::Disable& ev) {
@ -765,6 +766,9 @@ void Connected::react(const events::Disable& ev) {
void Connected::react(const events::PairedDeviceChanged& ev) { void Connected::react(const events::PairedDeviceChanged& ev) {
transit<Idle>(); transit<Idle>();
if (sPairedWith_) {
connect(*sPairedWith_);
}
} }
void Connected::react(const events::SourceChanged& ev) { void Connected::react(const events::SourceChanged& ev) {

@ -34,7 +34,7 @@ class Setting {
dirty_ = true; dirty_ = true;
} }
} }
auto get() -> std::optional<T>& { return val_; } auto get() -> std::optional<T> { return val_; }
/* Reads the stored value from NVS and parses it into the correct type. */ /* Reads the stored value from NVS and parses it into the correct type. */
auto load(nvs_handle_t) -> std::optional<T>; auto load(nvs_handle_t) -> std::optional<T>;

@ -391,7 +391,7 @@ auto NvsStorage::BluetoothNames() -> std::vector<bluetooth::MacAndName> {
auto NvsStorage::BluetoothName(const bluetooth::mac_addr_t& mac, auto NvsStorage::BluetoothName(const bluetooth::mac_addr_t& mac,
std::optional<std::string> name) -> void { std::optional<std::string> name) -> void {
std::lock_guard<std::mutex> lock{mutex_}; std::lock_guard<std::mutex> lock{mutex_};
auto& val = bt_names_.get(); auto val = bt_names_.get();
if (!val) { if (!val) {
val.emplace(); val.emplace();
} }

@ -371,10 +371,10 @@ auto popRichType(lua_State* L) -> LuaValue {
} }
auto Property::popValue(lua_State& s) -> bool { auto Property::popValue(lua_State& s) -> bool {
LuaValue new_val; LuaValue new_val{std::monostate{}};
if (lua_gettop(&s) >= 2) {
switch (lua_type(&s, 2)) { switch (lua_type(&s, 2)) {
case LUA_TNIL: case LUA_TNIL:
new_val = std::monostate{};
break; break;
case LUA_TNUMBER: case LUA_TNUMBER:
if (lua_isinteger(&s, 2)) { if (lua_isinteger(&s, 2)) {
@ -399,6 +399,7 @@ auto Property::popValue(lua_State& s) -> bool {
return false; return false;
} }
} }
}
return set(new_val); return set(new_val);
} }

Loading…
Cancel
Save