Skip to content

Commit

Permalink
debug in select, custom states and off for alarm
Browse files Browse the repository at this point in the history
  • Loading branch information
fuatakgun committed Jan 13, 2022
1 parent 40850b0 commit 5fba934
Show file tree
Hide file tree
Showing 15 changed files with 1,166 additions and 407 deletions.
31 changes: 18 additions & 13 deletions custom_components/eufy_security/__init__.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
import asyncio
from datetime import timedelta
import logging

import asyncio

from homeassistant.config_entries import ConfigEntry
from homeassistant.core import Config
from homeassistant.core import HomeAssistant
from homeassistant.helpers import aiohttp_client
from homeassistant.helpers.aiohttp_client import async_create_clientsession
from homeassistant.helpers.event import async_call_later, async_track_time_interval

from .const import COORDINATOR, CAPTCHA_CONFIG, CONF_PORT, CONF_HOST, DOMAIN, PLATFORMS, DEFAULT_SYNC_INTERVAL, CONF_USE_RTSP_SERVER_ADDON, DEFAULT_USE_RTSP_SERVER_ADDON, CONF_SYNC_INTERVAL, DEFAULT_SYNC_INTERVAL
from .const import CaptchaConfig
from homeassistant.core import Config, HomeAssistant
from homeassistant.helpers.event import async_track_time_interval

from .const import CAPTCHA_CONFIG, COORDINATOR, DOMAIN, PLATFORMS, CaptchaConfig
from .coordinator import EufySecurityDataUpdateCoordinator

_LOGGER: logging.Logger = logging.getLogger(__package__)
Expand Down Expand Up @@ -41,27 +36,36 @@ async def async_driver_connect(call):
hass.services.async_register(DOMAIN, "send_message", async_handle_send_message)
return True


async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry):
if hass.data.get(DOMAIN) is None:
hass.data.setdefault(DOMAIN, {})
captcha_config = hass.data[DOMAIN].get(CAPTCHA_CONFIG, CaptchaConfig())
coordinator = hass.data[DOMAIN].get(COORDINATOR, EufySecurityDataUpdateCoordinator(hass, config_entry, captcha_config))
coordinator = hass.data[DOMAIN].get(
COORDINATOR,
EufySecurityDataUpdateCoordinator(hass, config_entry, captcha_config),
)
hass.data[DOMAIN][COORDINATOR] = coordinator
hass.data[DOMAIN][CAPTCHA_CONFIG] = captcha_config

await coordinator.initialize()
await coordinator.async_refresh()
for platform in PLATFORMS:
coordinator.platforms.append(platform)
hass.async_add_job(hass.config_entries.async_forward_entry_setup(config_entry, platform))
hass.async_add_job(
hass.config_entries.async_forward_entry_setup(config_entry, platform)
)

async def update(event_time_utc):
coordinator.async_set_updated_data(coordinator.data)

coordinator.update_listener = async_track_time_interval(hass, update, timedelta(seconds=1))
coordinator.update_listener = async_track_time_interval(
hass, update, timedelta(seconds=1)
)
config_entry.add_update_listener(async_reload_entry)
return True


async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
coordinator = hass.data[DOMAIN][COORDINATOR]
unloaded = all(
Expand All @@ -79,6 +83,7 @@ async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) ->

return unloaded


async def async_reload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> None:
await async_unload_entry(hass, config_entry)
await async_setup_entry(hass, config_entry)
179 changes: 138 additions & 41 deletions custom_components/eufy_security/alarm_control_panel.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,48 @@
import asyncio
import logging

import voluptuous as vol
from decimal import Decimal

from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.components.alarm_control_panel import AlarmControlPanelEntity
from homeassistant.components.alarm_control_panel.const import (
SUPPORT_ALARM_ARM_AWAY,
SUPPORT_ALARM_ARM_HOME,
SUPPORT_ALARM_TRIGGER,
)
from homeassistant.components.alarm_control_panel.const import (
SUPPORT_ALARM_ARM_CUSTOM_BYPASS, # custom1
)
from homeassistant.components.alarm_control_panel.const import (
SUPPORT_ALARM_ARM_NIGHT, # custom2
)
from homeassistant.components.alarm_control_panel.const import (
SUPPORT_ALARM_ARM_VACATION, # custom3
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
STATE_ALARM_ARMED_AWAY,
STATE_ALARM_ARMED_HOME,
STATE_ALARM_DISARMED,
STATE_ALARM_TRIGGERED,
)
from homeassistant.helpers import entity_platform, service
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_platform
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.config_validation import make_entity_service_schema
from homeassistant.components.alarm_control_panel.const import SUPPORT_ALARM_ARM_AWAY, SUPPORT_ALARM_ARM_HOME, SUPPORT_ALARM_TRIGGER

