/* 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