core/homeassistant/components/fritzbox_callmonitor/base.py

110 lines
3.1 KiB
Python

"""Base class for fritzbox_callmonitor entities."""
from __future__ import annotations
from contextlib import suppress
from dataclasses import dataclass
from datetime import timedelta
import logging
import re
from fritzconnection.lib.fritzphonebook import FritzPhonebook
from homeassistant.util import Throttle
from .const import REGEX_NUMBER, UNKNOWN_NAME
_LOGGER = logging.getLogger(__name__)
# Return cached results if phonebook was downloaded less then this time ago.
MIN_TIME_PHONEBOOK_UPDATE = timedelta(hours=6)
@dataclass
class Contact:
"""Store details for one phonebook contact."""
name: str
numbers: list[str]
vip: bool
def __init__(
self, name: str, numbers: list[str] | None = None, category: str | None = None
) -> None:
"""Initialize the class."""
self.name = name
self.numbers = [re.sub(REGEX_NUMBER, "", nr) for nr in numbers or ()]
self.vip = category == "1"
unknown_contact = Contact(UNKNOWN_NAME)
class FritzBoxPhonebook:
"""Connects to a FritzBox router and downloads its phone book."""
fph: FritzPhonebook
phonebook_dict: dict[str, list[str]]
contacts: list[Contact]
number_dict: dict[str, Contact]
def __init__(
self,
host: str,
username: str,
password: str,
phonebook_id: int | None = None,
prefixes: list[str] | None = None,
) -> None:
"""Initialize the class."""
self.host = host
self.username = username
self.password = password
self.phonebook_id = phonebook_id
self.prefixes = prefixes
def init_phonebook(self) -> None:
"""Establish a connection to the FRITZ!Box and check if phonebook_id is valid."""
self.fph = FritzPhonebook(
address=self.host,
user=self.username,
password=self.password,
)
self.update_phonebook()
@Throttle(MIN_TIME_PHONEBOOK_UPDATE)
def update_phonebook(self) -> None:
"""Update the phone book dictionary."""
if self.phonebook_id is None:
return
self.fph.get_all_name_numbers(self.phonebook_id)
self.contacts = [
Contact(c.name, c.numbers, getattr(c, "category", None))
for c in self.fph.phonebook.contacts
]
self.number_dict = {nr: c for c in self.contacts for nr in c.numbers}
_LOGGER.debug("Fritz!Box phone book successfully updated")
def get_phonebook_ids(self) -> list[int]:
"""Return list of phonebook ids."""
return self.fph.phonebook_ids # type: ignore[no-any-return]
def get_contact(self, number: str) -> Contact:
"""Return a contact for a given phone number."""
number = re.sub(REGEX_NUMBER, "", str(number))
with suppress(KeyError):
return self.number_dict[number]
if not self.prefixes:
return unknown_contact
for prefix in self.prefixes:
with suppress(KeyError):
return self.number_dict[prefix + number]
with suppress(KeyError):
return self.number_dict[prefix + number.lstrip("0")]
return unknown_contact