Skip to content

Commit

Permalink
Use color picker to set color of smart bulbs
Browse files Browse the repository at this point in the history
  • Loading branch information
meisenzahl committed Aug 8, 2019
1 parent 547ed28 commit 712ed8c
Show file tree
Hide file tree
Showing 9 changed files with 304 additions and 4 deletions.
70 changes: 69 additions & 1 deletion src/colors/HSB.vala
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,72 @@
* Authored by: Marius Meisenzahl <[email protected]>
*/

public class Colors.HSB {}
public class Colors.HSB {
public uint16 hue;
public uint8 saturation;
public uint8 brightness;

public HSB () {}

public HSB.from_rgb (RGB rgb) {
double min, max, delta;
double h, s, b;

double red = rgb.red / 255.0;
double green = rgb.green / 255.0;
double blue = rgb.blue / 255.0;

min = red;
min = green < min ? green : min;
min = blue < min ? blue : min;

max = red;
max = green > max ? green : max;
max = blue > max ? blue : max;

b = max;
delta = max - min;

if (max != 0) {
s = delta / max;
} else {
s = 0;
h = 0;

hue = (uint16) (h + 0.5);
saturation = (uint8) (s * 100 + 0.5);
brightness = (uint8) (b * 100 + 0.5);

return;
}

if (max == min) {
h = 0;
s = 0;

hue = (uint16) (h + 0.5);
saturation = (uint8) (s * 100 + 0.5);
brightness = (uint8) (b * 100 + 0.5);

return;
}

if (red == max) {
h = (green - blue) / delta;
} else if (green == max) {
h = 2 + (blue - red) / delta;
} else {
h = 4 + (red - green) / delta;
}

h *= 60;

if (h < 0) {
h += 360;
}

hue = (uint16) (h + 0.5);
saturation = (uint8) (s * 100 + 0.5);
brightness = (uint8) (b * 100 + 0.5);
}
}
71 changes: 71 additions & 0 deletions src/colors/RGB.vala
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,75 @@ public class Colors.RGB {
public RGB.from_hex (string hex) {
hex.scanf ("%02x%02x%02x", &red, &green, &blue);
}

public RGB.from_hsb (HSB hsb) {
int i;
double f, p, q, t;
double r, g, b;

double hue, saturation, brightness;
hue = (double) hsb.hue;
saturation = (double) (hsb.saturation / 100.0);
brightness = (double) (hsb.brightness / 100.0);

if (saturation == 0) {
r = brightness;
g = brightness;
b = brightness;

red = (uint8) (r * 255 + 0.5);
green = (uint8) (g * 255 + 0.5);
blue = (uint8) (b * 255 + 0.5);

return;
}

hue /= 60;
i = (int) hue;
f = hue - i;
p = brightness * (1 - saturation);
q = brightness * (1 - saturation * f);
t = brightness * (1 - saturation * (1 - f));

switch (i) {
case 0:
r = brightness;
g = t;
b = p;
break;
case 1:
r = q;
g = brightness;
b = p;
break;
case 2:
r = p;
g = brightness;
b = t;
break;
case 3:
r = p;
g = q;
b = brightness;
break;
case 4:
r = t;
g = p;
b = brightness;
break;
default:
r = brightness;
g = p;
b = q;
break;
}

red = (uint8) (r * 255 + 0.5);
green = (uint8) (g * 255 + 0.5);
blue = (uint8) (b * 255 + 0.5);
}

public string to_hex () {
return "#%02x%02x%02x".printf (red, green, blue);
}
}
2 changes: 2 additions & 0 deletions src/controllers/DeviceController.vala
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ public abstract class Controllers.DeviceController : Object {

public abstract void switch_brightness (uint16 brightness);

public abstract void switch_hsb (uint16 hue, uint16 saturation, uint16 brightness);

public abstract void switch_color_temperature (uint16 color_temperature);

public abstract void switch_power (bool on);
Expand Down
9 changes: 9 additions & 0 deletions src/lifx/Controller.vala
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,15 @@ public class Lifx.Controller : Controllers.DeviceController {
lamp.brightness = brightness;
}

public override void switch_hsb (uint16 hue, uint16 saturation, uint16 brightness) {
var lamp = device as Lifx.Lamp;
service.set_color (lamp, hue, saturation, brightness, 0, 0);

lamp.hue = hue;
lamp.saturation = saturation;
lamp.brightness = brightness;
}

public override void switch_color_temperature (uint16 color_temperature) {
var lamp = device as Lifx.Lamp;
service.set_color (lamp, 0, 0, lamp.brightness, color_temperature, 0);
Expand Down
1 change: 1 addition & 0 deletions src/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ sources = [
'views/Overview.vala',
'widgets/Carousel.vala',
'widgets/CarouselItem.vala',
'widgets/ColorPicker.vala',
'widgets/IconPopover.vala',
'widgets/Overlay.vala',
'MainWindow.vala'
Expand Down
49 changes: 46 additions & 3 deletions src/pages/DevicePage.vala
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@

public class Pages.DevicePage : Pages.AbstractDevicePage {
private Controllers.DeviceController controller;
private Gtk.Scale hue_scale;
private Gtk.Scale saturation_scale;
private Gtk.Scale brightness_scale;

public DevicePage (Models.Device device) {
Object (
Expand All @@ -43,7 +46,7 @@ public class Pages.DevicePage : Pages.AbstractDevicePage {
var hue_label = new Gtk.Label (_("Hue: "));
hue_label.xalign = 1;

var hue_scale = new Gtk.Scale.with_range (
hue_scale = new Gtk.Scale.with_range (
Gtk.Orientation.HORIZONTAL, lamp.hue_min, lamp.hue_max, 1.0
);

Expand All @@ -63,7 +66,7 @@ public class Pages.DevicePage : Pages.AbstractDevicePage {
var saturation_label = new Gtk.Label (_("Saturation: "));
saturation_label.xalign = 1;

var saturation_scale = new Gtk.Scale.with_range (
saturation_scale = new Gtk.Scale.with_range (
Gtk.Orientation.HORIZONTAL, lamp.saturation_min, lamp.saturation_max, 1.0
);

Expand All @@ -85,7 +88,7 @@ public class Pages.DevicePage : Pages.AbstractDevicePage {
var brightness_label = new Gtk.Label (_("Brightness: "));
brightness_label.xalign = 1;

var brightness_scale = new Gtk.Scale.with_range (
brightness_scale = new Gtk.Scale.with_range (
Gtk.Orientation.HORIZONTAL, lamp.brightness_min, lamp.brightness_max, 1.0
);

Expand Down Expand Up @@ -124,6 +127,42 @@ public class Pages.DevicePage : Pages.AbstractDevicePage {
content_area.attach (color_temperature_label, 0, 3, 1, 1);
content_area.attach (color_temperature_scale, 1, 3, 1, 1);
}

if (lamp.supports_color) {
var c = new Colors.HSB ();
c.hue = remap_value (lamp.hue, lamp.hue_min, lamp.hue_max, 0, 360);
c.saturation = (uint8) remap_value (lamp.saturation, lamp.saturation_min, lamp.saturation_max, 0, 100);
c.brightness = (uint8) remap_value (lamp.brightness, lamp.brightness_min, lamp.brightness_max, 0, 100);

var color_picker = new Widgets.ColorPicker (MainWindow.get_default ());
color_picker.hsb = c;
color_picker.on_color_change.connect ((rgb) => {
var hsb = new Colors.HSB.from_rgb (rgb);

var hue = remap_value (hsb.hue, 0, 360, lamp.hue_min, lamp.hue_max);
var saturation = remap_value (hsb.saturation, 0, 100, lamp.saturation_min, lamp.saturation_max);
var brightness = remap_value (hsb.brightness, 0, 100, lamp.brightness_min, lamp.brightness_max);

#if DEMO_MODE
hue_scale.adjustment.value = hue;
lamp.hue = hue_scale.adjustment.value;

saturation_scale.adjustment.value = saturation;
lamp.saturation = saturation_scale.adjustment.value;

brightness_scale.adjustment.value = brightness;
lamp.brightness = brightness_scale.adjustment.value;
#else
controller.switch_hsb (hue, saturation, brightness);

hue_scale.adjustment.value = hue;
saturation_scale.adjustment.value = saturation;
brightness_scale.adjustment.value = brightness;
#endif
});

content_area.attach (color_picker, 0, 4, 1, 1);
}
}

controller.device.notify.connect (update_status);
Expand Down Expand Up @@ -178,4 +217,8 @@ public class Pages.DevicePage : Pages.AbstractDevicePage {
break;
}
}

private uint16 remap_value (uint16 value, uint16 in_min, uint16 in_max, uint16 out_min, uint16 out_max) {
return (value - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
}
9 changes: 9 additions & 0 deletions src/philips/hue/BridgeController.vala
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,15 @@ public class Philips.Hue.BridgeController {
switch_light_state (lamp, state);
}

public void switch_light_hsb (Philips.Hue.Lamp lamp, uint16 hue, uint16 saturation, uint16 brightness) {
var state = new Json.Object ();
state.set_int_member ("hue", hue);
state.set_int_member ("sat", saturation);
state.set_int_member ("bri", brightness);

switch_light_state (lamp, state);
}

public void switch_light_color_temperature (Philips.Hue.Lamp lamp, uint16 color_temperature) {
var state = new Json.Object ();
state.set_int_member ("ct", (uint16) (1000000 / color_temperature));
Expand Down
9 changes: 9 additions & 0 deletions src/philips/hue/Controller.vala
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,15 @@ public class Philips.Hue.Controller : Controllers.DeviceController {
lamp.brightness = brightness;
}

public override void switch_hsb (uint16 hue, uint16 saturation, uint16 brightness) {
var lamp = device as Philips.Hue.Lamp;
controller.switch_light_hsb (lamp, hue, saturation, brightness);

lamp.hue = hue;
lamp.saturation = saturation;
lamp.brightness = brightness;
}

public override void switch_color_temperature (uint16 color_temperature) {
var lamp = device as Philips.Hue.Lamp;
controller.switch_light_color_temperature (lamp, color_temperature);
Expand Down
88 changes: 88 additions & 0 deletions src/widgets/ColorPicker.vala
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
public class Widgets.ColorPicker: Gtk.DrawingArea {
private Gdk.RGBA color;
private Gtk.Window window;
private bool dialog_visible;

public signal void on_color_change (Colors.RGB color);

public ColorPicker (Gtk.Window window) {
this.window = window;
color.parse ("#4caf50");

set_size_request (140, 140);
add_events (Gdk.EventMask.ALL_EVENTS_MASK);

button_press_event.connect ((event) => {
if (event.button == 1 && !dialog_visible) {
var dialog = new Gtk.ColorSelectionDialog ("");
unowned Gtk.ColorSelection widget = dialog.get_color_selection ();

widget.current_rgba = color;

dialog.deletable = false;
dialog.transient_for = window;
dialog_visible = true;

if (dialog.run () == Gtk.ResponseType.OK) {
if (color != widget.current_rgba) {
color = widget.current_rgba;

var rgb = new Colors.RGB ();
rgb.red = (uint8) (color.red * 255 + 0.5);
rgb.green = (uint8) (color.green * 255 + 0.5);
rgb.blue = (uint8) (color.blue * 255 + 0.5);

on_color_change (rgb);
}
}

dialog_visible = false;
dialog.close ();
}

return true;
});
}

public override bool draw (Cairo.Context ctx) {
int width = get_allocated_width ();
int height = get_allocated_height ();

// Draw an arc:
double xc = width / 2.0;
double yc = height / 2.0;
double radius = (int.min (width, height) / 2.0);
double angle1 = 0;
double angle2 = 2 * Math.PI;

int shadow_width = 6;
double shadow_alpha = 1.0;
string shadow_color = "#A9A9A9";
for (int i = 1; i <= shadow_width; i++) {
ctx.arc (xc, yc, radius - i, angle1, angle2);
Gdk.RGBA c = Gdk.RGBA();
c.parse (shadow_color);
c.alpha = shadow_alpha / ((shadow_width - i + 1) * (shadow_width - i + 1));
Gdk.cairo_set_source_rgba (ctx, c);
ctx.stroke ();
}

ctx.arc (xc, yc, radius - shadow_width, angle1, angle2);
Gdk.cairo_set_source_rgba (ctx, color);
ctx.fill ();

return true;
}

public Colors.RGB rgb {
set {
color.parse (value.to_hex ());
}
}

public Colors.HSB hsb {
set {
color.parse (new Colors.RGB.from_hsb (value).to_hex ());
}
}
}

0 comments on commit 712ed8c

Please sign in to comment.