This commit is contained in:
Melora Hugues 2024-04-30 18:43:01 +02:00
parent b250a6b3c8
commit 081626b434
5 changed files with 137 additions and 14 deletions

View file

@ -60,21 +60,29 @@ type jsonConf struct {
McastGroup string `json:"multicast_group"`
SrcAddr string `json:"src_addr"`
} `json:"boot_provider"`
HomeAssistant struct {
Enabled bool `json:"enabled"`
Host string `json:"host"`
APIToken string `json:"token"`
} `json:"home_assistant"`
}
type AppConfig struct {
LogLevel logrus.Level
ServerMode ListeningMode
DataFilepath string
StaticDir string
Host string
PublicHost string
Port int
SockPath string
UPDMcastGroup string
UDPPort int
UDPIface string
UDPSrcAddr string
LogLevel logrus.Level
ServerMode ListeningMode
DataFilepath string
StaticDir string
Host string
PublicHost string
Port int
SockPath string
UPDMcastGroup string
UDPPort int
UDPIface string
UDPSrcAddr string
HomeAssistantEnabled bool
HomeAssistantHost string
HomeAssistantToken string
}
func parseLevel(lvlStr string) logrus.Level {
@ -108,6 +116,9 @@ func (ac *AppConfig) UnmarshalJSON(data []byte) error {
ac.UDPSrcAddr = jsonConf.BootProvider.SrcAddr
ac.DataFilepath = jsonConf.Storage.Path
ac.StaticDir = jsonConf.Storage.StaticDir
ac.HomeAssistantEnabled = jsonConf.HomeAssistant.Enabled
ac.HomeAssistantHost = jsonConf.HomeAssistant.Host
ac.HomeAssistantToken = jsonConf.HomeAssistant.APIToken
return nil
}

View file

@ -7,7 +7,9 @@ import (
"io"
"net/http"
"git.faercol.me/faercol/http-boot-server/bootserver/config"
"git.faercol.me/faercol/http-boot-server/bootserver/helpers"
"git.faercol.me/faercol/http-boot-server/bootserver/homeassistant"
"git.faercol.me/faercol/http-boot-server/bootserver/services"
"github.com/google/uuid"
"github.com/sirupsen/logrus"
@ -22,13 +24,15 @@ type setBootOptionPayload struct {
type BootController struct {
clientService *services.ClientHandlerService
appConf *config.AppConfig
l *logrus.Logger
}
func NewBootController(logger *logrus.Logger, service *services.ClientHandlerService) *BootController {
func NewBootController(logger *logrus.Logger, service *services.ClientHandlerService, conf *config.AppConfig) *BootController {
return &BootController{
clientService: service,
l: logger,
appConf: conf,
}
}
@ -61,6 +65,18 @@ func (bc *BootController) setBootOption(w http.ResponseWriter, r *http.Request)
return http.StatusInternalServerError, nil, fmt.Errorf("failed to set boot option for client: %w", err)
}
if bc.appConf.HomeAssistantEnabled {
bc.l.Debug("Notifying HomeAssistant of change")
newConf, err := bc.clientService.GetClientConfig(clientID)
if err != nil {
bc.l.Errorf("Failed to get new config to send to HA: %s", err.Error())
} else {
if err := homeassistant.New(bc.appConf).SendBootOption(r.Context(), newConf.Name, newConf.Options[newConf.SelectedOption].Name); err != nil {
bc.l.Errorf("Failed to notify HA: %s", err.Error())
}
}
}
return http.StatusAccepted, nil, nil
}

View file

@ -0,0 +1,80 @@
package homeassistant
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"time"
"git.faercol.me/faercol/http-boot-server/bootserver/config"
"git.faercol.me/faercol/http-boot-server/bootserver/logger"
)
type Entity struct {
State string `json:"state"`
ID string `json:"-"`
Attributes map[string]string `json:"attributes,omitempty"`
}
func newBootOptionEntity(device, option string) Entity {
return Entity{
State: option,
ID: "httpboot." + device,
Attributes: nil,
}
}
type HomeAssistantExporter struct {
clt *http.Client
baseURL string
token string
}
func New(conf *config.AppConfig) *HomeAssistantExporter {
clt := http.Client{}
return &HomeAssistantExporter{
clt: &clt,
baseURL: conf.HomeAssistantHost,
token: conf.HomeAssistantToken,
}
}
func (e *HomeAssistantExporter) SendBootOption(ctx context.Context, device string, option string) error {
subCtx, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel()
entity := newBootOptionEntity(device, option)
dat, err := json.Marshal(entity)
if err != nil {
return err
}
req, err := http.NewRequestWithContext(subCtx, http.MethodPost, e.baseURL+"/api/states/"+entity.ID, bytes.NewBuffer(dat))
if err != nil {
return err
}
req.Header.Add("Authorization", "Bearer "+e.token)
resp, err := e.clt.Do(req)
if err != nil {
return err
}
switch resp.StatusCode {
case http.StatusOK:
logger.L.Debugf("Updated boot info for device %s to %s", device, option)
case http.StatusCreated:
logger.L.Debugf("Created boot info for device %s with value %s", device, option)
default:
respBod, err := io.ReadAll(resp.Body)
if err != nil {
return err
}
return fmt.Errorf("unexpected returncode %d (%s)", resp.StatusCode, string(respBod))
}
return nil
}

View file

@ -8,6 +8,7 @@ import (
"time"
"git.faercol.me/faercol/http-boot-server/bootserver/config"
"git.faercol.me/faercol/http-boot-server/bootserver/homeassistant"
"git.faercol.me/faercol/http-boot-server/bootserver/logger"
"git.faercol.me/faercol/http-boot-server/bootserver/server"
"git.faercol.me/faercol/http-boot-server/bootserver/services"
@ -61,6 +62,21 @@ func main() {
logger.L.Fatalf("Failed to start UDP listener: %s", err.Error())
}
if conf.HomeAssistantEnabled {
logger.L.Info("Home assistant integration enabled, sending current configuration to the host")
haClt := homeassistant.New(conf)
cltSrv := services.NewClientHandlerService(conf.DataFilepath, logger.L)
clts, err := cltSrv.GetAllClientConfig()
if err != nil {
logger.L.Fatalf("Failed to get current clients from the storage: %s", err.Error())
}
for _, c := range clts {
if err := haClt.SendBootOption(context.Background(), c.Name, c.Options[c.SelectedOption].Name); err != nil {
logger.L.Errorf("Failed to send config to homeassistant: %s", err.Error())
}
}
}
go s.Run(mainCtx)
go listener.Run(mainCtx)

View file

@ -69,7 +69,7 @@ func New(appConf *config.AppConfig, logger *logrus.Logger) (*Server, error) {
controllers := map[string]http.Handler{
client.EnrollRoute: middlewares.WithLogger(client.NewEnrollController(logger, service, appConf.UDPPort, appConf.UPDMcastGroup), logger),
client.ConfigRoute: middlewares.WithLogger(client.NewGetConfigController(logger, service, appConf), logger),
client.SetBootRoute: middlewares.WithLogger(client.NewBootController(logger, service), logger),
client.SetBootRoute: middlewares.WithLogger(client.NewBootController(logger, service, appConf), logger),
ui.StaticRoute: middlewares.WithLogger(ui.NewStaticController(appConf.StaticDir), logger),
ui.UIRoute: middlewares.WithLogger(ui.NewUIController(logger, service, appConf.StaticDir), logger),
}