1
0
mirror of https://github.com/esphome/esphome.git synced 2025-09-02 03:12:20 +01:00
Files
esphome/esphome/components/lvgl/widgets/spinbox.py
2025-08-29 13:58:58 +12:00

188 lines
5.3 KiB
Python

from esphome import automation
import esphome.config_validation as cv
from esphome.const import CONF_ID, CONF_RANGE_FROM, CONF_RANGE_TO, CONF_STEP, CONF_VALUE
from ..automation import action_to_code, update_to_code
from ..defines import (
CONF_CURSOR,
CONF_DECIMAL_PLACES,
CONF_DIGITS,
CONF_MAIN,
CONF_ROLLOVER,
CONF_SCROLLBAR,
CONF_SELECTED,
CONF_SELECTED_DIGIT,
CONF_TEXTAREA_PLACEHOLDER,
)
from ..lv_validation import lv_bool, lv_float
from ..lvcode import lv
from ..types import LvNumber, ObjUpdateAction
from . import Widget, WidgetType, get_widgets
from .label import CONF_LABEL
from .textarea import CONF_TEXTAREA
CONF_SPINBOX = "spinbox"
lv_spinbox_t = LvNumber("lv_spinbox_t")
SPIN_ACTIONS = (
"INCREMENT",
"DECREMENT",
"STEP_NEXT",
"STEP_PREV",
"CLEAR",
)
def validate_spinbox(config):
max_val = 2**31 - 1
min_val = -1 - max_val
range_from = int(config[CONF_RANGE_FROM])
range_to = int(config[CONF_RANGE_TO])
step = config[CONF_SELECTED_DIGIT]
digits = config[CONF_DIGITS]
if (
range_from > max_val
or range_from < min_val
or range_to > max_val
or range_to < min_val
):
raise cv.Invalid("Range outside allowed limits", path=[CONF_RANGE_FROM])
if digits <= config[CONF_DECIMAL_PLACES]:
raise cv.Invalid(
"Number of digits must exceed number of decimal places", path=[CONF_DIGITS]
)
if step >= digits:
raise cv.Invalid(
"Initial selected digit must be less than number of digits",
path=[CONF_SELECTED_DIGIT],
)
return config
SPINBOX_SCHEMA = cv.Schema(
{
cv.Optional(CONF_VALUE): lv_float,
cv.Optional(CONF_RANGE_FROM, default=0): cv.float_,
cv.Optional(CONF_RANGE_TO, default=100): cv.float_,
cv.Optional(CONF_DIGITS, default=4): cv.int_range(1, 10),
cv.Optional(CONF_STEP): cv.invalid(
f"{CONF_STEP} has been replaced by {CONF_SELECTED_DIGIT}"
),
cv.Optional(CONF_SELECTED_DIGIT, default=0): cv.positive_int,
cv.Optional(CONF_DECIMAL_PLACES, default=0): cv.int_range(0, 6),
cv.Optional(CONF_ROLLOVER, default=False): lv_bool,
}
).add_extra(validate_spinbox)
SPINBOX_MODIFY_SCHEMA = {
cv.Required(CONF_VALUE): lv_float,
}
class SpinboxType(WidgetType):
def __init__(self):
super().__init__(
CONF_SPINBOX,
lv_spinbox_t,
(
CONF_MAIN,
CONF_SCROLLBAR,
CONF_SELECTED,
CONF_CURSOR,
CONF_TEXTAREA_PLACEHOLDER,
),
SPINBOX_SCHEMA,
SPINBOX_MODIFY_SCHEMA,
)
async def to_code(self, w: Widget, config):
if CONF_DIGITS in config:
digits = config[CONF_DIGITS]
scale = 10 ** config[CONF_DECIMAL_PLACES]
range_from = int(config[CONF_RANGE_FROM]) * scale
range_to = int(config[CONF_RANGE_TO]) * scale
step = config[CONF_SELECTED_DIGIT]
w.scale = scale
w.range_to = range_to
w.range_from = range_from
lv.spinbox_set_range(w.obj, range_from, range_to)
await w.set_property("step", 10**step)
await w.set_property(CONF_ROLLOVER, config)
lv.spinbox_set_digit_format(
w.obj, digits, digits - config[CONF_DECIMAL_PLACES]
)
if (value := config.get(CONF_VALUE)) is not None:
lv.spinbox_set_value(w.obj, await lv_float.process(value))
def get_scale(self, config):
return 10 ** config[CONF_DECIMAL_PLACES]
def get_uses(self):
return CONF_TEXTAREA, CONF_LABEL
def get_max(self, config: dict):
return config[CONF_RANGE_TO]
def get_min(self, config: dict):
return config[CONF_RANGE_FROM]
def get_step(self, config: dict):
return 10 ** config[CONF_SELECTED_DIGIT]
spinbox_spec = SpinboxType()
@automation.register_action(
"lvgl.spinbox.increment",
ObjUpdateAction,
cv.maybe_simple_value(
{
cv.Required(CONF_ID): cv.use_id(lv_spinbox_t),
},
key=CONF_ID,
),
)
async def spinbox_increment(config, action_id, template_arg, args):
widgets = await get_widgets(config)
async def do_increment(w: Widget):
lv.spinbox_increment(w.obj)
return await action_to_code(widgets, do_increment, action_id, template_arg, args)
@automation.register_action(
"lvgl.spinbox.decrement",
ObjUpdateAction,
cv.maybe_simple_value(
{
cv.Required(CONF_ID): cv.use_id(lv_spinbox_t),
},
key=CONF_ID,
),
)
async def spinbox_decrement(config, action_id, template_arg, args):
widgets = await get_widgets(config)
async def do_increment(w: Widget):
lv.spinbox_decrement(w.obj)
return await action_to_code(widgets, do_increment, action_id, template_arg, args)
@automation.register_action(
"lvgl.spinbox.update",
ObjUpdateAction,
cv.Schema(
{
cv.Required(CONF_ID): cv.use_id(lv_spinbox_t),
cv.Required(CONF_VALUE): lv_float,
}
),
)
async def spinbox_update_to_code(config, action_id, template_arg, args):
return await update_to_code(config, action_id, template_arg, args)