Commit Graph

256 Commits

Author SHA1 Message Date
Raman Gupta
4d2d367651 python-client: package as scrypted-sdk for PyPI (#2095)
* python-client: link transitive server modules required by plugin_remote

plugin_remote now imports cluster_labels, cluster_setup, plugin_console,
plugin_pip, and plugin_volume (plus a lazy plugin_repl import), but
packages/python-client only symlinks plugin_remote, rpc, rpc_reader, and
scrypted_python. As a result the client cannot be imported at all:

    ModuleNotFoundError: No module named 'cluster_labels'

Add symlinks for the missing modules, matching the existing pattern of
sharing one implementation with server/python.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WaSWgBV1XsZxWiYM16sWME

* python-client: extract reusable scrypted_client library from test.py

The only way to connect to Scrypted from Python has been to copy the
bootstrap out of test.py, which is not importable (module-level event
loop) and had drifted from the current rpc_reader/PluginRemote APIs
(writeJSON vs writeSerialized, missing ClusterSetup argument).

Move the transport and connection handshake into an importable
scrypted_client module:

- EioRpcTransport gains a close() method, an optional injectable
  aiohttp session for the engine.io connection, and queues its send
  loop on the running loop instead of via run_coroutine_threadsafe.
- connect_scrypted_client() performs login, the engine.io connect, and
  the getRemote handshake, with a connect timeout and consistent
  ScryptedConnectionError on failure. It also attaches the constructed
  SystemManager to remote.systemManager, the same wiring loadZip does
  for plugins, so PluginRemote.notify can dispatch events to
  systemManager.listen() callbacks.
- test.py becomes a small demo of the library and exits cleanly
  without os._exit(); the server URL is configurable via
  SCRYPTED_BASE_URL.

Verified live against a Scrypted server (device enumeration and OnOff
state reads).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WaSWgBV1XsZxWiYM16sWME

* python-client: bound all connect phases and tear down tasks on close

Address review feedback:

- close() now cancels and awaits both the send loop and the peer read
  loop (previously the send task was cancelled but never awaited and
  the read task was untracked), so closing the event loop after
  close() no longer risks 'Task was destroyed but it is pending'
  warnings.
- connect_scrypted_client() stores the read-loop task on the transport
  and propagates read-loop failures into the pending handshake future,
  so a link that dies mid-handshake fails immediately with the
  underlying error instead of waiting out the timeout.
- The timeout parameter now bounds every phase: the login POST
  (aiohttp ClientTimeout), the engine.io connect (asyncio.wait_for),
  and the wait for initial system state.
- Document session ownership: login_session is borrowed and never
  closed; an http_session given to EioRpcTransport is owned by the
  transport and closed by close().

Verified live against a Scrypted server: clean run with empty stderr,
plus connection-refused and bad-credential paths both raising
ScryptedConnectionError.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WaSWgBV1XsZxWiYM16sWME

* python-client: lazily import plugin host modules in plugin_remote

A client consuming plugin_remote (for SystemManager, DeviceManager,
MediaManager) only needs the types and the engine.io/rpc bits. The
plugin host modules (cluster_labels, plugin_console, plugin_pip,
plugin_volume) are only used inside loadZipWrapped, so import them
there -- importing plugin_remote no longer requires them, and the
client directory drops those symlinks (plugin_repl was already a lazy
import). cluster_setup stays: the client bootstrap constructs a
ClusterSetup.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* python-client: package as scrypted-sdk for PyPI

Adds a pyproject.toml and a hatchling build hook that generates the
scrypted_sdk package at build time: it copies the client-required
modules (client.py, rpc, rpc_reader, cluster_setup, plugin_remote, and
the scrypted_python types -- reading through the symlinks into
server/python and sdk/types) and mechanically rewrites the flat imports
to package-qualified ones (import rpc -> from scrypted_sdk import rpc),
so nothing is installed into consumers' top-level namespace. No runtime
code is modified and nothing generated is committed.

A python-sdk-v* tag builds, smoke-tests, and publishes to the existing
scrypted-sdk PyPI project via trusted publishing; the same workflow
builds and smoke-tests on PRs that touch the shared Python sources.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* python-client: add examples/light.py mirroring the typescript client example

The Python equivalent of packages/client/examples/light.ts: connect,
look up a light by name, turn it on, wait, turn it off, disconnect.
Works both with the installed scrypted-sdk package and directly from a
repo checkout (falls back to the flat modules in the parent directory).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 13:53:04 -07:00
Raman Gupta
c1ed7dff65 python-client: fix imports and extract a reusable scrypted_client library (#2094)
* python-client: link transitive server modules required by plugin_remote

plugin_remote now imports cluster_labels, cluster_setup, plugin_console,
plugin_pip, and plugin_volume (plus a lazy plugin_repl import), but
packages/python-client only symlinks plugin_remote, rpc, rpc_reader, and
scrypted_python. As a result the client cannot be imported at all:

    ModuleNotFoundError: No module named 'cluster_labels'

Add symlinks for the missing modules, matching the existing pattern of
sharing one implementation with server/python.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WaSWgBV1XsZxWiYM16sWME

* python-client: extract reusable scrypted_client library from test.py

The only way to connect to Scrypted from Python has been to copy the
bootstrap out of test.py, which is not importable (module-level event
loop) and had drifted from the current rpc_reader/PluginRemote APIs
(writeJSON vs writeSerialized, missing ClusterSetup argument).

Move the transport and connection handshake into an importable
scrypted_client module:

- EioRpcTransport gains a close() method, an optional injectable
  aiohttp session for the engine.io connection, and queues its send
  loop on the running loop instead of via run_coroutine_threadsafe.
- connect_scrypted_client() performs login, the engine.io connect, and
  the getRemote handshake, with a connect timeout and consistent
  ScryptedConnectionError on failure. It also attaches the constructed
  SystemManager to remote.systemManager, the same wiring loadZip does
  for plugins, so PluginRemote.notify can dispatch events to
  systemManager.listen() callbacks.
- test.py becomes a small demo of the library and exits cleanly
  without os._exit(); the server URL is configurable via
  SCRYPTED_BASE_URL.

Verified live against a Scrypted server (device enumeration and OnOff
state reads).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WaSWgBV1XsZxWiYM16sWME

* python-client: bound all connect phases and tear down tasks on close

Address review feedback:

- close() now cancels and awaits both the send loop and the peer read
  loop (previously the send task was cancelled but never awaited and
  the read task was untracked), so closing the event loop after
  close() no longer risks 'Task was destroyed but it is pending'
  warnings.
- connect_scrypted_client() stores the read-loop task on the transport
  and propagates read-loop failures into the pending handshake future,
  so a link that dies mid-handshake fails immediately with the
  underlying error instead of waiting out the timeout.
- The timeout parameter now bounds every phase: the login POST
  (aiohttp ClientTimeout), the engine.io connect (asyncio.wait_for),
  and the wait for initial system state.
- Document session ownership: login_session is borrowed and never
  closed; an http_session given to EioRpcTransport is owned by the
  transport and closed by close().

Verified live against a Scrypted server: clean run with empty stderr,
plus connection-refused and bad-credential paths both raising
ScryptedConnectionError.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WaSWgBV1XsZxWiYM16sWME

* python-client: lazily import plugin host modules in plugin_remote

A client consuming plugin_remote (for SystemManager, DeviceManager,
MediaManager) only needs the types and the engine.io/rpc bits. The
plugin host modules (cluster_labels, plugin_console, plugin_pip,
plugin_volume) are only used inside loadZipWrapped, so import them
there -- importing plugin_remote no longer requires them, and the
client directory drops those symlinks (plugin_repl was already a lazy
import). cluster_setup stays: the client bootstrap constructs a
ClusterSetup.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 13:36:55 -07:00
Raman Gupta
5945b17c5e python-client: fix test.py for current rpc_reader and PluginRemote APIs (#2087)
test.py has drifted from the server code it exercises: RpcTransport
implementations must provide writeSerialized (rpc.py no longer calls
writeJSON), and PluginRemote.__init__ now takes a ClusterSetup as its
first argument instead of the peer. Verified against a live scrypted
server (connects and enumerates devices).

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 12:53:35 -07:00
Koushik Dutta
21ecfd85ef client: strict mode 2026-04-02 15:59:56 -07:00
Koushik Dutta
25bf83832a http-fetch: fix buffer post 2026-03-31 09:06:21 -07:00
Koushik Dutta
181d0fdc92 packages: expose http fetch 2026-03-31 08:38:02 -07:00
Koushik Dutta
21ce1af766 packages: publish auth-fetch 2026-03-31 08:09:25 -07:00
Koushik Dutta
89b780590a packages: publish auth-fetch 2026-03-31 08:04:45 -07:00
Koushik Dutta
ebe6bcc58f client: add worker/fork support to web client 2025-12-16 12:22:03 -08:00
Koushik Dutta
61cf589800 sdk: update tool calls to include id 2025-12-09 16:03:26 -08:00
Koushik Dutta
2f45e72bd3 client: add dev hook 2025-10-30 09:43:00 -07:00
Koushik Dutta
457fbc594e client: improve base url detection 2025-10-21 23:34:45 -07:00
Koushik Dutta
aadb190c13 client: dont sent query token to connectRPCObject if accessing without a CORS request. 2025-10-21 10:34:29 -07:00
Koushik Dutta
c6a93cf245 sdk/client: update 2025-10-03 08:36:14 -07:00
Koushik Dutta
911b3f6014 sdk/client: update 2025-10-03 08:03:47 -07:00
Koushik Dutta
29714f82d5 client: include query headers in connectRPCObject 2025-09-22 22:43:49 -07:00
Koushik Dutta
c2054fc7e0 client: fix connectRPCObject always using scrypted cloud 2025-09-22 22:36:49 -07:00
Koushik Dutta
50b312b290 client: fix connectRPCObject always using scrypted cloud 2025-09-22 22:20:53 -07:00
Koushik Dutta
8f1c5fdf3c snapshot/sdk: add resolution property 2025-09-19 11:41:48 -07:00
Koushik Dutta
2e17e58060 client: cluster peer connect cleanups 2025-09-18 11:17:20 -07:00
Koushik Dutta
c9b88e6d8f client: implement connectRPCObject timeouts, fix typo 2025-09-17 09:00:49 -07:00
Koushik Dutta
eaa6da005b sdk/client: add optional dedicated connections and lifetime to connectRPCObject 2025-09-17 08:31:44 -07:00
Koushik Dutta
ca855bb9a6 client: fix http connection type reporting 2025-09-06 08:41:24 -07:00
Koushik Dutta
7240f328b3 client: update ScryptedClientConnectionType 2025-09-03 12:11:00 -07:00
Koushik Dutta
9de2b480ff webrtc: wip connectRPCObject 2025-08-28 11:31:37 -07:00
Koushik Dutta
8bbd112f60 webrtc: wip datachannels 2025-08-28 09:44:41 -07:00
Koushik Dutta
6c98fa62be client: rpc exports 2025-08-28 08:55:49 -07:00
Koushik Dutta
b784995ebb webrtc: wip transmission window updates 2025-08-25 16:33:25 -07:00
Koushik Dutta
f556ae7ff3 webrtc/sdk: initial lossless datachannel api 2025-08-25 10:02:39 -07:00
Koushik Dutta
1aa4d45caa sdk: update 2025-07-03 23:45:36 -07:00
Koushik Dutta
28fb2b0853 packages/deferred: publish 2025-07-03 23:02:48 -07:00
Koushik Dutta
4fae4fba3b sdk: update 2025-07-03 20:05:26 -07:00
Koushik Dutta
369ad59324 amcrest/http: fix http authentication when it includes query parameters 2025-07-02 09:05:24 -07:00
Koushik Dutta
add53d07f3 core: publish ui 2025-06-17 09:39:54 -07:00
Koushik Dutta
5b7cc826a6 sdk/client: fix build issues 2025-06-14 19:54:05 -07:00
Koushik Dutta
155a1ceb38 rpc: publish 2025-04-15 15:10:30 -07:00
Koushik Dutta
51d4aa7b3e sdk: update 2025-03-27 09:38:28 -07:00
Koushik Dutta
f729c76346 various: remove defunct ffmpeg args 2025-03-16 19:09:40 -07:00
Koushik Dutta
bd3b4ac387 client: publish 2025-03-13 10:12:56 -07:00
Koushik Dutta
0b24e57262 sdk: cleanup peer dependencies 2025-03-04 10:00:40 -08:00
Koushik Dutta
4db26a1779 sdk: relax types 2025-03-04 08:15:32 -08:00
Koushik Dutta
992fe98f5e core/sdk: update 2025-02-22 19:39:08 -08:00
Koushik Dutta
9426db12aa client/server: update deps 2025-02-19 14:56:53 -08:00
Koushik Dutta
9c3dab18da sdk/client/server/core/amcrest: add support for video text overlays 2025-02-07 13:07:55 -08:00
Koushik Dutta
e9ec78909b core: Fix missing buttons 2025-01-22 13:46:38 -08:00
Koushik Dutta
04065a3487 core/client: publish 2024-12-10 09:42:00 -08:00
Koushik Dutta
f942a13e90 client: add cluster manager 2024-12-10 09:32:23 -08:00
Koushik Dutta
21d020919a client: update des 2024-11-15 10:02:13 -08:00
Koushik Dutta
511f348cbe onvif: initial ptz preset support 2024-10-04 20:16:44 -07:00
Koushik Dutta
d38abcd563 core: ptz preset/home support 2024-10-04 15:41:08 -07:00