Skip to content

Commit

Permalink
Pull 559 preparation (#634)
Browse files Browse the repository at this point in the history
* changes

* tapo-siren-fix

* getMost-list-breaking-change

* siren-and-select

* comments

* fixes-and-comments

* numbers

* siren

* unneded-mapings

* cleanup

* testing-fixes

* cleanup

* Fix: PR does not work with C200 1.0 1.3.6

* fix-siren

* Update README.md to add instructions in case the cloud password does not work.

* Update README.md

* Update README.md

* Update README.md

* Implement color mode

Reference:
https://developers.home-assistant.io/docs/core/entity/light#color-modes

> New integrations must implement both color_mode and supported_color_modes.
> If an integration is upgraded to support color mode, both color_mode and
> supported_color_modes should be implemented.

This also fixes this warning at startup:

```
WARNING (MainThread) [homeassistant.components.light] None (<class 'custom_components.tapo_control.light.TapoWhitelight'>) does not set supported color modes, this will stop working in Home Assistant Core 2025.3, please create a bug report at https://github.com/JurajNyiri/HomeAssistant-Tapo-Control/issues
```

* Create ru.json

* Update README.md

* Update README.md

* Update README.md

* Update README.md

* Fix #568: IP address is not a supported Tapo device

* Update issues.yml

* Update issues.yml

* Update issues.yml

* Update issues.yml

* Update issues.yml

* Update issues.yml

* Update issues.yml

* Update issues.yml

* Update issues.yml

* Update issues.yml

* Update issues.yml

* Update issues.yml

* Update issues.yml

* Update issues.yml

Temporarily disable cloud password close bot

* Update issues.yml

* Update README.md

* Update README.md

* Update README.md

* Update README.md

* Update README.md

* Update README.md

* changes

* tapo-siren-fix

* getMost-list-breaking-change

* siren-and-select

* comments

* fixes-and-comments

* numbers

* siren

* unneded-mapings

* cleanup

* testing-fixes

* cleanup

* fix-siren

* Handle cleanup of config entry

* Fix: PR does not work with C200 1.0 1.3.6

* alarm-status-false-default

* Fix #616: Warning for async_forward_entry_setup

* Fix #612, #615, #530, #525

* Fix: Blocking call when copying file from cold to hot storage

* Add: Binary sensor with details about media sync setup

* Fix #572: ffmpeg for sound detection doesn't terminate on Home Assistant restart

* Fix #561: Unexpected error fetching Tapo resource status data

* Fix #571: Incorrect device_class for D230 doorbell battery sensor

* Fix: Descalate "Recording is currently in progress." warning to info on media sync

* Add: HDR switch

* Add: Experimental optimization for battery cameras

* Fix: Update interval is now based on whether the device has a battery or not

* Update: Automatically determine whether the device is currently running on battery or not and adjust update interval

* Fix: Camera entities ignoring update threshold

* Update README.md

* Update README.md

* Create add_camera_with_new_firmware.md

* Update README.md

* Update add_camera_with_new_firmware.md

* Update add_camera_with_new_firmware.md

* Fix #629, #621: Setup for switch entities fails for some cameras

* Add #457: Battery optimization for solar cameras

* Update: Migrate update interval to config entry storage

* Add: Configurable update interval

* Update: Bump version

* Update: Guides for new firmwares workaround

* Update: Readme instructions about stopping use of tapo app not required

* Update: Readme formatting

* Update README.md

* Update add_camera_with_new_firmware.md

* Update README.md

* Fix: Removal of disabled device results in error and warning, time sync debug log

* Fix #450: Config entry name resets on reconfiguration

* Update: Bump version

* Fix: stopManualAlarm instead of start on siren off

* Fix: Mute error on siren on/off

* Update: Revert result_has_error changes

* Update: Ensure compatibility with #632

---------

Co-authored-by: marcosngomezi <[email protected]>
Co-authored-by: Antoine Buchser <[email protected]>
Co-authored-by: L. López <[email protected]>
Co-authored-by: Virenbar <[email protected]>
Co-authored-by: Nathan Spencer <[email protected]>
  • Loading branch information
6 people authored Jul 20, 2024
1 parent 21a5e32 commit 4255ad1
Show file tree
Hide file tree
Showing 7 changed files with 356 additions and 45 deletions.
2 changes: 1 addition & 1 deletion custom_components/tapo_control/camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ def updateTapo(self, camData):

for attr, value in camData["basic_info"].items():
self._attr_extra_state_attributes[attr] = value
self._attr_extra_state_attributes["alarm"] = camData["alarm"]
self._attr_extra_state_attributes["alarm"] = camData["alarm_config"]["automatic"]
self._attr_extra_state_attributes["user"] = camData["user"]
# Disable incorrect location report by camera
self._attr_extra_state_attributes["longitude"] = 0
Expand Down
2 changes: 1 addition & 1 deletion custom_components/tapo_control/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

from homeassistant.helpers import config_validation as cv

PYTAPO_REQUIRED_VERSION = "3.3.23"
PYTAPO_REQUIRED_VERSION = "3.3.24"
DOMAIN = "tapo_control"
BRAND = "TP-Link"
ALARM_MODE = "alarm_mode"
Expand Down
4 changes: 2 additions & 2 deletions custom_components/tapo_control/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
"codeowners": [
"@JurajNyiri"
],
"version": "5.4.30",
"version": "5.4.31",
"requirements": [
"pytapo==3.3.23"
"pytapo==3.3.24"
],
"dependencies": [
"ffmpeg",
Expand Down
135 changes: 134 additions & 1 deletion custom_components/tapo_control/number.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from homeassistant.config_entries import ConfigEntry
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.entity import EntityCategory
from homeassistant.const import STATE_UNAVAILABLE
from homeassistant.const import STATE_UNAVAILABLE,UnitOfTime

from .const import DOMAIN, LOGGER
from .tapo.entities import TapoEntity, TapoNumberEntity
Expand Down Expand Up @@ -73,6 +73,23 @@ async def setupEntities(entry):
if tapoSpeakerVolume:
LOGGER.debug("Adding tapoSpeakerVolume...")
numbers.append(tapoSpeakerVolume)
if (
"alarm_config" in entry["camData"]
and ("siren_volume" in entry["camData"]["alarm_config"] or "alarm_volume" in entry["camData"]["alarm_config"])
):
tapoSirenVolume = TapoSirenVolume(entry,hass,config_entry)
if tapoSirenVolume:
LOGGER.debug("Adding TapoSirenVolume...")
numbers.append(tapoSirenVolume)

if (
"alarm_config" in entry["camData"]
and ("siren_duration" in entry["camData"]["alarm_config"] or "alarm_duration" in entry["camData"]["alarm_config"])
):
tapoSirenDuration = TapoSirenDuration(entry,hass,config_entry)
if tapoSirenDuration:
LOGGER.debug("Adding TapoSirenDuration...")
numbers.append(tapoSirenDuration)

return numbers

Expand Down Expand Up @@ -241,3 +258,119 @@ def updateTapo(self, camData):
self._attr_state = STATE_UNAVAILABLE
else:
self._attr_state = camData["speakerVolume"]

class TapoSirenVolume(TapoNumberEntity):
def __init__(self, entry: dict, hass: HomeAssistant, config_entry):
LOGGER.debug("TapoSirenVolume - init - start")
self._attr_min_value = 1
self._attr_max_value = 10
self._attr_native_min_value = 1
self._attr_native_max_value = 10
self._attr_step = 1
self._hass = hass
self.is_hub = entry["camData"]["alarm_is_hubSiren"]
if "siren_volume" in entry["camData"]["alarm_config"]:
self.value_key = "siren_volume"
else:
self.value_key = "alarm_volume"

self._attr_native_value = entry["camData"]["alarm_config"][self.value_key]

TapoNumberEntity.__init__(
self,
"Siren volume",
entry,
hass,
config_entry,
"mdi:volume-high",
)
LOGGER.debug("TapoSirenVolume - init - end")

async def async_update(self) -> None:
await self._coordinator.async_request_refresh()

@property
def entity_category(self):
return EntityCategory.CONFIG

async def async_set_native_value(self, value: float) -> None:
if self.is_hub:
result = await self._hass.async_add_executor_job(
self._controller.setHubSirenConfig, None, None, str(int(value))
)
else:
strval = "low"
if value>3:
strval = "normal"
if value>7:
strval = "high"
result = await self._hass.async_add_executor_job(
self._controller.executeFunction, "setAlarmConfig", {"msg_alarm": {self.value_key: strval}}
)
if "error_code" not in result or result["error_code"] == 0:
self._attr_state = value
self.async_write_ha_state()
await self._coordinator.async_request_refresh()

def updateTapo(self, camData):
if not camData:
self._attr_state = STATE_UNAVAILABLE
else:
self._attr_state = camData["alarm_config"][self.value_key]

class TapoSirenDuration(TapoNumberEntity):
def __init__(self, entry: dict, hass: HomeAssistant, config_entry):
LOGGER.debug("TapoSirenDuration - init - start")
self._attr_min_value = 1
self._attr_native_min_value = 1
self._attr_max_value = 300
self._attr_native_max_value = 300
self.is_hub = entry["camData"]["alarm_is_hubSiren"]
if self.is_hub:
self._attr_max_value = 599
self._attr_native_max_value = 599
self._attr_step = 1
self._attr_native_unit_of_measurement: UnitOfTime.SECONDS
self._hass = hass
if "siren_duration" in entry["camData"]["alarm_config"]:
self.value_key = "siren_duration"
else:
self.value_key = "alarm_duration"
self._attr_native_value = entry["camData"]["alarm_config"][self.value_key]

TapoNumberEntity.__init__(
self,
"Siren duration",
entry,
hass,
config_entry,
"mdi:clock-end",
)
LOGGER.debug("TapoSirenDuration - init - end")

async def async_update(self) -> None:
await self._coordinator.async_request_refresh()

@property
def entity_category(self):
return EntityCategory.CONFIG

async def async_set_native_value(self, value: float) -> None:
if self.is_hub:
result = await self._hass.async_add_executor_job(
self._controller.setHubSirenConfig, int(value)
)
else:
result = await self._hass.async_add_executor_job(
self._controller.executeFunction, "setAlarmConfig", {"msg_alarm": {self.value_key: int(value)}}
)
if "error_code" not in result or result["error_code"] == 0:
self._attr_state = value
self.async_write_ha_state()
await self._coordinator.async_request_refresh()

def updateTapo(self, camData):
if not camData:
self._attr_state = STATE_UNAVAILABLE
else:
self._attr_state = camData["alarm_config"][self.value_key]
60 changes: 57 additions & 3 deletions custom_components/tapo_control/select.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,13 @@ async def setupEntities(entry):
LOGGER.debug("Adding tapoAutomaticAlarmModeSelect...")
selects.append(tapoAutomaticAlarmModeSelect)

tapoSirenTypeSelect = await check_and_create(
entry, hass, TapoSirenTypeSelect, "getSirenTypeList", config_entry
)
if tapoSirenTypeSelect:
LOGGER.debug("Adding tapoSirenTypeSelect...")
selects.append(tapoSirenTypeSelect)

tapoMotionDetectionSelect = await check_and_create(
entry, hass, TapoMotionDetectionSelect, "getMotionDetection", config_entry
)
Expand Down Expand Up @@ -370,11 +377,11 @@ def updateTapo(self, camData):
if not camData:
self._attr_state = STATE_UNAVAILABLE
else:
if camData["alarm"] == "off":
if camData["alarm_config"]["automatic"] == "off":
self._attr_current_option = "off"
else:
light = "light" in camData["alarm_mode"]
sound = "sound" in camData["alarm_mode"]
light = "light" in camData["alarm_config"]["mode"]
sound = "sound" in camData["alarm_config"]["mode"]
if light and sound:
self._attr_current_option = "both"
elif light and not sound:
Expand Down Expand Up @@ -837,3 +844,50 @@ async def async_select_option(self, option: str) -> None:
@property
def entity_category(self):
return None


class TapoSirenTypeSelect(TapoSelectEntity):
def __init__(self, entry: dict, hass: HomeAssistant, config_entry):
self._attr_options = entry["camData"]["alarm_siren_type_list"]
self._attr_current_option = entry["camData"]["alarm_config"]["siren_type"]
self.hub = entry["camData"]["alarm_is_hubSiren"]
TapoSelectEntity.__init__(
self,
"Siren Type",
entry,
hass,
config_entry,
"mdi:home-sound-in-outline",
)

def updateTapo(self, camData):
if not camData:
self._attr_state = STATE_UNAVAILABLE
else:
if "siren_type" in camData["alarm_config"]:
self._attr_current_option = camData["alarm_config"]["siren_type"]
else:
self._attr_state = STATE_UNAVAILABLE

self._attr_state = self._attr_current_option
LOGGER.debug("Updating TapoHubSirenTypeSelect to: " + str(self._attr_state))

async def async_select_option(self, option: str) -> None:
if self.hub:
result = await self.hass.async_add_executor_job(
self._controller.setHubSirenConfig, None, option
)
else:
result = await self.hass.async_add_executor_job(
self._controller.executeFunction,
"setAlarmConfig",
{
"msg_alarm": {
"siren_type": option,
}
},
)
if "error_code" not in result or result["error_code"] == 0:
self._attr_state = option
self.async_write_ha_state()
await self._coordinator.async_request_refresh()
Loading

0 comments on commit 4255ad1

Please sign in to comment.