initial commit all working modules

time
  file
  home assistant
  object (based on class member)
  extended string
  plus bundle script
master
Kebler Network System Administrator 2022-09-02 17:15:27 -07:00
parent 516383197b
commit 46e295451e
6 changed files with 689 additions and 0 deletions

12
bundle Executable file
View File

@ -0,0 +1,12 @@
#!/bin/bash
# todo set utility directory and also customize start.be
MODULES=/data/coding/berry/modules
# todo bash lower case no space function
rm ./utils.tapp
zip -j -Z store ./utils.tapp \
$MODULES/util/stringe.be \
$MODULES/util/file.be \
$MODULES/util/object.be \
$MODULES/util/time.be \
$MODULES/util/ha.be

139
file.be Normal file
View File

@ -0,0 +1,139 @@
# load and save a json file
import json
# ##########################
# uncomment on dev machine, commented on esp
# import os
# path=os.path
import path # uncommented on esp, commented on dev
import string
#############################
class File
var _filename
var _ext
# must use nil for filename if only setting extension
def init(filename,ext)
self._ext = ext ? '.'+str(ext) : '.json'
self._filename = self.addext(filename)
end
def addext(filename)
if !filename filename=self._filename end
# print ('extension', self._ext, filename)
var name = string.find(filename,'.') < 0 ? filename+self._ext : filename
return name
end
def delete(filename)
filename = filename ? self.addext(filename) : self._filename
if path.remove(filename)
print('deleted file', filename)
else
print('unable to delte file', filename)
end
end
def name(filename)
if filename self._filename = self.addext(filename) end
return self._filename
end
# returns parsed json
def load(filename)
filename = filename ? self.addext(filename) : self._filename
var f # file object
if path.exists(filename)
var obj
try
f = open(filename, "r")
obj = json.load(f.read())
f.close()
except .. as e, m
if f != nil f.close() end
raise e, m
end
if debug[1]
print ('=============')
print ('loaded file ',filename, '. Returning parsed object')
print (obj)
print ('=============')
end
return obj
else
print('Warning: file does not exist, nothing loaded', filename)
end
return nil
end
# obj is requried
def save(obj,filename)
if ! obj
print('ERROR: must pass a object to save as json')
return nil
end
filename = filename ? self.addext(filename) : self._filename
if ! filename
print('ERROR: no filename set to save object as json')
return nil
end
var f # file object
try
f = open(filename, "w")
self._json_fdump_any(f,obj)
f.close()
except .. as e, m
if f != nil f.close() end
f.close()
raise e, m
end
if debug[3] print('success writing to file',filename) end
end
# internal utility methods
def _json_fdump_map(f, v)
f.write('{')
var sep = nil
for k:v.keys()
if sep != nil f.write(sep) end
f.write(json.dump(str(k)))
f.write(':')
self._json_fdump_any(f, v[k])
sep = ","
end
f.write('}')
end
def _json_fdump_list(f, v)
f.write('[')
var i = 0
while i < size(v)
if i > 0 f.write(',') end
self._json_fdump_any(f, v[i])
i += 1
end
f.write(']')
end
def _json_fdump_any(f, v)
import json
if isinstance(v, map)
self._json_fdump_map(f, v)
elif isinstance(v, list)v
self._json_fdump_list(f, v)
else
f.write(json.dump(v))
end
end
end # FILE class
var file = module('file')
# file.create = File
file = File
return file

62
ha.be Normal file
View File

