diff --git a/custom_components/tapo/binary_sensor.py b/custom_components/tapo/binary_sensor.py new file mode 100644 index 0000000..70469cc --- /dev/null +++ b/custom_components/tapo/binary_sensor.py @@ -0,0 +1,17 @@ +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant + +from custom_components.tapo.common_setup import TapoUpdateCoordinator +from custom_components.tapo.tapo_sensor_entity import ( + TapoOverheatSensor, +) +from custom_components.tapo.const import ( + DOMAIN, +) + + +async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry, async_add_devices): + # get tapo helper + coordinator: TapoUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] + sensors = [TapoOverheatSensor(coordinator)] + async_add_devices(sensors, True) diff --git a/custom_components/tapo/common_setup.py b/custom_components/tapo/common_setup.py index 38b312d..a15b3f7 100644 --- a/custom_components/tapo/common_setup.py +++ b/custom_components/tapo/common_setup.py @@ -97,4 +97,4 @@ async def _update_with_fallback(self, retry=True): except Exception as error: if retry: await self.api.login() - return await self._update_with_fallback(False) \ No newline at end of file + return await self._update_with_fallback(False) diff --git a/custom_components/tapo/config_flow.py b/custom_components/tapo/config_flow.py index a0e2d40..4bd70fc 100755 --- a/custom_components/tapo/config_flow.py +++ b/custom_components/tapo/config_flow.py @@ -22,7 +22,15 @@ # TODO adjust the data schema to the data that you need STEP_USER_DATA_SCHEMA = vol.Schema( - {CONF_HOST: str, CONF_USERNAME: str, CONF_PASSWORD: str} + { + vol.Required( + CONF_HOST, description="The IP address of your tapo device (must be static)" + ): str, + vol.Required( + CONF_USERNAME, description="The username used with Tapo App, so your email" + ): str, + vol.Required(CONF_PASSWORD, description="The password used with Tapo App"): str, + } ) HOST_REGEX = r"^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$|^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)+([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])$" @@ -39,7 +47,8 @@ async def async_step_user(self, user_input=None): """Handle the initial step.""" if user_input is None: return self.async_show_form( - step_id="user", data_schema=STEP_USER_DATA_SCHEMA + step_id="user", + data_schema=STEP_USER_DATA_SCHEMA, ) errors = {} diff --git a/custom_components/tapo/const.py b/custom_components/tapo/const.py index e15f526..c435039 100755 --- a/custom_components/tapo/const.py +++ b/custom_components/tapo/const.py @@ -21,8 +21,7 @@ ISSUE_URL = "https://github.com/petretiandrea/home-assistant-tapo-p100/issues" # list the platforms that you want to support. -# TODO: add suport for ligth and use "model" from get_state of tapo -PLATFORMS = ["switch", "light", "sensor"] +PLATFORMS = ["switch", "light", "sensor", "binary_sensor"] CONF_HOST = "host" CONF_USERNAME = "username" diff --git a/custom_components/tapo/sensor.py b/custom_components/tapo/sensor.py index e2a9e60..c19bc40 100644 --- a/custom_components/tapo/sensor.py +++ b/custom_components/tapo/sensor.py @@ -4,6 +4,8 @@ from custom_components.tapo.common_setup import TapoUpdateCoordinator from custom_components.tapo.tapo_sensor_entity import ( TapoCurrentEnergySensor, + TapoOverheatSensor, + TapoSignalSensor, TapoTodayEnergySensor, ) from custom_components.tapo.const import ( @@ -18,7 +20,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry, async_add_devices): # get tapo helper coordinator: TapoUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] + sensors = [TapoSignalSensor(coordinator)] if coordinator.data.model.lower() in SUPPORTED_DEVICE_AS_SWITCH_POWER_MONITOR: - sensors = [factory(coordinator) for factory in SUPPORTED_SENSOR] - async_add_devices(sensors, True) + sensors.extend([factory(coordinator) for factory in SUPPORTED_SENSOR]) + + async_add_devices(sensors, True) diff --git a/custom_components/tapo/tapo_sensor_entity.py b/custom_components/tapo/tapo_sensor_entity.py index fd2c4ad..a1011af 100644 --- a/custom_components/tapo/tapo_sensor_entity.py +++ b/custom_components/tapo/tapo_sensor_entity.py @@ -1,10 +1,9 @@ from dataclasses import dataclass -import enum from typing import Optional -from homeassistant.config_entries import ConfigEntry -from homeassistant.core import HomeAssistant -from homeassistant.components.switch import SwitchEntity -from homeassistant.components.sensor import STATE_CLASS_MEASUREMENT, SensorEntity +from homeassistant.components.sensor import ( + STATE_CLASS_MEASUREMENT, + SensorEntity, +) from homeassistant.components.sensor import ( STATE_CLASS_TOTAL_INCREASING, ) @@ -13,6 +12,8 @@ ENERGY_KILO_WATT_HOUR, DEVICE_CLASS_ENERGY, DEVICE_CLASS_POWER, + DEVICE_CLASS_SIGNAL_STRENGTH, + SIGNAL_STRENGTH_DECIBELS_MILLIWATT, ) from homeassistant.helpers.typing import StateType from custom_components.tapo.common_setup import TapoUpdateCoordinator @@ -72,10 +73,9 @@ def __init__(self, coordiantor: TapoUpdateCoordinator): @property def native_value(self) -> StateType: - if self.coordinator.data.energy_info != None: + if self.coordinator.data.energy_info is not None: return self.coordinator.data.energy_info.today_energy / 1000 - else: - return None + return None class TapoCurrentEnergySensor(TapoSensor): @@ -93,7 +93,46 @@ def __init__(self, coordiantor: TapoUpdateCoordinator): @property def native_value(self) -> StateType: data: TapoDeviceState = self.coordinator.data - if data.energy_info != None: + if data.energy_info is not None: return data.energy_info.current_power / 1000 - else: - return None + return None + + +class TapoOverheatSensor(TapoSensor): + def __init__(self, coordiantor: TapoUpdateCoordinator): + super().__init__( + coordiantor, + SensorConfig( + name="overheat", + device_class="heat", + state_class=None, + unit_measure=None, + ), + ) + + @property + def native_value(self) -> StateType: + data: TapoDeviceState = self.coordinator.data + if data is not None: + return data.overheated + return None + + +class TapoSignalSensor(TapoSensor): + def __init__(self, coordiantor: TapoUpdateCoordinator): + super().__init__( + coordiantor, + SensorConfig( + name="signal level", + device_class=DEVICE_CLASS_SIGNAL_STRENGTH, + state_class=None, + unit_measure=SIGNAL_STRENGTH_DECIBELS_MILLIWATT, + ), + ) + + @property + def native_value(self) -> StateType: + data: TapoDeviceState = self.coordinator.data + if data is not None: + return data.rssi + return 0 \ No newline at end of file