@agentdeck/bridge 1.0.3 → 1.0.5
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/assets/fm-helper/agentdeck-fm-helper +0 -0
- package/dist/adapters/claude-code.d.ts.map +1 -1
- package/dist/adapters/claude-code.js +3 -0
- package/dist/adapters/claude-code.js.map +1 -1
- package/dist/adb-reverse.d.ts.map +1 -1
- package/dist/adb-reverse.js +4 -2
- package/dist/adb-reverse.js.map +1 -1
- package/dist/apme/collector.js +2 -2
- package/dist/apme/collector.js.map +1 -1
- package/dist/apme/outcome.js +2 -2
- package/dist/apme/outcome.js.map +1 -1
- package/dist/apme/runner.js +3 -3
- package/dist/apme/runner.js.map +1 -1
- package/dist/ble-sync-spawn.js +1 -1
- package/dist/ble-sync-spawn.js.map +1 -1
- package/dist/calendar.d.ts +82 -0
- package/dist/calendar.d.ts.map +1 -0
- package/dist/calendar.js +324 -0
- package/dist/calendar.js.map +1 -0
- package/dist/card-feed.d.ts +63 -5
- package/dist/card-feed.d.ts.map +1 -1
- package/dist/card-feed.js +218 -14
- package/dist/card-feed.js.map +1 -1
- package/dist/card-modules.d.ts +4 -0
- package/dist/card-modules.d.ts.map +1 -1
- package/dist/card-modules.js +5 -3
- package/dist/card-modules.js.map +1 -1
- package/dist/check-deps.js +4 -4
- package/dist/check-deps.js.map +1 -1
- package/dist/cli.d.ts +35 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +406 -76
- package/dist/cli.js.map +1 -1
- package/dist/codex-otel.d.ts +89 -0
- package/dist/codex-otel.d.ts.map +1 -0
- package/dist/codex-otel.js +487 -0
- package/dist/codex-otel.js.map +1 -0
- package/dist/codex-rate-limits.d.ts.map +1 -1
- package/dist/codex-rate-limits.js +15 -3
- package/dist/codex-rate-limits.js.map +1 -1
- package/dist/daemon-server.d.ts +4 -0
- package/dist/daemon-server.d.ts.map +1 -1
- package/dist/daemon-server.js +797 -54
- package/dist/daemon-server.js.map +1 -1
- package/dist/daemon-ws-client.d.ts +108 -24
- package/dist/daemon-ws-client.d.ts.map +1 -1
- package/dist/daemon-ws-client.js +175 -39
- package/dist/daemon-ws-client.js.map +1 -1
- package/dist/daemon.js +1 -1
- package/dist/device-voice-reply.d.ts.map +1 -1
- package/dist/device-voice-reply.js +15 -3
- package/dist/device-voice-reply.js.map +1 -1
- package/dist/device-voice.d.ts +37 -0
- package/dist/device-voice.d.ts.map +1 -1
- package/dist/device-voice.js +130 -1
- package/dist/device-voice.js.map +1 -1
- package/dist/diag-analyzer.d.ts.map +1 -1
- package/dist/diag-analyzer.js +1 -0
- package/dist/diag-analyzer.js.map +1 -1
- package/dist/display-monitor.d.ts.map +1 -1
- package/dist/display-monitor.js +1 -0
- package/dist/display-monitor.js.map +1 -1
- package/dist/esp32-serial.d.ts +4 -0
- package/dist/esp32-serial.d.ts.map +1 -1
- package/dist/esp32-serial.js +6 -3
- package/dist/esp32-serial.js.map +1 -1
- package/dist/foundation-models-helper.d.ts +23 -0
- package/dist/foundation-models-helper.d.ts.map +1 -1
- package/dist/foundation-models-helper.js +32 -0
- package/dist/foundation-models-helper.js.map +1 -1
- package/dist/gateway-probe.js +1 -1
- package/dist/gateway-probe.js.map +1 -1
- package/dist/glance-frame.d.ts +64 -0
- package/dist/glance-frame.d.ts.map +1 -0
- package/dist/glance-frame.js +364 -0
- package/dist/glance-frame.js.map +1 -0
- package/dist/hook-codex-sessions.d.ts +40 -0
- package/dist/hook-codex-sessions.d.ts.map +1 -0
- package/dist/hook-codex-sessions.js +174 -0
- package/dist/hook-codex-sessions.js.map +1 -0
- package/dist/idotmatrix/idotmatrix-daemon-sync.d.ts.map +1 -1
- package/dist/idotmatrix/idotmatrix-daemon-sync.js +5 -18
- package/dist/idotmatrix/idotmatrix-daemon-sync.js.map +1 -1
- package/dist/idotmatrix/idotmatrix-discover.d.ts.map +1 -1
- package/dist/idotmatrix/idotmatrix-discover.js +12 -17
- package/dist/idotmatrix/idotmatrix-discover.js.map +1 -1
- package/dist/idotmatrix/idotmatrix-settings.d.ts +15 -0
- package/dist/idotmatrix/idotmatrix-settings.d.ts.map +1 -1
- package/dist/idotmatrix/idotmatrix-settings.js +21 -0
- package/dist/idotmatrix/idotmatrix-settings.js.map +1 -1
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +88 -7
- package/dist/index.js.map +1 -1
- package/dist/linux-service.d.ts +52 -0
- package/dist/linux-service.d.ts.map +1 -0
- package/dist/linux-service.js +206 -0
- package/dist/linux-service.js.map +1 -0
- package/dist/logger.d.ts +6 -0
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +15 -0
- package/dist/logger.js.map +1 -1
- package/dist/mdns-discover.d.ts +19 -0
- package/dist/mdns-discover.d.ts.map +1 -0
- package/dist/mdns-discover.js +109 -0
- package/dist/mdns-discover.js.map +1 -0
- package/dist/model-catalog.d.ts.map +1 -1
- package/dist/model-catalog.js +1 -0
- package/dist/model-catalog.js.map +1 -1
- package/dist/modules/adb-module.js +1 -1
- package/dist/modules/adb-module.js.map +1 -1
- package/dist/output-parser.d.ts +9 -0
- package/dist/output-parser.d.ts.map +1 -1
- package/dist/output-parser.js +67 -16
- package/dist/output-parser.js.map +1 -1
- package/dist/passive-observer.d.ts +57 -8
- package/dist/passive-observer.d.ts.map +1 -1
- package/dist/passive-observer.js +210 -74
- package/dist/passive-observer.js.map +1 -1
- package/dist/pixoo/pixoo-bridge.d.ts.map +1 -1
- package/dist/pixoo/pixoo-bridge.js +47 -6
- package/dist/pixoo/pixoo-bridge.js.map +1 -1
- package/dist/pixoo/pixoo-renderer.d.ts +2 -1
- package/dist/pixoo/pixoo-renderer.d.ts.map +1 -1
- package/dist/pixoo/pixoo-renderer.js +137 -8
- package/dist/pixoo/pixoo-renderer.js.map +1 -1
- package/dist/pocket-autonomy.d.ts +78 -0
- package/dist/pocket-autonomy.d.ts.map +1 -0
- package/dist/pocket-autonomy.js +529 -0
- package/dist/pocket-autonomy.js.map +1 -0
- package/dist/pty-manager.d.ts.map +1 -1
- package/dist/pty-manager.js +3 -2
- package/dist/pty-manager.js.map +1 -1
- package/dist/python-ble-runtime.d.ts +64 -0
- package/dist/python-ble-runtime.d.ts.map +1 -0
- package/dist/python-ble-runtime.js +216 -0
- package/dist/python-ble-runtime.js.map +1 -0
- package/dist/remote-sessions.d.ts +64 -0
- package/dist/remote-sessions.d.ts.map +1 -0
- package/dist/remote-sessions.js +81 -0
- package/dist/remote-sessions.js.map +1 -0
- package/dist/review-runner.js +2 -2
- package/dist/review-runner.js.map +1 -1
- package/dist/session-aggregator.d.ts +2 -0
- package/dist/session-aggregator.d.ts.map +1 -1
- package/dist/session-aggregator.js +6 -1
- package/dist/session-aggregator.js.map +1 -1
- package/dist/session-focus-relay.d.ts +47 -1
- package/dist/session-focus-relay.d.ts.map +1 -1
- package/dist/session-focus-relay.js +130 -15
- package/dist/session-focus-relay.js.map +1 -1
- package/dist/session-push-channel.d.ts +31 -0
- package/dist/session-push-channel.d.ts.map +1 -0
- package/dist/session-push-channel.js +94 -0
- package/dist/session-push-channel.js.map +1 -0
- package/dist/session-registry.d.ts +126 -0
- package/dist/session-registry.d.ts.map +1 -1
- package/dist/session-registry.js +180 -3
- package/dist/session-registry.js.map +1 -1
- package/dist/subagent-timeline.d.ts +34 -0
- package/dist/subagent-timeline.d.ts.map +1 -0
- package/dist/subagent-timeline.js +156 -0
- package/dist/subagent-timeline.js.map +1 -0
- package/dist/timebox/timebox-daemon-sync.d.ts.map +1 -1
- package/dist/timebox/timebox-daemon-sync.js +5 -20
- package/dist/timebox/timebox-daemon-sync.js.map +1 -1
- package/dist/timebox/timebox-discover.d.ts.map +1 -1
- package/dist/timebox/timebox-discover.js +7 -16
- package/dist/timebox/timebox-discover.js.map +1 -1
- package/dist/tui/gauge.d.ts +4 -2
- package/dist/tui/gauge.d.ts.map +1 -1
- package/dist/tui/gauge.js +8 -4
- package/dist/tui/gauge.js.map +1 -1
- package/dist/tui/renderer.d.ts.map +1 -1
- package/dist/tui/renderer.js +28 -0
- package/dist/tui/renderer.js.map +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/usage-api.d.ts +43 -0
- package/dist/usage-api.d.ts.map +1 -1
- package/dist/usage-api.js +114 -9
- package/dist/usage-api.js.map +1 -1
- package/dist/usage-event.d.ts +19 -0
- package/dist/usage-event.d.ts.map +1 -1
- package/dist/usage-event.js +35 -0
- package/dist/usage-event.js.map +1 -1
- package/dist/utils/project-name.d.ts.map +1 -1
- package/dist/utils/project-name.js +1 -0
- package/dist/utils/project-name.js.map +1 -1
- package/dist/version-check.d.ts.map +1 -1
- package/dist/version-check.js +3 -2
- package/dist/version-check.js.map +1 -1
- package/dist/voice-assistant.d.ts.map +1 -1
- package/dist/voice-assistant.js +2 -1
- package/dist/voice-assistant.js.map +1 -1
- package/dist/voice.d.ts.map +1 -1
- package/dist/voice.js +1 -0
- package/dist/voice.js.map +1 -1
- package/dist/weather.d.ts +72 -0
- package/dist/weather.d.ts.map +1 -0
- package/dist/weather.js +206 -0
- package/dist/weather.js.map +1 -0
- package/dist/windows-service.js +5 -5
- package/dist/windows-service.js.map +1 -1
- package/fm-helper/AgentDeckFMHelper.swift +200 -0
- package/package.json +20 -14
- package/python/requirements-ble.txt +5 -0
- package/src/idotmatrix/brightness.py +40 -0
- package/src/idotmatrix/identity_generated.py +50 -0
- package/src/idotmatrix/scan.py +55 -0
- package/src/idotmatrix/sync.py +400 -0
- package/src/pysync/matrix_sync_common.py +89 -0
- package/src/timebox/scan_ble.py +52 -0
- package/src/timebox/sync_ble.py +394 -0
package/dist/weather.js
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Glance weather provider (Open-Meteo) — daemon-side module for the card-feed
|
|
3
|
+
* sleep dashboard. The device never fetches weather itself; the daemon fetches,
|
|
4
|
+
* caches, and pre-renders it into `CardFeedResponse.glance.weather`
|
|
5
|
+
* (shared/src/protocol.ts § Glance).
|
|
6
|
+
*
|
|
7
|
+
* Open-Meteo is key-less and free for non-commercial local use; one bounded
|
|
8
|
+
* request per cache window. Config comes from settings.json:
|
|
9
|
+
*
|
|
10
|
+
* "weather": { "lat": 37.57, "lon": 126.98, "place": "Seoul" }
|
|
11
|
+
*
|
|
12
|
+
* No config → no weather in the glance (never a guess, never IP geolocation).
|
|
13
|
+
*/
|
|
14
|
+
import { GLANCE_RAIN_PROBABILITY_MIN } from '@agentdeck/shared';
|
|
15
|
+
/** Serve from cache inside this window — weather cadence is slower than the
|
|
16
|
+
* fastest pull cadence (900s), so a fresh fetch per pull would be waste. */
|
|
17
|
+
export const WEATHER_CACHE_MS = 30 * 60 * 1000;
|
|
18
|
+
/** After a fetch failure, keep serving the last good report up to this age —
|
|
19
|
+
* a flaky WAN must degrade to slightly-old weather, not a blank panel. */
|
|
20
|
+
export const WEATHER_STALE_SERVE_MS = 3 * 60 * 60 * 1000;
|
|
21
|
+
/** External peer await — timeout is first-line, not optional. */
|
|
22
|
+
export const WEATHER_FETCH_TIMEOUT_MS = 5000;
|
|
23
|
+
export function parseWeatherSettings(settings) {
|
|
24
|
+
const w = settings?.weather;
|
|
25
|
+
if (!w || typeof w !== 'object')
|
|
26
|
+
return null;
|
|
27
|
+
const lat = Number(w.lat);
|
|
28
|
+
const lon = Number(w.lon);
|
|
29
|
+
if (!Number.isFinite(lat) || !Number.isFinite(lon))
|
|
30
|
+
return null;
|
|
31
|
+
if (Math.abs(lat) > 90 || Math.abs(lon) > 180)
|
|
32
|
+
return null;
|
|
33
|
+
return {
|
|
34
|
+
lat,
|
|
35
|
+
lon,
|
|
36
|
+
...(typeof w.place === 'string' && w.place ? { place: w.place } : {}),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/** WMO weather interpretation code → one short summary word. Kept ASCII so
|
|
40
|
+
* every panel font can draw it; codes ride alongside for icon-capable
|
|
41
|
+
* clients. */
|
|
42
|
+
export function wmoSummary(code) {
|
|
43
|
+
if (code === undefined || !Number.isFinite(code))
|
|
44
|
+
return '';
|
|
45
|
+
if (code === 0)
|
|
46
|
+
return 'Clear';
|
|
47
|
+
if (code <= 2)
|
|
48
|
+
return 'Fair';
|
|
49
|
+
if (code === 3)
|
|
50
|
+
return 'Cloudy';
|
|
51
|
+
if (code === 45 || code === 48)
|
|
52
|
+
return 'Fog';
|
|
53
|
+
if (code >= 51 && code <= 57)
|
|
54
|
+
return 'Drizzle';
|
|
55
|
+
if (code >= 61 && code <= 67)
|
|
56
|
+
return 'Rain';
|
|
57
|
+
if (code >= 71 && code <= 77)
|
|
58
|
+
return 'Snow';
|
|
59
|
+
if (code >= 80 && code <= 82)
|
|
60
|
+
return 'Showers';
|
|
61
|
+
if (code === 85 || code === 86)
|
|
62
|
+
return 'Snow';
|
|
63
|
+
if (code >= 95)
|
|
64
|
+
return 'Storm';
|
|
65
|
+
return 'Cloudy';
|
|
66
|
+
}
|
|
67
|
+
export function buildForecastUrl(cfg) {
|
|
68
|
+
const p = new URLSearchParams({
|
|
69
|
+
latitude: String(cfg.lat),
|
|
70
|
+
longitude: String(cfg.lon),
|
|
71
|
+
current: 'temperature_2m,weather_code',
|
|
72
|
+
hourly: 'precipitation_probability',
|
|
73
|
+
daily: 'weather_code,temperature_2m_min,temperature_2m_max,precipitation_probability_max',
|
|
74
|
+
timezone: 'auto',
|
|
75
|
+
forecast_days: '2',
|
|
76
|
+
});
|
|
77
|
+
return `https://api.open-meteo.com/v1/forecast?${p.toString()}`;
|
|
78
|
+
}
|
|
79
|
+
const hmOf = (iso) => iso.slice(11, 16);
|
|
80
|
+
const dayOf = (iso) => iso.slice(0, 10);
|
|
81
|
+
/** First remaining rain window today: contiguous hours ≥ the probability
|
|
82
|
+
* floor, starting from the current hour. */
|
|
83
|
+
export function findTodayRainWindow(hourly, now) {
|
|
84
|
+
const times = hourly?.time ?? [];
|
|
85
|
+
const probs = hourly?.precipitation_probability ?? [];
|
|
86
|
+
const today = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`;
|
|
87
|
+
const nowHm = `${String(now.getHours()).padStart(2, '0')}:00`;
|
|
88
|
+
let start;
|
|
89
|
+
let end;
|
|
90
|
+
let peak = 0;
|
|
91
|
+
for (let i = 0; i < times.length && i < probs.length; i++) {
|
|
92
|
+
const t = times[i];
|
|
93
|
+
if (dayOf(t) !== today) {
|
|
94
|
+
if (start)
|
|
95
|
+
break; // window ran to midnight
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
const hm = hmOf(t);
|
|
99
|
+
if (hm < nowHm)
|
|
100
|
+
continue;
|
|
101
|
+
const p = probs[i];
|
|
102
|
+
if (typeof p === 'number' && p >= GLANCE_RAIN_PROBABILITY_MIN) {
|
|
103
|
+
if (!start)
|
|
104
|
+
start = hm;
|
|
105
|
+
end = hm;
|
|
106
|
+
if (p > peak)
|
|
107
|
+
peak = p;
|
|
108
|
+
}
|
|
109
|
+
else if (start) {
|
|
110
|
+
break; // window closed
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (!start)
|
|
114
|
+
return undefined;
|
|
115
|
+
const win = { startHm: start, probability: Math.round(peak) };
|
|
116
|
+
if (end && end !== start)
|
|
117
|
+
win.endHm = end;
|
|
118
|
+
return win;
|
|
119
|
+
}
|
|
120
|
+
export function toGlanceWeather(raw, cfg, now) {
|
|
121
|
+
const out = {};
|
|
122
|
+
if (cfg.place)
|
|
123
|
+
out.place = cfg.place;
|
|
124
|
+
const cur = raw.current;
|
|
125
|
+
if (cur && typeof cur.temperature_2m === 'number')
|
|
126
|
+
out.tempC = Math.round(cur.temperature_2m);
|
|
127
|
+
if (cur && typeof cur.weather_code === 'number') {
|
|
128
|
+
out.code = cur.weather_code;
|
|
129
|
+
out.summary = wmoSummary(cur.weather_code);
|
|
130
|
+
}
|
|
131
|
+
const d = raw.daily;
|
|
132
|
+
if (d?.time?.length) {
|
|
133
|
+
const todayMin = d.temperature_2m_min?.[0];
|
|
134
|
+
const todayMax = d.temperature_2m_max?.[0];
|
|
135
|
+
if (typeof todayMin === 'number')
|
|
136
|
+
out.todayMinC = Math.round(todayMin);
|
|
137
|
+
if (typeof todayMax === 'number')
|
|
138
|
+
out.todayMaxC = Math.round(todayMax);
|
|
139
|
+
if (d.time.length > 1) {
|
|
140
|
+
const t = { summary: wmoSummary(d.weather_code?.[1]) };
|
|
141
|
+
if (typeof d.weather_code?.[1] === 'number')
|
|
142
|
+
t.code = d.weather_code[1];
|
|
143
|
+
if (typeof d.temperature_2m_min?.[1] === 'number')
|
|
144
|
+
t.minC = Math.round(d.temperature_2m_min[1]);
|
|
145
|
+
if (typeof d.temperature_2m_max?.[1] === 'number')
|
|
146
|
+
t.maxC = Math.round(d.temperature_2m_max[1]);
|
|
147
|
+
if (typeof d.precipitation_probability_max?.[1] === 'number') {
|
|
148
|
+
t.rainProbability = Math.round(d.precipitation_probability_max[1]);
|
|
149
|
+
}
|
|
150
|
+
out.tomorrow = t;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
const rain = findTodayRainWindow(raw.hourly, now);
|
|
154
|
+
if (rain)
|
|
155
|
+
out.rain = rain;
|
|
156
|
+
return out;
|
|
157
|
+
}
|
|
158
|
+
export class WeatherProvider {
|
|
159
|
+
fetchImpl;
|
|
160
|
+
log;
|
|
161
|
+
cache = null;
|
|
162
|
+
inflight = null;
|
|
163
|
+
constructor(fetchImpl = fetch, log = () => { }) {
|
|
164
|
+
this.fetchImpl = fetchImpl;
|
|
165
|
+
this.log = log;
|
|
166
|
+
}
|
|
167
|
+
/** Current weather for `cfg`, from cache when fresh. Resolves `undefined`
|
|
168
|
+
* when unconfigured or when no report (fresh or stale-servable) exists —
|
|
169
|
+
* the glance simply omits weather. Never throws. */
|
|
170
|
+
async get(cfg, now = Date.now()) {
|
|
171
|
+
if (!cfg)
|
|
172
|
+
return undefined;
|
|
173
|
+
const key = `${cfg.lat},${cfg.lon}`;
|
|
174
|
+
const cached = this.cache;
|
|
175
|
+
if (cached && cached.key === key && now - cached.at < WEATHER_CACHE_MS)
|
|
176
|
+
return cached.data;
|
|
177
|
+
if (this.inflight)
|
|
178
|
+
return this.inflight;
|
|
179
|
+
this.inflight = this.fetchFresh(cfg, key, now).finally(() => { this.inflight = null; });
|
|
180
|
+
return this.inflight;
|
|
181
|
+
}
|
|
182
|
+
async fetchFresh(cfg, key, now) {
|
|
183
|
+
const ctl = new AbortController();
|
|
184
|
+
const timer = setTimeout(() => ctl.abort(), WEATHER_FETCH_TIMEOUT_MS);
|
|
185
|
+
try {
|
|
186
|
+
const res = await this.fetchImpl(buildForecastUrl(cfg), { signal: ctl.signal });
|
|
187
|
+
if (!res.ok)
|
|
188
|
+
throw new Error(`open-meteo HTTP ${res.status}`);
|
|
189
|
+
const raw = (await res.json());
|
|
190
|
+
const data = toGlanceWeather(raw, cfg, new Date(now));
|
|
191
|
+
this.cache = { key, at: now, data };
|
|
192
|
+
return data;
|
|
193
|
+
}
|
|
194
|
+
catch (err) {
|
|
195
|
+
this.log(`[weather] fetch failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
196
|
+
const cached = this.cache;
|
|
197
|
+
if (cached && cached.key === key && now - cached.at < WEATHER_STALE_SERVE_MS)
|
|
198
|
+
return cached.data;
|
|
199
|
+
return undefined;
|
|
200
|
+
}
|
|
201
|
+
finally {
|
|
202
|
+
clearTimeout(timer);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
//# sourceMappingURL=weather.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"weather.js","sourceRoot":"","sources":["../src/weather.ts"],"names":[],"mappings":"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"}
|
package/dist/windows-service.js
CHANGED
|
@@ -111,7 +111,7 @@ export function installWindowsTask() {
|
|
|
111
111
|
writeFileSync(tmpFile, '' + xml, 'utf16le');
|
|
112
112
|
try {
|
|
113
113
|
// /F overwrites an existing task (idempotent; mirrors macOS unload-before-load).
|
|
114
|
-
execSync(`schtasks /Create /TN "${TASK_NAME}" /XML "${tmpFile}" /F`, { stdio: 'pipe' });
|
|
114
|
+
execSync(`schtasks /Create /TN "${TASK_NAME}" /XML "${tmpFile}" /F`, { stdio: 'pipe', windowsHide: true });
|
|
115
115
|
}
|
|
116
116
|
finally {
|
|
117
117
|
try {
|
|
@@ -123,7 +123,7 @@ export function installWindowsTask() {
|
|
|
123
123
|
/** True if the AgentDeckDaemon scheduled task is registered. */
|
|
124
124
|
export function taskExists() {
|
|
125
125
|
try {
|
|
126
|
-
execSync(`schtasks /Query /TN "${TASK_NAME}"`, { stdio: 'pipe' });
|
|
126
|
+
execSync(`schtasks /Query /TN "${TASK_NAME}"`, { stdio: 'pipe', windowsHide: true });
|
|
127
127
|
return true;
|
|
128
128
|
}
|
|
129
129
|
catch {
|
|
@@ -132,14 +132,14 @@ export function taskExists() {
|
|
|
132
132
|
}
|
|
133
133
|
/** Start the registered task immediately (so no logout is required). */
|
|
134
134
|
export function runWindowsTask() {
|
|
135
|
-
execSync(`schtasks /Run /TN "${TASK_NAME}"`, { stdio: 'pipe' });
|
|
135
|
+
execSync(`schtasks /Run /TN "${TASK_NAME}"`, { stdio: 'pipe', windowsHide: true });
|
|
136
136
|
}
|
|
137
137
|
/** Stop a running task instance (best-effort; ignores "not running"). */
|
|
138
138
|
export function endWindowsTask() {
|
|
139
|
-
execSync(`schtasks /End /TN "${TASK_NAME}"`, { stdio: 'pipe' });
|
|
139
|
+
execSync(`schtasks /End /TN "${TASK_NAME}"`, { stdio: 'pipe', windowsHide: true });
|
|
140
140
|
}
|
|
141
141
|
/** Delete the registered task (/F suppresses the confirm prompt). */
|
|
142
142
|
export function deleteWindowsTask() {
|
|
143
|
-
execSync(`schtasks /Delete /TN "${TASK_NAME}" /F`, { stdio: 'pipe' });
|
|
143
|
+
execSync(`schtasks /Delete /TN "${TASK_NAME}" /F`, { stdio: 'pipe', windowsHide: true });
|
|
144
144
|
}
|
|
145
145
|
//# sourceMappingURL=windows-service.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"windows-service.js","sourceRoot":"","sources":["../src/windows-service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAC/C,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,IAAI,CAAC;AAC/C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AAEpC,MAAM,CAAC,MAAM,SAAS,GAAG,iBAAiB,CAAC;AAE3C,MAAM,UAAU,SAAS,CAAC,CAAS;IACjC,OAAO,CAAC;SACL,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AAC7B,CAAC;AAED,+EAA+E;AAC/E,+EAA+E;AAC/E,8EAA8E;AAC9E,yDAAyD;AACzD,MAAM,UAAU,mBAAmB;IACjC,MAAM,OAAO,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACxD,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,QAAQ,EAAE,KAAK,EAAE,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,EAAE,CAAC;AACpE,CAAC;AAED,MAAM,UAAU,kBAAkB;IAChC,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC;IACtC,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,QAAQ,EAAE,CAAC,QAAQ,CAAC;IACzD,OAAO,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AAC9C,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,IAAuD;IAC3F,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,mBAAmB,EAAE,EAAE,GAAG,IAAI,EAAE,CAAC;IAC9D,MAAM,IAAI,GAAG,IAAI,EAAE,IAAI,IAAI,kBAAkB,EAAE,CAAC;IAChD,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,YAAY,CAAC,CAAC;IACjD,MAAM,OAAO,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IAChC,6EAA6E;IAC7E,6EAA6E;IAC7E,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,KAAK,6BAA6B,CAAC,CAAC;IAC/D,MAAM,OAAO,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IAChC,+EAA+E;IAC/E,2EAA2E;IAC3E,0DAA0D;IAC1D,OAAO;;;;aAII,SAAS;;;;;gBAKN,OAAO;;;;;gBAKP,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA6BN,OAAO;mBACL,IAAI;0BACG,SAAS,CAAC,UAAU,CAAC;;;QAGvC,CAAC;AACT,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,kBAAkB;IAChC,MAAM,GAAG,GAAG,qBAAqB,EAAE,CAAC;IACpC,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,EAAE,EAAE,kBAAkB,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IAClF,yEAAyE;IACzE,aAAa,CAAC,OAAO,EAAE,GAAG,GAAG,GAAG,EAAE,SAAS,CAAC,CAAC;IAC7C,IAAI,CAAC;QACH,iFAAiF;QACjF,QAAQ,CAAC,yBAAyB,SAAS,WAAW,OAAO,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"windows-service.js","sourceRoot":"","sources":["../src/windows-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -19,6 +19,8 @@ struct HelperRequest: Decodable {
|
|
|
19
19
|
let text: String?
|
|
20
20
|
let voice: String?
|
|
21
21
|
let rate: Double?
|
|
22
|
+
/// `record`: hard cap on capture length; the reply arrives at stop or here.
|
|
23
|
+
let maxMs: Int?
|
|
22
24
|
}
|
|
23
25
|
|
|
24
26
|
@main
|
|
@@ -60,6 +62,20 @@ struct AgentDeckFMHelper {
|
|
|
60
62
|
return
|
|
61
63
|
}
|
|
62
64
|
|
|
65
|
+
if request.type == "record" {
|
|
66
|
+
// Recording spans an unknown PTT hold, and the stop arrives as a
|
|
67
|
+
// later stdin line — so this must not block the read loop.
|
|
68
|
+
Task.detached { await handleRecord(request) }
|
|
69
|
+
return
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if request.type == "record_stop" || request.type == "record_cancel" {
|
|
73
|
+
let stopped = activeRecording.finish(
|
|
74
|
+
cancelled: request.type == "record_cancel", reason: "stopped")
|
|
75
|
+
write(["id": request.id, "stopped": stopped])
|
|
76
|
+
return
|
|
77
|
+
}
|
|
78
|
+
|
|
63
79
|
guard let prompt = request.prompt, !prompt.isEmpty else {
|
|
64
80
|
write(["id": request.id, "error": "bad_request", "reason": "missing prompt"])
|
|
65
81
|
return
|
|
@@ -165,6 +181,111 @@ struct AgentDeckFMHelper {
|
|
|
165
181
|
}
|
|
166
182
|
}
|
|
167
183
|
|
|
184
|
+
// MARK: - Microphone capture
|
|
185
|
+
|
|
186
|
+
private static let activeRecording = RecordingBox()
|
|
187
|
+
|
|
188
|
+
/// Capture the host microphone into a 16 kHz mono PCM16 WAV. This is the
|
|
189
|
+
/// piece that retired the old Stream Deck Voice dial: capture used to mean
|
|
190
|
+
/// borrowing iTerm2's mic grant via AppleScript plus Homebrew `sox`. Here
|
|
191
|
+
/// the daemon's own bundled helper records, so the TCC grant belongs to
|
|
192
|
+
/// this process tree and there is nothing to install.
|
|
193
|
+
///
|
|
194
|
+
/// The reply is deferred: it is written when `record_stop`/`record_cancel`
|
|
195
|
+
/// arrives on stdin or `maxMs` elapses, whichever comes first.
|
|
196
|
+
private static func handleRecord(_ request: HelperRequest) async {
|
|
197
|
+
guard let outPath = request.wav, !outPath.isEmpty else {
|
|
198
|
+
write(["id": request.id, "error": "bad_request", "reason": "missing wav output path"])
|
|
199
|
+
return
|
|
200
|
+
}
|
|
201
|
+
let granted = await ensureMicAuthorization()
|
|
202
|
+
guard granted else {
|
|
203
|
+
write([
|
|
204
|
+
"id": request.id,
|
|
205
|
+
"error": "unauthorized",
|
|
206
|
+
"reason": "microphone access not granted — grant it in System Settings › Privacy & Security › Microphone",
|
|
207
|
+
])
|
|
208
|
+
return
|
|
209
|
+
}
|
|
210
|
+
let sampleRate = 16000.0
|
|
211
|
+
guard let outFormat = AVAudioFormat(commonFormat: .pcmFormatInt16,
|
|
212
|
+
sampleRate: sampleRate,
|
|
213
|
+
channels: 1,
|
|
214
|
+
interleaved: true) else {
|
|
215
|
+
write(["id": request.id, "error": "record_failed", "reason": "output format unavailable"])
|
|
216
|
+
return
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
let engine = AVAudioEngine()
|
|
220
|
+
let input = engine.inputNode
|
|
221
|
+
let inFormat = input.outputFormat(forBus: 0)
|
|
222
|
+
guard inFormat.sampleRate > 0, inFormat.channelCount > 0 else {
|
|
223
|
+
write(["id": request.id, "error": "record_failed", "reason": "no audio input device"])
|
|
224
|
+
return
|
|
225
|
+
}
|
|
226
|
+
let sink = PCMSink()
|
|
227
|
+
guard activeRecording.begin(requestId: request.id, engine: engine, sink: sink) else {
|
|
228
|
+
write(["id": request.id, "error": "busy", "reason": "a recording is already in progress"])
|
|
229
|
+
return
|
|
230
|
+
}
|
|
231
|
+
input.installTap(onBus: 0, bufferSize: 4096, format: inFormat) { buffer, _ in
|
|
232
|
+
sink.append(buffer, to: outFormat)
|
|
233
|
+
}
|
|
234
|
+
do {
|
|
235
|
+
try engine.start()
|
|
236
|
+
} catch {
|
|
237
|
+
activeRecording.clear()
|
|
238
|
+
input.removeTap(onBus: 0)
|
|
239
|
+
write(["id": request.id, "error": "record_failed", "reason": String(describing: error)])
|
|
240
|
+
return
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
let maxMs = max(1000, min(request.maxMs ?? 30_000, 120_000))
|
|
244
|
+
let outcome = await activeRecording.wait(maxMs: maxMs)
|
|
245
|
+
engine.stop()
|
|
246
|
+
input.removeTap(onBus: 0)
|
|
247
|
+
|
|
248
|
+
if outcome.cancelled {
|
|
249
|
+
write(["id": request.id, "cancelled": true])
|
|
250
|
+
return
|
|
251
|
+
}
|
|
252
|
+
if let failure = sink.failure {
|
|
253
|
+
write(["id": request.id, "error": "record_failed", "reason": failure])
|
|
254
|
+
return
|
|
255
|
+
}
|
|
256
|
+
let pcm = sink.pcm
|
|
257
|
+
guard pcm.count >= 2 else {
|
|
258
|
+
write(["id": request.id, "error": "record_failed", "reason": "no audio captured"])
|
|
259
|
+
return
|
|
260
|
+
}
|
|
261
|
+
do {
|
|
262
|
+
try wavData(pcm: pcm, sampleRate: Int(sampleRate)).write(to: URL(fileURLWithPath: outPath))
|
|
263
|
+
} catch {
|
|
264
|
+
write(["id": request.id, "error": "record_failed", "reason": String(describing: error)])
|
|
265
|
+
return
|
|
266
|
+
}
|
|
267
|
+
write([
|
|
268
|
+
"id": request.id,
|
|
269
|
+
"wav": outPath,
|
|
270
|
+
"sampleRate": Int(sampleRate),
|
|
271
|
+
"samples": pcm.count / 2,
|
|
272
|
+
"durationMs": Int(Double(pcm.count / 2) / sampleRate * 1000.0),
|
|
273
|
+
"stopReason": outcome.reason,
|
|
274
|
+
])
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
private static func ensureMicAuthorization() async -> Bool {
|
|
278
|
+
switch AVCaptureDevice.authorizationStatus(for: .audio) {
|
|
279
|
+
case .authorized: return true
|
|
280
|
+
case .notDetermined:
|
|
281
|
+
return await withCheckedContinuation { continuation in
|
|
282
|
+
let box = ResumeOnce(continuation)
|
|
283
|
+
AVCaptureDevice.requestAccess(for: .audio) { box.resume($0) }
|
|
284
|
+
}
|
|
285
|
+
default: return false
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
168
289
|
// MARK: - Text to speech
|
|
169
290
|
|
|
170
291
|
/// Speak a reply through the host's audio output. Kept in the same helper
|
|
@@ -312,13 +433,92 @@ struct AgentDeckFMHelper {
|
|
|
312
433
|
#endif
|
|
313
434
|
}
|
|
314
435
|
|
|
436
|
+
/// stdout is shared by the serial request loop and the detached record
|
|
437
|
+
/// task; the lock keeps two replies from interleaving inside one line.
|
|
438
|
+
private static let writeLock = NSLock()
|
|
439
|
+
|
|
315
440
|
private static func write(_ object: [String: Any]) {
|
|
316
441
|
guard JSONSerialization.isValidJSONObject(object),
|
|
317
442
|
let data = try? JSONSerialization.data(withJSONObject: object),
|
|
318
443
|
let text = String(data: data, encoding: .utf8) else {
|
|
319
444
|
return
|
|
320
445
|
}
|
|
446
|
+
writeLock.lock()
|
|
321
447
|
FileHandle.standardOutput.write(Data((text + "\n").utf8))
|
|
448
|
+
writeLock.unlock()
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/// State for the single in-flight microphone capture. One at a time by design:
|
|
453
|
+
/// the daemon models push-to-talk, and two overlapping engine taps on the same
|
|
454
|
+
/// input device produce garbage anyway.
|
|
455
|
+
private final class RecordingBox: @unchecked Sendable {
|
|
456
|
+
struct Outcome { let cancelled: Bool; let reason: String }
|
|
457
|
+
|
|
458
|
+
private let lock = NSLock()
|
|
459
|
+
private var requestId: Int?
|
|
460
|
+
private var engine: AVAudioEngine?
|
|
461
|
+
private var sink: PCMSink?
|
|
462
|
+
private var continuation: ResumeOnce<Outcome>?
|
|
463
|
+
/// Bumped per recording so a stale max-duration timer from a finished
|
|
464
|
+
/// capture cannot terminate the one that started after it.
|
|
465
|
+
private var generation = 0
|
|
466
|
+
|
|
467
|
+
func begin(requestId: Int, engine: AVAudioEngine, sink: PCMSink) -> Bool {
|
|
468
|
+
lock.lock(); defer { lock.unlock() }
|
|
469
|
+
guard self.requestId == nil else { return false }
|
|
470
|
+
self.requestId = requestId
|
|
471
|
+
self.engine = engine
|
|
472
|
+
self.sink = sink
|
|
473
|
+
generation += 1
|
|
474
|
+
return true
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
func wait(maxMs: Int) async -> Outcome {
|
|
478
|
+
let gen = currentGeneration()
|
|
479
|
+
let outcome: Outcome = await withCheckedContinuation { cont in
|
|
480
|
+
arm(ResumeOnce(cont))
|
|
481
|
+
DispatchQueue.global().asyncAfter(deadline: .now() + .milliseconds(maxMs)) { [weak self] in
|
|
482
|
+
guard let self, self.isLive(generation: gen) else { return }
|
|
483
|
+
_ = self.finish(cancelled: false, reason: "max_duration")
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
return outcome
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
private func currentGeneration() -> Int {
|
|
490
|
+
lock.lock(); defer { lock.unlock() }
|
|
491
|
+
return generation
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
private func arm(_ box: ResumeOnce<Outcome>) {
|
|
495
|
+
lock.lock(); defer { lock.unlock() }
|
|
496
|
+
continuation = box
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
private func isLive(generation gen: Int) -> Bool {
|
|
500
|
+
lock.lock(); defer { lock.unlock() }
|
|
501
|
+
return generation == gen && requestId != nil
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
/// Returns false when no recording is active — the stop raced a max-length
|
|
505
|
+
/// finish or arrived with nothing running.
|
|
506
|
+
func finish(cancelled: Bool, reason: String) -> Bool {
|
|
507
|
+
lock.lock()
|
|
508
|
+
let cont = continuation
|
|
509
|
+
let active = requestId != nil
|
|
510
|
+
continuation = nil
|
|
511
|
+
requestId = nil
|
|
512
|
+
engine = nil
|
|
513
|
+
sink = nil
|
|
514
|
+
lock.unlock()
|
|
515
|
+
guard active, let cont else { return false }
|
|
516
|
+
cont.resume(Outcome(cancelled: cancelled, reason: cancelled ? "cancelled" : reason))
|
|
517
|
+
return true
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
func clear() {
|
|
521
|
+
_ = finish(cancelled: true, reason: "cleared")
|
|
322
522
|
}
|
|
323
523
|
}
|
|
324
524
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentdeck/bridge",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -14,22 +14,36 @@
|
|
|
14
14
|
"assets/fonts",
|
|
15
15
|
"assets/fm-helper",
|
|
16
16
|
"fm-helper",
|
|
17
|
-
"scripts/build-fm-helper.mjs"
|
|
17
|
+
"scripts/build-fm-helper.mjs",
|
|
18
|
+
"python",
|
|
19
|
+
"src/idotmatrix/*.py",
|
|
20
|
+
"src/timebox/*.py",
|
|
21
|
+
"src/pysync/*.py"
|
|
18
22
|
],
|
|
19
23
|
"main": "./dist/index.js",
|
|
20
24
|
"bin": {
|
|
21
25
|
"agentdeck": "./dist/cli.js"
|
|
22
26
|
},
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "tsc",
|
|
29
|
+
"build:fm-helper": "node scripts/build-fm-helper.mjs",
|
|
30
|
+
"prepack": "node scripts/build-fm-helper.mjs",
|
|
31
|
+
"dev": "tsc --watch",
|
|
32
|
+
"clean": "rm -rf dist",
|
|
33
|
+
"typecheck": "tsc --noEmit",
|
|
34
|
+
"start": "node dist/cli.js claude"
|
|
35
|
+
},
|
|
23
36
|
"dependencies": {
|
|
37
|
+
"@agentdeck/hooks": "workspace:*",
|
|
38
|
+
"@agentdeck/shared": "workspace:*",
|
|
24
39
|
"@anthropic-ai/sdk": "^0.111.0",
|
|
25
40
|
"bonjour-service": "^1.3.0",
|
|
26
41
|
"commander": "^14.0.3",
|
|
27
42
|
"express": "^5.2.1",
|
|
28
43
|
"qrcode": "^1.5.4",
|
|
44
|
+
"sharp": "^0.34.5",
|
|
29
45
|
"strip-ansi": "^7.2.0",
|
|
30
|
-
"ws": "^8.20.0"
|
|
31
|
-
"@agentdeck/shared": "1.0.3",
|
|
32
|
-
"@agentdeck/hooks": "1.0.3"
|
|
46
|
+
"ws": "^8.20.0"
|
|
33
47
|
},
|
|
34
48
|
"optionalDependencies": {
|
|
35
49
|
"@picovoice/porcupine-node": "^4.0.2",
|
|
@@ -45,13 +59,5 @@
|
|
|
45
59
|
"@types/qrcode": "^1.5.6",
|
|
46
60
|
"@types/ws": "^8.18.1",
|
|
47
61
|
"typescript": "^6.0.3"
|
|
48
|
-
},
|
|
49
|
-
"scripts": {
|
|
50
|
-
"build": "tsc",
|
|
51
|
-
"build:fm-helper": "node scripts/build-fm-helper.mjs",
|
|
52
|
-
"dev": "tsc --watch",
|
|
53
|
-
"clean": "rm -rf dist",
|
|
54
|
-
"typecheck": "tsc --noEmit",
|
|
55
|
-
"start": "node dist/cli.js claude"
|
|
56
62
|
}
|
|
57
|
-
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import sys
|
|
3
|
+
import argparse
|
|
4
|
+
from idotmatrix import ConnectionManager
|
|
5
|
+
from idotmatrix.modules.common import Common as IdmCommon
|
|
6
|
+
|
|
7
|
+
async def set_brightness(address: str, brightness: int):
|
|
8
|
+
manager = ConnectionManager()
|
|
9
|
+
idm_common = IdmCommon()
|
|
10
|
+
idm_common.conn = manager
|
|
11
|
+
|
|
12
|
+
try:
|
|
13
|
+
print(f"Connecting to iDotMatrix ({address})...")
|
|
14
|
+
await manager.connectByAddress(address)
|
|
15
|
+
print(f"Setting brightness to {brightness}%...")
|
|
16
|
+
await idm_common.setBrightness(brightness)
|
|
17
|
+
print("Brightness set successfully.")
|
|
18
|
+
except Exception as e:
|
|
19
|
+
print(f"ERROR: Failed to set brightness: {e}")
|
|
20
|
+
sys.exit(1)
|
|
21
|
+
finally:
|
|
22
|
+
try:
|
|
23
|
+
await manager.disconnect()
|
|
24
|
+
except Exception:
|
|
25
|
+
pass
|
|
26
|
+
|
|
27
|
+
def main():
|
|
28
|
+
parser = argparse.ArgumentParser(description="Set iDotMatrix Brightness")
|
|
29
|
+
parser.add_argument("-a", "--address", required=True, help="BLE MAC/UUID Address")
|
|
30
|
+
parser.add_argument("-b", "--brightness", type=int, required=True, help="Brightness percent (5-100)")
|
|
31
|
+
args = parser.parse_args()
|
|
32
|
+
|
|
33
|
+
if args.brightness not in range(5, 101):
|
|
34
|
+
print("ERROR: Brightness must be between 5 and 100 percent.")
|
|
35
|
+
sys.exit(1)
|
|
36
|
+
|
|
37
|
+
asyncio.run(set_brightness(args.address, args.brightness))
|
|
38
|
+
|
|
39
|
+
if __name__ == "__main__":
|
|
40
|
+
main()
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# GENERATED FILE — DO NOT EDIT.
|
|
2
|
+
# Source of truth: shared/src/idotmatrix-identity.ts
|
|
3
|
+
# Regenerate: pnpm generate-idotmatrix-identity (drift gated by shared/src/__tests__/idotmatrix-identity.test.ts)
|
|
4
|
+
"""Brand-independent identity of an iDotMatrix-protocol BLE panel.
|
|
5
|
+
|
|
6
|
+
The same hardware advertises as 'IDM-...' (iDotMatrix) or 'iPixel-...', so a
|
|
7
|
+
scan filters on the advertised service first and only falls back to the name
|
|
8
|
+
families.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
IDOTMATRIX_SERVICE_UUID = "000000fa-0000-1000-8000-00805f9b34fb"
|
|
12
|
+
IDOTMATRIX_WRITE_CHARACTERISTIC_UUID = "0000fa02-0000-1000-8000-00805f9b34fb"
|
|
13
|
+
IDOTMATRIX_NAME_PREFIXES = ["IDM-", "IPIXEL"]
|
|
14
|
+
|
|
15
|
+
_HEX = set("0123456789abcdef")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def normalize_uuid(value):
|
|
19
|
+
"""Expand a 16-bit ('fa02') or 32-bit ('000000fa') UUID to Bluetooth-base form.
|
|
20
|
+
|
|
21
|
+
Short forms are left-padded first: 'fa' and '00fa' are the same service.
|
|
22
|
+
"""
|
|
23
|
+
s = (value or "").strip().lower()
|
|
24
|
+
if not s or not set(s) <= _HEX:
|
|
25
|
+
return s
|
|
26
|
+
if len(s) <= 4:
|
|
27
|
+
return "0000" + s.rjust(4, "0") + "-0000-1000-8000-00805f9b34fb"
|
|
28
|
+
if len(s) <= 8:
|
|
29
|
+
return s.rjust(8, "0") + "-0000-1000-8000-00805f9b34fb"
|
|
30
|
+
return s
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def matches_name(name, extra_prefixes=()):
|
|
34
|
+
"""Whether an advertised local name belongs to a known panel family."""
|
|
35
|
+
n = (name or "").strip().upper()
|
|
36
|
+
if not n:
|
|
37
|
+
return False
|
|
38
|
+
for prefix in list(IDOTMATRIX_NAME_PREFIXES) + list(extra_prefixes or ()):
|
|
39
|
+
p = (prefix or "").strip().upper()
|
|
40
|
+
if p and n.startswith(p):
|
|
41
|
+
return True
|
|
42
|
+
return False
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def matches_advertisement(name, service_uuids, extra_prefixes=()):
|
|
46
|
+
"""The discovery predicate: service UUID first, name families second."""
|
|
47
|
+
for uuid in service_uuids or ():
|
|
48
|
+
if normalize_uuid(uuid) == IDOTMATRIX_SERVICE_UUID:
|
|
49
|
+
return True
|
|
50
|
+
return matches_name(name, extra_prefixes)
|