Skip to content

Instantly share code, notes, and snippets.

@PaSaSaP
Last active October 3, 2023 08:25
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save PaSaSaP/633ce239c7066060a1abbdf8522a66ba to your computer and use it in GitHub Desktop.
Save PaSaSaP/633ce239c7066060a1abbdf8522a66ba to your computer and use it in GitHub Desktop.
Parse Nextion HMI project file so script creates dir tree structure containing event codes.
#!/usr/bin/env python3
"""
Parse HMI Nextion file and put result into directory tree.
While parsing Nextion cannot have opened that project.
Tested for Nextion 1.63.3
"""
import sys
import os
from os.path import basename, exists, realpath
from os import mkdir
from re import compile
class Attr:
def __init__(self, num):
self.num = num
self.objname = ""
self.typename = ""
self.codesload = ""
self.codesloadend = ""
self.codesdown = ""
self.codesup = ""
self.codesunload = ""
self.codestimer = ""
self.codesplayend = ""
self.page_attr = None
def create_files_inside(self, dir_name):
if self.is_some_non_empty_event():
if self.page_attr is None:
print(F"Probably false positive, {self.objname} has not page_attr")
return
attr_dir = dir_name + "/" + self.page_attr.objname
if not exists(attr_dir):
mkdir(attr_dir)
attr_fname_prefix = attr_dir + "/" + self.objname + "."
self.create_file(attr_fname_prefix + "init_pre.txt", self.codesload)
self.create_file(attr_fname_prefix + "init_post.txt", self.codesloadend)
self.create_file(attr_fname_prefix + "touch_press.txt", self.codesdown)
self.create_file(attr_fname_prefix + "touch_release.txt", self.codesup)
self.create_file(attr_fname_prefix + "page_exit.txt", self.codesunload)
self.create_file(attr_fname_prefix + "timer.txt", self.codestimer)
self.create_file(attr_fname_prefix + "play_end.txt", self.codesplayend)
def is_some_non_empty_event(self):
return any((self.codesload, self.codesloadend, self.codesdown, self.codesup,
self.codesunload, self.codestimer, self.codesplayend))
def create_file(self, fname, code):
if code:
with open(fname, "wb") as f:
f.write(code.encode())
f.write(b"\n")
def dot_name(self):
return self.page_attr.objname + '.' + self.objname if self.page_attr else self.objname
def __eq__(self, rhs):
return self.dot_name() == rhs.dot_name()
def __repr__(self):
return F"<{self.dot_name()}>"
def get_event_code(i, strings, line, regex):
event_code = ""
strings_jump = 0
check = regex.match(line)
if check:
strings_jump = int(check[1])
if strings_jump > 0:
event_code = "\n".join(strings[i+1:i+1+strings_jump])
return event_code, strings_jump
def main():
if len(sys.argv) < 3:
print("pass nextion file and output dir as parameters")
print(F"Example: {basename(sys.argv[0])} project.hmi project_events")
sys.exit(1)
f = open(sys.argv[1], "rb")
strings = []
line = ''
i = 0
printable = False
for c in f.read():
if c == 0:
i = i + 1 if i else 0
if not printable:
continue
ch = chr(c)
printable = ch.isprintable()
if printable:
if i == 3:
# Line ends with 4 bytes where first is non-zero, rest is zero
# first can be ascii char so in that case remove last char in previous line
# print(F"{strings[-1]} -> {strings[-1][:-1]}")
strings[-1] = strings[-1][:-1]
i = 0
line += ch
else:
i = 0
if len(line) > 0:
strings.append(line)
i = 1
line = ''
f.close()
#print(strings)
#sys.exit(0)
attrs = []
attr = None
strings_jump = 0
attr_regex = compile(r'^att-(\d+)$')
codesload_regex = compile(r'^codesload-(\d+)$')
codesloadend_regex = compile(r'^codesloadend-(\d+)$')
codesdown_regex = compile(r'^codesdown-(\d+)$')
codesup_regex = compile(r'^codesup-(\d+)$')
codesunload_regex = compile(r'^codesunload-(\d+)$')
codestimer_regex = compile(r'^codestimer-(\d+)$')
codesplayend_regex = compile(r'^codesplayend-(\d+)$')
objname_next = False
type_next = False
current_page_attr = None
for i, line in enumerate(strings):
if strings_jump:
strings_jump -= 1
continue
if len(line) == 0:
continue
attr_check = attr_regex.match(line)
if attr_check:
if attr is not None:
attr.page_attr = current_page_attr
#if attr not in attrs:
attrs.append(attr)
attr = Attr(attr_check[1])
continue
event_code, strings_jump = get_event_code(i, strings, line, codesload_regex)
if strings_jump:
attr.codesload = event_code
continue
event_code, strings_jump = get_event_code(i, strings, line, codesloadend_regex)
if strings_jump:
attr.codesloadend = event_code
continue
event_code, strings_jump = get_event_code(i, strings, line, codesdown_regex)
if strings_jump:
attr.codesdown = event_code
continue
event_code, strings_jump = get_event_code(i, strings, line, codesup_regex)
if strings_jump:
attr.codesup = event_code
continue
event_code, strings_jump = get_event_code(i, strings, line, codesunload_regex)
if strings_jump:
attr.codesunload = event_code
continue
event_code, strings_jump = get_event_code(i, strings, line, codestimer_regex)
if strings_jump:
attr.codestimer = event_code
continue
event_code, strings_jump = get_event_code(i, strings, line, codesplayend_regex)
if strings_jump:
attr.codesplayend = event_code
continue
if objname_next:
if line != "vscope": # Dont name variables 'vscope'!
if attr.is_some_non_empty_event():
# event codes are in last position of entry so probably some missing data or idk...
attr.page_attr = current_page_attr
#if attr not in attrs:
attrs.append(attr)
attr = Attr(0)
attr.objname = line
#else:
# attr = None
objname_next = False
elif line == "objname":
objname_next = True
elif type_next:
attr.typename = line
if line == 'y':
current_page_attr = attr
type_next = False
elif line == "type":
type_next = True
#print(attrs)
base_dir = sys.argv[2]
if not exists(base_dir):
mkdir(base_dir) # script does not remove any files just in case...
for attr in attrs:
attr.create_files_inside(base_dir)
print("If any of code events have been found then they are located inside dir:")
print(realpath(base_dir))
print("Use Your favourite text searching tool!")
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment