Skip to content

Commit

Permalink
HACS update to multiple intagrations #16
Browse files Browse the repository at this point in the history
  • Loading branch information
BeardedTinker committed Dec 30, 2023
1 parent 9bea2ab commit 8aaaa0b
Show file tree
Hide file tree
Showing 20 changed files with 832 additions and 91 deletions.
86 changes: 80 additions & 6 deletions custom_components/battery_notes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from __future__ import annotations

import logging
from datetime import datetime

import homeassistant.helpers.config_validation as cv
import voluptuous as vol
Expand All @@ -15,14 +16,18 @@
from homeassistant.core import HomeAssistant, callback
from homeassistant.const import __version__ as HA_VERSION # noqa: N812
from homeassistant.helpers.aiohttp_client import async_get_clientsession

from homeassistant.helpers.typing import ConfigType
from homeassistant.helpers import device_registry as dr

from .discovery import DiscoveryManager
from .library_coordinator import BatteryNotesLibraryUpdateCoordinator
from .library_updater import (
LibraryUpdaterClient,
)
from .coordinator import BatteryNotesCoordinator
from .store import (
async_get_registry,
)

from .const import (
DOMAIN,
Expand All @@ -31,6 +36,11 @@
CONF_ENABLE_AUTODISCOVERY,
CONF_LIBRARY,
DATA_UPDATE_COORDINATOR,
CONF_SHOW_ALL_DEVICES,
SERVICE_BATTERY_REPLACED,
SERVICE_BATTERY_REPLACED_SCHEMA,
DATA_COORDINATOR,
ATTR_REMOVE,
)

MIN_HA_VERSION = "2023.7"
Expand All @@ -44,13 +54,15 @@
{
vol.Optional(CONF_ENABLE_AUTODISCOVERY, default=True): cv.boolean,
vol.Optional(CONF_LIBRARY, default="library.json"): cv.string,
vol.Optional(CONF_SHOW_ALL_DEVICES, default=False): cv.boolean,
},
),
),
},
extra=vol.ALLOW_EXTRA,
)

ATTR_SERVICE_DEVICE_ID = "device_id"

async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Integration setup."""
Expand All @@ -66,18 +78,24 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:

domain_config: ConfigType = config.get(DOMAIN) or {
CONF_ENABLE_AUTODISCOVERY: True,
CONF_SHOW_ALL_DEVICES: False,
}

hass.data[DOMAIN] = {
DOMAIN_CONFIG: domain_config,
}

coordinator = BatteryNotesLibraryUpdateCoordinator(
store = await async_get_registry(hass)

coordinator = BatteryNotesCoordinator(hass, store)
hass.data[DOMAIN][DATA_COORDINATOR] = coordinator

library_coordinator = BatteryNotesLibraryUpdateCoordinator(
hass=hass,
client=LibraryUpdaterClient(session=async_get_clientsession(hass)),
)

hass.data[DOMAIN][DATA_UPDATE_COORDINATOR] = coordinator
hass.data[DOMAIN][DATA_UPDATE_COORDINATOR] = library_coordinator

await coordinator.async_refresh()

Expand All @@ -97,9 +115,28 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:

entry.async_on_unload(entry.add_update_listener(async_update_options))

# Register custom services
register_services(hass)

return True


async def async_remove_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> None:
"""Device removed, tidy up store."""

if "device_id" not in config_entry.data:
return

device_id = config_entry.data["device_id"]

coordinator = hass.data[DOMAIN][DATA_COORDINATOR]
data = {ATTR_REMOVE: True}

coordinator.async_update_device_config(device_id=device_id, data=data)

_LOGGER.debug("Removed Device %s", device_id)


@callback
async def async_update_options(hass: HomeAssistant, entry: ConfigEntry) -> None:
"""Update options."""
Expand All @@ -111,6 +148,43 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)


async def config_entry_update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:
"""Update listener, called when the config entry options are changed."""
await hass.config_entries.async_reload(entry.entry_id)
@callback
def register_services(hass):
"""Register services used by battery notes component."""

async def handle_battery_replaced(call):
"""Handle the service call."""
device_id = call.data.get(ATTR_SERVICE_DEVICE_ID, "")

device_registry = dr.async_get(hass)

device_entry = device_registry.async_get(device_id)
if not device_entry:
return

for entry_id in device_entry.config_entries:
if (
entry := hass.config_entries.async_get_entry(entry_id)
) and entry.domain == DOMAIN:
date_replaced = datetime.utcnow()

coordinator = hass.data[DOMAIN][DATA_COORDINATOR]
device_entry = {"battery_last_replaced": date_replaced}

coordinator.async_update_device_config(
device_id=device_id, data=device_entry
)

await coordinator._async_update_data()
await coordinator.async_request_refresh()

_LOGGER.debug(
"Device %s battery replaced on %s", device_id, str(date_replaced)
)

hass.services.async_register(
DOMAIN,
SERVICE_BATTERY_REPLACED,
handle_battery_replaced,
schema=SERVICE_BATTERY_REPLACED_SCHEMA,
)
229 changes: 229 additions & 0 deletions custom_components/battery_notes/button.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
"""Button platform for battery_notes."""
from __future__ import annotations

from dataclasses import dataclass
from datetime import datetime

import voluptuous as vol

from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_ENTITY_ID
from homeassistant.core import HomeAssistant, callback, Event
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers import (
config_validation as cv,
device_registry as dr,
entity_registry as er,
)
from homeassistant.components.button import (
PLATFORM_SCHEMA,
ButtonEntity,
ButtonEntityDescription,
)
from homeassistant.helpers.entity import DeviceInfo, EntityCategory
from homeassistant.helpers.event import (
async_track_entity_registry_updated_event,
)

from homeassistant.helpers.reload import async_setup_reload_service
from homeassistant.helpers.typing import (
ConfigType,
)

from homeassistant.const import (
CONF_NAME,
CONF_UNIQUE_ID,
CONF_DEVICE_ID,
)

from . import PLATFORMS

from .const import (
DOMAIN,
DATA_COORDINATOR,
)

from .entity import (
BatteryNotesEntityDescription,
)


@dataclass
class BatteryNotesButtonEntityDescription(
BatteryNotesEntityDescription,
ButtonEntityDescription,
):
"""Describes Battery Notes button entity."""

unique_id_suffix: str


ENTITY_DESCRIPTIONS: tuple[BatteryNotesButtonEntityDescription, ...] = (
BatteryNotesButtonEntityDescription(
unique_id_suffix="_battery_replaced_button",
key="battery_replaced",
translation_key="battery_replaced",
icon="mdi:battery-sync",
entity_category=EntityCategory.DIAGNOSTIC,
),
)

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
{vol.Optional(CONF_NAME): cv.string, vol.Required(CONF_DEVICE_ID): cv.string}
)


@callback
def async_add_to_device(hass: HomeAssistant, entry: ConfigEntry) -> str | None:
"""Add our config entry to the device."""
device_registry = dr.async_get(hass)

device_id = entry.data.get(CONF_DEVICE_ID)
device_registry.async_update_device(device_id, add_config_entry_id=entry.entry_id)

return device_id


async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Initialize Battery Type config entry."""
entity_registry = er.async_get(hass)
device_registry = dr.async_get(hass)