@ -0,0 +1,62 @@
import string
import mqtt
import json
import object
def entity_create(Name,opts)
# opts = { type: (sensor/binary-sensor), jsonkey: , icon:, topic_prefix, topic, unit, custom}
if !Name
print('must pass a name for the home assistant entity')
return nil
end
opts = object(opts)
opts.type = opts.type ? opts.type : 'sensor'
print('######### creating Home Assitant entity:',Name, '#############')
var mac = string.tolower(string.split(tasmota.wifi()['mac'],':').concat())
# var mac = '1q2w3e4r5t'
var name = string.tolower(string.split(Name,' ').concat('_'))
var id = mac+'-'+name
# print(mac,name, id)
var topic = tasmota.cmd('topic')['Topic']
var Device = tasmota.cmd('status')['Status']['DeviceName']
var device = string.tolower(string.split(Device,' ').concat('_'))
# var topic = 'tastmotatesting'
# var dis_topic = 'tasmota/discovery/'+id+'/sensors/config'
var dis_topic = 'homeassistant/sensor/'+mac+'/'+ name +'/config'
print(dis_topic)
var p = object() # payload
p.name = Device + ' ' + Name
p.uniq_id = id
p.device = {'cns': [['mac',mac]]}
if opts.topic
p.state_topic = opts.topic
else
if opts.topic_prefix
p.state_topic = opts.topic_prefix + '/' + device + '/' + name
else
p.state_topic = 'stat/'+ topic + '/'+ name
end
end
if opts.unit p.unit_of_measurement = opts.unit end
if opts.icon p.icon = opts.icon end
if opts.jsonkey p.value_template = '{{ value_json.'+ opts.jsonkey + ' }}' end
if opts.value_template p.value_template = opts.value_template end
if opts.custom
# opts.custom = type(opts.custom == 'string') ? json.load(opts.custom) : opts.custom
print('merged custom', opts.custom)
p.merge(opts.custom,true)
print(p.get())
end
print(p.dump())
print("############ Entity Created #######################")
# (topic:string, payload:string[, retain:bool, start:int, len:int]) -
mqtt.publish(dis_topic,p.dump(), true)
return [p.get(), dis_topic, mac , device]
end
var ha = module('ha')
ha.entity_create = entity_create
return ha

252
object.be Normal file
View File

