Help with LuCI app

i want to make a control interface for a service that i run under RUNIT control, i thought it would be simple but it turned out it seems pretty hard to achieve, even tho i only have 4 textfield, a toggle switch and a text area for log.
First here is a view of what i want to make:

Capture d’écran 2020-01-20 à 14.55.27

this view is made with html/css/javascript in jfiddle page

I also tried to do the thing as a LuCI app, in lua only, but i cannot get any close to it, mainly because
-i cant have the text fields inline
-there is apparently no toggle switch buttons
-i cannot manage to get the modified text fields values to save them back to the json

Here is what the app does, when the page is loaded it read a json to get server address, port up, port down and pupulate the fields, it runs a command to get the status of the service (up or down) and set the toggle switch accordingly, finally it read a specific log, keep last lines in a variable and display them in the text area.
I have managed to read the json with lua and get the appropriate fields and save last lines of log and get the service status in variables. I also managed to somewhat control the service from buttons, since lua doesnt have toggle switch I used one start button, one stop button and wrote the service status in a field (not very nice). I also managed to show the log in the text area, but only at page loading.
Here is what i did not manage to do:
-format text fields properly (inline)
-have a toggle switch instead of 2 button + 1 field
-get the fields values after they are modified
-update the json with these values and save it back
-update the log each 30 seconds or so
-make and use an HTML template which would probably solve all these problems.

Below are the functions i have put together to control service and deal with the json and log. The reason of this mishmash on the json is because for some reason jsonc is not able to deal with the brackets, it crashes, so i have to remove the brackets before using jsonc. Of course it is a problem because i have to save the json file back later, and the service which uses it expects these brackets.

local res
local id
local addr
local status
local log

function getServiceStatus()
  local handle = io.popen("sv s gwservice")
  local result = string.gsub(handle:read("*a"),"\n","")
  if Contains(string.sub(result, 1, 6), "run")
  then status = "running"
  elseif Contains(string.sub(result, 1, 6), "down")
  then status = "stopped"
  return status

function startService()
  local handle = io.popen("sv start gwservice")
  local result = handle:read("*a")
  return result

function stopService()
  local handle = io.popen("sv stop gwservice")
  local result = handle:read("*a")
  return result

function getLogTail()
  local f ="/var/log/gwservice/current", "r")
  f:seek( "end",-689)
  log = f:read("*a")
	return log

function getServerAddress()
  local str = fs.readfile("/tmp/gwcfg/local_conf.json")
  str = string.gsub(str,"%[","")
  str = string.gsub(str,"%]","")
  res = luci.jsonc.parse(str)
  addr = res.gateway_conf.servers.server_address
  return addr

function getServerPortUp()
  return res.gateway_conf.servers.serv_port_up

function getServerPortDown()
  return res.gateway_conf.servers.serv_port_down

function getGatewayID()
  return res.gateway_conf.gateway_ID
  "gateway_conf": {
    "gateway_ID": "1A5562EBFE56EB1C",
    "servers": [
        "server_address": "",
        "serv_port_up": 1700,
        "serv_port_down": 1700,
        "serv_enabled": true
    "description": "Gateway"

Now if anyone could give direction on how i should do this interface properly, with an HTML template (i dont understand how it works together with the lua cbi) and save the datas back to the json that would be much appreciated!

-i cant have the text fields inline
I think you can, but it may be easier using a Template, which is a mix of HTML, JS and Lua, rather than a Form, which is all Lua. The good thing about Templates is you can have it dynamically updated easily. See the example below.

-there is apparently no toggle switch buttons

Closest thing I know of is the Flag type, here's an example:
default = m:field(Flag, "default", "Reset to Default", "Reset the modem to use default bands (all bands).")

-i cannot manage to get the modified text fields values to save them back to the json

I have not done exactly that before, but have you tried using luci.http.prepare_content("application/json")?


Example template view (note: missing the Controller dispatcher)

 Stripped down Status Page hello world example

	local fs = require "nixio.fs"
	local ipc = require "luci.ip"
	local util = require "luci.util"
	local stat = require ""
	local ver = require "luci.version"

	if luci.http.formvalue("status") == "1" then

		local rv = {
				foo = luci.sys.exec("echo hello"),
                                bar = luci.sys.exec("echo 69")




<script type="text/javascript">//<![CDATA[
	XHR.poll(5, '<%=REQUEST_URI%>', { status: 1 },
		function(x, info)

			var e;

			if (e = document.getElementById('hello'))
				e.innerHTML = info.hello;
			if (e = document.getElementById('bar')) {
				var temperature_note = "";
				if ((parseInt(info.temperature, 10)) > 70) {
					temperature_note = " - hot";
				} else {
					temperature_note = " - normal";
				e.innerHTML = + "&deg C" + temperature_note;



<h2 name="content"><%:Hello World Page%></h2>

<div class="cbi-section">
	<h3><%:Modem Information%></h3>

	<div class="table" width="100%">
		<div class="tr"><div class="td left" width="33%"><%:Foo%></div><div class="td left" id="foo">-</div></div>
		<div class="tr"><div class="td left" width="33%"><%:Bar%></div><div class="td left" id="bar">-</div></div>