Skip to content

Commit 0309971

Browse files
committed
Refactor: Use new registration method
* Move service registration to __init__.py and convert lock services to instance methods. * Update services.yaml and strings.json to use target instead of entity_id. Signed-off-by: Andrew Grimberg <[email protected]>
1 parent 6e3fc05 commit 0309971

File tree

4 files changed

+116
-154
lines changed

4 files changed

+116
-154
lines changed

homeassistant/components/schlage/__init__.py

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,15 @@
44

55
from pycognito.exceptions import WarrantException
66
import pyschlage
7+
import voluptuous as vol
78

9+
from homeassistant.components.lock import DOMAIN as LOCK_DOMAIN
810
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME, Platform
9-
from homeassistant.core import HomeAssistant
11+
from homeassistant.core import HomeAssistant, SupportsResponse
1012
from homeassistant.exceptions import ConfigEntryAuthFailed
13+
from homeassistant.helpers import config_validation as cv, service
1114

15+
from .const import DOMAIN, SERVICE_ADD_CODE, SERVICE_DELETE_CODE, SERVICE_GET_CODES
1216
from .coordinator import SchlageConfigEntry, SchlageDataUpdateCoordinator
1317

1418
PLATFORMS: list[Platform] = [
@@ -36,6 +40,40 @@ async def async_setup_entry(hass: HomeAssistant, entry: SchlageConfigEntry) -> b
3640

3741
await coordinator.async_config_entry_first_refresh()
3842
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
43+
44+
service.async_register_platform_entity_service(
45+
hass,
46+
DOMAIN,
47+
SERVICE_ADD_CODE,
48+
entity_domain=LOCK_DOMAIN,
49+
schema={
50+
vol.Required("name"): cv.string,
51+
vol.Required("code"): cv.matches_regex(r"^\d{4,8}$"),
52+
},
53+
func=SERVICE_ADD_CODE,
54+
)
55+
56+
service.async_register_platform_entity_service(
57+
hass,
58+
DOMAIN,
59+
SERVICE_DELETE_CODE,
60+
entity_domain=LOCK_DOMAIN,
61+
schema={
62+
vol.Required("name"): cv.string,
63+
},
64+
func=SERVICE_DELETE_CODE,
65+
)
66+
67+
service.async_register_platform_entity_service(
68+
hass,
69+
DOMAIN,
70+
SERVICE_GET_CODES,
71+
entity_domain=LOCK_DOMAIN,
72+
schema=None,
73+
func=SERVICE_GET_CODES,
74+
supports_response=SupportsResponse.ONLY,
75+
)
76+
3977
return True
4078

4179

homeassistant/components/schlage/lock.py

Lines changed: 64 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,13 @@
55
from typing import Any
66

77
from pyschlage.code import AccessCode
8-
import voluptuous as vol
98

109
from homeassistant.components.lock import LockEntity
11-
from homeassistant.core import (
12-
HomeAssistant,
13-
ServiceCall,
14-
ServiceResponse,
15-
SupportsResponse,
16-
callback,
17-
)
10+
from homeassistant.core import HomeAssistant, ServiceResponse, callback
1811
from homeassistant.exceptions import ServiceValidationError
19-
from homeassistant.helpers import config_validation as cv, entity_platform
2012
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
2113

22-
from .const import DOMAIN, SERVICE_ADD_CODE, SERVICE_DELETE_CODE, SERVICE_GET_CODES
14+
from .const import DOMAIN
2315
from .coordinator import LockData, SchlageConfigEntry, SchlageDataUpdateCoordinator
2416
from .entity import SchlageEntity
2517

@@ -41,16 +33,56 @@ def _add_new_locks(locks: dict[str, LockData]) -> None:
4133
_add_new_locks(coordinator.data.locks)
4234
coordinator.new_locks_callbacks.append(_add_new_locks)
4335

44-
# Custom services
45-
def _validate_code_name(codes: dict[str, AccessCode] | None, name: str) -> None:
36+
37+
class SchlageLockEntity(SchlageEntity, LockEntity):
38+
"""Schlage lock entity."""
39+
40+
_attr_name = None
41+
42+
def __init__(
43+
self, coordinator: SchlageDataUpdateCoordinator, device_id: str
44+
) -> None:
45+
"""Initialize a Schlage Lock."""
46+
super().__init__(coordinator=coordinator, device_id=device_id)
47+
self._update_attrs()
48+
49+
@callback
50+
def _handle_coordinator_update(self) -> None:
51+
"""Handle updated data from the coordinator."""
52+
if self.device_id in self.coordinator.data.locks:
53+
self._update_attrs()
54+
super()._handle_coordinator_update()
55+
56+
def _update_attrs(self) -> None:
57+
"""Update our internal state attributes."""
58+
self._attr_is_locked = self._lock.is_locked
59+
self._attr_is_jammed = self._lock.is_jammed
60+
self._attr_changed_by = self._lock.last_changed_by()
61+
62+
async def async_lock(self, **kwargs: Any) -> None:
63+
"""Lock the device."""
64+
await self.hass.async_add_executor_job(self._lock.lock)
65+
await self.coordinator.async_request_refresh()
66+
67+
async def async_unlock(self, **kwargs: Any) -> None:
68+
"""Unlock the device."""
69+
await self.hass.async_add_executor_job(self._lock.unlock)
70+
await self.coordinator.async_request_refresh()
71+
72+
# Door code services
73+
def _validate_code_name(
74+
self, codes: dict[str, AccessCode] | None, name: str
75+
) -> None:
4676
"""Validate that the code name doesn't already exist."""
4777
if codes and any(code.name.lower() == name.lower() for code in codes.values()):
4878
raise ServiceValidationError(
4979
translation_domain=DOMAIN,
5080
translation_key="schlage_name_exists",
5181
)
5282

53-
def _validate_code_value(codes: dict[str, AccessCode] | None, code: str) -> None:
83+
def _validate_code_value(
84+
self, codes: dict[str, AccessCode] | None, code: str
85+
) -> None:
5486
"""Validate that the code value doesn't already exist."""
5587
if codes and any(
5688
existing_code.code == code for existing_code in codes.values()
@@ -60,18 +92,8 @@ def _validate_code_value(codes: dict[str, AccessCode] | None, code: str) -> None
6092
translation_key="schlage_code_exists",
6193
)
6294

63-
async def _add_code(entity: SchlageLockEntity, calls: ServiceCall) -> None:
95+
async def add_code(self, name: str, code: str) -> None:
6496
"""Add a lock code."""
65-
name = calls.data.get("name")
66-
code = calls.data.get("code")
67-
68-
# name is required by voluptuous, the following is just to satisfy type
69-
# checker, it should never be None
70-
if name is None:
71-
raise ServiceValidationError(
72-
translation_domain=DOMAIN,
73-
translation_key="schlage_name_required",
74-
) # pragma: no cover
7597

7698
# code is required by voluptuous, the following is just to satisfy type
7799
# checker, it should never be None
@@ -81,27 +103,19 @@ async def _add_code(entity: SchlageLockEntity, calls: ServiceCall) -> None:
81103
translation_key="schlage_code_required",
82104
) # pragma: no cover
83105