@ -0,0 +1,252 @@
# uci
import file
# native
import string
import json
# really a a nested map designe to work like a javascript object with . notation
class Object
var _obj
var _autosave # will save object(map) whenever changes are made, default: false
var _autoload # if true will attempt to load from file on init, default: true
var _status
var file # attached file instance
var _deep # depth to follow nested maps, default = -1 which mean walk until no more
# forbidden key names
# any key starting with _
# init
# member
# setmember
# assign
# retrieve
# load
# save
# get
# set
# file
# filename
# exporti
# importi
# for passing only opts must set obj to {} or nil, e.g. (nil,{'autosave':true})
def init(obj,filename,opts)
if (type(obj) == 'string')
if debug[3] print('no object passed a filename was passed') end
opts = filename
filename = obj
obj = nil
end
if isinstance(filename, map)
opts = filename
filename = nil
end
if ! opts opts = {} end
self.assign(self._isMap(obj,true)) # will set to empty map if obj is nil
if filename
if debug[3] print('filename passsed, adding file instance', filename) end
self.file = file(filename,opts.find('ext')) # create file instance
end
self._autosave = opts.find('autosave') == nil ? false : opts.find('autosave')
self._autoload = opts.find('autoload') == nil ? true : opts.find('autoload')
if self.file
if self._isMap(obj)
if self._autosave
if debug[3] print('autosaving passed object map to', self.file.name()) end
self.save()
end
else
if self._autoload
if debug[3] print('autoloading', self.file.name()) end
return self.load()
end
end
end
end
#
def load (filename)
if self.file
if debug[3] print ('loading before', self._obj) end
var obj = self.file.load(filename)
if isinstance(obj,map)
self._obj = obj
return obj
# walk object looking for additional maps?
else
if debug[3] print ('Warning: Unable to load file or file does not contain a map') end
end
if debug[3] print ('loading after',self._obj) end
end
return nil
end
def save (filename)
if self.file
if debug[3]
print ('saving', self.file.addext(filename))
print ('object to be saved', self._obj)
# print ('contents of current file', self.load(filename))
end
self.file.save(self._obj,filename)
# if debug[3] print ('after saving',self.load(filename)) end
end
end
def _isMap (m,init)
if isinstance(m, map)
return m
else
if init
return {}
else
return nil
end
end
end
def merge(m,overwrite)
for k: m.keys()
if overwrite
self._obj.setitem(k,m[k])
else
self._obj.insert(k,m[k])
end
end
return self._obj
end
def assign(obj)
if ! obj obj={} end
if isinstance(obj, map)
self._obj=obj
else
end
return self._obj
end
def retrieve()
return self._obj
end
def exporti(save)
var obj = self._obj
if save && self.file
self.save()
obj = nil
end
return { 'filename': self.filename(), 'obj':obj, 'opts':{'autosave':self._autosave, 'autoload':self._autoload} }
end
def importi(s)
print ('setting to import',s)
return Object(s.item('obj'),s.item('filename'),s.item('opts'))
end
def filename()
if self.file
return self.file.name()
else
return nil
end
end
def del(keys)
self.set(keys,'__remove__')
end
# # sets a nested property
def set(keys,value)
def _set(keys,value,obj)
if !obj obj = self._obj end
var key = keys.pop()
if !obj.find(key)
obj[key] = {}
end
if keys.size()
return _set(keys,value,obj[key])
else
if value == '__remove__'
obj.remove(key)
else
obj[key]=value
end
return true
end
end
if type(keys) == 'string' keys = string.split(keys,".") end
if !isinstance(keys,list)
print('ERROR: list of keys not passed', keys)
return nil
end
keys.reverse()
if debug[3] print('setting',keys,'to',value) end
if _set(keys,value)
if self.file && self._autosave self.save() end
end
end
def dump(subkey)
if subkey == nil return json.dump(self.get()) end
return json.dump(self.get(subkey))
end
# gets a nested property
def get(keys)
if keys == nil return self._obj end
def _get(keys,obj)
if !obj
obj = self._obj
end
var key = keys.pop()
var _obj = obj.find(key)
if debug[1]
print('current key:', key, 'remaining keys:', keys )
print('current nested object: ',_obj)
end
if isinstance(_obj,map)
if keys.size()
return _get(keys,_obj)
else
return _obj
end
else
return _obj
end
end
if type(keys) == 'string' keys = string.split(keys,".") end
if !isinstance(keys,list)
print('ERROR: list of keys not passed', keys)
return nil
end
keys.reverse()
if debug[3] print('getting',keys) end
return _get(keys)
end
# - virtual member getter, if a key does not exists return `nil`-#
def member(key)
return self._obj.find(key)
end
#- virtual member setter -#
def setmember(key, value)
if debug[3]
print ('setting key=>', key,':', value)
end
self._obj[key] = value
if self._autosave self.save() end
return self.member(key)
end
end # Object Class
var object = module('object')
object = Object
return object

16
stringe.be Normal file
View File

