Skip to content

Commit

Permalink
Add contact vip info to fritzbox_callmonitor sensor (home-assistant#1…
Browse files Browse the repository at this point in the history
  • Loading branch information
cdce8p authored Dec 13, 2024
1 parent c0f6535 commit 7e2d3eb
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 22 deletions.
44 changes: 33 additions & 11 deletions homeassistant/components/fritzbox_callmonitor/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from __future__ import annotations

from contextlib import suppress
from dataclasses import dataclass
from datetime import timedelta
import logging
import re
Expand All @@ -19,12 +20,33 @@
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]]
number_dict: dict[str, str]
contacts: list[Contact]
number_dict: dict[str, Contact]

def __init__(
self,
Expand Down Expand Up @@ -56,32 +78,32 @@ def update_phonebook(self) -> None:
if self.phonebook_id is None:
return

self.phonebook_dict = self.fph.get_all_names(self.phonebook_id)
self.number_dict = {
re.sub(REGEX_NUMBER, "", nr): name
for name, nrs in self.phonebook_dict.items()
for nr in nrs
}
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_name(self, number: str) -> str:
"""Return a name for a given phone number."""
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_NAME
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_NAME
return unknown_contact
27 changes: 17 additions & 10 deletions homeassistant/components/fritzbox_callmonitor/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from homeassistant.helpers.entity_platform import AddEntitiesCallback

from . import FritzBoxCallMonitorConfigEntry
from .base import FritzBoxPhonebook
from .base import Contact, FritzBoxPhonebook
from .const import (
ATTR_PREFIXES,
CONF_PHONEBOOK,
Expand Down Expand Up @@ -96,7 +96,7 @@ def __init__(
self._host = host
self._port = port
self._monitor: FritzBoxCallMonitor | None = None
self._attributes: dict[str, str | list[str]] = {}
self._attributes: dict[str, str | list[str] | bool] = {}

self._attr_translation_placeholders = {"phonebook_name": phonebook_name}
self._attr_unique_id = unique_id
Expand Down Expand Up @@ -152,20 +152,20 @@ def set_state(self, state: CallState) -> None:
"""Set the state."""
self._attr_native_value = state

def set_attributes(self, attributes: Mapping[str, str]) -> None:
def set_attributes(self, attributes: Mapping[str, str | bool]) -> None:
"""Set the state attributes."""
self._attributes = {**attributes}

@property
def extra_state_attributes(self) -> dict[str, str | list[str]]:
def extra_state_attributes(self) -> dict[str, str | list[str] | bool]:
"""Return the state attributes."""
if self._prefixes:
self._attributes[ATTR_PREFIXES] = self._prefixes
return self._attributes

def number_to_name(self, number: str) -> str:
"""Return a name for a given phone number."""
return self._fritzbox_phonebook.get_name(number)
def number_to_contact(self, number: str) -> Contact:
"""Return a contact for a given phone number."""
return self._fritzbox_phonebook.get_contact(number)

def update(self) -> None:
"""Update the phonebook if it is defined."""
Expand Down Expand Up @@ -225,35 +225,42 @@ def _parse(self, event: str) -> None:
df_in = "%d.%m.%y %H:%M:%S"
df_out = "%Y-%m-%dT%H:%M:%S"
isotime = datetime.strptime(line[0], df_in).strftime(df_out)
att: dict[str, str | bool]
if line[1] == FritzState.RING:
self._sensor.set_state(CallState.RINGING)
contact = self._sensor.number_to_contact(line[3])
att = {
"type": "incoming",
"from": line[3],
"to": line[4],
"device": line[5],
"initiated": isotime,
"from_name": self._sensor.number_to_name(line[3]),
"from_name": contact.name,
"vip": contact.vip,
}
self._sensor.set_attributes(att)
elif line[1] == FritzState.CALL:
self._sensor.set_state(CallState.DIALING)
contact = self._sensor.number_to_contact(line[5])
att = {
"type": "outgoing",
"from": line[4],
"to": line[5],
"device": line[6],
"initiated": isotime,
"to_name": self._sensor.number_to_name(line[5]),
"to_name": contact.name,
"vip": contact.vip,
}
self._sensor.set_attributes(att)
elif line[1] == FritzState.CONNECT:
self._sensor.set_state(CallState.TALKING)
contact = self._sensor.number_to_contact(line[4])
att = {
"with": line[4],
"device": line[3],
"accepted": isotime,
"with_name": self._sensor.number_to_name(line[4]),
"with_name": contact.name,
"vip": contact.vip,
}
self._sensor.set_attributes(att)
elif line[1] == FritzState.DISCONNECT:
Expand Down
3 changes: 2 additions & 1 deletion homeassistant/components/fritzbox_callmonitor/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@
"accepted": { "name": "Accepted" },
"with_name": { "name": "With name" },
"duration": { "name": "Duration" },
"closed": { "name": "Closed" }
"closed": { "name": "Closed" },
"vip": { "name": "Important" }
}
}
}
Expand Down

0 comments on commit 7e2d3eb

Please sign in to comment.