device_id = config_entry.data.get(CONF_DEVICE_ID)

async def async_registry_updated(event: Event) -> None:
"""Handle entity registry update."""
data = event.data
if data["action"] == "remove":
await hass.config_entries.async_remove(config_entry.entry_id)

if data["action"] != "update":
return

if "entity_id" in data["changes"]:
# Entity_id changed, reload the config entry
await hass.config_entries.async_reload(config_entry.entry_id)

if device_id and "device_id" in data["changes"]:
# If the tracked battery note is no longer in the device, remove our config entry
# from the device
if (
not (entity_entry := entity_registry.async_get(data[CONF_ENTITY_ID]))
or not device_registry.async_get(device_id)
or entity_entry.device_id == device_id
):
# No need to do any cleanup
return

device_registry.async_update_device(
device_id, remove_config_entry_id=config_entry.entry_id
)

config_entry.async_on_unload(
async_track_entity_registry_updated_event(
hass, config_entry.entry_id, async_registry_updated
)
)

device_id = async_add_to_device(hass, config_entry)

async_add_entities(
BatteryNotesButton(
hass,
description,
f"{config_entry.entry_id}{description.unique_id_suffix}",
device_id,
)
for description in ENTITY_DESCRIPTIONS
)


async def async_setup_platform(
hass: HomeAssistant,
config: ConfigType,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up the battery type button."""
device_id: str = config[CONF_DEVICE_ID]

await async_setup_reload_service(hass, DOMAIN, PLATFORMS)

async_add_entities(
BatteryNotesButton(
hass,
description,
f"{config.get(CONF_UNIQUE_ID)}{description.unique_id_suffix}",
device_id,
)
for description in ENTITY_DESCRIPTIONS
)


class BatteryNotesButton(ButtonEntity):
"""Represents a battery replaced button."""

_attr_should_poll = False

entity_description: BatteryNotesButtonEntityDescription

def __init__(
self,
hass: HomeAssistant,
description: BatteryNotesButtonEntityDescription,
unique_id: str,
device_id: str,
) -> None:
"""Create a battery replaced button."""
device_registry = dr.async_get(hass)

self.entity_description = description
self._attr_unique_id = unique_id
self._attr_has_entity_name = True
self._device_id = device_id

self._device_id = device_id
if device_id and (device := device_registry.async_get(device_id)):
self._attr_device_info = DeviceInfo(
connections=device.connections,
identifiers=device.identifiers,
)

async def async_added_to_hass(self) -> None:
"""Handle added to Hass."""
# Update entity options
registry = er.async_get(self.hass)
if registry.async_get(self.entity_id) is not None:
registry.async_update_entity_options(
self.entity_id,
DOMAIN,
{"entity_id": self._attr_unique_id},
)

async def update_battery_last_replaced(self):
"""Handle sensor state changes."""

# device_id = self._device_id

# device_entry = {
# "battery_last_replaced" : datetime.utcnow()
# }

# coordinator = self.hass.data[DOMAIN][DATA_COORDINATOR]
# coordinator.async_update_device_config(device_id = device_id, data = device_entry)

self.async_write_ha_state()

async def async_press(self) -> None:
"""Press the button."""
device_id = self._device_id

device_entry = {"battery_last_replaced": datetime.utcnow()}

coordinator = self.hass.data[DOMAIN][DATA_COORDINATOR]
coordinator.async_update_device_config(device_id=device_id, data=device_entry)
await coordinator._async_update_data()
await coordinator.async_request_refresh()
Loading

0 comments on commit 8aaaa0b

Please sign in to comment.