Skip to content

Commit

Permalink
Mypy & R2 (#2439)
Browse files Browse the repository at this point in the history
* Entity

* Description class inheritance

* Duplicate definition

* Attributes

* Missing service return

* Service param typing

* Entity registry updated

* Template sensor

* Config flow title

* Store

* init

* Repairs

* Typing

* Typing

* Typing

* Check wrapped_battery

* Typing

* Add dev version suffix to non github packaged versions

* Bump HA min version to 2024.10

* Update device: Smart door and window sensor (ZSS-JM-GWM-C-MS) by Moes (#2418)

* Apply automatic changes

* Update events documentation

* Fix doc links

* Fix docs links

* New Crowdin translations by GitHub Action (#2420)

Co-authored-by: Crowdin Bot <[email protected]>

* Update device: MCCGQ02HL by Xiaomi (#2422)

* Apply automatic changes

* New Crowdin translations by GitHub Action (#2424)

* New Crowdin translations by GitHub Action (#2425)

Co-authored-by: Crowdin Bot <[email protected]>

* Adjusting the battery type for the QingPing CGG1 temp sensor (#2427)

* New Crowdin translations by GitHub Action (#2429)

Co-authored-by: Crowdin Bot <[email protected]>

* Update device: Model A by Waveshare (#2431)

Co-authored-by: uvjim <[email protected]>

* Apply automatic changes

* Update device: Meter by SwitchBot (#2433)

* Apply automatic changes

* New Crowdin translations by GitHub Action (#2436)

* Device: Ecowitt - GW2001 (#2435)

* Update device: GW2001 by Ecowitt

* Update library.json

* Apply automatic changes

---------

Co-authored-by: mehuman <[email protected]>
Co-authored-by: Andrew Jackson <[email protected]>
Co-authored-by: andrew-codechimp <[email protected]>

* Update device: Duette by Hunter Douglas by Hunter Douglas (#2438)

Co-authored-by: marinus61 <[email protected]>

* Apply automatic changes

* Typing

# Conflicts:
#	custom_components/battery_notes/library_updater.py

# Conflicts:
#	custom_components/battery_notes/library.py

* Fix library url

* Change to R2 URL

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: andrew-codechimp <[email protected]>
Co-authored-by: Crowdin Bot <[email protected]>
Co-authored-by: Jordan Zucker <[email protected]>
Co-authored-by: uvjim <[email protected]>
Co-authored-by: mehuman <[email protected]>
Co-authored-by: marinus61 <[email protected]>
  • Loading branch information
8 people authored Nov 24, 2024
1 parent fd82401 commit 69f3bc5
Show file tree
Hide file tree
Showing 20 changed files with 99 additions and 93 deletions.
6 changes: 3 additions & 3 deletions custom_components/battery_notes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
hass.data[DOMAIN][DATA_LIBRARY_UPDATER] = library_updater

if domain_config.get(CONF_ENABLE_AUTODISCOVERY):
discovery_manager = DiscoveryManager(hass, config)
discovery_manager = DiscoveryManager(hass, domain_config)
await discovery_manager.start_discovery()
else:
_LOGGER.debug("Auto discovery disabled")
Expand Down Expand Up @@ -181,7 +181,7 @@ async def async_remove_entry(hass: HomeAssistant, config_entry: ConfigEntry) ->
return

device: BatteryNotesDevice = hass.data[DOMAIN][DATA].devices[config_entry.entry_id]
if not device:
if not device or not device.coordinator.device_id:
return

data = {ATTR_REMOVE: True}
Expand Down Expand Up @@ -221,7 +221,7 @@ async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry):
# Version 1 had a single config for qty & type, split them
_LOGGER.debug("Migrating config entry from version %s", config_entry.version)

matches: re.Match = re.search(
matches = re.search(
r"^(\d+)(?=x)(?:x\s)(\w+$)|([\s\S]+)", config_entry.data[CONF_BATTERY_TYPE]
)
if matches:
Expand Down
11 changes: 6 additions & 5 deletions custom_components/battery_notes/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@
_LOGGER = logging.getLogger(__name__)


@dataclass
@dataclass(frozen=True, kw_only=True)
class BatteryNotesBinarySensorEntityDescription(
BatteryNotesEntityDescription,
BinarySensorEntityDescription,
Expand Down Expand Up @@ -123,7 +123,7 @@ async def async_setup_entry(

device_id = config_entry.data.get(CONF_DEVICE_ID)

async def async_registry_updated(event: Event) -> None:
async def async_registry_updated(event: Event[er.EventEntityRegistryUpdatedData]) -> None:
"""Handle entity registry update."""
data = event.data
if data["action"] == "remove":
Expand Down Expand Up @@ -177,8 +177,6 @@ async def async_registry_updated(event: Event) -> None:
device_class=BinarySensorDeviceClass.BATTERY,
)

device: BatteryNotesDevice = hass.data[DOMAIN][DATA].devices[config_entry.entry_id]

if coordinator.battery_low_template is not None:
async_add_entities(
[
Expand Down Expand Up @@ -314,6 +312,7 @@ class BatteryNotesBatteryLowTemplateSensor(
"""Represents a low battery threshold binary sensor."""

_attr_should_poll = False
_self_ref_update_count = 0

def __init__(
self,
Expand Down Expand Up @@ -587,6 +586,8 @@ def _handle_coordinator_update(self) -> None:
"""Handle updated data from the coordinator."""

if (
not self.coordinator.wrapped_battery
or
(
wrapped_battery_state := self.hass.states.get(
self.coordinator.wrapped_battery.entity_id
Expand Down Expand Up @@ -616,7 +617,7 @@ def _handle_coordinator_update(self) -> None:
)

@property
def extra_state_attributes(self) -> dict[str, str] | None:
def extra_state_attributes(self) -> dict[str, Any] | None:
"""Return the state attributes of battery low."""

attrs = {
Expand Down
4 changes: 2 additions & 2 deletions custom_components/battery_notes/button.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
_LOGGER = logging.getLogger(__name__)


@dataclass
@dataclass(frozen=True, kw_only=True)
class BatteryNotesButtonEntityDescription(
BatteryNotesEntityDescription,
ButtonEntityDescription,
Expand Down Expand Up @@ -104,7 +104,7 @@ async def async_setup_entry(

device_id = config_entry.data.get(CONF_DEVICE_ID, None)

async def async_registry_updated(event: Event) -> None:
async def async_registry_updated(event: Event[er.EventEntityRegistryUpdatedData]) -> None:
"""Handle entity registry update."""
data = event.data
if data["action"] == "remove":
Expand Down
5 changes: 3 additions & 2 deletions custom_components/battery_notes/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -515,11 +515,12 @@ async def save_options(
errors["base"] = "orphaned_battery_note"
return errors

title: Any = ""
if CONF_NAME in user_input:
title = user_input.get(CONF_NAME)
elif source_entity_id:
elif source_entity_id and entity_entry:
title = entity_entry.name or entity_entry.original_name
else:
elif device_entry:
title = device_entry.name_by_user or device_entry.name

self._process_user_input(user_input, schema)
Expand Down
4 changes: 2 additions & 2 deletions custom_components/battery_notes/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

LOGGER: Logger = getLogger(__package__)

MIN_HA_VERSION = "2024.9"
MIN_HA_VERSION = "2024.10"

manifestfile = Path(__file__).parent / "manifest.json"
with open(file=manifestfile, encoding="UTF-8") as json_file:
Expand All @@ -30,7 +30,7 @@

DEFAULT_BATTERY_LOW_THRESHOLD = 10
DEFAULT_BATTERY_INCREASE_THRESHOLD = 25
DEFAULT_LIBRARY_URL = "https://raw.githubusercontent.com/andrew-codechimp/HA-Battery-Notes/main/custom_components/battery_notes/data/library.json" # pylint: disable=line-too-long
DEFAULT_LIBRARY_URL = "https://battery-notes-data.codechimp.org/library.json"

CONF_SOURCE_ENTITY_ID = "source_entity_id"
CONF_BATTERY_TYPE = "battery_type"
Expand Down
22 changes: 11 additions & 11 deletions custom_components/battery_notes/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,25 +48,25 @@
class BatteryNotesCoordinator(DataUpdateCoordinator):
"""Define an object to hold Battery Notes device."""

device_id: str = None
source_entity_id: str = None
device_id: str | None = None
source_entity_id: str | None = None
device_name: str
battery_type: str
battery_quantity: int
battery_low_threshold: int
battery_low_template: str | None
wrapped_battery: RegistryEntry
_current_battery_level: str = None
wrapped_battery: RegistryEntry | None = None
_current_battery_level: str | None = None
enable_replaced: bool = True
_round_battery: bool = False
_previous_battery_low: bool = None
_previous_battery_level: str = None
_previous_battery_low: bool | None = None
_previous_battery_level: str | None = None
_battery_low_template_state: bool = False
_previous_battery_low_template_state: bool = None
_source_entity_name: str = None
_previous_battery_low_template_state: bool | None = None
_source_entity_name: str | None = None

def __init__(
self, hass, store: BatteryNotesStorage, wrapped_battery: RegistryEntry
self, hass, store: BatteryNotesStorage, wrapped_battery: RegistryEntry | None
):
"""Initialize."""
self.store = store
Expand Down Expand Up @@ -242,13 +242,13 @@ def last_replaced(self) -> datetime | None:
return None

@last_replaced.setter
def last_replaced(self, value):
def last_replaced(self, value: datetime):
"""Set the last replaced datetime and store it."""
entry = {LAST_REPLACED: value}

if self.source_entity_id:
self.async_update_entity_config(entity_id=self.source_entity_id, data=entry)
else:
elif self.device_id:
self.async_update_device_config(device_id=self.device_id, data=entry)

@property
Expand Down
17 changes: 9 additions & 8 deletions custom_components/battery_notes/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import logging
from datetime import datetime
from typing import cast

from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
from homeassistant.components.sensor import (
Expand Down Expand Up @@ -41,11 +42,11 @@
class BatteryNotesDevice:
"""Manages a Battery Note device."""

config: ConfigEntry = None
store: BatteryNotesStorage = None
coordinator: BatteryNotesCoordinator = None
wrapped_battery: RegistryEntry = None
device_name: str = None
config: ConfigEntry
store: BatteryNotesStorage
coordinator: BatteryNotesCoordinator
wrapped_battery: RegistryEntry | None = None
device_name: str | None = None

def __init__(self, hass: HomeAssistant, config: ConfigEntry) -> None:
"""Initialize the device."""
Expand Down Expand Up @@ -205,9 +206,9 @@ async def async_setup(self) -> bool:
self.coordinator.device_id = device_id
self.coordinator.source_entity_id = source_entity_id
self.coordinator.device_name = self.device_name
self.coordinator.battery_type = config.data.get(CONF_BATTERY_TYPE)
self.coordinator.battery_type = cast(str, config.data.get(CONF_BATTERY_TYPE))
try:
self.coordinator.battery_quantity = int(
self.coordinator.battery_quantity = cast(int,
config.data.get(CONF_BATTERY_QUANTITY)
)
except ValueError:
Expand Down Expand Up @@ -255,7 +256,7 @@ async def async_setup(self) -> bool:
last_replaced,
)

self.coordinator.last_replaced = last_replaced
self.coordinator.last_replaced = datetime.fromisoformat(last_replaced) if last_replaced else None

# If there is not a last_reported set to now
if not self.coordinator.last_reported:
Expand Down
9 changes: 5 additions & 4 deletions custom_components/battery_notes/diagnostics.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,16 @@ async def async_get_config_entry_diagnostics(
) -> dict[str, Any]:
"""Return diagnostics for a config entry."""

device_id = config_entry.data.get(CONF_DEVICE_ID, None)
source_entity_id = config_entry.data.get(CONF_SOURCE_ENTITY_ID, None)

device_registry = dr.async_get(hass)
entity_registry = er.async_get(hass)

device_id = config_entry.data.get(CONF_DEVICE_ID, None)
source_entity_id = config_entry.data.get(CONF_SOURCE_ENTITY_ID, None)

if source_entity_id:
entity = entity_registry.async_get(source_entity_id)
device_id = entity.device_id
if entity:
device_id = entity.device_id

diagnostics = {"entry": config_entry.as_dict()}

Expand Down
4 changes: 2 additions & 2 deletions custom_components/battery_notes/discovery.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ async def autodiscover_model(

async def get_model_information(
device_entry: dr.DeviceEntry,
) -> DeviceBatteryDetails | None:
) -> ModelInfo | None:
"""See if we have enough information to automatically setup the battery type."""

manufacturer = device_entry.manufacturer
Expand Down Expand Up @@ -129,7 +129,7 @@ def should_process_device(self, device_entry: dr.DeviceEntry) -> bool:
def _init_entity_discovery(
self,
device_entry: dr.DeviceEntry,
device_battery_details: DeviceBatteryDetails | None,
device_battery_details: DeviceBatteryDetails,
) -> None:
"""Dispatch the discovery flow for a given entity."""
existing_entries = [
Expand Down
4 changes: 2 additions & 2 deletions custom_components/battery_notes/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@
from homeassistant.helpers.entity import EntityDescription


@dataclass
@dataclass(frozen=True, kw_only=True)
class BatteryNotesRequiredKeysMixin:
"""Mixin for required keys."""

unique_id_suffix: str


@dataclass
@dataclass(frozen=True, kw_only=True)
class BatteryNotesEntityDescription(EntityDescription, BatteryNotesRequiredKeysMixin):
"""Generic Battery Notes entity description."""
2 changes: 1 addition & 1 deletion custom_components/battery_notes/library.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
class Library: # pylint: disable=too-few-public-methods
"""Hold all known battery types."""

_devices = []
_devices: list = []

def __init__(self, hass: HomeAssistant) -> None:
"""Init."""
Expand Down
16 changes: 8 additions & 8 deletions custom_components/battery_notes/library_updater.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,30 +60,30 @@ def __init__(self, hass: HomeAssistant):
)

@callback
async def timer_update(self, time):
async def timer_update(self, now: datetime):
"""Need to update the library."""
if await self.time_to_update_library() is False:
return

await self.get_library_updates(time)
await self.get_library_updates(now)

if DOMAIN_CONFIG not in self.hass.data[DOMAIN]:
return

domain_config: dict = self.hass.data[DOMAIN][DOMAIN_CONFIG]

if domain_config.get(CONF_ENABLE_AUTODISCOVERY):
discovery_manager = DiscoveryManager(self.hass, self.hass.config)
discovery_manager = DiscoveryManager(self.hass, domain_config)
await discovery_manager.start_discovery()
else:
_LOGGER.debug("Auto discovery disabled")

@callback
async def get_library_updates(self, time):
async def get_library_updates(self, now: datetime):
# pylint: disable=unused-argument
"""Make a call to GitHub to get the latest library.json."""

def _update_library_json(library_file: str, content: str) -> dict[str, Any]:
def _update_library_json(library_file: str, content: str) -> None:
with open(library_file, mode="w", encoding="utf-8") as file:
file.write(content)
file.close()
Expand Down Expand Up @@ -160,16 +160,16 @@ def __init__(
self._library_url = library_url
self._session = session

async def async_get_data(self) -> any:
"""Get data from the hosted library."""
async def async_get_data(self) -> Any:
"""Get data from the API."""
_LOGGER.debug(f"Updating library from {self._library_url}")
return await self._api_wrapper(method="get", url=self._library_url)

async def _api_wrapper(
self,
method: str,
url: str,
) -> any:
) -> Any:
"""Get information from the API."""
try:
async with async_timeout.timeout(10):
Expand Down
2 changes: 1 addition & 1 deletion custom_components/battery_notes/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@
"integration_type": "device",
"iot_class": "calculated",
"issue_tracker": "https://github.com/andrew-codechimp/ha-battery-notes/issues",
"version": "2.0.0"
"version": "2.0.0-dev"
}
11 changes: 8 additions & 3 deletions custom_components/battery_notes/repairs.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,26 @@

from __future__ import annotations

from typing import cast

import voluptuous as vol
from homeassistant import data_entry_flow
from homeassistant.components.repairs import RepairsFlow
from homeassistant.core import HomeAssistant
from homeassistant.helpers import issue_registry as ir

REQUIRED_KEYS = ("entry_id", "device_id", "source_entity_id")

class MissingDeviceRepairFlow(RepairsFlow):
"""Handler for an issue fixing flow."""

def __init__(self, data: dict[str, str | int | float | None] | None) -> None:
"""Initialize."""
self.entry_id = data["entry_id"]
self.device_id = data["device_id"]
self.source_entity_id = data["source_entity_id"]
if not data or any(key not in data for key in REQUIRED_KEYS):
raise ValueError("Missing data")
self.entry_id = cast(str, data["entry_id"])
self.device_id = cast(str, data["device_id"])
self.source_entity_id = cast(str, data["source_entity_id"])

async def async_step_init(
self, user_input: dict[str, str] | None = None
Expand Down
Loading

0 comments on commit 69f3bc5

Please sign in to comment.