-
-
Notifications
You must be signed in to change notification settings - Fork 35.8k
Add services for managing Schlage door codes #151014
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| { | ||
| "services": { | ||
| "add_code": { | ||
| "service": "mdi:key-plus" | ||
| }, | ||
| "delete_code": { | ||
| "service": "mdi:key-minus" | ||
| }, | ||
| "get_codes": { | ||
| "service": "mdi:table-key" | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -4,10 +4,14 @@ | |||||||||||||||||||
|
|
||||||||||||||||||||
| from typing import Any | ||||||||||||||||||||
|
|
||||||||||||||||||||
| from pyschlage.code import AccessCode | ||||||||||||||||||||
|
|
||||||||||||||||||||
| from homeassistant.components.lock import LockEntity | ||||||||||||||||||||
| from homeassistant.core import HomeAssistant, callback | ||||||||||||||||||||
| from homeassistant.core import HomeAssistant, ServiceResponse, callback | ||||||||||||||||||||
| from homeassistant.exceptions import ServiceValidationError | ||||||||||||||||||||
| from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback | ||||||||||||||||||||
|
|
||||||||||||||||||||
| from .const import DOMAIN | ||||||||||||||||||||
| from .coordinator import LockData, SchlageConfigEntry, SchlageDataUpdateCoordinator | ||||||||||||||||||||
| from .entity import SchlageEntity | ||||||||||||||||||||
|
|
||||||||||||||||||||
|
|
@@ -64,3 +68,84 @@ async def async_unlock(self, **kwargs: Any) -> None: | |||||||||||||||||||
| """Unlock the device.""" | ||||||||||||||||||||
| await self.hass.async_add_executor_job(self._lock.unlock) | ||||||||||||||||||||
| await self.coordinator.async_request_refresh() | ||||||||||||||||||||
|
|
||||||||||||||||||||
| # Door code services | ||||||||||||||||||||
| def _validate_code_name( | ||||||||||||||||||||
| self, codes: dict[str, AccessCode] | None, name: str | ||||||||||||||||||||
| ) -> None: | ||||||||||||||||||||
| """Validate that the code name doesn't already exist.""" | ||||||||||||||||||||
| if codes and any(code.name.lower() == name.lower() for code in codes.values()): | ||||||||||||||||||||
| raise ServiceValidationError( | ||||||||||||||||||||
| translation_domain=DOMAIN, | ||||||||||||||||||||
| translation_key="schlage_name_exists", | ||||||||||||||||||||
| ) | ||||||||||||||||||||
|
|
||||||||||||||||||||
| def _validate_code_value( | ||||||||||||||||||||
| self, codes: dict[str, AccessCode] | None, code: str | ||||||||||||||||||||
| ) -> None: | ||||||||||||||||||||
| """Validate that the code value doesn't already exist.""" | ||||||||||||||||||||
| if codes and any( | ||||||||||||||||||||
| existing_code.code == code for existing_code in codes.values() | ||||||||||||||||||||
| ): | ||||||||||||||||||||
| raise ServiceValidationError( | ||||||||||||||||||||
| translation_domain=DOMAIN, | ||||||||||||||||||||
| translation_key="schlage_code_exists", | ||||||||||||||||||||
| ) | ||||||||||||||||||||
|
|
||||||||||||||||||||
| async def add_code(self, name: str, code: str) -> None: | ||||||||||||||||||||
| """Add a lock code.""" | ||||||||||||||||||||
|
|
||||||||||||||||||||
| # code is required by voluptuous, the following is just to satisfy type | ||||||||||||||||||||
| # checker, it should never be None | ||||||||||||||||||||
| if code is None: | ||||||||||||||||||||
| raise ServiceValidationError( | ||||||||||||||||||||
| translation_domain=DOMAIN, | ||||||||||||||||||||
| translation_key="schlage_code_required", | ||||||||||||||||||||
| ) # pragma: no cover | ||||||||||||||||||||
|
Comment on lines
+98
to
+104
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I just noticed that |
||||||||||||||||||||
|
|
||||||||||||||||||||
| codes = self._lock.access_codes | ||||||||||||||||||||
| self._validate_code_name(codes, name) | ||||||||||||||||||||
| self._validate_code_value(codes, code) | ||||||||||||||||||||
|
|
||||||||||||||||||||
| access_code = AccessCode(name=name, code=code) | ||||||||||||||||||||
| await self.coordinator.hass.async_add_executor_job( | ||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||
| self._lock.add_access_code, access_code | ||||||||||||||||||||
| ) | ||||||||||||||||||||
| await self.coordinator.async_request_refresh() | ||||||||||||||||||||
|
|
||||||||||||||||||||
| async def delete_code(self, name: str) -> None: | ||||||||||||||||||||
| """Delete a lock code.""" | ||||||||||||||||||||
| codes = self._lock.access_codes | ||||||||||||||||||||
| if not codes: | ||||||||||||||||||||
| return | ||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should this raise ServiceValidationError? |
||||||||||||||||||||
|
|
||||||||||||||||||||
| code_id_to_delete = next( | ||||||||||||||||||||
| ( | ||||||||||||||||||||
| code_id | ||||||||||||||||||||
| for code_id, code_data in codes.items() | ||||||||||||||||||||
| if code_data.name.lower() == name.lower() | ||||||||||||||||||||
| ), | ||||||||||||||||||||
| None, | ||||||||||||||||||||
| ) | ||||||||||||||||||||
|
|
||||||||||||||||||||
| if not code_id_to_delete: | ||||||||||||||||||||
| return | ||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should this raise ServiceValidationError? |
||||||||||||||||||||
|
|
||||||||||||||||||||
| if self._lock.access_codes: | ||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we already check if |
||||||||||||||||||||
| await self.coordinator.hass.async_add_executor_job( | ||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||
| self._lock.access_codes[code_id_to_delete].delete | ||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||
| ) | ||||||||||||||||||||
| await self.coordinator.async_request_refresh() | ||||||||||||||||||||
|
|
||||||||||||||||||||
| async def get_codes(self) -> ServiceResponse: | ||||||||||||||||||||
| """Get lock codes.""" | ||||||||||||||||||||
|
|
||||||||||||||||||||
| if self._lock.access_codes: | ||||||||||||||||||||
| return { | ||||||||||||||||||||
| code: { | ||||||||||||||||||||
| "name": self._lock.access_codes[code].name, | ||||||||||||||||||||
| "code": self._lock.access_codes[code].code, | ||||||||||||||||||||
| } | ||||||||||||||||||||
| for code in self._lock.access_codes | ||||||||||||||||||||
| } | ||||||||||||||||||||
| return {} | ||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| get_codes: | ||
| target: | ||
| entity: | ||
| domain: lock | ||
| integration: schlage | ||
|
|
||
| add_code: | ||
| target: | ||
| entity: | ||
| domain: lock | ||
| integration: schlage | ||
| fields: | ||
| name: | ||
| required: true | ||
| example: "Example Person" | ||
| selector: | ||
| text: | ||
| multiline: false | ||
| code: | ||
| required: true | ||
| example: "1111" | ||
| selector: | ||
| text: | ||
| multiline: false | ||
|
|
||
| delete_code: | ||
| target: | ||
| entity: | ||
| domain: lock | ||
| integration: schlage | ||
| fields: | ||
| name: | ||
| required: true | ||
| example: "Example Person" | ||
| selector: | ||
| text: | ||
| multiline: false |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -58,6 +58,48 @@ | |
| "exceptions": { | ||
| "schlage_refresh_failed": { | ||
| "message": "Failed to refresh Schlage data" | ||
| }, | ||
| "schlage_code_exists": { | ||
| "message": "A PIN code with this value already exists on the lock" | ||
| }, | ||
| "schlage_code_required": { | ||
| "message": "PIN code is required" | ||
| }, | ||
| "schlage_name_exists": { | ||
| "message": "A PIN code with this name already exists on the lock" | ||
| }, | ||
| "schlage_name_required": { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is unused |
||
| "message": "PIN name is required" | ||
| } | ||
| }, | ||
| "services": { | ||
| "add_code": { | ||
| "name": "Add PIN code", | ||
| "description": "Add a PIN code to a lock.", | ||
| "fields": { | ||
| "name": { | ||
| "name": "PIN Name", | ||
| "description": "Name for PIN code. Must be unique to lock." | ||
| }, | ||
| "code": { | ||
| "name": "PIN code", | ||
| "description": "The PIN code to add. Must be unique to lock and be between 4 and 8 digits long." | ||
| } | ||
| } | ||
| }, | ||
| "delete_code": { | ||
| "name": "Delete PIN code", | ||
| "description": "Delete a PIN code from a lock.", | ||
| "fields": { | ||
| "name": { | ||
| "name": "PIN Name", | ||
| "description": "Name of PIN code to delete." | ||
| } | ||
| } | ||
| }, | ||
| "get_codes": { | ||
| "name": "Get PIN codes", | ||
| "description": "Retrieve all PIN codes from the lock" | ||
| } | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.