Skip to content

Commit

Permalink
Sync with "ludeeus/integration_blueprint"
Browse files Browse the repository at this point in the history
  • Loading branch information
amitfin committed Sep 2, 2024
1 parent 7a7717b commit 02b391c
Show file tree
Hide file tree
Showing 17 changed files with 336 additions and 291 deletions.
14 changes: 10 additions & 4 deletions .devcontainer.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "daily_schedule",
"image": "mcr.microsoft.com/devcontainers/python:3.12-bookworm",
"image": "mcr.microsoft.com/devcontainers/python:3.12",
"postCreateCommand": "scripts/setup",
"forwardPorts": [
8123
Expand All @@ -15,10 +15,11 @@
"vscode": {
"extensions": [
"charliermarsh.ruff",
"ms-python.python",
"github.vscode-pull-request-github",
"ms-python.python",
"ms-python.vscode-pylance",
"ryanluker.vscode-coverage-gutters",
"ms-python.vscode-pylance"
"streetsidesoftware.code-spell-checker"
],
"settings": {
"files.eol": "\n",
Expand All @@ -34,11 +35,16 @@
"editor.formatOnSave": true,
"editor.formatOnType": true,
"files.trimTrailingWhitespace": true,
"python.analysis.typeCheckingMode": "basic",
"python.analysis.diagnosticMode": "workspace",
"python.analysis.autoImportCompletions": true,
"python.defaultInterpreterPath": "/usr/local/bin/python",
"[python]": {
"editor.defaultFormatter": "charliermarsh.ruff"
}
}
}
},
"remoteUser": "vscode"
"remoteUser": "vscode",
"features": {}
}
5 changes: 4 additions & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,8 @@ jobs:
- name: "Install requirements"
run: python3 -m pip install -r requirements.txt

- name: "Run"
- name: "Lint"
run: python3 -m ruff check .

