diff --git a/gallery/src/pages/components/ha-hs-color-picker.markdown b/gallery/src/pages/components/ha-hs-color-picker.markdown
new file mode 100644
index 0000000000..ec37f2ad97
--- /dev/null
+++ b/gallery/src/pages/components/ha-hs-color-picker.markdown
@@ -0,0 +1,3 @@
+---
+title: HS Color Picker
+---
diff --git a/gallery/src/pages/components/ha-hs-color-picker.ts b/gallery/src/pages/components/ha-hs-color-picker.ts
new file mode 100644
index 0000000000..53eaf21876
--- /dev/null
+++ b/gallery/src/pages/components/ha-hs-color-picker.ts
@@ -0,0 +1,120 @@
+import "../../../../src/components/ha-hs-color-picker";
+
+import { css, html, LitElement, TemplateResult } from "lit";
+import { customElement, state } from "lit/decorators";
+
+import "../../../../src/components/ha-card";
+import "../../../../src/components/ha-slider";
+import { hsv2rgb } from "../../../../src/common/color/convert-color";
+
+@customElement("demo-components-ha-hs-color-picker")
+export class DemoHaHsColorPicker extends LitElement {
+ @state()
+ brightness = 255;
+
+ @state()
+ value: [number, number] = [0, 0];
+
+ @state()
+ liveValue?: [number, number];
+
+ private _brightnessChanged(ev) {
+ this.brightness = Number(ev.target.value);
+ }
+
+ private _hsColorCursor(ev) {
+ this.liveValue = ev.detail.value;
+ }
+
+ private _hsColorChanged(ev) {
+ this.value = ev.detail.value;
+ }
+
+ private _hueChanged(ev) {
+ this.value = [ev.target.value, this.value[1]];
+ }
+
+ private _saturationChanged(ev) {
+ this.value = [this.value[0], ev.target.value];
+ }
+
+ protected render(): TemplateResult {
+ const h = (this.liveValue ?? this.value)[0];
+ const s = (this.liveValue ?? this.value)[1];
+
+ const rgb = hsv2rgb([h, s, this.brightness]);
+
+ return html`
+
+
+
${h}° - ${Math.round(s * 100)}%
+
${rgb.map((v) => Math.round(v)).join(", ")}
+
+
Hue : ${this.value[0]}
+
+
+
Saturation : ${this.value[1]}
+
+
+
Color Brighness : ${this.brightness}
+
+
+
+
+ `;
+ }
+
+ static get styles() {
+ return css`
+ ha-card {
+ max-width: 600px;
+ margin: 24px auto;
+ }
+ .card-content {
+ display: flex;
+ align-items: center;
+ flex-direction: column;
+ }
+ ha-hs-color-picker {
+ width: 400px;
+ }
+ .value {
+ font-size: 22px;
+ font-weight: bold;
+ margin: 0 0 12px 0;
+ }
+ `;
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "demo-components-ha-hs-color-picker": DemoHaHsColorPicker;
+ }
+}
diff --git a/gallery/src/pages/components/ha-temp-color-picker.markdown b/gallery/src/pages/components/ha-temp-color-picker.markdown
new file mode 100644
index 0000000000..3221e55ee8
--- /dev/null
+++ b/gallery/src/pages/components/ha-temp-color-picker.markdown
@@ -0,0 +1,3 @@
+---
+title: Temp Color Picker
+---
diff --git a/gallery/src/pages/components/ha-temp-color-picker.ts b/gallery/src/pages/components/ha-temp-color-picker.ts
new file mode 100644
index 0000000000..d924e745bf
--- /dev/null
+++ b/gallery/src/pages/components/ha-temp-color-picker.ts
@@ -0,0 +1,117 @@
+import "../../../../src/components/ha-temp-color-picker";
+
+import { css, html, LitElement, TemplateResult } from "lit";
+import { customElement, state } from "lit/decorators";
+
+import "../../../../src/components/ha-card";
+import "../../../../src/components/ha-slider";
+
+@customElement("demo-components-ha-temp-color-picker")
+export class DemoHaTempColorPicker extends LitElement {
+ @state()
+ min = 3000;
+
+ @state()
+ max = 7000;
+
+ @state()
+ value = 4000;
+
+ @state()
+ liveValue?: number;
+
+ private _minChanged(ev) {
+ this.min = Number(ev.target.value);
+ }
+
+ private _maxChanged(ev) {
+ this.max = Number(ev.target.value);
+ }
+
+ private _valueChanged(ev) {
+ this.value = Number(ev.target.value);
+ }
+
+ private _tempColorCursor(ev) {
+ this.liveValue = ev.detail.value;
+ }
+
+ private _tempColorChanged(ev) {
+ this.value = ev.detail.value;
+ }
+
+ protected render(): TemplateResult {
+ return html`
+
+
+
${this.liveValue ?? this.value} K
+
+
Min temp : ${this.min} K
+
+
+
Max temp : ${this.max} K
+
+
+
Value : ${this.value} K
+
+
+
+
+ `;
+ }
+
+ static get styles() {
+ return css`
+ ha-card {
+ max-width: 600px;
+ margin: 24px auto;
+ }
+ .card-content {
+ display: flex;
+ align-items: center;
+ flex-direction: column;
+ }
+ ha-temp-color-picker {
+ width: 400px;
+ }
+ .value {
+ font-size: 22px;
+ font-weight: bold;
+ margin: 0 0 12px 0;
+ }
+ `;
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "demo-components-ha-temp-color-picker": DemoHaTempColorPicker;
+ }
+}
diff --git a/src/components/ha-color-picker.js b/src/components/ha-color-picker.js
deleted file mode 100644
index f94018ab71..0000000000
--- a/src/components/ha-color-picker.js
+++ /dev/null
@@ -1,644 +0,0 @@
-import { html } from "@polymer/polymer/lib/utils/html-tag";
-/* eslint-plugin-disable lit */
-import { PolymerElement } from "@polymer/polymer/polymer-element";
-import { hs2rgb, rgb2hs } from "../common/color/convert-color";
-import { EventsMixin } from "../mixins/events-mixin";
-/**
- * Color-picker custom element
- *
- * @appliesMixin EventsMixin
- */
-class HaColorPicker extends EventsMixin(PolymerElement) {
- static get template() {
- return html`
-
-
-
-
-
- `;
- }
-
- static get properties() {
- return {
- hsColor: {
- type: Object,
- },
-
- // use these properties to update the state via attributes
- desiredHsColor: {
- type: Object,
- observer: "applyHsColor",
- },
-
- // use these properties to update the state via attributes
- desiredRgbColor: {
- type: Object,
- observer: "applyRgbColor",
- },
-
- // width, height and radius apply to the coordinates of
- // of the canvas.
- // border width are relative to these numbers
- // the onscreen displayed size should be controlled with css
- // and should be the same or smaller
- width: {
- type: Number,
- value: 500,
- },
-
- height: {
- type: Number,
- value: 500,
- },
-
- radius: {
- type: Number,
- value: 225,
- },
-
- // the amount segments for the hue
- // 0 = continuous gradient
- // other than 0 gives 'pie-pieces'
- hueSegments: {
- type: Number,
- value: 0,
- observer: "segmentationChange",
- },
-
- // the amount segments for the hue
- // 0 = continuous gradient
- // 1 = only fully saturated
- // > 1 = segments from white to fully saturated
- saturationSegments: {
- type: Number,
- value: 0,
- observer: "segmentationChange",
- },
-
- // set to true to make the segments purely esthetical
- // this allows selection off all collors, also
- // interpolated between the segments
- ignoreSegments: {
- type: Boolean,
- value: false,
- },
-
- // throttle te amount of 'colorselected' events fired
- // value is timeout in milliseconds
- throttle: {
- type: Number,
- value: 500,
- },
- };
- }
-
- ready() {
- super.ready();
- this.setupLayers();
- this.drawColorWheel();
- this.drawMarker();
-
- if (this.desiredHsColor) {
- this.applyHsColor(this.desiredHsColor);
- }
-
- if (this.desiredRgbColor) {
- this.applyRgbColor(this.desiredRgbColor);
- }
-
- this.interactionLayer.addEventListener("mousedown", (ev) =>
- this.onMouseDown(ev)
- );
- this.interactionLayer.addEventListener("touchstart", (ev) =>
- this.onTouchStart(ev)
- );
- }
-
- // converts browser coordinates to canvas canvas coordinates
- // origin is wheel center
- // returns {x: X, y: Y} object
- convertToCanvasCoordinates(clientX, clientY) {
- const svgPoint = this.interactionLayer.createSVGPoint();
- svgPoint.x = clientX;
- svgPoint.y = clientY;
- const cc = svgPoint.matrixTransform(
- this.interactionLayer.getScreenCTM().inverse()
- );
- return { x: cc.x, y: cc.y };
- }
-
- // Mouse events
-
- onMouseDown(ev) {
- const cc = this.convertToCanvasCoordinates(ev.clientX, ev.clientY);
- // return if we're not on the wheel
- if (!this.isInWheel(cc.x, cc.y)) {
- return;
- }
- // a mousedown in wheel is always a color select action
- this.onMouseSelect(ev);
- // allow dragging
- this.canvas.classList.add("mouse", "dragging");
- this.addEventListener("mousemove", this.onMouseSelect);
- this.addEventListener("mouseup", this.onMouseUp);
- }
-
- onMouseUp() {
- this.canvas.classList.remove("mouse", "dragging");
- this.removeEventListener("mousemove", this.onMouseSelect);
- }
-
- onMouseSelect(ev) {
- requestAnimationFrame(() => this.processUserSelect(ev));
- }
-
- // Touch events
-
- onTouchStart(ev) {
- const touch = ev.changedTouches[0];
- const cc = this.convertToCanvasCoordinates(touch.clientX, touch.clientY);
- // return if we're not on the wheel
- if (!this.isInWheel(cc.x, cc.y)) {
- return;
- }
- if (ev.target === this.marker) {
- // drag marker
- ev.preventDefault();
- this.canvas.classList.add("touch", "dragging");
- this.addEventListener("touchmove", this.onTouchSelect);
- this.addEventListener("touchend", this.onTouchEnd);
- return;
- }
- // don't fire color selection immediately,
- // wait for touchend and invalidate when we scroll
- this.tapBecameScroll = false;
- this.addEventListener("touchend", this.onTap);
- this.addEventListener(
- "touchmove",
- () => {
- this.tapBecameScroll = true;
- },
- { passive: true }
- );
- }
-
- onTap(ev) {
- if (this.tapBecameScroll) {
- return;
- }
- ev.preventDefault();
- this.onTouchSelect(ev);
- }
-
- onTouchEnd() {
- this.canvas.classList.remove("touch", "dragging");
- this.removeEventListener("touchmove", this.onTouchSelect);
- }
-
- onTouchSelect(ev) {
- requestAnimationFrame(() => this.processUserSelect(ev.changedTouches[0]));
- }
-
- /*
- * General event/selection handling
- */
-
- // Process user input to color
- processUserSelect(ev) {
- const canvasXY = this.convertToCanvasCoordinates(ev.clientX, ev.clientY);
- const hs = this.getColor(canvasXY.x, canvasXY.y);
- let rgb;
- if (!this.isInWheel(canvasXY.x, canvasXY.y)) {
- const [r, g, b] = hs2rgb([hs.h, hs.s]);
- rgb = { r, g, b };
- } else {
- rgb = this.getRgbColor(canvasXY.x, canvasXY.y);
- }
- this.onColorSelect(hs, rgb);
- }
-
- // apply color to marker position and canvas
- onColorSelect(hs, rgb) {
- this.setMarkerOnColor(hs); // marker always follows mouse 'raw' hs value (= mouse position)
- if (!this.ignoreSegments) {
- // apply segments if needed
- hs = this.applySegmentFilter(hs);
- }
- // always apply the new color to the interface / canvas
- this.applyColorToCanvas(hs);
- // throttling is applied to updating the exposed colors (properties)
- // and firing of events
- if (this.colorSelectIsThrottled) {
- // make sure we apply the last selected color
- // eventually after throttle limit has passed
- clearTimeout(this.ensureFinalSelect);
- this.ensureFinalSelect = setTimeout(() => {
- this.fireColorSelected(hs, rgb); // do it for the final time
- }, this.throttle);
- return;
- }
- this.fireColorSelected(hs, rgb); // do it
- this.colorSelectIsThrottled = true;
- setTimeout(() => {
- this.colorSelectIsThrottled = false;
- }, this.throttle);
- }
-
- // set color values and fire colorselected event
- fireColorSelected(hs, rgb) {
- this.hsColor = hs;
- this.fire("colorselected", { hs, rgb });
- }
-
- /*
- * Interface updating
- */
-
- // set marker position to the given color
- setMarkerOnColor(hs) {
- if (!this.marker || !this.tooltip) {
- return;
- }
- const dist = hs.s * this.radius;
- const theta = ((hs.h - 180) / 180) * Math.PI;
- const markerdX = -dist * Math.cos(theta);
- const markerdY = -dist * Math.sin(theta);
- const translateString = `translate(${markerdX},${markerdY})`;
- this.marker.setAttribute("transform", translateString);
- this.tooltip.setAttribute("transform", translateString);
- }
-
- // apply given color to interface elements
- applyColorToCanvas(hs) {
- if (!this.interactionLayer) {
- return;
- }
- // we're not really converting hs to hsl here, but we keep it cheap
- // setting the color on the interactionLayer, the svg elements can inherit
- this.interactionLayer.style.color = `hsl(${hs.h}, 100%, ${
- 100 - hs.s * 50
- }%)`;
- }
-
- applyHsColor(hs) {
- // do nothing is we already have the same color
- if (this.hsColor && this.hsColor.h === hs.h && this.hsColor.s === hs.s) {
- return;
- }
- this.setMarkerOnColor(hs); // marker is always set on 'raw' hs position
- if (!this.ignoreSegments) {
- // apply segments if needed
- hs = this.applySegmentFilter(hs);
- }
- this.hsColor = hs;
- // always apply the new color to the interface / canvas
- this.applyColorToCanvas(hs);
- }
-
- applyRgbColor(rgb) {
- const [h, s] = rgb2hs(rgb);
- this.applyHsColor({ h, s });
- }
-
- /*
- * input processing helpers
- */
-
- // get angle (degrees)
- getAngle(dX, dY) {
- const theta = Math.atan2(-dY, -dX); // radians from the left edge, clockwise = positive
- const angle = (theta / Math.PI) * 180 + 180; // degrees, clockwise from right
- return angle;
- }
-
- // returns true when coordinates are in the colorwheel
- isInWheel(x, y) {
- return this.getDistance(x, y) <= 1;
- }
-
- // returns distance from wheel center, 0 = center, 1 = edge, >1 = outside
- getDistance(dX, dY) {
- return Math.sqrt(dX * dX + dY * dY) / this.radius;
- }
-
- /*
- * Getting colors
- */
-
- getColor(x, y) {
- const hue = this.getAngle(x, y); // degrees, clockwise from right
- const relativeDistance = this.getDistance(x, y); // edge of radius = 1
- const sat = Math.min(relativeDistance, 1); // Distance from center
- return { h: hue, s: sat };
- }
-
- getRgbColor(x, y) {
- // get current pixel
- const imageData = this.backgroundLayer
- .getContext("2d")
- .getImageData(x + 250, y + 250, 1, 1);
- const pixel = imageData.data;
- return { r: pixel[0], g: pixel[1], b: pixel[2] };
- }
-
- applySegmentFilter(hs) {
- // apply hue segment steps
- if (this.hueSegments) {
- const angleStep = 360 / this.hueSegments;
- const halfAngleStep = angleStep / 2;
- hs.h -= halfAngleStep; // take the 'centered segemnts' into account
- if (hs.h < 0) {
- hs.h += 360;
- } // don't end up below 0
- const rest = hs.h % angleStep;
- hs.h -= rest - angleStep;
- }
-
- // apply saturation segment steps
- if (this.saturationSegments) {
- if (this.saturationSegments === 1) {
- hs.s = 1;
- } else {
- const segmentSize = 1 / this.saturationSegments;
- const saturationStep = 1 / (this.saturationSegments - 1);
- const calculatedSat = Math.floor(hs.s / segmentSize) * saturationStep;
- hs.s = Math.min(calculatedSat, 1);
- }
- }
- return hs;
- }
-
- /*
- * Drawing related stuff
- */
-
- setupLayers() {
- this.canvas = this.$.canvas;
- this.backgroundLayer = this.$.backgroundLayer;
- this.interactionLayer = this.$.interactionLayer;
-
- // coordinate origin position (center of the wheel)
- this.originX = this.width / 2;
- this.originY = this.originX;
-
- // synchronise width/height coordinates
- this.backgroundLayer.width = this.width;
- this.backgroundLayer.height = this.height;
- this.interactionLayer.setAttribute(
- "viewBox",
- `${-this.originX} ${-this.originY} ${this.width} ${this.height}`
- );
- }
-
- drawColorWheel() {
- /*
- * Setting up all paremeters
- */
- let shadowColor;
- let shadowOffsetX;
- let shadowOffsetY;
- let shadowBlur;
- const context = this.backgroundLayer.getContext("2d");
- // postioning and sizing
- const cX = this.originX;
- const cY = this.originY;
- const radius = this.radius;
- const counterClockwise = false;
- // styling of the wheel
- const wheelStyle = window.getComputedStyle(this.backgroundLayer, null);
- const borderWidth = parseInt(
- wheelStyle.getPropertyValue("--wheel-borderwidth"),
- 10
- );
- const borderColor = wheelStyle
- .getPropertyValue("--wheel-bordercolor")
- .trim();
- const wheelShadow = wheelStyle.getPropertyValue("--wheel-shadow").trim();
- // extract shadow properties from CSS variable
- // the shadow should be defined as: "10px 5px 5px 0px COLOR"
- if (wheelShadow !== "none") {
- const values = wheelShadow.split("px ");
- shadowColor = values.pop();
- shadowOffsetX = parseInt(values[0], 10);
- shadowOffsetY = parseInt(values[1], 10);
- shadowBlur = parseInt(values[2], 10) || 0;
- }
- const borderRadius = radius + borderWidth / 2;
- const wheelRadius = radius;
- const shadowRadius = radius + borderWidth;
-
- /*
- * Drawing functions
- */
- function drawCircle(hueSegments, saturationSegments) {
- hueSegments = hueSegments || 360; // reset 0 segments to 360
- const angleStep = 360 / hueSegments;
- const halfAngleStep = angleStep / 2; // center segments on color
- for (let angle = 0; angle <= 360; angle += angleStep) {
- const startAngle = (angle - halfAngleStep) * (Math.PI / 180);
- const endAngle = (angle + halfAngleStep + 1) * (Math.PI / 180);
- context.beginPath();
- context.moveTo(cX, cY);
- context.arc(
- cX,
- cY,
- wheelRadius,
- startAngle,
- endAngle,
- counterClockwise
- );
- context.closePath();
- // gradient
- const gradient = context.createRadialGradient(
- cX,
- cY,
- 0,
- cX,
- cY,
- wheelRadius
- );
- let lightness = 100;
- // first gradient stop
- gradient.addColorStop(0, `hsl(${angle}, 100%, ${lightness}%)`);
- // segment gradient stops
- if (saturationSegments > 0) {
- const ratioStep = 1 / saturationSegments;
- let ratio = 0;
- for (let stop = 1; stop < saturationSegments; stop += 1) {
- const prevLighness = lightness;
- ratio = stop * ratioStep;
- lightness = 100 - 50 * ratio;
- gradient.addColorStop(
- ratio,
- `hsl(${angle}, 100%, ${prevLighness}%)`
- );
- gradient.addColorStop(ratio, `hsl(${angle}, 100%, ${lightness}%)`);
- }
- gradient.addColorStop(ratio, `hsl(${angle}, 100%, 50%)`);
- }
- // last gradient stop
- gradient.addColorStop(1, `hsl(${angle}, 100%, 50%)`);
-
- context.fillStyle = gradient;
- context.fill();
- }
- }
-
- function drawShadow() {
- context.save();
- context.beginPath();
- context.arc(cX, cY, shadowRadius, 0, 2 * Math.PI, false);
- context.shadowColor = shadowColor;
- context.shadowOffsetX = shadowOffsetX;
- context.shadowOffsetY = shadowOffsetY;
- context.shadowBlur = shadowBlur;
- context.fillStyle = "white";
- context.fill();
- context.restore();
- }
-
- function drawBorder() {
- context.beginPath();
- context.arc(cX, cY, borderRadius, 0, 2 * Math.PI, false);
- context.lineWidth = borderWidth;
- context.strokeStyle = borderColor;
- context.stroke();
- }
-
- /*
- * Call the drawing functions
- * draws the shadow, wheel and border
- */
- if (wheelStyle.shadow !== "none") {
- drawShadow();
- }
- drawCircle(this.hueSegments, this.saturationSegments);
- if (borderWidth > 0) {
- drawBorder();
- }
- }
-
- /*
- * Draw the (draggable) marker and tooltip
- * on the interactionLayer)
- */
-
- drawMarker() {
- const svgElement = this.interactionLayer;
- const markerradius = this.radius * 0.08;
- const tooltipradius = this.radius * 0.15;
- const TooltipOffsetY = -(tooltipradius * 3);
- const TooltipOffsetX = 0;
-
- svgElement.marker = document.createElementNS(
- "http://www.w3.org/2000/svg",
- "circle"
- );
- svgElement.marker.setAttribute("id", "marker");
- svgElement.marker.setAttribute("r", markerradius);
- this.marker = svgElement.marker;
- svgElement.appendChild(svgElement.marker);
-
- svgElement.tooltip = document.createElementNS(
- "http://www.w3.org/2000/svg",
- "circle"
- );
- svgElement.tooltip.setAttribute("id", "colorTooltip");
- svgElement.tooltip.setAttribute("r", tooltipradius);
- svgElement.tooltip.setAttribute("cx", TooltipOffsetX);
- svgElement.tooltip.setAttribute("cy", TooltipOffsetY);
- this.tooltip = svgElement.tooltip;
- svgElement.appendChild(svgElement.tooltip);
- }
-
- segmentationChange() {
- if (this.backgroundLayer) {
- this.drawColorWheel();
- }
- }
-}
-customElements.define("ha-color-picker", HaColorPicker);
diff --git a/src/components/ha-hs-color-picker.ts b/src/components/ha-hs-color-picker.ts
new file mode 100644
index 0000000000..33fa5977f2
--- /dev/null
+++ b/src/components/ha-hs-color-picker.ts
@@ -0,0 +1,329 @@
+import { DIRECTION_ALL, Manager, Pan, Tap } from "@egjs/hammerjs";
+import { css, html, LitElement, PropertyValues, svg } from "lit";
+import { customElement, property, query, state } from "lit/decorators";
+import { classMap } from "lit/directives/class-map";
+import { styleMap } from "lit/directives/style-map";
+import { hsv2rgb, rgb2hex } from "../common/color/convert-color";
+import { fireEvent } from "../common/dom/fire_event";
+
+function xy2polar(x: number, y: number) {
+ const r = Math.sqrt(x * x + y * y);
+ const phi = Math.atan2(y, x);
+ return [r, phi];
+}
+
+function polar2xy(r: number, phi: number) {
+ const x = Math.cos(phi) * r;
+ const y = Math.sin(phi) * r;
+ return [x, y];
+}
+
+function rad2deg(rad: number) {
+ return (rad / (2 * Math.PI)) * 360;
+}
+
+function deg2rad(deg: number) {
+ return (deg / 360) * 2 * Math.PI;
+}
+
+function drawColorWheel(ctx: CanvasRenderingContext2D, colorBrightness = 255) {
+ const radius = ctx.canvas.width / 2;
+
+ ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
+ ctx.beginPath();
+
+ const cX = ctx.canvas.width / 2;
+ const cY = ctx.canvas.width / 2;
+ for (let angle = 0; angle < 360; angle += 1) {
+ const startAngle = deg2rad(angle - 0.5);
+ const endAngle = deg2rad(angle + 1.5);
+
+ ctx.beginPath();
+ ctx.moveTo(cX, cY);
+ ctx.arc(cX, cY, radius, startAngle, endAngle);
+ ctx.closePath();
+
+ const gradient = ctx.createRadialGradient(cX, cY, 0, cX, cY, radius);
+ const start = rgb2hex(hsv2rgb([angle, 0, colorBrightness]));
+ const end = rgb2hex(hsv2rgb([angle, 1, colorBrightness]));
+ gradient.addColorStop(0, start);
+ gradient.addColorStop(1, end);
+ ctx.fillStyle = gradient;
+ ctx.fill();
+ }
+}
+
+@customElement("ha-hs-color-picker")
+class HaHsColorPicker extends LitElement {
+ @property({ type: Boolean, reflect: true })
+ public disabled = false;
+
+ @property({ type: Number, attribute: false })
+ public renderSize?: number;
+
+ @property({ type: Number })
+ public value?: [number, number];
+
+ @property({ type: Number })
+ public colorBrightness?: number;
+
+ @query("#canvas") private _canvas!: HTMLCanvasElement;
+
+ private _mc?: HammerManager;
+
+ @state()
+ private _pressed?: string;
+
+ @state()
+ private _cursorPosition?: [number, number];
+
+ @state()
+ private _localValue?: [number, number];
+
+ protected firstUpdated(changedProps: PropertyValues): void {
+ super.firstUpdated(changedProps);
+ this._setupListeners();
+ this._generateColorWheel();
+ }
+
+ private _generateColorWheel() {
+ const ctx = this._canvas.getContext("2d")!;
+ drawColorWheel(ctx, this.colorBrightness);
+ }
+
+ connectedCallback(): void {
+ super.connectedCallback();
+ this._setupListeners();
+ }
+
+ disconnectedCallback(): void {
+ super.disconnectedCallback();
+ this._destroyListeners();
+ }
+
+ protected updated(changedProps: PropertyValues): void {
+ super.updated(changedProps);
+ if (changedProps.has("colorBrightness")) {
+ this._generateColorWheel();
+ }
+ if (changedProps.has("value")) {
+ if (
+ this.value !== undefined &&
+ (this._localValue?.[0] !== this.value[0] ||
+ this._localValue?.[1] !== this.value[1])
+ ) {
+ this._resetPosition();
+ }
+ }
+ }
+
+ _setupListeners() {
+ if (this._canvas && !this._mc) {
+ this._mc = new Manager(this._canvas);
+ this._mc.add(
+ new Pan({
+ direction: DIRECTION_ALL,
+ enable: true,
+ })
+ );
+
+ this._mc.add(new Tap({ event: "singletap" }));
+
+ let savedPosition;
+ this._mc.on("panstart", (e) => {
+ if (this.disabled) return;
+ this._pressed = e.pointerType;
+ savedPosition = this._cursorPosition;
+ });
+ this._mc.on("pancancel", () => {
+ if (this.disabled) return;
+ this._pressed = undefined;
+ this._cursorPosition = savedPosition;
+ });
+ this._mc.on("panmove", (e) => {
+ if (this.disabled) return;
+ this._cursorPosition = this._getPositionFromEvent(e);
+ this._localValue = this._getValueFromCoord(...this._cursorPosition);
+ fireEvent(this, "cursor-moved", { value: this._localValue });
+ });
+ this._mc.on("panend", (e) => {
+ if (this.disabled) return;
+ this._pressed = undefined;
+ this._cursorPosition = this._getPositionFromEvent(e);
+ this._localValue = this._getValueFromCoord(...this._cursorPosition);
+ fireEvent(this, "cursor-moved", { value: undefined });
+ fireEvent(this, "value-changed", { value: this._localValue });
+ });
+
+ this._mc.on("singletap", (e) => {
+ if (this.disabled) return;
+ this._cursorPosition = this._getPositionFromEvent(e);
+ this._localValue = this._getValueFromCoord(...this._cursorPosition);
+ fireEvent(this, "value-changed", { value: this._localValue });
+ });
+ }
+ }
+
+ private _resetPosition() {
+ if (this.value === undefined) return;
+ this._cursorPosition = this._getCoordsFromValue(this.value);
+ this._localValue = this.value;
+ }
+
+ private _getCoordsFromValue = (value: [number, number]): [number, number] => {
+ const phi = deg2rad(value[0]);
+
+ const r = Math.min(value[1], 1);
+
+ const [x, y] = polar2xy(r, phi);
+
+ return [x, y];
+ };
+
+ private _getValueFromCoord = (x: number, y: number): [number, number] => {
+ const [r, phi] = xy2polar(x, y);
+
+ const deg = Math.round(rad2deg(phi)) % 360;
+
+ const hue = (deg + 360) % 360;
+ const saturation = Math.round(Math.min(r, 1) * 100) / 100;
+
+ return [hue, saturation];
+ };
+
+ private _getPositionFromEvent = (e: HammerInput): [number, number] => {
+ const x = e.center.x;
+ const y = e.center.y;
+ const boundingRect = e.target.getBoundingClientRect();
+ const offsetX = boundingRect.left;
+ const offsetY = boundingRect.top;
+ const maxX = e.target.clientWidth;
+ const maxY = e.target.clientHeight;
+
+ const _x = (2 * (x - offsetX)) / maxX - 1;
+ const _y = (2 * (y - offsetY)) / maxY - 1;
+
+ const [r, phi] = xy2polar(_x, _y);
+ const [__x, __y] = polar2xy(Math.min(1, r), phi);
+ return [__x, __y];
+ };
+
+ _destroyListeners() {
+ if (this._mc) {
+ this._mc.destroy();
+ this._mc = undefined;
+ }
+ }
+
+ render() {
+ const size = this.renderSize || 400;
+ const canvasSize = size * window.devicePixelRatio;
+
+ const rgb =
+ this._localValue !== undefined
+ ? hsv2rgb([
+ this._localValue[0],
+ this._localValue[1],
+ this.colorBrightness ?? 255,
+ ])
+ : ([255, 255, 255] as [number, number, number]);
+
+ const [x, y] = this._cursorPosition ?? [0, 0];
+
+ const cx = ((x + 1) * size) / 2;
+ const cy = ((y + 1) * size) / 2;
+
+ const markerPosition = `${cx}px, ${cy}px`;
+ const markerScale = this._pressed ? "1.5" : "1";
+ const markerOffset =
+ this._pressed === "touch" ? `0px, -${size / 8}px` : "0px, 0px";
+
+ return html`
+
+
+
+
+ `;
+ }
+
+ renderSVGFilter() {
+ return svg`
+
+
+
+
+ `;
+ }
+
+ static get styles() {
+ return css`
+ :host {
+ display: block;
+ }
+ .container {
+ position: relative;
+ width: 100%;
+ height: 100%;
+ cursor: pointer;
+ display: flex;
+ }
+ canvas {
+ width: 100%;
+ height: 100%;
+ }
+ svg {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ pointer-events: none;
+ }
+ circle {
+ fill: black;
+ stroke: white;
+ stroke-width: 2;
+ filter: url(#marker-shadow);
+ }
+ .container:not(.pressed) circle {
+ transition: transform 100ms ease-in-out, fill 100ms ease-in-out;
+ }
+ .container:not(.pressed) .cursor {
+ transition: transform 200ms ease-in-out;
+ }
+ `;
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "ha-hs-color-picker": HaHsColorPicker;
+ }
+}
diff --git a/src/components/ha-temp-color-picker.ts b/src/components/ha-temp-color-picker.ts
new file mode 100644
index 0000000000..ddc97b53ed
--- /dev/null
+++ b/src/components/ha-temp-color-picker.ts
@@ -0,0 +1,354 @@
+import { DIRECTION_ALL, Manager, Pan, Tap } from "@egjs/hammerjs";
+import { css, html, LitElement, PropertyValues, svg } from "lit";
+import { customElement, property, query, state } from "lit/decorators";
+import { classMap } from "lit/directives/class-map";
+import { styleMap } from "lit/directives/style-map";
+import { rgb2hex } from "../common/color/convert-color";
+import { fireEvent } from "../common/dom/fire_event";
+
+declare global {
+ interface HASSDomEvents {
+ "cursor-moved": { value?: any };
+ }
+}
+
+function xy2polar(x: number, y: number) {
+ const r = Math.sqrt(x * x + y * y);
+ const phi = Math.atan2(y, x);
+ return [r, phi];
+}
+
+function polar2xy(r: number, phi: number) {
+ const x = Math.cos(phi) * r;
+ const y = Math.sin(phi) * r;
+ return [x, y];
+}
+
+function temperature2rgb(temperature: number): [number, number, number] {
+ const value = temperature / 100;
+ return [getRed(value), getGreen(value), getBlue(value)];
+}
+
+function getRed(temperature: number): number {
+ if (temperature <= 66) {
+ return 255;
+ }
+ const tmp_red = 329.698727446 * (temperature - 60) ** -0.1332047592;
+ return clamp(tmp_red);
+}
+
+function getGreen(temperature: number): number {
+ let green: number;
+ if (temperature <= 66) {
+ green = 99.4708025861 * Math.log(temperature) - 161.1195681661;
+ } else {
+ green = 288.1221695283 * (temperature - 60) ** -0.0755148492;
+ }
+ return clamp(green);
+}
+
+function getBlue(temperature: number): number {
+ if (temperature >= 66) {
+ return 255;
+ }
+ if (temperature <= 19) {
+ return 0;
+ }
+ const blue = 138.5177312231 * Math.log(temperature - 10) - 305.0447927307;
+ return clamp(blue);
+}
+
+function clamp(value: number): number {
+ return Math.max(0, Math.min(255, value));
+}
+
+function drawColorWheel(
+ ctx: CanvasRenderingContext2D,
+ minTemp: number,
+ maxTemp: number
+) {
+ ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
+ const radius = ctx.canvas.width / 2;
+
+ const min = Math.max(minTemp, 2000);
+ const max = Math.min(maxTemp, 40000);
+
+ for (let y = -radius; y < radius; y += 1) {
+ const x = radius * Math.sqrt(1 - (y / radius) ** 2);
+
+ const fraction = (y / radius + 1) / 2;
+
+ const temperature = min + fraction * (max - min);
+
+ const color = rgb2hex(temperature2rgb(temperature));
+
+ ctx.fillStyle = color;
+ ctx.fillRect(radius - x, radius + y - 0.5, 2 * x, 2);
+ ctx.fill();
+ }
+}
+
+@customElement("ha-temp-color-picker")
+class HaTempColorPicker extends LitElement {
+ @property({ type: Boolean, reflect: true })
+ public disabled = false;
+
+ @property({ type: Number, attribute: false })
+ public renderSize?: number;
+
+ @property({ type: Number })
+ public value?: number;
+
+ @property() min = 2000;
+
+ @property() max = 10000;
+
+ @query("#canvas") private _canvas!: HTMLCanvasElement;
+
+ private _mc?: HammerManager;
+
+ @state()
+ private _pressed?: string;
+
+ @state()
+ private _cursorPosition?: [number, number];
+
+ @state()
+ private _localValue?: number;
+
+ protected firstUpdated(changedProps: PropertyValues): void {
+ super.firstUpdated(changedProps);
+ this._setupListeners();
+ this._generateColorWheel();
+ }
+
+ private _generateColorWheel() {
+ const ctx = this._canvas.getContext("2d")!;
+ drawColorWheel(ctx, this.min, this.max);
+ }
+
+ connectedCallback(): void {
+ super.connectedCallback();
+ this._setupListeners();
+ }
+
+ disconnectedCallback(): void {
+ super.disconnectedCallback();
+ this._destroyListeners();
+ }
+
+ protected updated(changedProps: PropertyValues): void {
+ super.updated(changedProps);
+ if (changedProps.has("min") || changedProps.has("max")) {
+ this._generateColorWheel();
+ this._resetPosition();
+ }
+ if (changedProps.has("value")) {
+ if (this.value !== undefined && this._localValue !== this.value) {
+ this._resetPosition();
+ }
+ }
+ }
+
+ _setupListeners() {
+ if (this._canvas && !this._mc) {
+ this._mc = new Manager(this._canvas);
+ this._mc.add(
+ new Pan({
+ direction: DIRECTION_ALL,
+ enable: true,
+ threshold: 0,
+ })
+ );
+
+ this._mc.add(new Tap({ event: "singletap" }));
+
+ let savedPosition;
+ this._mc.on("panstart", (e) => {
+ if (this.disabled) return;
+ this._pressed = e.pointerType;
+ savedPosition = this._cursorPosition;
+ });
+ this._mc.on("pancancel", () => {
+ if (this.disabled) return;
+ this._pressed = undefined;
+ this._cursorPosition = savedPosition;
+ });
+ this._mc.on("panmove", (e) => {
+ if (this.disabled) return;
+ this._cursorPosition = this._getPositionFromEvent(e);
+ this._localValue = this._getValueFromCoord(...this._cursorPosition);
+ fireEvent(this, "cursor-moved", { value: this._localValue });
+ });
+ this._mc.on("panend", (e) => {
+ if (this.disabled) return;
+ this._pressed = undefined;
+ this._cursorPosition = this._getPositionFromEvent(e);
+ this._localValue = this._getValueFromCoord(...this._cursorPosition);
+ fireEvent(this, "cursor-moved", { value: undefined });
+ fireEvent(this, "value-changed", { value: this._localValue });
+ });
+
+ this._mc.on("singletap", (e) => {
+ if (this.disabled) return;
+ this._cursorPosition = this._getPositionFromEvent(e);
+ this._localValue = this._getValueFromCoord(...this._cursorPosition);
+ fireEvent(this, "value-changed", { value: this._localValue });
+ });
+ }
+ }
+
+ private _resetPosition() {
+ if (this.value === undefined) return;
+ const [, y] = this._getCoordsFromValue(this.value);
+ const currentX = this._cursorPosition?.[0] ?? 0;
+ const x =
+ Math.sign(currentX) * Math.min(Math.sqrt(1 - y ** 2), Math.abs(currentX));
+ this._cursorPosition = [x, y];
+ this._localValue = this.value;
+ }
+
+ private _getCoordsFromValue = (temperature: number): [number, number] => {
+ const fraction = (temperature - this.min) / (this.max - this.min);
+ const y = 2 * fraction - 1;
+ return [0, y];
+ };
+
+ private _getValueFromCoord = (_x: number, y: number): number => {
+ const fraction = (y + 1) / 2;
+ const temperature = this.min + fraction * (this.max - this.min);
+ return Math.round(temperature);
+ };
+
+ private _getPositionFromEvent = (e: HammerInput): [number, number] => {
+ const x = e.center.x;
+ const y = e.center.y;
+ const boundingRect = e.target.getBoundingClientRect();
+ const offsetX = boundingRect.left;
+ const offsetY = boundingRect.top;
+ const maxX = e.target.clientWidth;
+ const maxY = e.target.clientHeight;
+
+ const _x = (2 * (x - offsetX)) / maxX - 1;
+ const _y = (2 * (y - offsetY)) / maxY - 1;
+
+ const [r, phi] = xy2polar(_x, _y);
+ const [__x, __y] = polar2xy(Math.min(1, r), phi);
+ return [__x, __y];
+ };
+
+ _destroyListeners() {
+ if (this._mc) {
+ this._mc.destroy();
+ this._mc = undefined;
+ }
+ }
+
+ render() {
+ const size = this.renderSize || 400;
+ const canvasSize = size * window.devicePixelRatio;
+
+ const rgb =
+ this._localValue !== undefined
+ ? temperature2rgb(this._localValue)
+ : ([255, 255, 255] as [number, number, number]);
+
+ const [x, y] = this._cursorPosition ?? [0, 0];
+
+ const cx = ((x + 1) * size) / 2;
+ const cy = ((y + 1) * size) / 2;
+
+ const markerPosition = `${cx}px, ${cy}px`;
+ const markerScale = this._pressed ? "1.5" : "1";
+ const markerOffset =
+ this._pressed === "touch" ? `0px, -${size / 8}px` : "0px, 0px";
+
+ return html`
+
+
+
+
+ `;
+ }
+
+ renderSVGFilter() {
+ return svg`
+
+
+
+
+ `;
+ }
+
+ static get styles() {
+ return css`
+ :host {
+ display: block;
+ }
+ .container {
+ position: relative;
+ width: 100%;
+ height: 100%;
+ cursor: pointer;
+ display: flex;
+ }
+ canvas {
+ width: 100%;
+ height: 100%;
+ }
+ svg {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ pointer-events: none;
+ }
+ circle {
+ fill: black;
+ stroke: white;
+ stroke-width: 2;
+ filter: url(#marker-shadow);
+ }
+ .container:not(.pressed) circle {
+ transition: transform 100ms ease-in-out, fill 100ms ease-in-out;
+ }
+ .container:not(.pressed) .cursor {
+ transition: transform 200ms ease-in-out;
+ }
+ `;
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "ha-temp-color-picker": HaTempColorPicker;
+ }
+}
diff --git a/src/dialogs/more-info/components/lights/ha-more-info-view-light-color-picker.ts b/src/dialogs/more-info/components/lights/ha-more-info-view-light-color-picker.ts
index 2fac5f0bdb..c17e116536 100644
--- a/src/dialogs/more-info/components/lights/ha-more-info-view-light-color-picker.ts
+++ b/src/dialogs/more-info/components/lights/ha-more-info-view-light-color-picker.ts
@@ -1,7 +1,6 @@
import "@material/mwc-button";
import "@material/mwc-tab-bar/mwc-tab-bar";
import "@material/mwc-tab/mwc-tab";
-import { mdiPalette } from "@mdi/js";
import {
css,
CSSResultGroup,
@@ -11,12 +10,13 @@ import {
PropertyValues,
} from "lit";
import { customElement, property, state } from "lit/decorators";
+import { hs2rgb, rgb2hs } from "../../../../common/color/convert-color";
import { throttle } from "../../../../common/util/throttle";
import "../../../../components/ha-button-toggle-group";
-import "../../../../components/ha-color-picker";
-import "../../../../components/ha-control-slider";
+import "../../../../components/ha-hs-color-picker";
import "../../../../components/ha-icon-button-prev";
import "../../../../components/ha-labeled-slider";
+import "../../../../components/ha-temp-color-picker";
import {
getLightCurrentModeRgbColor,
LightColorMode,
@@ -35,8 +35,6 @@ class MoreInfoViewLightColorPicker extends LitElement {
@property() public params?: LightColorPickerViewParams;
- @state() private _ctSliderValue?: number;
-
@state() private _cwSliderValue?: number;
@state() private _wwSliderValue?: number;
@@ -47,11 +45,9 @@ class MoreInfoViewLightColorPicker extends LitElement {
@state() private _brightnessAdjusted?: number;
- @state() private _hueSegments = 24;
+ @state() private _hsPickerValue?: [number, number];
- @state() private _saturationSegments = 8;
-
- @state() private _colorPickerColor?: [number, number, number];
+ @state() private _ctPickerValue?: number;
@state() private _mode?: Mode;
@@ -94,47 +90,34 @@ class MoreInfoViewLightColorPicker extends LitElement {
)}
`
- : ""}
+ : nothing}
${this._mode === LightColorMode.COLOR_TEMP
? html`
- ${this._ctSliderValue ? `${this._ctSliderValue} K` : nothing}
+ ${this._ctPickerValue ? `${this._ctPickerValue} K` : nothing}
-
-
+
`
- : ""}
+ : nothing}
${this._mode === "color"
? html`
-
-
-
-
-
+
+
${supportsRgbw || supportsRgbww
? html`
`
- : ""}
+ : nothing}
`
- : ""}
+ : nothing}
`;
}
@@ -212,7 +195,7 @@ class MoreInfoViewLightColorPicker extends LitElement {
this._brightnessAdjusted = maxVal;
}
}
- this._ctSliderValue =
+ this._ctPickerValue =
stateObj.attributes.color_mode === LightColorMode.COLOR_TEMP
? stateObj.attributes.color_temp_kelvin
: undefined;
@@ -239,14 +222,12 @@ class MoreInfoViewLightColorPicker extends LitElement {
? Math.round((Math.max(...currentRgbColor.slice(0, 3)) * 100) / 255)
: undefined;
- this._colorPickerColor = currentRgbColor?.slice(0, 3) as [
- number,
- number,
- number
- ];
+ this._hsPickerValue = currentRgbColor
+ ? rgb2hs(currentRgbColor.slice(0, 3) as [number, number, number])
+ : undefined;
} else {
- this._colorPickerColor = [0, 0, 0];
- this._ctSliderValue = undefined;
+ this._hsPickerValue = [0, 0];
+ this._ctPickerValue = undefined;
this._wvSliderValue = undefined;
this._cwSliderValue = undefined;
this._wwSliderValue = undefined;
@@ -295,14 +276,79 @@ class MoreInfoViewLightColorPicker extends LitElement {
this._mode = newMode;
}
- private _ctSliderMoved(ev: CustomEvent) {
+ private _hsColorCursorMoved(ev: CustomEvent) {
+ if (!ev.detail.value) {
+ return;
+ }
+ this._hsPickerValue = ev.detail.value;
+
+ this._throttleUpdateColor();
+ }
+
+ private _throttleUpdateColor = throttle(() => this._updateColor(), 500);
+
+ private _updateColor() {
+ const hs_color = this._hsPickerValue!;
+ const rgb_color = hs2rgb(hs_color);
+
+ if (
+ lightSupportsColorMode(this.stateObj!, LightColorMode.RGBWW) ||
+ lightSupportsColorMode(this.stateObj!, LightColorMode.RGBW)
+ ) {
+ this._setRgbWColor(
+ this._colorBrightnessSliderValue
+ ? this._adjustColorBrightness(
+ rgb_color,
+ (this._colorBrightnessSliderValue * 255) / 100
+ )
+ : rgb_color
+ );
+ } else if (lightSupportsColorMode(this.stateObj!, LightColorMode.RGB)) {
+ if (this._brightnessAdjusted) {
+ const brightnessAdjust = (this._brightnessAdjusted / 255) * 100;
+ const brightnessPercentage = Math.round(
+ ((this.stateObj!.attributes.brightness || 0) * brightnessAdjust) / 255
+ );
+ this.hass.callService("light", "turn_on", {
+ entity_id: this.stateObj!.entity_id,
+ brightness_pct: brightnessPercentage,
+ rgb_color: this._adjustColorBrightness(
+ rgb_color,
+ this._brightnessAdjusted,
+ true
+ ),
+ });
+ } else {
+ this.hass.callService("light", "turn_on", {
+ entity_id: this.stateObj!.entity_id,
+ rgb_color,
+ });
+ }
+ } else {
+ this.hass.callService("light", "turn_on", {
+ entity_id: this.stateObj!.entity_id,
+ hs_color: [hs_color[0], hs_color[1] * 100],
+ });
+ }
+ }
+
+ private _hsColorChanged(ev: CustomEvent) {
+ if (!ev.detail.value) {
+ return;
+ }
+ this._hsPickerValue = ev.detail.value;
+
+ this._updateColor();
+ }
+
+ private _ctColorCursorMoved(ev: CustomEvent) {
const ct = ev.detail.value;
- if (isNaN(ct) || this._ctSliderValue === ct) {
+ if (isNaN(ct) || this._ctPickerValue === ct) {
return;
}
- this._ctSliderValue = ct;
+ this._ctPickerValue = ct;
this._throttleUpdateColorTemp();
}
@@ -310,18 +356,18 @@ class MoreInfoViewLightColorPicker extends LitElement {
private _throttleUpdateColorTemp = throttle(() => {
this.hass.callService("light", "turn_on", {
entity_id: this.stateObj!.entity_id,
- color_temp_kelvin: this._ctSliderValue,
+ color_temp_kelvin: this._ctPickerValue,
});
}, 500);
- private _ctSliderChanged(ev: CustomEvent) {
+ private _ctColorChanged(ev: CustomEvent) {
const ct = ev.detail.value;
- if (isNaN(ct) || this._ctSliderValue === ct) {
+ if (isNaN(ct) || this._ctPickerValue === ct) {
return;
}
- this._ctSliderValue = ct;
+ this._ctPickerValue = ct;
this.hass.callService("light", "turn_on", {
entity_id: this.stateObj!.entity_id,
@@ -399,16 +445,6 @@ class MoreInfoViewLightColorPicker extends LitElement {
);
}
- private _segmentClick() {
- if (this._hueSegments === 24 && this._saturationSegments === 8) {
- this._hueSegments = 0;
- this._saturationSegments = 0;
- } else {
- this._hueSegments = 24;
- this._saturationSegments = 8;
- }
- }
-
private _adjustColorBrightness(
rgbColor: [number, number, number],
value?: number,
@@ -448,68 +484,6 @@ class MoreInfoViewLightColorPicker extends LitElement {
}
}
- /**
- * Called when a new color has been picked.
- * should be throttled with the 'throttle=' attribute of the color picker
- */
- private _colorPicked(
- ev: CustomEvent<{
- hs: { h: number; s: number };
- rgb: { r: number; g: number; b: number };
- }>
- ) {
- this._colorPickerColor = [
- ev.detail.rgb.r,
- ev.detail.rgb.g,
- ev.detail.rgb.b,
- ];
-
- if (
- lightSupportsColorMode(this.stateObj!, LightColorMode.RGBWW) ||
- lightSupportsColorMode(this.stateObj!, LightColorMode.RGBW)
- ) {
- this._setRgbWColor(
- this._colorBrightnessSliderValue
- ? this._adjustColorBrightness(
- [ev.detail.rgb.r, ev.detail.rgb.g, ev.detail.rgb.b],
- (this._colorBrightnessSliderValue * 255) / 100
- )
- : [ev.detail.rgb.r, ev.detail.rgb.g, ev.detail.rgb.b]
- );
- } else if (lightSupportsColorMode(this.stateObj!, LightColorMode.RGB)) {
- const rgb_color: [number, number, number] = [
- ev.detail.rgb.r,
- ev.detail.rgb.g,
- ev.detail.rgb.b,
- ];
- if (this._brightnessAdjusted) {
- const brightnessAdjust = (this._brightnessAdjusted / 255) * 100;
- const brightnessPercentage = Math.round(
- ((this.stateObj!.attributes.brightness || 0) * brightnessAdjust) / 255
- );
- this.hass.callService("light", "turn_on", {
- entity_id: this.stateObj!.entity_id,
- brightness_pct: brightnessPercentage,
- rgb_color: this._adjustColorBrightness(
- rgb_color,
- this._brightnessAdjusted,
- true
- ),
- });
- } else {
- this.hass.callService("light", "turn_on", {
- entity_id: this.stateObj!.entity_id,
- rgb_color,
- });
- }
- } else {
- this.hass.callService("light", "turn_on", {
- entity_id: this.stateObj!.entity_id,
- hs_color: [ev.detail.hs.h, ev.detail.hs.s * 100],
- });
- }
- }
-
static get styles(): CSSResultGroup {
return [
css`
@@ -526,35 +500,16 @@ class MoreInfoViewLightColorPicker extends LitElement {
flex: 1;
}
- .segmentation-container {
- position: relative;
- max-height: 500px;
- display: flex;
- justify-content: center;
+ ha-hs-color-picker {
+ max-width: 320px;
+ min-width: 200px;
+ margin: 44px 0 44px 0;
}
- .segmentation-button {
- position: absolute;
- top: 5%;
- left: 0;
- color: var(--secondary-text-color);
- }
-
- ha-color-picker {
- --ha-color-picker-wheel-borderwidth: 5;
- --ha-color-picker-wheel-bordercolor: white;
- --ha-color-picker-wheel-shadow: none;
- --ha-color-picker-marker-borderwidth: 2;
- --ha-color-picker-marker-bordercolor: white;
- }
-
- ha-control-slider {
- height: 45vh;
- max-height: 320px;
- min-height: 200px;
- margin: 20px 0;
- --control-slider-thickness: 100px;
- --control-slider-border-radius: 24px;
+ ha-temp-color-picker {
+ max-width: 320px;
+ min-width: 200px;
+ margin: 20px 0 44px 0;
}
ha-labeled-slider {
@@ -572,17 +527,6 @@ class MoreInfoViewLightColorPicker extends LitElement {
direction: ltr;
}
- .color-temp {
- --control-slider-background: -webkit-linear-gradient(
- top,
- rgb(166, 209, 255) 0%,
- white 50%,
- rgb(255, 160, 0) 100%
- );
- --control-slider-background-opacity: 1;
- margin-bottom: 44px;
- }
-
hr {
border-color: var(--divider-color);
border-bottom: none;