package pooRecorder import ( "bytes" "database/sql" "encoding/json" "fmt" "net/http" "os" "time" "github.com/spf13/viper" "github.com/t-liu93/home-automation-backend/util/notion" "golang.org/x/exp/slog" ) type recordDetail struct { Status string `json:"status"` Latitude string `json:"latitude"` Longitude string `json:"longitude"` } type pooStatusHttpSensorAttributes struct { LastPoo string `json:"last_poo"` } type pooStatusHttpSensor struct { EntityId string `json:"entity_id"` State string `json:"state"` Attributes pooStatusHttpSensorAttributes `json:"attributes"` } var ( db *sql.DB ) func publishPooStatus(pooStatus pooStatusHttpSensor) { if viper.InConfig("pooRecorder.homeassistantIp") && viper.InConfig("pooRecorder.homeassistantPort") && viper.InConfig("pooRecorder.homeassistantToken") { homeAssistantIp := viper.GetString("pooRecorder.homeassistantIp") homeAssistantPort := viper.GetString("pooRecorder.homeassistantPort") url := fmt.Sprintf("http://%s:%s/api/states/%s", homeAssistantIp, homeAssistantPort, pooStatus.EntityId) payload, err := json.Marshal(pooStatus) if err != nil { slog.Warn(fmt.Sprintln("HandleRecordPoo Error marshalling poo status", err)) return } req, err := http.NewRequest("POST", url, bytes.NewBuffer(payload)) if err != nil { slog.Warn(fmt.Sprintln("HandleRecordPoo Error creating request", err)) return } req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", "Bearer "+viper.GetString("pooRecorder.homeassistantToken")) client := &http.Client{ Timeout: time.Second * 1, } resp, err := client.Do(req) if err != nil { slog.Warn(fmt.Sprintln("HandleRecordPoo Error sending request", err)) } if resp.StatusCode != http.StatusOK { slog.Warn(fmt.Sprintln("HandleRecordPoo Unexpected response status", resp.StatusCode)) } defer resp.Body.Close() } else { slog.Warn("HandleRecordPoo Home Assistant IP, port, or token not found in config file") } } func storeStatus(record recordDetail, timestamp time.Time) { tableId := viper.GetString("pooRecorder.tableId") recordDate := timestamp.Format("2006-01-02") recordTime := timestamp.Format("15:04") slog.Info(fmt.Sprintln("Recording poo", record.Status, "at", record.Latitude, record.Longitude)) go func() { header, err := notion.GetTableRows(tableId, 1, "") if err != nil { slog.Warn(fmt.Sprintln("HandleRecordPoo Failed to get table header", err)) return } if len(header) == 0 { slog.Warn("HandleRecordPoo Table header not found") return } headerId := header[0].GetID() err = notion.WriteTableRow([]string{recordDate, recordTime, record.Status, record.Latitude + "," + record.Longitude}, tableId, headerId.String()) if err != nil { slog.Warn(fmt.Sprintln("HandleRecordPoo Failed to write table row", err)) } }() } func migrateDb() { } func initDb() { if !viper.InConfig("pooRecorder.dbPath") { slog.Info("HandleRecordPoo dbPath not found in config file, using default: pooRecorder.db") viper.SetDefault("pooRecorder.dbPath", "pooRecorder.db") } dbPath := viper.GetString("pooRecorder.dbPath") err := error(nil) db, err = sql.Open("sqlite3", dbPath) if err != nil { slog.Error(fmt.Sprintln("PooRecorderInit Error opening database", err)) os.Exit(1) } err = db.Ping() if err != nil { slog.Error(fmt.Sprintln("PooRecorderInit Error pinging database", err)) os.Exit(1) } _, err = db.Exec(`CREATE TABLE IF NOT EXISTS poo_records ( timestamp TEXT PRIMARY KEY, status TEXT, latitude TEXT, longitude TEXT)`) } func Init() { initDb() } func HandleRecordPoo(w http.ResponseWriter, r *http.Request) { var record recordDetail if !viper.InConfig("pooRecorder.tableId") { slog.Warn("HandleRecordPoo Table ID not found in config file") http.Error(w, "Table ID not found in config file", http.StatusInternalServerError) return } decorder := json.NewDecoder(r.Body) decorder.DisallowUnknownFields() err := decorder.Decode(&record) if err != nil { slog.Warn(fmt.Sprintln("HandleRecordPoo Error decoding request body", err)) http.Error(w, err.Error(), http.StatusBadRequest) return } now := time.Now() storeStatus(record, now) timeAttribute := now.Format("Mon | 2006-01-02 | 15:04") pooStatus := pooStatusHttpSensor{ EntityId: "sensor.test_poo_sensor", State: record.Status, Attributes: pooStatusHttpSensorAttributes{ LastPoo: timeAttribute, }, } publishPooStatus(pooStatus) }