- name: "Format"
run: python3 -m ruff format . --check
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ __pycache__
*/build/*
*/dist/*
.DS_Store
.ruff_cache

# misc
.coverage
Expand Down
51 changes: 19 additions & 32 deletions .ruff.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,44 +2,31 @@

target-version = "py312"

lint.select = [
"B007", # Loop control variable {name} not used within loop body
"B014", # Exception handler with duplicate exception
"C", # complexity
"D", # docstrings
"E", # pycodestyle
"F", # pyflakes/autoflake
"ICN001", # import concentions; {name} should be imported as {asname}
"PGH004", # Use specific rule codes when using noqa
"PLC0414", # Useless import alias. Import alias does not rename original package.
"SIM105", # Use contextlib.suppress({exception}) instead of try-except-pass
"SIM117", # Merge with-statements that use the same scope
"SIM118", # Use {key} in {dict} instead of {key} in {dict}.keys()
"SIM201", # Use {left} != {right} instead of not {left} == {right}
"SIM212", # Use {a} if {a} else {b} instead of {b} if not {a} else {a}
"SIM300", # Yoda conditions. Use 'age == 42' instead of '42 == age'.
"SIM401", # Use get from dict with default instead of an if block
"T20", # flake8-print
"TRY004", # Prefer TypeError exception for invalid type
"RUF006", # Store a reference to the return value of asyncio.create_task
"UP", # pyupgrade
"W", # pycodestyle
[lint]
select = [
"ALL",
]

lint.ignore = [
"D202", # No blank lines allowed after function docstring
"D203", # 1 blank line required before class docstring
"D213", # Multi-line docstring summary should start at the second line
"D404", # First word of the docstring should not be This
"D406", # Section name should end with a newline
"D407", # Section name underlining
"D411", # Missing blank line before section
"E501", # line too long
"E731", # do not assign a lambda expression, use a def
ignore = [
"ANN101", # Missing type annotation for `self` in method
"ANN401", # Dynamically typed expressions (typing.Any) are disallowed
"D203", # no-blank-line-before-class (incompatible with formatter)
"D212", # multi-line-summary-first-line (incompatible with formatter)
"COM812", # incompatible with formatter
"ISC001", # incompatible with formatter
]

[lint.per-file-ignores]
"tests/*" = [
"S101", # Assert
"PLR2004", # Magic value comparison
]

[lint.flake8-pytest-style]
fixture-parentheses = false

[lint.pyupgrade]
keep-runtime-typing = true

[lint.mccabe]
max-complexity = 25
9 changes: 7 additions & 2 deletions custom_components/daily_schedule/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
"""Daily schedule integration."""

from __future__ import annotations

from homeassistant.config_entries import ConfigEntry
from typing import TYPE_CHECKING

from homeassistant.const import Platform
from homeassistant.core import HomeAssistant

if TYPE_CHECKING:
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant


async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
Expand Down
30 changes: 17 additions & 13 deletions custom_components/daily_schedule/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,15 @@

from __future__ import annotations

from collections.abc import Callable, MutableMapping
import datetime
from typing import Any
import voluptuous as vol
from typing import TYPE_CHECKING, Any

from homeassistant.components.binary_sensor import BinarySensorEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import event as event_helper, entity_platform
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity_platform import AddEntitiesCallback
import homeassistant.util.dt as dt_util
import voluptuous as vol
from homeassistant.components.binary_sensor import BinarySensorEntity
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import entity_platform
from homeassistant.helpers import event as event_helper

from .const import (
ATTR_NEXT_TOGGLE,
Expand All @@ -26,6 +23,13 @@
)
from .schedule import Schedule

if TYPE_CHECKING:
import datetime
from collections.abc import Callable, MutableMapping

from homeassistant.config_entries import ConfigEntry
from homeassistant.helpers.entity_platform import AddEntitiesCallback


def remove_micros_and_tz(time: datetime.time) -> str:
"""Remove microseconds and timezone from a time object."""
Expand Down Expand Up @@ -88,7 +92,7 @@ def is_on(self) -> bool:
return self._schedule.containing(self._now().time())

@callback
def _clean_up_listener(self):
def _clean_up_listener(self) -> None:
"""Remove the update timer."""
if self._unsub_update is not None:
self._unsub_update()
Expand All @@ -100,15 +104,15 @@ async def async_added_to_hass(self) -> None:
self.async_on_remove(self._clean_up_listener)
self._update_state()

async def async_set(self, schedule: list[dict[str, str]]) -> None:
"""Update the config entry with the new list. Required for non-admin use cases."""
async def async_set(self, schedule: list[dict[str, Any]]) -> None:
"""Update the config entry with the new list (non-admin support)."""
self.hass.config_entries.async_update_entry(
self._config_entry,
options={CONF_SCHEDULE: Schedule(schedule).to_list()},
)

@callback
def _update_state(self, *_) -> None:
def _update_state(self, _: datetime.datetime | None = None) -> None:
"""Update the state and schedule next update."""
self._clean_up_listener()
next_update = self._schedule.next_update(self._now())
Expand Down
36 changes: 22 additions & 14 deletions custom_components/daily_schedule/config_flow.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,26 @@
"""Config flow for daily schedule integration."""

from __future__ import annotations

from typing import Any
from typing import TYPE_CHECKING, Any

import homeassistant.helpers.config_validation as cv
import voluptuous as vol

from homeassistant.config_entries import ConfigEntry, ConfigFlow, OptionsFlow
from homeassistant.config_entries import (
ConfigEntry,
ConfigFlow,
OptionsFlow,
)
from homeassistant.const import CONF_NAME
from homeassistant.core import callback
from homeassistant.data_entry_flow import FlowResult
from homeassistant.helpers import selector
import homeassistant.helpers.config_validation as cv

from .const import CONF_FROM, CONF_SCHEDULE, CONF_TO, CONF_UTC, DOMAIN
from .schedule import Schedule

if TYPE_CHECKING:
from homeassistant.data_entry_flow import FlowResult

ADD_RANGE = "add_range"
RANGE_DELIMITER = " - "

Expand Down Expand Up @@ -44,7 +50,7 @@
class DailyScheduleConfigFlow(ConfigFlow, domain=DOMAIN):
"""Config flow."""

def __init__(self):
def __init__(self) -> None:
"""Initialize a new flow."""
self.options: dict[str, Any] = {CONF_SCHEDULE: []}

Expand All @@ -55,7 +61,6 @@ async def async_step_user(
errors: dict[str, str] = {}

if user_input is not None:

# Verify uniqueness of the name (used as the key).
duplicated = filter(
lambda entry: entry.title == user_input[CONF_NAME],
Expand All @@ -65,7 +70,6 @@ async def async_step_user(
errors["base"] = "duplicated"

if not errors:

if user_input[ADD_RANGE]:
self.options[CONF_NAME] = user_input[CONF_NAME]
self.options[CONF_UTC] = user_input[CONF_UTC]
Expand All @@ -88,7 +92,6 @@ async def async_step_time_range(
errors: dict[str, str] = {}

if user_input is not None:

# Validate the new schedule.
time_ranges = self.options[CONF_SCHEDULE].copy()
time_ranges.append(
Expand All @@ -110,7 +113,7 @@ async def async_step_time_range(
data={},
options={
CONF_SCHEDULE: self.options[CONF_SCHEDULE],
CONF_UTC: self.options[CONF_UTC]
CONF_UTC: self.options[CONF_UTC],
},
)

Expand All @@ -137,7 +140,6 @@ async def async_step_init(self, user_input: dict[str, Any]) -> FlowResult:
errors: dict[str, str] = {}

if user_input is not None:

# Get all time ranges except for the ones which were unchecked by the user.
time_ranges = [
{
Expand Down Expand Up @@ -186,8 +188,14 @@ async def async_step_init(self, user_input: dict[str, Any]) -> FlowResult:
schema = OPTIONS_SCHEMA

conf_utc = self.config_entry.options.get(CONF_UTC, False)
schema = schema.extend(vol.Schema({
vol.Required(CONF_UTC, default=conf_utc): selector.BooleanSelector(),
}).schema)
schema = schema.extend(
vol.Schema(
{
vol.Required(
CONF_UTC, default=conf_utc
): selector.BooleanSelector(),
}
).schema
)

return self.async_show_form(step_id="init", data_schema=schema, errors=errors)
3 changes: 3 additions & 0 deletions custom_components/daily_schedule/const.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
"""Constants for the Daily Schedule integration."""

from __future__ import annotations

import logging
from typing import Final

Expand Down
7 changes: 4 additions & 3 deletions custom_components/daily_schedule/diagnostics.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@

from __future__ import annotations

from typing import Any
from typing import TYPE_CHECKING, Any

from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
if TYPE_CHECKING:
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant


async def async_get_config_entry_diagnostics(
Expand Down
Loading

0 comments on commit 02b391c

Please sign in to comment.