Test out admin mode commands; it looks like the basic idea works for now at least
This commit is contained in:
parent
f1723ff5bd
commit
3a7bb60360
20
dev/dev.htm
20
dev/dev.htm
|
@ -166,11 +166,12 @@ async function updateCharts() {
|
||||||
}
|
}
|
||||||
|
|
||||||
const sleep = (ms) => new Promise(r => setTimeout(r, ms))
|
const sleep = (ms) => new Promise(r => setTimeout(r, ms))
|
||||||
|
var chart_update_millis = 2000
|
||||||
|
|
||||||
async function chartupdater() {
|
async function chartupdater() {
|
||||||
if (chart_updater != null) return
|
if (chart_updater != null) return
|
||||||
|
|
||||||
await sleep(2000)
|
await sleep(chart_update_millis)
|
||||||
// wait at least two seconds to avoid wasting a lot of bandwidth
|
// wait at least two seconds to avoid wasting a lot of bandwidth
|
||||||
const resp = await fetch("/api/update")
|
const resp = await fetch("/api/update")
|
||||||
chart_updater = null
|
chart_updater = null
|
||||||
|
@ -200,6 +201,19 @@ async function waitThenUpdateStatus() {
|
||||||
updateStatus()
|
updateStatus()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function testAdminMode() {
|
||||||
|
const msg = JSON.stringify({
|
||||||
|
auth: "password",
|
||||||
|
data: {
|
||||||
|
manual_mode: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
await fetch("/api/admin", {
|
||||||
|
method: "POST",
|
||||||
|
body: msg
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
window.onload = () => {
|
window.onload = () => {
|
||||||
initCharts()
|
initCharts()
|
||||||
updateCharts()
|
updateCharts()
|
||||||
|
@ -212,6 +226,9 @@ window.onload = () => {
|
||||||
document.getElementById('autoupdate').addEventListener('click', (e) => {
|
document.getElementById('autoupdate').addEventListener('click', (e) => {
|
||||||
autoupdate = document.getElementById('autoupdate').checked
|
autoupdate = document.getElementById('autoupdate').checked
|
||||||
})
|
})
|
||||||
|
document.getElementById('test-admin').addEventListener('click', (e) => {
|
||||||
|
testAdminMode()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
|
@ -223,6 +240,7 @@ window.onload = () => {
|
||||||
<form>
|
<form>
|
||||||
<input type=checkbox id=autoupdate checked>Autoupdate</input>
|
<input type=checkbox id=autoupdate checked>Autoupdate</input>
|
||||||
<input type=button id=update value="Update"></input>
|
<input type=button id=update value="Update"></input>
|
||||||
|
<input type=button id=test-admin value="Test admin mode"></input>
|
||||||
<!-- TODO add decimation and window size options -->
|
<!-- TODO add decimation and window size options -->
|
||||||
<span id=status></span>
|
<span id=status></span>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -31,6 +31,8 @@ type ShroomStatus struct {
|
||||||
NumConnections int
|
NumConnections int
|
||||||
Wait chan struct{}
|
Wait chan struct{}
|
||||||
StatusWait chan struct{}
|
StatusWait chan struct{}
|
||||||
|
|
||||||
|
Commands chan []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ShroomStatus) Update() {
|
func (s *ShroomStatus) Update() {
|
||||||
|
@ -71,7 +73,7 @@ func parseMsg(line []byte, db *sql.DB, status *ShroomStatus) {
|
||||||
// we got a data packet
|
// we got a data packet
|
||||||
status.Update()
|
status.Update()
|
||||||
} else if data.Status != -1 {
|
} else if data.Status != -1 {
|
||||||
log.Println("received status ", data.Status)
|
//log.Println("received status ", data.Status)
|
||||||
status.Lock()
|
status.Lock()
|
||||||
// TODO change to have more detailed data
|
// TODO change to have more detailed data
|
||||||
status.HumidifierOn = (data.Status & 1) == 1
|
status.HumidifierOn = (data.Status & 1) == 1
|
||||||
|
@ -96,6 +98,12 @@ func InitTcpServer(db *sql.DB, status *ShroomStatus) {
|
||||||
log.Println("tcp accept error: ", err)
|
log.Println("tcp accept error: ", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// not spawning a goroutine here
|
||||||
|
// should limit the number of connections to
|
||||||
|
// one hopefully
|
||||||
|
|
||||||
|
// wrapping in a func() so that I can use defer
|
||||||
|
// to automatically decrement the number of connections
|
||||||
func() {
|
func() {
|
||||||
status.Lock()
|
status.Lock()
|
||||||
status.NumConnections += 1
|
status.NumConnections += 1
|
||||||
|
@ -112,8 +120,27 @@ func InitTcpServer(db *sql.DB, status *ShroomStatus) {
|
||||||
|
|
||||||
log.Println("connection started")
|
log.Println("connection started")
|
||||||
|
|
||||||
|
// write loop; waits for commands and forwards them
|
||||||
|
exiting := make(chan struct{})
|
||||||
go func() {
|
go func() {
|
||||||
// TODO deal with the write side of the connection
|
for {
|
||||||
|
select {
|
||||||
|
case v, ok := <-status.Commands:
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
v = append(v, []byte("\n")...)
|
||||||
|
_, err := conn.Write(v)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("tcp write err: ", err)
|
||||||
|
}
|
||||||
|
case <-exiting:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
defer func() {
|
||||||
|
close(exiting)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// deal with the read side of the connection
|
// deal with the read side of the connection
|
||||||
|
|
|
@ -29,6 +29,11 @@ type statusJson struct {
|
||||||
Humidifier bool `json:"humidifier"`
|
Humidifier bool `json:"humidifier"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type adminMsg struct {
|
||||||
|
Auth string `json:"auth"`
|
||||||
|
Msg map[string]interface{} `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
func dumpData(db *sql.DB, multiplier int64) func(http.ResponseWriter, *http.Request) {
|
func dumpData(db *sql.DB, multiplier int64) func(http.ResponseWriter, *http.Request) {
|
||||||
return func(w http.ResponseWriter, req *http.Request) {
|
return func(w http.ResponseWriter, req *http.Request) {
|
||||||
now := time.Now().Unix()
|
now := time.Now().Unix()
|
||||||
|
@ -70,6 +75,7 @@ func main() {
|
||||||
status := s.ShroomStatus{
|
status := s.ShroomStatus{
|
||||||
Wait: make(chan struct{}),
|
Wait: make(chan struct{}),
|
||||||
StatusWait: make(chan struct{}),
|
StatusWait: make(chan struct{}),
|
||||||
|
Commands: make(chan []byte),
|
||||||
}
|
}
|
||||||
s.InitTcpServer(db, &status)
|
s.InitTcpServer(db, &status)
|
||||||
|
|
||||||
|
@ -106,7 +112,7 @@ func main() {
|
||||||
msg, err := json.Marshal(s)
|
msg, err := json.Marshal(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("unable to marshal json: %w", err)
|
err = fmt.Errorf("unable to marshal json: %w", err)
|
||||||
w.WriteHeader(500)
|
w.WriteHeader(400)
|
||||||
w.Write([]byte(err.Error()))
|
w.Write([]byte(err.Error()))
|
||||||
} else {
|
} else {
|
||||||
w.Write(msg)
|
w.Write(msg)
|
||||||
|
@ -114,9 +120,49 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
adminHandler := func(w http.ResponseWriter, req *http.Request) {
|
adminHandler := func(w http.ResponseWriter, req *http.Request) {
|
||||||
w.WriteHeader(500)
|
if req.Method != "POST" {
|
||||||
w.Write([]byte("unimplemented"))
|
w.WriteHeader(405)
|
||||||
// TODO
|
w.Write([]byte("method must be POST"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
msg := make([]byte, 256)
|
||||||
|
l, err := req.Body.Read(msg)
|
||||||
|
if err != nil && l == 0 {
|
||||||
|
err = fmt.Errorf("unable to read body: %w", err)
|
||||||
|
w.WriteHeader(400)
|
||||||
|
w.Write([]byte(err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
adminReq := adminMsg{}
|
||||||
|
err = json.Unmarshal(msg[:l], &adminReq)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("unable to unmarshal body as json: %w", err)
|
||||||
|
w.WriteHeader(400)
|
||||||
|
w.Write([]byte(err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO switch to embedded secret
|
||||||
|
if adminReq.Auth != "password" {
|
||||||
|
w.WriteHeader(401)
|
||||||
|
w.Write([]byte(err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
inner_msg, err := json.Marshal(adminReq.Msg)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("unable to marshal inner message: %w", err)
|
||||||
|
w.WriteHeader(400)
|
||||||
|
w.Write([]byte(err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case status.Commands <- inner_msg:
|
||||||
|
w.Write([]byte("ok"))
|
||||||
|
default:
|
||||||
|
w.WriteHeader(503)
|
||||||
|
w.Write([]byte("unable to forward request; controller may not be connected"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateHandler := func(w http.ResponseWriter, req *http.Request) {
|
updateHandler := func(w http.ResponseWriter, req *http.Request) {
|
||||||
|
|
Loading…
Reference in New Issue