diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md deleted file mode 100644 index 548276d02..000000000 --- a/.github/ISSUE_TEMPLATE/bug-report.md +++ /dev/null @@ -1,63 +0,0 @@ ---- -name: Bug Report -about: Report a bug in adaptive-lighting. -title: '' -labels: kind/bug, kind/feature, need/triage -assignees: '' - ---- - -# Home Assistant Adaptive Lighting Issue Template - -## Bug Reports - -If you need help with using or configuring Adaptive Lighting, please [open a Q&A discussion thread here](https://github.com/basnijholt/adaptive-lighting/discussions/new?category=q-a) instead. - -### Before submitting a bug report, please follow these troubleshooting steps: - -Please confirm that you have completed the following steps: - -- [ ] I have updated to the [latest Adaptive Lighting version](https://github.com/basnijholt/adaptive-lighting/releases) available in [HACS](https://hacs.xyz/). -- [ ] I have reviewed the [Troubleshooting Section](https://github.com/basnijholt/adaptive-lighting#sos-troubleshooting) in the [README](https://github.com/basnijholt/adaptive-lighting#readme). -- [ ] (If using Zigbee2MQTT) I have read the [Zigbee2MQTT troubleshooting guide](https://github.com/basnijholt/adaptive-lighting#zigbee2mqtt) in the [README](https://github.com/basnijholt/adaptive-lighting#readme). -- [ ] I have checked the [V2 Roadmap](https://github.com/basnijholt/adaptive-lighting/discussions/291) and [open issues](https://github.com/basnijholt/adaptive-lighting/issues) to ensure my issue isn't a duplicate. - - -### Required information for bug reports: - -Please include the following information in your issue. - -*Issues missing this information may not be addressed.* - -1. **Debug logs** captured while the issue occurred. [See here for instructions on enabling debug logging](https://github.com/basnijholt/adaptive-lighting#troubleshooting): - -``` - -``` - -2. [Your Adaptive Lighting configuration](https://github.com/basnijholt/adaptive-lighting#gear-configuration): - -``` - -``` - -3. (If using Zigbee2MQTT), provide your configuration files (**remove all personal information before posting**): - - `devices.yaml` - - `groups.yaml` - - `configuration.yaml` ⚠️; **Warning** _**REMOVE ALL of the PERSONAL INFORMATION BELOW before posting**_ ⚠️; - - mqtt: `server`: - - mqtt: `user`: - - mqtt: `password`: - - advanced: `pan_id`: - - advanced: `network_key`: - - anything in `log_syslog` if you use this - - Brand and model number of problematic light(s) -``` - -``` - -4. Describe the bug and how to reproduce it: - - - -5. Steps to reproduce the behavior: diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml deleted file mode 100644 index c4eeda14a..000000000 --- a/.github/ISSUE_TEMPLATE/config.yml +++ /dev/null @@ -1,14 +0,0 @@ -blank_issues_enabled: false -contact_links: - - name: Getting Help on adaptive-lighting - url: https://github.com/basnijholt/adaptive-lighting/discussions/categories/q-a - about: Q&A section of the discussion tab - - name: Share your idea - url: https://github.com/basnijholt/adaptive-lighting/discussions/categories/ideas - about: And discuss it with the community - - name: General discussions about this component - url: https://github.com/basnijholt/adaptive-lighting/discussions/categories/general - about: General discussions about this component - - name: Share your setup with adaptive-lighting - url: https://github.com/basnijholt/adaptive-lighting/discussions/categories/show-and-tell - about: Or see what other people do with this component diff --git a/.github/ISSUE_TEMPLATE/doc.md b/.github/ISSUE_TEMPLATE/doc.md deleted file mode 100644 index f6458d77f..000000000 --- a/.github/ISSUE_TEMPLATE/doc.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -name: Documentation Issue -about: Report missing, erroneous docs, broken links or propose new docs -title: '' -labels: kind/docs_issue, need/triage -assignees: '' - ---- - -#### Location - - - -#### Description - - diff --git a/.github/ISSUE_TEMPLATE/enhancement.md b/.github/ISSUE_TEMPLATE/enhancement.md deleted file mode 100644 index d25a96894..000000000 --- a/.github/ISSUE_TEMPLATE/enhancement.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: Enhancement -about: Suggest an improvement to an existing feature. -title: '' -labels: kind/enhancement, need/triage -assignees: '' - ---- diff --git a/.github/ISSUE_TEMPLATE/feature.md b/.github/ISSUE_TEMPLATE/feature.md deleted file mode 100644 index 088b35e4f..000000000 --- a/.github/ISSUE_TEMPLATE/feature.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: Feature -about: Suggest a new feature -title: '' -labels: kind/feature, need/triage -assignees: '' - ---- diff --git a/.github/auto-comment.yml b/.github/auto-comment.yml deleted file mode 100644 index c12722406..000000000 --- a/.github/auto-comment.yml +++ /dev/null @@ -1,6 +0,0 @@ -# Comment to a new issue. -# Disabled -# issueOpened: "" - -# Disabled -# pullRequestOpened: "" diff --git a/.github/config.yml b/.github/config.yml deleted file mode 100644 index 915efced4..000000000 --- a/.github/config.yml +++ /dev/null @@ -1,64 +0,0 @@ -# Configuration for welcome - https://github.com/behaviorbot/welcome - -# Configuration for new-issue-welcome - https://github.com/behaviorbot/new-issue-welcome -# Comment to be posted to on first time issues -newIssueWelcomeComment: > - Thank you for submitting your first issue to this repository! A maintainer - will be here shortly to triage and review. - - In the meantime, please double-check that you have provided all the - necessary information to make this process easy! Any information that can - help save additional round trips is useful! We currently aim to give - initial feedback within **two business days**. If this does not happen, feel - free to leave a comment. - - Please keep an eye on how this issue will be labeled, as labels give an - overview of priorities, assignments and additional actions requested by the - maintainers: - - - "Priority" labels will show how urgent this is for the team. - - "Status" labels will show if this is ready to be worked on, blocked, or in progress. - - "Need" labels will indicate if additional input or analysis is required. - - Finally, remember to use [the discussion tab](https://github.com/basnijholt/adaptive-lighting/discussions) if you just need general - support. - -# Configuration for new-pr-welcome - https://github.com/behaviorbot/new-pr-welcome -# Comment to be posted to on PRs from first time contributors in your repository -newPRWelcomeComment: > - Thank you for submitting this PR! - - A maintainer will be here shortly to review it. - - We are super grateful! Help us by making sure that: - - * The context for this PR is clear, with relevant discussion, decisions - and stakeholders linked/mentioned. - - * Your contribution itself is clear (code comments, self-review for the - rest) and in its best form. - - Getting other community members to do a review would be great help too on - complex PRs. If you are unsure about something, just leave us a comment. - - Next steps: - - * A maintainer will triage and assign priority to this PR, commenting on - any missing things and potentially assigning a reviewer for high - priority items. - - * The PR gets reviews, discussed and approvals as needed. - - * The PR is merged by maintainers when it has been approved and comments addressed. - - We currently aim to provide initial feedback/triaging within **two business - days**. Please keep an eye on any labelling actions, as these will indicate - priorities and status of your contribution. - - We are very grateful for your contribution! - - -# Configuration for first-pr-merge - https://github.com/behaviorbot/first-pr-merge -# Comment to be posted to on pull requests merged by a first time user -# Currently disabled -#firstPRMergeComment: "" diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml deleted file mode 100644 index ad38c0ea9..000000000 --- a/.github/release-drafter.yml +++ /dev/null @@ -1,6 +0,0 @@ -template: | - ## What’s Changed - - $CHANGES - - **Full Changelog**: https://github.com/$OWNER/$REPOSITORY/compare/$PREVIOUS_TAG...v$RESOLVED_VERSION diff --git a/.github/renovate.json b/.github/renovate.json index d27d86ea8..8bb135d74 100644 --- a/.github/renovate.json +++ b/.github/renovate.json @@ -1,7 +1,7 @@ { "$schema": "https://docs.renovatebot.com/renovate-schema.json", "rebaseWhen": "behind-base-branch", - "dependencyDashboard": true, + "dependencyDashboard": false, "labels": [ "dependencies", "no-stale" @@ -29,7 +29,7 @@ "minor", "patch" ], - "automerge": true + "automerge": false } ], "extends": [ diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 7eb1e082d..8e2cd8f09 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -47,6 +47,9 @@ jobs: echo "Webapp integrated at site/simulator/" - name: Upload artifact + # PRs can never deploy (the deploy job is gated to push on main), so only + # upload the Pages artifact on push. PRs still run the build as a sanity check. + if: github.event_name != 'pull_request' uses: actions/upload-pages-artifact@v4 with: path: ./site diff --git a/.github/workflows/markdown-code-runner.yml b/.github/workflows/markdown-code-runner.yml index efd768316..a9cab4d5f 100644 --- a/.github/workflows/markdown-code-runner.yml +++ b/.github/workflows/markdown-code-runner.yml @@ -30,19 +30,12 @@ jobs: - name: Check for changes run: | + # Drift gate only: fail loudly if generated content is stale. The PR + # trigger keeps `main` clean, so we no longer auto-commit/push to main + # (that raced with toc.yaml and re-triggered every push:[main] workflow). if [ -n "$(git status --porcelain)" ]; then - if [ "${{ github.event_name }}" == "pull_request" ]; then - echo "::error::Auto-generated files are not up to date. Please run './scripts/update-generated-content' locally and push the changes." - exit 1 - else - echo "Changes detected, committing and pushing..." - git add -u . - git config --local user.email "github-actions[bot]@users.noreply.github.com" - git config --local user.name "github-actions[bot]" - git commit -m "Update auto-generated content" - git pull --rebase - git push - fi - else - echo "No changes detected." + echo "::error::Auto-generated files are not up to date. Run './scripts/update-generated-content' locally and commit the changes." + git diff + exit 1 fi + echo "No changes detected." diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml deleted file mode 100644 index e3badf0f1..000000000 --- a/.github/workflows/release-drafter.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: Release Drafter - -on: - push: - branches: - - main - pull_request: - types: [opened, reopened, synchronize] - -permissions: - contents: read - -jobs: - update_release_draft: - permissions: - contents: write - pull-requests: write - runs-on: ubuntu-latest - steps: - - uses: release-drafter/release-drafter@v6 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/toc.yaml b/.github/workflows/toc.yaml deleted file mode 100644 index a26657574..000000000 --- a/.github/workflows/toc.yaml +++ /dev/null @@ -1,12 +0,0 @@ -on: - push: - branches: [main] -name: TOC Generator -jobs: - generateTOC: - name: TOC Generator - runs-on: ubuntu-latest - steps: - - uses: technote-space/toc-generator@v4 - with: - TOC_TITLE: "" diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ee63ca7a6..9782a98bd 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -12,10 +12,9 @@ repos: hooks: - id: ruff args: ["--fix"] - - repo: https://github.com/psf/black - rev: 25.1.0 - hooks: - - id: black + # ruff-format replaces black so the formatter matches local tooling + # (editors run ruff format) and avoids black-vs-ruff formatting churn. + - id: ruff-format - repo: https://github.com/Yelp/detect-secrets rev: v1.5.0 hooks: diff --git a/.ruff.toml b/.ruff.toml index e5f1b5241..25111713b 100644 --- a/.ruff.toml +++ b/.ruff.toml @@ -9,6 +9,8 @@ select = ["ALL"] ignore = [ "ANN", "ANN401", # Dynamically typed expressions (typing.Any) are disallowed in {name} + "COM812", # Trailing comma -- conflicts with ruff-format; let the formatter own it + "ISC001", # Implicit string concat on one line -- conflicts with ruff-format "D401", # First line of docstring should be in imperative mood "E501", # line too long "FBT001", # Boolean positional arg in function definition diff --git a/custom_components/adaptive_lighting/const.py b/custom_components/adaptive_lighting/const.py index b3029a290..9cd4ae8f9 100644 --- a/custom_components/adaptive_lighting/const.py +++ b/custom_components/adaptive_lighting/const.py @@ -111,8 +111,7 @@ class TakeOverControlMode(Enum): CONF_INITIAL_TRANSITION, DEFAULT_INITIAL_TRANSITION = "initial_transition", 1 DOCS[CONF_INITIAL_TRANSITION] = ( - "Duration of the first transition when lights turn " - "from `off` to `on` in seconds. ⏲️" + "Duration of the first transition when lights turn from `off` to `on` in seconds. ⏲️" ) CONF_SLEEP_TRANSITION, DEFAULT_SLEEP_TRANSITION = "sleep_transition", 1 @@ -207,8 +206,7 @@ class TakeOverControlMode(Enum): CONF_MAX_SUNRISE_TIME = "max_sunrise_time" DOCS[CONF_MAX_SUNRISE_TIME] = ( - "Set the latest virtual sunrise time (HH:MM:SS), allowing" - " for earlier sunrises. 🌅" + "Set the latest virtual sunrise time (HH:MM:SS), allowing for earlier sunrises. 🌅" ) CONF_SUNSET_OFFSET, DEFAULT_SUNSET_OFFSET = "sunset_offset", 0 diff --git a/tests/test_init.py b/tests/test_init.py index 7c91ee9cd..a707ca134 100644 --- a/tests/test_init.py +++ b/tests/test_init.py @@ -86,9 +86,9 @@ async def test_sensor_platform_not_loaded(hass): sensor_entities = hass.states.async_entity_ids("sensor") al_sensors = [e for e in sensor_entities if "adaptive_lighting" in e] - assert ( - al_sensors == [] - ), f"No adaptive_lighting sensor entities should exist, found: {al_sensors}" + assert al_sensors == [], ( + f"No adaptive_lighting sensor entities should exist, found: {al_sensors}" + ) async def test_no_attr_adaptive_lighting_manager_in_domain_data_on_entry_setup(hass): diff --git a/tests/test_switch.py b/tests/test_switch.py index 3dd8abdea..48f18db56 100644 --- a/tests/test_switch.py +++ b/tests/test_switch.py @@ -716,9 +716,9 @@ def increased_color_temp(): # - With adapt_only_on_bare_turn_on=True: SHOULD mark as manually controlled (to preserve scenes) # - With adapt_only_on_bare_turn_on=False: should NOT mark (fix for issue #1378) if adapt_only_on_bare_turn_on: - assert ( - manual_control[ENTITY_LIGHT_1] == LightControlAttributes.BRIGHTNESS - ), manual_control + assert manual_control[ENTITY_LIGHT_1] == LightControlAttributes.BRIGHTNESS, ( + manual_control + ) else: assert not manual_control[ENTITY_LIGHT_1], manual_control # Reset for next test @@ -727,9 +727,9 @@ def increased_color_temp(): assert not manual_control[ENTITY_LIGHT_1], manual_control # Now change brightness while ON - this should always be manual control await turn_light(True, brightness=increased_brightness()) - assert ( - manual_control[ENTITY_LIGHT_1] == LightControlAttributes.BRIGHTNESS - ), manual_control + assert manual_control[ENTITY_LIGHT_1] == LightControlAttributes.BRIGHTNESS, ( + manual_control + ) # Check that toggling (sleep mode) switch resets manual control for entity_id in [ENTITY_SWITCH, ENTITY_SLEEP_MODE_SWITCH]: @@ -2744,9 +2744,9 @@ async def test_skipped_lights_context_not_from_arbitrary_switch(hass): # Find the skipped event (contains ":skpp:" in context) skipped_events = [e for e in events if ":skpp:" in e.context.id] - assert ( - len(skipped_events) == 1 - ), f"Expected 1 skipped event, got {len(skipped_events)}" + assert len(skipped_events) == 1, ( + f"Expected 1 skipped event, got {len(skipped_events)}" + ) skipped_event = skipped_events[0] skipped_context_id = skipped_event.context.id @@ -2973,12 +2973,12 @@ async def update(force: bool = False): last_sd = switch.manager.last_service_data.get(ENTITY_LIGHT_1) assert last_sd is not None, "last_service_data not set after force adapt" - assert ( - ATTR_BRIGHTNESS in last_sd - ), f"brightness missing from last_service_data after split calls: {last_sd}" - assert ( - ATTR_COLOR_TEMP_KELVIN in last_sd or ATTR_RGB_COLOR in last_sd - ), f"color missing from last_service_data after split calls: {last_sd}" + assert ATTR_BRIGHTNESS in last_sd, ( + f"brightness missing from last_service_data after split calls: {last_sd}" + ) + assert ATTR_COLOR_TEMP_KELVIN in last_sd or ATTR_RGB_COLOR in last_sd, ( + f"color missing from last_service_data after split calls: {last_sd}" + ) al_brightness = light.brightness switch.manager.manual_control[ENTITY_LIGHT_1] = LightControlAttributes.NONE @@ -3008,9 +3008,9 @@ async def _flush_attr_state(hass, entity_id): await update(force=False) - assert ( - light.brightness == manual_brightness - ), f"AL overrode manual brightness {manual_brightness} with {al_brightness}" + assert light.brightness == manual_brightness, ( + f"AL overrode manual brightness {manual_brightness} with {al_brightness}" + ) async def test_extra_state_attributes_includes_per_axis_siblings(hass): @@ -3130,9 +3130,9 @@ async def test_unsupported_skip_warns_once_per_light(hass, caplog): and ENTITY_LIGHT_1 in r.getMessage() and "intersect the currently-adapting axes" in r.getMessage() ] - assert ( - len(warning_records) == 1 - ), f"expected one warning on first skip, got {len(warning_records)}" + assert len(warning_records) == 1, ( + f"expected one warning on first skip, got {len(warning_records)}" + ) assert ENTITY_LIGHT_1 in switch._unsupported_skip_warned caplog.clear() @@ -3146,9 +3146,9 @@ async def test_unsupported_skip_warns_once_per_light(hass, caplog): ) assert second is None warning_records = [r for r in caplog.records if r.levelno == logging.WARNING] - assert ( - warning_records == [] - ), f"expected no warnings on second skip, got {warning_records}" + assert warning_records == [], ( + f"expected no warnings on second skip, got {warning_records}" + ) debug_records = [ r for r in caplog.records @@ -3324,18 +3324,18 @@ def test_expand_light_groups_const_wiring(): VALIDATION_TUPLES, ) - assert ( - DEFAULT_EXPAND_LIGHT_GROUPS is True - ), "Default must be True to preserve legacy behavior for existing users" + assert DEFAULT_EXPAND_LIGHT_GROUPS is True, ( + "Default must be True to preserve legacy behavior for existing users" + ) entries = {k: (default, validator) for k, default, validator in VALIDATION_TUPLES} assert CONF_EXPAND_LIGHT_GROUPS in entries assert entries[CONF_EXPAND_LIGHT_GROUPS][0] is True assert entries[CONF_EXPAND_LIGHT_GROUPS][1] is bool - assert ( - CONF_EXPAND_LIGHT_GROUPS in STEP_OPTIONS["workarounds"] - ), "Option must live in workarounds step so users can toggle it in the UI" + assert CONF_EXPAND_LIGHT_GROUPS in STEP_OPTIONS["workarounds"], ( + "Option must live in workarounds step so users can toggle it in the UI" + ) assert CONF_EXPAND_LIGHT_GROUPS in DOCS assert DOCS[CONF_EXPAND_LIGHT_GROUPS], "User-facing description must not be empty" diff --git a/webapp/app.py b/webapp/app.py index d3db44d99..354cbbb5c 100644 --- a/webapp/app.py +++ b/webapp/app.py @@ -185,7 +185,7 @@ def plot_color_temp(inputs: dict[str, Any], sleep_mode: bool) -> plt.Figure: ax.set_xlim(0, 24) ax.set_xticks(np.arange(0, 25, 1)) yticks = np.arange(-1, 1.1, 0.1) - ax.set_yticks(yticks, [f"{label*100:.0f}%" for label in yticks]) + ax.set_yticks(yticks, [f"{label * 100:.0f}%" for label in yticks]) ax.set_xlabel("Time (hours)") ax.legend() ax.set_ylabel("Sun position (%)")