seymourlib 0.1.0__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.
- seymourlib-0.1.0/LICENSE +21 -0
- seymourlib-0.1.0/PKG-INFO +215 -0
- seymourlib-0.1.0/README.md +169 -0
- seymourlib-0.1.0/pyproject.toml +114 -0
- seymourlib-0.1.0/setup.cfg +4 -0
- seymourlib-0.1.0/src/seymourlib/__init__.py +0 -0
- seymourlib-0.1.0/src/seymourlib/cli.py +249 -0
- seymourlib-0.1.0/src/seymourlib/client.py +270 -0
- seymourlib-0.1.0/src/seymourlib/exceptions.py +14 -0
- seymourlib-0.1.0/src/seymourlib/protocol.py +388 -0
- seymourlib-0.1.0/src/seymourlib/py.typed +0 -0
- seymourlib-0.1.0/src/seymourlib/transport.py +87 -0
- seymourlib-0.1.0/src/seymourlib.egg-info/PKG-INFO +215 -0
- seymourlib-0.1.0/src/seymourlib.egg-info/SOURCES.txt +19 -0
- seymourlib-0.1.0/src/seymourlib.egg-info/dependency_links.txt +1 -0
- seymourlib-0.1.0/src/seymourlib.egg-info/entry_points.txt +2 -0
- seymourlib-0.1.0/src/seymourlib.egg-info/requires.txt +30 -0
- seymourlib-0.1.0/src/seymourlib.egg-info/top_level.txt +1 -0
- seymourlib-0.1.0/tests/test_client.py +842 -0
- seymourlib-0.1.0/tests/test_protocol.py +437 -0
- seymourlib-0.1.0/tests/test_transport.py +621 -0
seymourlib-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Richard Berg
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: seymourlib
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python client for Seymour masking screens, connected via direct serial or Global Caché iTach IP2SL
|
|
5
|
+
Author-email: Richard Berg <ars@richardberg.net>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: omepage, https://github.com/richard-berg/seymourlib
|
|
8
|
+
Project-URL: source, https://github.com/richard-berg/seymourlib.git
|
|
9
|
+
Project-URL: issues, https://github.com/richard-berg/seymourlib/issues
|
|
10
|
+
Keywords: seymour,masking,screen,serial,itach,ip2sl,home-assistant
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Topic :: Home Automation
|
|
17
|
+
Requires-Python: >=3.11
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
License-File: LICENSE
|
|
20
|
+
Requires-Dist: anyio>=3.4
|
|
21
|
+
Requires-Dist: attrs>=25.4.0
|
|
22
|
+
Requires-Dist: tenacity>=8.0
|
|
23
|
+
Requires-Dist: async-typer>=0.1.10
|
|
24
|
+
Provides-Extra: serial
|
|
25
|
+
Requires-Dist: pyserial_asyncio>=0.6; extra == "serial"
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: pytest>=7; extra == "dev"
|
|
28
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
|
|
29
|
+
Requires-Dist: pytest-timeout>=2; extra == "dev"
|
|
30
|
+
Requires-Dist: coverage>=7; extra == "dev"
|
|
31
|
+
Requires-Dist: mypy>=1.10; extra == "dev"
|
|
32
|
+
Requires-Dist: ruff>=0.6; extra == "dev"
|
|
33
|
+
Requires-Dist: types-setuptools; extra == "dev"
|
|
34
|
+
Requires-Dist: pyserial_asyncio>=0.6; extra == "dev"
|
|
35
|
+
Provides-Extra: lint
|
|
36
|
+
Requires-Dist: ruff>=0.6; extra == "lint"
|
|
37
|
+
Provides-Extra: typecheck
|
|
38
|
+
Requires-Dist: mypy>=1.10; extra == "typecheck"
|
|
39
|
+
Provides-Extra: test
|
|
40
|
+
Requires-Dist: pytest>=7; extra == "test"
|
|
41
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == "test"
|
|
42
|
+
Requires-Dist: pytest-timeout>=2; extra == "test"
|
|
43
|
+
Requires-Dist: coverage>=7; extra == "test"
|
|
44
|
+
Requires-Dist: pyserial_asyncio>=0.6; extra == "test"
|
|
45
|
+
Dynamic: license-file
|
|
46
|
+
|
|
47
|
+
# seymourlib
|
|
48
|
+
|
|
49
|
+
[](https://github.com/richard-berg/seymourlib/actions/workflows/ci.yml)
|
|
50
|
+
[](https://codecov.io/gh/richard-berg/seymourlib)
|
|
51
|
+
[](https://pypi.org/project/seymourlib/)
|
|
52
|
+
[](LICENSE)
|
|
53
|
+
|
|
54
|
+
Python client for Seymour masking screens, connected via direct serial or Global Caché iTach IP2SL. Built for rock-solid control loops, [Home Assistant](https://www.home-assistant.io/) integrations, and quick bench testing. 😎
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Highlights
|
|
59
|
+
|
|
60
|
+
- Async-first client and transport abstractions (TCP/IP2SL vs native serial) with consistent APIs.
|
|
61
|
+
- Protocol helpers that mirror the official Seymour RS232 spec, including parsing/validation utilities.
|
|
62
|
+
- `seymourctl` CLI for one-off commands, scripting, and smoke tests in CI.
|
|
63
|
+
- Batteries-included retry logic (`tenacity`), timeout handling, and graceful cancellation via `anyio`.
|
|
64
|
+
|
|
65
|
+
## Architecture at a Glance
|
|
66
|
+
|
|
67
|
+
```mermaid
|
|
68
|
+
flowchart TB
|
|
69
|
+
App[Your automation / HA stack] -->|async API| Client[SeymourClient]
|
|
70
|
+
Client --> Protocol[Protocol helpers]
|
|
71
|
+
Client --> Transport
|
|
72
|
+
Transport -->|TCP 4999| IP2SL[Global Caché IP2SL]
|
|
73
|
+
Transport -->|RS232| Screen[Seymour Masking Screen]
|
|
74
|
+
IP2SL -->|RS232| Screen
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Installation
|
|
78
|
+
|
|
79
|
+
### Requirements
|
|
80
|
+
|
|
81
|
+
- Python 3.11+
|
|
82
|
+
- `uv` >= 0.2 (recommended) or classic `pip`
|
|
83
|
+
|
|
84
|
+
### Using uv (dev + extras)
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
uv sync --all-extras
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Using pip (runtime only)
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
python -m venv .venv
|
|
94
|
+
source .venv/bin/activate # or .venv\Scripts\activate on Windows
|
|
95
|
+
pip install "seymourlib[serial]"
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
> 💡 The optional `serial` extra pulls in `pyserial-asyncio` so the same client works over USB/RS232 without the IP bridge.
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## First-Time Setup Checklist
|
|
103
|
+
|
|
104
|
+
1. **Clone & bootstrap**
|
|
105
|
+
- `git clone https://github.com/richard-berg/seymourlib && cd seymourlib`
|
|
106
|
+
- `uv sync --all-extras`
|
|
107
|
+
2. **Sanity-test the library**
|
|
108
|
+
- `uv run pytest` for local regression.
|
|
109
|
+
- `uv run seymourctl -v status` to hit your target once hardware is reachable.
|
|
110
|
+
3. **Wire up hardware**
|
|
111
|
+
- Confirm straight-through RS232 between IP2SL and the Seymour control port.
|
|
112
|
+
- For direct USB serial, a FTDI-based adapter keeps timing stable.
|
|
113
|
+
4. **Pick your transport**
|
|
114
|
+
- `TCPTransport(host, port=4999)` for IP2SL.
|
|
115
|
+
- `SerialTransport(port="COM5")` for direct connection (requires `[serial]`).
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## Usage Examples
|
|
120
|
+
|
|
121
|
+
### Python API
|
|
122
|
+
|
|
123
|
+
```python
|
|
124
|
+
import asyncio
|
|
125
|
+
from seymourlib.client import SeymourClient
|
|
126
|
+
from seymourlib.transport import TCPTransport
|
|
127
|
+
|
|
128
|
+
async def main() -> None:
|
|
129
|
+
async with SeymourClient(TCPTransport("192.168.1.70")) as client:
|
|
130
|
+
status = await client.get_status()
|
|
131
|
+
print(status)
|
|
132
|
+
|
|
133
|
+
asyncio.run(main())
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### CLI
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
uv run seymourctl --host 192.168.1.70 status
|
|
140
|
+
uv run seymourctl --host 192.168.1.70 preset apply 235
|
|
141
|
+
uv run seymourctl --help
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## IP2SL Setup & Diagnostics
|
|
147
|
+
|
|
148
|
+
### 1. Test direct serial first
|
|
149
|
+
|
|
150
|
+
- Connect a laptop directly to the Seymour RS232 port with a known-good USB adapter.
|
|
151
|
+
- Use `RealTerm`, `CoolTerm`, or any tty tool (115200 8N1 by default - verify via Seymour docs) to send a `PS?` (position status) command.
|
|
152
|
+
- Once commands succeed locally, you know the projector + cable + command syntax are correct before adding the network hop.
|
|
153
|
+
|
|
154
|
+
### 2. Discover the IP2SL via iHelp
|
|
155
|
+
|
|
156
|
+
- Download [Global Caché iHelp](https://www.globalcache.com/files/software/iHelp.zip) and run it on the same subnet.
|
|
157
|
+
- Press **Scan** to locate the IP2SL; note the MAC, firmware, and current IP.
|
|
158
|
+
- If nothing shows up, temporarily disable host firewalls or plug directly into a laptop + small DHCP scope.
|
|
159
|
+
|
|
160
|
+
### 3. Pin the network configuration
|
|
161
|
+
|
|
162
|
+
- Point a browser at `http://<your-ip>/` (from step 2) if you want a point-and-click GUI for DHCP/static IP, firmware, and reboot controls.
|
|
163
|
+
- Otherwise, install [Global Caché iTest](https://www.globalcache.com/files/software/iTest.zip).
|
|
164
|
+
- Use iTest or telnet into port **4998** (the configuration/control port).
|
|
165
|
+
- Send `set dhcp 0`, `set ip <static>`, `set mask <subnet>`, `set gateway <gw>`, then `save` to burn settings.
|
|
166
|
+
- Reboot (power cycle or `reboot` command) and confirm the device responds on the static IP.
|
|
167
|
+
- Document the MAC/IP in your DHCP reservations to avoid future collisions.
|
|
168
|
+
|
|
169
|
+
### 4. Lock the serial parameters
|
|
170
|
+
|
|
171
|
+
- Open the IP2SL web UI and switch to the **Serial** tab, or telnet/iTest into port **4998** for command-line configuration.
|
|
172
|
+
- Ensure the port is set to **115200 baud, 8 data bits, no parity, 1 stop bit** to match Seymour’s RS232 spec.
|
|
173
|
+
- Saving via the UI writes immediately; if you used the control port, run `set baud 115200`, `set data 8`, `set parity 0`, `set stop 1`, then `save` and reboot so the bridge always boots with the correct framing.
|
|
174
|
+
|
|
175
|
+
### 5. Exercise the serial bridge with iTest
|
|
176
|
+
|
|
177
|
+
- Open iTest (or `telnet`)
|
|
178
|
+
- Target port **4999** on the static IP, choose **RAW** mode, and click **Connect**.
|
|
179
|
+
- Send a few simple commands (e.g. `[01S]`, `[01P]`). The response should echo immediately.
|
|
180
|
+
- If latency creeps in, verify telnet keep-alives/device busy LEDs. Keep the window open while you walk through automation flows to watch the live traffic.
|
|
181
|
+
|
|
182
|
+
### 6. Promote to seymourlib
|
|
183
|
+
|
|
184
|
+
- Update your `.env` / config with the same host/port verified above.
|
|
185
|
+
- Start with `uv run seymourctl -v status` (verbose logging shows full request/response frames).
|
|
186
|
+
- Flip to automation ([Home Assistant](https://www.home-assistant.io/), Homebridge, etc.) once CLI calls are boringly reliable.
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## Troubleshooting & Field Notes
|
|
191
|
+
|
|
192
|
+
- ⚡ **No response over TCP but serial works** - Ensure port 4999 is open on local firewalls and that the IP2SL LED blinks on connect; if not, recheck the static IP settings from step 3.
|
|
193
|
+
- 🔌 **Commands hang after a day** - The IP2SL can queue stale sockets; send `set rs232disconnect 1` over port 4998 so idle TCP clients auto-close.
|
|
194
|
+
- 🔁 **Race conditions from HA automations** - Wrap your automations with seymourlib's async context manager so only one command pipeline exists per transport.
|
|
195
|
+
- 🧪 **Need deeper diagnostics** - Add `--verbose` to `seymourctl` to dump protocol frames, or [enable debug logging](https://docs.python.org/3/library/logging.html) to capture per-request timestamps.
|
|
196
|
+
- 🛠️ **Firmware drift** - Global Caché publishes release notes; mismatched firmware sometimes defaults back to DHCP on reboot. Always export a config snapshot in iHelp after upgrades.
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
## Related Docs & Inspiration
|
|
201
|
+
|
|
202
|
+
- [Seymour Screen Excellence RS232 Protocol PDF](https://www.seymourscreenexcellence.com/) (request via dealer portal).
|
|
203
|
+
- [Global Caché Knowledge Base](https://globalcache.zendesk.com/).
|
|
204
|
+
- [Home Assistant Template Switch Cookbook](https://www.home-assistant.io/integrations/template/) for inspiration on wiring seymourlib into HA.
|
|
205
|
+
- [CoolTerm Serial Terminal](https://freeware.the-meiers.org/) - handy for the direct-serial smoke test.
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## Contributing
|
|
210
|
+
|
|
211
|
+
- Fork, create a feature branch, and open a PR (tests + type checks 🚦 required).
|
|
212
|
+
- `pre-commit run` before pushing.
|
|
213
|
+
- Issues and hardware findings are welcome - real-world serial logs are gold for regression tests.
|
|
214
|
+
|
|
215
|
+
Need something else? Open a discussion or ping via Issues and we’ll figure it out together. 🙌
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# seymourlib
|
|
2
|
+
|
|
3
|
+
[](https://github.com/richard-berg/seymourlib/actions/workflows/ci.yml)
|
|
4
|
+
[](https://codecov.io/gh/richard-berg/seymourlib)
|
|
5
|
+
[](https://pypi.org/project/seymourlib/)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
|
|
8
|
+
Python client for Seymour masking screens, connected via direct serial or Global Caché iTach IP2SL. Built for rock-solid control loops, [Home Assistant](https://www.home-assistant.io/) integrations, and quick bench testing. 😎
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Highlights
|
|
13
|
+
|
|
14
|
+
- Async-first client and transport abstractions (TCP/IP2SL vs native serial) with consistent APIs.
|
|
15
|
+
- Protocol helpers that mirror the official Seymour RS232 spec, including parsing/validation utilities.
|
|
16
|
+
- `seymourctl` CLI for one-off commands, scripting, and smoke tests in CI.
|
|
17
|
+
- Batteries-included retry logic (`tenacity`), timeout handling, and graceful cancellation via `anyio`.
|
|
18
|
+
|
|
19
|
+
## Architecture at a Glance
|
|
20
|
+
|
|
21
|
+
```mermaid
|
|
22
|
+
flowchart TB
|
|
23
|
+
App[Your automation / HA stack] -->|async API| Client[SeymourClient]
|
|
24
|
+
Client --> Protocol[Protocol helpers]
|
|
25
|
+
Client --> Transport
|
|
26
|
+
Transport -->|TCP 4999| IP2SL[Global Caché IP2SL]
|
|
27
|
+
Transport -->|RS232| Screen[Seymour Masking Screen]
|
|
28
|
+
IP2SL -->|RS232| Screen
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Installation
|
|
32
|
+
|
|
33
|
+
### Requirements
|
|
34
|
+
|
|
35
|
+
- Python 3.11+
|
|
36
|
+
- `uv` >= 0.2 (recommended) or classic `pip`
|
|
37
|
+
|
|
38
|
+
### Using uv (dev + extras)
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
uv sync --all-extras
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Using pip (runtime only)
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
python -m venv .venv
|
|
48
|
+
source .venv/bin/activate # or .venv\Scripts\activate on Windows
|
|
49
|
+
pip install "seymourlib[serial]"
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
> 💡 The optional `serial` extra pulls in `pyserial-asyncio` so the same client works over USB/RS232 without the IP bridge.
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## First-Time Setup Checklist
|
|
57
|
+
|
|
58
|
+
1. **Clone & bootstrap**
|
|
59
|
+
- `git clone https://github.com/richard-berg/seymourlib && cd seymourlib`
|
|
60
|
+
- `uv sync --all-extras`
|
|
61
|
+
2. **Sanity-test the library**
|
|
62
|
+
- `uv run pytest` for local regression.
|
|
63
|
+
- `uv run seymourctl -v status` to hit your target once hardware is reachable.
|
|
64
|
+
3. **Wire up hardware**
|
|
65
|
+
- Confirm straight-through RS232 between IP2SL and the Seymour control port.
|
|
66
|
+
- For direct USB serial, a FTDI-based adapter keeps timing stable.
|
|
67
|
+
4. **Pick your transport**
|
|
68
|
+
- `TCPTransport(host, port=4999)` for IP2SL.
|
|
69
|
+
- `SerialTransport(port="COM5")` for direct connection (requires `[serial]`).
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Usage Examples
|
|
74
|
+
|
|
75
|
+
### Python API
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
import asyncio
|
|
79
|
+
from seymourlib.client import SeymourClient
|
|
80
|
+
from seymourlib.transport import TCPTransport
|
|
81
|
+
|
|
82
|
+
async def main() -> None:
|
|
83
|
+
async with SeymourClient(TCPTransport("192.168.1.70")) as client:
|
|
84
|
+
status = await client.get_status()
|
|
85
|
+
print(status)
|
|
86
|
+
|
|
87
|
+
asyncio.run(main())
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### CLI
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
uv run seymourctl --host 192.168.1.70 status
|
|
94
|
+
uv run seymourctl --host 192.168.1.70 preset apply 235
|
|
95
|
+
uv run seymourctl --help
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## IP2SL Setup & Diagnostics
|
|
101
|
+
|
|
102
|
+
### 1. Test direct serial first
|
|
103
|
+
|
|
104
|
+
- Connect a laptop directly to the Seymour RS232 port with a known-good USB adapter.
|
|
105
|
+
- Use `RealTerm`, `CoolTerm`, or any tty tool (115200 8N1 by default - verify via Seymour docs) to send a `PS?` (position status) command.
|
|
106
|
+
- Once commands succeed locally, you know the projector + cable + command syntax are correct before adding the network hop.
|
|
107
|
+
|
|
108
|
+
### 2. Discover the IP2SL via iHelp
|
|
109
|
+
|
|
110
|
+
- Download [Global Caché iHelp](https://www.globalcache.com/files/software/iHelp.zip) and run it on the same subnet.
|
|
111
|
+
- Press **Scan** to locate the IP2SL; note the MAC, firmware, and current IP.
|
|
112
|
+
- If nothing shows up, temporarily disable host firewalls or plug directly into a laptop + small DHCP scope.
|
|
113
|
+
|
|
114
|
+
### 3. Pin the network configuration
|
|
115
|
+
|
|
116
|
+
- Point a browser at `http://<your-ip>/` (from step 2) if you want a point-and-click GUI for DHCP/static IP, firmware, and reboot controls.
|
|
117
|
+
- Otherwise, install [Global Caché iTest](https://www.globalcache.com/files/software/iTest.zip).
|
|
118
|
+
- Use iTest or telnet into port **4998** (the configuration/control port).
|
|
119
|
+
- Send `set dhcp 0`, `set ip <static>`, `set mask <subnet>`, `set gateway <gw>`, then `save` to burn settings.
|
|
120
|
+
- Reboot (power cycle or `reboot` command) and confirm the device responds on the static IP.
|
|
121
|
+
- Document the MAC/IP in your DHCP reservations to avoid future collisions.
|
|
122
|
+
|
|
123
|
+
### 4. Lock the serial parameters
|
|
124
|
+
|
|
125
|
+
- Open the IP2SL web UI and switch to the **Serial** tab, or telnet/iTest into port **4998** for command-line configuration.
|
|
126
|
+
- Ensure the port is set to **115200 baud, 8 data bits, no parity, 1 stop bit** to match Seymour’s RS232 spec.
|
|
127
|
+
- Saving via the UI writes immediately; if you used the control port, run `set baud 115200`, `set data 8`, `set parity 0`, `set stop 1`, then `save` and reboot so the bridge always boots with the correct framing.
|
|
128
|
+
|
|
129
|
+
### 5. Exercise the serial bridge with iTest
|
|
130
|
+
|
|
131
|
+
- Open iTest (or `telnet`)
|
|
132
|
+
- Target port **4999** on the static IP, choose **RAW** mode, and click **Connect**.
|
|
133
|
+
- Send a few simple commands (e.g. `[01S]`, `[01P]`). The response should echo immediately.
|
|
134
|
+
- If latency creeps in, verify telnet keep-alives/device busy LEDs. Keep the window open while you walk through automation flows to watch the live traffic.
|
|
135
|
+
|
|
136
|
+
### 6. Promote to seymourlib
|
|
137
|
+
|
|
138
|
+
- Update your `.env` / config with the same host/port verified above.
|
|
139
|
+
- Start with `uv run seymourctl -v status` (verbose logging shows full request/response frames).
|
|
140
|
+
- Flip to automation ([Home Assistant](https://www.home-assistant.io/), Homebridge, etc.) once CLI calls are boringly reliable.
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## Troubleshooting & Field Notes
|
|
145
|
+
|
|
146
|
+
- ⚡ **No response over TCP but serial works** - Ensure port 4999 is open on local firewalls and that the IP2SL LED blinks on connect; if not, recheck the static IP settings from step 3.
|
|
147
|
+
- 🔌 **Commands hang after a day** - The IP2SL can queue stale sockets; send `set rs232disconnect 1` over port 4998 so idle TCP clients auto-close.
|
|
148
|
+
- 🔁 **Race conditions from HA automations** - Wrap your automations with seymourlib's async context manager so only one command pipeline exists per transport.
|
|
149
|
+
- 🧪 **Need deeper diagnostics** - Add `--verbose` to `seymourctl` to dump protocol frames, or [enable debug logging](https://docs.python.org/3/library/logging.html) to capture per-request timestamps.
|
|
150
|
+
- 🛠️ **Firmware drift** - Global Caché publishes release notes; mismatched firmware sometimes defaults back to DHCP on reboot. Always export a config snapshot in iHelp after upgrades.
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## Related Docs & Inspiration
|
|
155
|
+
|
|
156
|
+
- [Seymour Screen Excellence RS232 Protocol PDF](https://www.seymourscreenexcellence.com/) (request via dealer portal).
|
|
157
|
+
- [Global Caché Knowledge Base](https://globalcache.zendesk.com/).
|
|
158
|
+
- [Home Assistant Template Switch Cookbook](https://www.home-assistant.io/integrations/template/) for inspiration on wiring seymourlib into HA.
|
|
159
|
+
- [CoolTerm Serial Terminal](https://freeware.the-meiers.org/) - handy for the direct-serial smoke test.
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## Contributing
|
|
164
|
+
|
|
165
|
+
- Fork, create a feature branch, and open a PR (tests + type checks 🚦 required).
|
|
166
|
+
- `pre-commit run` before pushing.
|
|
167
|
+
- Issues and hardware findings are welcome - real-world serial logs are gold for regression tests.
|
|
168
|
+
|
|
169
|
+
Need something else? Open a discussion or ping via Issues and we’ll figure it out together. 🙌
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=65", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "seymourlib"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Python client for Seymour masking screens, connected via direct serial or Global Caché iTach IP2SL"
|
|
9
|
+
keywords = ["seymour", "masking", "screen", "serial", "itach", "ip2sl", "home-assistant"]
|
|
10
|
+
authors = [{name = "Richard Berg", email = "ars@richardberg.net"}]
|
|
11
|
+
classifiers = [
|
|
12
|
+
"Development Status :: 3 - Alpha",
|
|
13
|
+
"Intended Audience :: Developers",
|
|
14
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
15
|
+
"Programming Language :: Python :: 3.11",
|
|
16
|
+
"Programming Language :: Python :: 3.12",
|
|
17
|
+
"Topic :: Home Automation",
|
|
18
|
+
]
|
|
19
|
+
license = "MIT"
|
|
20
|
+
license-files = ["LICENSE"]
|
|
21
|
+
readme = "README.md"
|
|
22
|
+
requires-python = ">=3.11"
|
|
23
|
+
dependencies = [
|
|
24
|
+
"anyio>=3.4",
|
|
25
|
+
"attrs>=25.4.0",
|
|
26
|
+
"tenacity>=8.0",
|
|
27
|
+
"async-typer>=0.1.10",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
[project.urls]
|
|
31
|
+
omepage = "https://github.com/richard-berg/seymourlib"
|
|
32
|
+
source = "https://github.com/richard-berg/seymourlib.git"
|
|
33
|
+
issues = "https://github.com/richard-berg/seymourlib/issues"
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
[project.optional-dependencies]
|
|
37
|
+
serial = [
|
|
38
|
+
"pyserial_asyncio>=0.6",
|
|
39
|
+
]
|
|
40
|
+
dev = [
|
|
41
|
+
"pytest>=7",
|
|
42
|
+
"pytest-asyncio>=0.23",
|
|
43
|
+
"pytest-timeout>=2",
|
|
44
|
+
"coverage>=7",
|
|
45
|
+
"mypy>=1.10",
|
|
46
|
+
"ruff>=0.6",
|
|
47
|
+
"types-setuptools",
|
|
48
|
+
"pyserial_asyncio>=0.6",
|
|
49
|
+
]
|
|
50
|
+
lint = [
|
|
51
|
+
"ruff>=0.6",
|
|
52
|
+
]
|
|
53
|
+
typecheck = [
|
|
54
|
+
"mypy>=1.10",
|
|
55
|
+
]
|
|
56
|
+
test = [
|
|
57
|
+
"pytest>=7",
|
|
58
|
+
"pytest-asyncio>=0.23",
|
|
59
|
+
"pytest-timeout>=2",
|
|
60
|
+
"coverage>=7",
|
|
61
|
+
"pyserial_asyncio>=0.6",
|
|
62
|
+
]
|
|
63
|
+
|
|
64
|
+
[project.scripts]
|
|
65
|
+
seymourctl = "seymourlib.cli:main"
|
|
66
|
+
|
|
67
|
+
[tool.ruff]
|
|
68
|
+
line-length = 100
|
|
69
|
+
target-version = "py312"
|
|
70
|
+
fix = true
|
|
71
|
+
unsafe-fixes = false
|
|
72
|
+
lint.select = [
|
|
73
|
+
"E", # pycodestyle errors
|
|
74
|
+
"F", # pyflakes
|
|
75
|
+
"W", # warnings
|
|
76
|
+
"I", # import sorting
|
|
77
|
+
"UP", # pyupgrade
|
|
78
|
+
"B", # bugbear
|
|
79
|
+
"ASYNC", # async best practices
|
|
80
|
+
]
|
|
81
|
+
lint.ignore = [
|
|
82
|
+
"E501", # line length handled separately
|
|
83
|
+
]
|
|
84
|
+
|
|
85
|
+
[tool.ruff.format]
|
|
86
|
+
quote-style = "double"
|
|
87
|
+
indent-style = "space"
|
|
88
|
+
line-ending = "lf"
|
|
89
|
+
|
|
90
|
+
# ----------------------------------------
|
|
91
|
+
# mypy configuration
|
|
92
|
+
# ----------------------------------------
|
|
93
|
+
[tool.mypy]
|
|
94
|
+
python_version = "3.12"
|
|
95
|
+
warn_unused_configs = true
|
|
96
|
+
warn_redundant_casts = true
|
|
97
|
+
warn_unused_ignores = true
|
|
98
|
+
warn_return_any = true
|
|
99
|
+
warn_unreachable = true
|
|
100
|
+
strict = true
|
|
101
|
+
|
|
102
|
+
# Ignore missing type hints in dependencies, except ours
|
|
103
|
+
ignore_missing_imports = true
|
|
104
|
+
|
|
105
|
+
[tool.setuptools.package-data]
|
|
106
|
+
"seymourlib" = ["py.typed"]
|
|
107
|
+
|
|
108
|
+
# ----------------------------------------
|
|
109
|
+
# Pytest configuration
|
|
110
|
+
# ----------------------------------------
|
|
111
|
+
[tool.pytest.ini_options]
|
|
112
|
+
addopts = "-q --timeout=10"
|
|
113
|
+
asyncio_mode = "auto"
|
|
114
|
+
testpaths = ["tests"]
|
|
File without changes
|