diff --git a/.devcontainer.json b/.devcontainer.json index fa662b01..252ad54c 100644 --- a/.devcontainer.json +++ b/.devcontainer.json @@ -1,6 +1,6 @@ { "name": "jseidl/hass-magic_areas", - "image": "mcr.microsoft.com/devcontainers/python:3.12-bookworm", + "image": "mcr.microsoft.com/devcontainers/python:3.12", "postCreateCommand": "scripts/setup", "forwardPorts": [ 8123 @@ -17,24 +17,25 @@ "ms-python.python", "github.vscode-pull-request-github", "ryanluker.vscode-coverage-gutters", - "ms-python.vscode-pylance" + "ms-python.vscode-pylance", + "charliermarsh.ruff", ], "settings": { "files.eol": "\n", "editor.tabSize": 4, - "python.pythonPath": "/usr/bin/python3", - "python.analysis.autoSearchPaths": false, - "python.formatting.provider": "black", - "python.formatting.blackPath": "/usr/local/py-utils/bin/black", - "editor.formatOnPaste": false, + "editor.formatOnPaste": true, "editor.formatOnSave": true, "editor.formatOnType": true, - "files.trimTrailingWhitespace": true + "files.trimTrailingWhitespace": true, + "python.analysis.typeCheckingMode": "basic", + "python.analysis.autoImportCompletions": true, + "python.defaultInterpreterPath": "/usr/local/bin/python", + "[python]": { + "editor.defaultFormatter": "charliermarsh.ruff" + } } } }, "remoteUser": "vscode", - "features": { - "ghcr.io/devcontainers/features/rust:1": {} - } -} + "features": {} +} \ No newline at end of file diff --git a/custom_components/magic_areas/light.py b/custom_components/magic_areas/light.py index 42b5ffd4..04eb69d6 100644 --- a/custom_components/magic_areas/light.py +++ b/custom_components/magic_areas/light.py @@ -151,65 +151,41 @@ def _get_active_lights(self) -> list[str]: async def async_turn_on(self, **kwargs) -> None: """Forward the turn_on command to all lights in the light group.""" - data = {} + data = kwargs.copy() - # Copy parameters over - for arg_keyword, arg_value in kwargs.items(): - data[arg_keyword] = arg_value + # Get active lights or default to all lights + active_lights = self._get_active_lights() or self._entity_ids - # Active lights - active_lights = self._get_active_lights() - targeted_lights = self._entity_ids + # Get current brightness of the group + group_state = self.hass.states.get(self.entity_id) + current_brightness = ( + group_state.attributes.get("brightness", 0) if group_state else 0 + ) - if active_lights: - _LOGGER.debug( - "%s: restricting call to active lights: %s", - self.area.name, - str(active_lights), - ) + # Desired brightness from data or default to max + desired_brightness = data.get("brightness", 255) + brightness_difference = desired_brightness - current_brightness - targeted_lights = active_lights + service_calls = [] - # Split entities by supported features - entity_map = {SUPPORT_COLOR: [], SUPPORT_COLOR_TEMP: []} - for entity_id in targeted_lights: + for entity_id in active_lights: + service_data = data.copy() state = self.hass.states.get(entity_id) if not state: continue - support = state.attributes.get(ATTR_SUPPORTED_FEATURES) - - if bool(support & SUPPORT_COLOR): - if bool(support & SUPPORT_COLOR_TEMP): - entity_map[SUPPORT_COLOR_TEMP].append(entity_id) - else: - entity_map[SUPPORT_COLOR].append(entity_id) - - no_color_support = list( - set(targeted_lights) - - set(entity_map[SUPPORT_COLOR]) - - set(entity_map[SUPPORT_COLOR_TEMP]) - ) - - service_calls = [] - if entity_map[SUPPORT_COLOR_TEMP]: - service_data = data.copy() - service_data[ATTR_ENTITY_ID] = entity_map[SUPPORT_COLOR_TEMP] - service_call = self.hass.services.async_call( - LIGHT_DOMAIN, - SERVICE_TURN_ON, - service_data, - blocking=True, - context=self._context, - ) - service_calls.append(service_call) + support = state.attributes.get(ATTR_SUPPORTED_FEATURES, 0) - if entity_map[SUPPORT_COLOR]: - service_data = data.copy() - service_data[ATTR_ENTITY_ID] = entity_map[SUPPORT_COLOR] + # Adjust brightness + if "brightness" in data: + current_entity_brightness = state.attributes.get("brightness", 0) + new_brightness = max( + 1, min(255, current_entity_brightness + brightness_difference) + ) + service_data["brightness"] = new_brightness - # Perform color_temp emulation if ATTR_COLOR_TEMP is passed - if ATTR_COLOR_TEMP in service_data: + # Handle color temperature and color support + if support & SUPPORT_COLOR_TEMP and ATTR_COLOR_TEMP in service_data: temp_k = color_util.color_temperature_mired_to_kelvin( service_data[ATTR_COLOR_TEMP] ) @@ -217,33 +193,22 @@ async def async_turn_on(self, **kwargs) -> None: service_data[ATTR_HS_COLOR] = hs_color del service_data[ATTR_COLOR_TEMP] - service_call = self.hass.services.async_call( - LIGHT_DOMAIN, - SERVICE_TURN_ON, - service_data, - blocking=True, - context=self._context, - ) - service_calls.append(service_call) - - if no_color_support: - service_data = data.copy() - service_data[ATTR_ENTITY_ID] = no_color_support - if ATTR_COLOR_TEMP in service_data: - del service_data[ATTR_COLOR_TEMP] - if ATTR_HS_COLOR in service_data: - del service_data[ATTR_HS_COLOR] - - service_call = self.hass.services.async_call( - LIGHT_DOMAIN, - SERVICE_TURN_ON, - service_data, - blocking=True, - context=self._context, + if not support & SUPPORT_COLOR: + service_data.pop(ATTR_COLOR_TEMP, None) + service_data.pop(ATTR_HS_COLOR, None) + + service_data[ATTR_ENTITY_ID] = entity_id + service_calls.append( + self.hass.services.async_call( + LIGHT_DOMAIN, + SERVICE_TURN_ON, + service_data, + blocking=True, + context=self._context, + ) ) - service_calls.append(service_call) - # Perform calls + # Perform all service calls concurrently await asyncio.gather(*service_calls) diff --git a/scripts/setup b/scripts/setup old mode 100644 new mode 100755