Skip to content

Commit

Permalink
doc: fix python formatting
Browse files Browse the repository at this point in the history
  • Loading branch information
fuatakgun committed Jun 16, 2023
1 parent 44ec1bb commit 3edb2f2
Show file tree
Hide file tree
Showing 10 changed files with 107 additions and 286 deletions.
33 changes: 7 additions & 26 deletions custom_components/eufy_security/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,14 @@
_LOGGER: logging.Logger = logging.getLogger(__package__)


async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback) -> None:
"""Setup binary sensor entities."""
coordinator: EufySecurityDataUpdateCoordinator = hass.data[DOMAIN][COORDINATOR]
product_properties = get_product_properties_by_filter(
[coordinator.devices.values(), coordinator.stations.values()],
PlatformToPropertyType[Platform.BINARY_SENSOR.name].value,
)
entities = [
EufySecurityBinarySensor(coordinator, metadata)
for metadata in product_properties
]
entities = [EufySecurityBinarySensor(coordinator, metadata) for metadata in product_properties]

for device in coordinator.devices.values():
entities.append(EufySecurityProductEntity(coordinator, device))
Expand All @@ -44,9 +37,7 @@ async def async_setup_entry(
class EufySecurityBinarySensor(BinarySensorEntity, EufySecurityEntity):
"""Base binary sensor entity for integration"""

def __init__(
self, coordinator: EufySecurityDataUpdateCoordinator, metadata: Metadata
) -> None:
def __init__(self, coordinator: EufySecurityDataUpdateCoordinator, metadata: Metadata) -> None:
super().__init__(coordinator, metadata)

@property
Expand All @@ -58,20 +49,14 @@ def is_on(self):
class EufySecurityProductEntity(BinarySensorEntity, CoordinatorEntity):
"""Debug entity for integration"""

def __init__(
self, coordinator: EufySecurityDataUpdateCoordinator, product: Product
) -> None:
def __init__(self, coordinator: EufySecurityDataUpdateCoordinator, product: Product) -> None:
super().__init__(coordinator)
self.product = product
self.product.set_state_update_listener(coordinator.async_update_listeners)

self._attr_unique_id = (
f"{DOMAIN}_{self.product.product_type.value}_{self.product.serial_no}_debug"
)
self._attr_unique_id = f"{DOMAIN}_{self.product.product_type.value}_{self.product.serial_no}_debug"
self._attr_should_poll = False
self._attr_name = (
f"{self.product.name} Debug ({self.product.product_type.value})"
)
self._attr_name = f"{self.product.name} Debug ({self.product.product_type.value})"

@property
def is_on(self):
Expand All @@ -81,11 +66,7 @@ def is_on(self):
@property
def extra_state_attributes(self):
return {
"properties": {
i: self.product.properties[i]
for i in self.product.properties
if i != "picture"
},
"properties": {i: self.product.properties[i] for i in self.product.properties if i != "picture"},
# "metadata": self.product.metadata_org,
"commands": self.product.commands,
"voices": self.product.voices if self.product.is_camera else None,
Expand Down
96 changes: 21 additions & 75 deletions custom_components/eufy_security/camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,69 +30,41 @@
_LOGGER: logging.Logger = logging.getLogger(__package__)


async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback) -> None:
"""Setup camera entities."""
coordinator: EufySecurityDataUpdateCoordinator = hass.data[DOMAIN][COORDINATOR]
product_properties = []
for product in coordinator.devices.values():
if product.is_camera is True:
product_properties.append(
Metadata.parse(product, {"name": "camera", "label": "Camera"})
)
product_properties.append(Metadata.parse(product, {"name": "camera", "label": "Camera"}))

entities = [
EufySecurityCamera(coordinator, metadata) for metadata in product_properties
]
entities = [EufySecurityCamera(coordinator, metadata) for metadata in product_properties]
async_add_entities(entities)

# register entity level services
platform = entity_platform.async_get_current_platform()
platform.async_register_entity_service("generate_image", {}, "_generate_image")
platform.async_register_entity_service(
"start_p2p_livestream", {}, "_start_livestream"
)
platform.async_register_entity_service(
"stop_p2p_livestream", {}, "_stop_livestream"
)
platform.async_register_entity_service(
"start_rtsp_livestream", {}, "_start_rtsp_livestream"
)
platform.async_register_entity_service(
"stop_rtsp_livestream", {}, "_stop_rtsp_livestream"
)
platform.async_register_entity_service(
"ptz", Schema.PTZ_SERVICE_SCHEMA.value, "_async_ptz"
)
platform.async_register_entity_service("start_p2p_livestream", {}, "_start_livestream")
platform.async_register_entity_service("stop_p2p_livestream", {}, "_stop_livestream")
platform.async_register_entity_service("start_rtsp_livestream", {}, "_start_rtsp_livestream")
platform.async_register_entity_service("stop_rtsp_livestream", {}, "_stop_rtsp_livestream")
platform.async_register_entity_service("ptz", Schema.PTZ_SERVICE_SCHEMA.value, "_async_ptz")
platform.async_register_entity_service("ptz_up", {}, "_async_ptz_up")
platform.async_register_entity_service("ptz_down", {}, "_async_ptz_down")
platform.async_register_entity_service("ptz_left", {}, "_async_ptz_left")
platform.async_register_entity_service("ptz_right", {}, "_async_ptz_right")
platform.async_register_entity_service("ptz_360", {}, "_async_ptz_360")

platform.async_register_entity_service(
"trigger_camera_alarm_with_duration",
Schema.TRIGGER_ALARM_SERVICE_SCHEMA.value,
"_async_alarm_trigger",
)
platform.async_register_entity_service("trigger_camera_alarm_with_duration", Schema.TRIGGER_ALARM_SERVICE_SCHEMA.value, "_async_alarm_trigger")
platform.async_register_entity_service("reset_alarm", {}, "_async_reset_alarm")
platform.async_register_entity_service(
"quick_response",
Schema.QUICK_RESPONSE_SERVICE_SCHEMA.value,
"_async_quick_response",
)
platform.async_register_entity_service("quick_response", Schema.QUICK_RESPONSE_SERVICE_SCHEMA.value, "_async_quick_response")
platform.async_register_entity_service("snooze", Schema.SNOOZE.value, "_snooze")


class EufySecurityCamera(Camera, EufySecurityEntity):
"""Base camera entity for integration"""

def __init__(
self, coordinator: EufySecurityDataUpdateCoordinator, metadata: Metadata
) -> None:
def __init__(self, coordinator: EufySecurityDataUpdateCoordinator, metadata: Metadata) -> None:
Camera.__init__(self)
EufySecurityEntity.__init__(self, coordinator, metadata)
self._attr_supported_features = CameraEntityFeature.STREAM
Expand Down Expand Up @@ -137,9 +109,7 @@ async def async_create_stream(self):
return await super().async_create_stream()

async def _start_hass_streaming(self):
await wait_for_value_to_equal(
self.product.__dict__, "stream_status", StreamStatus.STREAMING
)
await wait_for_value_to_equal(self.product.__dict__, "stream_status", StreamStatus.STREAMING)
await self._stop_hass_streaming()
await self.async_create_stream()
if self.stream is not None:
Expand Down Expand Up @@ -167,45 +137,29 @@ async def _get_image_from_hass_stream(self, width, height):

async def _get_image_from_stream_url(self, width, height):
while True:
result = await ffmpeg.async_get_image(
self.hass, await self.stream_source(), width=width, height=height
)
result = await ffmpeg.async_get_image(self.hass, await self.stream_source(), width=width, height=height)
if result is not None:
_LOGGER.debug(f"_get_image_from_stream_url - received {len(result)}")
return result
_LOGGER.debug(f"_get_image_from_stream_url - is_empty {result is None}")
await asyncio.sleep(STREAM_SLEEP_SECONDS)

async def async_camera_image(
self, width: int | None = None, height: int | None = None
) -> bytes | None:
async def async_camera_image(self, width: int | None = None, height: int | None = None) -> bytes | None:
_LOGGER.debug(f"image 1 - {self.is_streaming} - {self.stream}")
if self.is_streaming is True:
if self.stream is not None:
with contextlib.suppress(asyncio.TimeoutError):
self._last_image = await asyncio.wait_for(
self._get_image_from_stream_url(width, height),
STREAM_TIMEOUT_SECONDS,
)
self._last_image = await asyncio.wait_for(self._get_image_from_stream_url(width, height), STREAM_TIMEOUT_SECONDS)
# self._last_image = await asyncio.wait_for(self._get_image_from_hass_stream(width, height), STREAM_TIMEOUT_SECONDS)
_LOGGER.debug(
f"image 2 with hass stream - is_empty {self._last_image is None}"
)
_LOGGER.debug(f"image 2 with hass stream - is_empty {self._last_image is None}")
else:
with contextlib.suppress(asyncio.TimeoutError):
self._last_image = await asyncio.wait_for(
self._get_image_from_stream_url(width, height),
STREAM_TIMEOUT_SECONDS,
)
_LOGGER.debug(
f"image 2 without hass stream - is_empty {self._last_image is None}"
)
self._last_image = await asyncio.wait_for(self._get_image_from_stream_url(width, height), STREAM_TIMEOUT_SECONDS)
_LOGGER.debug(f"image 2 without hass stream - is_empty {self._last_image is None}")

else:
if self.product.picture_base64 is not None:
self._last_image = bytearray(
self.product.picture_base64["data"]["data"]
)
self._last_image = bytearray(self.product.picture_base64["data"]["data"])

_LOGGER.debug(f"async_camera_image 5 - is_empty {self._last_image is None}")
if self._last_image is not None:
Expand Down Expand Up @@ -282,13 +236,5 @@ async def _generate_image(self) -> None:
async def _async_quick_response(self, voice_id: int) -> None:
await self.product.quick_response(voice_id)

async def _snooze(
self,
snooze_time: int,
snooze_chime: bool,
snooze_motion: bool,
snooze_homebase: bool,
) -> None:
await self.product.snooze(
snooze_time, snooze_chime, snooze_motion, snooze_homebase
)
async def _snooze(self, snooze_time: int, snooze_chime: bool, snooze_motion: bool, snooze_homebase: bool) -> None:
await self.product.snooze(snooze_time, snooze_chime, snooze_motion, snooze_homebase)
10 changes: 4 additions & 6 deletions custom_components/eufy_security/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

from .const import COORDINATOR, DOMAIN
from .eufy_security_api.api_client import ApiClient
from .eufy_security_api.exceptions import WebSocketConnectionError
from .eufy_security_api.exceptions import WebSocketConnectionException
from .model import Config, ConfigField

_LOGGER = logging.getLogger(__name__)
Expand All @@ -24,7 +24,7 @@ def __init__(self, config_entry: ConfigEntry) -> None:
"""Initialize option flow handler"""
self.config = Config.parse(config_entry)
self.config_entry = config_entry
_LOGGER.debug(f"{DOMAIN} EufySecurityOptionFlowHandler - {config_entry.data}")
_LOGGER.debug(f"{DOMAIN} EufySecurityOptionFlowHandler - {config_entry.options}")
self.schema = vol.Schema(
{
vol.Optional(ConfigField.sync_interval.name, default=self.config.sync_interval): int,
Expand Down Expand Up @@ -113,7 +113,7 @@ async def _test_credentials(self, host, port): # pylint: disable=unused-argumen
await api_client.ws_connect()
await api_client.disconnect()
return True
except WebSocketConnectionError as ex: # pylint: disable=broad-except
except WebSocketConnectionException as ex: # pylint: disable=broad-except
_LOGGER.error(f"{DOMAIN} Exception in login : %s - traceback: %s", ex, traceback.format_exc())
return False

Expand Down Expand Up @@ -144,8 +144,6 @@ async def async_step_reauth_confirm(self, user_input=None):
vol.Required(ConfigField.captcha_input.name): str,
}
),
description_placeholders={
"captcha_img": '<img id="eufy_security_captcha" src="' + coordinator.config.captcha_img + '"/>'
},
description_placeholders={"captcha_img": '<img id="eufy_security_captcha" src="' + coordinator.config.captcha_img + '"/>'},
)
return await self.async_step_user(user_input)
26 changes: 15 additions & 11 deletions custom_components/eufy_security/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
from homeassistant.helpers import aiohttp_client
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed

