def limitlessled_temperature(self): """Convert Open Peer Power color temperature units to percentage.""" max_kelvin = color_temperature_mired_to_kelvin(self.min_mireds) min_kelvin = color_temperature_mired_to_kelvin(self.max_mireds) width = max_kelvin - min_kelvin kelvin = color_temperature_mired_to_kelvin(self._temperature) temperature = (kelvin - min_kelvin) / width return max(0, min(1, temperature))
def find_hsbk(opp, **kwargs): """Find the desired color from a number of possible inputs.""" hue, saturation, brightness, kelvin = [None] * 4 preprocess_turn_on_alternatives(opp, kwargs) if ATTR_HS_COLOR in kwargs: hue, saturation = kwargs[ATTR_HS_COLOR] elif ATTR_RGB_COLOR in kwargs: hue, saturation = color_util.color_RGB_to_hs(*kwargs[ATTR_RGB_COLOR]) elif ATTR_XY_COLOR in kwargs: hue, saturation = color_util.color_xy_to_hs(*kwargs[ATTR_XY_COLOR]) if hue is not None: hue = int(hue / 360 * 65535) saturation = int(saturation / 100 * 65535) kelvin = 3500 if ATTR_COLOR_TEMP in kwargs: kelvin = int( color_util.color_temperature_mired_to_kelvin( kwargs[ATTR_COLOR_TEMP])) saturation = 0 if ATTR_BRIGHTNESS in kwargs: brightness = convert_8_to_16(kwargs[ATTR_BRIGHTNESS]) hsbk = [hue, saturation, brightness, kelvin] return None if hsbk == [None] * 4 else hsbk
def turn_on(self, **kwargs): """Turn the light on.""" if not self.is_on: self._lamp.switch(True) if ATTR_BRIGHTNESS in kwargs: brightness = int((kwargs[ATTR_BRIGHTNESS] / 255.0) * 200.0) self._lamp.brightness(brightness) return if ATTR_HS_COLOR in kwargs: rgb = color_util.color_hs_to_RGB(*kwargs[ATTR_HS_COLOR]) self._lamp.rgb(*rgb) return if ATTR_COLOR_TEMP in kwargs: kelvin = int( color_util.color_temperature_mired_to_kelvin( kwargs[ATTR_COLOR_TEMP])) self._lamp.white(kelvin) return if ATTR_EFFECT in kwargs: effect = kwargs[ATTR_EFFECT] self._lamp.effect(effect) return
def async_update_state(self, new_state): """Update light after state change.""" # Handle State state = new_state.state if state == STATE_ON and self.char_on.value != 1: self.char_on.set_value(1) elif state == STATE_OFF and self.char_on.value != 0: self.char_on.set_value(0) # Handle Brightness if CHAR_BRIGHTNESS in self.chars: brightness = new_state.attributes.get(ATTR_BRIGHTNESS) if isinstance(brightness, (int, float)): brightness = round(brightness / 255 * 100, 0) # The openpeerpower component might report its brightness as 0 but is # not off. But 0 is a special value in homekit. When you turn on a # homekit accessory it will try to restore the last brightness state # which will be the last value saved by char_brightness.set_value. # But if it is set to 0, HomeKit will update the brightness to 100 as # it thinks 0 is off. # # Therefore, if the the brightness is 0 and the device is still on, # the brightness is mapped to 1 otherwise the update is ignored in # order to avoid this incorrect behavior. if brightness == 0 and state == STATE_ON: brightness = 1 if self.char_brightness.value != brightness: self.char_brightness.set_value(brightness) # Handle color temperature if CHAR_COLOR_TEMPERATURE in self.chars: color_temperature = new_state.attributes.get(ATTR_COLOR_TEMP) if isinstance(color_temperature, (int, float)): color_temperature = round(color_temperature, 0) if self.char_color_temperature.value != color_temperature: self.char_color_temperature.set_value(color_temperature) # Handle Color if CHAR_SATURATION in self.chars and CHAR_HUE in self.chars: if ATTR_HS_COLOR in new_state.attributes: hue, saturation = new_state.attributes[ATTR_HS_COLOR] elif ATTR_COLOR_TEMP in new_state.attributes: hue, saturation = color_temperature_to_hs( color_temperature_mired_to_kelvin( new_state.attributes[ATTR_COLOR_TEMP] ) ) else: hue, saturation = None, None if isinstance(hue, (int, float)) and isinstance(saturation, (int, float)): hue = round(hue, 0) saturation = round(saturation, 0) if hue != self.char_hue.value: self.char_hue.set_value(hue) if saturation != self.char_saturation.value: self.char_saturation.set_value(saturation)
def turn_on(self, **kwargs): """Turn on or control the light.""" if (ATTR_BRIGHTNESS not in kwargs and ATTR_HS_COLOR not in kwargs and ATTR_COLOR_TEMP not in kwargs): self._tuya.turn_on() if ATTR_BRIGHTNESS in kwargs: self._tuya.set_brightness(kwargs[ATTR_BRIGHTNESS]) if ATTR_HS_COLOR in kwargs: self._tuya.set_color(kwargs[ATTR_HS_COLOR]) if ATTR_COLOR_TEMP in kwargs: color_temp = colorutil.color_temperature_mired_to_kelvin( kwargs[ATTR_COLOR_TEMP]) self._tuya.set_color_temp(color_temp)
def turn_on(self, **kwargs): """Turn on the light.""" if ATTR_COLOR_TEMP in kwargs and self._device.is_color_capable: self._device.set_color_temp( int(color_temperature_mired_to_kelvin( kwargs[ATTR_COLOR_TEMP]))) return if ATTR_HS_COLOR in kwargs and self._device.is_color_capable: self._device.set_color(kwargs[ATTR_HS_COLOR]) return if ATTR_BRIGHTNESS in kwargs and self._device.is_dimmable: # Convert Open Peer Power brightness (0-255) to Abode brightness (0-99) # If 100 is sent to Abode, response is 99 causing an error self._device.set_level(ceil(kwargs[ATTR_BRIGHTNESS] * 99 / 255.0)) return self._device.switch_on()
def turn_on(self, **kwargs): """Turn the device on.""" transition = int(kwargs.get(ATTR_TRANSITION, 0) * 10) if ATTR_EFFECT in kwargs: self.play_effect(kwargs[ATTR_EFFECT], transition) return if ATTR_HS_COLOR in kwargs: self._rgb_color = color_util.color_hs_to_RGB(*kwargs[ATTR_HS_COLOR]) self._luminary.set_rgb(*self._rgb_color, transition) if ATTR_COLOR_TEMP in kwargs: self._color_temp = kwargs[ATTR_COLOR_TEMP] self._luminary.set_temperature( int(color_util.color_temperature_mired_to_kelvin(self._color_temp)), transition, ) self._is_on = True if ATTR_BRIGHTNESS in kwargs: self._brightness = kwargs[ATTR_BRIGHTNESS] self._luminary.set_luminance(int(self._brightness / 2.55), transition) else: self._luminary.set_onoff(True)
def ct_to_hs(temp): """Convert color temperature (mireds) to hs.""" colorlist = list( color_util.color_temperature_to_hs( color_util.color_temperature_mired_to_kelvin(temp))) return [int(val) for val in colorlist]
async def async_turn_on(self, **kwargs) -> None: """Turn on light.""" if self.block.type == "relay": self.control_result = await self.set_state(turn="on") self.async_write_op_state() return set_mode = None supported_color_modes = self._supported_color_modes params: dict[str, Any] = {"turn": "on"} if ATTR_BRIGHTNESS in kwargs and brightness_supported( supported_color_modes): brightness_pct = int(100 * (kwargs[ATTR_BRIGHTNESS] + 1) / 255) if hasattr(self.block, "gain"): params["gain"] = brightness_pct if hasattr(self.block, "brightness"): params["brightness"] = brightness_pct if ATTR_COLOR_TEMP in kwargs and COLOR_MODE_COLOR_TEMP in supported_color_modes: color_temp = color_temperature_mired_to_kelvin( kwargs[ATTR_COLOR_TEMP]) color_temp = min(self._max_kelvin, max(self._min_kelvin, color_temp)) # Color temperature change - used only in white mode, switch device mode to white set_mode = "white" params["temp"] = int(color_temp) if ATTR_RGB_COLOR in kwargs and COLOR_MODE_RGB in supported_color_modes: # Color channels change - used only in color mode, switch device mode to color set_mode = "color" (params["red"], params["green"], params["blue"]) = kwargs[ATTR_RGB_COLOR] if ATTR_RGBW_COLOR in kwargs and COLOR_MODE_RGBW in supported_color_modes: # Color channels change - used only in color mode, switch device mode to color set_mode = "color" (params["red"], params["green"], params["blue"], params["white"]) = kwargs[ATTR_RGBW_COLOR] if ATTR_EFFECT in kwargs: # Color effect change - used only in color mode, switch device mode to color set_mode = "color" if self.wrapper.model == "SHBLB-1": effect_dict = SHBLB_1_RGB_EFFECTS else: effect_dict = STANDARD_RGB_EFFECTS if kwargs[ATTR_EFFECT] in effect_dict.values(): params["effect"] = [ k for k, v in effect_dict.items() if v == kwargs[ATTR_EFFECT] ][0] else: _LOGGER.error( "Effect '%s' not supported by device %s", kwargs[ATTR_EFFECT], self.wrapper.model, ) if await self.set_light_mode(set_mode): self.control_result = await self.set_state(**params) self.async_write_op_state()
def test_color_temperature_mired_to_kelvin(): """Test color_temperature_mired_to_kelvin.""" assert color_util.color_temperature_mired_to_kelvin(40) == 25000 assert color_util.color_temperature_mired_to_kelvin(200) == 5000 with pytest.raises(ZeroDivisionError): assert color_util.color_temperature_mired_to_kelvin(0)
async def async_turn_on(self, **kwargs): """Forward the turn_on command to all lights in the light group.""" data = {ATTR_ENTITY_ID: self._entity_ids} emulate_color_temp_entity_ids = [] if ATTR_BRIGHTNESS in kwargs: data[ATTR_BRIGHTNESS] = kwargs[ATTR_BRIGHTNESS] if ATTR_HS_COLOR in kwargs: data[ATTR_HS_COLOR] = kwargs[ATTR_HS_COLOR] if ATTR_COLOR_TEMP in kwargs: data[ATTR_COLOR_TEMP] = kwargs[ATTR_COLOR_TEMP] # Create a new entity list to mutate updated_entities = list(self._entity_ids) # Walk through initial entity ids, split entity lists by support for entity_id in self._entity_ids: state = self.opp.states.get(entity_id) if not state: continue support = state.attributes.get(ATTR_SUPPORTED_FEATURES) # Only pass color temperature to supported entity_ids if bool(support & SUPPORT_COLOR) and not bool( support & SUPPORT_COLOR_TEMP ): emulate_color_temp_entity_ids.append(entity_id) updated_entities.remove(entity_id) data[ATTR_ENTITY_ID] = updated_entities if ATTR_WHITE_VALUE in kwargs: data[ATTR_WHITE_VALUE] = kwargs[ATTR_WHITE_VALUE] if ATTR_EFFECT in kwargs: data[ATTR_EFFECT] = kwargs[ATTR_EFFECT] if ATTR_TRANSITION in kwargs: data[ATTR_TRANSITION] = kwargs[ATTR_TRANSITION] if ATTR_FLASH in kwargs: data[ATTR_FLASH] = kwargs[ATTR_FLASH] if not emulate_color_temp_entity_ids: await self.opp.services.async_call( light.DOMAIN, light.SERVICE_TURN_ON, data, blocking=True ) return emulate_color_temp_data = data.copy() temp_k = color_util.color_temperature_mired_to_kelvin( emulate_color_temp_data[ATTR_COLOR_TEMP] ) hs_color = color_util.color_temperature_to_hs(temp_k) emulate_color_temp_data[ATTR_HS_COLOR] = hs_color del emulate_color_temp_data[ATTR_COLOR_TEMP] emulate_color_temp_data[ATTR_ENTITY_ID] = emulate_color_temp_entity_ids await asyncio.gather( self.opp.services.async_call( light.DOMAIN, light.SERVICE_TURN_ON, data, blocking=True ), self.opp.services.async_call( light.DOMAIN, light.SERVICE_TURN_ON, emulate_color_temp_data, blocking=True, ), )
async def async_set_color_temp(self, value: float): """Set the color temperature of the device.""" kelvin = color_util.color_temperature_mired_to_kelvin(value) kelvin = max(min(kelvin, 30000.0), 1.0) await self._device.set_color_temperature(kelvin, set_status=True)
async def async_turn_on(self, **kwargs): """Instruct the light to turn on.""" transition_time = None if ATTR_TRANSITION in kwargs: transition_time = int(kwargs[ATTR_TRANSITION]) * 10 dimmer_command = None if ATTR_BRIGHTNESS in kwargs: brightness = kwargs[ATTR_BRIGHTNESS] brightness = min(brightness, 254) dimmer_data = { ATTR_DIMMER: brightness, ATTR_TRANSITION_TIME: transition_time, } dimmer_command = self._device_control.set_dimmer(**dimmer_data) transition_time = None else: dimmer_command = self._device_control.set_state(True) color_command = None if ATTR_HS_COLOR in kwargs and self._device_control.can_set_color: hue = int(kwargs[ATTR_HS_COLOR][0] * (self._device_control.max_hue / 360)) sat = int(kwargs[ATTR_HS_COLOR][1] * (self._device_control.max_saturation / 100)) color_data = { ATTR_HUE: hue, ATTR_SAT: sat, ATTR_TRANSITION_TIME: transition_time, } color_command = self._device_control.set_hsb(**color_data) transition_time = None temp_command = None if ATTR_COLOR_TEMP in kwargs and (self._device_control.can_set_temp or self._device_control.can_set_color): temp = kwargs[ATTR_COLOR_TEMP] # White Spectrum bulb if self._device_control.can_set_temp: if temp > self.max_mireds: temp = self.max_mireds elif temp < self.min_mireds: temp = self.min_mireds temp_data = { ATTR_COLOR_TEMP: temp, ATTR_TRANSITION_TIME: transition_time, } temp_command = self._device_control.set_color_temp(**temp_data) transition_time = None # Color bulb (CWS) # color_temp needs to be set with hue/saturation elif self._device_control.can_set_color: temp_k = color_util.color_temperature_mired_to_kelvin(temp) hs_color = color_util.color_temperature_to_hs(temp_k) hue = int(hs_color[0] * (self._device_control.max_hue / 360)) sat = int(hs_color[1] * (self._device_control.max_saturation / 100)) color_data = { ATTR_HUE: hue, ATTR_SAT: sat, ATTR_TRANSITION_TIME: transition_time, } color_command = self._device_control.set_hsb(**color_data) transition_time = None # HSB can always be set, but color temp + brightness is bulb dependent command = dimmer_command if command is not None: command += color_command else: command = color_command if self._device_control.can_combine_commands: await self._api(command + temp_command) else: if temp_command is not None: await self._api(temp_command) if command is not None: await self._api(command)
async def test_services(opp: OpenPeerPower, caplog): """Test Yeelight services.""" config_entry = MockConfigEntry( domain=DOMAIN, data={ **CONFIG_ENTRY_DATA, CONF_MODE_MUSIC: True, CONF_SAVE_ON_CHANGE: True, CONF_NIGHTLIGHT_SWITCH: True, }, ) config_entry.add_to_opp(opp) mocked_bulb = _mocked_bulb() with _patch_discovery(MODULE), patch(f"{MODULE}.Bulb", return_value=mocked_bulb): assert await opp.config_entries.async_setup(config_entry.entry_id) await opp.async_block_till_done() async def _async_test_service( service, data, method, payload=None, domain=DOMAIN, failure_side_effect=BulbException, ): err_count = len( [x for x in caplog.records if x.levelno == logging.ERROR]) # success mocked_method = MagicMock() setattr(type(mocked_bulb), method, mocked_method) await opp.services.async_call(domain, service, data, blocking=True) if payload is None: mocked_method.assert_called_once() elif type(payload) == list: mocked_method.assert_called_once_with(*payload) else: mocked_method.assert_called_once_with(**payload) assert (len([x for x in caplog.records if x.levelno == logging.ERROR]) == err_count) # failure if failure_side_effect: mocked_method = MagicMock(side_effect=failure_side_effect) setattr(type(mocked_bulb), method, mocked_method) await opp.services.async_call(domain, service, data, blocking=True) assert (len([ x for x in caplog.records if x.levelno == logging.ERROR ]) == err_count + 1) # turn_on brightness = 100 color_temp = 200 transition = 1 await opp.services.async_call( "light", SERVICE_TURN_ON, { ATTR_ENTITY_ID: ENTITY_LIGHT, ATTR_BRIGHTNESS: brightness, ATTR_COLOR_TEMP: color_temp, ATTR_FLASH: FLASH_LONG, ATTR_EFFECT: EFFECT_STOP, ATTR_TRANSITION: transition, }, blocking=True, ) mocked_bulb.turn_on.assert_called_once_with( duration=transition * 1000, light_type=LightType.Main, power_mode=PowerMode.NORMAL, ) mocked_bulb.turn_on.reset_mock() mocked_bulb.start_music.assert_called_once() mocked_bulb.set_brightness.assert_called_once_with( brightness / 255 * 100, duration=transition * 1000, light_type=LightType.Main) mocked_bulb.set_color_temp.assert_called_once_with( color_temperature_mired_to_kelvin(color_temp), duration=transition * 1000, light_type=LightType.Main, ) mocked_bulb.start_flow.assert_called_once() # flash mocked_bulb.stop_flow.assert_called_once_with(light_type=LightType.Main) # turn_on nightlight await _async_test_service( SERVICE_TURN_ON, {ATTR_ENTITY_ID: ENTITY_NIGHTLIGHT}, "turn_on", payload={ "duration": DEFAULT_TRANSITION, "light_type": LightType.Main, "power_mode": PowerMode.MOONLIGHT, }, domain="light", ) # turn_off await _async_test_service( SERVICE_TURN_OFF, { ATTR_ENTITY_ID: ENTITY_LIGHT, ATTR_TRANSITION: transition }, "turn_off", domain="light", payload={ "duration": transition * 1000, "light_type": LightType.Main }, ) # set_mode mode = "rgb" await _async_test_service( SERVICE_SET_MODE, { ATTR_ENTITY_ID: ENTITY_LIGHT, ATTR_MODE: "rgb" }, "set_power_mode", [PowerMode[mode.upper()]], ) # start_flow await _async_test_service( SERVICE_START_FLOW, { ATTR_ENTITY_ID: ENTITY_LIGHT, ATTR_TRANSITIONS: [{ YEELIGHT_TEMPERATURE_TRANSACTION: [1900, 2000, 60] }], }, "start_flow", ) # set_color_scene await _async_test_service( SERVICE_SET_COLOR_SCENE, { ATTR_ENTITY_ID: ENTITY_LIGHT, ATTR_RGB_COLOR: [10, 20, 30], ATTR_BRIGHTNESS: 50, }, "set_scene", [SceneClass.COLOR, 10, 20, 30, 50], ) # set_hsv_scene await _async_test_service( SERVICE_SET_HSV_SCENE, { ATTR_ENTITY_ID: ENTITY_LIGHT, ATTR_HS_COLOR: [180, 50], ATTR_BRIGHTNESS: 50 }, "set_scene", [SceneClass.HSV, 180, 50, 50], ) # set_color_temp_scene await _async_test_service( SERVICE_SET_COLOR_TEMP_SCENE, { ATTR_ENTITY_ID: ENTITY_LIGHT, ATTR_KELVIN: 4000, ATTR_BRIGHTNESS: 50 }, "set_scene", [SceneClass.CT, 4000, 50], ) # set_color_flow_scene await _async_test_service( SERVICE_SET_COLOR_FLOW_SCENE, { ATTR_ENTITY_ID: ENTITY_LIGHT, ATTR_TRANSITIONS: [{ YEELIGHT_TEMPERATURE_TRANSACTION: [1900, 2000, 60] }], }, "set_scene", ) # set_auto_delay_off_scene await _async_test_service( SERVICE_SET_AUTO_DELAY_OFF_SCENE, { ATTR_ENTITY_ID: ENTITY_LIGHT, ATTR_MINUTES: 1, ATTR_BRIGHTNESS: 50 }, "set_scene", [SceneClass.AUTO_DELAY_OFF, 50, 1], ) # set_music_mode failure enable await _async_test_service( SERVICE_SET_MUSIC_MODE, { ATTR_ENTITY_ID: ENTITY_LIGHT, ATTR_MODE_MUSIC: "true" }, "start_music", failure_side_effect=AssertionError, ) # set_music_mode disable await _async_test_service( SERVICE_SET_MUSIC_MODE, { ATTR_ENTITY_ID: ENTITY_LIGHT, ATTR_MODE_MUSIC: "false" }, "stop_music", failure_side_effect=None, ) # set_music_mode success enable await _async_test_service( SERVICE_SET_MUSIC_MODE, { ATTR_ENTITY_ID: ENTITY_LIGHT, ATTR_MODE_MUSIC: "true" }, "start_music", failure_side_effect=None, ) # test _cmd wrapper error handler err_count = len([x for x in caplog.records if x.levelno == logging.ERROR]) type(mocked_bulb).turn_on = MagicMock() type(mocked_bulb).set_brightness = MagicMock(side_effect=BulbException) await opp.services.async_call( "light", SERVICE_TURN_ON, { ATTR_ENTITY_ID: ENTITY_LIGHT, ATTR_BRIGHTNESS: 50 }, blocking=True, ) assert (len([x for x in caplog.records if x.levelno == logging.ERROR]) == err_count + 1)