mirror of https://github.com/arendst/Tasmota.git
OpenHASP v1.0
This commit is contained in:
parent
a65e6ac0b0
commit
e3ebffcc72
|
@ -1,764 +0,0 @@
|
||||||
import string
|
|
||||||
import json
|
|
||||||
|
|
||||||
# lv.start()
|
|
||||||
# scr = lv.scr_act() # default screean object
|
|
||||||
# scr.set_style_bg_color(lv.color(0x0000A0), lv.PART_MAIN | lv.STATE_DEFAULT)
|
|
||||||
|
|
||||||
lv.start()
|
|
||||||
|
|
||||||
hres = lv.get_hor_res() # should be 320
|
|
||||||
vres = lv.get_ver_res() # should be 240
|
|
||||||
|
|
||||||
scr = lv.scr_act() # default screean object
|
|
||||||
#f20 = lv.montserrat_font(20) # load embedded Montserrat 20
|
|
||||||
r20 = lv.font_robotocondensed_latin1(20)
|
|
||||||
r16 = lv.font_robotocondensed_latin1(16)
|
|
||||||
|
|
||||||
th2 = lv.theme_openhasp_init(0, lv.color(0xFF00FF), lv.color(0x303030), false, r16)
|
|
||||||
scr.get_disp().set_theme(th2)
|
|
||||||
# TODO
|
|
||||||
scr.set_style_bg_color(lv.color(lv.COLOR_WHITE),0)
|
|
||||||
|
|
||||||
# apply theme to layer_top, but keep it transparent
|
|
||||||
lv.theme_apply(lv.layer_top())
|
|
||||||
lv.layer_top().set_style_bg_opa(0,0)
|
|
||||||
|
|
||||||
|
|
||||||
# takes an attribute name and responds if it needs color conversion
|
|
||||||
def is_color_attribute(t)
|
|
||||||
import string
|
|
||||||
t = str(t)
|
|
||||||
# contains `color` but does not contain `color_`
|
|
||||||
return (string.find(t, "color") >= 0) && (string.find(t, "color_") < 0)
|
|
||||||
end
|
|
||||||
|
|
||||||
# parse hex string
|
|
||||||
def parse_hex(s)
|
|
||||||
import string
|
|
||||||
s = string.toupper(s) # turn to uppercase
|
|
||||||
var val = 0
|
|
||||||
for i:0..size(s)-1
|
|
||||||
var c = s[i]
|
|
||||||
# var c_int = string.byte(c)
|
|
||||||
if c == "#" continue end # skip '#' prefix if any
|
|
||||||
if c == "x" || c == "X" continue end # skip 'x' or 'X'
|
|
||||||
|
|
||||||
if c >= "A" && c <= "F"
|
|
||||||
val = (val << 4) | string.byte(c) - 55
|
|
||||||
elif c >= "0" && c <= "9"
|
|
||||||
val = (val << 4) | string.byte(c) - 48
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return val
|
|
||||||
end
|
|
||||||
|
|
||||||
def parse_color(s)
|
|
||||||
s = str(s)
|
|
||||||
if s[0] == '#'
|
|
||||||
return lv.color(parse_hex(s))
|
|
||||||
else
|
|
||||||
import string
|
|
||||||
import introspect
|
|
||||||
var col_name = "COLOR_" + string.toupper(s)
|
|
||||||
var col_try = introspect.get(lv, col_name)
|
|
||||||
if col_try != nil
|
|
||||||
return lv.color(col_try)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
# fail safe with black color
|
|
||||||
return lv.color(0x000000)
|
|
||||||
end
|
|
||||||
|
|
||||||
#- ------------------------------------------------------------
|
|
||||||
Class `lvh_obj` encapsulating `lv_obj``
|
|
||||||
|
|
||||||
Provide a mapping for virtual members
|
|
||||||
Stores the associated page and object id
|
|
||||||
|
|
||||||
Adds specific virtual members used by OpenHASP
|
|
||||||
- ------------------------------------------------------------ -#
|
|
||||||
class lvh_obj
|
|
||||||
# _lv_class refers to the lvgl class encapsulated, and is overriden by subclasses
|
|
||||||
static _lv_class = lv.obj
|
|
||||||
static _lv_part2_selector # selector for secondary part (like knob of arc)
|
|
||||||
|
|
||||||
# attributes to ignore when set at object level (they are managed by page)
|
|
||||||
static _attr_ignore = [
|
|
||||||
"id",
|
|
||||||
"obj",
|
|
||||||
"page",
|
|
||||||
"comment",
|
|
||||||
"parentid",
|
|
||||||
"auto_size", # TODO not sure it's still needed in LVGL8
|
|
||||||
]
|
|
||||||
#- mapping from OpenHASP attribute to LVGL attribute -#
|
|
||||||
#- if mapping is null, we use set_X and get_X from our own class -#
|
|
||||||
static _attr_map = {
|
|
||||||
"x": "x",
|
|
||||||
"y": "y",
|
|
||||||
"w": "width",
|
|
||||||
"h": "height",
|
|
||||||
# arc
|
|
||||||
"asjustable": nil,
|
|
||||||
"mode": nil,
|
|
||||||
"start_angle": "bg_start_angle",
|
|
||||||
"start_angle1": "start_angle",
|
|
||||||
"end_angle": "bg_end_angle",
|
|
||||||
"end_angle1": "end_angle",
|
|
||||||
"radius": "style_radius",
|
|
||||||
"border_side": "style_border_side",
|
|
||||||
"bg_opa": "style_bg_opa",
|
|
||||||
"border_width": "style_border_width",
|
|
||||||
"line_width": nil, # depebds on class
|
|
||||||
"line_width1": nil, # depebds on class
|
|
||||||
"action": nil, # store the action in self._action
|
|
||||||
"hidden": nil, # apply to self
|
|
||||||
"enabled": nil, # apply to self
|
|
||||||
"click": nil, # synonym to enabled
|
|
||||||
"toggle": nil,
|
|
||||||
"bg_color": "style_bg_color",
|
|
||||||
"bg_grad_color": "style_bg_grad_color",
|
|
||||||
"type": nil,
|
|
||||||
# below automatically create a sub-label
|
|
||||||
"text": nil, # apply to self
|
|
||||||
"value_str": nil, # synonym to 'text'
|
|
||||||
"align": nil,
|
|
||||||
"text_font": nil,
|
|
||||||
"value_font": nil, # synonym to text_font
|
|
||||||
"text_color": nil,
|
|
||||||
"value_color": nil, # synonym to text_color
|
|
||||||
"value_ofs_x": nil,
|
|
||||||
"value_ofs_y": nil,
|
|
||||||
#
|
|
||||||
"min": nil,
|
|
||||||
"max": nil,
|
|
||||||
"val": "value",
|
|
||||||
"rotation": "rotation",
|
|
||||||
# img
|
|
||||||
"src": "src",
|
|
||||||
"image_recolor": "style_img_recolor",
|
|
||||||
"image_recolor_opa": "style_img_recolor_opa",
|
|
||||||
# spinner
|
|
||||||
"angle": nil,
|
|
||||||
"speed": nil,
|
|
||||||
# padding of knob
|
|
||||||
"pad_top2": nil,
|
|
||||||
"pad_bottom2": nil,
|
|
||||||
"pad_left2": nil,
|
|
||||||
"pad_right2": nil,
|
|
||||||
"pad_all2": nil,
|
|
||||||
"radius2": nil,
|
|
||||||
}
|
|
||||||
|
|
||||||
var _lv_obj # native lvgl object
|
|
||||||
var _lv_label # sub-label if exists
|
|
||||||
var _action # action for OpenHASP
|
|
||||||
|
|
||||||
# init
|
|
||||||
# - create the LVGL encapsulated object
|
|
||||||
# arg1: parent object
|
|
||||||
# arg2: json line object
|
|
||||||
def init(parent, jline)
|
|
||||||
var obj_class = self._lv_class # need to assign to a var to distinguish from method call
|
|
||||||
self._lv_obj = obj_class(parent) # instanciate LVGL object
|
|
||||||
self.post_init()
|
|
||||||
end
|
|
||||||
|
|
||||||
# post-init, to be overriden
|
|
||||||
def post_init()
|
|
||||||
end
|
|
||||||
|
|
||||||
# get LVGL encapsulated object
|
|
||||||
def get_obj()
|
|
||||||
return self._lv_obj
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_action(t)
|
|
||||||
self._action = str(t)
|
|
||||||
end
|
|
||||||
def get_action()
|
|
||||||
return self._action()
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_line_width(t)
|
|
||||||
self._lv_obj.set_style_line_width(int(t), lv.PART_MAIN | lv.STATE_DEFAULT)
|
|
||||||
end
|
|
||||||
def get_line_width()
|
|
||||||
return self._lv_obj.get_style_line_width(lv.PART_MAIN | lv.STATE_DEFAULT)
|
|
||||||
end
|
|
||||||
|
|
||||||
#- ------------------------------------------------------------
|
|
||||||
Mapping of synthetic attributes
|
|
||||||
- text
|
|
||||||
- hidden
|
|
||||||
- enabled
|
|
||||||
- ------------------------------------------------------------ -#
|
|
||||||
#- `hidden` attributes mapped to OBJ_FLAG_HIDDEN -#
|
|
||||||
def set_hidden(h)
|
|
||||||
if h
|
|
||||||
self._lv_obj.add_flag(lv.OBJ_FLAG_HIDDEN)
|
|
||||||
else
|
|
||||||
self._lv_obj.clear_flag(lv.OBJ_FLAG_HIDDEN)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_hidden()
|
|
||||||
return self._lv_obj.has_flag(lv.OBJ_FLAG_HIDDEN)
|
|
||||||
end
|
|
||||||
|
|
||||||
#- `enabled` attributes mapped to OBJ_FLAG_CLICKABLE -#
|
|
||||||
def set_enabled(h)
|
|
||||||
if h
|
|
||||||
self._lv_obj.add_flag(lv.OBJ_FLAG_CLICKABLE)
|
|
||||||
else
|
|
||||||
self._lv_obj.clear_flag(lv.OBJ_FLAG_CLICKABLE)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_enabled()
|
|
||||||
return self._lv_obj.has_flag(lv.OBJ_FLAG_CLICKABLE)
|
|
||||||
end
|
|
||||||
# click is synonym to enabled
|
|
||||||
def set_click(t) self.set_enabled(t) end
|
|
||||||
def get_click() return self.get_enabled() end
|
|
||||||
|
|
||||||
#- `toggle` attributes mapped to STATE_CHECKED -#
|
|
||||||
def set_toggle(t)
|
|
||||||
if t == "TRUE" t = true end
|
|
||||||
if t == "FALSE" t = false end
|
|
||||||
if t
|
|
||||||
self._lv_obj.add_state(lv.STATE_CHECKED)
|
|
||||||
else
|
|
||||||
self._lv_obj.clear_state(lv.STATE_CHECKED)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_toggle()
|
|
||||||
return self._lv_obj.has_state(lv.STATE_CHECKED)
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_adjustable(t)
|
|
||||||
if t
|
|
||||||
self._lv_obj.add_flag(lv.OBJ_FLAG_CLICKABLE)
|
|
||||||
else
|
|
||||||
self._lv_obj.clear_flag(lv.OBJ_FLAG_CLICKABLE)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
def get_adjustable()
|
|
||||||
return self._lv_obj.has_flag(lv.OBJ_FLAG_CLICKABLE)
|
|
||||||
end
|
|
||||||
|
|
||||||
#- set_text: create a `lv_label` sub object to the current object -#
|
|
||||||
#- (default case, may be overriden by object that directly take text) -#
|
|
||||||
def check_label()
|
|
||||||
if self._lv_label == nil
|
|
||||||
self._lv_label = lv.label(self.get_obj())
|
|
||||||
self._lv_label.set_align(lv.ALIGN_CENTER);
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_text(t)
|
|
||||||
self.check_label()
|
|
||||||
self._lv_label.set_text(str(t))
|
|
||||||
end
|
|
||||||
def set_value_str(t) self.set_text(t) end
|
|
||||||
|
|
||||||
def get_text()
|
|
||||||
if self._lv_label == nil return nil end
|
|
||||||
return self._lv_label.get_text()
|
|
||||||
end
|
|
||||||
def get_value_str() return self.get_text() end
|
|
||||||
|
|
||||||
def set_align(t)
|
|
||||||
var align
|
|
||||||
self.check_label()
|
|
||||||
if t == 0 || t == "left"
|
|
||||||
align = lv.TEXT_ALIGN_LEFT
|
|
||||||
elif t == 1 || t == "center"
|
|
||||||
align = lv.TEXT_ALIGN_CENTER
|
|
||||||
elif t == 2 || t == "right"
|
|
||||||
align = lv.TEXT_ALIGN_RIGHT
|
|
||||||
end
|
|
||||||
self._lv_label.set_style_text_align(align, lv.PART_MAIN | lv.STATE_DEFAULT)
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_align()
|
|
||||||
if self._lv_label == nil return nil end
|
|
||||||
var align self._lv_label.get_style_text_align(lv.PART_MAIN | lv.STATE_DEFAULT)
|
|
||||||
if align == lv.TEXT_ALIGN_LEFT
|
|
||||||
return "left"
|
|
||||||
elif align == lv.TEXT_ALIGN_CENTER
|
|
||||||
return "center"
|
|
||||||
elif align == lv.TEXT_ALIGN_RIGHT
|
|
||||||
return "right"
|
|
||||||
else
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_text_font(t)
|
|
||||||
self.check_label()
|
|
||||||
var f = lv.font_robotocondensed_latin1(int(t))
|
|
||||||
if f != nil
|
|
||||||
self._lv_label.set_style_text_font(f, lv.PART_MAIN | lv.STATE_DEFAULT)
|
|
||||||
else
|
|
||||||
print("HSP: Unsupported font size: robotocondensed-latin1", t)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
def get_text_font()
|
|
||||||
end
|
|
||||||
def set_value_font(t) self.set_text_font(t) end
|
|
||||||
def get_value_font() return self.get_text_font() end
|
|
||||||
|
|
||||||
def set_text_color(t)
|
|
||||||
self.check_label()
|
|
||||||
self._lv_label.set_style_text_color(parse_color(t), lv.PART_MAIN | lv.STATE_DEFAULT)
|
|
||||||
end
|
|
||||||
def get_text_color()
|
|
||||||
return self._text_color
|
|
||||||
end
|
|
||||||
def set_value_color(t) self.set_text_color(t) end
|
|
||||||
def get_value_color() return self.get_value_color() end
|
|
||||||
|
|
||||||
def set_value_ofs_x(t)
|
|
||||||
self.check_label()
|
|
||||||
self._lv_label.set_x(int(t))
|
|
||||||
end
|
|
||||||
def get_value_ofs_x()
|
|
||||||
return self._lv_label.get_x()
|
|
||||||
end
|
|
||||||
def set_value_ofs_y(t)
|
|
||||||
self.check_label()
|
|
||||||
self._lv_label.set_y(int(t))
|
|
||||||
end
|
|
||||||
def get_value_ofs_y()
|
|
||||||
return self._lv_label.get_y()
|
|
||||||
end
|
|
||||||
|
|
||||||
# secondary element
|
|
||||||
def set_pad_top2(t)
|
|
||||||
if self._lv_part2_selector != nil
|
|
||||||
self._lv_obj.set_style_pad_top(int(t), self._lv_part2_selector | lv.STATE_DEFAULT)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
def set_pad_bottom2(t)
|
|
||||||
if self._lv_part2_selector != nil
|
|
||||||
self._lv_obj.set_style_pad_bottom(int(t), self._lv_part2_selector | lv.STATE_DEFAULT)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
def set_pad_left2(t)
|
|
||||||
if self._lv_part2_selector != nil
|
|
||||||
self._lv_obj.set_style_pad_left(int(t), self._lv_part2_selector | lv.STATE_DEFAULT)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
def set_pad_right2(t)
|
|
||||||
if self._lv_part2_selector != nil
|
|
||||||
self._lv_obj.set_style_pad_right(int(t), self._lv_part2_selector | lv.STATE_DEFAULT)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
def set_pad_all2(t)
|
|
||||||
if self._lv_part2_selector != nil
|
|
||||||
self._lv_obj.set_style_pad_all(int(t), self._lv_part2_selector | lv.STATE_DEFAULT)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_pad_top()
|
|
||||||
if self._lv_part2_selector != nil
|
|
||||||
return self._lv_obj.get_style_pad_top(self._lv_part2_selector | lv.STATE_DEFAULT)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
def get_pad_bottomo()
|
|
||||||
if self._lv_part2_selector != nil
|
|
||||||
return self._lv_obj.get_style_pad_bottom(self._lv_part2_selector | lv.STATE_DEFAULT)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
def get_pad_left()
|
|
||||||
if self._lv_part2_selector != nil
|
|
||||||
return self._lv_obj.get_style_pad_left(self._lv_part2_selector | lv.STATE_DEFAULT)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
def get_pad_right()
|
|
||||||
if self._lv_part2_selector != nil
|
|
||||||
return self._lv_obj.get_style_pad_right(self._lv_part2_selector | lv.STATE_DEFAULT)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
def get_pad_all()
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_radius2(t)
|
|
||||||
if self._lv_part2_selector != nil
|
|
||||||
self._lv_obj.set_style_radius(int(t), self._lv_part2_selector | lv.STATE_DEFAULT)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
def get_radius2()
|
|
||||||
if self._lv_part2_selector != nil
|
|
||||||
return self._lv_obj.get_style_radius(self._lv_part2_selector | lv.STATE_DEFAULT)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
#- ------------------------------------------------------------
|
|
||||||
Mapping of virtual attributes
|
|
||||||
- ------------------------------------------------------------ -#
|
|
||||||
def member(k)
|
|
||||||
# tostring is a special case, we shouldn't raise an exception for it
|
|
||||||
if k == 'tostring' return nil end
|
|
||||||
#
|
|
||||||
if self._attr_map.has(k)
|
|
||||||
import introspect
|
|
||||||
var kv = self._attr_map[k]
|
|
||||||
if kv
|
|
||||||
var f = introspect.get(self._lv_obj, "get_" + kv)
|
|
||||||
if type(f) == 'function'
|
|
||||||
return f(self._lv_obj)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
# call self method
|
|
||||||
var f = introspect.get(self, "get_" + k)
|
|
||||||
if type(f) == 'function'
|
|
||||||
return f(self, k)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
raise "value_error", "unknown attribute " + str(k)
|
|
||||||
end
|
|
||||||
|
|
||||||
def setmember(k, v)
|
|
||||||
import string
|
|
||||||
# print(">> setmember", k, v)
|
|
||||||
# print(">>", classname(self), self._attr_map)
|
|
||||||
if self._attr_ignore.find(k) != nil
|
|
||||||
return
|
|
||||||
elif self._attr_map.has(k)
|
|
||||||
import introspect
|
|
||||||
var kv = self._attr_map[k]
|
|
||||||
if kv
|
|
||||||
var f = introspect.get(self._lv_obj, "set_" + kv)
|
|
||||||
# if the attribute contains 'color', convert to lv_color
|
|
||||||
if type(kv) == 'string' && is_color_attribute(kv)
|
|
||||||
v = parse_color(v)
|
|
||||||
end
|
|
||||||
# print("f=", f, v, kv, self._lv_obj, self)
|
|
||||||
if type(f) == 'function'
|
|
||||||
if string.find(kv, "style_") == 0
|
|
||||||
# style function need a selector as second parameter
|
|
||||||
f(self._lv_obj, v, lv.PART_MAIN | lv.STATE_DEFAULT)
|
|
||||||
else
|
|
||||||
f(self._lv_obj, v)
|
|
||||||
end
|
|
||||||
return
|
|
||||||
else
|
|
||||||
print("HSP: Could not find function set_"+kv)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
# call self method
|
|
||||||
var f = introspect.get(self, "set_" + k)
|
|
||||||
# print("f==",f)
|
|
||||||
if type(f) == 'function'
|
|
||||||
f(self, v)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
else
|
|
||||||
print("HSP: unknown attribute:", k)
|
|
||||||
end
|
|
||||||
# silently ignore if the attribute name is not supported
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
#- ------------------------------------------------------------
|
|
||||||
Other widgets
|
|
||||||
- ------------------------------------------------------------ -#
|
|
||||||
|
|
||||||
#- ------------------------------------------------------------
|
|
||||||
label
|
|
||||||
#- ------------------------------------------------------------#
|
|
||||||
class lvh_label : lvh_obj
|
|
||||||
static _lv_class = lv.label
|
|
||||||
# label do not need a sub-label
|
|
||||||
def post_init()
|
|
||||||
self._lv_label = self._lv_obj
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
#- ------------------------------------------------------------
|
|
||||||
arc
|
|
||||||
#- ------------------------------------------------------------#
|
|
||||||
class lvh_arc : lvh_obj
|
|
||||||
static _lv_class = lv.arc
|
|
||||||
static _lv_part2_selector = lv.PART_KNOB
|
|
||||||
|
|
||||||
# line_width converts to arc_width
|
|
||||||
def set_line_width(t)
|
|
||||||
self._lv_obj.set_style_arc_width(int(t), lv.PART_MAIN | lv.STATE_DEFAULT)
|
|
||||||
end
|
|
||||||
def get_line_width()
|
|
||||||
return self._lv_obj.get_arc_line_width(lv.PART_MAIN | lv.STATE_DEFAULT)
|
|
||||||
end
|
|
||||||
def set_line_width1(t)
|
|
||||||
self._lv_obj.set_style_arc_width(int(t), lv.PART_INDICATOR | lv.STATE_DEFAULT)
|
|
||||||
end
|
|
||||||
def get_line_width1()
|
|
||||||
return self._lv_obj.get_arc_line_width(lv.PART_INDICATOR | lv.STATE_DEFAULT)
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_min(t)
|
|
||||||
self._lv_obj.set_range(int(t), self.get_max())
|
|
||||||
end
|
|
||||||
def set_max(t)
|
|
||||||
self._lv_obj.set_range(self.get_min(), int(t))
|
|
||||||
end
|
|
||||||
def get_min()
|
|
||||||
return self._lv_obj.get_min_value()
|
|
||||||
end
|
|
||||||
def get_max()
|
|
||||||
return self._lv_obj.get_max_value()
|
|
||||||
end
|
|
||||||
def set_type(t)
|
|
||||||
var mode
|
|
||||||
if t == 0 mode = lv.ARC_MODE_NORMAL
|
|
||||||
elif t == 1 mode = lv.ARC_MODE_REVERSE
|
|
||||||
elif t == 2 mode = lv.ARC_MODE_SYMMETRICAL
|
|
||||||
end
|
|
||||||
if mode != nil
|
|
||||||
self._lv_obj.set_mode(mode)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
def get_type()
|
|
||||||
return self._lv_obj.get_mode()
|
|
||||||
end
|
|
||||||
# mode
|
|
||||||
def set_mode(t)
|
|
||||||
var mode
|
|
||||||
if mode == "expand" self._lv_obj.set_width(lv.SIZE_CONTENT)
|
|
||||||
elif mode == "break" mode = lv.LABEL_LONG_WRAP
|
|
||||||
elif mode == "dots" mode = lv.LABEL_LONG_DOT
|
|
||||||
elif mode == "scroll" mode = lv.LABEL_LONG_SCROLL
|
|
||||||
elif mode == "loop" mode = lv.LABEL_LONG_SCROLL_CIRCULAR
|
|
||||||
elif mode == "crop" mode = lv.LABEL_LONG_CLIP
|
|
||||||
end
|
|
||||||
if mode != nil
|
|
||||||
self._lv_obj.lv_label_set_long_mode(mode)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
def get_mode()
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
#- ------------------------------------------------------------
|
|
||||||
switch
|
|
||||||
#- ------------------------------------------------------------#
|
|
||||||
class lvh_switch : lvh_obj
|
|
||||||
static _lv_class = lv.switch
|
|
||||||
static _lv_part2_selector = lv.PART_KNOB
|
|
||||||
end
|
|
||||||
|
|
||||||
#- ------------------------------------------------------------
|
|
||||||
spinner
|
|
||||||
#- ------------------------------------------------------------#
|
|
||||||
class lvh_spinner : lvh_arc
|
|
||||||
static _lv_class = lv.spinner
|
|
||||||
|
|
||||||
# init
|
|
||||||
# - create the LVGL encapsulated object
|
|
||||||
# arg1: parent object
|
|
||||||
# arg2: json line object
|
|
||||||
def init(parent, jline)
|
|
||||||
var angle = jline.find("angle", 60)
|
|
||||||
var speed = jline.find("speed", 1000)
|
|
||||||
self._lv_obj = lv.spinner(parent, speed, angle)
|
|
||||||
self.post_init()
|
|
||||||
end
|
|
||||||
|
|
||||||
# ignore attributes, spinner can't be changed once created
|
|
||||||
def set_angle(t) end
|
|
||||||
def get_angle() end
|
|
||||||
def set_speed(t) end
|
|
||||||
def get_speed() end
|
|
||||||
end
|
|
||||||
|
|
||||||
#- creat sub-classes of lvh_obj and map the LVGL class in static '_lv_class' attribute -#
|
|
||||||
class lvh_bar : lvh_obj static _lv_class = lv.bar end
|
|
||||||
class lvh_btn : lvh_obj static _lv_class = lv.btn end
|
|
||||||
class lvh_btnmatrix : lvh_obj static _lv_class = lv.btnmatrix end
|
|
||||||
class lvh_checkbox : lvh_obj static _lv_class = lv.checkbox end
|
|
||||||
class lvh_dropdown : lvh_obj static _lv_class = lv.dropdown end
|
|
||||||
class lvh_img : lvh_obj static _lv_class = lv.img end
|
|
||||||
class lvh_line : lvh_obj static _lv_class = lv.line end
|
|
||||||
class lvh_roller : lvh_obj static _lv_class = lv.roller end
|
|
||||||
class lvh_slider : lvh_obj static _lv_class = lv.slider end
|
|
||||||
class lvh_textarea : lvh_obj static _lv_class = lv.textarea end
|
|
||||||
|
|
||||||
#- ----------------------------------------------------------------------------
|
|
||||||
Class `lvh_page` encapsulating `lv_obj` as screen (created with lv.obj(0))
|
|
||||||
- ----------------------------------------------------------------------------- -#
|
|
||||||
# ex of transition: lv.scr_load_anim(scr, lv.SCR_LOAD_ANIM_MOVE_RIGHT, 500, 0, false)
|
|
||||||
class lvh_page
|
|
||||||
var _obj_id # (map) of objects by id numbers
|
|
||||||
var _page_id # (int) id number of the page
|
|
||||||
var _lv_scr # (lv_obj) lvgl screen object
|
|
||||||
|
|
||||||
#- init(page_number) -#
|
|
||||||
def init(page_number)
|
|
||||||
import global
|
|
||||||
|
|
||||||
# if no parameter, default to page #1
|
|
||||||
if page_number == nil page_number = 1 end
|
|
||||||
|
|
||||||
self._page_id = page_number # remember our page_number
|
|
||||||
self._obj_id = {} # init list of objects
|
|
||||||
if page_number == 1
|
|
||||||
self._lv_scr = lv.scr_act() # default screen
|
|
||||||
elif page_number == 0
|
|
||||||
self._lv_scr = lv.layer_top() # top layer, visible over all screens
|
|
||||||
else
|
|
||||||
self._lv_scr = lv.obj(0) # allocate a new screen
|
|
||||||
# self._lv_scr.set_style_bg_color(lv.color(0x000000), lv.PART_MAIN | lv.STATE_DEFAULT) # set black background
|
|
||||||
self._lv_scr.set_style_bg_color(lv.color(0xFFFFFF), lv.PART_MAIN | lv.STATE_DEFAULT) # set white background
|
|
||||||
end
|
|
||||||
|
|
||||||
# create a global for this page of form p<page_number>, ex p1
|
|
||||||
var glob_name = string.format("p%i", self._page_id)
|
|
||||||
global.(glob_name) = self
|
|
||||||
end
|
|
||||||
|
|
||||||
#- retrieve lvgl screen object for this page -#
|
|
||||||
def get_scr()
|
|
||||||
return self._lv_scr
|
|
||||||
end
|
|
||||||
|
|
||||||
#- add an object to this page -#
|
|
||||||
def set_obj(id, o)
|
|
||||||
self._obj_id[id] = o
|
|
||||||
end
|
|
||||||
def get_obj(id)
|
|
||||||
return self._obj_id.find(id)
|
|
||||||
end
|
|
||||||
|
|
||||||
#- return id of this page -#
|
|
||||||
def id()
|
|
||||||
return self._page_id
|
|
||||||
end
|
|
||||||
|
|
||||||
#- show this page, with animation -#
|
|
||||||
def show(anim, duration)
|
|
||||||
# ignore if there is no screen, like for id 0
|
|
||||||
if self._lv_scr == nil return nil end
|
|
||||||
# ignore if the screen is already active
|
|
||||||
if self._lv_scr._p == lv.scr_act()._p return end # do nothing
|
|
||||||
|
|
||||||
# default animation is lv.SCR_LOAD_ANIM_MOVE_RIGHT
|
|
||||||
if anim == nil anim = lv.SCR_LOAD_ANIM_MOVE_RIGHT end
|
|
||||||
# default duration of 500ms
|
|
||||||
if duration == nil duration = 500 end
|
|
||||||
|
|
||||||
# load new screen with anumation, no delay, 500ms transition time, no auto-delete
|
|
||||||
lv.scr_load_anim(self._lv_scr, lv.SCR_LOAD_ANIM_MOVE_RIGHT, duration, 0, false)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
#- pages -#
|
|
||||||
var lvh_page_cur = lvh_page(1)
|
|
||||||
var lvh_pages = { 1: lvh_page_cur } # always create page #1
|
|
||||||
|
|
||||||
f = open("pages.jsonl","r")
|
|
||||||
var jsonl = string.split(f.read(), "\n")
|
|
||||||
f.close()
|
|
||||||
|
|
||||||
#- ------------------------------------------------------------
|
|
||||||
Parse page information
|
|
||||||
|
|
||||||
Create a new page object if required
|
|
||||||
Change the active page
|
|
||||||
- ------------------------------------------------------------ -#
|
|
||||||
def parse_page(jline)
|
|
||||||
if jline.has("page") && type(jline["page"]) == 'int'
|
|
||||||
var page = int(jline["page"])
|
|
||||||
# does the page already exist?
|
|
||||||
if lvh_pages.has(page)
|
|
||||||
# yes, just change the current page
|
|
||||||
lvh_page_cur = lvh_pages[page]
|
|
||||||
else
|
|
||||||
# no, create a new page
|
|
||||||
lvh_page_cur = lvh_page(page)
|
|
||||||
lvh_pages[page] = lvh_page_cur
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
#- ------------------------------------------------------------
|
|
||||||
Parse single object
|
|
||||||
|
|
||||||
- ------------------------------------------------------------ -#
|
|
||||||
def parse_obj(jline, page)
|
|
||||||
import global
|
|
||||||
import introspect
|
|
||||||
|
|
||||||
# line must contain 'obj' and 'id', otherwise it is ignored
|
|
||||||
if jline.has("obj") && jline.has("id") && type(jline["id"]) == 'int'
|
|
||||||
# 'obj_id' must be between 1 and 254
|
|
||||||
var obj_id = int(jline["id"])
|
|
||||||
if obj_id < 1 || obj_id > 254
|
|
||||||
raise "value error", "invalid id " + str(obj_id)
|
|
||||||
end
|
|
||||||
|
|
||||||
# extract openhasp class, prefix with `lvh_`. Ex: `btn` becomes `lvh_btn`
|
|
||||||
var obj_type = jline["obj"]
|
|
||||||
|
|
||||||
# extract parent
|
|
||||||
var parent
|
|
||||||
var parent_id = int(jline.find("parentid"))
|
|
||||||
if parent_id != nil
|
|
||||||
var parent_obj = lvh_page_cur.get_obj(parent_id)
|
|
||||||
if parent_obj != nil
|
|
||||||
parent = parent_obj._lv_obj
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if parent == nil
|
|
||||||
parent = page.get_scr()
|
|
||||||
end
|
|
||||||
|
|
||||||
# check if a class with the requested name exists
|
|
||||||
var obj_class = introspect.get(global, "lvh_" + obj_type)
|
|
||||||
if obj_class == nil
|
|
||||||
raise "value error", "cannot find object of type " + str(obj_type)
|
|
||||||
end
|
|
||||||
|
|
||||||
# instanciate the object, passing the lvgl screen as paren object
|
|
||||||
var obj = obj_class(parent, jline)
|
|
||||||
|
|
||||||
# add object to page object
|
|
||||||
lvh_page_cur.set_obj(obj_id, obj)
|
|
||||||
# set attributes
|
|
||||||
# try every attribute, if not supported it is silently ignored
|
|
||||||
for k:jline.keys()
|
|
||||||
# introspect.set(obj, k, jline[k])
|
|
||||||
obj.(k) = jline[k]
|
|
||||||
end
|
|
||||||
|
|
||||||
# create a global variable for this object of form p<page>b<id>, ex p1b2
|
|
||||||
var glob_name = string.format("p%ib%i", lvh_page_cur.id(), obj_id)
|
|
||||||
global.(glob_name) = obj
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# ex:
|
|
||||||
# {'page': 1, 'h': 50, 'obj': 'label', 'hidden': false, 'text': 'Hello', 'x': 5, 'id': 1, 'enabled': true, 'y': 5, 'w': 50}
|
|
||||||
# {"page":1,"id":2,"obj":"btn","x":5,"y":90,"h":90,"w":50,"text":"World","enabled":false,"hidden":false}
|
|
||||||
|
|
||||||
#- ------------------------------------------------------------
|
|
||||||
Parse jsonl file line by line
|
|
||||||
|
|
||||||
- ------------------------------------------------------------ -#
|
|
||||||
tasmota.yield()
|
|
||||||
for j:jsonl
|
|
||||||
var jline = json.load(j)
|
|
||||||
|
|
||||||
# parse page first
|
|
||||||
if type(jline) == 'instance'
|
|
||||||
parse_page(jline)
|
|
||||||
parse_obj(jline, lvh_page_cur)
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,117 +0,0 @@
|
||||||
# lv_tasmota_log class
|
|
||||||
|
|
||||||
class lv_tasmota_log_roboto : lv.obj
|
|
||||||
var label # contains the sub lv_label object
|
|
||||||
var lines
|
|
||||||
var line_len
|
|
||||||
var log_reader
|
|
||||||
var log_level
|
|
||||||
|
|
||||||
def init(parent)
|
|
||||||
super(self).init(parent)
|
|
||||||
self.set_width(parent.get_width())
|
|
||||||
self.set_pos(0, 0)
|
|
||||||
|
|
||||||
self.set_style_bg_color(lv.color(0x000000), lv.PART_MAIN | lv.STATE_DEFAULT)
|
|
||||||
self.set_style_bg_opa(255, lv.PART_MAIN | lv.STATE_DEFAULT)
|
|
||||||
self.move_background()
|
|
||||||
self.set_style_border_opa(255, lv.PART_MAIN | lv.STATE_DEFAULT)
|
|
||||||
self.set_style_radius(0, lv.PART_MAIN | lv.STATE_DEFAULT)
|
|
||||||
self.set_style_pad_all(2, lv.PART_MAIN | lv.STATE_DEFAULT)
|
|
||||||
self.set_style_border_color(lv.color(0x0099EE), lv.PART_MAIN | lv.STATE_DEFAULT)
|
|
||||||
self.set_style_border_width(1, lv.PART_MAIN | lv.STATE_DEFAULT)
|
|
||||||
self.refr_size()
|
|
||||||
self.refr_pos()
|
|
||||||
|
|
||||||
self.label = lv.label(self)
|
|
||||||
self.label.set_width(self.get_width() - 12)
|
|
||||||
|
|
||||||
self.label.set_style_text_color(lv.color(0x00FF00), lv.PART_MAIN | lv.STATE_DEFAULT)
|
|
||||||
self.label.set_long_mode(lv.LABEL_LONG_CLIP)
|
|
||||||
var roboto12 = lv.font_robotocondensed_latin1(12)
|
|
||||||
self.label.set_style_text_font(roboto12, lv.PART_MAIN | lv.STATE_DEFAULT)
|
|
||||||
# var lg_font = lv.font_montserrat(10)
|
|
||||||
# self.set_style_text_font(lg_font, lv.PART_MAIN | lv.STATE_DEFAULT)
|
|
||||||
self.label.set_text("") # bug, still displays "Text"
|
|
||||||
|
|
||||||
self.add_event_cb( / obj, evt -> self.size_changed_cb(obj, evt), lv.EVENT_SIZE_CHANGED | lv.EVENT_STYLE_CHANGED | lv.EVENT_DELETE, 0)
|
|
||||||
|
|
||||||
self.lines = []
|
|
||||||
self.line_len = 0
|
|
||||||
self.log_reader = tasmota_log_reader()
|
|
||||||
self.log_level = 2
|
|
||||||
self._size_changed()
|
|
||||||
|
|
||||||
tasmota.add_driver(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_lines_count(line_len)
|
|
||||||
if line_len > self.line_len # increase lines
|
|
||||||
for i: self.line_len .. line_len-1
|
|
||||||
self.lines.insert(0, "")
|
|
||||||
end
|
|
||||||
elif line_len < self.line_len # decrease lines
|
|
||||||
for i: line_len .. self.line_len-1
|
|
||||||
self.lines.remove(0)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
self.line_len = line_len
|
|
||||||
end
|
|
||||||
|
|
||||||
def _size_changed()
|
|
||||||
# print(">>> lv.EVENT_SIZE_CHANGED")
|
|
||||||
var pad_hor = self.get_style_pad_left(lv.PART_MAIN | lv.STATE_DEFAULT)
|
|
||||||
+ self.get_style_pad_right(lv.PART_MAIN | lv.STATE_DEFAULT)
|
|
||||||
+ self.get_style_border_width(lv.PART_MAIN | lv.STATE_DEFAULT) * 2
|
|
||||||
+ 3
|
|
||||||
var pad_ver = self.get_style_pad_top(lv.PART_MAIN | lv.STATE_DEFAULT)
|
|
||||||
+ self.get_style_pad_bottom(lv.PART_MAIN | lv.STATE_DEFAULT)
|
|
||||||
+ self.get_style_border_width(lv.PART_MAIN | lv.STATE_DEFAULT) * 2
|
|
||||||
+ 3
|
|
||||||
var w = self.get_width() - pad_hor
|
|
||||||
var h = self.get_height() - pad_ver
|
|
||||||
self.label.set_size(w, h)
|
|
||||||
|
|
||||||
# compute how many lines should be displayed
|
|
||||||
var h_font = lv.font_get_line_height(self.label.get_style_text_font(0)) # current font's height
|
|
||||||
var lines_count = ((h * 2 / h_font) + 1 ) / 2
|
|
||||||
# print("h_font",h_font,"h",h,"lines_count",lines_count)
|
|
||||||
self.set_lines_count(lines_count)
|
|
||||||
end
|
|
||||||
|
|
||||||
def size_changed_cb(obj, event)
|
|
||||||
var code = event.code
|
|
||||||
if code == lv.EVENT_SIZE_CHANGED || code == lv.EVENT_STYLE_CHANGED
|
|
||||||
self._size_changed()
|
|
||||||
elif code == lv.EVENT_DELETE
|
|
||||||
tasmota.remove_driver(self)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def every_second()
|
|
||||||
var dirty = false
|
|
||||||
for n:0..20
|
|
||||||
var line = self.log_reader.get_log(self.log_level)
|
|
||||||
if line == nil break end # no more logs
|
|
||||||
self.lines.remove(0) # remove first line
|
|
||||||
self.lines.push(line)
|
|
||||||
dirty = true
|
|
||||||
end
|
|
||||||
if dirty self.update() end
|
|
||||||
end
|
|
||||||
|
|
||||||
def update()
|
|
||||||
var msg = self.lines.concat("\n")
|
|
||||||
self.label.set_text(msg)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return lv_tasmota_log_roboto
|
|
||||||
|
|
||||||
# import lv_tasmota_log
|
|
||||||
# var lg = lv_tasmota_log(scr, 6)
|
|
||||||
# lg.set_size(hres, 95)
|
|
||||||
# lg.set_pos(0, stat_line.get_height() + 40)
|
|
||||||
# tasmota.add_driver(lg)
|
|
||||||
|
|
||||||
# var roboto12 = lv.font_robotocondensed_latin1(12) lg.set_style_text_font(roboto12, lv.PART_MAIN | lv.STATE_DEFAULT)
|
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,33 @@
|
||||||
|
{"page":0,"comment":"---------- Upper stat line ----------"}
|
||||||
|
{"id":0,"text_color":"#FFFFFF"}
|
||||||
|
{"id":11,"obj":"label","x":0,"y":0,"w":320,"pad_right":90,"h":22,"bg_color":"#D00000","bg_opa":255,"radius":0,"border_side":0,"text":"Tasmota","text_font":"montserrat-20"}
|
||||||
|
|
||||||
|
{"id":15,"obj":"lv_wifi_arcs","x":291,"y":0,"w":29,"h":22,"radius":0,"border_side":0,"bg_color":"#000000","line_color":"#FFFFFF"}
|
||||||
|
{"id":16,"obj":"lv_clock","x":232,"y":3,"w":55,"h":16,"radius":0,"border_side":0}
|
||||||
|
|
||||||
|
{"comment":"---------- Bottom buttons - prev/home/next ----------"}
|
||||||
|
{"id":101,"obj":"btn","x":20,"y":210,"w":80,"h":25,"action":"prev","bg_color":"#1fa3ec","radius":10,"border_side":1,"text":"\uF053","text_font":"montserrat-20"}
|
||||||
|
{"id":102,"obj":"btn","x":120,"y":210,"w":80,"h":25,"action":"back","bg_color":"#1fa3ec","radius":10,"border_side":1,"text":"\uF015","text_font":"montserrat-20"}
|
||||||
|
{"id":103,"obj":"btn","x":220,"y":210,"w":80,"h":25,"action":"next","bg_color":"#1fa3ec","radius":10,"border_side":1,"text":"\uF054","text_font":"montserrat-20"}
|
||||||
|
|
||||||
|
{"page":2,"comment":"---------- Page 2 ----------"}
|
||||||
|
{"id":0,"bg_color":"#0000A0","bg_grad_color":"#000000","bg_grad_dir":1,"text_color":"#FFFFFF"}
|
||||||
|
|
||||||
|
{"comment":"---------- Wifi status ----------"}
|
||||||
|
{"id":20,"obj":"lv_wifi_graph","x":257,"y":25,"w":60,"h":40,"radius":0}
|
||||||
|
{"id":21,"obj":"lv_tasmota_info","x":3,"y":25,"w":251,"h":40,"radius":0}
|
||||||
|
{"id":22,"obj":"lv_tasmota_log","x":3,"y":68,"w":314,"h":90,"radius":0,"text_font":12}
|
||||||
|
|
||||||
|
{"page":1,"comment":"---------- Page 1 ----------"}
|
||||||
|
{"id":0,"bg_color":"#0000A0","bg_grad_color":"#000000","bg_grad_dir":1,"text_color":"#FFFFFF"}
|
||||||
|
|
||||||
|
{"id":2,"obj":"arc","x":20,"y":65,"w":80,"h":100,"border_side":0,"type":0,"rotation":0,"start_angle":180,"end_angle":0,"start_angle1":180,"value_font":12,"value_ofs_x":0,"value_ofs_y":-14,"bg_opa":0,"text":"--.-°C","min":200,"max":800,"val":0,"val_rule":"ESP32#Temperature","val_rule_formula":"val * 10","text_rule":"ESP32#Temperature","text_rule_format":"%2.1f °C"}
|
||||||
|
|
||||||
|
{"id":5,"obj":"label","x":2,"y":35,"w":120,"text":"Temperature","align":1}
|
||||||
|
|
||||||
|
{"id":10,"obj":"label","x":172,"y":35,"w":140,"text":"MPU","align":0}
|
||||||
|
{"id":11,"obj":"label","x":172,"y":55,"w":140,"text":"x=","align":0,"text_rule":"MPU9250#AX","text_rule_format":"x=%6.3f","text_rule_formula":"val / 1000"}
|
||||||
|
{"id":12,"obj":"label","x":172,"y":75,"w":140,"text":"y=","align":0,"text_rule":"MPU9250#AY","text_rule_format":"y=%6.3f","text_rule_formula":"val / 1000"}
|
||||||
|
{"id":13,"obj":"label","x":172,"y":95,"w":140,"text":"z=","align":0,"text_rule":"MPU9250#AZ","text_rule_format":"z=%6.3f","text_rule_formula":"val / 1000"}
|
||||||
|
|
||||||
|
{"comment":"--- Trigger sensors every 2 seconds ---","berry_run":"tasmota.add_cron('*/2 * * * * *', def () tasmota.publish_rule(tasmota.read_sensors()) end, 'oh_every_5_s')"}
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,7 @@
|
||||||
|
# start openhasp
|
||||||
|
# package with
|
||||||
|
# zip -j -0 openhasp.tapp openhasp_core/*
|
||||||
|
import openhasp
|
||||||
|
|
||||||
|
# defer start to make sure all modules and tapp applications were loaded first
|
||||||
|
tasmota.set_timer(1000, /-> openhasp.start())
|
File diff suppressed because it is too large
Load Diff
|
@ -1,5 +1,6 @@
|
||||||
{"page":1,"comment":"---------- Page 1 ----------"}
|
{"page":1,"comment":"---------- Page 1 ----------"}
|
||||||
{"page":1,"id":0,"bg_color":"#FFFFFF","bg_grad_color":"#FFFFFF","text_color":"#000000","radius":0,"border_side":0}
|
{"page":1,"id":0,"bg_color":"#FFFFFF","bg_grad_color":"#FFFFFF","text_color":"#000000","radius":0,"border_side":0}
|
||||||
|
|
||||||
{"page":1,"id":1,"obj":"btn","x":0,"y":0,"w":240,"h":30,"text":"LIVING ROOM","value_font":22,"bg_color":"#2C3E50","bg_grad_color":"#2C3E50","text_color":"#FFFFFF","radius":0,"border_side":0}
|
{"page":1,"id":1,"obj":"btn","x":0,"y":0,"w":240,"h":30,"text":"LIVING ROOM","value_font":22,"bg_color":"#2C3E50","bg_grad_color":"#2C3E50","text_color":"#FFFFFF","radius":0,"border_side":0}
|
||||||
|
|
||||||
{"page":1,"id":2,"obj":"arc","x":20,"y":65,"w":80,"h":100,"max":40,"border_side":0,"type":0,"rotation":0,"start_angle":180,"end_angle":0,"start_angle1":180,"value_font":12,"value_ofs_x":0,"value_ofs_y":-14,"bg_opa":0,"text":"21.2°C","min":-20,"max":50,"val":21}
|
{"page":1,"id":2,"obj":"arc","x":20,"y":65,"w":80,"h":100,"max":40,"border_side":0,"type":0,"rotation":0,"start_angle":180,"end_angle":0,"start_angle1":180,"value_font":12,"value_ofs_x":0,"value_ofs_y":-14,"bg_opa":0,"text":"21.2°C","min":-20,"max":50,"val":21}
|
|
@ -0,0 +1,44 @@
|
||||||
|
{"page":0,"comment":"---------- Upper stat line ----------"}
|
||||||
|
{"id":0,"text_color":"#FFFFFF"}
|
||||||
|
{"id":11,"obj":"label","x":0,"y":0,"w":320,"pad_right":90,"h":22,"bg_color":"#D00000","bg_opa":255,"radius":0,"border_side":0,"text":"Tasmota","text_font":"montserrat-20"}
|
||||||
|
|
||||||
|
{"id":15,"obj":"lv_wifi_arcs","x":291,"y":0,"w":29,"h":22,"radius":0,"border_side":0,"bg_color":"#000000","line_color":"#FFFFFF"}
|
||||||
|
{"id":16,"obj":"lv_clock","x":232,"y":3,"w":55,"h":16,"radius":0,"border_side":0}
|
||||||
|
|
||||||
|
{"comment":"---------- Bottom buttons - prev/home/next ----------"}
|
||||||
|
{"id":101,"obj":"btn","x":20,"y":210,"w":80,"h":25,"action":"prev","bg_color":"#1fa3ec","radius":10,"border_side":1,"text":"\uF053","text_font":"montserrat-20"}
|
||||||
|
{"id":102,"obj":"btn","x":120,"y":210,"w":80,"h":25,"action":"back","bg_color":"#1fa3ec","radius":10,"border_side":1,"text":"\uF015","text_font":"montserrat-20"}
|
||||||
|
{"id":103,"obj":"btn","x":220,"y":210,"w":80,"h":25,"action":"next","bg_color":"#1fa3ec","radius":10,"border_side":1,"text":"\uF054","text_font":"montserrat-20"}
|
||||||
|
|
||||||
|
{"page":2,"comment":"---------- Page 2 ----------"}
|
||||||
|
{"id":0,"bg_color":"#0000A0","bg_grad_color":"#000000","bg_grad_dir":1,"text_color":"#FFFFFF"}
|
||||||
|
|
||||||
|
{"comment":"---------- Wifi status ----------"}
|
||||||
|
{"id":20,"obj":"lv_wifi_graph","x":257,"y":25,"w":60,"h":40,"radius":0}
|
||||||
|
{"id":21,"obj":"lv_tasmota_info","x":3,"y":25,"w":251,"h":40,"radius":0}
|
||||||
|
{"id":22,"obj":"lv_tasmota_log","x":3,"y":68,"w":314,"h":90,"radius":0,"text_font":12}
|
||||||
|
|
||||||
|
{"page":1,"comment":"---------- Page 1 ----------"}
|
||||||
|
{"id":0,"bg_color":"#0000A0","bg_grad_color":"#000000","bg_grad_dir":1,"text_color":"#FFFFFF"}
|
||||||
|
|
||||||
|
{"id":2,"obj":"arc","x":20,"y":65,"w":80,"h":100,"border_side":0,"type":0,"rotation":0,"start_angle":180,"end_angle":0,"start_angle1":180,"value_font":12,"value_ofs_x":0,"value_ofs_y":-14,"bg_opa":0,"text":"--.-°C","min":200,"max":800,"val":0,"val_rule":"ESP32#Temperature","val_rule_formula":"val * 10","text_rule":"ESP32#Temperature","text_rule_format":"%2.1f °C"}
|
||||||
|
|
||||||
|
{"id":5,"obj":"label","x":2,"y":35,"w":140,"text":"Temperature","align":1}
|
||||||
|
|
||||||
|
{"id":10,"obj":"label","x":172,"y":35,"w":140,"text":"MPU","align":0}
|
||||||
|
{"id":11,"obj":"label","x":172,"y":55,"w":140,"text":"x=","align":0,"text_rule":"MPU9250#AX","text_rule_format":"x=%6.3f","text_rule_formula":"val / 1000"}
|
||||||
|
{"id":12,"obj":"label","x":172,"y":75,"w":140,"text":"y=","align":0,"text_rule":"MPU9250#AY","text_rule_format":"y=%6.3f","text_rule_formula":"val / 1000"}
|
||||||
|
{"id":13,"obj":"label","x":172,"y":95,"w":140,"text":"z=","align":0,"text_rule":"MPU9250#AZ","text_rule_format":"z=%6.3f","text_rule_formula":"val / 1000"}
|
||||||
|
|
||||||
|
|
||||||
|
{"page":3,"comment":"---------- Page 3 ----------"}
|
||||||
|
{"page":3,"id":1,"obj":"btn","x":0,"y":20,"w":240,"h":30,"text":"PAGE 3","text_font":16,"bg_color":"#2C3E50","text_color":"#FFFFFF","radius":0,"border_side":0,"click":0}
|
||||||
|
|
||||||
|
{"page":3,"id":11,"obj":"img","src":"A:/noun_Fan_35097_140.png","auto_size":1,"w":140,"h":140,"x":50,"y":75,"image_recolor":"lime","image_recolor_opa":150}
|
||||||
|
{"page":3,"id":12,"obj":"spinner","parentid":11,"x":7,"y":6,"w":126,"h":126,"bg_opa":0,"border_width":0,"line_width":7,"line_width1":7,"type":2,"angle":120,"speed":1000,"value_str":3,"value_font":24}
|
||||||
|
|
||||||
|
{"page":4,"comment":"---------- Page 4 ----------"}
|
||||||
|
{"page":4,"id":1,"obj":"btn","x":0,"y":20,"w":240,"h":30,"text":"PAGE 4","value_font":24,"bg_color":"#2C3E50","text_color":"#FFFFFF","radius":0,"border_side":0,"click":0}
|
||||||
|
{"page":4,"id":2,"obj":"obj","x":5,"y":35,"w":230,"h":250,"click":0}
|
||||||
|
|
||||||
|
{"comment":"--- Trigger sensors every 2 seconds ---","berry_run":"tasmota.add_cron('*/2 * * * * *', def () tasmota.publish_rule(tasmota.read_sensors()) end, 'oh_every_5_s')"}
|
Binary file not shown.
|
@ -0,0 +1,6 @@
|
||||||
|
# pre-load widgets so future `import` will be already in memory
|
||||||
|
# create tapp file with:
|
||||||
|
# rm rm openhasp_widgets.tapp; zip -j -0 openhasp_widgets.tapp openhasp_widgets/*
|
||||||
|
import lv_tasmota_log
|
||||||
|
import lv_tasmota_info
|
||||||
|
import lv_wifi_graph
|
|
@ -24,14 +24,18 @@ class lv_tasmota_log : lv.obj
|
||||||
self.refr_pos()
|
self.refr_pos()
|
||||||
|
|
||||||
self.label = lv.label(self)
|
self.label = lv.label(self)
|
||||||
self.label.set_width(self.get_width() - 12)
|
|
||||||
|
|
||||||
self.label.set_style_text_color(lv.color(0x00FF00), lv.PART_MAIN | lv.STATE_DEFAULT)
|
self.label.set_style_text_color(lv.color(0x00FF00), lv.PART_MAIN | lv.STATE_DEFAULT)
|
||||||
self.label.set_long_mode(lv.LABEL_LONG_CLIP)
|
self.label.set_long_mode(lv.LABEL_LONG_CLIP)
|
||||||
self.label.set_text("") # bug, still displays "Text"
|
self.label.set_text("") # bug, still displays "Text"
|
||||||
|
|
||||||
self.add_event_cb( / obj, evt -> self.size_changed_cb(obj, evt), lv.EVENT_SIZE_CHANGED | lv.EVENT_STYLE_CHANGED | lv.EVENT_DELETE, 0)
|
self.label.set_width(self.get_width() - 12)
|
||||||
|
self.label.set_height(self.get_height() - 6)
|
||||||
|
|
||||||
|
self.add_event_cb( / -> self._size_changed(), lv.EVENT_SIZE_CHANGED, 0)
|
||||||
|
self.add_event_cb( / -> self._size_changed(), lv.EVENT_STYLE_CHANGED, 0)
|
||||||
|
self.add_event_cb( / -> tasmota.remove_driver(self), lv.EVENT_DELETE, 0)
|
||||||
|
|
||||||
self.lines = []
|
self.lines = []
|
||||||
self.line_len = 0
|
self.line_len = 0
|
||||||
self.log_reader = tasmota_log_reader()
|
self.log_reader = tasmota_log_reader()
|
||||||
|
@ -54,7 +58,7 @@ class lv_tasmota_log : lv.obj
|
||||||
self.line_len = line_len
|
self.line_len = line_len
|
||||||
end
|
end
|
||||||
|
|
||||||
def _size_changed()
|
def _size_changed(obj, evt)
|
||||||
# print(">>> lv.EVENT_SIZE_CHANGED")
|
# print(">>> lv.EVENT_SIZE_CHANGED")
|
||||||
var pad_hor = self.get_style_pad_left(lv.PART_MAIN | lv.STATE_DEFAULT)
|
var pad_hor = self.get_style_pad_left(lv.PART_MAIN | lv.STATE_DEFAULT)
|
||||||
+ self.get_style_pad_right(lv.PART_MAIN | lv.STATE_DEFAULT)
|
+ self.get_style_pad_right(lv.PART_MAIN | lv.STATE_DEFAULT)
|
||||||
|
@ -64,7 +68,7 @@ class lv_tasmota_log : lv.obj
|
||||||
+ self.get_style_pad_bottom(lv.PART_MAIN | lv.STATE_DEFAULT)
|
+ self.get_style_pad_bottom(lv.PART_MAIN | lv.STATE_DEFAULT)
|
||||||
+ self.get_style_border_width(lv.PART_MAIN | lv.STATE_DEFAULT) * 2
|
+ self.get_style_border_width(lv.PART_MAIN | lv.STATE_DEFAULT) * 2
|
||||||
+ 3
|
+ 3
|
||||||
var w = self.get_width() - pad_hor
|
var w = self.get_width() - pad_hor - 2
|
||||||
var h = self.get_height() - pad_ver
|
var h = self.get_height() - pad_ver
|
||||||
self.label.set_size(w, h)
|
self.label.set_size(w, h)
|
||||||
# print("w",w,"h",h,"pad_hor",pad_hor,"pad_ver",pad_ver)
|
# print("w",w,"h",h,"pad_hor",pad_hor,"pad_ver",pad_ver)
|
||||||
|
@ -76,15 +80,6 @@ class lv_tasmota_log : lv.obj
|
||||||
self.set_lines_count(lines_count)
|
self.set_lines_count(lines_count)
|
||||||
end
|
end
|
||||||
|
|
||||||
def size_changed_cb(obj, event)
|
|
||||||
var code = event.code
|
|
||||||
if code == lv.EVENT_SIZE_CHANGED || code == lv.EVENT_STYLE_CHANGED
|
|
||||||
self._size_changed()
|
|
||||||
elif code == lv.EVENT_DELETE
|
|
||||||
tasmota.remove_driver(self)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def every_second()
|
def every_second()
|
||||||
var dirty = false
|
var dirty = false
|
||||||
for n:0..20
|
for n:0..20
|
Loading…
Reference in New Issue