Compare commits

..

6 Commits

Author SHA1 Message Date
4efce925f0 scheduler: fix runtime reset when date changes
Signed-off-by: Thomas Klaehn <tkl@blackfinn.de>
2025-09-29 11:22:56 +02:00
c85d0d1f68 apiservice/state: Enhancement: store state cache
Signed-off-by: Thomas Klaehn <tkl@blackfinn.de>
2025-08-15 09:05:51 +02:00
b76d394b16 app/scheduler: Enhancement: detect date change & update daily runtimes
accordingly

Signed-off-by: Thomas Klaehn <tkl@blackfinn.de>
2025-08-15 09:03:49 +02:00
ce31b61b1b app/storage: Enhancement: Set path with member function rather than
directly

Signed-off-by: Thomas Klaehn <tkl@blackfinn.de>
2025-08-15 09:02:33 +02:00
9cf9fb7263 webui/index.html: Fix: remove debug prints
Signed-off-by: Thomas Klaehn <tkl@blackfinn.de>
2025-08-15 09:00:49 +02:00
d6d09abe9f apiservice/soil/moisture: Fix: correct equation for soil moisture
calculation

Signed-off-by: Thomas Klaehn <tkl@blackfinn.de>
2025-08-15 08:59:23 +02:00
6 changed files with 101 additions and 27 deletions

View File

@@ -82,7 +82,7 @@ func moisture() (types.Telemetry, error) {
// re-calculate to %
// 100 % := 3.0 V
// 0 % := 0.0 V
ret.Value = -0.03*ret.Value + 3
ret.Value = -33.333*ret.Value + 100
ret.Unit = "%"
return ret, nil
}

View File