from .const import DOMAIN
from .eufy_security_api.api_client import ApiClient
from .eufy_security_api.exceptions import (
CaptchaRequiredException,
DriverNotConnectedError,
DriverNotConnectedException,
MultiFactorCodeRequiredException,
WebSocketConnectionError,
WebSocketConnectionException,
)
from .model import Config

Expand All @@ -26,12 +26,10 @@ class EufySecurityDataUpdateCoordinator(DataUpdateCoordinator):

def __init__(self, hass: HomeAssistant, config_entry: ConfigEntry) -> None:
self.config: Config = Config.parse(config_entry)
super().__init__(
hass, _LOGGER, name=DOMAIN, update_method=self._update_local, update_interval=timedelta(seconds=self.config.sync_interval)
)
super().__init__(hass, _LOGGER, name=DOMAIN, update_method=self._update_local, update_interval=timedelta(seconds=self.config.sync_interval))
self._platforms = []
self.data = {}
self._api = ApiClient(self.config, aiohttp_client.async_get_clientsession(self.hass))
self._api = ApiClient(self.config, aiohttp_client.async_get_clientsession(self.hass), self._on_error)

async def initialize(self):
"""Initialize the integration"""
Expand All @@ -44,9 +42,9 @@ async def initialize(self):
except MultiFactorCodeRequiredException as exc:
self.config.mfa_required = True
raise ConfigEntryAuthFailed() from exc
except DriverNotConnectedError as exc:
except DriverNotConnectedException as exc:
raise ConfigEntryNotReady() from exc
except WebSocketConnectionError as exc:
except WebSocketConnectionException as exc:
raise ConfigEntryNotReady() from exc

@property
Expand Down Expand Up @@ -82,9 +80,15 @@ async def set_log_level(self, log_level: str) -> None:
await self._api.set_log_level(log_level)

async def _update_local(self):
await self._api.poll_refresh()
return self.data
try:
await self._api.poll_refresh()
return self.data
except WebSocketConnectionException as exc:
raise UpdateFailed(f"Error communicating with Add-on: {exc}") from exc

async def disconnect(self):
"""disconnect from api"""
await self._api.disconnect()

def _on_error(self, error):
self.hass.components.persistent_notification.create(f"Error: {error}", title="Eufy Security - Error", notification_id="eufy_security_error")
Loading

0 comments on commit 3edb2f2

Please sign in to comment.