from .const import COORDINATOR, DOMAIN, STATE_ALARM_CUSTOM1, STATE_ALARM_CUSTOM2, STATE_ALARM_CUSTOM3, STATE_GUARD_GEO, STATE_GUARD_SCHEDULE, Device
from .entity import EufySecurityEntity
from .const import (
COORDINATOR,
DOMAIN,
STATE_ALARM_CUSTOM1,
STATE_ALARM_CUSTOM2,
STATE_ALARM_CUSTOM3,
STATE_GUARD_GEO,
STATE_GUARD_OFF,
STATE_GUARD_SCHEDULE,
Device,
)
from .coordinator import EufySecurityDataUpdateCoordinator
from .entity import EufySecurityEntity

_LOGGER: logging.Logger = logging.getLogger(__package__)

Expand All @@ -32,88 +55,154 @@
5: STATE_ALARM_CUSTOM3,
6: STATE_ALARM_DISARMED,
47: STATE_GUARD_GEO,
63: STATE_ALARM_DISARMED
63: STATE_ALARM_DISARMED,
}

STATES_TO_CODES = {
STATE_ALARM_ARMED_AWAY: 0,
STATE_ALARM_ARMED_HOME: 1,
STATE_GUARD_SCHEDULE: 2,
STATE_ALARM_CUSTOM1: 3,
STATE_ALARM_CUSTOM2: 4,
STATE_ALARM_CUSTOM3: 5,
STATE_GUARD_GEO: 47,
STATE_ALARM_DISARMED: 63,
}
OFF_CODE = 6

ALARM_TRIGGER_SCHEMA = make_entity_service_schema(
{vol.Required('duration'): cv.Number}
)
CUSTOM_CODES = [3, 4, 5]

STATES_TO_CODES = {v: k for k, v in CODES_TO_STATES.items()}

ALARM_TRIGGER_SCHEMA = make_entity_service_schema({vol.Required("duration"): cv.Number})

async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry, async_add_devices):

async def async_setup_entry(
hass: HomeAssistant, config_entry: ConfigEntry, async_add_devices
):
coordinator: EufySecurityDataUpdateCoordinator = hass.data[DOMAIN][COORDINATOR]

entities = []
for device in coordinator.stations.values():
if not device.state.get("guardMode", None) is None:
entities.append(EufySecurityAlarmControlPanel(coordinator, config_entry, device))
entities.append(
EufySecurityAlarmControlPanel(coordinator, config_entry, device)
)

async_add_devices(entities, True)
# register entity level services
platform = entity_platform.async_get_current_platform()
platform.async_register_entity_service("alarm_guard_schedule", {}, "alarm_guard_schedule")
platform.async_register_entity_service(
"alarm_guard_schedule", {}, "alarm_guard_schedule"
)
platform.async_register_entity_service("alarm_arm_custom1", {}, "alarm_arm_custom1")
platform.async_register_entity_service("alarm_arm_custom2", {}, "alarm_arm_custom2")
platform.async_register_entity_service("alarm_arm_custom3", {}, "alarm_arm_custom3")
platform.async_register_entity_service("alarm_guard_geo", {}, "alarm_guard_geo")
platform.async_register_entity_service("alarm_trigger_with_duration", ALARM_TRIGGER_SCHEMA, "alarm_trigger_with_duration")
platform.async_register_entity_service(
"alarm_trigger_with_duration",
ALARM_TRIGGER_SCHEMA,
"alarm_trigger_with_duration",
)
platform.async_register_entity_service("reset_alarm", {}, "reset_alarm")



class EufySecurityAlarmControlPanel(EufySecurityEntity, AlarmControlPanelEntity):
def __init__(self, coordinator: EufySecurityDataUpdateCoordinator, config_entry: ConfigEntry, device: Device):
def __init__(
self,
coordinator: EufySecurityDataUpdateCoordinator,
config_entry: ConfigEntry,
device: Device,
):
EufySecurityEntity.__init__(self, coordinator, config_entry, device)
AlarmControlPanelEntity.__init__(self)
self._attr_code_arm_required = False
self._attr_supported_features = SUPPORT_ALARM_ARM_HOME | SUPPORT_ALARM_ARM_AWAY | SUPPORT_ALARM_TRIGGER

if self.coordinator.config.map_extra_alarm_modes is True:
_LOGGER.debug(f"{DOMAIN} - alarm init - extra modes enabled")
self._attr_supported_features = (
SUPPORT_ALARM_ARM_HOME
| SUPPORT_ALARM_ARM_AWAY
| SUPPORT_ALARM_TRIGGER
| SUPPORT_ALARM_ARM_CUSTOM_BYPASS
| SUPPORT_ALARM_ARM_NIGHT
| SUPPORT_ALARM_ARM_VACATION
)
else:
_LOGGER.debug(f"{DOMAIN} - alarm init - extra modes disabled")
self._attr_supported_features = (
SUPPORT_ALARM_ARM_HOME | SUPPORT_ALARM_ARM_AWAY | SUPPORT_ALARM_TRIGGER
)

async def set_guard_mode(self, target_mode: str):
await self.coordinator.async_set_guard_mode(self.device.serial_number, STATES_TO_CODES[target_mode])
if target_mode == STATE_GUARD_OFF:
code = OFF_CODE
else:
code = STATES_TO_CODES[target_mode]

await self.coordinator.async_set_guard_mode(self.device.serial_number, code)

