Skip to content

Commit

Permalink
Cleanup after add_to_platform_abort is called, try-except intercept, …
Browse files Browse the repository at this point in the history
…update strings.json, fixes #658 (#659)

* Cleanup after add_to_platform_abort is called, fixes #658

* Add note

* try-catch because of priv

* Catch all exceptions

* Bump to 1.17.3 in manifest.json

* assert that hass is not None

* Rename _light_event_action -> _light_state_event_action

* update .github/update-strings.py

* Update README.md, strings.json, and services.yaml

* update name

* Fixes

* Update README.md, strings.json, and services.yaml

* Add name

* Do not add name

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
  • Loading branch information
basnijholt and github-actions[bot] authored Jul 26, 2023
1 parent 8696092 commit e9297d5
Show file tree
Hide file tree
Showing 7 changed files with 447 additions and 51 deletions.
24 changes: 23 additions & 1 deletion .github/update-strings.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import sys
from pathlib import Path

import yaml

sys.path.append(str(Path(__file__).parent.parent))

from custom_components.adaptive_lighting import const # noqa: E402
Expand All @@ -13,20 +15,40 @@
with strings_fname.open() as f:
strings = json.load(f)

# Set "options"
data = {k: f"{k}: {const.DOCS[k]}" for k, _, _ in const.VALIDATION_TUPLES}
strings["options"]["step"]["init"]["data"] = data

# Set "services"
services_filename = Path("custom_components") / "adaptive_lighting" / "services.yaml"
with open(services_filename) as f: # noqa: PTH123
services = yaml.safe_load(f)
services_json = {}
for service_name, dct in services.items():
services_json[service_name] = {
"name": service_name,
"description": dct["description"],
"fields": {},
}
for field_name, field in dct["fields"].items():
services_json[service_name]["fields"][field_name] = {
"description": field["description"],
"name": field_name,
}
strings["services"] = services_json

# Write changes to strings.json
with strings_fname.open("w") as f:
json.dump(strings, f, indent=2, ensure_ascii=False)
f.write("\n")


# Sync changes from strings.json to en.json
with en_fname.open() as f:
en = json.load(f)

en["config"]["step"]["user"] = strings["config"]["step"]["user"]
en["options"]["step"]["init"]["data"] = data
en["services"] = services_json

with en_fname.open("w") as f:
json.dump(en, f, indent=2, ensure_ascii=False)
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/update-readme.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Update README.md
name: Update README.md, strings.json, and services.yaml

on:
push:
Expand Down Expand Up @@ -34,12 +34,12 @@ jobs:
- name: Run markdown-code-runner
run: markdown-code-runner --debug README.md

- name: Run update strings.json
run: python .github/update-strings.py

- name: Run update services.yaml
run: python .github/update-services.py

- name: Run update strings.json
run: python .github/update-strings.py

- name: Commit updated README.md, strings.json, and services.yaml
id: commit
run: |
Expand Down
20 changes: 13 additions & 7 deletions custom_components/adaptive_lighting/hass_utils.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
"""Utility functions for HA core."""
import logging
from collections.abc import Awaitable, Callable

from homeassistant.core import HomeAssistant, ServiceCall
from homeassistant.util.read_only_dict import ReadOnlyDict

from .adaptation_utils import ServiceData

_LOGGER = logging.getLogger(__name__)


