discstation 0.1.26 → 0.1.27

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.
@@ -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 30000
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;
@@ -792,10 +787,6 @@ void setup() {
792
787
  pinMode(BTN_SELECT_PIN, INPUT_PULLUP);
793
788
  pinMode(BTN_UP_PIN, INPUT_PULLUP);
794
789
  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
790
  pinMode(POT_PIN, INPUT);
800
791
  analogReadResolution(12);
801
792
 
@@ -812,13 +803,6 @@ void setup() {
812
803
  delay(3000);
813
804
  }
814
805
 
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
806
  drawStandby();
823
807
  lastMsgTime = millis();
824
808
  lastInputTime = millis();
@@ -1134,10 +1118,6 @@ void loop() {
1134
1118
  (long)(millis() - lastInputTime) >= IDLE_BLANK_MS) {
1135
1119
  display.ssd1306_command(0xAE);
1136
1120
  displayBlank = true;
1137
- digitalWrite(LED_POWER_PIN, LOW);
1138
- }
1139
- if (!displayBlank) {
1140
- digitalWrite(LED_POWER_PIN, HIGH);
1141
1121
  }
1142
1122
 
1143
1123
  if (uiState != UI_DISCONNECTED &&
@@ -1145,28 +1125,5 @@ void loop() {
1145
1125
  drawDisconnected();
1146
1126
  }
1147
1127
 
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
1128
  delay(20);
1172
1129
  }
@@ -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 30000 // if a live link drops, force a re-join after this
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;
@@ -757,10 +751,6 @@ void setup() {
757
751
  pinMode(BTN_SELECT_PIN, INPUT_PULLUP);
758
752
  pinMode(BTN_UP_PIN, INPUT_PULLUP);
759
753
  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
754
  pinMode(POT_PIN, INPUT);
765
755
  analogReadResolution(12);
766
756
  analogSetPinAttenuation(POT_PIN, ADC_11db);
@@ -778,14 +768,6 @@ void setup() {
778
768
  delay(3000);
779
769
  }
780
770
 
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
771
  drawStandby();
790
772
  lastMsgTime = millis();
791
773
  lastInputTime = millis();
@@ -1115,10 +1097,6 @@ void loop() {
1115
1097
  (long)(millis() - lastInputTime) >= IDLE_BLANK_MS) {
1116
1098
  display.ssd1306_command(0xAE);
1117
1099
  displayBlank = true;
1118
- digitalWrite(LED_POWER_PIN, LOW);
1119
- }
1120
- if (!displayBlank) {
1121
- digitalWrite(LED_POWER_PIN, HIGH);
1122
1100
  }
1123
1101
 
1124
1102
  // --- PING timeout (disconnected) ---
@@ -1127,29 +1105,5 @@ void loop() {
1127
1105
  drawDisconnected();
1128
1106
  }
1129
1107
 
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
1108
  delay(20);
1155
1109
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "discstation",
3
- "version": "0.1.26",
3
+ "version": "0.1.27",
4
4
  "description": "DiscStation optical-media appliance host — one cross-OS installer",
5
5
  "bin": {
6
6
  "discstation-setup": "scripts/setup.mjs",
@@ -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=5):
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():
@@ -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. Result cached ~60s so the
83
- reconnect loop / hot-swap poll don't hammer mDNS.
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
- if time.time() - _remote_cache["at"] < 60:
96
- return _remote_cache["value"]
98
+ addr = _mdns_addr()
99
+ if addr:
100
+ return addr
97
101
 
98
- value = _discover_remote()
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 _discover_remote():
105
- try:
106
- from zeroconf import Zeroconf, ServiceBrowser
107
- except ImportError:
108
- Zeroconf = None
109
- if Zeroconf is not None:
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 add_service(self, zc, type_, name):
114
- info = zc.get_service_info(type_, name, timeout=1500)
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
- update_service = add_service
140
+ add_service = _seen
141
+ update_service = _seen
122
142
 
123
- def remove_service(self, *a):
124
- pass
143
+ def remove_service(self, zc, type_, name):
144
+ found.pop("addr", None)
125
145
 
126
- zc = Zeroconf()
127
- try:
128
- ServiceBrowser(zc, _REMOTE_MDNS_TYPE, _Listener())
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
- try:
138
- return socket.gethostbyname(_REMOTE_DEFAULT_HOST)
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):