@ -0,0 +1,16 @@
import string
def tolower(astr)
return string.tr(astr,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")
end
def nospaces(astr)
return string.tr(astr," ","")
end
var stringe = module('stringe')
stringe.tolower=tolower
stringe.nospaces=nospaces
return stringe

208
time.be Normal file
View File

@ -0,0 +1,208 @@
import string
class Time
var utc
var last
var times
def init ()
self.utc = false
self.last = nil
self.times = {}
end
static def iso(epoch)
return tasmota.time_str(epoch)
end
static def zone(offset)
return tasmota.cmd("timezone "+str(offset))['Timezone']
end
# dynamic methods
def prop(epoch,prop)
if type(epoch) != 'int'
prop = epoch
epoch = self.now('e')
end
prop = prop ? prop : ""
var t = tasmota.time_dump(epoch)
if t.contains(prop)
return t.item(prop)
else
return t
end
end
def isLocal(epoch)
return ! self.utc
end
def save (name,epoch)
if ! name
return false
end
if epoch == 'last'
epoch = self.last
end
if type(epoch) != 'integer'
epoch = self.now('e')
end
self.times.setitem(name,epoch)
return self.get(name)
end
def remove (name)
self.times.remove(name)
end
def get (name)
return name ? self.times.find(name) : self.times
end
def elapsed (name, unit)
if self.get(name)
var elapsed = self.now('e') - self.get(name)
if unit == 'hms' return self.hms(elapsed) end
return elapsed
end
end
def now(format)
var e = tasmota.rtc()
if format == 'r'
return e
end
e = e[self.utc ? 'utc' : 'local']
self.last = e
if format == nil
return self.iso(e)
end
if format == 'full'
return tasmota.strftime(e)
end
if format == 'e'
return e
end
end
def hms(t) # in seconds
var h = t / 3600
var s = (t % 3600)
var m = s / 60
s = s % 60
return string.format('%d:%d:%d',h,m,s)
end
def tomorrow(unit)
return self.day(unit,self.now('e') + 86400)
end
def yesterday(unit)
return self.day(unit,self.now('e') - 86400)
end
def today(unit)
return self.day(unit)
end
def day(unit,e)
if type(unit) == int
e = unit
unit = nil
end
e = e ? e : self.now('e')
if unit == 'json'
var d = tasmota.time_dump(e)
d.remove('hour')
d.remove('min')
d.remove('sec')
return d
end
if unit == 'full' return tasmota.strftime("%d %B %Y",e) end
if unit == 'dmy' return tasmota.strftime("%d/%m/%Y",e) end
if unit == 'ymd' return tasmota.strftime("%Y-%m-%d",e) end
return tasmota.strftime("%m/%d/%Y",e)
end
def tod(unit)
var t = {}
t = tasmota.time_dump(self.now('e'))
if unit == 'json'
t.remove('weekday')
t.remove('month')
t.remove('year')
t.remove('day')
return t
end
if unit == 'sec'
return t['hour']*3600+t['min']*60+t['sec']
end
if unit == 'hour'
return t['hour']+t['min']*1.0/60+t['sec']*1.0/3600
end
if unit == 'min'
return t['hour']*60+t['min']+t['sec']*1.0/60
end
if unit == '24'
return string.format('%02d:%02d:%02d',t['hour'],t['min'],t['sec'])
end
var half = t['hour'] > 11 ? "P.M" : "A.M"
var hour = t['hour'] > 12 ? t['hour'] - 12 : t['hour']
return string.format('%02d:%02d:%02d %s',hour,t['min'],t['sec'],half)
end
def test ()
print('epoch: ',self.now('e'))
print('iso: ',self.now())
print('raw: ',self.now('r'))
print('props: ',self.prop())
print('prop year: ',self.prop('year'))
print('prop year 0: ',self.prop(0,'year'))
print('day default(mdy): ',self.day())
print('day json: ',self.day('json'))
print('day dmy: ',self.day('dmy'))
print('day ymd: ',self.day('ymd'))
print('day full: ',self.day('full'))
print('tomorrow ymd: ',self.tomorrow('ymd'))
print('today ymd: ',self.today('ymd'))
print('yesterda ymd: ',self.yesterday('ymd'))
print('tod default: ',self.tod())
print('tod json: ',self.tod('json'))
print('tod sec: ',self.tod('sec'))
print('tod minutes: ',self.tod('min'))
print('tod hours: ',self.tod('hour'))
print('tod 24: ',self.tod('24'))
self.save('test1')
print('test1: ',self.get('test1'))
print('last: ',self.last)
self.save('test2')
print('test2: ',self.get('test2'))
print('last: ',self.last)
print('all saved:', self.get())
print('hms',self.hms(1),self.hms(61),self.hms(3661))
end
end
var time = module('time')
time = Time()
if debug[3] print('returning time instance') end
return time