mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 11:17:21 +00:00
Add contact vip info to fritzbox_callmonitor sensor (#132913)
This commit is contained in:
parent
c0f6535d11
commit
7e2d3eb482
@ -3,6 +3,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from contextlib import suppress
|
from contextlib import suppress
|
||||||
|
from dataclasses import dataclass
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
@ -19,12 +20,33 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
MIN_TIME_PHONEBOOK_UPDATE = timedelta(hours=6)
|
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:
|
class FritzBoxPhonebook:
|
||||||
"""Connects to a FritzBox router and downloads its phone book."""
|
"""Connects to a FritzBox router and downloads its phone book."""
|
||||||
|
|
||||||
fph: FritzPhonebook
|
fph: FritzPhonebook
|
||||||
phonebook_dict: dict[str, list[str]]
|
phonebook_dict: dict[str, list[str]]
|
||||||
number_dict: dict[str, str]
|
contacts: list[Contact]
|
||||||
|
number_dict: dict[str, Contact]
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@ -56,27 +78,27 @@ class FritzBoxPhonebook:
|
|||||||
if self.phonebook_id is None:
|
if self.phonebook_id is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
self.phonebook_dict = self.fph.get_all_names(self.phonebook_id)
|
self.fph.get_all_name_numbers(self.phonebook_id)
|
||||||
self.number_dict = {
|
self.contacts = [
|
||||||
re.sub(REGEX_NUMBER, "", nr): name
|
Contact(c.name, c.numbers, getattr(c, "category", None))
|
||||||
for name, nrs in self.phonebook_dict.items()
|
for c in self.fph.phonebook.contacts
|
||||||
for nr in nrs
|
]
|
||||||
}
|
self.number_dict = {nr: c for c in self.contacts for nr in c.numbers}
|
||||||
_LOGGER.debug("Fritz!Box phone book successfully updated")
|
_LOGGER.debug("Fritz!Box phone book successfully updated")
|
||||||
|
|
||||||
def get_phonebook_ids(self) -> list[int]:
|
def get_phonebook_ids(self) -> list[int]:
|
||||||
"""Return list of phonebook ids."""
|
"""Return list of phonebook ids."""
|
||||||
return self.fph.phonebook_ids # type: ignore[no-any-return]
|
return self.fph.phonebook_ids # type: ignore[no-any-return]
|
||||||
|
|
||||||
def get_name(self, number: str) -> str:
|
def get_contact(self, number: str) -> Contact:
|
||||||
"""Return a name for a given phone number."""
|
"""Return a contact for a given phone number."""
|
||||||
number = re.sub(REGEX_NUMBER, "", str(number))
|
number = re.sub(REGEX_NUMBER, "", str(number))
|
||||||
|
|
||||||
with suppress(KeyError):
|
with suppress(KeyError):
|
||||||
return self.number_dict[number]
|
return self.number_dict[number]
|
||||||
|
|
||||||
if not self.prefixes:
|
if not self.prefixes:
|
||||||
return UNKNOWN_NAME
|
return unknown_contact
|
||||||
|
|
||||||
for prefix in self.prefixes:
|
for prefix in self.prefixes:
|
||||||
with suppress(KeyError):
|
with suppress(KeyError):
|
||||||
@ -84,4 +106,4 @@ class FritzBoxPhonebook:
|
|||||||
with suppress(KeyError):
|
with suppress(KeyError):
|
||||||
return self.number_dict[prefix + number.lstrip("0")]
|
return self.number_dict[prefix + number.lstrip("0")]
|
||||||
|
|
||||||
return UNKNOWN_NAME
|
return unknown_contact
|
||||||
|
@ -20,7 +20,7 @@ from homeassistant.helpers.device_registry import DeviceInfo
|
|||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
|
||||||
from . import FritzBoxCallMonitorConfigEntry
|
from . import FritzBoxCallMonitorConfigEntry
|
||||||
from .base import FritzBoxPhonebook
|
from .base import Contact, FritzBoxPhonebook
|
||||||
from .const import (
|
from .const import (
|
||||||
ATTR_PREFIXES,
|
ATTR_PREFIXES,
|
||||||
CONF_PHONEBOOK,
|
CONF_PHONEBOOK,
|
||||||
@ -96,7 +96,7 @@ class FritzBoxCallSensor(SensorEntity):
|
|||||||
self._host = host
|
self._host = host
|
||||||
self._port = port
|
self._port = port
|
||||||
self._monitor: FritzBoxCallMonitor | None = None
|
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_translation_placeholders = {"phonebook_name": phonebook_name}
|
||||||
self._attr_unique_id = unique_id
|
self._attr_unique_id = unique_id
|
||||||
@ -152,20 +152,20 @@ class FritzBoxCallSensor(SensorEntity):
|
|||||||
"""Set the state."""
|
"""Set the state."""
|
||||||
self._attr_native_value = 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."""
|
"""Set the state attributes."""
|
||||||
self._attributes = {**attributes}
|
self._attributes = {**attributes}
|
||||||
|
|
||||||
@property
|
@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."""
|
"""Return the state attributes."""
|
||||||
if self._prefixes:
|
if self._prefixes:
|
||||||
self._attributes[ATTR_PREFIXES] = self._prefixes
|
self._attributes[ATTR_PREFIXES] = self._prefixes
|
||||||
return self._attributes
|
return self._attributes
|
||||||
|
|
||||||
def number_to_name(self, number: str) -> str:
|
def number_to_contact(self, number: str) -> Contact:
|
||||||
"""Return a name for a given phone number."""
|
"""Return a contact for a given phone number."""
|
||||||
return self._fritzbox_phonebook.get_name(number)
|
return self._fritzbox_phonebook.get_contact(number)
|
||||||
|
|
||||||
def update(self) -> None:
|
def update(self) -> None:
|
||||||
"""Update the phonebook if it is defined."""
|
"""Update the phonebook if it is defined."""
|
||||||
@ -225,35 +225,42 @@ class FritzBoxCallMonitor:
|
|||||||
df_in = "%d.%m.%y %H:%M:%S"
|
df_in = "%d.%m.%y %H:%M:%S"
|
||||||
df_out = "%Y-%m-%dT%H:%M:%S"
|
df_out = "%Y-%m-%dT%H:%M:%S"
|
||||||
isotime = datetime.strptime(line[0], df_in).strftime(df_out)
|
isotime = datetime.strptime(line[0], df_in).strftime(df_out)
|
||||||
|
att: dict[str, str | bool]
|
||||||
if line[1] == FritzState.RING:
|
if line[1] == FritzState.RING:
|
||||||
self._sensor.set_state(CallState.RINGING)
|
self._sensor.set_state(CallState.RINGING)
|
||||||
|
contact = self._sensor.number_to_contact(line[3])
|
||||||
att = {
|
att = {
|
||||||
"type": "incoming",
|
"type": "incoming",
|
||||||
"from": line[3],
|
"from": line[3],
|
||||||
"to": line[4],
|
"to": line[4],
|
||||||
"device": line[5],
|
"device": line[5],
|
||||||
"initiated": isotime,
|
"initiated": isotime,
|
||||||
"from_name": self._sensor.number_to_name(line[3]),
|
"from_name": contact.name,
|
||||||
|
"vip": contact.vip,
|
||||||
}
|
}
|
||||||
self._sensor.set_attributes(att)
|
self._sensor.set_attributes(att)
|
||||||
elif line[1] == FritzState.CALL:
|
elif line[1] == FritzState.CALL:
|
||||||
self._sensor.set_state(CallState.DIALING)
|
self._sensor.set_state(CallState.DIALING)
|
||||||
|
contact = self._sensor.number_to_contact(line[5])
|
||||||
att = {
|
att = {
|
||||||
"type": "outgoing",
|
"type": "outgoing",
|
||||||
"from": line[4],
|
"from": line[4],
|
||||||
"to": line[5],
|
"to": line[5],
|
||||||
"device": line[6],
|
"device": line[6],
|
||||||
"initiated": isotime,
|
"initiated": isotime,
|
||||||
"to_name": self._sensor.number_to_name(line[5]),
|
"to_name": contact.name,
|
||||||
|
"vip": contact.vip,
|
||||||
}
|
}
|
||||||
self._sensor.set_attributes(att)
|
self._sensor.set_attributes(att)
|
||||||
elif line[1] == FritzState.CONNECT:
|
elif line[1] == FritzState.CONNECT:
|
||||||
self._sensor.set_state(CallState.TALKING)
|
self._sensor.set_state(CallState.TALKING)
|
||||||
|
contact = self._sensor.number_to_contact(line[4])
|
||||||
att = {
|
att = {
|
||||||
"with": line[4],
|
"with": line[4],
|
||||||
"device": line[3],
|
"device": line[3],
|
||||||
"accepted": isotime,
|
"accepted": isotime,
|
||||||
"with_name": self._sensor.number_to_name(line[4]),
|
"with_name": contact.name,
|
||||||
|
"vip": contact.vip,
|
||||||
}
|
}
|
||||||
self._sensor.set_attributes(att)
|
self._sensor.set_attributes(att)
|
||||||
elif line[1] == FritzState.DISCONNECT:
|
elif line[1] == FritzState.DISCONNECT:
|
||||||
|
@ -78,7 +78,8 @@
|
|||||||
"accepted": { "name": "Accepted" },
|
"accepted": { "name": "Accepted" },
|
||||||
"with_name": { "name": "With name" },
|
"with_name": { "name": "With name" },
|
||||||
"duration": { "name": "Duration" },
|
"duration": { "name": "Duration" },
|
||||||
"closed": { "name": "Closed" }
|
"closed": { "name": "Closed" },
|
||||||
|
"vip": { "name": "Important" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user