This commit is contained in:
+115
-91
@@ -12,13 +12,14 @@ import (
|
||||
)
|
||||
|
||||
type deviceRecord struct {
|
||||
ID int `db:"id"`
|
||||
BridgeID int `db:"bridge_id"`
|
||||
InternalID string `db:"internal_id"`
|
||||
Icon string `db:"icon"`
|
||||
Name string `db:"name"`
|
||||
Capabilities string `db:"capabilities"`
|
||||
ButtonNames string `db:"button_names"`
|
||||
ID int `db:"id"`
|
||||
BridgeID int `db:"bridge_id"`
|
||||
InternalID string `db:"internal_id"`
|
||||
Icon string `db:"icon"`
|
||||
Name string `db:"name"`
|
||||
Capabilities string `db:"capabilities"`
|
||||
ButtonNames string `db:"button_names"`
|
||||
SceneAssignmentJSON json.RawMessage `db:"scene_assignments"`
|
||||
}
|
||||
|
||||
type deviceStateRecord struct {
|
||||
@@ -117,25 +118,32 @@ func (r *DeviceRepo) FetchByReference(ctx context.Context, kind models.Reference
|
||||
return r.populate(ctx, records)
|
||||
}
|
||||
|
||||
func (r *DeviceRepo) Save(ctx context.Context, device *models.Device, mode models.SaveMode) error {
|
||||
func (r *DeviceRepo) SaveMany(ctx context.Context, mode models.SaveMode, devices []models.Device) error {
|
||||
tx, err := r.DBX.Beginx()
|
||||
if err != nil {
|
||||
return dbErr(err)
|
||||
}
|
||||
defer tx.Rollback()
|
||||
|
||||
record := deviceRecord{
|
||||
ID: device.ID,
|
||||
BridgeID: device.BridgeID,
|
||||
InternalID: device.InternalID,
|
||||
Icon: device.Icon,
|
||||
Name: device.Name,
|
||||
Capabilities: strings.Join(models.DeviceCapabilitiesToStrings(device.Capabilities), ","),
|
||||
ButtonNames: strings.Join(device.ButtonNames, ","),
|
||||
}
|
||||
for i, device := range devices {
|
||||
scenesJSON, err := json.Marshal(device.SceneAssignments)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if device.ID > 0 {
|
||||
_, err := tx.NamedExecContext(ctx, `
|
||||
record := deviceRecord{
|
||||
ID: device.ID,
|
||||
BridgeID: device.BridgeID,
|
||||
InternalID: device.InternalID,
|
||||
SceneAssignmentJSON: scenesJSON,
|
||||
Icon: device.Icon,
|
||||
Name: device.Name,
|
||||
Capabilities: strings.Join(models.DeviceCapabilitiesToStrings(device.Capabilities), ","),
|
||||
ButtonNames: strings.Join(device.ButtonNames, ","),
|
||||
}
|
||||
|
||||
if device.ID > 0 {
|
||||
_, err := tx.NamedExecContext(ctx, `
|
||||
UPDATE device SET
|
||||
internal_id = :internal_id,
|
||||
icon = :icon,
|
||||
@@ -144,98 +152,111 @@ func (r *DeviceRepo) Save(ctx context.Context, device *models.Device, mode model
|
||||
button_names = :button_names
|
||||
WHERE id=:id
|
||||
`, record)
|
||||
if err != nil {
|
||||
return dbErr(err)
|
||||
}
|
||||
|
||||
// Let's just be lazy for now, optimize later if need be.
|
||||
if mode == 0 || mode&models.SMTags != 0 {
|
||||
_, err = tx.ExecContext(ctx, "DELETE FROM device_tag WHERE device_id=?", record.ID)
|
||||
if err != nil {
|
||||
return dbErr(err)
|
||||
}
|
||||
|
||||
// Let's just be lazy for now, optimize later if need be.
|
||||
if mode == 0 || mode&models.SMTags != 0 {
|
||||
_, err = tx.ExecContext(ctx, "DELETE FROM device_tag WHERE device_id=?", record.ID)
|
||||
if err != nil {
|
||||
return dbErr(err)
|
||||
}
|
||||
}
|
||||
|
||||
if mode == 0 || mode&models.SMProperties != 0 {
|
||||
_, err = tx.ExecContext(ctx, "DELETE FROM device_property WHERE device_id=?", record.ID)
|
||||
if err != nil {
|
||||
return dbErr(err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
res, err := tx.NamedExecContext(ctx, `
|
||||
INSERT INTO device (bridge_id, internal_id, icon, name, capabilities, button_names)
|
||||
VALUES (:bridge_id, :internal_id, :icon, :name, :capabilities, :button_names)
|
||||
`, record)
|
||||
if err != nil {
|
||||
return dbErr(err)
|
||||
}
|
||||
|
||||
lastID, err := res.LastInsertId()
|
||||
if err != nil {
|
||||
return dbErr(err)
|
||||
}
|
||||
|
||||
record.ID = int(lastID)
|
||||
devices[i].ID = int(lastID)
|
||||
}
|
||||
|
||||
if mode == 0 || mode&models.SMTags != 0 {
|
||||
for _, tag := range device.Tags {
|
||||
_, err := tx.ExecContext(ctx, "INSERT INTO device_tag (device_id, tag_name) VALUES (?, ?)", record.ID, tag)
|
||||
if err != nil {
|
||||
return dbErr(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if mode == 0 || mode&models.SMProperties != 0 {
|
||||
_, err = tx.ExecContext(ctx, "DELETE FROM device_property WHERE device_id=?", record.ID)
|
||||
if err != nil {
|
||||
return dbErr(err)
|
||||
for key, value := range device.UserProperties {
|
||||
_, err := tx.ExecContext(ctx, "INSERT INTO device_property (device_id, prop_key, prop_value, is_user) VALUES (?, ?, ?, 1)",
|
||||
record.ID, key, value,
|
||||
)
|
||||
if err != nil {
|
||||
return dbErr(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
res, err := tx.NamedExecContext(ctx, `
|
||||
INSERT INTO device (bridge_id, internal_id, icon, name, capabilities, button_names)
|
||||
VALUES (:bridge_id, :internal_id, :icon, :name, :capabilities, :button_names)
|
||||
`, record)
|
||||
if err != nil {
|
||||
return dbErr(err)
|
||||
}
|
||||
|
||||
lastID, err := res.LastInsertId()
|
||||
if err != nil {
|
||||
return dbErr(err)
|
||||
}
|
||||
for key, value := range device.DriverProperties {
|
||||
j, err := json.Marshal(value)
|
||||
if err != nil {
|
||||
// Eh, it'll get filled by the driver anyway
|
||||
continue
|
||||
}
|
||||
|
||||
record.ID = int(lastID)
|
||||
device.ID = int(lastID)
|
||||
}
|
||||
|
||||
if mode == 0 || mode&models.SMTags != 0 {
|
||||
for _, tag := range device.Tags {
|
||||
_, err := tx.ExecContext(ctx, "INSERT INTO device_tag (device_id, tag_name) VALUES (?, ?)", record.ID, tag)
|
||||
if err != nil {
|
||||
return dbErr(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if mode == 0 || mode&models.SMProperties != 0 {
|
||||
for key, value := range device.UserProperties {
|
||||
_, err := tx.ExecContext(ctx, "INSERT INTO device_property (device_id, prop_key, prop_value, is_user) VALUES (?, ?, ?, 1)",
|
||||
record.ID, key, value,
|
||||
)
|
||||
if err != nil {
|
||||
return dbErr(err)
|
||||
_, err = tx.ExecContext(ctx, "INSERT INTO device_property (device_id, prop_key, prop_value, is_user) VALUES (?, ?, ?, 0)",
|
||||
record.ID, key, string(j),
|
||||
)
|
||||
if err != nil {
|
||||
// Return err here anyway, it might put the tx in a bad state to ignore it.
|
||||
return dbErr(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for key, value := range device.DriverProperties {
|
||||
j, err := json.Marshal(value)
|
||||
if err != nil {
|
||||
// Eh, it'll get filled by the driver anyway
|
||||
continue
|
||||
}
|
||||
|
||||
_, err = tx.ExecContext(ctx, "INSERT INTO device_property (device_id, prop_key, prop_value, is_user) VALUES (?, ?, ?, 0)",
|
||||
record.ID, key, string(j),
|
||||
)
|
||||
if err != nil {
|
||||
// Return err here anyway, it might put the tx in a bad state to ignore it.
|
||||
return dbErr(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if mode == 0 || mode&models.SMState != 0 {
|
||||
_, err = tx.NamedExecContext(ctx, `
|
||||
if mode == 0 || mode&models.SMState != 0 {
|
||||
_, err = tx.NamedExecContext(ctx, `
|
||||
REPLACE INTO device_state(device_id, hue, saturation, kelvin, power, intensity)
|
||||
VALUES (:device_id, :hue, :saturation, :kelvin, :power, :intensity)
|
||||
`, deviceStateRecord{
|
||||
DeviceID: record.ID,
|
||||
Hue: device.State.Color.Hue,
|
||||
Saturation: device.State.Color.Saturation,
|
||||
Kelvin: device.State.Color.Kelvin,
|
||||
Power: device.State.Power,
|
||||
Intensity: device.State.Intensity,
|
||||
})
|
||||
if err != nil {
|
||||
return dbErr(err)
|
||||
`, deviceStateRecord{
|
||||
DeviceID: record.ID,
|
||||
Hue: device.State.Color.Hue,
|
||||
Saturation: device.State.Color.Saturation,
|
||||
Kelvin: device.State.Color.Kelvin,
|
||||
Power: device.State.Power,
|
||||
Intensity: device.State.Intensity,
|
||||
})
|
||||
if err != nil {
|
||||
return dbErr(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tx.Commit()
|
||||
}
|
||||
|
||||
func (r *DeviceRepo) Save(ctx context.Context, device *models.Device, mode models.SaveMode) error {
|
||||
devices := []models.Device{*device}
|
||||
|
||||
err := r.SaveMany(ctx, mode, devices)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*device = devices[0]
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *DeviceRepo) Delete(ctx context.Context, device *models.Device) error {
|
||||
_, err := r.DBX.ExecContext(ctx, "DELETE FROM device WHERE Id=?", device.ID)
|
||||
if err != nil {
|
||||
@@ -328,12 +349,15 @@ func (r *DeviceRepo) populate(ctx context.Context, records []deviceRecord) ([]mo
|
||||
InternalID: record.InternalID,
|
||||
Icon: record.Icon,
|
||||
Name: record.Name,
|
||||
SceneAssignments: make([]models.DeviceSceneAssignment, 0, 4),
|
||||
ButtonNames: strings.Split(record.ButtonNames, ","),
|
||||
DriverProperties: make(map[string]interface{}, 8),
|
||||
UserProperties: make(map[string]string, 8),
|
||||
Tags: make([]string, 0, 8),
|
||||
}
|
||||
|
||||
_ = json.Unmarshal(record.SceneAssignmentJSON, &device.SceneAssignments)
|
||||
|
||||
if device.ButtonNames[0] == "" {
|
||||
device.ButtonNames = device.ButtonNames[:0]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
package mysql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"git.aiterp.net/lucifer/new-server/models"
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
type sceneRecord struct {
|
||||
ID int `db:"id"`
|
||||
Name string `db:"name"`
|
||||
IntervalMS int64 `db:"interval_ms"`
|
||||
RoleJSON json.RawMessage `db:"roles"`
|
||||
}
|
||||
|
||||
type SceneRepo struct {
|
||||
DBX *sqlx.DB
|
||||
}
|
||||
|
||||
func (r *SceneRepo) Find(ctx context.Context, id int) (*models.Scene, error) {
|
||||
var scene sceneRecord
|
||||
err := r.DBX.GetContext(ctx, &scene, "SELECT * FROM scene WHERE id = ?", id)
|
||||
if err != nil {
|
||||
return nil, dbErr(err)
|
||||
}
|
||||
|
||||
return r.populateOne(&scene)
|
||||
}
|
||||
|
||||
func (r *SceneRepo) FetchAll(ctx context.Context) ([]models.Scene, error) {
|
||||
scenes := make([]sceneRecord, 0, 8)
|
||||
err := r.DBX.SelectContext(ctx, &scenes, "SELECT * FROM scene")
|
||||
if err != nil {
|
||||
return nil, dbErr(err)
|
||||
}
|
||||
|
||||
return r.populate(scenes)
|
||||
}
|
||||
|
||||
func (r *SceneRepo) Save(ctx context.Context, scene *models.Scene) error {
|
||||
j, err := json.Marshal(scene.Roles)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if scene.ID > 0 {
|
||||
_, err := r.DBX.ExecContext(
|
||||
ctx,
|
||||
"UPDATE scene SET name = ?, interval_ms = ?, roles = ? WHERE id = ?",
|
||||
scene.Name, scene.IntervalMS, j, scene.ID,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return dbErr(err)
|
||||
}
|
||||
} else {
|
||||
rs, err := r.DBX.ExecContext(
|
||||
ctx,
|
||||
"INSERT INTO scene (name, interval_ms, roles) VALUES (?, ?, ?)",
|
||||
scene.Name, scene.IntervalMS, j,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return dbErr(err)
|
||||
}
|
||||
|
||||
id, err := rs.LastInsertId()
|
||||
if err != nil {
|
||||
return dbErr(err)
|
||||
}
|
||||
|
||||
scene.ID = int(id)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *SceneRepo) Delete(ctx context.Context, scene *models.Scene) error {
|
||||
_, err := r.DBX.ExecContext(ctx, "DELETE FROM scene WHERE id = ?", scene.ID)
|
||||
if err != nil {
|
||||
return dbErr(err)
|
||||
}
|
||||
|
||||
scene.ID = 0
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *SceneRepo) populateOne(record *sceneRecord) (*models.Scene, error) {
|
||||
records, err := r.populate([]sceneRecord{*record})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &records[0], nil
|
||||
}
|
||||
|
||||
func (r *SceneRepo) populate(records []sceneRecord) ([]models.Scene, error) {
|
||||
res := make([]models.Scene, 0, len(records))
|
||||
|
||||
for _, record := range records {
|
||||
scene := models.Scene{
|
||||
ID: record.ID,
|
||||
Name: record.Name,
|
||||
IntervalMS: record.IntervalMS,
|
||||
Roles: make([]models.SceneRole, 0, 8),
|
||||
}
|
||||
|
||||
err := json.Unmarshal(record.RoleJSON, &scene.Roles)
|
||||
if err != nil {
|
||||
return nil, dbErr(err)
|
||||
}
|
||||
|
||||
res = append(res, scene)
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
Reference in New Issue
Block a user