subsequence 0.6.4__py3-none-any.whl
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.
- subsequence/__init__.py +231 -0
- subsequence/__main__.py +24 -0
- subsequence/assets/web/index.html +345 -0
- subsequence/cadences.py +113 -0
- subsequence/chord_graphs/__init__.py +100 -0
- subsequence/chord_graphs/aeolian_minor.py +158 -0
- subsequence/chord_graphs/chromatic_mediant.py +113 -0
- subsequence/chord_graphs/diminished.py +97 -0
- subsequence/chord_graphs/dorian_minor.py +127 -0
- subsequence/chord_graphs/functional_major.py +102 -0
- subsequence/chord_graphs/hooktheory_major.py +88 -0
- subsequence/chord_graphs/lydian_major.py +130 -0
- subsequence/chord_graphs/mixolydian.py +98 -0
- subsequence/chord_graphs/phrygian_minor.py +76 -0
- subsequence/chord_graphs/suspended.py +109 -0
- subsequence/chord_graphs/turnaround_global.py +157 -0
- subsequence/chord_graphs/whole_tone.py +77 -0
- subsequence/chords.py +419 -0
- subsequence/composition.py +6099 -0
- subsequence/conductor.py +238 -0
- subsequence/constants/__init__.py +24 -0
- subsequence/constants/durations.py +37 -0
- subsequence/constants/instruments/__init__.py +13 -0
- subsequence/constants/instruments/gm_cc.py +46 -0
- subsequence/constants/instruments/gm_drums.py +53 -0
- subsequence/constants/instruments/gm_instruments.py +32 -0
- subsequence/constants/instruments/roland_tr8s.py +320 -0
- subsequence/constants/instruments/vermona_drm1_drums.py +87 -0
- subsequence/constants/midi_notes.py +29 -0
- subsequence/constants/pulses.py +17 -0
- subsequence/constants/velocity.py +22 -0
- subsequence/definitions.py +232 -0
- subsequence/display.py +617 -0
- subsequence/easing.py +347 -0
- subsequence/event_emitter.py +109 -0
- subsequence/form_state.py +665 -0
- subsequence/forms.py +257 -0
- subsequence/groove.py +323 -0
- subsequence/harmonic_rhythm.py +83 -0
- subsequence/harmonic_state.py +352 -0
- subsequence/harmony.py +197 -0
- subsequence/held_notes.py +91 -0
- subsequence/helpers/__init__.py +0 -0
- subsequence/helpers/network.py +58 -0
- subsequence/helpers/wing.py +430 -0
- subsequence/intervals.py +436 -0
- subsequence/keystroke.py +249 -0
- subsequence/link_clock.py +128 -0
- subsequence/live_client.py +187 -0
- subsequence/live_reloader.py +298 -0
- subsequence/live_server.py +161 -0
- subsequence/melodic_state.py +483 -0
- subsequence/midi.py +97 -0
- subsequence/midi_utils.py +329 -0
- subsequence/mini_notation.py +164 -0
- subsequence/motifs.py +2356 -0
- subsequence/osc.py +194 -0
- subsequence/pattern.py +363 -0
- subsequence/pattern_algorithmic.py +2010 -0
- subsequence/pattern_builder.py +2589 -0
- subsequence/pattern_midi.py +1208 -0
- subsequence/progressions.py +1913 -0
- subsequence/py.typed +0 -0
- subsequence/roles.py +63 -0
- subsequence/sequence_utils.py +3123 -0
- subsequence/sequencer.py +2086 -0
- subsequence/tuning.py +453 -0
- subsequence/voicings.py +151 -0
- subsequence/web_ui.py +337 -0
- subsequence/weighted_graph.py +156 -0
- subsequence-0.6.4.dist-info/METADATA +208 -0
- subsequence-0.6.4.dist-info/RECORD +78 -0
- subsequence-0.6.4.dist-info/WHEEL +5 -0
- subsequence-0.6.4.dist-info/entry_points.txt +2 -0
- subsequence-0.6.4.dist-info/licenses/LICENSE +661 -0
- subsequence-0.6.4.dist-info/scm_file_list.json +193 -0
- subsequence-0.6.4.dist-info/scm_version.json +8 -0
- subsequence-0.6.4.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
"""Behringer WING mixer helper.
|
|
2
|
+
|
|
3
|
+
NOTE: This module is specific to Behringer WING series mixers (WING, WING Rack,
|
|
4
|
+
WING Compact). It is NOT part of the core Subsequence module and is provided
|
|
5
|
+
as a convenience for users who want to integrate WING discovery and address
|
|
6
|
+
exploration with their Subsequence compositions.
|
|
7
|
+
|
|
8
|
+
OSC port: The WING listens on UDP **port 2223** for all OSC traffic.
|
|
9
|
+
|
|
10
|
+
Typical workflow
|
|
11
|
+
----------------
|
|
12
|
+
::
|
|
13
|
+
|
|
14
|
+
import subsequence
|
|
15
|
+
import subsequence.helpers.wing as wing
|
|
16
|
+
|
|
17
|
+
# 1. Find the mixer on the LAN
|
|
18
|
+
device = wing.discover()
|
|
19
|
+
if device is None:
|
|
20
|
+
raise RuntimeError("No WING found on the network")
|
|
21
|
+
|
|
22
|
+
ip = device["ip"]
|
|
23
|
+
print(f"Found {device['model']} at {ip} (firmware {device['firmware']})")
|
|
24
|
+
|
|
25
|
+
# 2. Tell Subsequence to send OSC to it
|
|
26
|
+
composition.osc(send_port=wing.WING_PORT, send_host=ip)
|
|
27
|
+
|
|
28
|
+
# 3. In your patterns, use standard p.osc() / p.osc_ramp() with WING addresses
|
|
29
|
+
@composition.pattern(channel=0, length=4)
|
|
30
|
+
def mixer (p):
|
|
31
|
+
p.osc_ramp("/ch/1/fdr", 0.0, 0.75, shape="ease_in") # fade up channel 1
|
|
32
|
+
|
|
33
|
+
# 4. Explore available addresses at development time
|
|
34
|
+
wing.print_node(ip, "/ch/1") # list all parameters under channel 1
|
|
35
|
+
wing.print_node(ip, "/ch/1/fdr") # inspect the fader leaf value
|
|
36
|
+
|
|
37
|
+
CLI usage
|
|
38
|
+
---------
|
|
39
|
+
::
|
|
40
|
+
|
|
41
|
+
# Discover — prints the device info
|
|
42
|
+
python -m subsequence.helpers.wing
|
|
43
|
+
|
|
44
|
+
# Query a node — pretty-prints its structure / value
|
|
45
|
+
python -m subsequence.helpers.wing /ch/1
|
|
46
|
+
python -m subsequence.helpers.wing /ch/1/fdr
|
|
47
|
+
python -m subsequence.helpers.wing /
|
|
48
|
+
|
|
49
|
+
Address conventions
|
|
50
|
+
-------------------
|
|
51
|
+
Addresses use the WING's internal node tree. Key top-level nodes::
|
|
52
|
+
|
|
53
|
+
/ch/1..40 Input channels
|
|
54
|
+
/aux/1..8 Aux inputs
|
|
55
|
+
/bus/1..16 Mix buses
|
|
56
|
+
/main/lr Main L/R bus
|
|
57
|
+
/main/m Mono / centre bus
|
|
58
|
+
/mtx/1..8 Matrix outputs
|
|
59
|
+
/fx/1..8 FX returns
|
|
60
|
+
/dca/1..8 DCA groups
|
|
61
|
+
/mgrp/1..8 Mute groups
|
|
62
|
+
|
|
63
|
+
Useful leaf addresses per channel (e.g. ``/ch/1/…``)::
|
|
64
|
+
|
|
65
|
+
fdr Fader level, 0.0 (−∞) .. 1.0 (≈+10 dB)
|
|
66
|
+
pan Pan, 0.0 (full L) .. 1.0 (full R), 0.5 = centre
|
|
67
|
+
mute Mute, 0 = unmuted, 1 = muted
|
|
68
|
+
name Channel name (string)
|
|
69
|
+
col Colour index (int)
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
import logging
|
|
73
|
+
import socket
|
|
74
|
+
import sys
|
|
75
|
+
import typing
|
|
76
|
+
|
|
77
|
+
import pythonosc.osc_message
|
|
78
|
+
import pythonosc.osc_message_builder
|
|
79
|
+
|
|
80
|
+
import subsequence.helpers.network
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
logger = logging.getLogger(__name__)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
WING_PORT: int = 2223
|
|
87
|
+
"""Default UDP port for the Behringer WING."""
|
|
88
|
+
|
|
89
|
+
_BROADCAST_ADDRS: typing.List[str] = ["255.255.255.255"]
|
|
90
|
+
"""Broadcast addresses tried by :func:`discover`. Supplemented at runtime with
|
|
91
|
+
subnet-specific addresses derived from local interfaces."""
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
# ── Internal helpers ──────────────────────────────────────────────────────────
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _build_osc (address: str) -> bytes:
|
|
98
|
+
"""Build a no-argument OSC message for *address*."""
|
|
99
|
+
return pythonosc.osc_message_builder.OscMessageBuilder(address=address).build().dgram
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _parse_osc (data: bytes) -> typing.Optional[pythonosc.osc_message.OscMessage]:
|
|
103
|
+
"""Parse raw bytes into an OscMessage, returning None on failure."""
|
|
104
|
+
try:
|
|
105
|
+
return pythonosc.osc_message.OscMessage(data)
|
|
106
|
+
except Exception:
|
|
107
|
+
return None
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def _classify (params: typing.List[typing.Any]) -> str:
|
|
111
|
+
"""Return 'node' or 'leaf' based on the response param types.
|
|
112
|
+
|
|
113
|
+
- Any float or int in the params → numeric leaf.
|
|
114
|
+
- All strings, more than one → node (directory listing of children).
|
|
115
|
+
- Single string → string leaf.
|
|
116
|
+
"""
|
|
117
|
+
if not params:
|
|
118
|
+
return "leaf"
|
|
119
|
+
if any(isinstance(p, (float, int)) and not isinstance(p, bool) for p in params):
|
|
120
|
+
return "leaf"
|
|
121
|
+
if len(params) > 1:
|
|
122
|
+
return "node"
|
|
123
|
+
return "leaf"
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
# ── Public API ────────────────────────────────────────────────────────────────
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def discover (
|
|
130
|
+
port: int = WING_PORT,
|
|
131
|
+
timeout: float = 2.0,
|
|
132
|
+
) -> typing.Optional[typing.Dict[str, str]]:
|
|
133
|
+
"""Auto-discover a Behringer WING on the local network.
|
|
134
|
+
|
|
135
|
+
Sends an OSC ``/?`` broadcast to UDP port *port* and waits for a reply.
|
|
136
|
+
The WING responds with a comma-separated info string containing its IP,
|
|
137
|
+
model, and firmware version.
|
|
138
|
+
|
|
139
|
+
Parameters:
|
|
140
|
+
port: UDP port to broadcast on (default 2223 — the WING's OSC port).
|
|
141
|
+
timeout: Seconds to wait for a reply.
|
|
142
|
+
|
|
143
|
+
Returns:
|
|
144
|
+
A ``dict`` with keys ``ip``, ``device``, ``model``, ``form_factor``,
|
|
145
|
+
``firmware``, or ``None`` if no device replied.
|
|
146
|
+
|
|
147
|
+
Example::
|
|
148
|
+
|
|
149
|
+
device = wing.discover()
|
|
150
|
+
if device:
|
|
151
|
+
print(device["ip"]) # "192.168.0.116"
|
|
152
|
+
print(device["firmware"]) # "3.1-0-g9f314617:release"
|
|
153
|
+
"""
|
|
154
|
+
dgram = _build_osc("/?")
|
|
155
|
+
|
|
156
|
+
# Build list of broadcast addresses to try: global first, then subnet-specific
|
|
157
|
+
broadcasts = list(_BROADCAST_ADDRS) + subsequence.helpers.network.get_local_broadcasts()
|
|
158
|
+
# Deduplicate while preserving order
|
|
159
|
+
seen: typing.Set[str] = set()
|
|
160
|
+
unique_broadcasts: typing.List[str] = []
|
|
161
|
+
for b in broadcasts:
|
|
162
|
+
if b not in seen:
|
|
163
|
+
seen.add(b)
|
|
164
|
+
unique_broadcasts.append(b)
|
|
165
|
+
|
|
166
|
+
for broadcast in unique_broadcasts:
|
|
167
|
+
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
168
|
+
try:
|
|
169
|
+
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
|
|
170
|
+
sock.settimeout(timeout / len(unique_broadcasts))
|
|
171
|
+
sock.sendto(dgram, (broadcast, port))
|
|
172
|
+
try:
|
|
173
|
+
data, addr = sock.recvfrom(4096)
|
|
174
|
+
except socket.timeout:
|
|
175
|
+
continue
|
|
176
|
+
except OSError as e:
|
|
177
|
+
# The docstring promises None when nothing replies — a machine
|
|
178
|
+
# with no route for this broadcast (offline laptop, ENETUNREACH)
|
|
179
|
+
# is "nothing replied", not a crash. Try the next address.
|
|
180
|
+
logger.debug(f"WING discovery broadcast to {broadcast} failed: {e}")
|
|
181
|
+
continue
|
|
182
|
+
finally:
|
|
183
|
+
sock.close()
|
|
184
|
+
|
|
185
|
+
msg = _parse_osc(data)
|
|
186
|
+
if msg is None:
|
|
187
|
+
continue
|
|
188
|
+
|
|
189
|
+
params = list(msg.params)
|
|
190
|
+
if not params or not isinstance(params[0], str):
|
|
191
|
+
continue
|
|
192
|
+
|
|
193
|
+
# Response: "WING,192.168.0.116,WING-PP-20021049,wing-rack,ID,firmware"
|
|
194
|
+
fields = params[0].split(",")
|
|
195
|
+
result: typing.Dict[str, str] = {
|
|
196
|
+
"ip": addr[0],
|
|
197
|
+
"device": fields[0] if len(fields) > 0 else "",
|
|
198
|
+
"model": fields[2] if len(fields) > 2 else "",
|
|
199
|
+
"form_factor": fields[3] if len(fields) > 3 else "",
|
|
200
|
+
"firmware": fields[5] if len(fields) > 5 else "",
|
|
201
|
+
}
|
|
202
|
+
return result
|
|
203
|
+
|
|
204
|
+
return None
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def query (
|
|
208
|
+
host: str,
|
|
209
|
+
address: str,
|
|
210
|
+
port: int = WING_PORT,
|
|
211
|
+
timeout: float = 2.0,
|
|
212
|
+
) -> typing.Optional[typing.Dict[str, typing.Any]]:
|
|
213
|
+
"""Query a node or leaf address on the WING and return a structured dict.
|
|
214
|
+
|
|
215
|
+
Sends an argument-less OSC message to *address*. The WING responds with
|
|
216
|
+
either a list of child node names (for a node address) or the current value
|
|
217
|
+
of a parameter (for a leaf address).
|
|
218
|
+
|
|
219
|
+
Parameters:
|
|
220
|
+
host: WING IP address (e.g. ``"192.168.0.116"``).
|
|
221
|
+
address: OSC address to query (e.g. ``"/ch/1"`` or ``"/ch/1/fdr"``).
|
|
222
|
+
port: UDP port (default 2223).
|
|
223
|
+
timeout: Seconds to wait for a reply.
|
|
224
|
+
|
|
225
|
+
Returns:
|
|
226
|
+
A ``dict`` with keys:
|
|
227
|
+
|
|
228
|
+
- ``address`` (str): The OSC address of the response.
|
|
229
|
+
- ``type`` (str): ``"node"`` or ``"leaf"``.
|
|
230
|
+
- For **nodes**: ``children`` (list[str]) — child node names.
|
|
231
|
+
- For **string leaves**: ``value`` (str).
|
|
232
|
+
- For **numeric leaves**: ``value`` (str), ``value_f`` (float|None),
|
|
233
|
+
``value_i`` (int|None).
|
|
234
|
+
|
|
235
|
+
Returns ``None`` on timeout.
|
|
236
|
+
|
|
237
|
+
Example::
|
|
238
|
+
|
|
239
|
+
result = wing.query(ip, "/ch/1/fdr")
|
|
240
|
+
# {'address': '/ch/1/fdr', 'type': 'leaf', 'value': '0.0',
|
|
241
|
+
# 'value_f': 0.75, 'value_i': 0}
|
|
242
|
+
|
|
243
|
+
result = wing.query(ip, "/ch/1")
|
|
244
|
+
# {'address': '/ch/1', 'type': 'node',
|
|
245
|
+
# 'children': ['in', 'flt', 'clink', 'col', 'name', 'fdr', ...]}
|
|
246
|
+
"""
|
|
247
|
+
dgram = _build_osc(address)
|
|
248
|
+
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
249
|
+
try:
|
|
250
|
+
sock.settimeout(timeout)
|
|
251
|
+
sock.sendto(dgram, (host, port))
|
|
252
|
+
try:
|
|
253
|
+
data, _ = sock.recvfrom(65535)
|
|
254
|
+
except socket.timeout:
|
|
255
|
+
return None
|
|
256
|
+
finally:
|
|
257
|
+
sock.close()
|
|
258
|
+
|
|
259
|
+
msg = _parse_osc(data)
|
|
260
|
+
if msg is None:
|
|
261
|
+
return None
|
|
262
|
+
|
|
263
|
+
params = list(msg.params)
|
|
264
|
+
kind = _classify(params)
|
|
265
|
+
|
|
266
|
+
result: typing.Dict[str, typing.Any] = {
|
|
267
|
+
"address": msg.address,
|
|
268
|
+
"type": kind,
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if kind == "node":
|
|
272
|
+
result["children"] = [str(p) for p in params]
|
|
273
|
+
else:
|
|
274
|
+
# Leaf — extract typed values
|
|
275
|
+
result["value"] = str(params[0]) if params else ""
|
|
276
|
+
floats = [p for p in params if isinstance(p, float)]
|
|
277
|
+
ints = [p for p in params if isinstance(p, int) and not isinstance(p, bool)]
|
|
278
|
+
result["value_f"] = floats[0] if floats else None
|
|
279
|
+
result["value_i"] = ints[0] if ints else None
|
|
280
|
+
|
|
281
|
+
return result
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def walk (
|
|
285
|
+
host: str,
|
|
286
|
+
address: str = "/",
|
|
287
|
+
port: int = WING_PORT,
|
|
288
|
+
timeout: float = 2.0,
|
|
289
|
+
max_depth: int = 3,
|
|
290
|
+
_depth: int = 0,
|
|
291
|
+
) -> typing.Optional[typing.Dict[str, typing.Any]]:
|
|
292
|
+
"""Recursively walk the WING's node tree from *address*.
|
|
293
|
+
|
|
294
|
+
Returns a nested dict representing the subtree. Stops at *max_depth*
|
|
295
|
+
levels to avoid issuing hundreds of queries against a large mixer tree.
|
|
296
|
+
|
|
297
|
+
Parameters:
|
|
298
|
+
host: WING IP address.
|
|
299
|
+
address: Starting node address (default ``"/"`` — the root).
|
|
300
|
+
port: UDP port (default 2223).
|
|
301
|
+
timeout: Per-query timeout.
|
|
302
|
+
max_depth: Maximum recursion depth (default 3).
|
|
303
|
+
|
|
304
|
+
Returns:
|
|
305
|
+
Nested ``dict`` — same structure as :func:`query` but with an extra
|
|
306
|
+
``"children"`` key on nodes mapping child names to their own subtrees.
|
|
307
|
+
``None`` on timeout.
|
|
308
|
+
|
|
309
|
+
Example::
|
|
310
|
+
|
|
311
|
+
tree = wing.walk(ip, "/ch/1", max_depth=1)
|
|
312
|
+
for name, child in tree["children"].items():
|
|
313
|
+
print(name, child)
|
|
314
|
+
"""
|
|
315
|
+
result = query(host, address, port=port, timeout=timeout)
|
|
316
|
+
if result is None:
|
|
317
|
+
return None
|
|
318
|
+
|
|
319
|
+
if result["type"] == "node" and _depth < max_depth:
|
|
320
|
+
children_map: typing.Dict[str, typing.Any] = {}
|
|
321
|
+
for child_name in result["children"]:
|
|
322
|
+
child_address = address.rstrip("/") + "/" + child_name
|
|
323
|
+
child_result = walk(
|
|
324
|
+
host, child_address, port=port, timeout=timeout,
|
|
325
|
+
max_depth=max_depth, _depth=_depth + 1
|
|
326
|
+
)
|
|
327
|
+
children_map[child_name] = child_result
|
|
328
|
+
result["children"] = children_map
|
|
329
|
+
|
|
330
|
+
return result
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
def print_node (
|
|
334
|
+
host: str,
|
|
335
|
+
address: str = "/",
|
|
336
|
+
port: int = WING_PORT,
|
|
337
|
+
timeout: float = 2.0,
|
|
338
|
+
) -> None:
|
|
339
|
+
"""Query *address* and print a human-readable summary.
|
|
340
|
+
|
|
341
|
+
For nodes: prints the list of children.
|
|
342
|
+
For leaves: prints the current value(s).
|
|
343
|
+
|
|
344
|
+
Parameters:
|
|
345
|
+
host: WING IP address.
|
|
346
|
+
address: OSC address to query (default ``"/"``).
|
|
347
|
+
port: UDP port (default 2223).
|
|
348
|
+
timeout: Seconds to wait for a reply.
|
|
349
|
+
|
|
350
|
+
Example::
|
|
351
|
+
|
|
352
|
+
wing.print_node("192.168.0.116", "/ch/1")
|
|
353
|
+
wing.print_node("192.168.0.116", "/ch/1/fdr")
|
|
354
|
+
"""
|
|
355
|
+
result = query(host, address, port=port, timeout=timeout)
|
|
356
|
+
|
|
357
|
+
if result is None:
|
|
358
|
+
print(f"{address} (no reply)")
|
|
359
|
+
return
|
|
360
|
+
|
|
361
|
+
if result["type"] == "node":
|
|
362
|
+
children = result["children"]
|
|
363
|
+
print(f"{address} [{len(children)} children]")
|
|
364
|
+
for child in children:
|
|
365
|
+
print(f" {child}")
|
|
366
|
+
else:
|
|
367
|
+
value_f = result.get("value_f")
|
|
368
|
+
value_i = result.get("value_i")
|
|
369
|
+
value_str = result.get("value", "")
|
|
370
|
+
|
|
371
|
+
parts: typing.List[str] = [repr(value_str)]
|
|
372
|
+
if value_f is not None:
|
|
373
|
+
parts.append(f"float={value_f}")
|
|
374
|
+
if value_i is not None:
|
|
375
|
+
parts.append(f"int={value_i}")
|
|
376
|
+
|
|
377
|
+
print(f"{address} {', '.join(parts)}")
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
# ── CLI entry point ───────────────────────────────────────────────────────────
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
def _main () -> None:
|
|
384
|
+
"""Command-line interface.
|
|
385
|
+
|
|
386
|
+
Usage::
|
|
387
|
+
|
|
388
|
+
# Auto-discover WING on the LAN
|
|
389
|
+
python -m subsequence.helpers.wing
|
|
390
|
+
|
|
391
|
+
# Query a specific address (requires --host or auto-discovery)
|
|
392
|
+
python -m subsequence.helpers.wing /ch/1
|
|
393
|
+
python -m subsequence.helpers.wing /ch/1/fdr
|
|
394
|
+
python -m subsequence.helpers.wing --host 192.168.0.116 /ch/1
|
|
395
|
+
"""
|
|
396
|
+
args = sys.argv[1:]
|
|
397
|
+
|
|
398
|
+
host: typing.Optional[str] = None
|
|
399
|
+
address: typing.Optional[str] = None
|
|
400
|
+
|
|
401
|
+
i = 0
|
|
402
|
+
while i < len(args):
|
|
403
|
+
if args[i] == "--host" and i + 1 < len(args):
|
|
404
|
+
host = args[i + 1]
|
|
405
|
+
i += 2
|
|
406
|
+
elif args[i].startswith("/"):
|
|
407
|
+
address = args[i]
|
|
408
|
+
i += 1
|
|
409
|
+
else:
|
|
410
|
+
i += 1
|
|
411
|
+
|
|
412
|
+
# Discover if no host given
|
|
413
|
+
if host is None:
|
|
414
|
+
print("Discovering WING on local network...")
|
|
415
|
+
device = discover()
|
|
416
|
+
if device is None:
|
|
417
|
+
print("No WING found.")
|
|
418
|
+
return
|
|
419
|
+
host = device["ip"]
|
|
420
|
+
print(
|
|
421
|
+
f"Found {device['device']} {device['model']} ({device['form_factor']}) "
|
|
422
|
+
f"at {host} firmware {device['firmware']}"
|
|
423
|
+
)
|
|
424
|
+
|
|
425
|
+
if address is not None:
|
|
426
|
+
print_node(host, address)
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
if __name__ == "__main__":
|
|
430
|
+
_main()
|