/*
xdrv_52_3_berry_embedded.ino - Berry scripting language, embedded code
Copyright (C) 2021 Stephan Hadinger, Berry language by Guan Wenliang https://github.com/Skiars/berry
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#ifdef USE_BERRY
/*********************************************************************************************\
* Handlers for Berry calls and async
*
\*********************************************************************************************/
const char berry_prog[] =
""
// create a 'ntv' module to allow functions to be registered in a safe namespace
// "ntv = module('ntv') "
// auto-import modules
// // import alias
#ifdef USE_ENERGY_SENSOR
"import energy "
#endif
// Phase 1
// "class Tasmota: Tasmota_ntv "
// for now the variables are built, need to find a way to push in Flash
// "def init() "
// "end "
// // add `chars_in_string(s:string,c:string) -> int``
// // looks for any char in c, and return the position of the first char
// // or -1 if not found
// // inv is optional and inverses the behavior, i.e. look for chars not in the list
// "def chars_in_string(s,c,inv) "
// "var inverted = inv ? true : false "
// "for i:0..size(s)-1 "
// "var found = false "
// "for j:0..size(c)-1 "
// "if s[i] == c[j] found = true end "
// "end "
// "if inverted != found return i end "
// "end "
// "return -1 "
// "end "
// // find a key in map, case insensitive, return actual key or nil if not found
// "def find_key_i(m,keyi) "
// "import string "
// "var keyu = string.toupper(keyi) "
// "if classof(m) == map "
// "for k:m.keys() "
// "if string.toupper(k)==keyu || keyi=='?' "
// "return k "
// "end "
// "end "
// "end "
// "end "
// # split the item when there is an operator, returns a list of (left,op,right)
// // # ex: "Dimmer>50" -> ["Dimmer",tasmota_gt,"50"]
// "def find_op(item) "
// "import string "
// "var op_chars = '=<>!' "
// "var pos = self.chars_in_string(item, op_chars) "
// "if pos >= 0 "
// "var op_split = string.split(item,pos) "
// "var op_left = op_split[0] "
// "var op_rest = op_split[1] "
// "pos = self.chars_in_string(op_rest, op_chars, true) "
// "if pos >= 0 "
// "var op_split2 = string.split(op_rest,pos) "
// "var op_middle = op_split2[0] "
// "var op_right = op_split2[1] "
// "return [op_left,op_middle,op_right] "
// "end "
// "end "
// "return [item, nil, nil] "
// "end "
// // Rules
// "def add_rule(pat,f) "
// "if !self._rules "
// "self._rules={} "
// "end "
// "if type(f) == 'function' "
// "self._rules[pat] = f "
// "else "
// "raise 'value_error', 'the second argument is not a function' "
// "end "
// "end "
// "def remove_rule(pat) "
// "if self._rules "
// "self._rules.remove(pat) "
// "end "
// "end "
// // Rules trigger if match. return true if match, false if not
// "def try_rule(event, rule, f) "
// "import string "
// "var rl_list = self.find_op(rule) "
// "var sub_event = event "
// "var rl = string.split(rl_list[0],'#') "
// "for it:rl "
// "found=self.find_key_i(sub_event,it) "
// "if found == nil return false end "
// "sub_event = sub_event[found] "
// "end "
// "var op=rl_list[1]"
// "var op2=rl_list[2]"
// "if op "
// "if op=='==' "
// "if str(sub_event) != str(op2) return false end "
// "elif op=='!==' "
// "if str(sub_event) == str(op2) return false end "
// "elif op=='=' "
// "if real(sub_event) != real(op2) return false end "
// "elif op=='!=' "
// "if real(sub_event) == real(op2) return false end "
// "elif op=='>' "
// "if real(sub_event) <= real(op2) return false end "
// "elif op=='>=' "
// "if real(sub_event) < real(op2) return false end "
// "elif op=='<' "
// "if real(sub_event) >= real(op2) return false end "
// "elif op=='<=' "
// "if real(sub_event) > real(op2) return false end "
// "end "
// "end "
// "f(sub_event, rl_list[0], event) "
// "return true "
// "end "
// // Run rules, i.e. check each individual rule
// // Returns true if at least one rule matched, false if none
// "def exec_rules(ev_json) "
// "if self._rules "
// "import json "
// "var ev = json.load(ev_json) "
// "var ret = false "
// "if ev == nil "
// "print('BRY: ERROR, bad json: '+ev_json, 3) "
// "else "
// "for r: self._rules.keys() "
// "ret = self.try_rule(ev,r,self._rules[r]) || ret "
// "end "
// "end "
// "return ret "
// "end "
// "return false "
// "end "
// "def set_timer(delay,f) "
// "if !self._timers self._timers=[] end "
// "self._timers.push([self.millis(delay),f]) "
// "end "
// // run every 50ms tick
// "def run_deferred() "
// "if self._timers "
// "var i=0 "
// "while i wire1 or wire2 or nil
// // scan for the first occurrence of the addr, starting with bus1 then bus2
// // optional: skip if index is disabled via I2CEnable
// "def wire_scan(addr,idx) "
// // skip if the I2C index is disabled
// "if idx != nil && !self.i2c_enabled(idx) return nil end "
// "if self.wire1.detect(addr) return self.wire1 end "
// "if self.wire2.detect(addr) return self.wire2 end "
// "return nil "
// "end "
// // set_light and get_light deprecetaion
// "def set_light(v,l) "
// "print('tasmota.set_light() is deprecated, use light.set()') "
// "import light "
// "if l != nil "
// "return light.set(v,l) "
// "else "
// "return light.set(v) "
// "end "
// "end "
// "def get_light(l) "
// "print('tasmota.get_light() is deprecated, use light.get()') "
// "import light "
// "if l != nil "
// "return light.get(l) "
// "else "
// "return light.get() "
// "end "
// "end "
// // cmd high-level function
// "def cmd(command) "
// "import json "
// "var ret = self._cmd(command) "
// "var j = json.load(ret) "
// "if type(j) == 'instance' "
// "return j "
// "else "
// "return {'response':j} "
// "end "
// "end "
// "end "
// // Monkey patch `Driver` class - To be continued
// "class Driver2 : Driver "
// "def add_cmd(c, f) "
// "var tasmota = self.get_tasmota() "
// "tasmota.add_cmd(c, / cmd, idx, payload, payload_json -> f(self, cmd, idx, payload, payload_json)) "
// "end "
// "end "
// "Driver = Driver2 "
// Instantiate tasmota object
"tasmota = Tasmota() "
"def log(m,l) tasmota.log(m,l) end "
"def load(f) tasmota.load(f) end "
#ifdef USE_LVGL
// instanciate singleton
// "class lvgl : lvgl_ntv "
// "end "
// "lv = lvgl() "
"import lvgl as lv "
"_lvgl_cb = [ {}, {}, {}, {}, {}, {} ] "
"_lvgl_cb_obj = [ {}, {}, {}, {}, {}, {} ] "
"def _lvgl_cb_dispatch(idx, obj, v1, v2, v3, v4) "
// "import string print(string.format('>>> idx=%i obj=0x%08X v1=%i', idx, obj, v1)) "
"var func = _lvgl_cb[idx].find(obj) "
"var inst = _lvgl_cb_obj[idx].find(obj) "
"if func != nil "
"return func(inst, v1, v2, v3, v4) "
"end "
"return nil "
"end "
// array of 6 callback types, each with key (lv_obj pointer converted to int, closure)
#endif // USE_LVGL
// Wire class
// "class Wire : Wire_ntv "
// // read bytes as `bytes()` object
// "def read_bytes(addr,reg,size) "
// "self._begin_transmission(addr) "
// "self._write(reg) "
// "self._end_transmission(false) "
// "self._request_from(addr,size) "
// "var ret=bytes(size) "
// "while (self._available()) "
// "ret..self._read() "
// "end "
// "return ret "
// "end "
// // write bytes from `bytes` object
// "def write_bytes(addr,reg,b) "
// "self._begin_transmission(addr) "
// "self._write(reg) "
// "self._write(b) "
// "self._end_transmission() "
// "end "
// "end "
#ifdef USE_I2C
"tasmota.wire1 = Wire(1) "
"tasmota.wire2 = Wire(2) "
"wire1 = tasmota.wire1 "
"wire2 = tasmota.wire2 "
#endif // USE_I2C
// auto-import gpio
"import gpio "
#ifdef USE_LIGHT
"import light "
#endif // USE_LIGHT
;
const char berry_autoexec[] =
// load "autoexec.be" using import, which loads either .be or .bec file
"import string "
"try "
"load('autoexec.be') "
"except .. as e,m "
"if e=='io_error' && string.find(m, \"autoexec.be\")>0 "
"log(\"BRY: no autoexec.be\") "
"else "
"log(\"BRY: exception in autoexec.be: \"+e+\": \"+m) "
"end "
"end "
;
#endif // USE_BERRY