ipguard-api 0.1.0b6__tar.gz → 0.1.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. {ipguard_api-0.1.0b6 → ipguard_api-0.1.2}/DEVELOPMENT.md +5 -5
  2. ipguard_api-0.1.2/PKG-INFO +51 -0
  3. ipguard_api-0.1.2/README.md +35 -0
  4. ipguard_api-0.1.2/docs/PROTOCOL.md +33 -0
  5. {ipguard_api-0.1.0b6 → ipguard_api-0.1.2}/docs/USAGE.md +2 -2
  6. {ipguard_api-0.1.0b6 → ipguard_api-0.1.2}/pyproject.toml +2 -1
  7. {ipguard_api-0.1.0b6 → ipguard_api-0.1.2}/uv.lock +1 -1
  8. ipguard_api-0.1.0b6/PKG-INFO +0 -47
  9. ipguard_api-0.1.0b6/README.md +0 -31
  10. {ipguard_api-0.1.0b6 → ipguard_api-0.1.2}/.gitignore +0 -0
  11. {ipguard_api-0.1.0b6 → ipguard_api-0.1.2}/.python-version +0 -0
  12. {ipguard_api-0.1.0b6 → ipguard_api-0.1.2}/LICENSE +0 -0
  13. {ipguard_api-0.1.0b6 → ipguard_api-0.1.2}/build-constraints.txt +0 -0
  14. {ipguard_api-0.1.0b6 → ipguard_api-0.1.2}/conftest.py +0 -0
  15. {ipguard_api-0.1.0b6 → ipguard_api-0.1.2}/scripts/packaging_policy.py +0 -0
  16. {ipguard_api-0.1.0b6 → ipguard_api-0.1.2}/scripts/release_metadata.py +0 -0
  17. {ipguard_api-0.1.0b6 → ipguard_api-0.1.2}/src/ipguard_api/__init__.py +0 -0
  18. {ipguard_api-0.1.0b6 → ipguard_api-0.1.2}/src/ipguard_api/client.py +0 -0
  19. {ipguard_api-0.1.0b6 → ipguard_api-0.1.2}/src/ipguard_api/errors.py +0 -0
  20. {ipguard_api-0.1.0b6 → ipguard_api-0.1.2}/src/ipguard_api/models.py +0 -0
  21. {ipguard_api-0.1.0b6 → ipguard_api-0.1.2}/src/ipguard_api/protocol.py +0 -0
  22. {ipguard_api-0.1.0b6 → ipguard_api-0.1.2}/src/ipguard_api/py.typed +0 -0
  23. {ipguard_api-0.1.0b6 → ipguard_api-0.1.2}/src/ipguard_api/transport.py +0 -0
  24. {ipguard_api-0.1.0b6 → ipguard_api-0.1.2}/src/ipguard_api/validation.py +0 -0
  25. {ipguard_api-0.1.0b6 → ipguard_api-0.1.2}/tests/test_cloud.py +0 -0
  26. {ipguard_api-0.1.0b6 → ipguard_api-0.1.2}/tests/test_contract.py +0 -0
  27. {ipguard_api-0.1.0b6 → ipguard_api-0.1.2}/tests/test_network_guard.py +0 -0
  28. {ipguard_api-0.1.0b6 → ipguard_api-0.1.2}/tests/test_packaging.py +0 -0
  29. {ipguard_api-0.1.0b6 → ipguard_api-0.1.2}/tests/test_protocol.py +0 -0
  30. {ipguard_api-0.1.0b6 → ipguard_api-0.1.2}/uv.toml +0 -0
@@ -33,13 +33,13 @@ The explicit Hatch `only-include` lists in [pyproject.toml](pyproject.toml) are
33
33
 
