[lvgl] Fix race condition involving numbers, switches etc. (#7345)

This commit is contained in:
Clyde Stubbs 2024-08-26 08:03:25 +10:00 committed by Jesse Hills
parent 8677763492
commit c1774c42c2
No known key found for this signature in database
GPG key ID: BEAAE804EFD8E83A
11 changed files with 33 additions and 12 deletions

View file

@ -266,7 +266,10 @@ async def to_code(config):
await add_top_layer(config) await add_top_layer(config)
await msgboxes_to_code(config) await msgboxes_to_code(config)
await disp_update(f"{lv_component}->get_disp()", config) await disp_update(f"{lv_component}->get_disp()", config)
Widget.set_completed() # At this point only the setup code should be generated
assert LvContext.added_lambda_count == 1
Widget.set_completed()
async with LvContext(lv_component):
await generate_triggers(lv_component) await generate_triggers(lv_component)
for conf in config.get(CONF_ON_IDLE, ()): for conf in config.get(CONF_ON_IDLE, ()):
templ = await cg.templatable(conf[CONF_TIMEOUT], [], cg.uint32) templ = await cg.templatable(conf[CONF_TIMEOUT], [], cg.uint32)

View file

@ -10,7 +10,7 @@ from ..defines import CONF_LVGL_ID, CONF_WIDGET
from ..lvcode import EVENT_ARG, LambdaContext, LvContext from ..lvcode import EVENT_ARG, LambdaContext, LvContext
from ..schemas import LVGL_SCHEMA from ..schemas import LVGL_SCHEMA
from ..types import LV_EVENT, lv_pseudo_button_t from ..types import LV_EVENT, lv_pseudo_button_t
from ..widgets import Widget, get_widgets from ..widgets import Widget, get_widgets, wait_for_widgets
CONFIG_SCHEMA = ( CONFIG_SCHEMA = (
binary_sensor_schema(BinarySensor) binary_sensor_schema(BinarySensor)
@ -29,6 +29,7 @@ async def to_code(config):
widget = await get_widgets(config, CONF_WIDGET) widget = await get_widgets(config, CONF_WIDGET)
widget = widget[0] widget = widget[0]
assert isinstance(widget, Widget) assert isinstance(widget, Widget)
await wait_for_widgets()
async with LambdaContext(EVENT_ARG) as pressed_ctx: async with LambdaContext(EVENT_ARG) as pressed_ctx:
pressed_ctx.add(sensor.publish_state(widget.is_pressed())) pressed_ctx.add(sensor.publish_state(widget.is_pressed()))
async with LvContext(paren) as ctx: async with LvContext(paren) as ctx:

View file

@ -8,7 +8,7 @@ from ..defines import CONF_LVGL_ID
from ..lvcode import LvContext from ..lvcode import LvContext
from ..schemas import LVGL_SCHEMA from ..schemas import LVGL_SCHEMA
from ..types import LvType, lvgl_ns from ..types import LvType, lvgl_ns
from ..widgets import get_widgets from ..widgets import get_widgets, wait_for_widgets
lv_led_t = LvType("lv_led_t") lv_led_t = LvType("lv_led_t")
LVLight = lvgl_ns.class_("LVLight", LightOutput) LVLight = lvgl_ns.class_("LVLight", LightOutput)
@ -28,5 +28,6 @@ async def to_code(config):
paren = await cg.get_variable(config[CONF_LVGL_ID]) paren = await cg.get_variable(config[CONF_LVGL_ID])
widget = await get_widgets(config, CONF_LED) widget = await get_widgets(config, CONF_LED)
widget = widget[0] widget = widget[0]
await wait_for_widgets()
async with LvContext(paren) as ctx: async with LvContext(paren) as ctx:
ctx.add(var.set_obj(widget.obj)) ctx.add(var.set_obj(widget.obj))

View file

@ -176,6 +176,8 @@ class LvContext(LambdaContext):
Code generation into the LVGL initialisation code (called in `setup()`) Code generation into the LVGL initialisation code (called in `setup()`)
""" """
added_lambda_count = 0
def __init__(self, lv_component, args=None): def __init__(self, lv_component, args=None):
self.args = args or LVGL_COMP_ARG self.args = args or LVGL_COMP_ARG
super().__init__(parameters=self.args) super().__init__(parameters=self.args)
@ -183,6 +185,7 @@ class LvContext(LambdaContext):
async def add_init_lambda(self): async def add_init_lambda(self):
cg.add(self.lv_component.add_init_lambda(await self.get_lambda())) cg.add(self.lv_component.add_init_lambda(await self.get_lambda()))
LvContext.added_lambda_count += 1
async def __aexit__(self, exc_type, exc_val, exc_tb): async def __aexit__(self, exc_type, exc_val, exc_tb):
await super().__aexit__(exc_type, exc_val, exc_tb) await super().__aexit__(exc_type, exc_val, exc_tb)

View file

@ -16,7 +16,7 @@ from ..lvcode import (
) )
from ..schemas import LVGL_SCHEMA from ..schemas import LVGL_SCHEMA
from ..types import LV_EVENT, LvNumber, lvgl_ns from ..types import LV_EVENT, LvNumber, lvgl_ns
from ..widgets import get_widgets from ..widgets import get_widgets, wait_for_widgets
LVGLNumber = lvgl_ns.class_("LVGLNumber", number.Number) LVGLNumber = lvgl_ns.class_("LVGLNumber", number.Number)
@ -44,6 +44,7 @@ async def to_code(config):
step=widget.get_step(), step=widget.get_step(),
) )
await wait_for_widgets()
async with LambdaContext([(cg.float_, "v")]) as control: async with LambdaContext([(cg.float_, "v")]) as control:
await widget.set_property( await widget.set_property(
"value", MockObj("v") * MockObj(widget.get_scale()), config[CONF_ANIMATED] "value", MockObj("v") * MockObj(widget.get_scale()), config[CONF_ANIMATED]

View file

@ -15,7 +15,7 @@ from ..lvcode import (
) )
from ..schemas import LVGL_SCHEMA from ..schemas import LVGL_SCHEMA
from ..types import LV_EVENT, LvSelect, lvgl_ns from ..types import LV_EVENT, LvSelect, lvgl_ns
from ..widgets import get_widgets from ..widgets import get_widgets, wait_for_widgets
LVGLSelect = lvgl_ns.class_("LVGLSelect", select.Select) LVGLSelect = lvgl_ns.class_("LVGLSelect", select.Select)
@ -37,6 +37,7 @@ async def to_code(config):
options = widget.config.get(CONF_OPTIONS, []) options = widget.config.get(CONF_OPTIONS, [])
selector = await select.new_select(config, options=options) selector = await select.new_select(config, options=options)
paren = await cg.get_variable(config[CONF_LVGL_ID]) paren = await cg.get_variable(config[CONF_LVGL_ID])
await wait_for_widgets()
async with LambdaContext(EVENT_ARG) as pub_ctx: async with LambdaContext(EVENT_ARG) as pub_ctx:
pub_ctx.add(selector.publish_index(widget.get_value())) pub_ctx.add(selector.publish_index(widget.get_value()))
async with LambdaContext([(cg.uint16, "v")]) as control: async with LambdaContext([(cg.uint16, "v")]) as control:

View file

@ -14,7 +14,7 @@ from ..lvcode import (
) )
from ..schemas import LVGL_SCHEMA from ..schemas import LVGL_SCHEMA
from ..types import LV_EVENT, LvNumber from ..types import LV_EVENT, LvNumber
from ..widgets import Widget, get_widgets from ..widgets import Widget, get_widgets, wait_for_widgets
CONFIG_SCHEMA = ( CONFIG_SCHEMA = (
sensor_schema(Sensor) sensor_schema(Sensor)
@ -33,6 +33,7 @@ async def to_code(config):
widget = await get_widgets(config, CONF_WIDGET) widget = await get_widgets(config, CONF_WIDGET)
widget = widget[0] widget = widget[0]
assert isinstance(widget, Widget) assert isinstance(widget, Widget)
await wait_for_widgets()
async with LambdaContext(EVENT_ARG) as lamb: async with LambdaContext(EVENT_ARG) as lamb:
lv_add(sensor.publish_state(widget.get_value())) lv_add(sensor.publish_state(widget.get_value()))
async with LvContext(paren, LVGL_COMP_ARG): async with LvContext(paren, LVGL_COMP_ARG):

View file

@ -16,7 +16,7 @@ from ..lvcode import (
) )
from ..schemas import LVGL_SCHEMA from ..schemas import LVGL_SCHEMA
from ..types import LV_EVENT, LV_STATE, lv_pseudo_button_t, lvgl_ns from ..types import LV_EVENT, LV_STATE, lv_pseudo_button_t, lvgl_ns
from ..widgets import get_widgets from ..widgets import get_widgets, wait_for_widgets
LVGLSwitch = lvgl_ns.class_("LVGLSwitch", Switch) LVGLSwitch = lvgl_ns.class_("LVGLSwitch", Switch)
CONFIG_SCHEMA = ( CONFIG_SCHEMA = (
@ -35,6 +35,7 @@ async def to_code(config):
paren = await cg.get_variable(config[CONF_LVGL_ID]) paren = await cg.get_variable(config[CONF_LVGL_ID])
widget = await get_widgets(config, CONF_WIDGET) widget = await get_widgets(config, CONF_WIDGET)
widget = widget[0] widget = widget[0]
await wait_for_widgets()
async with LambdaContext(EVENT_ARG) as checked_ctx: async with LambdaContext(EVENT_ARG) as checked_ctx:
checked_ctx.add(switch.publish_state(widget.get_value())) checked_ctx.add(switch.publish_state(widget.get_value()))
async with LambdaContext([(cg.bool_, "v")]) as control: async with LambdaContext([(cg.bool_, "v")]) as control:

View file

@ -15,7 +15,7 @@ from ..lvcode import (
) )
from ..schemas import LVGL_SCHEMA from ..schemas import LVGL_SCHEMA
from ..types import LV_EVENT, LvText, lvgl_ns from ..types import LV_EVENT, LvText, lvgl_ns
from ..widgets import get_widgets from ..widgets import get_widgets, wait_for_widgets
LVGLText = lvgl_ns.class_("LVGLText", text.Text) LVGLText = lvgl_ns.class_("LVGLText", text.Text)
@ -32,6 +32,7 @@ async def to_code(config):
paren = await cg.get_variable(config[CONF_LVGL_ID]) paren = await cg.get_variable(config[CONF_LVGL_ID])
widget = await get_widgets(config, CONF_WIDGET) widget = await get_widgets(config, CONF_WIDGET)
widget = widget[0] widget = widget[0]
await wait_for_widgets()
async with LambdaContext([(cg.std_string, "text_value")]) as control: async with LambdaContext([(cg.std_string, "text_value")]) as control:
await widget.set_property("text", "text_value.c_str())") await widget.set_property("text", "text_value.c_str())")
lv.event_send(widget.obj, API_EVENT, None) lv.event_send(widget.obj, API_EVENT, None)

View file

@ -10,7 +10,7 @@ from ..defines import CONF_LVGL_ID, CONF_WIDGET
from ..lvcode import API_EVENT, EVENT_ARG, UPDATE_EVENT, LambdaContext, LvContext from ..lvcode import API_EVENT, EVENT_ARG, UPDATE_EVENT, LambdaContext, LvContext
from ..schemas import LVGL_SCHEMA from ..schemas import LVGL_SCHEMA
from ..types import LV_EVENT, LvText from ..types import LV_EVENT, LvText
from ..widgets import get_widgets from ..widgets import get_widgets, wait_for_widgets
CONFIG_SCHEMA = ( CONFIG_SCHEMA = (
text_sensor_schema(TextSensor) text_sensor_schema(TextSensor)
@ -28,6 +28,7 @@ async def to_code(config):
paren = await cg.get_variable(config[CONF_LVGL_ID]) paren = await cg.get_variable(config[CONF_LVGL_ID])
widget = await get_widgets(config, CONF_WIDGET) widget = await get_widgets(config, CONF_WIDGET)
widget = widget[0] widget = widget[0]
await wait_for_widgets()
async with LambdaContext(EVENT_ARG) as pressed_ctx: async with LambdaContext(EVENT_ARG) as pressed_ctx:
pressed_ctx.add(sensor.publish_state(widget.get_value())) pressed_ctx.add(sensor.publish_state(widget.get_value()))
async with LvContext(paren) as ctx: async with LvContext(paren) as ctx:

View file

@ -1,4 +1,3 @@
import asyncio
import sys import sys
from typing import Any, Union from typing import Any, Union
@ -224,9 +223,17 @@ async def get_widget_(wid: Widget):
return await FakeAwaitable(get_widget_generator(wid)) return await FakeAwaitable(get_widget_generator(wid))
def widgets_wait_generator():
while True:
if Widget.widgets_completed:
return
yield
async def wait_for_widgets(): async def wait_for_widgets():
while not Widget.widgets_completed: if Widget.widgets_completed:
await asyncio.sleep(0) return
await FakeAwaitable(widgets_wait_generator())
async def get_widgets(config: Union[dict, list], id: str = CONF_ID) -> list[Widget]: async def get_widgets(config: Union[dict, list], id: str = CONF_ID) -> list[Widget]: