From f0e4cd1d560a60ff4e6fa0af6dbe7e8c35103a52 Mon Sep 17 00:00:00 2001 From: Kai Schnittcher Date: Tue, 27 Feb 2024 23:23:14 +0100 Subject: [PATCH] Fix Color --- libs/ColorHelper.php | 111 +++++++++++++++++++++++++++++++++++++ libs/Zigbee2MQTTHelper.php | 12 ++-- 2 files changed, 119 insertions(+), 4 deletions(-) diff --git a/libs/ColorHelper.php b/libs/ColorHelper.php index 8efb73ba..fa504edf 100644 --- a/libs/ColorHelper.php +++ b/libs/ColorHelper.php @@ -126,4 +126,115 @@ protected function RGBToHSB($R, $G, $B) $this->SendDebug(__FUNCTION__ . ' Output HSB', "Hue: $hue, Saturation: $saturation, Brightness: $brightness", 0); return ['hue' => $hue, 'saturation' => $saturation, 'brightness' => $brightness]; } + + + + + + + + + + + + + + + + protected function xyToRGB($x,$y,$bri){ + + // Calculate XYZ values + $z = 1 - $x - $y; + $Y = $bri / 254; // Brightness coeff. + if ($y == 0){ + $X = 0; + $Z = 0; + } else { + $X = ($Y / $y) * $x; + $Z = ($Y / $y) * $z; + } + + // Convert to sRGB D65 (official formula on meethue) + // old formula + // $r = $X * 3.2406 - $Y * 1.5372 - $Z * 0.4986; + // $g = - $X * 0.9689 + $Y * 1.8758 + $Z * 0.0415; + // $b = $X * 0.0557 - $Y * 0.204 + $Z * 1.057; + // formula 2016 + $r = $X * 1.656492 - $Y * 0.354851 - $Z * 0.255038; + $g = - $X * 0.707196 + $Y * 1.655397 + $Z * 0.036152; + $b = $X * 0.051713 - $Y * 0.121364 + $Z * 1.011530; + + // Apply reverse gamma correction + $r = ($r <= 0.0031308 ? 12.92 * $r : (1.055) * pow($r, (1 / 2.4)) - 0.055); + $g = ($g <= 0.0031308 ? 12.92 * $g : (1.055) * pow($g, (1 / 2.4)) - 0.055); + $b = ($b <= 0.0031308 ? 12.92 * $b : (1.055) * pow($b, (1 / 2.4)) - 0.055); + + // Calculate final RGB + $r = ($r < 0 ? 0 : round($r * 255)); + $g = ($g < 0 ? 0 : round($g * 255)); + $b = ($b < 0 ? 0 : round($b * 255)); + + $r = ($r > 255 ? 255 : $r); + $g = ($g > 255 ? 255 : $g); + $b = ($b > 255 ? 255 : $b); + + // Create a web RGB string (format #xxxxxx) + $this->SendDebug('RGB', 'R: ' . $r . ' G: ' . $g . ' B: ' . $b, 0); + + //$RGB = "#".substr("0".dechex($r),-2).substr("0".dechex($g),-2).substr("0".dechex($b),-2); + $color = sprintf('#%02x%02x%02x', $r, $g, $b); + + return $color; +} + + +protected function RGBToXy($RGB){ + // Get decimal RGB + $RGB = sprintf('#%02x%02x%02x', $RGB[0], $RGB[1], $RGB[2]); + $r = hexdec(substr($RGB,1,2)); + $g = hexdec(substr($RGB,3,2)); + $b = hexdec(substr($RGB,5,2)); + + // Calculate rgb as coef + $r = $r / 255; + $g = $g / 255; + $b = $b / 255; + + // Apply gamma correction + $r = ($r > 0.04045 ? pow(($r + 0.055) / 1.055, 2.4) : ($r / 12.92)); + $g = ($g > 0.04045 ? pow(($g + 0.055) / 1.055, 2.4) : ($g / 12.92)); + $b = ($b > 0.04045 ? pow(($b + 0.055) / 1.055, 2.4) : ($b / 12.92)); + + // Convert to XYZ (official formula on meethue) + // old formula + //$X = $r * 0.649926 + $g * 0.103455 + $b * 0.197109; + //$Y = $r * 0.234327 + $g * 0.743075 + $b * 0.022598; + //$Z = $r * 0 + $g * 0.053077 + $b * 1.035763; + // formula 2016 + $X = $r * 0.664511 + $g * 0.154324 + $b * 0.162028; + $Y = $r * 0.283881 + $g * 0.668433 + $b * 0.047685; + $Z = $r * 0.000088 + $g * 0.072310 + $b * 0.986039; + + // Calculate xy and bri + if (($X+$Y+$Z) == 0){ + $x = 0; + $y = 0; + } else { // round to 4 decimal max (=api max size) + $x = round($X / ($X + $Y + $Z),4); + $y = round($Y / ($X + $Y + $Z),4); + } + $bri = round($Y * 254); + if ($bri > 254){$bri = 254;} + + $cie['x'] = $x; + $cie['y'] = $y; + $cie['bri'] = $bri; + return $cie; +} + + + + + + } diff --git a/libs/Zigbee2MQTTHelper.php b/libs/Zigbee2MQTTHelper.php index a0a98198..ed72548b 100644 --- a/libs/Zigbee2MQTTHelper.php +++ b/libs/Zigbee2MQTTHelper.php @@ -1722,9 +1722,9 @@ public function ReceiveData($JSONString) if (array_key_exists('x', $Payload['color'])) { $this->SendDebug(__FUNCTION__ . ' Color', $Payload['color']['x'], 0); if (array_key_exists('brightness', $Payload)) { - $RGBColor = ltrim($this->CIEToRGB($Payload['color']['x'], $Payload['color']['y'], $Payload['brightness']), '#'); + $RGBColor = ltrim($this->xyToRGB($Payload['color']['x'], $Payload['color']['y'], $Payload['brightness']), '#'); } else { - $RGBColor = ltrim($this->CIEToRGB($Payload['color']['x'], $Payload['color']['y']), '#'); + $RGBColor = ltrim($this->xyToRGB($Payload['color']['x'], $Payload['color']['y'],255), '#'); } $this->SendDebug(__FUNCTION__ . ' Color RGB HEX', $RGBColor, 0); $this->SetValue('Z2M_Color', hexdec(($RGBColor))); @@ -2507,15 +2507,19 @@ private function setColor(int $color, string $mode, string $Z2MMode = 'color') switch ($mode) { case 'cie': $RGB = $this->HexToRGB($color); - $cie = $this->RGBToCIE($RGB[0], $RGB[1], $RGB[2]); + //$cie = $this->RGBToCIE($RGB[0], $RGB[1], $RGB[2]); + $cie = $this->RGBToXy($RGB); if ($Z2MMode = 'color') { $Payload['color'] = $cie; + $Payload['brightness'] = $cie['bri']; } elseif ($Z2MMode == 'color_rgb') { $Payload['color_rgb'] = $cie; } else { return; } - // No break. Add additional comment above this line if intentional + $PayloadJSON = json_encode($Payload, JSON_UNESCAPED_SLASHES); + $this->Z2MSet($PayloadJSON); + break; case 'hs': $this->SendDebug('setColor - Input Color', json_encode($color), 0); if (!is_array($color)) {