update more stuff
This commit is contained in:
313
datastore/datastore.go
Normal file
313
datastore/datastore.go
Normal file
@@ -0,0 +1,313 @@
|
||||
package datastore
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"iter"
|
||||
"time"
|
||||
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
// DataStore handles all database operations
|
||||
type DataStore struct {
|
||||
db *sql.DB
|
||||
}
|
||||
|
||||
// New creates a new DataStore instance
|
||||
func New(dbPath string) (*DataStore, error) {
|
||||
db, err := sql.Open("sqlite3", dbPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ds := &DataStore{db: db}
|
||||
if err := ds.initTables(); err != nil {
|
||||
db.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ds, nil
|
||||
}
|
||||
|
||||
// Close closes the database connection
|
||||
func (ds *DataStore) Close() error {
|
||||
return ds.db.Close()
|
||||
}
|
||||
|
||||
// initTables creates the necessary database tables
|
||||
func (ds *DataStore) initTables() error {
|
||||
tables := []string{
|
||||
`CREATE TABLE IF NOT EXISTS devices (
|
||||
id INTEGER PRIMARY KEY,
|
||||
name TEXT,
|
||||
unique_id TEXT UNIQUE
|
||||
)`,
|
||||
`CREATE TABLE IF NOT EXISTS sensors (
|
||||
sensor_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
protocol TEXT,
|
||||
model TEXT,
|
||||
id INTEGER,
|
||||
name TEXT,
|
||||
temperature_unique_id TEXT,
|
||||
humidity_unique_id TEXT,
|
||||
last_temperature TEXT,
|
||||
last_humidity TEXT,
|
||||
last_timestamp INTEGER,
|
||||
hidden INTEGER DEFAULT 0,
|
||||
UNIQUE(protocol, model, id)
|
||||
)`,
|
||||
`CREATE TABLE IF NOT EXISTS potential_devices (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
class TEXT,
|
||||
protocol TEXT,
|
||||
model TEXT,
|
||||
device_id TEXT,
|
||||
last_data TEXT,
|
||||
last_seen INTEGER
|
||||
)`,
|
||||
}
|
||||
|
||||
for _, table := range tables {
|
||||
if _, err := ds.db.Exec(table); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpsertDevice inserts or updates a device
|
||||
func (ds *DataStore) UpsertDevice(device *Device) error {
|
||||
_, err := ds.db.Exec(
|
||||
"INSERT OR REPLACE INTO devices (id, name, unique_id) VALUES (?, ?, ?)",
|
||||
device.ID, device.Name, device.UniqueID,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
// GetDevice retrieves a device by ID
|
||||
func (ds *DataStore) GetDevice(id int) (*Device, error) {
|
||||
device := &Device{}
|
||||
err := ds.db.QueryRow(
|
||||
"SELECT id, name, unique_id FROM devices WHERE id = ?",
|
||||
id,
|
||||
).Scan(&device.ID, &device.Name, &device.UniqueID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return device, nil
|
||||
}
|
||||
|
||||
// ListDevices returns an iterator over all devices
|
||||
func (ds *DataStore) ListDevices() iter.Seq[*Device] {
|
||||
return func(yield func(*Device) bool) {
|
||||
rows, err := ds.db.Query("SELECT id, name, unique_id FROM devices ORDER BY id")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
device := &Device{}
|
||||
if err := rows.Scan(&device.ID, &device.Name, &device.UniqueID); err != nil {
|
||||
continue
|
||||
}
|
||||
if !yield(device) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateDeviceName updates a device's name
|
||||
func (ds *DataStore) UpdateDeviceName(id int, name string) error {
|
||||
_, err := ds.db.Exec("UPDATE devices SET name = ? WHERE id = ?", name, id)
|
||||
return err
|
||||
}
|
||||
|
||||
// UpsertSensor inserts or updates a sensor
|
||||
func (ds *DataStore) UpsertSensor(sensor *Sensor) error {
|
||||
_, err := ds.db.Exec(
|
||||
`INSERT OR IGNORE INTO sensors
|
||||
(protocol, model, id, name, temperature_unique_id, humidity_unique_id)
|
||||
VALUES (?, ?, ?, ?, ?, ?)`,
|
||||
sensor.Protocol, sensor.Model, sensor.ID, sensor.Name,
|
||||
sensor.TemperatureUniqueID, sensor.HumidityUniqueID,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
// GetSensor retrieves a sensor by sensor_id
|
||||
func (ds *DataStore) GetSensor(sensorID int) (*Sensor, error) {
|
||||
sensor := &Sensor{}
|
||||
var lastTemp, lastHum sql.NullString
|
||||
var lastTs sql.NullInt64
|
||||
var hidden int
|
||||
|
||||
err := ds.db.QueryRow(
|
||||
`SELECT sensor_id, protocol, model, id, name,
|
||||
temperature_unique_id, humidity_unique_id,
|
||||
last_temperature, last_humidity, last_timestamp, hidden
|
||||
FROM sensors WHERE sensor_id = ?`,
|
||||
sensorID,
|
||||
).Scan(
|
||||
&sensor.SensorID, &sensor.Protocol, &sensor.Model, &sensor.ID, &sensor.Name,
|
||||
&sensor.TemperatureUniqueID, &sensor.HumidityUniqueID,
|
||||
&lastTemp, &lastHum, &lastTs, &hidden,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sensor.LastTemperature = lastTemp.String
|
||||
sensor.LastHumidity = lastHum.String
|
||||
sensor.LastTimestamp = lastTs.Int64
|
||||
sensor.Hidden = hidden != 0
|
||||
|
||||
return sensor, nil
|
||||
}
|
||||
|
||||
// GetSensorByIdentity retrieves a sensor by protocol, model, and id
|
||||
func (ds *DataStore) GetSensorByIdentity(protocol, model string, id int) (*Sensor, error) {
|
||||
sensor := &Sensor{}
|
||||
var lastTemp, lastHum sql.NullString
|
||||
var lastTs sql.NullInt64
|
||||
var hidden int
|
||||
|
||||
err := ds.db.QueryRow(
|
||||
`SELECT sensor_id, protocol, model, id, name,
|
||||
temperature_unique_id, humidity_unique_id,
|
||||
last_temperature, last_humidity, last_timestamp, hidden
|
||||
FROM sensors WHERE protocol = ? AND model = ? AND id = ?`,
|
||||
protocol, model, id,
|
||||
).Scan(
|
||||
&sensor.SensorID, &sensor.Protocol, &sensor.Model, &sensor.ID, &sensor.Name,
|
||||
&sensor.TemperatureUniqueID, &sensor.HumidityUniqueID,
|
||||
&lastTemp, &lastHum, &lastTs, &hidden,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sensor.LastTemperature = lastTemp.String
|
||||
sensor.LastHumidity = lastHum.String
|
||||
sensor.LastTimestamp = lastTs.Int64
|
||||
sensor.Hidden = hidden != 0
|
||||
|
||||
return sensor, nil
|
||||
}
|
||||
|
||||
// ListSensors returns an iterator over all sensors
|
||||
func (ds *DataStore) ListSensors() iter.Seq[*Sensor] {
|
||||
return func(yield func(*Sensor) bool) {
|
||||
rows, err := ds.db.Query(
|
||||
`SELECT sensor_id, protocol, model, id, name,
|
||||
temperature_unique_id, humidity_unique_id,
|
||||
last_temperature, last_humidity, last_timestamp, hidden
|
||||
FROM sensors ORDER BY sensor_id`,
|
||||
)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
sensor := &Sensor{}
|
||||
var lastTemp, lastHum sql.NullString
|
||||
var lastTs sql.NullInt64
|
||||
var hidden int
|
||||
|
||||
if err := rows.Scan(
|
||||
&sensor.SensorID, &sensor.Protocol, &sensor.Model, &sensor.ID, &sensor.Name,
|
||||
&sensor.TemperatureUniqueID, &sensor.HumidityUniqueID,
|
||||
&lastTemp, &lastHum, &lastTs, &hidden,
|
||||
); err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
sensor.LastTemperature = lastTemp.String
|
||||
sensor.LastHumidity = lastHum.String
|
||||
sensor.LastTimestamp = lastTs.Int64
|
||||
sensor.Hidden = hidden != 0
|
||||
|
||||
if !yield(sensor) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateSensorName updates a sensor's name
|
||||
func (ds *DataStore) UpdateSensorName(sensorID int, name string) error {
|
||||
_, err := ds.db.Exec("UPDATE sensors SET name = ? WHERE sensor_id = ?", name, sensorID)
|
||||
return err
|
||||
}
|
||||
|
||||
// UpdateSensorValue updates a sensor's last value
|
||||
func (ds *DataStore) UpdateSensorValue(protocol, model string, id int, dataType int, value string) error {
|
||||
column := ""
|
||||
switch dataType {
|
||||
case 1: // Temperature
|
||||
column = "last_temperature"
|
||||
case 2: // Humidity
|
||||
column = "last_humidity"
|
||||
default:
|
||||
return fmt.Errorf("unsupported data type: %d", dataType)
|
||||
}
|
||||
|
||||
query := fmt.Sprintf(
|
||||
"UPDATE sensors SET %s = ?, last_timestamp = ? WHERE protocol = ? AND model = ? AND id = ?",
|
||||
column,
|
||||
)
|
||||
_, err := ds.db.Exec(query, value, time.Now().Unix(), protocol, model, id)
|
||||
return err
|
||||
}
|
||||
|
||||
// SetSensorHidden updates a sensor's hidden status
|
||||
func (ds *DataStore) SetSensorHidden(sensorID int, hidden bool) error {
|
||||
hiddenVal := 0
|
||||
if hidden {
|
||||
hiddenVal = 1
|
||||
}
|
||||
_, err := ds.db.Exec("UPDATE sensors SET hidden = ? WHERE sensor_id = ?", hiddenVal, sensorID)
|
||||
return err
|
||||
}
|
||||
|
||||
// UpsertPotentialDevice inserts or updates a potential device
|
||||
func (ds *DataStore) UpsertPotentialDevice(device *PotentialDevice) error {
|
||||
_, err := ds.db.Exec(
|
||||
`INSERT OR REPLACE INTO potential_devices
|
||||
(class, protocol, model, device_id, last_data, last_seen)
|
||||
VALUES (?, ?, ?, ?, ?, ?)`,
|
||||
device.Class, device.Protocol, device.Model, device.DeviceID,
|
||||
device.LastData, device.LastSeen,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
// ListPotentialDevices returns an iterator over all potential devices
|
||||
func (ds *DataStore) ListPotentialDevices() iter.Seq[*PotentialDevice] {
|
||||
return func(yield func(*PotentialDevice) bool) {
|
||||
rows, err := ds.db.Query(
|
||||
`SELECT id, class, protocol, model, device_id, last_data, last_seen
|
||||
FROM potential_devices ORDER BY last_seen DESC`,
|
||||
)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
device := &PotentialDevice{}
|
||||
if err := rows.Scan(
|
||||
&device.ID, &device.Class, &device.Protocol, &device.Model,
|
||||
&device.DeviceID, &device.LastData, &device.LastSeen,
|
||||
); err != nil {
|
||||
continue
|
||||
}
|
||||
if !yield(device) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user