@@ -9,6 +9,7 @@ import (
"sync"
"time"
apiservice_devices "waterservice/internal/apiservice/devices"
"waterservice/internal/app/storage"
)
type state string
@@ -22,6 +23,7 @@ type State struct {
}
const (
storage_path = "/var/lib/waterservice/state/state.json"
StateOn state = "on"
StateOff state = "off"
)
@@ -31,12 +33,14 @@ var (
state_cache map[string]State
state_mutex sync.Mutex
store storage.Storage
cb_register_off registerOffSwitch
cb_unregister_off registerOffSwitch
)
func init() {
logger.SetFlags(log.Llongfile | log.Ltime)
store.SetPath(storage_path)
state_cache = map[string]State{}
}
@@ -46,6 +50,10 @@ func AddHandler() {
func Start() {
state_mutex.Lock()
res, err := store.Read()
if err != nil {
logger.Print(err)
// build default state_cache
for _, device := range apiservice_devices.GetDevices().Devices {
tmp := State{
Name: device.Name,
@@ -54,6 +62,12 @@ func Start() {
}
state_cache[device.Name] = tmp
}
} else {
err = json.Unmarshal(res, &state_cache)
if err != nil {
logger.Print(err)
}
}
state_mutex.Unlock()
go poll_states()
@@ -84,6 +98,15 @@ func SetState(st State) error {
tmp := state_cache[dev.Name]
tmp.State = st.State
state_cache[dev.Name] = tmp
res, err := json.Marshal(state_cache)
if err != nil {
logger.Print("unable to store state cache")
} else {
err = store.Write(res)
if err != nil {
logger.Print(err)
}
}
state_mutex.Unlock()
if cb_register_off != nil && cb_unregister_off != nil {
if st.State == StateOn {
@@ -104,6 +127,15 @@ func SetRuntime(name string, runtime time.Duration) {
tmp := state_cache[name]
tmp.Runtime = runtime
state_cache[name] = tmp
res, err := json.Marshal(state_cache)
if err != nil {
logger.Print("unable to store state cache")
} else {
err = store.Write(res)
if err != nil {
logger.Print(err)
}
}
state_mutex.Unlock()
}
@@ -166,6 +198,15 @@ func handle_patch(r *http.Request) error {
tmp := state_cache[dev.Name]
tmp.State = st.State
state_cache[dev.Name] = tmp
res, err := json.Marshal(state_cache)
if err != nil {
logger.Print("unable to store state cache")
} else {
err = store.Write(res)
if err != nil {
logger.Print(err)
}
}
state_mutex.Unlock()
if cb_register_off != nil && cb_unregister_off != nil {
if st.State == StateOn {
@@ -213,6 +254,15 @@ func poll_states() {
tmp := state_cache[dev.Name]
tmp.State = status
state_cache[dev.Name] = tmp
res, err := json.Marshal(state_cache)
if err != nil {
logger.Print(err)
} else {
err = store.Write(res)
if err != nil {
logger.Print(err)
}
}
state_mutex.Unlock()
}
time.Sleep(time.Second * 10)

View File

@@ -27,7 +27,7 @@ func init() {
}
func SetConfigFilePath(path string) {
store.Path = path
store.SetPath(path)
res, err := store.Read()
if err != nil {
logger.Print("unable to read config")

View File

@@ -31,17 +31,17 @@ func init() {
app_state_cache.Mode = ModeManual
// override defaults with stored values
store.Path = app_state_storage_path
store.SetPath(app_state_storage_path)
res, err := store.Read()
if err != nil {
logger.Print("unable to read app state cache")
logger.Print(err)
return
}
app_state_mutex.Lock()
err = json.Unmarshal(res, &app_state_cache)
app_state_mutex.Unlock()
if err != nil {
logger.Print("unable to evaluate config data")
logger.Print(err)
}
}
@@ -50,10 +50,13 @@ func SetMode(mode AppMode) {
app_state_cache.Mode = mode
res, err := json.Marshal(app_state_cache)
if err != nil {
logger.Print("unable to store app state cache")
logger.Print(err)
return
}
store.Write(res)
err = store.Write(res)
if err != nil {
logger.Print(err)
}
app_state_mutex.Unlock()
}
@@ -77,7 +80,7 @@ func app_state_saver() {
res, err := json.Marshal(app_state_cache)
app_state_mutex.Unlock()
if err != nil {
logger.Print("unable to marshal object to json")
logger.Print(err)
time.Sleep(time.Minute)
continue
}
@@ -87,6 +90,8 @@ func app_state_saver() {
}
func poll_auto_off() {
now := time.Now()
today := time.Date(now.Year(), now.Month(), now.Day(), 23, 59, 59, 0, now.Location())
for {
app_state_mutex.Lock()
for i, od := range app_state_cache.off_devices {
@@ -115,6 +120,20 @@ func poll_auto_off() {
}
}
app_state_mutex.Unlock()
// check date change
now := time.Now()
if now.After(today) {
logger.Print("date change detected")
// reset date
today = time.Date(now.Year(), now.Month(), now.Day(), 23, 59, 59, 0, now.Location())
devices := apiservice_devices.GetDevices()
for _, d := range devices.Devices {
// reset run times
apiservice_state.SetRuntime(d.Name, 0)
}
}
time.Sleep(time.Second)
}
}
@@ -123,7 +142,6 @@ func register_off_device(dev apiservice_devices.Device) {
for _, od := range app_state_cache.off_devices {
if od.device.Name == dev.Name {
// device already in off list
// FIXME: update off time
return
}
}

View File

@@ -1,13 +1,14 @@
package storage
import (
"errors"
"log"
"os"
"path/filepath"
)
type Storage struct {
Path string
path string
}
var (
@@ -18,17 +19,24 @@ func init() {
logger.SetFlags(log.Llongfile | log.Ltime)
}
func (storage Storage) Read() ([]byte, error) {
data, err := os.ReadFile(storage.Path)
func (s *Storage) SetPath(path string) {
s.path = path
}
func (s Storage) Read() ([]byte, error) {
if len(s.path) == 0 {
return nil, errors.New("path not set")
}
data, err := os.ReadFile(s.path)
if err != nil {
logger.Printf("unable to read %s (%s)", storage.Path, err.Error())
logger.Printf("unable to read %s (%s)", s.path, err.Error())
return nil, err
}
return data, nil
}
func (storage Storage) Write(data []byte) error {
dir := filepath.Dir(storage.Path)
func (s Storage) Write(data []byte) error {
dir := filepath.Dir(s.path)
_, err := os.Stat(dir)
if err != nil {
if os.IsNotExist(err) {
@@ -42,9 +50,9 @@ func (storage Storage) Write(data []byte) error {
return err
}
}
err = os.WriteFile(storage.Path, data, 0644)
err = os.WriteFile(s.path, data, 0644)
if err != nil {
logger.Printf("unable to store %s (%s)", storage.Path, err.Error())
logger.Printf("unable to store %s (%s)", s.path, err.Error())
}
return nil
}

View File

@@ -119,13 +119,11 @@
function check(checkbox, name) {
var obj;
console.log("name: ", name);
if(checkbox.checked) {
obj = '{"name":"' + name + '","state":"on"}'
} else {
obj = '{"name":"' + name + '","state":"off"}'
}
console.log(obj);
const xhr = new XMLHttpRequest();
xhr.open("PATCH", "/state");
xhr.setRequestHeader("Content-type", "application/json; charset=utf-8");