muxplex-deck 0.7.0__tar.gz → 0.8.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.
- {muxplex_deck-0.7.0 → muxplex_deck-0.8.0}/PKG-INFO +1 -1
- {muxplex_deck-0.7.0 → muxplex_deck-0.8.0}/pyproject.toml +1 -1
- {muxplex_deck-0.7.0 → muxplex_deck-0.8.0}/src/muxplex_deck/cli.py +31 -24
- {muxplex_deck-0.7.0 → muxplex_deck-0.8.0}/src/muxplex_deck/main.py +36 -3
- muxplex_deck-0.8.0/src/muxplex_deck/service.py +1664 -0
- muxplex_deck-0.7.0/src/muxplex_deck/service.py +0 -973
- {muxplex_deck-0.7.0 → muxplex_deck-0.8.0}/README.md +0 -0
- {muxplex_deck-0.7.0 → muxplex_deck-0.8.0}/src/deck_probe/__init__.py +0 -0
- {muxplex_deck-0.7.0 → muxplex_deck-0.8.0}/src/deck_probe/__main__.py +0 -0
- {muxplex_deck-0.7.0 → muxplex_deck-0.8.0}/src/deck_probe/capabilities.py +0 -0
- {muxplex_deck-0.7.0 → muxplex_deck-0.8.0}/src/deck_probe/events.py +0 -0
- {muxplex_deck-0.7.0 → muxplex_deck-0.8.0}/src/deck_probe/main.py +0 -0
- {muxplex_deck-0.7.0 → muxplex_deck-0.8.0}/src/deck_probe/rendering.py +0 -0
- {muxplex_deck-0.7.0 → muxplex_deck-0.8.0}/src/muxplex_deck/__init__.py +0 -0
- {muxplex_deck-0.7.0 → muxplex_deck-0.8.0}/src/muxplex_deck/__main__.py +0 -0
- {muxplex_deck-0.7.0 → muxplex_deck-0.8.0}/src/muxplex_deck/_vendor/hidapi/LICENSE-bsd.txt +0 -0
- {muxplex_deck-0.7.0 → muxplex_deck-0.8.0}/src/muxplex_deck/_vendor/hidapi/VERSION +0 -0
- {muxplex_deck-0.7.0 → muxplex_deck-0.8.0}/src/muxplex_deck/_vendor/hidapi/x64/hidapi.dll +0 -0
- {muxplex_deck-0.7.0 → muxplex_deck-0.8.0}/src/muxplex_deck/attention.py +0 -0
- {muxplex_deck-0.7.0 → muxplex_deck-0.8.0}/src/muxplex_deck/config.py +0 -0
- {muxplex_deck-0.7.0 → muxplex_deck-0.8.0}/src/muxplex_deck/device.py +0 -0
- {muxplex_deck-0.7.0 → muxplex_deck-0.8.0}/src/muxplex_deck/device_real.py +0 -0
- {muxplex_deck-0.7.0 → muxplex_deck-0.8.0}/src/muxplex_deck/emulator.py +0 -0
- {muxplex_deck-0.7.0 → muxplex_deck-0.8.0}/src/muxplex_deck/focus.py +0 -0
- {muxplex_deck-0.7.0 → muxplex_deck-0.8.0}/src/muxplex_deck/hidapi_win.py +0 -0
- {muxplex_deck-0.7.0 → muxplex_deck-0.8.0}/src/muxplex_deck/hidhelp.py +0 -0
- {muxplex_deck-0.7.0 → muxplex_deck-0.8.0}/src/muxplex_deck/init_wizard.py +0 -0
- {muxplex_deck-0.7.0 → muxplex_deck-0.8.0}/src/muxplex_deck/interaction.py +0 -0
- {muxplex_deck-0.7.0 → muxplex_deck-0.8.0}/src/muxplex_deck/layout.py +0 -0
- {muxplex_deck-0.7.0 → muxplex_deck-0.8.0}/src/muxplex_deck/rendering.py +0 -0
- {muxplex_deck-0.7.0 → muxplex_deck-0.8.0}/src/muxplex_deck/report.py +0 -0
- {muxplex_deck-0.7.0 → muxplex_deck-0.8.0}/src/muxplex_deck/statusfile.py +0 -0
- {muxplex_deck-0.7.0 → muxplex_deck-0.8.0}/src/muxplex_deck/usbnode.py +0 -0
- {muxplex_deck-0.7.0 → muxplex_deck-0.8.0}/src/muxplex_deck/views.py +0 -0
- {muxplex_deck-0.7.0 → muxplex_deck-0.8.0}/src/muxplex_deck/wsl.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: muxplex-deck
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.0
|
|
4
4
|
Summary: Hardware probe/diagnostic app for the Elgato Stream Deck+ (seed of the muxplex sidecar)
|
|
5
5
|
Author: Amplifier
|
|
6
6
|
Author-email: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
|
|
@@ -60,6 +60,15 @@ def _add_run_flags(parser: argparse.ArgumentParser) -> None:
|
|
|
60
60
|
default=8484,
|
|
61
61
|
help="Port for the emulator's web UI (default: 8484). Ignored without --emulator.",
|
|
62
62
|
)
|
|
63
|
+
parser.add_argument(
|
|
64
|
+
"--log-file",
|
|
65
|
+
default=None,
|
|
66
|
+
help="Write logs to this file instead of stderr (default: stderr, unchanged "
|
|
67
|
+
"behavior). All platforms -- required in practice under Windows Task "
|
|
68
|
+
"Scheduler, since pythonw.exe has no console for stderr to go to "
|
|
69
|
+
"(see WINDOWS_NATIVE_SPEC.md section 1.5); `muxplex-deck service install` "
|
|
70
|
+
"passes this automatically on Windows.",
|
|
71
|
+
)
|
|
63
72
|
|
|
64
73
|
|
|
65
74
|
def run(
|
|
@@ -67,6 +76,7 @@ def run(
|
|
|
67
76
|
*,
|
|
68
77
|
emulator: bool = False,
|
|
69
78
|
emulator_port: int = 8484,
|
|
79
|
+
log_file: str | None = None,
|
|
70
80
|
) -> int:
|
|
71
81
|
"""Load config, build the device backend, and run the sidecar's main loop.
|
|
72
82
|
|
|
@@ -90,7 +100,8 @@ def run(
|
|
|
90
100
|
print(str(exc), file=sys.stderr)
|
|
91
101
|
return 1
|
|
92
102
|
|
|
93
|
-
|
|
103
|
+
log_path = Path(log_file).expanduser() if log_file else None
|
|
104
|
+
return main_mod.run(cfg, manager, log_file=log_path)
|
|
94
105
|
|
|
95
106
|
|
|
96
107
|
# ---------------------------------------------------------------------------
|
|
@@ -758,19 +769,15 @@ def check_service_status() -> tuple[str, str]:
|
|
|
758
769
|
if sys.platform == "darwin":
|
|
759
770
|
manager, tool = "launchd", "launchctl"
|
|
760
771
|
elif sys.platform == "win32":
|
|
761
|
-
#
|
|
762
|
-
#
|
|
763
|
-
#
|
|
764
|
-
#
|
|
765
|
-
|
|
766
|
-
return "warn", (
|
|
767
|
-
"Service: background-service install isn't supported on "
|
|
768
|
-
"Windows yet -- run: muxplex-deck"
|
|
769
|
-
)
|
|
772
|
+
# Task Scheduler has no single "is the tool present" probe the way
|
|
773
|
+
# `shutil.which("systemctl")` does -- `schtasks.exe` ships with
|
|
774
|
+
# every Windows install, so there's nothing to gate on here; skip
|
|
775
|
+
# straight to the installed/active checks below.
|
|
776
|
+
manager, tool = "Task Scheduler", None
|
|
770
777
|
else:
|
|
771
778
|
manager, tool = "systemd", "systemctl"
|
|
772
779
|
|
|
773
|
-
if shutil.which(tool) is None:
|
|
780
|
+
if tool is not None and shutil.which(tool) is None:
|
|
774
781
|
return "warn", f"Service: {tool} not found -- run muxplex-deck directly"
|
|
775
782
|
|
|
776
783
|
if not service_is_installed():
|
|
@@ -1378,19 +1385,19 @@ def update(*, force: bool = False) -> None:
|
|
|
1378
1385
|
was_active = _service_is_active()
|
|
1379
1386
|
if was_active:
|
|
1380
1387
|
print(" Stopping service...")
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1388
|
+
# Was a hardcoded launchctl/systemctl-only block -- the `else`
|
|
1389
|
+
# branch ran `systemctl` unconditionally, which raises
|
|
1390
|
+
# `FileNotFoundError` on Windows (`check=False` only suppresses a
|
|
1391
|
+
# nonzero *exit*, not a failed *exec*). This was latent only
|
|
1392
|
+
# because `service_is_active()` returned False on Windows before
|
|
1393
|
+
# Task Scheduler support existed -- now that it can return True
|
|
1394
|
+
# there, this must dispatch per platform. `service.service_stop()`
|
|
1395
|
+
# already does exactly that (darwin/windows/systemd), so this is
|
|
1396
|
+
# strictly less code, one dispatch site, not a duplicated stop
|
|
1397
|
+
# implementation.
|
|
1398
|
+
from .service import service_stop
|
|
1399
|
+
|
|
1400
|
+
service_stop()
|
|
1394
1401
|
else:
|
|
1395
1402
|
print(" No active service found (skipping stop)")
|
|
1396
1403
|
|
|
@@ -50,9 +50,11 @@ from __future__ import annotations
|
|
|
50
50
|
|
|
51
51
|
import logging
|
|
52
52
|
import signal
|
|
53
|
+
import sys
|
|
53
54
|
import threading
|
|
54
55
|
import time
|
|
55
56
|
from collections.abc import Callable
|
|
57
|
+
from pathlib import Path
|
|
56
58
|
from urllib.parse import urlparse
|
|
57
59
|
|
|
58
60
|
from muxplex_client import (
|
|
@@ -94,7 +96,38 @@ FULL_BRIGHTNESS_PERCENT = 100
|
|
|
94
96
|
_MAX_VIEW_LABEL_CHARS = 20
|
|
95
97
|
|
|
96
98
|
|
|
97
|
-
def _configure_logging() -> None:
|
|
99
|
+
def _configure_logging(log_file: Path | None = None) -> None:
|
|
100
|
+
"""Configure logging, optionally to a rotating file instead of stderr.
|
|
101
|
+
|
|
102
|
+
`log_file` is required in practice on Windows under Task Scheduler:
|
|
103
|
+
`pythonw.exe` (the GUI-subsystem interpreter `service._resolve_pythonw()`
|
|
104
|
+
resolves to -- see WINDOWS_NATIVE_SPEC.md section 1.5) leaves
|
|
105
|
+
`sys.stdout`/`sys.stderr` as `None`, and `logging.StreamHandler()` around
|
|
106
|
+
a `None` stream would fail. `cli.py`'s `--log-file` flag (all platforms,
|
|
107
|
+
default `None`) is what plumbs a path down to here; on macOS/Linux this
|
|
108
|
+
stays `None` and behavior is byte-for-byte unchanged from before this
|
|
109
|
+
parameter existed.
|
|
110
|
+
"""
|
|
111
|
+
if log_file is not None:
|
|
112
|
+
from logging.handlers import RotatingFileHandler
|
|
113
|
+
|
|
114
|
+
log_file.parent.mkdir(parents=True, exist_ok=True)
|
|
115
|
+
handler = RotatingFileHandler(
|
|
116
|
+
log_file, maxBytes=2_000_000, backupCount=3, encoding="utf-8"
|
|
117
|
+
)
|
|
118
|
+
handler.setFormatter(
|
|
119
|
+
logging.Formatter(
|
|
120
|
+
fmt="%(asctime)s.%(msecs)03d %(levelname)s %(message)s",
|
|
121
|
+
datefmt="%H:%M:%S",
|
|
122
|
+
)
|
|
123
|
+
)
|
|
124
|
+
logging.basicConfig(level=logging.INFO, handlers=[handler])
|
|
125
|
+
return
|
|
126
|
+
if sys.stderr is None:
|
|
127
|
+
# Defensive: a console-less launch with no --log-file must not
|
|
128
|
+
# crash trying to build a StreamHandler around a None stream.
|
|
129
|
+
logging.basicConfig(level=logging.INFO, handlers=[logging.NullHandler()])
|
|
130
|
+
return
|
|
98
131
|
logging.basicConfig(
|
|
99
132
|
level=logging.INFO,
|
|
100
133
|
format="%(asctime)s.%(msecs)03d %(levelname)s %(message)s",
|
|
@@ -1035,8 +1068,8 @@ def _install_signal_handler() -> threading.Event:
|
|
|
1035
1068
|
return shutting_down
|
|
1036
1069
|
|
|
1037
1070
|
|
|
1038
|
-
def run(config: Config, manager: DeviceManager) -> int:
|
|
1039
|
-
_configure_logging()
|
|
1071
|
+
def run(config: Config, manager: DeviceManager, *, log_file: Path | None = None) -> int:
|
|
1072
|
+
_configure_logging(log_file)
|
|
1040
1073
|
|
|
1041
1074
|
shutting_down = _install_signal_handler()
|
|
1042
1075
|
hostname = urlparse(config.server_url).hostname or config.server_url
|