84-
codes = entity._lock.access_codes # noqa: SLF001
85-
_validate_code_name(codes, name)
86-
_validate_code_value(codes, code)
106+
codes = self._lock.access_codes
107+
self._validate_code_name(codes, name)
108+
self._validate_code_value(codes, code)
87109

88110
access_code = AccessCode(name=name, code=code)
89-
await hass.async_add_executor_job(entity._lock.add_access_code, access_code) # noqa: SLF001
90-
await coordinator.async_request_refresh()
111+
await self.coordinator.hass.async_add_executor_job(
112+
self._lock.add_access_code, access_code
113+
)
114+
await self.coordinator.async_request_refresh()
91115

92-
async def _delete_code(entity: SchlageLockEntity, calls: ServiceCall) -> None:
116+
async def delete_code(self, name: str) -> None:
93117
"""Delete a lock code."""
94-
name = calls.data.get("name")
95-
96-
# name is required by voluptuous, the following is just to satisfy type
97-
# checker, it should never be None
98-
if name is None:
99-
raise ServiceValidationError(
100-
translation_domain=DOMAIN,
101-
translation_key="schlage_name_required",
102-
) # pragma: no cover
103-
104-
codes = entity._lock.access_codes # noqa: SLF001
118+
codes = self._lock.access_codes
105119
if not codes:
106120
return
107121

@@ -117,84 +131,21 @@ async def _delete_code(entity: SchlageLockEntity, calls: ServiceCall) -> None:
117131
if not code_id_to_delete:
118132
return
119133

