wifi-controller 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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 jugglingbear
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,199 @@
1
+ Metadata-Version: 2.3
2
+ Name: wifi-controller
3
+ Version: 0.1.0
4
+ Summary: Cross-platform Wi-Fi controller with pluggable providers for macOS, Linux, and Windows.
5
+ License: MIT
6
+ Keywords: wifi,wireless,macos,linux,network
7
+ Author: jugglingbear
8
+ Requires-Python: >=3.10,<4.0
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Operating System :: MacOS
13
+ Classifier: Operating System :: POSIX :: Linux
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: System :: Networking
20
+ Requires-Dist: bear-tools (>=0.1.37,<0.2.0)
21
+ Project-URL: Homepage, https://github.com/jugglingbear/wifi_controller
22
+ Project-URL: Repository, https://github.com/jugglingbear/wifi_controller
23
+ Description-Content-Type: text/markdown
24
+
25
+ # wifi-controller
26
+
27
+ A cross-platform Wi-Fi controller for Python with pluggable provider architecture.
28
+ Supports macOS and Linux out of the box, with an optional Swift-based scanner for
29
+ macOS 15+ where Apple redacts SSIDs without Location Services authorization.
30
+
31
+ ## Features
32
+
33
+ - **Cross-platform** -- built-in providers for macOS (`networksetup`, `ipconfig`,
34
+ `system_profiler`) and Linux (`nmcli`, `iwgetid`)
35
+ - **Pluggable providers** -- register your own scan/connect/disconnect implementations
36
+ with priority-based resolution
37
+ - **macOS SSID redaction workaround** -- optional Swift scanner (`extras/ssid_scanner/`)
38
+ uses CoreWLAN + CoreLocation to return real SSIDs on macOS 15+
39
+ - **Zero dependencies** -- pure Python, stdlib only
40
+
41
+ ## Installation
42
+
43
+ ```bash
44
+ pip install wifi-controller
45
+ ```
46
+
47
+ Or with Poetry:
48
+
49
+ ```bash
50
+ poetry add wifi-controller
51
+ ```
52
+
53
+ ## Quick Start
54
+
55
+ ```python
56
+ from wifi_controller import WiFiController
57
+
58
+ wifi = WiFiController()
59
+
60
+ # Get current network
61
+ ssid = wifi.get_current_ssid()
62
+ print(f"Connected to: {ssid}")
63
+
64
+ # Scan nearby networks
65
+ networks = wifi.scan()
66
+ for net in networks:
67
+ print(f" {net.ssid} (RSSI={net.rssi}, CH={net.channel})")
68
+
69
+ # Connect to a network
70
+ wifi.connect("MyNetwork", "hunter2")
71
+
72
+ # Poll for a specific SSID
73
+ found = wifi.scan_for_ssid("MyNetwork", timeout_sec=30)
74
+
75
+ # Disconnect
76
+ wifi.disconnect()
77
+ ```
78
+
79
+ ## macOS 15+ SSID Redaction
80
+
81
+ Starting with macOS 15 (Sequoia), Apple redacts SSID information from
82
+ `system_profiler`, `CoreWLAN`, and other system APIs unless the calling process
83
+ has Location Services authorization via a signed app bundle.
84
+
85
+ The built-in Python providers **cannot** work around this limitation. To get
86
+ real SSIDs on macOS 15+, build the Swift scanner from `extras/ssid_scanner/`:
87
+
88
+ ```bash
89
+ # Prerequisites: Xcode Command Line Tools + Apple Development certificate
90
+ make -C extras/ssid_scanner check # verify prerequisites
91
+ make -C extras/ssid_scanner all # build and sign
92
+ ```
93
+
94
+ Then register the Swift providers:
95
+
96
+ ```python
97
+ from wifi_controller import WiFiController
98
+ from wifi_controller._swift import (
99
+ SwiftSsidScannerCurrentSSID,
100
+ SwiftSsidScannerScan,
101
+ SwiftSsidScannerConnect,
102
+ SwiftSsidScannerDisconnect,
103
+ )
104
+
105
+ wifi = WiFiController()
106
+ binary = "extras/ssid_scanner/ssid_scanner" # path to built binary
107
+
108
+ wifi.register_scan_provider(SwiftSsidScannerScan(binary), priority=10)
109
+ wifi.register_current_ssid_provider(SwiftSsidScannerCurrentSSID(binary), priority=10)
110
+ wifi.register_connect_provider(SwiftSsidScannerConnect(binary), priority=10)
111
+ wifi.register_disconnect_provider(SwiftSsidScannerDisconnect(binary), priority=10)
112
+
113
+ # Now scan() returns real SSIDs on macOS 15+
114
+ networks = wifi.scan()
115
+ ```
116
+
117
+ ## Custom Providers
118
+
119
+ Implement any of the four provider ABCs to add support for additional tools:
120
+
121
+ ```python
122
+ from wifi_controller import WiFiController, SSIDScanProvider, SSIDInfo
123
+
124
+ class MyCustomScanner(SSIDScanProvider):
125
+ @property
126
+ def name(self) -> str:
127
+ return "my_scanner"
128
+
129
+ def is_available(self) -> bool:
130
+ return True # check if your tool is installed
131
+
132
+ def scan_ssids(self, interface: str, timeout: int = 15) -> list[SSIDInfo]:
133
+ # ... your implementation ...
134
+ return [SSIDInfo(ssid="Example", bssid="00:11:22:33:44:55", rssi=-42, channel=6)]
135
+
136
+ wifi = WiFiController()
137
+ wifi.register_scan_provider(MyCustomScanner(), priority=20)
138
+ ```
139
+
140
+ Provider ABCs:
141
+
142
+ | ABC | Operation |
143
+ |-----|-----------|
144
+ | `CurrentSSIDProvider` | Get the currently-connected SSID |
145
+ | `SSIDScanProvider` | Scan for nearby networks |
146
+ | `SSIDConnectProvider` | Connect to a network (SSID + password) |
147
+ | `SSIDDisconnectProvider` | Disconnect from the current network |
148
+
149
+ Higher priority providers are tried first. The first provider whose
150
+ `is_available()` returns `True` is used and cached for subsequent calls.
151
+
152
+ ## Project Layout
153
+
154
+ ```
155
+ wifi_controller/
156
+ ├── src/wifi_controller/ # Python package (ships on PyPI)
157
+ │ ├── __init__.py # WiFiController orchestrator
158
+ │ ├── _types.py # SSIDInfo, WiFiConnectionError
159
+ │ ├── _abc.py # Four provider ABCs
160
+ │ ├── _macos.py # Built-in macOS providers
161
+ │ ├── _linux.py # Built-in Linux providers
162
+ │ └── _swift.py # Python wrappers for Swift binary
163
+ ├── extras/ssid_scanner/ # Swift source (not on PyPI, clone to use)
164
+ │ ├── scan.swift # CoreWLAN + CoreLocation scanner
165
+ │ ├── Makefile # Build, sign, test
166
+ │ └── *.plist # App bundle configuration
167
+ ├── docs/ # Architecture diagrams (PlantUML)
168
+ └── tests/ # Unit tests
169
+ ```
170
+
171
+ ## Architecture
172
+
173
+ See [docs/](docs/) for PlantUML diagrams covering:
174
+
175
+ - **Class diagram** -- provider ABCs, WiFiController, built-in implementations
176
+ - **Sequence diagram** -- provider resolution and operation flow
177
+ - **Component diagram** -- package structure and platform boundaries
178
+
179
+ ## Development
180
+
181
+ ```bash
182
+ # Install dev dependencies
183
+ poetry install
184
+
185
+ # Run tests
186
+ poetry run pytest
187
+
188
+ # Lint
189
+ poetry run ruff check src/ tests/
190
+
191
+ # Format
192
+ poetry run ruff format src/ tests/
193
+ ```
194
+
195
+ ## License
196
+
197
+ MIT -- see [LICENSE](LICENSE).
198
+
199
+
@@ -0,0 +1,174 @@
1
+ # wifi-controller
2
+
3
+ A cross-platform Wi-Fi controller for Python with pluggable provider architecture.
4
+ Supports macOS and Linux out of the box, with an optional Swift-based scanner for
5
+ macOS 15+ where Apple redacts SSIDs without Location Services authorization.
6
+
7
+ ## Features
8
+
9
+ - **Cross-platform** -- built-in providers for macOS (`networksetup`, `ipconfig`,
10
+ `system_profiler`) and Linux (`nmcli`, `iwgetid`)
11
+ - **Pluggable providers** -- register your own scan/connect/disconnect implementations
12
+ with priority-based resolution
13
+ - **macOS SSID redaction workaround** -- optional Swift scanner (`extras/ssid_scanner/`)
14
+ uses CoreWLAN + CoreLocation to return real SSIDs on macOS 15+
15
+ - **Zero dependencies** -- pure Python, stdlib only
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ pip install wifi-controller
21
+ ```
22
+
23
+ Or with Poetry:
24
+
25
+ ```bash
26
+ poetry add wifi-controller
27
+ ```
28
+
29
+ ## Quick Start
30
+
31
+ ```python
32
+ from wifi_controller import WiFiController
33
+
34
+ wifi = WiFiController()
35
+
36
+ # Get current network
37
+ ssid = wifi.get_current_ssid()
38
+ print(f"Connected to: {ssid}")
39
+
40
+ # Scan nearby networks
41
+ networks = wifi.scan()
42
+ for net in networks:
43
+ print(f" {net.ssid} (RSSI={net.rssi}, CH={net.channel})")
44
+
45
+ # Connect to a network
46
+ wifi.connect("MyNetwork", "hunter2")
47
+
48
+ # Poll for a specific SSID
49
+ found = wifi.scan_for_ssid("MyNetwork", timeout_sec=30)
50
+
51
+ # Disconnect
52
+ wifi.disconnect()
53
+ ```
54
+
55
+ ## macOS 15+ SSID Redaction
56
+
57
+ Starting with macOS 15 (Sequoia), Apple redacts SSID information from
58
+ `system_profiler`, `CoreWLAN`, and other system APIs unless the calling process
59
+ has Location Services authorization via a signed app bundle.
60
+
61
+ The built-in Python providers **cannot** work around this limitation. To get
62
+ real SSIDs on macOS 15+, build the Swift scanner from `extras/ssid_scanner/`:
63
+
64
+ ```bash
65
+ # Prerequisites: Xcode Command Line Tools + Apple Development certificate
66
+ make -C extras/ssid_scanner check # verify prerequisites
67
+ make -C extras/ssid_scanner all # build and sign
68
+ ```
69
+
70
+ Then register the Swift providers:
71
+
72
+ ```python
73
+ from wifi_controller import WiFiController
74
+ from wifi_controller._swift import (
75
+ SwiftSsidScannerCurrentSSID,
76
+ SwiftSsidScannerScan,
77
+ SwiftSsidScannerConnect,
78
+ SwiftSsidScannerDisconnect,
79
+ )
80
+
81
+ wifi = WiFiController()
82
+ binary = "extras/ssid_scanner/ssid_scanner" # path to built binary
83
+
84
+ wifi.register_scan_provider(SwiftSsidScannerScan(binary), priority=10)
85
+ wifi.register_current_ssid_provider(SwiftSsidScannerCurrentSSID(binary), priority=10)
86
+ wifi.register_connect_provider(SwiftSsidScannerConnect(binary), priority=10)
87
+ wifi.register_disconnect_provider(SwiftSsidScannerDisconnect(binary), priority=10)
88
+
89
+ # Now scan() returns real SSIDs on macOS 15+
90
+ networks = wifi.scan()
91
+ ```
92
+
93
+ ## Custom Providers
94
+
95
+ Implement any of the four provider ABCs to add support for additional tools:
96
+
97
+ ```python
98
+ from wifi_controller import WiFiController, SSIDScanProvider, SSIDInfo
99
+
100
+ class MyCustomScanner(SSIDScanProvider):
101
+ @property
102
+ def name(self) -> str:
103
+ return "my_scanner"
104
+
105
+ def is_available(self) -> bool:
106
+ return True # check if your tool is installed
107
+
108
+ def scan_ssids(self, interface: str, timeout: int = 15) -> list[SSIDInfo]:
109
+ # ... your implementation ...
110
+ return [SSIDInfo(ssid="Example", bssid="00:11:22:33:44:55", rssi=-42, channel=6)]
111
+
112
+ wifi = WiFiController()
113
+ wifi.register_scan_provider(MyCustomScanner(), priority=20)
114
+ ```
115
+
116
+ Provider ABCs:
117
+
118
+ | ABC | Operation |
119
+ |-----|-----------|
120
+ | `CurrentSSIDProvider` | Get the currently-connected SSID |
121
+ | `SSIDScanProvider` | Scan for nearby networks |
122
+ | `SSIDConnectProvider` | Connect to a network (SSID + password) |
123
+ | `SSIDDisconnectProvider` | Disconnect from the current network |
124
+
125
+ Higher priority providers are tried first. The first provider whose
126
+ `is_available()` returns `True` is used and cached for subsequent calls.
127
+
128
+ ## Project Layout
129
+
130
+ ```
131
+ wifi_controller/
132
+ ├── src/wifi_controller/ # Python package (ships on PyPI)
133
+ │ ├── __init__.py # WiFiController orchestrator
134
+ │ ├── _types.py # SSIDInfo, WiFiConnectionError
135
+ │ ├── _abc.py # Four provider ABCs
136
+ │ ├── _macos.py # Built-in macOS providers
137
+ │ ├── _linux.py # Built-in Linux providers
138
+ │ └── _swift.py # Python wrappers for Swift binary
139
+ ├── extras/ssid_scanner/ # Swift source (not on PyPI, clone to use)
140
+ │ ├── scan.swift # CoreWLAN + CoreLocation scanner
141
+ │ ├── Makefile # Build, sign, test
142
+ │ └── *.plist # App bundle configuration
143
+ ├── docs/ # Architecture diagrams (PlantUML)
144
+ └── tests/ # Unit tests
145
+ ```
146
+
147
+ ## Architecture
148
+
149
+ See [docs/](docs/) for PlantUML diagrams covering:
150
+
151
+ - **Class diagram** -- provider ABCs, WiFiController, built-in implementations
152
+ - **Sequence diagram** -- provider resolution and operation flow
153
+ - **Component diagram** -- package structure and platform boundaries
154
+
155
+ ## Development
156
+
157
+ ```bash
158
+ # Install dev dependencies
159
+ poetry install
160
+
161
+ # Run tests
162
+ poetry run pytest
163
+
164
+ # Lint
165
+ poetry run ruff check src/ tests/
166
+
167
+ # Format
168
+ poetry run ruff format src/ tests/
169
+ ```
170
+
171
+ ## License
172
+
173
+ MIT -- see [LICENSE](LICENSE).
174
+
@@ -0,0 +1,48 @@
1
+ [build-system]
2
+ requires = ["poetry-core>=1.0.0"]
3
+ build-backend = "poetry.core.masonry.api"
4
+
5
+ [tool.poetry]
6
+ name = "wifi-controller"
7
+ version = "0.1.0"
8
+ description = "Cross-platform Wi-Fi controller with pluggable providers for macOS, Linux, and Windows."
9
+ authors = ["jugglingbear"]
10
+ license = "MIT"
11
+ readme = "README.md"
12
+ homepage = "https://github.com/jugglingbear/wifi_controller"
13
+ repository = "https://github.com/jugglingbear/wifi_controller"
14
+ keywords = ["wifi", "wireless", "macos", "linux", "network"]
15
+ classifiers = [
16
+ "Development Status :: 3 - Alpha",
17
+ "Intended Audience :: Developers",
18
+ "License :: OSI Approved :: MIT License",
19
+ "Operating System :: MacOS",
20
+ "Operating System :: POSIX :: Linux",
21
+ "Programming Language :: Python :: 3",
22
+ "Programming Language :: Python :: 3.10",
23
+ "Programming Language :: Python :: 3.11",
24
+ "Programming Language :: Python :: 3.12",
25
+ "Programming Language :: Python :: 3.13",
26
+ "Topic :: System :: Networking",
27
+ ]
28
+ packages = [{ include = "wifi_controller", from = "src" }]
29
+
30
+ [tool.poetry.dependencies]
31
+ python = ">=3.10,<4.0"
32
+ bear-tools = "^0.1.37"
33
+
34
+ [tool.poetry.group.dev.dependencies]
35
+ pytest = ">=7.0"
36
+ pytest-cov = ">=4.0"
37
+ ruff = ">=0.4"
38
+
39
+ [tool.pytest.ini_options]
40
+ testpaths = ["tests"]
41
+
42
+ [tool.ruff]
43
+ target-version = "py310"
44
+ line-length = 120
45
+ src = ["src"]
46
+
47
+ [tool.ruff.lint]
48
+ select = ["E", "F", "W", "I", "UP", "B", "SIM"]