Fix mqtt light brightness slider (#17075)
* Enable brightness slider for RGB If we are using RGB with no brightness topic, the brighness slider should still be visible, as we can scale the RGB amount to give us the brightness. * Output RGB scaled by brightness If we are outputting to an RGB device, but do not have a dedicated brightness topic set, when the brightness slider is changed, we should output the current colour's HS, with the V coming from the brightness slider. * Brightness from RGB when we're not using a brightness topic When we aren't using a brightness topic, set the brightness slider based on the received value from an RGB -> HSV conversion. * Test for new brightness state scaled by RGB This adds a test to make sure the brightness stored in the state is being computed correctly from the RGB value when a dedicated brightness topic is not set. * Changes from review Fixes formatting of supported features flags, and checks HS colour hasn't been set when operating in RGB-only mode * Set optimistic brightness correctly in rgb mode When we're using rgb mode to set the brightness, we want to set optimistic brightness if: we are running in optimistic mode OR the brightness state topic isn't set and we have a brightness command topic OR the rgb state topic isn't set and we don't have a brightness command topic * Add test for turn_on in RGB brightness modepull/17642/head
parent
e980d1b9fe
commit
2e973c7572
|
@ -211,7 +211,11 @@ class MqttLight(MqttAvailability, MqttDiscoveryUpdate, Light):
|
|||
self._optimistic_rgb = \
|
||||
optimistic or topic[CONF_RGB_STATE_TOPIC] is None
|
||||
self._optimistic_brightness = (
|
||||
optimistic or topic[CONF_BRIGHTNESS_STATE_TOPIC] is None)
|
||||
optimistic or
|
||||
(topic[CONF_BRIGHTNESS_COMMAND_TOPIC] is not None and
|
||||
topic[CONF_BRIGHTNESS_STATE_TOPIC] is None) or
|
||||
(topic[CONF_BRIGHTNESS_COMMAND_TOPIC] is None and
|
||||
topic[CONF_RGB_STATE_TOPIC] is None))
|
||||
self._optimistic_color_temp = (
|
||||
optimistic or topic[CONF_COLOR_TEMP_STATE_TOPIC] is None)
|
||||
self._optimistic_effect = (
|
||||
|
@ -233,7 +237,8 @@ class MqttLight(MqttAvailability, MqttDiscoveryUpdate, Light):
|
|||
self._white_value = None
|
||||
self._supported_features = 0
|
||||
self._supported_features |= (
|
||||
topic[CONF_RGB_COMMAND_TOPIC] is not None and SUPPORT_COLOR)
|
||||
topic[CONF_RGB_COMMAND_TOPIC] is not None and
|
||||
(SUPPORT_COLOR | SUPPORT_BRIGHTNESS))
|
||||
self._supported_features |= (
|
||||
topic[CONF_BRIGHTNESS_COMMAND_TOPIC] is not None and
|
||||
SUPPORT_BRIGHTNESS)
|
||||
|
@ -325,6 +330,10 @@ class MqttLight(MqttAvailability, MqttDiscoveryUpdate, Light):
|
|||
|
||||
rgb = [int(val) for val in payload.split(',')]
|
||||
self._hs = color_util.color_RGB_to_hs(*rgb)
|
||||
if self._topic[CONF_BRIGHTNESS_STATE_TOPIC] is None:
|
||||
percent_bright = \
|
||||
float(color_util.color_RGB_to_hsv(*rgb)[2]) / 100.0
|
||||
self._brightness = int(percent_bright * 255)
|
||||
self.async_schedule_update_ha_state()
|
||||
|
||||
if self._topic[CONF_RGB_STATE_TOPIC] is not None:
|
||||
|
@ -616,6 +625,27 @@ class MqttLight(MqttAvailability, MqttDiscoveryUpdate, Light):
|
|||
if self._optimistic_brightness:
|
||||
self._brightness = kwargs[ATTR_BRIGHTNESS]
|
||||
should_update = True
|
||||
elif ATTR_BRIGHTNESS in kwargs and ATTR_HS_COLOR not in kwargs and\
|
||||
self._topic[CONF_RGB_COMMAND_TOPIC] is not None:
|
||||
rgb = color_util.color_hsv_to_RGB(
|
||||
self._hs[0], self._hs[1], kwargs[ATTR_BRIGHTNESS] / 255 * 100)
|
||||
tpl = self._templates[CONF_RGB_COMMAND_TEMPLATE]
|
||||
if tpl:
|
||||
rgb_color_str = tpl.async_render({
|
||||
'red': rgb[0],
|
||||
'green': rgb[1],
|
||||
'blue': rgb[2],
|
||||
})
|
||||
else:
|
||||
rgb_color_str = '{},{},{}'.format(*rgb)
|
||||
|
||||
mqtt.async_publish(
|
||||
self.hass, self._topic[CONF_RGB_COMMAND_TOPIC],
|
||||
rgb_color_str, self._qos, self._retain)
|
||||
|
||||
if self._optimistic_brightness:
|
||||
self._brightness = kwargs[ATTR_BRIGHTNESS]
|
||||
should_update = True
|
||||
|
||||
if ATTR_COLOR_TEMP in kwargs and \
|
||||
self._topic[CONF_COLOR_TEMP_COMMAND_TOPIC] is not None:
|
||||
|
|
|
@ -393,6 +393,41 @@ class TestLightMQTT(unittest.TestCase):
|
|||
self.assertEqual(255,
|
||||
light_state.attributes['brightness'])
|
||||
|
||||
def test_brightness_from_rgb_controlling_scale(self):
|
||||
"""Test the brightness controlling scale."""
|
||||
with assert_setup_component(1, light.DOMAIN):
|
||||
assert setup_component(self.hass, light.DOMAIN, {
|
||||
light.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
'name': 'test',
|
||||
'state_topic': 'test_scale_rgb/status',
|
||||
'command_topic': 'test_scale_rgb/set',
|
||||
'rgb_state_topic': 'test_scale_rgb/rgb/status',
|
||||
'rgb_command_topic': 'test_scale_rgb/rgb/set',
|
||||
'qos': 0,
|
||||
'payload_on': 'on',
|
||||
'payload_off': 'off'
|
||||
}
|
||||
})
|
||||
|
||||
state = self.hass.states.get('light.test')
|
||||
self.assertEqual(STATE_OFF, state.state)
|
||||
self.assertIsNone(state.attributes.get('brightness'))
|
||||
self.assertFalse(state.attributes.get(ATTR_ASSUMED_STATE))
|
||||
|
||||
fire_mqtt_message(self.hass, 'test_scale_rgb/status', 'on')
|
||||
fire_mqtt_message(self.hass, 'test_scale_rgb/rgb/status', '255,0,0')
|
||||
self.hass.block_till_done()
|
||||
|
||||
state = self.hass.states.get('light.test')
|
||||
self.assertEqual(255, state.attributes.get('brightness'))
|
||||
|
||||
fire_mqtt_message(self.hass, 'test_scale_rgb/rgb/status', '127,0,0')
|
||||
self.hass.block_till_done()
|
||||
|
||||
state = self.hass.states.get('light.test')
|
||||
self.assertEqual(127, state.attributes.get('brightness'))
|
||||
|
||||
def test_white_value_controlling_scale(self):
|
||||
"""Test the white_value controlling scale."""
|
||||
with assert_setup_component(1, light.DOMAIN):
|
||||
|
@ -894,6 +929,39 @@ class TestLightMQTT(unittest.TestCase):
|
|||
mock.call('test_light/bright', 50, 0, False)
|
||||
], any_order=True)
|
||||
|
||||
def test_on_command_rgb(self):
|
||||
"""Test on command in RGB brightness mode."""
|
||||
config = {light.DOMAIN: {
|
||||
'platform': 'mqtt',
|
||||
'name': 'test',
|
||||
'command_topic': 'test_light/set',
|
||||
'rgb_command_topic': "test_light/rgb",
|
||||
}}
|
||||
|
||||
with assert_setup_component(1, light.DOMAIN):
|
||||
assert setup_component(self.hass, light.DOMAIN, config)
|
||||
|
||||
state = self.hass.states.get('light.test')
|
||||
self.assertEqual(STATE_OFF, state.state)
|
||||
|
||||
common.turn_on(self.hass, 'light.test', brightness=127)
|
||||
self.hass.block_till_done()
|
||||
|
||||
# Should get the following MQTT messages.
|
||||
# test_light/rgb: '127,127,127'
|
||||
# test_light/set: 'ON'
|
||||
self.mock_publish.async_publish.assert_has_calls([
|
||||
mock.call('test_light/rgb', '127,127,127', 0, False),
|
||||
mock.call('test_light/set', 'ON', 0, False),
|
||||
], any_order=True)
|
||||
self.mock_publish.async_publish.reset_mock()
|
||||
|
||||
common.turn_off(self.hass, 'light.test')
|
||||
self.hass.block_till_done()
|
||||
|
||||
self.mock_publish.async_publish.assert_called_once_with(
|
||||
'test_light/set', 'OFF', 0, False)
|
||||
|
||||
def test_default_availability_payload(self):
|
||||
"""Test availability by default payload with defined topic."""
|
||||
self.assertTrue(setup_component(self.hass, light.DOMAIN, {
|
||||
|
|
Loading…
Reference in New Issue