initial commit all working modules
time file home assistant object (based on class member) extended string plus bundle scriptmaster
parent
516383197b
commit
46e295451e
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
@ -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
|
Loading…
Reference in New Issue