34
34
  The SDK repository is [KennyTangHK/ipguard-api](https://github.com/KennyTangHK/ipguard-api). Its default branch is `master`. Ordinary CI checks run on pushes to `master` and on pull requests.
35
35
 
36
- Before the first release, configure PyPI Trusted Publishing for the `ipguard-api` project. Use repository owner `KennyTangHK`, repository name `ipguard-api`, workflow filename `publish.yml`, and GitHub environment `pypi`. Configure the matching GitHub environment and its release approval and tag rules as appropriate. See [PyPI publisher setup](https://docs.pypi.org/trusted-publishers/adding-a-publisher/) or [creating a new PyPI project](https://docs.pypi.org/trusted-publishers/creating-a-project-through-oidc/). Confirm package-name availability and publisher ownership in the target account.
36
+ The package is available on [PyPI](https://pypi.org/project/ipguard-api/). Releases use Trusted Publishing from `KennyTangHK/ipguard-api`, workflow `publish.yml`, and GitHub environment `pypi`. If the repository, workflow filename, or environment changes, update the [PyPI trusted publisher](https://docs.pypi.org/trusted-publishers/adding-a-publisher/) before the next release.
37
37
 
38
- When you have explicit authorization to release SDK changes:
38
+ To publish a release:
39
39
 
40
- 1. Set the intended version in [pyproject.toml](pyproject.toml). Run `uv lock` and the local checks. Use `X.Y.ZbN` for beta releases or `X.Y.Z` for stable releases. Review the lockfile change. A package version change must not silently upgrade external dependencies.
40
+ 1. Set the intended `X.Y.Z` version in [pyproject.toml](pyproject.toml). Keep the beta notice and Beta development-status classifier while the project is in beta. Run `uv lock` and the local checks. Review the lockfile change. A package version change must not silently upgrade external dependencies.
41
41
  2. Commit and push the reviewed changes to `master` in the independent SDK repository.
42
- 3. Push the matching `vX.Y.ZbN` or `vX.Y.Z` tag. The tag must match the committed package version exactly. Do not move or reuse a published tag or version.
42
+ 3. Push the matching `vX.Y.Z` tag. The tag must match the committed package version exactly. Do not move or reuse a published tag or version.
43
43
 
44
44
  The [publication workflow](.github/workflows/publish.yml) calls the checks workflow for the tagged commit. It downloads the three checked assets from that run. It then validates the event, tag, clean checkout, tested commit, remote tag, archive payloads, and hashes again. It stages only the two validated distribution archives for upload.
45
45
 
@@ -53,4 +53,4 @@ Keep the Android handset profile independent of package releases. Human-readable
53
53
 
54
54
  Incoming-call callbacks expose only sanitized board information. The SDK is passive by default. An explicit `auto_hangup=True` enables one immediate hangup attempt with cause zero after notification. The SDK does not retain media offers, answer calls, or implement media transport. Disconnect and shutdown cancel owned work. The SDK never replays uncertain attempts.
55
55
 
56
- Store credentials in the consumer's private session storage. Keep raw packets, session contents, arbitrary provider errors, call tokens, and media data out of logs, tracebacks, and callback data. [README.md](README.md) and [API usage](docs/USAGE.md) document the plaintext enrollment stage and other protocol limits. Synthetic tests show local client behavior. They do not show current provider acceptance, account-wide routing guarantees, or physical door outcomes.
56
+ Store credentials in the consumer's private session storage. Keep raw packets, session contents, arbitrary provider errors, call tokens, and media data out of logs, tracebacks, and callback data. See [Protocol behavior and limits](docs/PROTOCOL.md) for transport security and verification limits. [API usage](docs/USAGE.md) covers client setup and examples.
@@ -0,0 +1,51 @@
1
+ Metadata-Version: 2.5
2
+ Name: ipguard-api
3
+ Version: 0.1.2
4
+ Summary: Typed asynchronous client for the IPGUARD cloud service
5
+ Project-URL: Repository, https://github.com/KennyTangHK/ipguard-api
6
+ Project-URL: Issues, https://github.com/KennyTangHK/ipguard-api/issues
7
+ Project-URL: Documentation, https://github.com/KennyTangHK/ipguard-api/blob/master/docs/USAGE.md
8
+ License-Expression: Apache-2.0
9
+ License-File: LICENSE
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Programming Language :: Python :: 3.14
12
+ Classifier: Typing :: Typed
13
+ Requires-Python: <3.15,>=3.14.2
14
+ Requires-Dist: pyzmq<28,>=27.2.0
15
+ Description-Content-Type: text/markdown
16
+
17
+ # ipguard-api
18
+
19
+ An asynchronous Python client for the IPGUARD cloud service. Connect an existing account, discover door relays, receive incoming-call notifications, and send door-release commands.
20
+
21
+ **Beta software.** This software is not a stable release.
22
+
23
+ This is an independent community project. It is not affiliated with, sponsored by, or endorsed by IPGUARD, NACD, or Noralsy.
24
+
25
+ Use this software lawfully. Use it only with your own authorized account and with buildings or doors that you have permission to access and control. Do not use it for unauthorized access or abuse.
26
+
27
+ **Use at your own risk.** To the extent permitted by applicable law, the software is provided without warranty. Its authors and contributors accept no responsibility or liability for its use. The warranty and liability limits in the [Apache-2.0 license](LICENSE) apply.
28
+
29
+ ## Installation
30
+
31
+ Install from [PyPI](https://pypi.org/project/ipguard-api/):
32
+
33
+ ```sh
34
+ python -m pip install ipguard-api
35
+ ```
36
+
37
+ See the [API guide](docs/USAGE.md) for account setup and examples.
38
+
39
+ ## Limitations
40
+
41
+ **Video calling and camera streaming are not supported.** The client does not answer calls or provide audio conversation, Bluetooth access, SIP calling, or manual call controls.
42
+
43
+ Incoming-call notifications are passive by default. Set `auto_hangup=True` to make one immediate hangup attempt after a notification. This is intended to trigger carrier-call fallback sooner. The provider and account configuration determine the actual routing and timing.
44
+
45
+ Enrollment uses unencrypted TCP. A door-release acknowledgement does not confirm that the physical door opened. See [protocol behavior and limits](docs/PROTOCOL.md) for transport security, supported behavior and verification limits.
46
+
47
+ ## Development
48
+
49
+ See the [development guide](DEVELOPMENT.md) for local setup, tests, and releases.
50
+
51
+ For Home Assistant, use [hass-ipguard](https://github.com/KennyTangHK/hass-ipguard). This Python library can also be used independently.
@@ -0,0 +1,35 @@
1
+ # ipguard-api
2
+
3
+ An asynchronous Python client for the IPGUARD cloud service. Connect an existing account, discover door relays, receive incoming-call notifications, and send door-release commands.
4
+
5
+ **Beta software.** This software is not a stable release.
6
+
7
+ This is an independent community project. It is not affiliated with, sponsored by, or endorsed by IPGUARD, NACD, or Noralsy.
8
+
9
+ Use this software lawfully. Use it only with your own authorized account and with buildings or doors that you have permission to access and control. Do not use it for unauthorized access or abuse.
10
+
11
+ **Use at your own risk.** To the extent permitted by applicable law, the software is provided without warranty. Its authors and contributors accept no responsibility or liability for its use. The warranty and liability limits in the [Apache-2.0 license](LICENSE) apply.
12
+
13
+ ## Installation
14
+
15
+ Install from [PyPI](https://pypi.org/project/ipguard-api/):
16
+
17
+ ```sh
18
+ python -m pip install ipguard-api
19
+ ```
20
+
21
+ See the [API guide](docs/USAGE.md) for account setup and examples.
22
+
23
+ ## Limitations
24
+
25
+ **Video calling and camera streaming are not supported.** The client does not answer calls or provide audio conversation, Bluetooth access, SIP calling, or manual call controls.
26
+
27
+ Incoming-call notifications are passive by default. Set `auto_hangup=True` to make one immediate hangup attempt after a notification. This is intended to trigger carrier-call fallback sooner. The provider and account configuration determine the actual routing and timing.
28
+
29
+ Enrollment uses unencrypted TCP. A door-release acknowledgement does not confirm that the physical door opened. See [protocol behavior and limits](docs/PROTOCOL.md) for transport security, supported behavior and verification limits.
30
+
31
+ ## Development
32
+
33
+ See the [development guide](DEVELOPMENT.md) for local setup, tests, and releases.
34
+
35
+ For Home Assistant, use [hass-ipguard](https://github.com/KennyTangHK/hass-ipguard). This Python library can also be used independently.
@@ -0,0 +1,33 @@
1
+ # Protocol behavior and compatibility limits
2
+
3
+ This document describes the behavior and limits of `ipguard-api`. The library supports account enrollment, saved authorization, relay discovery, door-release commands and incoming-call notifications. For client setup and code examples, see [API usage](USAGE.md).
4
+
5
+ ## Enrollment and saved authorization
6
+
7
+ Verification requests and resends require an explicit method call. A reconnection with saved authorization does not request another code or replace an installation. The library refuses replacement by default. The application must obtain user consent before it enables replacement, which can disable the previous installation. Revoked authorization stops reconnection and requires enrollment again.
8
+
9
+ Serialized sessions contain the account identifier, installation UUID, verification code, server public key and any activation credential returned by the service. The application is responsible for private storage of this data. Treat serialized sessions and their backups as credentials. Do not include them in issue reports or debug captures. The SDK omits credentials, raw packets and arbitrary provider error text from diagnostics.
10
+
11
+ ## Transport security
12
+
13
+ The default enrollment endpoint is `tcp://zmq.campiserveur.com:5579`. This connection is not encrypted. The client gets the server public key during that exchange. ZeroMQ CURVE then protects the authenticated connection at `tcp://zmq.campiserveur.com:5577` with that key.
14
+
15
+ CURVE does not protect the earlier enrollment exchange. Enrollment does not use HTTPS, and the SDK has no encrypted enrollment fallback. Do not disable CURVE authentication to work around connection failures.
16
+
17
+ ## Door-release commands
18
+
19
+ A relay can receive a release command only if it has current remote-release permission and does not need badge confirmation. The SDK checks permission immediately before transmission. It rejects unavailable or busy operations. It does not put a release command in a queue for a later connection.
20
+
21
+ A successful result means that the board sent a matching acknowledgement. It does not show if the physical door moved, or if it is open or locked. If a command may have been sent but there is no matching acknowledgement, its outcome is unknown. The SDK does not automatically replay release commands. The application must report the uncertainty and leave any further attempt to the user.
22
+
23
+ ## Incoming calls
24
+
25
+ A fresh, validated incoming call on the active account connection causes a callback. Callbacks include sanitized board information when the caller can be identified. Unknown callers still cause a callback. The SDK suppresses duplicates for a limited time. It validates and discards media descriptions. It does not expose call tokens, caller routes or media credentials through callbacks. Calls missed during a disconnection are not recovered.
26
+
27
+ The SDK is passive by default. It does not acknowledge, answer or hang up an incoming call. An application can set `auto_hangup=True` to enable one immediate hangup attempt after notification. This is intended to start carrier-call fallback sooner. Routing and timing depend on the provider and account configuration. The SDK skips the attempt if the call or connection is no longer current or the write is busy. It cancels pending work on disconnect or close. It does not queue, retry or replay an uncertain write. See [Automatic hangup](USAGE.md#automatic-hangup) for client configuration.
28
+
29
+ Video calls, camera streaming, audio conversation, call answering, manual call controls, Firebase, Bluetooth and SIP calling are not supported. Incoming calls do not cause a door release.
30
+
31
+ ## Verification limits
32
+
33
+ Automated SDK tests use synthetic accounts and loopback plaintext/CURVE servers. These checks validate client behavior against fixtures. They do not establish current provider acceptance, credential lifetime, account aliases, simultaneous mobile installations, incoming-call delivery, carrier routing or a physical door release. Routing and delivery depend on the provider and account configuration. A live account or physical check requires explicit user direction.
@@ -81,7 +81,7 @@ Video calling and camera streaming are not supported. The SDK has no API to reta
81
81
 
82
82
  ### Automatic hangup
83
83
 
84
- `IpguardClient(auto_hangup=True)` enables one immediate hangup attempt after each fresh, validated incoming-call notification. This is intended to trigger carrier-call fallback sooner. The provider and account configuration determine the actual routing and timing. The standalone SDK defaults to `auto_hangup=False`. In that mode, it gives incoming-call notifications without sending a hangup. Home Assistant has an **Auto hangup** option for each account. It is enabled by default. Find it under **Settings → Devices & services → IPGUARD → Configure**.
84
+ `IpguardClient(auto_hangup=True)` enables one immediate hangup attempt after each fresh, validated incoming-call notification. This is intended to trigger carrier-call fallback sooner. The provider and account configuration determine the actual routing and timing. The SDK defaults to `auto_hangup=False`. In that mode, it gives incoming-call notifications without sending a hangup. Applications enable this option separately for each client.
85
85
 
86
86
  An attempt requires a current call, connection, and installation. The client skips busy, stale, disconnected, or superseded attempts. Call termination, disconnect, or shutdown cancels pending work. The client does not queue or replay attempts. It does not retry a send with an uncertain outcome. Duplicate suppression stays available across reconnects for the same installation. It does not persist after the client closes or the process restarts. The SDK has no manual hangup or call-answering API.
87
87
 
@@ -97,7 +97,7 @@ The SDK checks the current permission and rejects a concurrent release for the s
97
97
 
98
98
  ## Transport and logging
99
99
 
100
- The default activation endpoint is `tcp://zmq.campiserveur.com:5579`. The default secure messaging endpoint is `tcp://zmq.campiserveur.com:5577`. Activation uses unencrypted TCP, including for retrieval of the server public key. CURVE protects the subsequent authenticated connection. The client does not implement an encrypted enrollment fallback. Do not disable CURVE authentication to work around handshake failures.
100
+ For the default endpoints and enrollment security, see [Transport security](PROTOCOL.md#transport-security). Do not disable CURVE authentication to work around connection failures.
101
101
 
102
102
  The constructor accepts test endpoints, `timeout`, `reconnect_delay`, and `max_reconnect_delay`. Saved sessions retain their endpoints.
103
103
 
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "ipguard-api"
7
- version = "0.1.0b6"
7
+ version = "0.1.2"
8
8
  description = "Typed asynchronous client for the IPGUARD cloud service"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.14.2,<3.15"
@@ -58,6 +58,7 @@ only-include = [
58
58
  "uv.lock",
59
59
  "build-constraints.txt",
60
60
  "DEVELOPMENT.md",
61
+ "docs/PROTOCOL.md",
61
62
  "docs/USAGE.md",
62
63
  "README.md",
63
64
  "LICENSE",
@@ -136,7 +136,7 @@ wheels = [
136
136
 
137
137
  [[package]]
138
138
  name = "ipguard-api"
139
- version = "0.1.0b6"
139
+ version = "0.1.2"
140
140
  source = { editable = "." }
141
141
  dependencies = [
142
142
  { name = "pyzmq" },
@@ -1,47 +0,0 @@
1
- Metadata-Version: 2.5
2
- Name: ipguard-api
3
- Version: 0.1.0b6
4
- Summary: Typed asynchronous client for the IPGUARD cloud service
5
- Project-URL: Repository, https://github.com/KennyTangHK/ipguard-api
6
- Project-URL: Issues, https://github.com/KennyTangHK/ipguard-api/issues
7
- Project-URL: Documentation, https://github.com/KennyTangHK/ipguard-api/blob/master/docs/USAGE.md
8
- License-Expression: Apache-2.0
9
- License-File: LICENSE
10
- Classifier: Development Status :: 4 - Beta
11
- Classifier: Programming Language :: Python :: 3.14
12
- Classifier: Typing :: Typed
13
- Requires-Python: <3.15,>=3.14.2
14
- Requires-Dist: pyzmq<28,>=27.2.0
15
- Description-Content-Type: text/markdown
16
-
17
- # ipguard-api
18
-
19
- Source repository: [KennyTangHK/ipguard-api](https://github.com/KennyTangHK/ipguard-api). The default branch is `master`.
20
-
21
- **Beta software.** This software is not a stable release.
22
-
23
- This is an independent community project. It is not affiliated with, sponsored by, or endorsed by IPGUARD, NACD, or Noralsy.
24
-
25
- Use this software lawfully. Use it only with your own authorized account and with buildings or doors that you have permission to access and control. Do not use it for unauthorized access or abuse.
26
-
27
- **Use at your own risk.** To the extent permitted by applicable law, the software is provided without warranty. Its authors and contributors accept no responsibility or liability for its use. The warranty and liability limits in the [Apache-2.0 license](LICENSE) apply.
28
-
29
- `ipguard-api` is a typed asynchronous Python library for an authorized IPGUARD cloud account. It supports phone and email enrollment, ZeroMQ CURVE connections, relay discovery, incoming-call callbacks, and explicit remote release commands. It does not depend on Home Assistant.
30
-
31
- **Video calling and camera streaming are not supported.** By default, incoming calls give passive notifications. The library does not retain media offers or answer calls. To attempt one immediate hangup after the notification, set `auto_hangup=True` for the client. This attempt is intended to trigger carrier-call fallback sooner. The provider and account configuration determine the actual routing and timing.
32
-
33
- For enrollment, saved sessions, event subscriptions, and command errors, see [API usage](docs/USAGE.md). For tests and release builds, see [Development](DEVELOPMENT.md).
34
-
35
- The [package metadata](pyproject.toml) defines the Python and dependency requirements. The [lockfile](uv.lock) records the resolved dependencies. The PyPI package is `ipguard-api`; the import package is `ipguard_api`. The separate Home Assistant integration is at [hass-ipguard](https://github.com/KennyTangHK/hass-ipguard).
36
-
37
- The [protocol implementation](src/ipguard_api/protocol.py) defines the protocol behavior. The [handset profile](src/ipguard_api/models.py) defines the client metadata values. Direct enrollment uses unencrypted TCP to retrieve the server public key. The authenticated channel then uses CURVE. A board acknowledgement does not show the physical state of a door. Bluetooth, SIP, pickup, and media transport and decoding are outside the scope of this library. Local fixtures test client behavior with synthetic accounts. They do not show current provider acceptance or physical door outcomes.
38
-
39
- ## Installation
40
-
41
- After a release is published to PyPI, install the package:
42
-
43
- ```sh
44
- python -m pip install --pre ipguard-api
45
- ```
46
-
47
- The `--pre` option permits beta releases. To install this checkout, use `python -m pip install .`. For a locked test environment, see the [development guide](DEVELOPMENT.md). Installation and import do not enroll an account or open a connection.
@@ -1,31 +0,0 @@
1
- # ipguard-api
2
-
3
- Source repository: [KennyTangHK/ipguard-api](https://github.com/KennyTangHK/ipguard-api). The default branch is `master`.
4
-
5
- **Beta software.** This software is not a stable release.
6
-
7
- This is an independent community project. It is not affiliated with, sponsored by, or endorsed by IPGUARD, NACD, or Noralsy.
8
-
9
- Use this software lawfully. Use it only with your own authorized account and with buildings or doors that you have permission to access and control. Do not use it for unauthorized access or abuse.
10
-
11
- **Use at your own risk.** To the extent permitted by applicable law, the software is provided without warranty. Its authors and contributors accept no responsibility or liability for its use. The warranty and liability limits in the [Apache-2.0 license](LICENSE) apply.
12
-
13
- `ipguard-api` is a typed asynchronous Python library for an authorized IPGUARD cloud account. It supports phone and email enrollment, ZeroMQ CURVE connections, relay discovery, incoming-call callbacks, and explicit remote release commands. It does not depend on Home Assistant.
14
-
15
- **Video calling and camera streaming are not supported.** By default, incoming calls give passive notifications. The library does not retain media offers or answer calls. To attempt one immediate hangup after the notification, set `auto_hangup=True` for the client. This attempt is intended to trigger carrier-call fallback sooner. The provider and account configuration determine the actual routing and timing.
16
-
17
- For enrollment, saved sessions, event subscriptions, and command errors, see [API usage](docs/USAGE.md). For tests and release builds, see [Development](DEVELOPMENT.md).
18
-
19
- The [package metadata](pyproject.toml) defines the Python and dependency requirements. The [lockfile](uv.lock) records the resolved dependencies. The PyPI package is `ipguard-api`; the import package is `ipguard_api`. The separate Home Assistant integration is at [hass-ipguard](https://github.com/KennyTangHK/hass-ipguard).
20
-
21
- The [protocol implementation](src/ipguard_api/protocol.py) defines the protocol behavior. The [handset profile](src/ipguard_api/models.py) defines the client metadata values. Direct enrollment uses unencrypted TCP to retrieve the server public key. The authenticated channel then uses CURVE. A board acknowledgement does not show the physical state of a door. Bluetooth, SIP, pickup, and media transport and decoding are outside the scope of this library. Local fixtures test client behavior with synthetic accounts. They do not show current provider acceptance or physical door outcomes.
22
-
23
- ## Installation
24
-
25
- After a release is published to PyPI, install the package:
26
-
27
- ```sh
28
- python -m pip install --pre ipguard-api
29
- ```
30
-
31
- The `--pre` option permits beta releases. To install this checkout, use `python -m pip install .`. For a locked test environment, see the [development guide](DEVELOPMENT.md). Installation and import do not enroll an account or open a connection.
File without changes
File without changes
File without changes
File without changes