Skip to content

Commit

Permalink
Add planckian helpers for HSBType (#353)
Browse files Browse the repository at this point in the history
Signed-off-by: Cody Cutrer <cody@cutrer.us>
  • Loading branch information
ccutrer authored Oct 28, 2024
1 parent b67db63 commit f610bd2
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 1 deletion.
71 changes: 70 additions & 1 deletion lib/openhab/core/types/hsb_type.rb
Original file line number Diff line number Diff line change
Expand Up @@ -190,11 +190,80 @@ def to_s
# @return [[PercentType, PercentType, PercentType]]

# @!attribute [r] cct
# @return [QuantityType] The color temperature in Kelvin
# @return [QuantityType] The correlated color temperature in Kelvin
# @since openHAB 4.3
# @see https://en.wikipedia.org/wiki/Planckian_locus Planckian Locus
def cct
ColorUtil.xy_to_kelvin(to_xy[0..1].map { |x| x.double_value / 100 }) | "K"
end

# @!attribute [r] duv
# The distance that this color is from the planckian locus
#
# @return [Float] The delta u, v
#
# @see planckian?
# @see planckian_cct
# @see https://en.wikipedia.org/wiki/Planckian_locus Planckian Locus
# @since openHAB 4.3
def duv
ColorUtil.xy_to_duv(to_xy[0..1].map { |x| x.double_value / 100 })
end

# Checks if this color is within a certain tolerance of the planckian locus ("white")
#
# @param [Float] duv_tolerance The maximum allowed distance from the planckian locus
# @param [Numeric, PercentType] maximum_saturation The maximum allowed saturation.
# Some colors (bright green for example) may be close to the planckian locus,
# but you don't want to treat them as "white" because they are very saturated.
# @return [true, false]
#
# @note The parameters and defaults for this method are subject to change in future
# releases of this library, and should be considered beta. For now, the default
# parameters should be sufficient to detect most colors that Apple's HomeKit color
# temperature color chooser uses as planckian, without detecting most other "real"
# colors as planckian.
# @see duv
# @see planckian_cct
# @see https://en.wikipedia.org/wiki/Planckian_locus Planckian Locus
# @since openHAB 4.3
def planckian?(duv_tolerance: 0.015, maximum_saturation: 75)
duv.abs < duv_tolerance && saturation < maximum_saturation
end
alias_method :white_cct?, :planckian?

# Returns the color temperature of this color _if_ it is within a certain tolerance
# of the planckian locus ("white"), otherwise returns `nil`.
#
# @param [Range, NumberItem] range An allowed range to additionally restrict
# if the CCT should be returned. A NumberItem that represents a CCT channel
# may be provided, and {NumberItem#range NumberItem#range} will be used instead. If the range
# does not have units (is {QuantityType}), it is interpreted as being in Kelvin.
# @return [QuantityType, nil] The color temperature in Kelvin
# (unless the range is in mireds; then it will be in mireds)
#
# @note Additional parameters are forwarded to {#planckian?}
# @see planckian?
# @see https://en.wikipedia.org/wiki/Planckian_locus Planckian Locus
# @since openHAB 4.3
def planckian_cct(range: nil, **kwargs)
return unless planckian?(**kwargs)

range = range.range if range.is_a?(NumberItem)
cct = self.cct
if range
range_type = range.begin || range.end
if !range_type.is_a?(QuantityType)
range = Range.new(range.begin | "K", range.end | "K")
elsif range_type.unit.to_s == "mired"
cct |= "mired"
end
end
return nil if range && !range.cover?(cct)

cct
end
alias_method :white_cct, :planckian_cct
end
end
end
Expand Down
41 changes: 41 additions & 0 deletions spec/openhab/core/types/hsb_type_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,47 @@
end
end

describe "#planckian_cct", if: OpenHAB::Core.version >= OpenHAB::Core::V4_3 do
it "returns a value for a pure 'white'" do
warm_white = HSBType.from_cct(2700)
expect(warm_white.planckian_cct.to_i).to be 2699
end

it "returns a value if in range (bare range)" do
warm_white = HSBType.from_cct(2700)
expect(warm_white.planckian_cct(range: 2000..6000).to_i).to be 2699
end

it "returns a value if in range (K range)" do
warm_white = HSBType.from_cct(2700)
expect(warm_white.planckian_cct(range: (2000 | "K")..(6000 | "K")).to_i).to be 2699
end

it "returns a value if in range (mired range)" do
warm_white = HSBType.from_cct(2700)
expect(warm_white.planckian_cct(range: (167 | "mired")..(500 | "mired")).to_i).to be 370
end

it "returns nil for red" do
expect(HSBType::RED.planckian_cct).to be_nil
end

it "returns nil if the CCT is out of range (bare range)" do
color = HSBType.from_cct(2000)
expect(color.planckian_cct(range: 2700..6000)).to be_nil
end

it "returns nil if the CCT is out of range (K range)" do
color = HSBType.from_cct(2000)
expect(color.planckian_cct(range: (2700 | "K")..(6000 | "K"))).to be_nil
end

it "returns nil if the CCT is out of range (mired range)" do
color = HSBType.from_cct(2000)
expect(color.planckian_cct(range: (167 | "mired")..(370 | "mired"))).to be_nil
end
end

it "is inspectable" do
expect(HSBType.new.inspect).to eql "0 °,0%,0%"
end
Expand Down

0 comments on commit f610bd2

Please sign in to comment.