Add two-way databinding for lua, and flesh out the lua statusbar

custom
jacqueline 1 year ago
parent 8a0a167adb
commit 71ed09a6f7
  1. 6
      lib/lvgl/lv_conf.h
  2. 0
      lua/assets/audio.png
  3. 0
      lua/assets/battery_20.png
  4. 0
      lua/assets/battery_40.png
  5. 0
      lua/assets/battery_60.png
  6. 0
      lua/assets/battery_80.png
  7. 0
      lua/assets/battery_empty.png
  8. BIN
      lua/assets/battery_full.bin
  9. 0
      lua/assets/battery_full.png
  10. BIN
      lua/assets/bt.png
  11. 0
      lua/assets/bt_conn.png
  12. 0
      lua/assets/pause.png
  13. 0
      lua/assets/play.png
  14. 37
      lua/backstack.lua
  15. 4
      lua/main.lua
  16. 19
      lua/main_menu.lua
  17. 79
      lua/widgets.lua
  18. 2
      src/lua/CMakeLists.txt
  19. 45
      src/lua/bridge.cpp
  20. 9
      src/lua/include/bridge.hpp
  21. 2
      src/lua/include/lua_thread.hpp
  22. 47
      src/lua/include/property.hpp
  23. 2
      src/lua/lua_thread.cpp
  24. 196
      src/lua/property.cpp
  25. 19
      src/ui/include/ui_fsm.hpp
  26. 43
      src/ui/ui_fsm.cpp

@ -597,9 +597,9 @@
/*File system interfaces for common APIs */ /*File system interfaces for common APIs */
/*API for fopen, fread, etc*/ /*API for fopen, fread, etc*/
#define LV_USE_FS_STDIO 0 #define LV_USE_FS_STDIO 1
#if LV_USE_FS_STDIO #if LV_USE_FS_STDIO
#define LV_FS_STDIO_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ #define LV_FS_STDIO_LETTER '/' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/
#define LV_FS_STDIO_PATH "" /*Set the working directory. File/directory paths will be appended to it.*/ #define LV_FS_STDIO_PATH "" /*Set the working directory. File/directory paths will be appended to it.*/
#define LV_FS_STDIO_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/ #define LV_FS_STDIO_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/
#endif #endif
@ -628,7 +628,7 @@
#endif #endif
/*PNG decoder library*/ /*PNG decoder library*/
#define LV_USE_PNG 0 #define LV_USE_PNG 1
/*BMP decoder library*/ /*BMP decoder library*/
#define LV_USE_BMP 0 #define LV_USE_BMP 0

Before

Width:  |  Height:  |  Size: 623 B

After

Width:  |  Height:  |  Size: 623 B

Before

Width:  |  Height:  |  Size: 617 B

After

Width:  |  Height:  |  Size: 617 B

Before

Width:  |  Height:  |  Size: 617 B

After

Width:  |  Height:  |  Size: 617 B

Before

Width:  |  Height:  |  Size: 618 B

After

Width:  |  Height:  |  Size: 618 B

Before

Width:  |  Height:  |  Size: 622 B

After

Width:  |  Height:  |  Size: 622 B

Before

Width:  |  Height:  |  Size: 614 B

After

Width:  |  Height:  |  Size: 614 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 618 B

After

Width:  |  Height:  |  Size: 618 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Before

Width:  |  Height:  |  Size: 654 B

After

Width:  |  Height:  |  Size: 654 B

Before

Width:  |  Height:  |  Size: 608 B

After

Width:  |  Height:  |  Size: 608 B

Before

Width:  |  Height:  |  Size: 616 B

After

Width:  |  Height:  |  Size: 616 B

