More sensors for SMS integration (#70486)
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>pull/73804/head
parent
305dff0dc1
commit
551929a175
|
@ -1,6 +1,9 @@
|
|||
"""The sms component."""
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
|
||||
import async_timeout
|
||||
import gammu # pylint: disable=import-error
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
|
||||
|
@ -8,8 +11,18 @@ from homeassistant.const import CONF_DEVICE, Platform
|
|||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||
|
||||
from .const import CONF_BAUD_SPEED, DEFAULT_BAUD_SPEED, DOMAIN, SMS_GATEWAY
|
||||
from .const import (
|
||||
CONF_BAUD_SPEED,
|
||||
DEFAULT_BAUD_SPEED,
|
||||
DEFAULT_SCAN_INTERVAL,
|
||||
DOMAIN,
|
||||
GATEWAY,
|
||||
NETWORK_COORDINATOR,
|
||||
SIGNAL_COORDINATOR,
|
||||
SMS_GATEWAY,
|
||||
)
|
||||
from .gateway import create_sms_gateway
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
@ -30,6 +43,8 @@ CONFIG_SCHEMA = vol.Schema(
|
|||
extra=vol.ALLOW_EXTRA,
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
"""Configure Gammu state machine."""
|
||||
|
@ -61,7 +76,21 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||
gateway = await create_sms_gateway(config, hass)
|
||||
if not gateway:
|
||||
return False
|
||||
hass.data[DOMAIN][SMS_GATEWAY] = gateway
|
||||
|
||||
signal_coordinator = SignalCoordinator(hass, gateway)
|
||||
network_coordinator = NetworkCoordinator(hass, gateway)
|
||||
|
||||
# Fetch initial data so we have data when entities subscribe
|
||||
await signal_coordinator.async_config_entry_first_refresh()
|
||||
await network_coordinator.async_config_entry_first_refresh()
|
||||
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
hass.data[DOMAIN][SMS_GATEWAY] = {
|
||||
SIGNAL_COORDINATOR: signal_coordinator,
|
||||
NETWORK_COORDINATOR: network_coordinator,
|
||||
GATEWAY: gateway,
|
||||
}
|
||||
|
||||
hass.config_entries.async_setup_platforms(entry, PLATFORMS)
|
||||
|
||||
return True
|
||||
|
@ -71,7 +100,51 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||
"""Unload a config entry."""
|
||||
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
if unload_ok:
|
||||
gateway = hass.data[DOMAIN].pop(SMS_GATEWAY)
|
||||
gateway = hass.data[DOMAIN].pop(SMS_GATEWAY)[GATEWAY]
|
||||
await gateway.terminate_async()
|
||||
|
||||
return unload_ok
|
||||
|
||||
|
||||
class SignalCoordinator(DataUpdateCoordinator):
|
||||
"""Signal strength coordinator."""
|
||||
|
||||
def __init__(self, hass, gateway):
|
||||
"""Initialize signal strength coordinator."""
|
||||
super().__init__(
|
||||
hass,
|
||||
_LOGGER,
|
||||
name="Device signal state",
|
||||
update_interval=timedelta(seconds=DEFAULT_SCAN_INTERVAL),
|
||||
)
|
||||
self._gateway = gateway
|
||||
|
||||
async def _async_update_data(self):
|
||||
"""Fetch device signal quality."""
|
||||
try:
|
||||
async with async_timeout.timeout(10):
|
||||
return await self._gateway.get_signal_quality_async()
|
||||
except gammu.GSMError as exc:
|
||||
raise UpdateFailed(f"Error communicating with device: {exc}") from exc
|
||||
|
||||
|
||||
class NetworkCoordinator(DataUpdateCoordinator):
|
||||
"""Network info coordinator."""
|
||||
|
||||
def __init__(self, hass, gateway):
|
||||
"""Initialize network info coordinator."""
|
||||
super().__init__(
|
||||
hass,
|
||||
_LOGGER,
|
||||
name="Device network state",
|
||||
update_interval=timedelta(seconds=DEFAULT_SCAN_INTERVAL),
|
||||
)
|
||||
self._gateway = gateway
|
||||
|
||||
async def _async_update_data(self):
|
||||
"""Fetch device network info."""
|
||||
try:
|
||||
async with async_timeout.timeout(10):
|
||||
return await self._gateway.get_network_info_async()
|
||||
except gammu.GSMError as exc:
|
||||
raise UpdateFailed(f"Error communicating with device: {exc}") from exc
|
||||
|
|
|
@ -1,8 +1,17 @@
|
|||
"""Constants for sms Component."""
|
||||
from typing import Final
|
||||
|
||||
from homeassistant.components.sensor import SensorDeviceClass, SensorEntityDescription
|
||||
from homeassistant.const import PERCENTAGE, SIGNAL_STRENGTH_DECIBELS
|
||||
from homeassistant.helpers.entity import EntityCategory
|
||||
|
||||
DOMAIN = "sms"
|
||||
SMS_GATEWAY = "SMS_GATEWAY"
|
||||
SMS_STATE_UNREAD = "UnRead"
|
||||
SIGNAL_COORDINATOR = "signal_coordinator"
|
||||
NETWORK_COORDINATOR = "network_coordinator"
|
||||
GATEWAY = "gateway"
|
||||
DEFAULT_SCAN_INTERVAL = 30
|
||||
CONF_BAUD_SPEED = "baud_speed"
|
||||
DEFAULT_BAUD_SPEED = "0"
|
||||
DEFAULT_BAUD_SPEEDS = [
|
||||
|
@ -27,3 +36,61 @@ DEFAULT_BAUD_SPEEDS = [
|
|||
{"value": "76800", "label": "76800"},
|
||||
{"value": "115200", "label": "115200"},
|
||||
]
|
||||
|
||||
SIGNAL_SENSORS: Final[dict[str, SensorEntityDescription]] = {
|
||||
"SignalStrength": SensorEntityDescription(
|
||||
key="SignalStrength",
|
||||
name="Signal Strength",
|
||||
device_class=SensorDeviceClass.SIGNAL_STRENGTH,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
native_unit_of_measurement=SIGNAL_STRENGTH_DECIBELS,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
"SignalPercent": SensorEntityDescription(
|
||||
key="SignalPercent",
|
||||
icon="mdi:signal-cellular-3",
|
||||
name="Signal Percent",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
entity_registry_enabled_default=True,
|
||||
),
|
||||
"BitErrorRate": SensorEntityDescription(
|
||||
key="BitErrorRate",
|
||||
name="Bit Error Rate",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
}
|
||||
|
||||
NETWORK_SENSORS: Final[dict[str, SensorEntityDescription]] = {
|
||||
"NetworkName": SensorEntityDescription(
|
||||
key="NetworkName",
|
||||
name="Network Name",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
"State": SensorEntityDescription(
|
||||
key="State",
|
||||
name="Network Status",
|
||||
entity_registry_enabled_default=True,
|
||||
),
|
||||
"NetworkCode": SensorEntityDescription(
|
||||
key="NetworkCode",
|
||||
name="GSM network code",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
"CID": SensorEntityDescription(
|
||||
key="CID",
|
||||
name="Cell ID",
|
||||
icon="mdi:radio-tower",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
"LAC": SensorEntityDescription(
|
||||
key="LAC",
|
||||
name="Local Area Code",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
}
|
||||
|
|
|
@ -154,6 +154,10 @@ class Gateway:
|
|||
"""Get the current signal level of the modem."""
|
||||
return await self._worker.get_signal_quality_async()
|
||||
|
||||
async def get_network_info_async(self):
|
||||
"""Get the current network info of the modem."""
|
||||
return await self._worker.get_network_info_async()
|
||||
|
||||
async def terminate_async(self):
|
||||
"""Terminate modem connection."""
|
||||
return await self._worker.terminate_async()
|
||||
|
|
|
@ -8,7 +8,7 @@ from homeassistant.components.notify import PLATFORM_SCHEMA, BaseNotificationSer
|
|||
from homeassistant.const import CONF_NAME, CONF_RECIPIENT, CONF_TARGET
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
from .const import DOMAIN, SMS_GATEWAY
|
||||
from .const import DOMAIN, GATEWAY, SMS_GATEWAY
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
@ -24,7 +24,7 @@ def get_service(hass, config, discovery_info=None):
|
|||
_LOGGER.error("SMS gateway not found, cannot initialize service")
|
||||
return
|
||||
|
||||
gateway = hass.data[DOMAIN][SMS_GATEWAY]
|
||||
gateway = hass.data[DOMAIN][SMS_GATEWAY][GATEWAY]
|
||||
|
||||
if discovery_info is None:
|
||||
number = config[CONF_RECIPIENT]
|
||||
|
|
|
@ -1,22 +1,20 @@
|
|||
"""Support for SMS dongle sensor."""
|
||||
import logging
|
||||
|
||||
import gammu # pylint: disable=import-error
|
||||
|
||||
from homeassistant.components.sensor import (
|
||||
SensorDeviceClass,
|
||||
SensorEntity,
|
||||
SensorEntityDescription,
|
||||
)
|
||||
from homeassistant.components.sensor import SensorEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import SIGNAL_STRENGTH_DECIBELS
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity import DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from .const import DOMAIN, SMS_GATEWAY
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
from .const import (
|
||||
DOMAIN,
|
||||
GATEWAY,
|
||||
NETWORK_COORDINATOR,
|
||||
NETWORK_SENSORS,
|
||||
SIGNAL_COORDINATOR,
|
||||
SIGNAL_SENSORS,
|
||||
SMS_GATEWAY,
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
|
@ -24,61 +22,46 @@ async def async_setup_entry(
|
|||
config_entry: ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the GSM Signal Sensor sensor."""
|
||||
gateway = hass.data[DOMAIN][SMS_GATEWAY]
|
||||
imei = await gateway.get_imei_async()
|
||||
async_add_entities(
|
||||
[
|
||||
GSMSignalSensor(
|
||||
hass,
|
||||
gateway,
|
||||
imei,
|
||||
SensorEntityDescription(
|
||||
key="signal",
|
||||
name=f"gsm_signal_imei_{imei}",
|
||||
device_class=SensorDeviceClass.SIGNAL_STRENGTH,
|
||||
native_unit_of_measurement=SIGNAL_STRENGTH_DECIBELS,
|
||||
entity_registry_enabled_default=False,
|
||||
),
|
||||
"""Set up all device sensors."""
|
||||
sms_data = hass.data[DOMAIN][SMS_GATEWAY]
|
||||
signal_coordinator = sms_data[SIGNAL_COORDINATOR]
|
||||
network_coordinator = sms_data[NETWORK_COORDINATOR]
|
||||
gateway = sms_data[GATEWAY]
|
||||
unique_id = str(await gateway.get_imei_async())
|
||||
entities = []
|
||||
for description in SIGNAL_SENSORS.values():
|
||||
entities.append(
|
||||
DeviceSensor(
|
||||
signal_coordinator,
|
||||
description,
|
||||
unique_id,
|
||||
)
|
||||
],
|
||||
True,
|
||||
)
|
||||
)
|
||||
for description in NETWORK_SENSORS.values():
|
||||
entities.append(
|
||||
DeviceSensor(
|
||||
network_coordinator,
|
||||
description,
|
||||
unique_id,
|
||||
)
|
||||
)
|
||||
async_add_entities(entities, True)
|
||||
|
||||
|
||||
class GSMSignalSensor(SensorEntity):
|
||||
"""Implementation of a GSM Signal sensor."""
|
||||
class DeviceSensor(CoordinatorEntity, SensorEntity):
|
||||
"""Implementation of a device sensor."""
|
||||
|
||||
def __init__(self, hass, gateway, imei, description):
|
||||
"""Initialize the GSM Signal sensor."""
|
||||
def __init__(self, coordinator, description, unique_id):
|
||||
"""Initialize the device sensor."""
|
||||
super().__init__(coordinator)
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(DOMAIN, str(imei))},
|
||||
identifiers={(DOMAIN, unique_id)},
|
||||
name="SMS Gateway",
|
||||
)
|
||||
self._attr_unique_id = str(imei)
|
||||
self._hass = hass
|
||||
self._gateway = gateway
|
||||
self._state = None
|
||||
self._attr_unique_id = f"{unique_id}_{description.key}"
|
||||
self.entity_description = description
|
||||
|
||||
@property
|
||||
def available(self):
|
||||
"""Return if the sensor data are available."""
|
||||
return self._state is not None
|
||||
|
||||
@property
|
||||
def native_value(self):
|
||||
"""Return the state of the device."""
|
||||
return self._state["SignalStrength"]
|
||||
|
||||
async def async_update(self):
|
||||
"""Get the latest data from the modem."""
|
||||
try:
|
||||
self._state = await self._gateway.get_signal_quality_async()
|
||||
except gammu.GSMError as exc:
|
||||
_LOGGER.error("Failed to read signal quality: %s", exc)
|
||||
|
||||
@property
|
||||
def extra_state_attributes(self):
|
||||
"""Return the sensor attributes."""
|
||||
return self._state
|
||||
return self.coordinator.data.get(self.entity_description.key)
|
||||
|
|
Loading…
Reference in New Issue