Add missed call sensor to Freebox (#36895)

This commit is contained in:
Quentame 2020-06-30 21:55:46 +02:00 committed by GitHub
parent c1ec8971ae
commit 01ba578016
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 83 additions and 36 deletions

View file

@ -46,6 +46,15 @@ CONNECTION_SENSORS = {
},
}
CALL_SENSORS = {
"missed": {
SENSOR_NAME: "Freebox missed calls",
SENSOR_UNIT: None,
SENSOR_ICON: "mdi:phone-missed",
SENSOR_DEVICE_CLASS: None,
},
}
TEMPERATURE_SENSOR_TEMPLATE = {
SENSOR_NAME: None,
SENSOR_UNIT: TEMP_CELSIUS,

View file

@ -1,6 +1,5 @@
"""Support for Freebox devices (Freebox v6 and Freebox mini 4K)."""
from datetime import datetime
import logging
from typing import Dict
from homeassistant.components.device_tracker import SOURCE_TYPE_ROUTER
@ -14,8 +13,6 @@ from homeassistant.helpers.typing import HomeAssistantType
from .const import DEFAULT_DEVICE_NAME, DEVICE_ICONS, DOMAIN
from .router import FreeboxRouter
_LOGGER = logging.getLogger(__name__)
async def async_setup_entry(
hass: HomeAssistantType, entry: ConfigEntry, async_add_entities
@ -65,9 +62,8 @@ class FreeboxDevice(ScannerEntity):
self._active = False
self._attrs = {}
self._unsub_dispatcher = None
def update(self) -> None:
@callback
def async_update_state(self) -> None:
"""Update the Freebox device."""
device = self._router.devices[self._mac]
self._active = device["active"]
@ -128,21 +124,24 @@ class FreeboxDevice(ScannerEntity):
"""No polling needed."""
return False
async def async_on_demand_update(self):
@callback
def async_on_demand_update(self):
"""Update state."""
self.async_schedule_update_ha_state(True)
self.async_update_state()
self.async_write_ha_state()
async def async_added_to_hass(self):
"""Register state update callback."""
self._unsub_dispatcher = async_dispatcher_connect(
self.hass, self._router.signal_device_update, self.async_on_demand_update
self.async_update_state()
self.async_on_remove(
async_dispatcher_connect(
self.hass,
self._router.signal_device_update,
self.async_on_demand_update,
)
)
async def async_will_remove_from_hass(self):
"""Clean up after entity before removal."""
self._unsub_dispatcher()
def icon_for_freebox_device(device) -> str:
"""Return a host icon from his type."""
"""Return a device icon from its type."""
return DEVICE_ICONS.get(device["host_type"], "mdi:help-network")

View file

@ -2,7 +2,7 @@
from datetime import datetime, timedelta
import logging
from pathlib import Path
from typing import Dict, Optional
from typing import Any, Dict, List, Optional
from aiofreepybox import Freepybox
from aiofreepybox.api.wifi import Wifi
@ -47,9 +47,10 @@ class FreeboxRouter:
self._sw_v = None
self._attrs = {}
self.devices: Dict[str, any] = {}
self.devices: Dict[str, Any] = {}
self.sensors_temperature: Dict[str, int] = {}
self.sensors_connection: Dict[str, float] = {}
self.call_list: List[Dict[str, Any]] = []
self.listeners = []
@ -81,7 +82,7 @@ class FreeboxRouter:
async def update_devices(self) -> None:
"""Update Freebox devices."""
new_device = False
fbx_devices: Dict[str, any] = await self._api.lan.get_hosts_list()
fbx_devices: Dict[str, Any] = await self._api.lan.get_hosts_list()
# Adds the Freebox itself
fbx_devices.append(
@ -111,7 +112,7 @@ class FreeboxRouter:
async def update_sensors(self) -> None:
"""Update Freebox sensors."""
# System sensors
syst_datas: Dict[str, any] = await self._api.system.get_config()
syst_datas: Dict[str, Any] = await self._api.system.get_config()
# According to the doc `syst_datas["sensors"]` is temperature sensors in celsius degree.
# Name and id of sensors may vary under Freebox devices.
@ -119,7 +120,7 @@ class FreeboxRouter:
self.sensors_temperature[sensor["name"]] = sensor["value"]
# Connection sensors
connection_datas: Dict[str, any] = await self._api.connection.get_status()
connection_datas: Dict[str, Any] = await self._api.connection.get_status()
for sensor_key in CONNECTION_SENSORS:
self.sensors_connection[sensor_key] = connection_datas[sensor_key]
@ -134,6 +135,8 @@ class FreeboxRouter:
"serial": syst_datas["serial"],
}
self.call_list = await self._api.call.get_call_list()
async_dispatcher_send(self.hass, self.signal_sensor_update)
async def reboot(self) -> None:
@ -147,7 +150,7 @@ class FreeboxRouter:
self._api = None
@property
def device_info(self) -> Dict[str, any]:
def device_info(self) -> Dict[str, Any]:
"""Return the device information."""
return {
"connections": {(CONNECTION_NETWORK_MAC, self.mac)},
@ -173,8 +176,8 @@ class FreeboxRouter:
return f"{DOMAIN}-{self._host}-sensor-update"
@property
def sensors(self) -> Wifi:
"""Return the wifi."""
def sensors(self) -> Dict[str, Any]:
"""Return sensors."""
return {**self.sensors_temperature, **self.sensors_connection}
@property

View file

@ -1,14 +1,16 @@
"""Support for Freebox devices (Freebox v6 and Freebox mini 4K)."""
import logging
from typing import Dict
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import DATA_RATE_KILOBYTES_PER_SECOND
from homeassistant.core import callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.typing import HomeAssistantType
import homeassistant.util.dt as dt_util
from .const import (
CALL_SENSORS,
CONNECTION_SENSORS,
DOMAIN,
SENSOR_DEVICE_CLASS,
@ -19,8 +21,6 @@ from .const import (
)
from .router import FreeboxRouter
_LOGGER = logging.getLogger(__name__)
async def async_setup_entry(
hass: HomeAssistantType, entry: ConfigEntry, async_add_entities
@ -43,6 +43,9 @@ async def async_setup_entry(
FreeboxSensor(router, sensor_key, CONNECTION_SENSORS[sensor_key])
)
for sensor_key in CALL_SENSORS:
entities.append(FreeboxCallSensor(router, sensor_key, CALL_SENSORS[sensor_key]))
async_add_entities(entities, True)
@ -62,9 +65,8 @@ class FreeboxSensor(Entity):
self._device_class = sensor[SENSOR_DEVICE_CLASS]
self._unique_id = f"{self._router.mac} {self._name}"
self._unsub_dispatcher = None
def update(self) -> None:
@callback
def async_update_state(self) -> None:
"""Update the Freebox sensor."""
state = self._router.sensors[self._sensor_type]
if self._unit == DATA_RATE_KILOBYTES_PER_SECOND:
@ -112,16 +114,50 @@ class FreeboxSensor(Entity):
"""No polling needed."""
return False
async def async_on_demand_update(self):
@callback
def async_on_demand_update(self):
"""Update state."""
self.async_schedule_update_ha_state(True)
self.async_update_state()
self.async_write_ha_state()
async def async_added_to_hass(self):
"""Register state update callback."""
self._unsub_dispatcher = async_dispatcher_connect(
self.hass, self._router.signal_sensor_update, self.async_on_demand_update
self.async_update_state()
self.async_on_remove(
async_dispatcher_connect(
self.hass,
self._router.signal_sensor_update,
self.async_on_demand_update,
)
)
async def async_will_remove_from_hass(self):
"""Clean up after entity before removal."""
self._unsub_dispatcher()
class FreeboxCallSensor(FreeboxSensor):
"""Representation of a Freebox call sensor."""
def __init__(
self, router: FreeboxRouter, sensor_type: str, sensor: Dict[str, any]
) -> None:
"""Initialize a Freebox call sensor."""
self._call_list_for_type = []
super().__init__(router, sensor_type, sensor)
@callback
def async_update_state(self) -> None:
"""Update the Freebox call sensor."""
self._call_list_for_type = []
for call in self._router.call_list:
if not call["new"]:
continue
if call["type"] == self._sensor_type:
self._call_list_for_type.append(call)
self._state = len(self._call_list_for_type)
@property
def device_state_attributes(self) -> Dict[str, any]:
"""Return device specific state attributes."""
return {
dt_util.utc_from_timestamp(call["datetime"]).isoformat(): call["name"]
for call in self._call_list_for_type
}