discstation 0.1.26 → 0.1.28
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.
- package/arduino/c6/DiscStation_C6.ino +11 -49
- package/arduino/v1/DiscStation.ino +11 -52
- package/package.json +1 -1
- package/src/discstation.py +1 -1
- package/src/discstation_host.py +41 -30
|
@@ -24,8 +24,6 @@
|
|
|
24
24
|
#define BTN_SELECT_PIN 21 // D3
|
|
25
25
|
#define BTN_UP_PIN 20 // D9/MISO
|
|
26
26
|
#define BTN_DOWN_PIN 18 // D10/MOSI
|
|
27
|
-
#define LED_POWER_PIN 15 // Built-in user LED
|
|
28
|
-
#define LED_ACT_PIN 16 // D6/TX
|
|
29
27
|
#define POT_PIN 0 // D0/A0 (ADC)
|
|
30
28
|
|
|
31
29
|
#define DEBOUNCE_MS 50
|
|
@@ -35,11 +33,10 @@
|
|
|
35
33
|
#define STANDBY_BLANK_MS 60000
|
|
36
34
|
#define IDLE_BLANK_MS 45000 // blank the OLED after this long with no input on HOME/STANDBY
|
|
37
35
|
#define PING_TIMEOUT_MS 30000
|
|
38
|
-
#define LED_BLINK_MS 250
|
|
39
36
|
|
|
40
37
|
#define WIFI_RESET_HOLD_MS 10000
|
|
41
38
|
#define WIFI_CONNECT_TIMEOUT_MS 18000
|
|
42
|
-
#define WIFI_RETRY_MS
|
|
39
|
+
#define WIFI_RETRY_MS 15000
|
|
43
40
|
|
|
44
41
|
#define MAX_HOME_MODES 5
|
|
45
42
|
#define BURN_COUNT 4
|
|
@@ -237,8 +234,6 @@ unsigned long downLastDebounce = 0;
|
|
|
237
234
|
int ffSpeedIndex = 0;
|
|
238
235
|
int rewSpeedIndex = 0;
|
|
239
236
|
|
|
240
|
-
bool actLedState = false;
|
|
241
|
-
unsigned long lastLedBlink = 0;
|
|
242
237
|
int homeIndex = 0;
|
|
243
238
|
int burnModeIndex = 0;
|
|
244
239
|
int burnSpeedIndex = 0;
|
|
@@ -550,8 +545,10 @@ void drawLoading() {
|
|
|
550
545
|
display.display();
|
|
551
546
|
}
|
|
552
547
|
|
|
553
|
-
|
|
554
|
-
|
|
548
|
+
// Returns true if this call actually woke a blanked screen - callers use that
|
|
549
|
+
// to swallow the wake press so it doesn't also trigger a menu action.
|
|
550
|
+
bool wakeDisplay() {
|
|
551
|
+
if (!displayBlank || !displayOk) return false;
|
|
555
552
|
display.ssd1306_command(0xAF);
|
|
556
553
|
displayBlank = false;
|
|
557
554
|
switch (uiState) {
|
|
@@ -566,6 +563,7 @@ void wakeDisplay() {
|
|
|
566
563
|
case UI_LOADING: drawLoading(); break;
|
|
567
564
|
case UI_SETUP: drawSetup(); break;
|
|
568
565
|
}
|
|
566
|
+
return true;
|
|
569
567
|
}
|
|
570
568
|
|
|
571
569
|
void drawPlay() {
|
|
@@ -660,6 +658,8 @@ void parseMessage(String msg) {
|
|
|
660
658
|
line1 = msg.substring(5);
|
|
661
659
|
line2 = "Playing disc";
|
|
662
660
|
playVolume = readPercent();
|
|
661
|
+
Out.print("POT:"); // push the knob's current level so playback starts at it
|
|
662
|
+
Out.println(playVolume);
|
|
663
663
|
drawPlay();
|
|
664
664
|
|
|
665
665
|
} else if (msg.startsWith("PLAY_MODE:")) {
|
|
@@ -792,10 +792,6 @@ void setup() {
|
|
|
792
792
|
pinMode(BTN_SELECT_PIN, INPUT_PULLUP);
|
|
793
793
|
pinMode(BTN_UP_PIN, INPUT_PULLUP);
|
|
794
794
|
pinMode(BTN_DOWN_PIN, INPUT_PULLUP);
|
|
795
|
-
pinMode(LED_POWER_PIN, OUTPUT);
|
|
796
|
-
pinMode(LED_ACT_PIN, OUTPUT);
|
|
797
|
-
digitalWrite(LED_POWER_PIN, HIGH);
|
|
798
|
-
digitalWrite(LED_ACT_PIN, LOW);
|
|
799
795
|
pinMode(POT_PIN, INPUT);
|
|
800
796
|
analogReadResolution(12);
|
|
801
797
|
|
|
@@ -812,13 +808,6 @@ void setup() {
|
|
|
812
808
|
delay(3000);
|
|
813
809
|
}
|
|
814
810
|
|
|
815
|
-
for (int i = 0; i < 3; i++) {
|
|
816
|
-
digitalWrite(LED_ACT_PIN, HIGH);
|
|
817
|
-
delay(150);
|
|
818
|
-
digitalWrite(LED_ACT_PIN, LOW);
|
|
819
|
-
delay(150);
|
|
820
|
-
}
|
|
821
|
-
|
|
822
811
|
drawStandby();
|
|
823
812
|
lastMsgTime = millis();
|
|
824
813
|
lastInputTime = millis();
|
|
@@ -826,7 +815,7 @@ void setup() {
|
|
|
826
815
|
}
|
|
827
816
|
|
|
828
817
|
void handleSelectPress(bool longPress) {
|
|
829
|
-
wakeDisplay();
|
|
818
|
+
if (wakeDisplay()) { lastInputTime = millis(); return; } // wake-only press, swallow the action
|
|
830
819
|
lastInputTime = millis();
|
|
831
820
|
if (uiState == UI_HOME) {
|
|
832
821
|
if (longPress) {
|
|
@@ -912,7 +901,7 @@ void handleSelectPress(bool longPress) {
|
|
|
912
901
|
}
|
|
913
902
|
|
|
914
903
|
void handleUp(bool longPress) {
|
|
915
|
-
wakeDisplay();
|
|
904
|
+
if (wakeDisplay()) { lastInputTime = millis(); return; } // wake-only press, swallow the action
|
|
916
905
|
lastInputTime = millis();
|
|
917
906
|
if (uiState == UI_HOME) {
|
|
918
907
|
if (homeCount > 0) {
|
|
@@ -949,7 +938,7 @@ void handleUp(bool longPress) {
|
|
|
949
938
|
}
|
|
950
939
|
|
|
951
940
|
void handleDown(bool longPress) {
|
|
952
|
-
wakeDisplay();
|
|
941
|
+
if (wakeDisplay()) { lastInputTime = millis(); return; } // wake-only press, swallow the action
|
|
953
942
|
lastInputTime = millis();
|
|
954
943
|
if (uiState == UI_HOME) {
|
|
955
944
|
if (homeCount > 0) {
|
|
@@ -1134,10 +1123,6 @@ void loop() {
|
|
|
1134
1123
|
(long)(millis() - lastInputTime) >= IDLE_BLANK_MS) {
|
|
1135
1124
|
display.ssd1306_command(0xAE);
|
|
1136
1125
|
displayBlank = true;
|
|
1137
|
-
digitalWrite(LED_POWER_PIN, LOW);
|
|
1138
|
-
}
|
|
1139
|
-
if (!displayBlank) {
|
|
1140
|
-
digitalWrite(LED_POWER_PIN, HIGH);
|
|
1141
1126
|
}
|
|
1142
1127
|
|
|
1143
1128
|
if (uiState != UI_DISCONNECTED &&
|
|
@@ -1145,28 +1130,5 @@ void loop() {
|
|
|
1145
1130
|
drawDisconnected();
|
|
1146
1131
|
}
|
|
1147
1132
|
|
|
1148
|
-
{
|
|
1149
|
-
bool ledOn = false;
|
|
1150
|
-
if (uiState == UI_BURN_READY || uiState == UI_PLAY || uiState == UI_WAITING || uiState == UI_IP) {
|
|
1151
|
-
ledOn = true;
|
|
1152
|
-
} else if (uiState == UI_STATUS) {
|
|
1153
|
-
if (progressPercent >= 0) {
|
|
1154
|
-
ledOn = (millis() - lastLedBlink > LED_BLINK_MS);
|
|
1155
|
-
if (ledOn) {
|
|
1156
|
-
lastLedBlink = millis();
|
|
1157
|
-
actLedState = !actLedState;
|
|
1158
|
-
}
|
|
1159
|
-
ledOn = actLedState;
|
|
1160
|
-
} else {
|
|
1161
|
-
ledOn = true;
|
|
1162
|
-
}
|
|
1163
|
-
} else if (uiState == UI_HOME || uiState == UI_STANDBY || uiState == UI_LOADING) {
|
|
1164
|
-
ledOn = false;
|
|
1165
|
-
} else if (uiState == UI_DISCONNECTED) {
|
|
1166
|
-
ledOn = true;
|
|
1167
|
-
}
|
|
1168
|
-
digitalWrite(LED_ACT_PIN, ledOn ? HIGH : LOW);
|
|
1169
|
-
}
|
|
1170
|
-
|
|
1171
1133
|
delay(20);
|
|
1172
1134
|
}
|
|
@@ -25,8 +25,6 @@
|
|
|
25
25
|
#define BTN_SELECT_PIN 14
|
|
26
26
|
#define BTN_UP_PIN 13
|
|
27
27
|
#define BTN_DOWN_PIN 15
|
|
28
|
-
#define LED_POWER_PIN 32
|
|
29
|
-
#define LED_ACT_PIN 33
|
|
30
28
|
#define POT_PIN 34
|
|
31
29
|
|
|
32
30
|
#define DEBOUNCE_MS 50
|
|
@@ -36,11 +34,10 @@
|
|
|
36
34
|
#define STANDBY_BLANK_MS 60000
|
|
37
35
|
#define IDLE_BLANK_MS 45000 // blank the OLED after this long with no input on HOME/STANDBY
|
|
38
36
|
#define PING_TIMEOUT_MS 30000
|
|
39
|
-
#define LED_BLINK_MS 250
|
|
40
37
|
|
|
41
38
|
#define WIFI_RESET_HOLD_MS 10000 // hold SELECT this long on HOME to wipe Wi-Fi creds
|
|
42
39
|
#define WIFI_CONNECT_TIMEOUT_MS 18000 // give a stored-creds join this long before falling to the portal
|
|
43
|
-
#define WIFI_RETRY_MS
|
|
40
|
+
#define WIFI_RETRY_MS 15000 // if a live link drops, force a re-join after this
|
|
44
41
|
|
|
45
42
|
#define MAX_HOME_MODES 5
|
|
46
43
|
#define BURN_COUNT 4
|
|
@@ -241,9 +238,6 @@ unsigned long downLastDebounce = 0;
|
|
|
241
238
|
int ffSpeedIndex = 0;
|
|
242
239
|
int rewSpeedIndex = 0;
|
|
243
240
|
|
|
244
|
-
// LED state
|
|
245
|
-
bool actLedState = false;
|
|
246
|
-
unsigned long lastLedBlink = 0;
|
|
247
241
|
int homeIndex = 0;
|
|
248
242
|
int burnModeIndex = 0;
|
|
249
243
|
int burnSpeedIndex = 0;
|
|
@@ -537,8 +531,10 @@ void drawDisconnected() {
|
|
|
537
531
|
display.display();
|
|
538
532
|
}
|
|
539
533
|
|
|
540
|
-
|
|
541
|
-
|
|
534
|
+
// Returns true if this call actually woke a blanked screen - callers use that
|
|
535
|
+
// to swallow the wake press so it doesn't also trigger a menu action.
|
|
536
|
+
bool wakeDisplay() {
|
|
537
|
+
if (!displayBlank || !displayOk) return false;
|
|
542
538
|
display.ssd1306_command(0xAF);
|
|
543
539
|
displayBlank = false;
|
|
544
540
|
switch (uiState) {
|
|
@@ -552,6 +548,7 @@ void wakeDisplay() {
|
|
|
552
548
|
case UI_DISCONNECTED: drawDisconnected(); break;
|
|
553
549
|
case UI_SETUP: drawSetup(); break;
|
|
554
550
|
}
|
|
551
|
+
return true;
|
|
555
552
|
}
|
|
556
553
|
|
|
557
554
|
void drawPlay() {
|
|
@@ -646,6 +643,8 @@ void parseMessage(String msg) {
|
|
|
646
643
|
line1 = msg.substring(5);
|
|
647
644
|
line2 = "Playing disc";
|
|
648
645
|
playVolume = readPercent();
|
|
646
|
+
Out.print("POT:"); // push the knob's current level so playback starts at it
|
|
647
|
+
Out.println(playVolume);
|
|
649
648
|
drawPlay();
|
|
650
649
|
|
|
651
650
|
} else if (msg.startsWith("PLAY_MODE:")) {
|
|
@@ -757,10 +756,6 @@ void setup() {
|
|
|
757
756
|
pinMode(BTN_SELECT_PIN, INPUT_PULLUP);
|
|
758
757
|
pinMode(BTN_UP_PIN, INPUT_PULLUP);
|
|
759
758
|
pinMode(BTN_DOWN_PIN, INPUT_PULLUP);
|
|
760
|
-
pinMode(LED_POWER_PIN, OUTPUT);
|
|
761
|
-
pinMode(LED_ACT_PIN, OUTPUT);
|
|
762
|
-
digitalWrite(LED_POWER_PIN, HIGH);
|
|
763
|
-
digitalWrite(LED_ACT_PIN, LOW);
|
|
764
759
|
pinMode(POT_PIN, INPUT);
|
|
765
760
|
analogReadResolution(12);
|
|
766
761
|
analogSetPinAttenuation(POT_PIN, ADC_11db);
|
|
@@ -778,14 +773,6 @@ void setup() {
|
|
|
778
773
|
delay(3000);
|
|
779
774
|
}
|
|
780
775
|
|
|
781
|
-
// Blink activity LED 3x to confirm it works
|
|
782
|
-
for (int i = 0; i < 3; i++) {
|
|
783
|
-
digitalWrite(LED_ACT_PIN, HIGH);
|
|
784
|
-
delay(150);
|
|
785
|
-
digitalWrite(LED_ACT_PIN, LOW);
|
|
786
|
-
delay(150);
|
|
787
|
-
}
|
|
788
|
-
|
|
789
776
|
drawStandby();
|
|
790
777
|
lastMsgTime = millis();
|
|
791
778
|
lastInputTime = millis();
|
|
@@ -793,7 +780,7 @@ void setup() {
|
|
|
793
780
|
}
|
|
794
781
|
|
|
795
782
|
void handleSelectPress(bool longPress) {
|
|
796
|
-
wakeDisplay();
|
|
783
|
+
if (wakeDisplay()) { lastInputTime = millis(); return; } // wake-only press, swallow the action
|
|
797
784
|
lastInputTime = millis();
|
|
798
785
|
if (uiState == UI_HOME) {
|
|
799
786
|
if (longPress) {
|
|
@@ -883,7 +870,7 @@ void handleSelectPress(bool longPress) {
|
|
|
883
870
|
}
|
|
884
871
|
|
|
885
872
|
void handleUp(bool longPress) {
|
|
886
|
-
wakeDisplay();
|
|
873
|
+
if (wakeDisplay()) { lastInputTime = millis(); return; } // wake-only press, swallow the action
|
|
887
874
|
lastInputTime = millis();
|
|
888
875
|
if (uiState == UI_HOME) {
|
|
889
876
|
if (homeCount > 0) {
|
|
@@ -923,7 +910,7 @@ void handleUp(bool longPress) {
|
|
|
923
910
|
}
|
|
924
911
|
|
|
925
912
|
void handleDown(bool longPress) {
|
|
926
|
-
wakeDisplay();
|
|
913
|
+
if (wakeDisplay()) { lastInputTime = millis(); return; } // wake-only press, swallow the action
|
|
927
914
|
lastInputTime = millis();
|
|
928
915
|
if (uiState == UI_HOME) {
|
|
929
916
|
if (homeCount > 0) {
|
|
@@ -1115,10 +1102,6 @@ void loop() {
|
|
|
1115
1102
|
(long)(millis() - lastInputTime) >= IDLE_BLANK_MS) {
|
|
1116
1103
|
display.ssd1306_command(0xAE);
|
|
1117
1104
|
displayBlank = true;
|
|
1118
|
-
digitalWrite(LED_POWER_PIN, LOW);
|
|
1119
|
-
}
|
|
1120
|
-
if (!displayBlank) {
|
|
1121
|
-
digitalWrite(LED_POWER_PIN, HIGH);
|
|
1122
1105
|
}
|
|
1123
1106
|
|
|
1124
1107
|
// --- PING timeout (disconnected) ---
|
|
@@ -1127,29 +1110,5 @@ void loop() {
|
|
|
1127
1110
|
drawDisconnected();
|
|
1128
1111
|
}
|
|
1129
1112
|
|
|
1130
|
-
// --- Activity LED ---
|
|
1131
|
-
{
|
|
1132
|
-
bool ledOn = false;
|
|
1133
|
-
if (uiState == UI_BURN_READY || uiState == UI_PLAY || uiState == UI_WAITING || uiState == UI_IP) {
|
|
1134
|
-
ledOn = true;
|
|
1135
|
-
} else if (uiState == UI_STATUS) {
|
|
1136
|
-
if (progressPercent >= 0) {
|
|
1137
|
-
ledOn = (millis() - lastLedBlink > LED_BLINK_MS);
|
|
1138
|
-
if (ledOn) {
|
|
1139
|
-
lastLedBlink = millis();
|
|
1140
|
-
actLedState = !actLedState;
|
|
1141
|
-
}
|
|
1142
|
-
ledOn = actLedState;
|
|
1143
|
-
} else {
|
|
1144
|
-
ledOn = true;
|
|
1145
|
-
}
|
|
1146
|
-
} else if (uiState == UI_HOME || uiState == UI_STANDBY) {
|
|
1147
|
-
ledOn = false;
|
|
1148
|
-
} else if (uiState == UI_DISCONNECTED) {
|
|
1149
|
-
ledOn = true;
|
|
1150
|
-
}
|
|
1151
|
-
digitalWrite(LED_ACT_PIN, ledOn ? HIGH : LOW);
|
|
1152
|
-
}
|
|
1153
|
-
|
|
1154
1113
|
delay(20);
|
|
1155
1114
|
}
|
package/package.json
CHANGED
package/src/discstation.py
CHANGED
|
@@ -122,7 +122,7 @@ class TcpSerial:
|
|
|
122
122
|
A dead link raises serial.SerialException from in_waiting/write, which is
|
|
123
123
|
what check_serial_alive() / main()'s reconnect loop already expect."""
|
|
124
124
|
|
|
125
|
-
def __init__(self, host, port=2323, connect_timeout=
|
|
125
|
+
def __init__(self, host, port=2323, connect_timeout=3):
|
|
126
126
|
if host.count(":") == 1 and not host.startswith("["): # "ip:port"
|
|
127
127
|
host, _, p = host.rpartition(":")
|
|
128
128
|
if p.isdigit():
|
package/src/discstation_host.py
CHANGED
|
@@ -72,6 +72,7 @@ def serial_port():
|
|
|
72
72
|
_REMOTE_MDNS_TYPE = "_discstation._tcp.local."
|
|
73
73
|
_REMOTE_DEFAULT_HOST = "discstation.local"
|
|
74
74
|
_remote_cache = {"at": 0.0, "value": None}
|
|
75
|
+
_remote_browser = {} # {"zc": Zeroconf|None, "found": {"addr": ...}} - built once, lives for the process
|
|
75
76
|
|
|
76
77
|
|
|
77
78
|
def remote_host():
|
|
@@ -79,8 +80,10 @@ def remote_host():
|
|
|
79
80
|
|
|
80
81
|
`DISC_REMOTE_HOST` unset / `auto` (default) -> mDNS browse for the
|
|
81
82
|
firmware's `_discstation._tcp` advert, falling back to resolving
|
|
82
|
-
`discstation.local` via the OS resolver.
|
|
83
|
-
|
|
83
|
+
`discstation.local` via the OS resolver. A hit is cached 60s (stable
|
|
84
|
+
address, don't hammer mDNS); a miss only ~8s, so a host that just
|
|
85
|
+
rejoined the appliance's network reconnects promptly instead of
|
|
86
|
+
waiting out a full minute.
|
|
84
87
|
`DISC_REMOTE_HOST=<ip|host>` -> that address, verbatim, instantly.
|
|
85
88
|
`DISC_REMOTE_HOST=off` / `none` / `0` -> None (never look; USB / web only).
|
|
86
89
|
|
|
@@ -92,52 +95,60 @@ def remote_host():
|
|
|
92
95
|
if setting and setting != "auto":
|
|
93
96
|
return (os.environ.get("DISC_REMOTE_HOST") or "").strip()
|
|
94
97
|
|
|
95
|
-
|
|
96
|
-
|
|
98
|
+
addr = _mdns_addr()
|
|
99
|
+
if addr:
|
|
100
|
+
return addr
|
|
97
101
|
|
|
98
|
-
|
|
102
|
+
# zeroconf absent / nothing advertised yet -> OS resolver for discstation.local.
|
|
103
|
+
# Cache it: a hit 60s, a miss ~8s (so a host that just rejoined reconnects fast).
|
|
104
|
+
ttl = 60 if _remote_cache["value"] else 8
|
|
105
|
+
if time.time() - _remote_cache["at"] < ttl:
|
|
106
|
+
return _remote_cache["value"]
|
|
107
|
+
try:
|
|
108
|
+
value = socket.gethostbyname(_REMOTE_DEFAULT_HOST)
|
|
109
|
+
except OSError:
|
|
110
|
+
value = None
|
|
99
111
|
_remote_cache["at"] = time.time()
|
|
100
112
|
_remote_cache["value"] = value
|
|
101
113
|
return value
|
|
102
114
|
|
|
103
115
|
|
|
104
|
-
def
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
116
|
+
def _mdns_addr():
|
|
117
|
+
"""Latest IPv4 advertised for the firmware's _discstation._tcp service, or None.
|
|
118
|
+
|
|
119
|
+
A single background ServiceBrowser is started on the first call and reused for
|
|
120
|
+
the whole process. (Creating a fresh Zeroconf per lookup - this runs every few
|
|
121
|
+
seconds while the link is down - leaked sockets and engine threads until the
|
|
122
|
+
process hit its fd limit and every transport died.)"""
|
|
123
|
+
if not _remote_browser:
|
|
124
|
+
try:
|
|
125
|
+
from zeroconf import Zeroconf, ServiceBrowser
|
|
126
|
+
except ImportError:
|
|
127
|
+
_remote_browser["zc"] = None
|
|
128
|
+
return None
|
|
110
129
|
found = {}
|
|
111
130
|
|
|
112
131
|
class _Listener:
|
|
113
|
-
def
|
|
114
|
-
info = zc.get_service_info(type_, name, timeout=
|
|
132
|
+
def _seen(self, zc, type_, name):
|
|
133
|
+
info = zc.get_service_info(type_, name, timeout=1000)
|
|
115
134
|
if info:
|
|
116
135
|
for addr in info.parsed_addresses():
|
|
117
136
|
if "." in addr: # IPv4
|
|
118
137
|
found["addr"] = addr
|
|
119
138
|
return
|
|
120
139
|
|
|
121
|
-
|
|
140
|
+
add_service = _seen
|
|
141
|
+
update_service = _seen
|
|
122
142
|
|
|
123
|
-
def remove_service(self,
|
|
124
|
-
|
|
143
|
+
def remove_service(self, zc, type_, name):
|
|
144
|
+
found.pop("addr", None)
|
|
125
145
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
deadline = time.time() + 2.5
|
|
130
|
-
while time.time() < deadline and "addr" not in found:
|
|
131
|
-
time.sleep(0.1)
|
|
132
|
-
finally:
|
|
133
|
-
zc.close()
|
|
134
|
-
if found.get("addr"):
|
|
135
|
-
return found["addr"]
|
|
146
|
+
_remote_browser["found"] = found
|
|
147
|
+
_remote_browser["zc"] = Zeroconf()
|
|
148
|
+
ServiceBrowser(_remote_browser["zc"], _REMOTE_MDNS_TYPE, _Listener())
|
|
136
149
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
except OSError:
|
|
140
|
-
return None
|
|
150
|
+
found = _remote_browser.get("found")
|
|
151
|
+
return found.get("addr") if found else None
|
|
141
152
|
|
|
142
153
|
|
|
143
154
|
def _stable_serial_path(device):
|