@ -0,0 +1,37 @@
local lvgl = require("lvgl")
local backstack = {
root = lvgl.Object(nil, {
w = lvgl.HOR_RES(),
h = lvgl.VER_RES(),
}),
stack = {},
}
function backstack:Top()
return self.stack[#self.stack]
end
function backstack:SetTopParent(parent)
local top = self:Top()
if top and top.root then
top.root:set_parent(parent)
end
end
function backstack:Push(screen)
self:SetTopParent(nil)
table.insert(self.stack, screen)
self:SetTopParent(self.root)
end
function backstack:Pop(num)
num = num or 1
for _ = 1, num do
local removed = table.remove(self.stack)
removed.root:delete()
end
self:SetTopParent(self.root)
end
return backstack

@ -1 +1,3 @@
require("main_menu"):Create() local backstack = require("backstack")
local main_menu = require("main_menu"):Create(backstack.root)
backstack:Push(main_menu)

@ -5,8 +5,9 @@ local database = require("database")
local main_menu = {} local main_menu = {}
function main_menu:Create() function main_menu:Create(parent)
local root = lvgl.Object(nil, { local menu = {}
menu.root = lvgl.Object(parent, {
flex = { flex = {
flex_direction = "column", flex_direction = "column",
flex_wrap = "wrap", flex_wrap = "wrap",
@ -17,31 +18,33 @@ function main_menu:Create()
w = lvgl.HOR_RES(), w = lvgl.HOR_RES(),
h = lvgl.VER_RES(), h = lvgl.VER_RES(),
}) })
root:center() menu.root:center()
widgets.StatusBar(root, {}) menu.status_bar = widgets.StatusBar(menu.root, {})
local list = lvgl.List(root, { menu.list = lvgl.List(menu.root, {
w = lvgl.PCT(100), w = lvgl.PCT(100),
h = lvgl.PCT(100), h = lvgl.PCT(100),
flex_grow = 1, flex_grow = 1,
}) })
list:add_btn(nil, "Now Playing"):onClicked(function() menu.list:add_btn(nil, "Now Playing"):onClicked(function()
legacy_ui.open_now_playing(); legacy_ui.open_now_playing();
end) end)
local indexes = database.get_indexes() local indexes = database.get_indexes()
for id, name in ipairs(indexes) do for id, name in ipairs(indexes) do
local btn = list:add_btn(nil, name) local btn = menu.list:add_btn(nil, name)
btn:onClicked(function() btn:onClicked(function()
legacy_ui.open_browse(id); legacy_ui.open_browse(id);
end) end)
end end
list:add_btn(nil, "Settings"):onClicked(function() menu.list:add_btn(nil, "Settings"):onClicked(function()
legacy_ui.open_settings(); legacy_ui.open_settings();
end) end)
return menu
end end
return main_menu return main_menu

@ -1,9 +1,14 @@
local lvgl = require("lvgl") local lvgl = require("lvgl")
local power = require("power")
local bluetooth = require("bluetooth")
local playback = require("playback")
local widgets = {} local widgets = {}
function widgets.StatusBar(parent) function widgets.StatusBar(parent, opts)
local container = parent:Object { local status_bar = {}
status_bar.root = parent:Object {
flex = { flex = {
flex_direction = "row", flex_direction = "row",
justify_content = "flex-start", justify_content = "flex-start",
@ -11,27 +16,81 @@ function widgets.StatusBar(parent)
align_content = "center", align_content = "center",
}, },
w = lvgl.HOR_RES(), w = lvgl.HOR_RES(),
h = 16, h = 18,
} }
container:Label { if opts.back_cb then
status_bar.back = status_bar.root:Label {
w = lvgl.SIZE_CONTENT, w = lvgl.SIZE_CONTENT,
h = 12, h = 12,
text = "<", text = "<",
} }
status_bar.back:onClicked(opts.back_cb)
end
container:Label { status_bar.title = status_bar.root:Label {
w = lvgl.PCT(100), w = lvgl.PCT(100),
h = 16, h = 16,
text = "cool title", text = "",
flex_grow = 1, flex_grow = 1,
} }
if opts.title then
status_bar.title.set { text = opts.title }
end
container:Label { status_bar.playing = status_bar.root:Image {}
w = lvgl.SIZE_CONTENT, status_bar.bluetooth = status_bar.root:Image {}
h = 16, status_bar.battery = status_bar.root:Image {}
text = "69%",
status_bar.bindings = {
power.battery_pct:bind(function(percent)
local src
if percent >= 95 then
src = "battery_full.png"
elseif percent >= 75 then
src = "battery_80.png"
elseif percent >= 55 then
src = "battery_60.png"
elseif percent >= 35 then
src = "battery_40.png"
elseif percent >= 15 then
src = "battery_20.png"
else
src = "battery_empty.png"
end
status_bar.battery:set_src("//lua/assets/" .. src)
end),
playback.playing:bind(function(playing)
if playing then
status_bar.playing:set_src("//lua/assets/play.png")
else
status_bar.playing:set_src("//lua/assets/pause.png")
end
end),
playback.track:bind(function(track)
if track then
status_bar.playing:clear_flag(lvgl.FLAG.HIDDEN)
else
status_bar.playing:add_flag(lvgl.FLAG.HIDDEN)
end
end),
bluetooth.enabled:bind(function(en)
if en then
status_bar.bluetooth:clear_flag(lvgl.FLAG.HIDDEN)
else
status_bar.bluetooth:add_flag(lvgl.FLAG.HIDDEN)
end
end),
bluetooth.connected:bind(function(connected)
if connected then
status_bar.bluetooth:set_src("//lua/assets/bt_conn.png")
else
status_bar.bluetooth:set_src("//lua/assets/bt.png")
end
end),
} }
return status_bar
end end
return widgets return widgets

@ -3,7 +3,7 @@
# SPDX-License-Identifier: GPL-3.0-only # SPDX-License-Identifier: GPL-3.0-only
idf_component_register( idf_component_register(
SRCS "lua_thread.cpp" "bridge.cpp" SRCS "lua_thread.cpp" "bridge.cpp" "property.cpp"
INCLUDE_DIRS "include" INCLUDE_DIRS "include"
REQUIRES "drivers" "lvgl" "tinyfsm" "events" "system_fsm" "database" "esp_timer" "battery" "esp-idf-lua" "luavgl") REQUIRES "drivers" "lvgl" "tinyfsm" "events" "system_fsm" "database" "esp_timer" "battery" "esp-idf-lua" "luavgl")
target_compile_options(${COMPONENT_LIB} PRIVATE ${EXTRA_WARNINGS}) target_compile_options(${COMPONENT_LIB} PRIVATE ${EXTRA_WARNINGS})

@ -10,10 +10,12 @@
#include <string> #include <string>
#include "esp_log.h" #include "esp_log.h"
#include "event_queue.hpp" #include "lauxlib.h"
#include "lua.h"
#include "lua.hpp" #include "lua.hpp"
#include "lvgl.h" #include "lvgl.h"
#include "event_queue.hpp"
#include "property.hpp"
#include "service_locator.hpp" #include "service_locator.hpp"
#include "ui_events.hpp" #include "ui_events.hpp"
@ -53,9 +55,7 @@ static auto lua_legacy_ui(lua_State* state) -> int {
} }
static auto get_indexes(lua_State* state) -> int { static auto get_indexes(lua_State* state) -> int {
lua_pushstring(state, kBridgeKey); Bridge* instance = Bridge::Get(state);
lua_gettable(state, LUA_REGISTRYINDEX);
Bridge* instance = reinterpret_cast<Bridge*>(lua_touserdata(state, -1));
lua_newtable(state); lua_newtable(state);
@ -80,8 +80,14 @@ static auto lua_database(lua_State* state) -> int {
return 1; return 1;
} }
auto Bridge::Get(lua_State* state) -> Bridge* {
lua_pushstring(state, kBridgeKey);
lua_gettable(state, LUA_REGISTRYINDEX);
return reinterpret_cast<Bridge*>(lua_touserdata(state, -1));
}
Bridge::Bridge(system_fsm::ServiceLocator& services, lua_State& s) Bridge::Bridge(system_fsm::ServiceLocator& services, lua_State& s)
: services_(services), state_(s) { : services_(services), state_(s), bindings_(s) {
lua_pushstring(&s, kBridgeKey); lua_pushstring(&s, kBridgeKey);
lua_pushlightuserdata(&s, this); lua_pushlightuserdata(&s, this);
lua_settable(&s, LUA_REGISTRYINDEX); lua_settable(&s, LUA_REGISTRYINDEX);
@ -93,4 +99,31 @@ Bridge::Bridge(system_fsm::ServiceLocator& services, lua_State& s)
lua_pop(&s, 1); lua_pop(&s, 1);
} }
static auto new_property_module(lua_State* state) -> int {
const char* name = luaL_checkstring(state, 1);
luaL_newmetatable(state, name);
lua_pushstring(state, "__index");
lua_pushvalue(state, -2);
lua_settable(state, -3); // metatable.__index = metatable
return 1;
}
auto Bridge::AddPropertyModule(
const std::string& name,
std::vector<std::pair<std::string, std::shared_ptr<Property>>> props)
-> void {
// Create the module (or retrieve it if one with this name already exists)
luaL_requiref(&state_, name.c_str(), new_property_module, true);
for (const auto& prop : props) {
lua_pushstring(&state_, prop.first.c_str());
bindings_.Register(&state_, prop.second.get());
lua_settable(&state_, -3); // metatable.propname = property
}
lua_pop(&state_, 1); // pop the module off the stack
}
} // namespace lua } // namespace lua

@ -11,19 +11,28 @@
#include "lua.hpp" #include "lua.hpp"
#include "lvgl.h" #include "lvgl.h"
#include "property.hpp"
#include "service_locator.hpp" #include "service_locator.hpp"
namespace lua { namespace lua {
class Bridge { class Bridge {
public: public:
static auto Get(lua_State* state) -> Bridge*;
Bridge(system_fsm::ServiceLocator&, lua_State& s); Bridge(system_fsm::ServiceLocator&, lua_State& s);
auto AddPropertyModule(
const std::string&,
std::vector<std::pair<std::string, std::shared_ptr<Property>>>) -> void;
system_fsm::ServiceLocator& services() { return services_; } system_fsm::ServiceLocator& services() { return services_; }
PropertyBindings& bindings() { return bindings_; }
private: private:
system_fsm::ServiceLocator& services_; system_fsm::ServiceLocator& services_;
lua_State& state_; lua_State& state_;
PropertyBindings bindings_;
}; };
} // namespace lua } // namespace lua

@ -27,6 +27,8 @@ class LuaThread {
auto RunScript(const std::string& path) -> bool; auto RunScript(const std::string& path) -> bool;
auto bridge() -> Bridge& { return *bridge_; }
private: private:
LuaThread(std::unique_ptr<Allocator>&, std::unique_ptr<Bridge>&, lua_State*); LuaThread(std::unique_ptr<Allocator>&, std::unique_ptr<Bridge>&, lua_State*);

@ -0,0 +1,47 @@
/*
* Copyright 2023 jacqueline <me@jacqueline.id.au>
*
* SPDX-License-Identifier: GPL-3.0-only
*/
#pragma once
#include <memory>
#include <string>
#include "lua.hpp"
#include "lvgl.h"
#include "service_locator.hpp"
namespace lua {
using LuaValue = std::variant<std::monostate, int, float, bool, std::string>;
class Property {
public:
Property() : Property(std::monostate{}) {}
Property(const LuaValue&);
Property(const LuaValue&, std::function<bool(const LuaValue&)>);
auto IsTwoWay() -> bool { return cb_.has_value(); }
auto PushValue(lua_State& s) -> int;
auto PopValue(lua_State& s) -> bool;
auto Update(const LuaValue& new_val) -> void;
auto AddLuaBinding(lua_State*, int ref) -> void;
private:
LuaValue value_;
std::optional<std::function<bool(const LuaValue&)>> cb_;
std::vector<std::pair<lua_State*, int>> bindings_;
};
class PropertyBindings {
public:
PropertyBindings(lua_State&);
auto Register(lua_State*, Property*) -> void;
};
} // namespace lua

@ -5,11 +5,11 @@
*/ */
#include "lua_thread.hpp" #include "lua_thread.hpp"
#include <memory> #include <memory>
#include "esp_heap_caps.h" #include "esp_heap_caps.h"
#include "esp_log.h" #include "esp_log.h"
#include "lua.h"
#include "lua.hpp" #include "lua.hpp"
#include "luavgl.h" #include "luavgl.h"
#include "service_locator.hpp" #include "service_locator.hpp"

@ -0,0 +1,196 @@
/*
* Copyright 2023 jacqueline <me@jacqueline.id.au>
*
* SPDX-License-Identifier: GPL-3.0-only
*/
#include "property.hpp"
#include <memory>
#include <string>
#include "lua.h"
#include "lua.hpp"
#include "lvgl.h"
#include "service_locator.hpp"
namespace lua {
static const char kMetatableName[] = "property";
static const char kBindingsTable[] = "bindings";
static auto check_property(lua_State* state) -> Property* {
void* data = luaL_checkudata(state, 1, kMetatableName);
luaL_argcheck(state, data != NULL, 1, "`property` expected");
return *reinterpret_cast<Property**>(data);
}
static auto property_get(lua_State* state) -> int {
Property* p = check_property(state);
p->PushValue(*state);
return 1;
}
static auto property_set(lua_State* state) -> int {
Property* p = check_property(state);
luaL_argcheck(state, p->IsTwoWay(), 1, "property is read-only");
bool valid = p->PopValue(*state);
lua_pushboolean(state, valid);
return 1;
}
static auto property_bind(lua_State* state) -> int {
Property* p = check_property(state);
luaL_checktype(state, 2, LUA_TFUNCTION);
// Copy the function, as we need to invoke it then store our reference.
lua_pushvalue(state, 2);
// ...and another copy, since we return the original closure.
lua_pushvalue(state, 2);
// FIXME: This should ideally be lua_pcall, for safety.
p->PushValue(*state);
lua_call(state, 1, 0); // Invoke the initial binding.
lua_pushstring(state, kBindingsTable);
lua_gettable(state, LUA_REGISTRYINDEX); // REGISTRY[kBindingsTable]
lua_insert(state, -2); // Move bindings to the bottom, with fn above.
int ref = luaL_ref(state, -2); // bindings[ref] = fn
p->AddLuaBinding(state, ref);
// Pop the bindings table, leaving one of the copiesw of the callback fn at
// the top of the stack.
lua_pop(state, 1);
return 1;
}
static const struct luaL_Reg kPropertyBindingFuncs[] = {{"get", property_get},
{"set", property_set},
{"bind", property_bind},
{NULL, NULL}};
PropertyBindings::PropertyBindings(lua_State& s) {
// Create the metatable responsible for the Property API.
luaL_newmetatable(&s, kMetatableName);
lua_pushliteral(&s, "__index");
lua_pushvalue(&s, -2);
lua_settable(&s, -3); // metatable.__index = metatable
// Add our binding funcs (get, set, bind) to the metatable.
luaL_setfuncs(&s, kPropertyBindingFuncs, 0);
// Create a weak table in the registry to hold live bindings.
lua_pushstring(&s, kBindingsTable);
lua_newtable(&s); // bindings = {}
// Metatable for the weak table. Values are weak.
lua_newtable(&s); // meta = {}
lua_pushliteral(&s, "__mode");
lua_pushliteral(&s, "v");
lua_settable(&s, -3); // meta.__mode='v'
lua_setmetatable(&s, -2); // setmetatable(bindings, meta)
lua_settable(&s, LUA_REGISTRYINDEX); // REGISTRY[kBindingsTable] = bindings
}
auto PropertyBindings::Register(lua_State* s, Property* prop) -> void {
Property** data =
reinterpret_cast<Property**>(lua_newuserdata(s, sizeof(Property*)));
*data = prop;
luaL_setmetatable(s, kMetatableName);
}
template <class... Ts>
inline constexpr bool always_false_v = false;
Property::Property(const LuaValue& val) : value_(val), cb_() {}
Property::Property(const LuaValue& val,
std::function<bool(const LuaValue& val)> cb)
: value_(val), cb_(cb) {}
auto Property::PushValue(lua_State& s) -> int {
std::visit(
[&](auto&& arg) {
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, std::monostate>) {
lua_pushnil(&s);
} else if constexpr (std::is_same_v<T, int>) {
lua_pushinteger(&s, arg);
} else if constexpr (std::is_same_v<T, float>) {
lua_pushnumber(&s, arg);
} else if constexpr (std::is_same_v<T, bool>) {
lua_pushboolean(&s, arg);
} else if constexpr (std::is_same_v<T, std::string>) {
lua_pushstring(&s, arg.c_str());
} else {
static_assert(always_false_v<T>, "PushValue missing type");
}
},
value_);
return 1;
}
auto Property::PopValue(lua_State& s) -> bool {
LuaValue new_val;
switch (lua_type(&s, 2)) {
case LUA_TNIL:
new_val = std::monostate{};
break;
case LUA_TNUMBER:
if (lua_isinteger(&s, 2)) {
new_val = lua_tointeger(&s, 2);
} else {
new_val = lua_tonumber(&s, 2);
}
break;
case LUA_TBOOLEAN:
new_val = lua_toboolean(&s, 2);
break;
case LUA_TSTRING:
new_val = lua_tostring(&s, 2);
break;
default:
return false;
}
if (cb_ && std::invoke(*cb_, new_val)) {
Update(new_val);
return true;
}
return false;
}
auto Property::Update(const LuaValue& v) -> void {
value_ = v;
for (int i = bindings_.size() - 1; i >= 0; i--) {
auto& b = bindings_[i];
lua_pushstring(b.first, kBindingsTable);
lua_gettable(b.first, LUA_REGISTRYINDEX); // REGISTRY[kBindingsTable]
int type = lua_rawgeti(b.first, -1, b.second); // push bindings[i]
// Has closure has been GCed?
if (type == LUA_TNIL) {
// Clean up after ourselves.
lua_pop(b.first, 1);
// Remove the binding.
bindings_.erase(bindings_.begin() + i);
continue;
}
PushValue(*b.first); // push the argument
lua_call(b.first, 1, 0); // invoke the closure
}
}
auto Property::AddLuaBinding(lua_State* state, int ref) -> void {
bindings_.push_back({state, ref});
}
} // namespace lua

@ -21,6 +21,7 @@
#include "model_playback.hpp" #include "model_playback.hpp"
#include "model_top_bar.hpp" #include "model_top_bar.hpp"
#include "nvs.hpp" #include "nvs.hpp"
#include "property.hpp"
#include "relative_wheel.hpp" #include "relative_wheel.hpp"
#include "screen_playing.hpp" #include "screen_playing.hpp"
#include "screen_settings.hpp" #include "screen_settings.hpp"
@ -56,9 +57,9 @@ class UiState : public tinyfsm::Fsm<UiState> {
/* Fallback event handler. Does nothing. */ /* Fallback event handler. Does nothing. */
void react(const tinyfsm::Event& ev) {} void react(const tinyfsm::Event& ev) {}
void react(const system_fsm::BatteryStateChanged&); virtual void react(const system_fsm::BatteryStateChanged&);
void react(const audio::PlaybackStarted&); virtual void react(const audio::PlaybackStarted&);
void react(const audio::PlaybackFinished&); virtual void react(const audio::PlaybackFinished&);
void react(const audio::PlaybackUpdate&); void react(const audio::PlaybackUpdate&);
void react(const audio::QueueUpdate&); void react(const audio::QueueUpdate&);
@ -127,7 +128,19 @@ class Lua : public UiState {
void react(const internal::ShowNowPlaying&) override; void react(const internal::ShowNowPlaying&) override;
void react(const internal::ShowSettingsPage&) override; void react(const internal::ShowSettingsPage&) override;
void react(const system_fsm::BatteryStateChanged&) override;
void react(const audio::PlaybackStarted&) override;
void react(const audio::PlaybackFinished&) override;
using UiState::react; using UiState::react;
private:
std::shared_ptr<lua::Property> battery_pct_;
std::shared_ptr<lua::Property> battery_mv_;
std::shared_ptr<lua::Property> battery_charging_;
std::shared_ptr<lua::Property> bluetooth_en_;
std::shared_ptr<lua::Property> playback_playing_;
std::shared_ptr<lua::Property> playback_track_;
}; };
class Onboarding : public UiState { class Onboarding : public UiState {

@ -33,6 +33,7 @@
#include "modal_progress.hpp" #include "modal_progress.hpp"
#include "model_playback.hpp" #include "model_playback.hpp"
#include "nvs.hpp" #include "nvs.hpp"
#include "property.hpp"
#include "relative_wheel.hpp" #include "relative_wheel.hpp"
#include "screen.hpp" #include "screen.hpp"
#include "screen_lua.hpp" #include "screen_lua.hpp"
@ -183,7 +184,36 @@ void Lua::entry() {
sCurrentScreen.reset(new Screen()); sCurrentScreen.reset(new Screen());
lv_group_set_default(sCurrentScreen->group()); lv_group_set_default(sCurrentScreen->group());
auto bat =
sServices->battery().State().value_or(battery::Battery::BatteryState{});
battery_pct_ =
std::make_shared<lua::Property>(static_cast<int>(bat.percent));
battery_mv_ =
std::make_shared<lua::Property>(static_cast<int>(bat.millivolts));
battery_charging_ = std::make_shared<lua::Property>(bat.is_charging);
bluetooth_en_ = std::make_shared<lua::Property>(false);
playback_playing_ = std::make_shared<lua::Property>(false);
playback_track_ = std::make_shared<lua::Property>();
sLua.reset(lua::LuaThread::Start(*sServices, sCurrentScreen->content())); sLua.reset(lua::LuaThread::Start(*sServices, sCurrentScreen->content()));
sLua->bridge().AddPropertyModule("power",
{
{"battery_pct", battery_pct_},
{"battery_millivolts", battery_mv_},
{"plugged_in", battery_charging_},
});
sLua->bridge().AddPropertyModule("bluetooth",
{
{"enabled", bluetooth_en_},
{"connected", bluetooth_en_},
});
sLua->bridge().AddPropertyModule("playback",
{
{"playing", playback_playing_},
{"track", playback_track_},
});
sLua->RunScript("/lua/main.lua"); sLua->RunScript("/lua/main.lua");
lv_group_set_default(NULL); lv_group_set_default(NULL);
@ -216,6 +246,19 @@ void Lua::react(const internal::ShowSettingsPage& ev) {
transit<Browse>(); transit<Browse>();
} }
void Lua::react(const system_fsm::BatteryStateChanged& ev) {
battery_pct_->Update(static_cast<int>(ev.new_state.percent));
battery_mv_->Update(static_cast<int>(ev.new_state.millivolts));
}
void Lua::react(const audio::PlaybackStarted&) {
playback_playing_->Update(true);
}
void Lua::react(const audio::PlaybackFinished&) {
playback_playing_->Update(false);
}
void Onboarding::entry() { void Onboarding::entry() {
progress_ = 0; progress_ = 0;
has_formatted_ = false; has_formatted_ = false;

Loading…
Cancel
Save