diff --git a/package-lock.json b/package-lock.json index a8a1ec15..8377d2ce 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,7 @@ "devDependencies": { "@seamapi/fake-seam-connect": "1.83.1", "@seamapi/nextlove-sdk-generator": "^1.18.1", - "@seamapi/types": "1.406.9", + "@seamapi/types": "1.410.0", "del": "^7.1.0", "prettier": "^3.2.5" } @@ -475,9 +475,9 @@ } }, "node_modules/@seamapi/types": { - "version": "1.406.9", - "resolved": "https://registry.npmjs.org/@seamapi/types/-/types-1.406.9.tgz", - "integrity": "sha512-VLdfvKYcB/kc4WkE7HJMyTA7Qc8tybZFP7B6Rub8mv+dEvuyTRbfkIbc8sT0NVN1q3BajW4ZAfIeq56X4hsAPA==", + "version": "1.410.0", + "resolved": "https://registry.npmjs.org/@seamapi/types/-/types-1.410.0.tgz", + "integrity": "sha512-u06BwiA2wkbyHuONEua+3yKf+L4/9pSLbqfyjkxvf+ZFiKYlEJl5EcV0L7jQETwM4r/0puAu39FSHR8yd5QUfg==", "dev": true, "license": "MIT", "engines": { diff --git a/package.json b/package.json index 3ea53574..411294a5 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "devDependencies": { "@seamapi/fake-seam-connect": "1.83.1", "@seamapi/nextlove-sdk-generator": "^1.18.1", - "@seamapi/types": "1.406.9", + "@seamapi/types": "1.410.0", "del": "^7.1.0", "prettier": "^3.2.5" } diff --git a/seam/routes/__init__.py b/seam/routes/__init__.py index dfa21e09..da6e739c 100644 --- a/seam/routes/__init__.py +++ b/seam/routes/__init__.py @@ -2,6 +2,8 @@ from ..client import SeamHttpClient from .models import AbstractRoutes from .access_codes import AccessCodes +from .access_grants import AccessGrants +from .access_methods import AccessMethods from .acs import Acs from .action_attempts import ActionAttempts from .client_sessions import ClientSessions @@ -12,6 +14,7 @@ from .locks import Locks from .noise_sensors import NoiseSensors from .phones import Phones +from .spaces import Spaces from .thermostats import Thermostats from .user_identities import UserIdentities from .webhooks import Webhooks @@ -21,6 +24,8 @@ class Routes(AbstractRoutes): def __init__(self, client: SeamHttpClient, defaults: Dict[str, Any]): self.access_codes = AccessCodes(client=client, defaults=defaults) + self.access_grants = AccessGrants(client=client, defaults=defaults) + self.access_methods = AccessMethods(client=client, defaults=defaults) self.acs = Acs(client=client, defaults=defaults) self.action_attempts = ActionAttempts(client=client, defaults=defaults) self.client_sessions = ClientSessions(client=client, defaults=defaults) @@ -31,6 +36,7 @@ def __init__(self, client: SeamHttpClient, defaults: Dict[str, Any]): self.locks = Locks(client=client, defaults=defaults) self.noise_sensors = NoiseSensors(client=client, defaults=defaults) self.phones = Phones(client=client, defaults=defaults) + self.spaces = Spaces(client=client, defaults=defaults) self.thermostats = Thermostats(client=client, defaults=defaults) self.user_identities = UserIdentities(client=client, defaults=defaults) self.webhooks = Webhooks(client=client, defaults=defaults) diff --git a/seam/routes/access_grants.py b/seam/routes/access_grants.py new file mode 100644 index 00000000..58c874ba --- /dev/null +++ b/seam/routes/access_grants.py @@ -0,0 +1,96 @@ +from typing import Optional, Any, List, Dict, Union +from ..client import SeamHttpClient +from .models import AbstractAccessGrants + + +class AccessGrants(AbstractAccessGrants): + def __init__(self, client: SeamHttpClient, defaults: Dict[str, Any]): + self.client = client + self.defaults = defaults + + def create( + self, + *, + requested_access_methods: List[Dict[str, Any]], + user_identity_id: Optional[str] = None, + user_identity: Optional[Dict[str, Any]] = None, + acs_entrance_ids: Optional[List[str]] = None, + device_ids: Optional[List[str]] = None, + ends_at: Optional[str] = None, + location: Optional[Dict[str, Any]] = None, + location_ids: Optional[List[str]] = None, + space_ids: Optional[List[str]] = None, + starts_at: Optional[str] = None + ) -> None: + json_payload = {} + + if requested_access_methods is not None: + json_payload["requested_access_methods"] = requested_access_methods + if user_identity_id is not None: + json_payload["user_identity_id"] = user_identity_id + if user_identity is not None: + json_payload["user_identity"] = user_identity + if acs_entrance_ids is not None: + json_payload["acs_entrance_ids"] = acs_entrance_ids + if device_ids is not None: + json_payload["device_ids"] = device_ids + if ends_at is not None: + json_payload["ends_at"] = ends_at + if location is not None: + json_payload["location"] = location + if location_ids is not None: + json_payload["location_ids"] = location_ids + if space_ids is not None: + json_payload["space_ids"] = space_ids + if starts_at is not None: + json_payload["starts_at"] = starts_at + + self.client.post("/access_grants/create", json=json_payload) + + return None + + def delete(self, *, access_grant_id: str) -> None: + json_payload = {} + + if access_grant_id is not None: + json_payload["access_grant_id"] = access_grant_id + + self.client.post("/access_grants/delete", json=json_payload) + + return None + + def get(self, *, access_grant_id: str) -> None: + json_payload = {} + + if access_grant_id is not None: + json_payload["access_grant_id"] = access_grant_id + + self.client.post("/access_grants/get", json=json_payload) + + return None + + def list( + self, + *, + acs_entrance_id: Optional[str] = None, + acs_system_id: Optional[str] = None, + location_id: Optional[str] = None, + space_id: Optional[str] = None, + user_identity_id: Optional[str] = None + ) -> None: + json_payload = {} + + if acs_entrance_id is not None: + json_payload["acs_entrance_id"] = acs_entrance_id + if acs_system_id is not None: + json_payload["acs_system_id"] = acs_system_id + if location_id is not None: + json_payload["location_id"] = location_id + if space_id is not None: + json_payload["space_id"] = space_id + if user_identity_id is not None: + json_payload["user_identity_id"] = user_identity_id + + self.client.post("/access_grants/list", json=json_payload) + + return None diff --git a/seam/routes/access_methods.py b/seam/routes/access_methods.py new file mode 100644 index 00000000..d7336ed1 --- /dev/null +++ b/seam/routes/access_methods.py @@ -0,0 +1,39 @@ +from typing import Optional, Any, List, Dict, Union +from ..client import SeamHttpClient +from .models import AbstractAccessMethods + + +class AccessMethods(AbstractAccessMethods): + def __init__(self, client: SeamHttpClient, defaults: Dict[str, Any]): + self.client = client + self.defaults = defaults + + def delete(self, *, access_method_id: str) -> None: + json_payload = {} + + if access_method_id is not None: + json_payload["access_method_id"] = access_method_id + + self.client.post("/access_methods/delete", json=json_payload) + + return None + + def get(self, *, access_method_id: str) -> None: + json_payload = {} + + if access_method_id is not None: + json_payload["access_method_id"] = access_method_id + + self.client.post("/access_methods/get", json=json_payload) + + return None + + def list(self, *, access_grant_id: str) -> None: + json_payload = {} + + if access_grant_id is not None: + json_payload["access_grant_id"] = access_grant_id + + self.client.post("/access_methods/list", json=json_payload) + + return None diff --git a/seam/routes/acs_entrances.py b/seam/routes/acs_entrances.py index 1d8fff50..bfe899b0 100644 --- a/seam/routes/acs_entrances.py +++ b/seam/routes/acs_entrances.py @@ -43,7 +43,8 @@ def list( *, acs_credential_id: Optional[str] = None, acs_system_id: Optional[str] = None, - location_id: Optional[str] = None + location_id: Optional[str] = None, + space_id: Optional[str] = None ) -> List[AcsEntrance]: json_payload = {} @@ -53,6 +54,8 @@ def list( json_payload["acs_system_id"] = acs_system_id if location_id is not None: json_payload["location_id"] = location_id + if space_id is not None: + json_payload["space_id"] = space_id res = self.client.post("/acs/entrances/list", json=json_payload) diff --git a/seam/routes/connect_webviews.py b/seam/routes/connect_webviews.py index 2de28838..28bf1170 100644 --- a/seam/routes/connect_webviews.py +++ b/seam/routes/connect_webviews.py @@ -11,6 +11,7 @@ def __init__(self, client: SeamHttpClient, defaults: Dict[str, Any]): def create( self, *, + accepted_capabilities: Optional[List[str]] = None, accepted_providers: Optional[List[str]] = None, automatically_manage_new_devices: Optional[bool] = None, custom_metadata: Optional[Dict[str, Any]] = None, @@ -23,6 +24,8 @@ def create( ) -> ConnectWebview: json_payload = {} + if accepted_capabilities is not None: + json_payload["accepted_capabilities"] = accepted_capabilities if accepted_providers is not None: json_payload["accepted_providers"] = accepted_providers if automatically_manage_new_devices is not None: diff --git a/seam/routes/devices.py b/seam/routes/devices.py index 19c1d2ad..c9a7cebc 100644 --- a/seam/routes/devices.py +++ b/seam/routes/devices.py @@ -51,6 +51,7 @@ def list( limit: Optional[float] = None, manufacturer: Optional[str] = None, page_cursor: Optional[str] = None, + space_id: Optional[str] = None, unstable_location_id: Optional[str] = None, user_identifier_key: Optional[str] = None ) -> List[Device]: @@ -84,6 +85,8 @@ def list( json_payload["manufacturer"] = manufacturer if page_cursor is not None: json_payload["page_cursor"] = page_cursor + if space_id is not None: + json_payload["space_id"] = space_id if unstable_location_id is not None: json_payload["unstable_location_id"] = unstable_location_id if user_identifier_key is not None: diff --git a/seam/routes/devices_unmanaged.py b/seam/routes/devices_unmanaged.py index 6c6730a4..58996d0f 100644 --- a/seam/routes/devices_unmanaged.py +++ b/seam/routes/devices_unmanaged.py @@ -39,6 +39,7 @@ def list( limit: Optional[float] = None, manufacturer: Optional[str] = None, page_cursor: Optional[str] = None, + space_id: Optional[str] = None, unstable_location_id: Optional[str] = None, user_identifier_key: Optional[str] = None ) -> List[UnmanagedDevice]: @@ -72,6 +73,8 @@ def list( json_payload["manufacturer"] = manufacturer if page_cursor is not None: json_payload["page_cursor"] = page_cursor + if space_id is not None: + json_payload["space_id"] = space_id if unstable_location_id is not None: json_payload["unstable_location_id"] = unstable_location_id if user_identifier_key is not None: diff --git a/seam/routes/locks.py b/seam/routes/locks.py index cac924f3..d4a6d585 100644 --- a/seam/routes/locks.py +++ b/seam/routes/locks.py @@ -41,6 +41,7 @@ def list( limit: Optional[float] = None, manufacturer: Optional[str] = None, page_cursor: Optional[str] = None, + space_id: Optional[str] = None, unstable_location_id: Optional[str] = None, user_identifier_key: Optional[str] = None ) -> List[Device]: @@ -74,6 +75,8 @@ def list( json_payload["manufacturer"] = manufacturer if page_cursor is not None: json_payload["page_cursor"] = page_cursor + if space_id is not None: + json_payload["space_id"] = space_id if unstable_location_id is not None: json_payload["unstable_location_id"] = unstable_location_id if user_identifier_key is not None: diff --git a/seam/routes/models.py b/seam/routes/models.py index 2dffe664..1f77adbb 100644 --- a/seam/routes/models.py +++ b/seam/routes/models.py @@ -432,6 +432,7 @@ def from_dict(d: Dict[str, Any]): @dataclass class ConnectWebview: + accepted_capabilities: List[str] accepted_devices: List[str] accepted_providers: List[str] any_device_allowed: bool @@ -455,6 +456,7 @@ class ConnectWebview: @staticmethod def from_dict(d: Dict[str, Any]): return ConnectWebview( + accepted_capabilities=d.get("accepted_capabilities", None), accepted_devices=d.get("accepted_devices", None), accepted_providers=d.get("accepted_providers", None), any_device_allowed=d.get("any_device_allowed", None), @@ -794,23 +796,6 @@ def from_dict(d: Dict[str, Any]): ) -@dataclass -class Network: - created_at: str - display_name: str - network_id: str - workspace_id: str - - @staticmethod - def from_dict(d: Dict[str, Any]): - return Network( - created_at=d.get("created_at", None), - display_name=d.get("display_name", None), - network_id=d.get("network_id", None), - workspace_id=d.get("workspace_id", None), - ) - - @dataclass class NoiseThreshold: device_id: str @@ -906,6 +891,25 @@ def from_dict(d: Dict[str, Any]): ) +@dataclass +class Space: + created_at: str + display_name: str + name: str + space_id: str + workspace_id: str + + @staticmethod + def from_dict(d: Dict[str, Any]): + return Space( + created_at=d.get("created_at", None), + display_name=d.get("display_name", None), + name=d.get("name", None), + space_id=d.get("space_id", None), + workspace_id=d.get("workspace_id", None), + ) + + @dataclass class ThermostatSchedule: climate_preset_key: str @@ -1313,6 +1317,61 @@ def update( raise NotImplementedError() +class AbstractAccessGrants(abc.ABC): + + @abc.abstractmethod + def create( + self, + *, + requested_access_methods: List[Dict[str, Any]], + user_identity_id: Optional[str] = None, + user_identity: Optional[Dict[str, Any]] = None, + acs_entrance_ids: Optional[List[str]] = None, + device_ids: Optional[List[str]] = None, + ends_at: Optional[str] = None, + location: Optional[Dict[str, Any]] = None, + location_ids: Optional[List[str]] = None, + space_ids: Optional[List[str]] = None, + starts_at: Optional[str] = None + ) -> None: + raise NotImplementedError() + + @abc.abstractmethod + def delete(self, *, access_grant_id: str) -> None: + raise NotImplementedError() + + @abc.abstractmethod + def get(self, *, access_grant_id: str) -> None: + raise NotImplementedError() + + @abc.abstractmethod + def list( + self, + *, + acs_entrance_id: Optional[str] = None, + acs_system_id: Optional[str] = None, + location_id: Optional[str] = None, + space_id: Optional[str] = None, + user_identity_id: Optional[str] = None + ) -> None: + raise NotImplementedError() + + +class AbstractAccessMethods(abc.ABC): + + @abc.abstractmethod + def delete(self, *, access_method_id: str) -> None: + raise NotImplementedError() + + @abc.abstractmethod + def get(self, *, access_method_id: str) -> None: + raise NotImplementedError() + + @abc.abstractmethod + def list(self, *, access_grant_id: str) -> None: + raise NotImplementedError() + + class AbstractAcsAccessGroups(abc.ABC): @abc.abstractmethod @@ -1546,7 +1605,8 @@ def list( *, acs_credential_id: Optional[str] = None, acs_system_id: Optional[str] = None, - location_id: Optional[str] = None + location_id: Optional[str] = None, + space_id: Optional[str] = None ) -> List[AcsEntrance]: raise NotImplementedError() @@ -1791,6 +1851,7 @@ class AbstractConnectWebviews(abc.ABC): def create( self, *, + accepted_capabilities: Optional[List[str]] = None, accepted_providers: Optional[List[str]] = None, automatically_manage_new_devices: Optional[bool] = None, custom_metadata: Optional[Dict[str, Any]] = None, @@ -1903,6 +1964,7 @@ def list( limit: Optional[float] = None, manufacturer: Optional[str] = None, page_cursor: Optional[str] = None, + space_id: Optional[str] = None, unstable_location_id: Optional[str] = None, user_identifier_key: Optional[str] = None ) -> List[UnmanagedDevice]: @@ -1975,6 +2037,7 @@ def list( limit: Optional[float] = None, manufacturer: Optional[str] = None, page_cursor: Optional[str] = None, + space_id: Optional[str] = None, unstable_location_id: Optional[str] = None, user_identifier_key: Optional[str] = None ) -> List[Device]: @@ -2070,6 +2133,55 @@ def create_sandbox_phone( raise NotImplementedError() +class AbstractSpaces(abc.ABC): + + @abc.abstractmethod + def add_acs_entrances(self, *, acs_entrance_ids: List[str], space_id: str) -> None: + raise NotImplementedError() + + @abc.abstractmethod + def add_devices(self, *, device_ids: List[str], space_id: str) -> None: + raise NotImplementedError() + + @abc.abstractmethod + def create( + self, + *, + name: str, + acs_entrance_ids: Optional[List[str]] = None, + device_ids: Optional[List[str]] = None + ) -> Space: + raise NotImplementedError() + + @abc.abstractmethod + def delete(self, *, space_id: str) -> None: + raise NotImplementedError() + + @abc.abstractmethod + def get(self, *, space_id: str) -> Space: + raise NotImplementedError() + + @abc.abstractmethod + def list( + self, + ) -> List[Space]: + raise NotImplementedError() + + @abc.abstractmethod + def remove_acs_entrances( + self, *, acs_entrance_ids: List[str], space_id: str + ) -> None: + raise NotImplementedError() + + @abc.abstractmethod + def remove_devices(self, *, device_ids: List[str], space_id: str) -> None: + raise NotImplementedError() + + @abc.abstractmethod + def update(self, *, space_id: str, name: Optional[str] = None) -> Space: + raise NotImplementedError() + + class AbstractThermostatsDailyPrograms(abc.ABC): @abc.abstractmethod @@ -2549,6 +2661,7 @@ def list( limit: Optional[float] = None, manufacturer: Optional[str] = None, page_cursor: Optional[str] = None, + space_id: Optional[str] = None, unstable_location_id: Optional[str] = None, user_identifier_key: Optional[str] = None ) -> List[Device]: @@ -2603,6 +2716,7 @@ def list( limit: Optional[float] = None, manufacturer: Optional[str] = None, page_cursor: Optional[str] = None, + space_id: Optional[str] = None, unstable_location_id: Optional[str] = None, user_identifier_key: Optional[str] = None ) -> List[Device]: @@ -2713,6 +2827,7 @@ def list( limit: Optional[float] = None, manufacturer: Optional[str] = None, page_cursor: Optional[str] = None, + space_id: Optional[str] = None, unstable_location_id: Optional[str] = None, user_identifier_key: Optional[str] = None ) -> List[Device]: @@ -2842,6 +2957,8 @@ def users(self) -> AbstractAcsUsers: @dataclass class AbstractRoutes(abc.ABC): access_codes: AbstractAccessCodes + access_grants: AbstractAccessGrants + access_methods: AbstractAccessMethods acs: AbstractAcs action_attempts: AbstractActionAttempts client_sessions: AbstractClientSessions @@ -2852,6 +2969,7 @@ class AbstractRoutes(abc.ABC): locks: AbstractLocks noise_sensors: AbstractNoiseSensors phones: AbstractPhones + spaces: AbstractSpaces thermostats: AbstractThermostats user_identities: AbstractUserIdentities webhooks: AbstractWebhooks diff --git a/seam/routes/noise_sensors.py b/seam/routes/noise_sensors.py index 2087c911..84844355 100644 --- a/seam/routes/noise_sensors.py +++ b/seam/routes/noise_sensors.py @@ -39,6 +39,7 @@ def list( limit: Optional[float] = None, manufacturer: Optional[str] = None, page_cursor: Optional[str] = None, + space_id: Optional[str] = None, unstable_location_id: Optional[str] = None, user_identifier_key: Optional[str] = None ) -> List[Device]: @@ -72,6 +73,8 @@ def list( json_payload["manufacturer"] = manufacturer if page_cursor is not None: json_payload["page_cursor"] = page_cursor + if space_id is not None: + json_payload["space_id"] = space_id if unstable_location_id is not None: json_payload["unstable_location_id"] = unstable_location_id if user_identifier_key is not None: diff --git a/seam/routes/spaces.py b/seam/routes/spaces.py new file mode 100644 index 00000000..dbb7a629 --- /dev/null +++ b/seam/routes/spaces.py @@ -0,0 +1,120 @@ +from typing import Optional, Any, List, Dict, Union +from ..client import SeamHttpClient +from .models import AbstractSpaces, Space + + +class Spaces(AbstractSpaces): + def __init__(self, client: SeamHttpClient, defaults: Dict[str, Any]): + self.client = client + self.defaults = defaults + + def add_acs_entrances(self, *, acs_entrance_ids: List[str], space_id: str) -> None: + json_payload = {} + + if acs_entrance_ids is not None: + json_payload["acs_entrance_ids"] = acs_entrance_ids + if space_id is not None: + json_payload["space_id"] = space_id + + self.client.post("/spaces/add_acs_entrances", json=json_payload) + + return None + + def add_devices(self, *, device_ids: List[str], space_id: str) -> None: + json_payload = {} + + if device_ids is not None: + json_payload["device_ids"] = device_ids + if space_id is not None: + json_payload["space_id"] = space_id + + self.client.post("/spaces/add_devices", json=json_payload) + + return None + + def create( + self, + *, + name: str, + acs_entrance_ids: Optional[List[str]] = None, + device_ids: Optional[List[str]] = None + ) -> Space: + json_payload = {} + + if name is not None: + json_payload["name"] = name + if acs_entrance_ids is not None: + json_payload["acs_entrance_ids"] = acs_entrance_ids + if device_ids is not None: + json_payload["device_ids"] = device_ids + + res = self.client.post("/spaces/create", json=json_payload) + + return Space.from_dict(res["space"]) + + def delete(self, *, space_id: str) -> None: + json_payload = {} + + if space_id is not None: + json_payload["space_id"] = space_id + + self.client.post("/spaces/delete", json=json_payload) + + return None + + def get(self, *, space_id: str) -> Space: + json_payload = {} + + if space_id is not None: + json_payload["space_id"] = space_id + + res = self.client.post("/spaces/get", json=json_payload) + + return Space.from_dict(res["space"]) + + def list( + self, + ) -> List[Space]: + json_payload = {} + + res = self.client.post("/spaces/list", json=json_payload) + + return [Space.from_dict(item) for item in res["spaces"]] + + def remove_acs_entrances( + self, *, acs_entrance_ids: List[str], space_id: str + ) -> None: + json_payload = {} + + if acs_entrance_ids is not None: + json_payload["acs_entrance_ids"] = acs_entrance_ids + if space_id is not None: + json_payload["space_id"] = space_id + + self.client.post("/spaces/remove_acs_entrances", json=json_payload) + + return None + + def remove_devices(self, *, device_ids: List[str], space_id: str) -> None: + json_payload = {} + + if device_ids is not None: + json_payload["device_ids"] = device_ids + if space_id is not None: + json_payload["space_id"] = space_id + + self.client.post("/spaces/remove_devices", json=json_payload) + + return None + + def update(self, *, space_id: str, name: Optional[str] = None) -> Space: + json_payload = {} + + if space_id is not None: + json_payload["space_id"] = space_id + if name is not None: + json_payload["name"] = name + + res = self.client.post("/spaces/update", json=json_payload) + + return Space.from_dict(res["space"]) diff --git a/seam/routes/thermostats.py b/seam/routes/thermostats.py index ada02d3a..ff48998e 100644 --- a/seam/routes/thermostats.py +++ b/seam/routes/thermostats.py @@ -237,6 +237,7 @@ def list( limit: Optional[float] = None, manufacturer: Optional[str] = None, page_cursor: Optional[str] = None, + space_id: Optional[str] = None, unstable_location_id: Optional[str] = None, user_identifier_key: Optional[str] = None ) -> List[Device]: @@ -270,6 +271,8 @@ def list( json_payload["manufacturer"] = manufacturer if page_cursor is not None: json_payload["page_cursor"] = page_cursor + if space_id is not None: + json_payload["space_id"] = space_id if unstable_location_id is not None: json_payload["unstable_location_id"] = unstable_location_id if user_identifier_key is not None: