diff --git a/homeassistant/components/ozw/__init__.py b/homeassistant/components/ozw/__init__.py index fa0eddfbcd1..bb48c180971 100644 --- a/homeassistant/components/ozw/__init__.py +++ b/homeassistant/components/ozw/__init__.py @@ -114,7 +114,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): # Filter out CommandClasses we're definitely not interested in. if value.command_class in [ - CommandClass.CONFIGURATION, CommandClass.VERSION, CommandClass.MANUFACTURER_SPECIFIC, ]: diff --git a/homeassistant/components/ozw/discovery.py b/homeassistant/components/ozw/discovery.py index 3eb5d414ac5..84b1d4180e1 100644 --- a/homeassistant/components/ozw/discovery.py +++ b/homeassistant/components/ozw/discovery.py @@ -250,6 +250,16 @@ DISCOVERY_SCHEMAS = ( const.DISC_INDEX: ValueIndex.SWITCH_COLOR_CHANNELS, const.DISC_OPTIONAL: True, }, + "min_kelvin": { + const.DISC_COMMAND_CLASS: (CommandClass.CONFIGURATION,), + const.DISC_INDEX: 81, # PR for upstream to add SWITCH_COLOR_CT_WARM + const.DISC_OPTIONAL: True, + }, + "max_kelvin": { + const.DISC_COMMAND_CLASS: (CommandClass.CONFIGURATION,), + const.DISC_INDEX: 82, # PR for upstream to add SWITCH_COLOR_CT_COLD + const.DISC_OPTIONAL: True, + }, }, }, { # All other text/numeric sensors diff --git a/homeassistant/components/ozw/light.py b/homeassistant/components/ozw/light.py index ae2750618c4..2c7461976c4 100644 --- a/homeassistant/components/ozw/light.py +++ b/homeassistant/components/ozw/light.py @@ -30,9 +30,6 @@ COLOR_CHANNEL_COLD_WHITE = 0x02 COLOR_CHANNEL_RED = 0x04 COLOR_CHANNEL_GREEN = 0x08 COLOR_CHANNEL_BLUE = 0x10 -TEMP_COLOR_MAX = 500 # mired equivalent to 2000K -TEMP_COLOR_MIN = 154 # mired equivalent to 6500K -TEMP_COLOR_DIFF = TEMP_COLOR_MAX - TEMP_COLOR_MIN async def async_setup_entry(hass, config_entry, async_add_entities): @@ -71,6 +68,9 @@ class ZwaveLight(ZWaveDeviceEntity, LightEntity): self._white = None self._ct = None self._supported_features = SUPPORT_BRIGHTNESS + self._min_mireds = 153 # 6500K as a safe default + self._max_mireds = 370 # 2700K as a safe default + # make sure that supported features is correctly set self.on_value_update() @@ -136,6 +136,16 @@ class ZwaveLight(ZWaveDeviceEntity, LightEntity): """Return the color temperature.""" return self._ct + @property + def min_mireds(self): + """Return the coldest color_temp that this light supports.""" + return self._min_mireds + + @property + def max_mireds(self): + """Return the warmest color_temp that this light supports.""" + return self._max_mireds + @callback def async_set_duration(self, **kwargs): """Set the transition time for the brightness value. @@ -209,7 +219,11 @@ class ZwaveLight(ZWaveDeviceEntity, LightEntity): 0, min( 255, - round((TEMP_COLOR_MAX - round(color_temp)) / TEMP_COLOR_DIFF * 255), + round( + (self._max_mireds - color_temp) + / (self._max_mireds - self._min_mireds) + * 255 + ), ), ) warm = 255 - cold @@ -241,16 +255,26 @@ class ZwaveLight(ZWaveDeviceEntity, LightEntity): # Color Data String data = self.values.color.data[ATTR_VALUE] - # RGB is always present in the openzwave color data string. + # RGB is always present in the OpenZWave color data string. rgb = [int(data[1:3], 16), int(data[3:5], 16), int(data[5:7], 16)] self._hs = color_util.color_RGB_to_hs(*rgb) - # Parse remaining color channels. Openzwave appends white channels + # Parse remaining color channels. OpenZWave appends white channels # that are present. index = 7 temp_warm = 0 temp_cold = 0 + # Update color temp limits. + if self.values.min_kelvin: + self._max_mireds = color_util.color_temperature_kelvin_to_mired( + self.values.min_kelvin.data[ATTR_VALUE] + ) + if self.values.max_kelvin: + self._min_mireds = color_util.color_temperature_kelvin_to_mired( + self.values.max_kelvin.data[ATTR_VALUE] + ) + # Warm white if self._color_channels & COLOR_CHANNEL_WARM_WHITE: self._white = int(data[index : index + 2], 16) @@ -264,13 +288,12 @@ class ZwaveLight(ZWaveDeviceEntity, LightEntity): temp_cold = self._white # Calculate color temps based on white LED status - if temp_cold > 0: - self._ct = round(TEMP_COLOR_MAX - ((temp_cold / 255) * TEMP_COLOR_DIFF)) - # Only used if CW channel missing - elif temp_warm > 0: - self._ct = round(TEMP_COLOR_MAX - temp_warm) + if temp_cold or temp_warm: + self._ct = round( + self._max_mireds + - ((temp_cold / 255) * (self._max_mireds - self._min_mireds)) + ) - # If no rgb channels supported, report None. if not ( self._color_channels & COLOR_CHANNEL_RED or self._color_channels & COLOR_CHANNEL_GREEN diff --git a/tests/components/ozw/test_light.py b/tests/components/ozw/test_light.py index 0217ca5adf8..8f9892e37cd 100644 --- a/tests/components/ozw/test_light.py +++ b/tests/components/ozw/test_light.py @@ -284,7 +284,7 @@ async def test_light(hass, light_data, light_msg, light_rgb_msg, sent_messages): assert state.attributes["xy_color"] == (0.519, 0.429) # Test setting color temp - new_color = 465 + new_color = 200 await hass.services.async_call( "light", "turn_on", @@ -298,14 +298,14 @@ async def test_light(hass, light_data, light_msg, light_rgb_msg, sent_messages): msg = sent_messages[-2] assert msg["topic"] == "OpenZWave/1/command/setvalue/" - assert msg["payload"] == {"Value": "#000000e51a", "ValueIDKey": 659341335} + assert msg["payload"] == {"Value": "#00000037c8", "ValueIDKey": 659341335} # Feedback on state light_msg.decode() light_msg.payload["Value"] = byte_to_zwave_brightness(255) light_msg.encode() light_rgb_msg.decode() - light_rgb_msg.payload["Value"] = "#000000e51a" + light_rgb_msg.payload["Value"] = "#00000037c8" light_rgb_msg.encode() receive_message(light_msg) receive_message(light_rgb_msg) @@ -314,7 +314,7 @@ async def test_light(hass, light_data, light_msg, light_rgb_msg, sent_messages): state = hass.states.get("light.led_bulb_6_multi_colour_level") assert state is not None assert state.state == "on" - assert state.attributes["color_temp"] == 465 + assert state.attributes["color_temp"] == 200 # Test setting invalid color temp new_color = 120 @@ -347,7 +347,7 @@ async def test_light(hass, light_data, light_msg, light_rgb_msg, sent_messages): state = hass.states.get("light.led_bulb_6_multi_colour_level") assert state is not None assert state.state == "on" - assert state.attributes["color_temp"] == 154 + assert state.attributes["color_temp"] == 153 async def test_no_rgb_light(hass, light_no_rgb_data, light_no_rgb_msg, sent_messages): @@ -486,6 +486,9 @@ async def test_wc_light(hass, light_wc_data, light_msg, light_rgb_msg, sent_mess assert state is not None assert state.state == "off" + assert state.attributes["min_mireds"] == 153 + assert state.attributes["max_mireds"] == 370 + # Turn on the light new_color = 190 await hass.services.async_call( @@ -497,14 +500,14 @@ async def test_wc_light(hass, light_wc_data, light_msg, light_rgb_msg, sent_mess assert len(sent_messages) == 2 msg = sent_messages[-2] assert msg["topic"] == "OpenZWave/1/command/setvalue/" - assert msg["payload"] == {"Value": "#0000001be4", "ValueIDKey": 659341335} + assert msg["payload"] == {"Value": "#0000002bd4", "ValueIDKey": 659341335} # Feedback on state light_msg.decode() light_msg.payload["Value"] = byte_to_zwave_brightness(255) light_msg.encode() light_rgb_msg.decode() - light_rgb_msg.payload["Value"] = "#0000001be4" + light_rgb_msg.payload["Value"] = "#0000002bd4" light_rgb_msg.encode() receive_message(light_msg) receive_message(light_rgb_msg) @@ -513,7 +516,7 @@ async def test_wc_light(hass, light_wc_data, light_msg, light_rgb_msg, sent_mess state = hass.states.get("light.led_bulb_6_multi_colour_level") assert state is not None assert state.state == "on" - assert state.attributes["color_temp"] == 191 + assert state.attributes["color_temp"] == 190 async def test_new_ozw_light(hass, light_new_ozw_data, light_msg, sent_messages):