120-
if entity._lock.access_codes: # noqa: SLF001
121-
await hass.async_add_executor_job(
122-
entity._lock.access_codes[code_id_to_delete].delete # noqa: SLF001
134+
if self._lock.access_codes:
135+
await self.coordinator.hass.async_add_executor_job(
136+
self._lock.access_codes[code_id_to_delete].delete
123137
)
124-
await coordinator.async_request_refresh()
138+
await self.coordinator.async_request_refresh()
125139

126-
async def _get_codes(
127-
entity: SchlageLockEntity, calls: ServiceCall
128-
) -> ServiceResponse:
140+
async def get_codes(self) -> ServiceResponse:
129141
"""Get lock codes."""
130142

131-
if entity._lock.access_codes: # noqa: SLF001
143+
if self._lock.access_codes:
132144
return {
133145
code: {
134-
"name": entity._lock.access_codes[code].name, # noqa: SLF001
135-
"code": entity._lock.access_codes[code].code, # noqa: SLF001
146+
"name": self._lock.access_codes[code].name,
147+
"code": self._lock.access_codes[code].code,
136148
}
137-
for code in entity._lock.access_codes # noqa: SLF001
149+
for code in self._lock.access_codes
138150
}
139151
return {}
140-
141-
platform = entity_platform.async_get_current_platform()
142-
platform.async_register_entity_service(
143-
name=SERVICE_ADD_CODE,
144-
schema={
145-
vol.Required("name"): cv.string,
146-
vol.Required("code"): cv.matches_regex(r"^\d{4,8}$"),
147-
},
148-
func=_add_code,
149-
)
150-
151-
platform.async_register_entity_service(
152-
name=SERVICE_DELETE_CODE,
153-
schema={
154-
vol.Required("name"): cv.string,
155-
},
156-
func=_delete_code,
157-
)
158-
159-
platform.async_register_entity_service(
160-
name=SERVICE_GET_CODES,
161-
schema=None,
162-
func=_get_codes,
163-
supports_response=SupportsResponse.ONLY,
164-
)
165-
166-
167-
class SchlageLockEntity(SchlageEntity, LockEntity):
168-
"""Schlage lock entity."""
169-
170-
_attr_name = None
171-
172-
def __init__(
173-
self, coordinator: SchlageDataUpdateCoordinator, device_id: str
174-
) -> None:
175-
"""Initialize a Schlage Lock."""
176-
super().__init__(coordinator=coordinator, device_id=device_id)
177-
self._update_attrs()
178-
179-
@callback
180-
def _handle_coordinator_update(self) -> None:
181-
"""Handle updated data from the coordinator."""
182-
if self.device_id in self.coordinator.data.locks:
183-
self._update_attrs()
184-
super()._handle_coordinator_update()
185-
186-
def _update_attrs(self) -> None:
187-
"""Update our internal state attributes."""
188-
self._attr_is_locked = self._lock.is_locked
189-
self._attr_is_jammed = self._lock.is_jammed
190-
self._attr_changed_by = self._lock.last_changed_by()
191-
192-
async def async_lock(self, **kwargs: Any) -> None:
193-
"""Lock the device."""
194-
await self.hass.async_add_executor_job(self._lock.lock)
195-
await self.coordinator.async_request_refresh()
196-
197-
async def async_unlock(self, **kwargs: Any) -> None:
198-
"""Unlock the device."""
199-
await self.hass.async_add_executor_job(self._lock.unlock)
200-
await self.coordinator.async_request_refresh()

homeassistant/components/schlage/services.yaml

Lines changed: 12 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,15 @@
11
get_codes:
2-
fields:
3-
entity_id:
4-
required: true
5-
selector:
6-
entity:
7-
filter:
8-
domain: lock
9-
integration: schlage
10-
multiple: true
2+
target:
3+
entity:
4+
domain: lock
5+
integration: schlage
116

127
add_code:
8+
target:
9+
entity:
10+
domain: lock
11+
integration: schlage
1312
fields:
14-
entity_id:
15-
required: true
16-
selector:
17-
entity:
18-
filter:
19-
domain: lock
20-
integration: schlage
21-
multiple: true
2213
name:
2314
required: true
2415
example: "Example Person"
@@ -33,15 +24,11 @@ add_code:
3324
multiline: false
3425

3526
delete_code:
27+
target:
28+
entity:
29+
domain: lock
30+
integration: schlage
3631
fields:
37-
entity_id:
38-
required: true
39-
selector:
40-
entity:
41-
filter:
42-
domain: lock
43-
integration: schlage
44-
multiple: true
4532
name:
4633
required: true
4734
example: "Example Person"

homeassistant/components/schlage/strings.json

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,6 @@
7777
"name": "Add PIN code",
7878
"description": "Add a PIN code to a lock.",
7979
"fields": {
80-
"entity_id": {
81-
"name": "Lock",
82-
"description": "The lock to add the PIN code to."
83-
},
8480
"name": {
8581
"name": "PIN Name",
8682
"description": "Name for PIN code. Must be unique to lock."
@@ -95,10 +91,6 @@
9591
"name": "Delete PIN code",
9692
"description": "Delete a PIN code from a lock.",
9793
"fields": {
98-
"entity_id": {
99-
"name": "Lock",
100-
"description": "The lock to delete the PIN code from."
101-
},
10294
"name": {
10395
"name": "PIN Name",
10496
"description": "Name of PIN code to delete."
@@ -107,13 +99,7 @@
10799
},
108100
"get_codes": {
109101
"name": "Get PIN codes",
110-
"description": "Retrieve all PIN codes from the lock",
111-
"fields": {
112-
"entity_id": {
113-
"name": "Lock",
114-
"description": "The lock to retrieve PIN codes from."
115-
}
116-
}
102+
"description": "Retrieve all PIN codes from the lock"
117103
}
118104
}
119105
}

0 commit comments

Comments
 (0)