44
55from typing import Any
66
7+ from pyschlage .code import AccessCode
8+ import voluptuous as vol
9+
710from homeassistant .components .lock import LockEntity
8- from homeassistant .core import HomeAssistant , callback
11+ from homeassistant .core import (
12+ HomeAssistant ,
13+ ServiceCall ,
14+ ServiceResponse ,
15+ SupportsResponse ,
16+ callback ,
17+ )
18+ from homeassistant .exceptions import ServiceValidationError
19+ from homeassistant .helpers import config_validation as cv , entity_platform
920from homeassistant .helpers .entity_platform import AddConfigEntryEntitiesCallback
1021
22+ from .const import DOMAIN , SERVICE_ADD_CODE , SERVICE_DELETE_CODE , SERVICE_GET_CODES
1123from .coordinator import LockData , SchlageConfigEntry , SchlageDataUpdateCoordinator
1224from .entity import SchlageEntity
1325
@@ -29,6 +41,101 @@ def _add_new_locks(locks: dict[str, LockData]) -> None:
2941 _add_new_locks (coordinator .data .locks )
3042 coordinator .new_locks_callbacks .append (_add_new_locks )
3143
44+ # Custom services
45+ async def _add_code (entity : SchlageLockEntity , calls : ServiceCall ) -> None :
46+ """Add a lock code."""
47+ name = calls .data .get ("name" )
48+ code = calls .data .get ("code" )
49+
50+ codes = entity ._lock .access_codes # noqa: SLF001
51+ if codes :
52+ for existing_code in codes .values ():
53+ if existing_code .name == name :
54+ raise ServiceValidationError (
55+ translation_domain = DOMAIN ,
56+ translation_key = "schlage_name_exists" ,
57+ )
58+ if existing_code .code == code :
59+ raise ServiceValidationError (
60+ translation_domain = DOMAIN ,
61+ translation_key = "schlage_code_exists" ,
62+ )
63+ if name and code :
64+ access_code = AccessCode (name = name , code = code )
65+ await hass .async_add_executor_job (entity ._lock .add_access_code , access_code ) # noqa: SLF001
66+ await coordinator .async_request_refresh ()
67+
68+ async def _delete_code (entity : SchlageLockEntity , calls : ServiceCall ) -> None :
69+ """Delete a lock code."""
70+ name = calls .data .get ("name" )
71+
72+ # name is required by voluptions, the following is just to satisfy type
73+ # checker, it should never be None
74+ if name is None :
75+ raise ServiceValidationError (
76+ translation_domain = DOMAIN ,
77+ translation_key = "schlage_name_required" ,
78+ ) # pragma: no cover
79+
80+ codes = entity ._lock .access_codes # noqa: SLF001
81+ if not codes :
82+ return
83+
84+ code_id_to_delete = None
85+ for code_id , code_data in codes .items ():
86+ if code_data .name .lower () == name .lower ():
87+ code_id_to_delete = code_id
88+ break
89+
90+ if not code_id_to_delete :
91+ return
92+
93+ if entity ._lock .access_codes : # noqa: SLF001
94+ await hass .async_add_executor_job (
95+ entity ._lock .access_codes [code_id_to_delete ].delete # noqa: SLF001
96+ )
97+ await coordinator .async_request_refresh ()
98+
99+ async def _get_codes (
100+ entity : SchlageLockEntity , calls : ServiceCall
101+ ) -> ServiceResponse :
102+ """Get lock codes."""
103+
104+ if entity ._lock .access_codes : # noqa: SLF001
105+ return {
106+ code : {
107+ "name" : entity ._lock .access_codes [code ].name , # noqa: SLF001
108+ "code" : entity ._lock .access_codes [code ].code , # noqa: SLF001
109+ }
110+ for code in entity ._lock .access_codes # noqa: SLF001
111+ }
112+ return {}
113+
114+ platform = entity_platform .async_get_current_platform ()
115+ platform .async_register_entity_service (
116+ name = SERVICE_ADD_CODE ,
117+ schema = {
118+ vol .Required ("name" ): cv .string ,
119+ vol .Required ("code" ): cv .matches_regex (r"^\d{4,8}$" ),
120+ },
121+ func = _add_code ,
122+ )
123+
124+ platform .async_register_entity_service (
125+ name = SERVICE_DELETE_CODE ,
126+ schema = {
127+ vol .Required ("name" ): cv .string ,
128+ },
129+ func = _delete_code ,
130+ )
131+
132+ platform .async_register_entity_service (
133+ name = SERVICE_GET_CODES ,
134+ schema = None ,
135+ func = _get_codes ,
136+ supports_response = SupportsResponse .ONLY ,
137+ )
138+
32139
33140class SchlageLockEntity (SchlageEntity , LockEntity ):
34141 """Schlage lock entity."""
0 commit comments