def alarm_disarm(self, code) -> None:
asyncio.run_coroutine_threadsafe(self.set_guard_mode(STATE_ALARM_DISARMED), self.coordinator.hass.loop).result()
asyncio.run_coroutine_threadsafe(
self.set_guard_mode(STATE_ALARM_DISARMED), self.coordinator.hass.loop
).result()

def alarm_off(self, code) -> None:
asyncio.run_coroutine_threadsafe(
self.set_guard_mode(STATE_GUARD_OFF), self.coordinator.hass.loop
).result()

def alarm_arm_home(self, code) -> None:
asyncio.run_coroutine_threadsafe(self.set_guard_mode(STATE_ALARM_ARMED_HOME), self.coordinator.hass.loop).result()
asyncio.run_coroutine_threadsafe(
self.set_guard_mode(STATE_ALARM_ARMED_HOME), self.coordinator.hass.loop
).result()

def alarm_arm_away(self, code) -> None:
asyncio.run_coroutine_threadsafe(self.set_guard_mode(STATE_ALARM_ARMED_AWAY), self.coordinator.hass.loop).result()
asyncio.run_coroutine_threadsafe(
self.set_guard_mode(STATE_ALARM_ARMED_AWAY), self.coordinator.hass.loop
).result()

def alarm_arm_custom_bypass(self, code) -> None:
self.alarm_arm_custom1()

def alarm_arm_night(self, code) -> None:
self.alarm_arm_custom2()

def alarm_arm_vacation(self, code) -> None:
self.alarm_arm_custom3()

def alarm_guard_schedule(self) -> None:
asyncio.run_coroutine_threadsafe(self.set_guard_mode(STATE_GUARD_SCHEDULE), self.coordinator.hass.loop).result()
asyncio.run_coroutine_threadsafe(
self.set_guard_mode(STATE_GUARD_SCHEDULE), self.coordinator.hass.loop
).result()

def alarm_arm_custom1(self) -> None:
asyncio.run_coroutine_threadsafe(self.set_guard_mode(STATE_ALARM_CUSTOM1), self.coordinator.hass.loop).result()
asyncio.run_coroutine_threadsafe(
self.set_guard_mode(STATE_ALARM_CUSTOM1), self.coordinator.hass.loop
).result()

def alarm_arm_custom2(self) -> None:
asyncio.run_coroutine_threadsafe(self.set_guard_mode(STATE_ALARM_CUSTOM2), self.coordinator.hass.loop).result()
asyncio.run_coroutine_threadsafe(
self.set_guard_mode(STATE_ALARM_CUSTOM2), self.coordinator.hass.loop
).result()

def alarm_arm_custom3(self) -> None:
asyncio.run_coroutine_threadsafe(self.set_guard_mode(STATE_ALARM_CUSTOM3), self.coordinator.hass.loop).result()
asyncio.run_coroutine_threadsafe(
self.set_guard_mode(STATE_ALARM_CUSTOM3), self.coordinator.hass.loop
).result()

def alarm_guard_geo(self) -> None:
asyncio.run_coroutine_threadsafe(self.set_guard_mode(STATE_GUARD_GEO), self.coordinator.hass.loop).result()
asyncio.run_coroutine_threadsafe(
self.set_guard_mode(STATE_GUARD_GEO), self.coordinator.hass.loop
).result()

def alarm_trigger(self, code) -> None:
asyncio.run_coroutine_threadsafe(self.coordinator.async_trigger_alarm(self.device.serial_number), self.coordinator.hass.loop).result()
asyncio.run_coroutine_threadsafe(
self.coordinator.async_trigger_alarm(self.device.serial_number),
self.coordinator.hass.loop,
).result()

def alarm_trigger_with_duration(self, duration: int = 10) -> None:
asyncio.run_coroutine_threadsafe(self.coordinator.async_trigger_alarm(self.device.serial_number, duration), self.coordinator.hass.loop).result()
asyncio.run_coroutine_threadsafe(
self.coordinator.async_trigger_alarm(self.device.serial_number, duration),
self.coordinator.hass.loop,
).result()

def reset_alarm(self) -> None:
asyncio.run_coroutine_threadsafe(self.coordinator.async_reset_alarm(self.device.serial_number), self.coordinator.hass.loop).result()
asyncio.run_coroutine_threadsafe(
self.coordinator.async_reset_alarm(self.device.serial_number),
self.coordinator.hass.loop,
).result()

@property
def id(self):
Expand All @@ -133,4 +222,12 @@ def state(self):
self.device.state["alarmEvent"] = None
return STATE_ALARM_TRIGGERED
current_mode = self.device.state.get("currentMode")
return CODES_TO_STATES[current_mode]
if current_mode in CUSTOM_CODES:
position = CUSTOM_CODES.index(current_mode)
if position == 0:
self.coordinator.config.name_for_custom1
if position == 1:
self.coordinator.config.name_for_custom2
if position == 2:
self.coordinator.config.name_for_custom3
return CODES_TO_STATES[current_mode]
Loading

0 comments on commit 5fba934

Please sign in to comment.