rework color system and make it work with drivers.
continuous-integration/drone/push Build is passing

This commit is contained in:
2022-02-21 19:27:42 +01:00
parent d5cf30ea4b
commit df865574bf
16 changed files with 468 additions and 179 deletions
+4 -4
View File
@@ -40,7 +40,7 @@ func (s *hueLightState) Update(state models.DeviceState) {
if state.Power {
input.On = ptrBool(true)
if state.Color.IsKelvin() {
input.CT = ptrInt(1000000 / state.Color.Kelvin)
input.CT = ptrInt(1000000 / *state.Color.K)
if *input.CT < s.info.Capabilities.Control.CT.Min {
*input.CT = s.info.Capabilities.Control.CT.Min
}
@@ -51,13 +51,13 @@ func (s *hueLightState) Update(state models.DeviceState) {
if s.input.CT == nil || *s.input.CT != *input.CT {
s.stale = true
}
} else {
input.Hue = ptrInt(int(state.Color.Hue*(65536/360)) % 65536)
} else if color, ok := state.Color.ToHS(); ok {
input.Hue = ptrInt(int(color.HS.Hue*(65536/360)) % 65536)
if s.input.Hue == nil || *s.input.Hue != *input.Hue {
s.stale = true
}
input.Sat = ptrInt(int(state.Color.Saturation * 255))
input.Sat = ptrInt(int(color.HS.Sat * 255))
if *input.Sat > 254 {
*input.Sat = 254
}
+10 -9
View File
@@ -269,7 +269,7 @@ func (b *Bridge) MakeCongruent(ctx context.Context) (int, error) {
lightsOut := light.Power != nil && !device.State.Power
if !lightsOut {
if light.ColorTemperature != nil && device.State.Color.IsKelvin() {
mirek := 1000000 / device.State.Color.Kelvin
mirek := 1000000 / *device.State.Color.K
if mirek < light.ColorTemperature.MirekSchema.MirekMinimum {
mirek = light.ColorTemperature.MirekSchema.MirekMinimum
}
@@ -280,9 +280,10 @@ func (b *Bridge) MakeCongruent(ctx context.Context) (int, error) {
update.Mirek = &mirek
changed = true
}
} else if xy, ok := device.State.Color.ToXY(); ok && light.Color != nil {
xy = light.Color.Gamut.Conform(xy).Round()
} else if xyColor, ok := device.State.Color.ToXY(); ok && light.Color != nil {
xy := light.Color.Gamut.Conform(*xyColor.XY).Round()
if xy.DistanceTo(light.Color.XY) > 0.0009 || (light.ColorTemperature != nil && light.ColorTemperature.Mirek != nil) {
log.Println(xyColor.String(), device.State.Color.String())
update.ColorXY = &xy
changed = true
}
@@ -419,9 +420,7 @@ func (b *Bridge) GenerateDevices() []models.Device {
}
if light.ColorTemperature != nil {
if light.ColorTemperature.Mirek != nil {
device.State.Color = models.ColorValue{
Kelvin: int(1000000 / *light.ColorTemperature.Mirek),
}
device.State.Color.SetK(1000000 / *light.ColorTemperature.Mirek)
}
device.Capabilities = append(device.Capabilities, models.DCColorKelvin)
device.DriverProperties["maxTemperature"] = 1000000 / light.ColorTemperature.MirekSchema.MirekMinimum
@@ -530,9 +529,11 @@ func (b *Bridge) applyPatches(patches []ResourceData) {
resCopy.Color = &cp
resCopy.Color.XY = patch.Color.XY
cp2 := *resCopy.ColorTemperature
resCopy.ColorTemperature = &cp2
resCopy.ColorTemperature.Mirek = nil
if resCopy.ColorTemperature != nil {
cp2 := *resCopy.ColorTemperature
resCopy.ColorTemperature = &cp2
resCopy.ColorTemperature.Mirek = nil
}
}
if patch.ColorTemperature != nil && resCopy.ColorTemperature != nil {
cp := *resCopy.ColorTemperature
+6 -4
View File
@@ -169,9 +169,11 @@ func (b *Bridge) Run(ctx context.Context, debug bool) error {
state.deviceState = &models.DeviceState{
Power: p.On,
Color: models.ColorValue{
Hue: p.Hue,
Saturation: p.Sat,
Kelvin: p.Kelvin,
HS: &models.ColorHS{
Hue: p.Hue,
Sat: p.Sat,
},
K: &p.Kelvin,
},
Intensity: p.Bri,
}
@@ -204,7 +206,7 @@ func (b *Bridge) Run(ctx context.Context, debug bool) error {
b.checkAndUpdateState(state)
}
if time.Since(state.discoveredTime) > time.Second*10 && time.Since(state.fwSpamTime) > time.Second * 30 {
if time.Since(state.discoveredTime) > time.Second*10 && time.Since(state.fwSpamTime) > time.Second*30 {
state.fwSpamTime = time.Now()
if state.firmware == nil {
+17 -8
View File
@@ -36,17 +36,21 @@ func (s *State) generateUpdate() []Payload {
return results
}
c := s.deviceState.Color
c, ok := s.deviceState.Color.ToHSK()
if !ok {
c, _ = models.ParseColorValue("hsk:0,0,4000")
}
l := s.lightState
di := s.deviceState.Intensity
k := c.Kelvin
k := *c.K
if k == 0 {
k = 4000
}
if !equalish(c.Hue, l.Hue) || !equalish(c.Saturation, l.Sat) || !equalish(di, l.Bri) || k != l.Kelvin {
if !equalish(c.HS.Hue, l.Hue) || !equalish(c.HS.Sat, l.Sat) || !equalish(di, l.Bri) || k != l.Kelvin {
results = append(results, &SetColor{
Hue: c.Hue,
Sat: c.Saturation,
Hue: c.HS.Hue,
Sat: c.HS.Sat,
Bri: di,
Kelvin: k,
TransitionTime: time.Millisecond * 150,
@@ -72,11 +76,16 @@ func (s *State) handleAck(seq uint8) {
prevLabel = s.lightState.Label
}
color, ok := s.deviceState.Color.ToHSK()
if !ok {
color, _ = models.ParseColorValue("hsk:0,0,4000")
}
s.lightState = &LightState{
Hue: s.deviceState.Color.Hue,
Sat: s.deviceState.Color.Saturation,
Hue: color.HS.Hue,
Sat: color.HS.Sat,
Bri: s.deviceState.Intensity,
Kelvin: s.deviceState.Color.Kelvin,
Kelvin: *color.K,
On: s.deviceState.Power,
Label: prevLabel,
}
+21 -9
View File
@@ -10,7 +10,6 @@ import (
"io"
"io/ioutil"
"log"
"math"
"net"
"net/http"
"strconv"
@@ -31,11 +30,12 @@ type bridge struct {
func (b *bridge) Devices() []models.Device {
results := make([]models.Device, 0, len(b.panels))
for i, panel := range b.panels {
// Find normalized RGB and intensity
red := float64(panel.ColorRGBA[0]) / 255.0
green := float64(panel.ColorRGBA[1]) / 255.0
blue := float64(panel.ColorRGBA[2]) / 255.0
hue, sat, value := colorful.LinearRgb(red, green, blue).Hsv()
hue, sat, value := colorful.Color{R: red, G: green, B: blue}.Hsv()
rgb := colorful.Hsv(hue, sat, 1)
shapeType, shapeTypeOK := shapeTypeMap[panel.ShapeType]
if !shapeTypeOK {
@@ -70,10 +70,11 @@ func (b *bridge) Devices() []models.Device {
UserProperties: nil,
State: models.DeviceState{
Power: panel.ColorRGBA[3] == 0,
Color: models.ColorValue{
Hue: math.Mod(hue, 360),
Saturation: sat,
},
Color: models.ColorValue{RGB: &models.ColorRGB{
Red: rgb.R,
Green: rgb.G,
Blue: rgb.B,
}},
Intensity: value,
Temperature: 0,
},
@@ -172,9 +173,20 @@ func (b *bridge) Update(devices []models.Device) {
for _, panel := range b.panels {
if panel.ID == uint16(id) {
if device.State.Power {
color := colorful.Hsv(device.State.Color.Hue, device.State.Color.Saturation, device.State.Intensity)
rgbColor, ok := device.State.Color.ToRGB()
if !ok {
newColor := [4]byte{255, 255, 255, 255}
if newColor != panel.ColorRGBA {
panel.update(newColor, time.Now().Add(time.Millisecond*220))
}
red, green, blue := color.RGB255()
continue
}
rgb := rgbColor.RGB.AtIntensity(device.State.Intensity)
red := byte(rgb.Red * 255.9)
green := byte(rgb.Green * 255.9)
blue := byte(rgb.Blue * 255.9)
newColor := [4]byte{red, green, blue, 255}
if newColor != panel.ColorRGBA {
panel.update(newColor, time.Now().Add(time.Millisecond*220))
+12 -12
View File
@@ -30,6 +30,7 @@ type deviceStateRecord struct {
Power bool `db:"power"`
Intensity float64 `db:"intensity"`
Temperature int `db:"temperature"`
Color string `db:"color"`
}
type devicePropertyRecord struct {
@@ -228,13 +229,14 @@ func (r *DeviceRepo) SaveMany(ctx context.Context, mode models.SaveMode, devices
if mode == 0 || mode&models.SMState != 0 {
_, err = tx.NamedExecContext(ctx, `
REPLACE INTO device_state(device_id, hue, saturation, kelvin, power, intensity, temperature)
VALUES (:device_id, :hue, :saturation, :kelvin, :power, :intensity, :temperature)
REPLACE INTO device_state(device_id, hue, saturation, kelvin, power, intensity, color, temperature)
VALUES (:device_id, :hue, :saturation, :kelvin, :power, :intensity, :color, :temperature)
`, deviceStateRecord{
DeviceID: record.ID,
Hue: device.State.Color.Hue,
Saturation: device.State.Color.Saturation,
Kelvin: device.State.Color.Kelvin,
Hue: 40,
Saturation: 0,
Kelvin: 0,
Color: device.State.Color.String(),
Power: device.State.Power,
Intensity: device.State.Intensity,
Temperature: device.State.Temperature,
@@ -373,14 +375,12 @@ func (r *DeviceRepo) populate(ctx context.Context, records []deviceRecord) ([]mo
for _, state := range states {
if state.DeviceID == record.ID {
color, _ := models.ParseColorValue(state.Color)
device.State = models.DeviceState{
Power: state.Power,
Color: models.ColorValue{
Hue: state.Hue,
Saturation: state.Saturation,
Kelvin: state.Kelvin,
},
Intensity: state.Intensity,
Power: state.Power,
Color: color,
Intensity: state.Intensity,
Temperature: state.Temperature,
}
}
+10 -11
View File
@@ -12,6 +12,7 @@ type presetRecord struct {
Hue float64 `db:"hue"`
Saturation float64 `db:"saturation"`
Kelvin int `db:"kelvin"`
Value string `db:"value"`
}
type ColorPresetRepo struct {
@@ -42,8 +43,8 @@ func (c *ColorPresetRepo) Save(ctx context.Context, preset *models.ColorPreset)
if preset.ID > 0 {
_, err := c.DBX.ExecContext(
ctx,
"UPDATE color_preset SET name = ?, hue = ?, saturation = ?, kelvin = ? WHERE id = ?",
preset.Name, preset.Value.Hue, preset.Value.Saturation, preset.Value.Kelvin, preset.ID,
"UPDATE color_preset SET name = ?, value = ? WHERE id = ?",
preset.Name, preset.Value.String(), preset.ID,
)
if err != nil {
@@ -52,8 +53,8 @@ func (c *ColorPresetRepo) Save(ctx context.Context, preset *models.ColorPreset)
} else {
rs, err := c.DBX.ExecContext(
ctx,
"INSERT INTO color_preset (name, hue, saturation, kelvin) VALUES (?, ?, ?, ?)",
preset.Name, preset.Value.Hue, preset.Value.Saturation, preset.Value.Kelvin,
"INSERT INTO color_preset (name, value, hue, saturation, kelvin) VALUES (?, ?, 0, 0, 0)",
preset.Name, preset.Value.String(),
)
if err != nil {
@@ -84,14 +85,12 @@ func (c *ColorPresetRepo) Delete(ctx context.Context, preset *models.ColorPreset
func (c *ColorPresetRepo) fromRecords(records ...presetRecord) []models.ColorPreset {
newList := make([]models.ColorPreset, len(records), len(records))
for i, record := range records {
color, _ := models.ParseColorValue(record.Value)
newList[i] = models.ColorPreset{
ID: record.ID,
Name: record.Name,
Value: models.ColorValue{
Hue: record.Hue,
Saturation: record.Saturation,
Kelvin: record.Kelvin,
},
ID: record.ID,
Name: record.Name,
Value: color,
}
}