def setup_service_call_interceptor(
hass: HomeAssistant,
Expand Down Expand Up @@ -39,15 +42,18 @@ def setup_service_call_interceptor(
existing_service = registered_services[domain][service]

async def service_func_proxy(call: ServiceCall) -> None:
# Convert read-only data to writeable dictionary for modification by interceptor
data = dict(call.data)

# Call interceptor
await intercept_func(call, data)
try:
# Convert read-only data to writeable dictionary for modification by interceptor
data = dict(call.data)

# Convert data back to read-only
call.data = ReadOnlyDict(data)
# Call interceptor
await intercept_func(call, data)

# Convert data back to read-only
call.data = ReadOnlyDict(data)
except Exception as e: # noqa: BLE001
# Blindly catch all exceptions to avoid breaking light.turn_on
_LOGGER.error("Error in service_func_proxy: %s", e)
# Call original service handler with processed data
await existing_service.job.target(call)

Expand Down
2 changes: 1 addition & 1 deletion custom_components/adaptive_lighting/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@
"iot_class": "calculated",
"issue_tracker": "https://github.com/basnijholt/adaptive-lighting/issues",
"requirements": ["ulid-transform"],
"version": "1.17.2"
"version": "1.17.3"
}
176 changes: 176 additions & 0 deletions custom_components/adaptive_lighting/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,5 +56,181 @@
"option_error": "Invalid option",
"entity_missing": "One or more selected light entities are missing from Home Assistant"
}
},
"services": {
"apply": {
"name": "apply",
"description": "Applies the current Adaptive Lighting settings to lights.",
"fields": {
"entity_id": {
"description": "The `entity_id` of the switch with the settings to apply. 📝",
"name": "entity_id"
},
"lights": {
"description": "A light (or list of lights) to apply the settings to. 💡",
"name": "lights"
},
"transition": {
"description": "Duration of transition when lights change, in seconds. 🕑",
"name": "transition"
},
"adapt_brightness": {
"description": "Whether to adapt the brightness of the light. 🌞",
"name": "adapt_brightness"
},
"adapt_color": {
"description": "Whether to adapt the color on supporting lights. 🌈",
"name": "adapt_color"
},
"prefer_rgb_color": {
"description": "Whether to prefer RGB color adjustment over light color temperature when possible. 🌈",
"name": "prefer_rgb_color"
},
"turn_on_lights": {
"description": "Whether to turn on lights that are currently off. 🔆",
"name": "turn_on_lights"
}
}
},
"set_manual_control": {
"name": "set_manual_control",
"description": "Mark whether a light is 'manually controlled'.",
"fields": {
"entity_id": {
"description": "The `entity_id` of the switch in which to (un)mark the light as being `manually controlled`. 📝",
"name": "entity_id"
},
"lights": {
"description": "entity_id(s) of lights, if not specified, all lights in the switch are selected. 💡",
"name": "lights"
},
"manual_control": {
"description": "Whether to add (\"true\") or remove (\"false\") the light from the \"manual_control\" list. 🔒",
"name": "manual_control"
}
}
},
"change_switch_settings": {
"name": "change_switch_settings",
"description": "Change any settings you'd like in the switch. All options here are the same as in the config flow.",
"fields": {
"entity_id": {
"description": "Entity ID of the switch. 📝",
"name": "entity_id"
},
"use_defaults": {
"description": "Sets the default values not specified in this service call. Options: \"current\" (default, retains current values), \"factory\" (resets to documented defaults), or \"configuration\" (reverts to switch config defaults). ⚙️",
"name": "use_defaults"
},
"include_config_in_attributes": {
"description": "Show all options as attributes on the switch in Home Assistant when set to `true`. 📝",
"name": "include_config_in_attributes"
},
"turn_on_lights": {
"description": "Whether to turn on lights that are currently off. 🔆",
"name": "turn_on_lights"
},
"initial_transition": {
"description": "Duration of the first transition when lights turn from `off` to `on` in seconds. ⏲️",
"name": "initial_transition"
},
"sleep_transition": {
"description": "Duration of transition when \"sleep mode\" is toggled in seconds. 😴",
"name": "sleep_transition"
},
"max_brightness": {
"description": "Maximum brightness percentage. 💡",
"name": "max_brightness"
},
"max_color_temp": {
"description": "Coldest color temperature in Kelvin. ❄️",
"name": "max_color_temp"
},
"min_brightness": {
"description": "Minimum brightness percentage. 💡",
"name": "min_brightness"
},
"min_color_temp": {
"description": "Warmest color temperature in Kelvin. 🔥",
"name": "min_color_temp"
},
"only_once": {
"description": "Adapt lights only when they are turned on (`true`) or keep adapting them (`false`). 🔄",
"name": "only_once"
},
"prefer_rgb_color": {
"description": "Whether to prefer RGB color adjustment over light color temperature when possible. 🌈",
"name": "prefer_rgb_color"
},
"separate_turn_on_commands": {
"description": "Use separate `light.turn_on` calls for color and brightness, needed for some light types. 🔀",
"name": "separate_turn_on_commands"
},
"send_split_delay": {
"description": "Delay (ms) between `separate_turn_on_commands` for lights that don't support simultaneous brightness and color setting. ⏲️",
"name": "send_split_delay"
},
"sleep_brightness": {
"description": "Brightness percentage of lights in sleep mode. 😴",
"name": "sleep_brightness"
},
"sleep_rgb_or_color_temp": {
"description": "Use either `\"rgb_color\"` or `\"color_temp\"` in sleep mode. 🌙",
"name": "sleep_rgb_or_color_temp"
},
"sleep_rgb_color": {
"description": "RGB color in sleep mode (used when `sleep_rgb_or_color_temp` is \"rgb_color\"). 🌈",
"name": "sleep_rgb_color"
},
"sleep_color_temp": {
"description": "Color temperature in sleep mode (used when `sleep_rgb_or_color_temp` is `color_temp`) in Kelvin. 😴",
"name": "sleep_color_temp"
},
"sunrise_offset": {
"description": "Adjust sunrise time with a positive or negative offset in seconds. ⏰",
"name": "sunrise_offset"
},
"sunrise_time": {
"description": "Set a fixed time (HH:MM:SS) for sunrise. 🌅",
"name": "sunrise_time"
},
"sunset_offset": {
"description": "Adjust sunset time with a positive or negative offset in seconds. ⏰",
"name": "sunset_offset"
},
"sunset_time": {
"description": "Set a fixed time (HH:MM:SS) for sunset. 🌇",
"name": "sunset_time"
},
"max_sunrise_time": {
"description": "Set the latest virtual sunrise time (HH:MM:SS), allowing for earlier real sunrises. 🌅",
"name": "max_sunrise_time"
},
"min_sunset_time": {
"description": "Set the earliest virtual sunset time (HH:MM:SS), allowing for later real sunsets. 🌇",
"name": "min_sunset_time"
},
"take_over_control": {
"description": "Disable Adaptive Lighting if another source calls `light.turn_on` while lights are on and being adapted. Note that this calls `homeassistant.update_entity` every `interval`! 🔒",
"name": "take_over_control"
},
"detect_non_ha_changes": {
"description": "Detect non-`light.turn_on` state changes and stop adapting lights. Requires `take_over_control`. 🕵️",
"name": "detect_non_ha_changes"
},
"transition": {
"description": "Duration of transition when lights change, in seconds. 🕑",
"name": "transition"
},
"adapt_delay": {
"description": "Wait time (seconds) between light turn on and Adaptive Lighting applying changes. Might help to avoid flickering. ⏲️",
"name": "adapt_delay"
},
"autoreset_control_seconds": {
"description": "Automatically reset the manual control after a number of seconds. Set to 0 to disable. ⏲️",
"name": "autoreset_control_seconds"
}
}
}
}
}
Loading

0 comments on commit e9297d5

Please sign in to comment.