diff --git a/README.md b/README.md index fbb3bf2..9af3b57 100644 --- a/README.md +++ b/README.md @@ -42,12 +42,13 @@ selection and troubleshooting. Protocol and event details are documented in the ## Before You Install -The native `lg_webos` control path does not require Python. Native-only packages -can omit the Python client, `venv`, and `pip`. The current fresh-install flow -still provisions `bscpylgtv` as a compatibility fallback and installs the -brightness dialog, so release-bundle installation checks for Python 3 with a -`venv` that provisions `pip`, plus `zenity`. `swayidle` is needed only by an -existing explicit selection or as the deprecated compatibility fallback. +Fresh installation selects the native `lg_webos` control path, which does not +require Python. Native-only packages can omit the Python client, `venv`, and +`pip`. The release-bundle installer still provisions `bscpylgtv` as an explicit +compatibility fallback and installs the brightness dialog, so it checks for +Python 3 with a `venv` that provisions `pip`, plus `zenity`. `swayidle` is needed +only by an existing explicit selection or as the deprecated compatibility +fallback. ### Debian, Ubuntu, and Pop!_OS @@ -92,9 +93,10 @@ Do not run the installer with `sudo`; it requests elevated access when needed. The installer asks for the TV's IP address, MAC address, HDMI input, control platform, and desktop idle preferences, then installs the required services. -If you select the native `lg_webos` control platform, accept the pairing prompt -during setup. With the default `bscpylgtv` platform, the prompt may instead -appear on first use; see the +Fresh setup defaults to the native `lg_webos` platform and verifies pairing +before saving the configuration, so accept the prompt on the TV. You can +instead select the explicit `bscpylgtv` compatibility fallback; its prompt may +appear on first use. See the [bscpylgtv first-use guide](https://github.com/chros73/bscpylgtv/blob/master/docs/guides/first_use.md). To check, verify, and install the next release from your saved update channel, diff --git a/configure.sh b/configure.sh index d5f27a9..499a7c1 100755 --- a/configure.sh +++ b/configure.sh @@ -93,7 +93,9 @@ normalize_restore_policy() { current_tv_ip="" current_tv_mac="" current_input="HDMI_1" -current_tv_platform="$LG_BUDDY_DEFAULT_TV_PLATFORM" +# A missing platform in an existing profile remains the bscpylgtv compatibility +# default. Only a genuinely fresh profile starts from the native platform. +current_tv_platform="lg_webos" current_screen_backend="$LG_BUDDY_DEFAULT_SCREEN_BACKEND" current_screen_idle_blank="$LG_BUDDY_DEFAULT_SCREEN_IDLE_BLANK" current_screen_idle_timeout="$LG_BUDDY_DEFAULT_IDLE_TIMEOUT" @@ -272,20 +274,20 @@ else done echo "Choose the TV control platform:" - echo " 1) bscpylgtv (Python compatibility platform)" - echo " 2) lg_webos (native LG Buddy platform)" + echo " 1) lg_webos (native LG Buddy platform; recommended)" + echo " 2) bscpylgtv (Python compatibility fallback)" case "$current_tv_platform" in - bscpylgtv) default_platform_choice="1" ;; - lg_webos) default_platform_choice="2" ;; + lg_webos) default_platform_choice="1" ;; + bscpylgtv) default_platform_choice="2" ;; *) default_platform_choice="1" ;; esac while true; do PLATFORM_CHOICE="$(prompt_with_default "Enter number (1-2)" "$default_platform_choice")" case "$PLATFORM_CHOICE" in - 1) tv_platform="bscpylgtv"; break ;; - 2) tv_platform="lg_webos"; break ;; + 1) tv_platform="lg_webos"; break ;; + 2) tv_platform="bscpylgtv"; break ;; *) echo " Please enter 1 or 2." ;; esac done diff --git a/crates/lg-buddy/tests/cucumber_support/steps.rs b/crates/lg-buddy/tests/cucumber_support/steps.rs index c4a1256..a2f539d 100644 --- a/crates/lg-buddy/tests/cucumber_support/steps.rs +++ b/crates/lg-buddy/tests/cucumber_support/steps.rs @@ -354,9 +354,9 @@ fn run_command(world: &mut LgBuddyWorld, command: String) { world.run_named_command(&command); } -#[when("I choose native webOS during initial configuration")] -fn run_native_initial_configuration(world: &mut LgBuddyWorld) { - world.run_native_initial_configuration(); +#[when("I accept the default TV platform during initial configuration")] +fn run_default_initial_configuration(world: &mut LgBuddyWorld) { + world.run_default_initial_configuration(); } #[then("the command succeeds")] diff --git a/crates/lg-buddy/tests/cucumber_support/world.rs b/crates/lg-buddy/tests/cucumber_support/world.rs index 2d2d18d..cacbdbf 100644 --- a/crates/lg-buddy/tests/cucumber_support/world.rs +++ b/crates/lg-buddy/tests/cucumber_support/world.rs @@ -609,13 +609,13 @@ exit 1\n", }); } - pub fn run_native_initial_configuration(&mut self) { + pub fn run_default_initial_configuration(&mut self) { self.ensure_env().set("LG_BUDDY_NONINTERACTIVE", "1"); self.ensure_env().set("LG_BUDDY_TV_IP", "127.0.0.1"); self.ensure_env() .set("LG_BUDDY_TV_MAC", "22:33:44:55:66:77"); self.ensure_env().set("LG_BUDDY_INPUT", "HDMI_2"); - self.ensure_env().set("LG_BUDDY_TV_PLATFORM", "lg_webos"); + self.ensure_env().remove("LG_BUDDY_TV_PLATFORM"); self.ensure_env() .set("LG_BUDDY_RUNTIME_BINARY", env!("CARGO_BIN_EXE_lg-buddy")); self.ensure_env().set("LG_BUDDY_SKIP_SYSTEMD_ACTIONS", "1"); @@ -624,7 +624,8 @@ exit 1\n", .join("../..") .join("configure.sh"); let started = std::time::Instant::now(); - let output = ProcessCommand::new(configure) + let output = ProcessCommand::new("bash") + .arg(configure) .output() .expect("run initial configuration"); let duration = started.elapsed(); diff --git a/crates/lg-buddy/tests/features/webos.feature b/crates/lg-buddy/tests/features/webos.feature index ad39cc2..4f38bc6 100644 --- a/crates/lg-buddy/tests/features/webos.feature +++ b/crates/lg-buddy/tests/features/webos.feature @@ -3,10 +3,10 @@ Feature: Native webOS TV platform pairing when the user is setting up or actively controlling the TV without delaying shutdown, suspend, or network teardown when no stored credential is available. - Scenario: Initial configuration selects and pairs the native platform + Scenario: Fresh configuration defaults to and pairs the native platform Given an empty temporary LG Buddy config path And a native webOS26 TV on firmware 43.21.60 on input HDMI_2 with brightness 90 - When I choose native webOS during initial configuration + When I accept the default TV platform during initial configuration Then the command succeeds And stdout contains "TV Platform: lg_webos" And stdout contains "pairing required; accept the prompt on the TV" @@ -15,6 +15,12 @@ Feature: Native webOS TV platform And the native TV connection count is 1 And the native TV registration tokens are "none" And the native TV pairing prompt count is 1 + When I run the command "brightness get" + Then the command succeeds + And stdout is "90" + And the native TV connection count is 2 + And the native TV registration tokens are "none,webos-test-access-token" + And the native TV pairing prompt count is 1 Scenario: Opting in pairs the TV and the stored token authenticates later commands Given a temporary LG Buddy config using input HDMI_2 @@ -90,7 +96,7 @@ Feature: Native webOS TV platform And the native TV registration tokens are "webos-test-access-token" And the native TV pairing prompt count is 0 - Scenario: Unsetting native platform restores the default without preflight + Scenario: Unsetting native platform restores the missing-value compatibility default Given a temporary LG Buddy config using input HDMI_2 And the existing config selects TV platform "lg_webos" When I run the command "settings unset tv.platform" diff --git a/docs/architecture-overview.md b/docs/architecture-overview.md index 75cadc8..ce21640 100644 --- a/docs/architecture-overview.md +++ b/docs/architecture-overview.md @@ -679,9 +679,12 @@ remain inside the adapter. Wake-on-LAN keeps the configured network identity at ### TV Implementations -`tv.platform` selects the production TV implementation. `bscpylgtvcommand` -remains the compatibility default, including when an existing profile has no -platform value. `lg_webos` explicitly selects the native Rust implementation. +`tv.platform` selects the production TV implementation. Fresh profiles select +the native Rust `lg_webos` implementation and verify pairing before the profile +is saved. Existing profiles retain their explicit choice; a missing platform +value continues to resolve to `bscpylgtv` and is materialized as that +compatibility choice when configuration is rewritten. `bscpylgtvcommand` +remains available as an explicit fallback. The Rust runtime talks to it through `BscpylgtvCommandClient`, which: diff --git a/docs/defaults-and-configuration.md b/docs/defaults-and-configuration.md index 095f888..bc6e190 100644 --- a/docs/defaults-and-configuration.md +++ b/docs/defaults-and-configuration.md @@ -77,7 +77,7 @@ Good shapes: tvs_primary_ip=192.168.1.100 tvs_primary_mac=aa:bb:cc:dd:ee:ff tvs_primary_input=HDMI_2 -tvs_primary_platform=bscpylgtv +tvs_primary_platform=lg_webos screen_restore_policy=conservative screen_idle_blank=enabled system_sleep_wake_policy=enabled @@ -109,12 +109,14 @@ choice. `tvs_primary_platform` selects the control platform for the active TV profile: -- `bscpylgtv` remains the default, including for existing profiles where the - key is absent -- `lg_webos` is the experimental native Rust platform and can be selected - during initial configuration or with `lg-buddy settings set tv.platform lg_webos` -- initial native selection pairs before it is saved; ordinary TV operations - can also pair or repair credentials when needed +- fresh profiles select `lg_webos` and pair before the configuration is saved +- existing profiles keep their explicit platform; a missing platform continues + to resolve to `bscpylgtv` and is materialized as such when configuration is + rewritten +- `bscpylgtv` remains an accepted explicit compatibility fallback +- existing users can opt into native control with + `lg-buddy settings set tv.platform lg_webos`; ordinary foreground TV + operations can also pair or repair credentials when needed - shutdown, suspend, resume, startup, and network-teardown handling use stored credentials only and skip promptly when no credential is available - this is the only platform selector; there is no separate backend, adapter, diff --git a/docs/release-process.md b/docs/release-process.md index 8cf48b0..3e4fe43 100644 --- a/docs/release-process.md +++ b/docs/release-process.md @@ -65,6 +65,10 @@ There is no separate version input. response evidence for the deterministic mock. Stable promotion remains blocked until this exact prerelease commit has a successful canary. +Replace the publisher's generic release description with concise, +release-specific notes for user-visible default or compatibility changes before +announcing the release. + Do not push version tags manually. Protected `v*` tags and stream-alignment writes permit bypass only to the dedicated release App. A failed post-merge release run can be rerun safely: an incomplete draft remains private and is diff --git a/docs/testing-strategy.md b/docs/testing-strategy.md index 53df140..2178a5d 100644 --- a/docs/testing-strategy.md +++ b/docs/testing-strategy.md @@ -148,8 +148,10 @@ It is useful when we want to express scenarios like: - when the user returns after LG Buddy blanked the TV, LG Buddy restores the screen - when aggressive restore policy is enabled, wake/activity can restore even without a marker - when GNOME is available, backend detection resolves to `gnome` -- when the user chooses `lg_webos` during initial configuration, pairing stores - the credential before setup completes +- when fresh configuration accepts the default `lg_webos` platform, pairing + stores the credential before setup completes +- when an existing profile has no platform value, configuration preserves and + materializes the `bscpylgtv` compatibility fallback - when native credentials are missing or stale, ordinary TV commands pair or repair them as part of the operation - when native credentials are missing, shutdown and suspend-related commands diff --git a/docs/user-guide.md b/docs/user-guide.md index 4ba1df6..d72ec4e 100644 --- a/docs/user-guide.md +++ b/docs/user-guide.md @@ -82,7 +82,7 @@ Current settings are: | `tv.ip` | TV network address. | | `tv.mac` | TV MAC address used for Wake-on-LAN. | | `tv.input` | Input LG Buddy manages, such as `HDMI_2`. | -| `tv.platform` | TV control implementation: `bscpylgtv` or experimental `lg_webos`. | +| `tv.platform` | TV control implementation: native `lg_webos` or the `bscpylgtv` compatibility fallback. | | `screen.backend` | Desktop idle backend: `auto`, `gnome`, `wayland`, or deprecated compatibility value `swayidle`. | | `screen.idle_blank` | Enable or disable automatic idle blanking. | | `screen.idle_timeout` | Seconds of inactivity before blanking; defaults to 300. | @@ -185,10 +185,16 @@ input paths and hardware troubleshooting. `tv.platform` selects the TV control implementation: -- `bscpylgtv`: the compatibility default. -- `lg_webos`: the experimental native Rust webOS implementation. +- `lg_webos`: the native Rust implementation and fresh-profile default. +- `bscpylgtv`: the explicit Python compatibility fallback. -Select the native implementation with: +Fresh configuration verifies native pairing before it saves the profile. +Existing profiles retain their selected platform. If an older profile has no +platform key, it continues to resolve to `bscpylgtv`; rewriting that profile +through the configurator materializes the compatibility choice instead of +silently moving it to native control. + +Move an existing profile to the native implementation with: ```bash lg-buddy settings set tv.platform lg_webos @@ -200,12 +206,28 @@ commands can pair or repair credentials when necessary; unattended startup, shutdown, suspend, and resume handling use an existing credential and do not open a pairing prompt. -Return to the default with: +Select and persist the compatibility fallback with: ```bash -lg-buddy settings unset tv.platform +lg-buddy settings set tv.platform bscpylgtv ``` +`settings unset tv.platform` removes the explicit choice and therefore resolves +to `bscpylgtv` for legacy compatibility; it does not apply the fresh-profile +default. + +For support and troubleshooting, inspect the effective platform, its source, +and accepted values with: + +```bash +lg-buddy settings describe tv.platform +``` + +If native pairing or its power-state verification fails, setup leaves the +profile unsaved. Confirm that the TV is reachable and accept its pairing prompt, +then rerun configuration or `settings set tv.platform lg_webos`. Select +`bscpylgtv` explicitly if native control is not usable on that TV. + ## System Sleep And Wake Default installs power off the TV before system sleep and restore it after diff --git a/scripts/test-cross-version-upgrade.sh b/scripts/test-cross-version-upgrade.sh index 7d0bb7b..203a5c1 100755 --- a/scripts/test-cross-version-upgrade.sh +++ b/scripts/test-cross-version-upgrade.sh @@ -210,6 +210,7 @@ export LG_BUDDY_SKIP_PIP_INSTALL="1" export LG_BUDDY_TV_IP="192.168.50.20" export LG_BUDDY_TV_MAC="02:00:00:00:00:20" export LG_BUDDY_INPUT="HDMI_3" +export LG_BUDDY_TV_PLATFORM="bscpylgtv" export LG_BUDDY_SCREEN_BACKEND="auto" export LG_BUDDY_SYSTEM_SLEEP_WAKE_POLICY="enabled" export PIP_DISABLE_PIP_VERSION_CHECK="1" diff --git a/scripts/test-production-upgrade-canary.sh b/scripts/test-production-upgrade-canary.sh index 809c818..79ac5d9 100755 --- a/scripts/test-production-upgrade-canary.sh +++ b/scripts/test-production-upgrade-canary.sh @@ -152,6 +152,7 @@ export LG_BUDDY_SKIP_PIP_INSTALL="1" export LG_BUDDY_TV_IP="192.168.60.20" export LG_BUDDY_TV_MAC="02:00:00:00:00:60" export LG_BUDDY_INPUT="HDMI_3" +export LG_BUDDY_TV_PLATFORM="bscpylgtv" export LG_BUDDY_SCREEN_BACKEND="auto" export LG_BUDDY_SYSTEM_SLEEP_WAKE_POLICY="enabled" export PIP_DISABLE_PIP_VERSION_CHECK="1" diff --git a/scripts/test-release-bundle.sh b/scripts/test-release-bundle.sh index c0d0eb0..a48d567 100755 --- a/scripts/test-release-bundle.sh +++ b/scripts/test-release-bundle.sh @@ -344,17 +344,51 @@ printf '%s\n' "$VERSION_OUTPUT" | grep -q "^commit: " FRESH_CONFIG_HOME="$WORK_DIR/fresh-config-home" FRESH_CONFIG_OUTPUT="$WORK_DIR/fresh-config.output" +FRESH_NATIVE_RUNTIME="$WORK_DIR/fresh-native-runtime" +FRESH_NATIVE_PAIRING_MARKER="$WORK_DIR/fresh-native-pairing" +FRESH_NATIVE_TOKEN="$FRESH_CONFIG_HOME/.config/lg-buddy/tvs/primary/access-token.json" +cat >"$FRESH_NATIVE_RUNTIME" <<'EOF' +#!/bin/sh +set -eu + +[ "$#" -eq 4 ] && + [ "$1" = "settings" ] && + [ "$2" = "set" ] && + [ "$3" = "tv.platform" ] && + [ "$4" = "lg_webos" ] || exit 2 + +config_path="${LG_BUDDY_CONFIG:?}" +token_dir="$(dirname "$config_path")/tvs/primary" +sed -i 's/^tvs_primary_platform=bscpylgtv$/tvs_primary_platform=lg_webos/' "$config_path" +mkdir -p "$token_dir" +chmod 700 "$(dirname "$token_dir")" "$token_dir" +printf '{\n "access_token": "release-smoke-native-token"\n}\n' >"$token_dir/access-token.json" +chmod 600 "$token_dir/access-token.json" +: >"${LG_BUDDY_NATIVE_PAIRING_MARKER:?}" +echo "LG Buddy native webOS preflight: pairing required; accept the prompt on the TV." +echo "LG Buddy native webOS preflight: stored access token at $token_dir/access-token.json" +echo "LG Buddy native webOS preflight succeeded: power_state=Active" +EOF +chmod 755 "$FRESH_NATIVE_RUNTIME" mkdir -p "$FRESH_CONFIG_HOME" ( - unset LG_BUDDY_NONINTERACTIVE LG_BUDDY_SCREEN_BACKEND LG_BUDDY_CONFIG + unset LG_BUDDY_NONINTERACTIVE LG_BUDDY_TV_PLATFORM LG_BUDDY_SCREEN_BACKEND LG_BUDDY_CONFIG export HOME="$FRESH_CONFIG_HOME" export XDG_CONFIG_HOME="$FRESH_CONFIG_HOME/.config" - export LG_BUDDY_RUNTIME_BINARY="$BUNDLE_DIR/lg-buddy" + export LG_BUDDY_RUNTIME_BINARY="$FRESH_NATIVE_RUNTIME" + export LG_BUDDY_NATIVE_PAIRING_MARKER="$FRESH_NATIVE_PAIRING_MARKER" export LG_BUDDY_SKIP_SYSTEMD_ACTIONS="1" printf '%s\n' \ - '192.0.2.10' 'aa:bb:cc:dd:ee:ff' '2' '1' 'Y' '1' '300' '1' 'Y' \ + '192.0.2.10' 'aa:bb:cc:dd:ee:ff' '2' '' 'Y' '1' '300' '1' 'Y' \ | "$BUNDLE_DIR/configure.sh" >"$FRESH_CONFIG_OUTPUT" 2>&1 ) +grep -F -q 'TV Platform: lg_webos' "$FRESH_CONFIG_OUTPUT" +grep -F -q 'pairing required; accept the prompt on the TV' "$FRESH_CONFIG_OUTPUT" +grep -q '^tvs_primary_platform=lg_webos$' "$FRESH_CONFIG_HOME/.config/lg-buddy/config.env" +assert_file "$FRESH_NATIVE_PAIRING_MARKER" +assert_file "$FRESH_NATIVE_TOKEN" +assert_mode "$FRESH_NATIVE_TOKEN" 600 +python3 -c 'import json, sys; assert json.load(open(sys.argv[1], encoding="utf-8")) == {"access_token": "release-smoke-native-token"}' "$FRESH_NATIVE_TOKEN" grep -F -q ' 3) wayland' "$FRESH_CONFIG_OUTPUT" if grep -F -q 'swayidle' "$FRESH_CONFIG_OUTPUT"; then echo "Fresh interactive configuration presented swayidle." @@ -371,6 +405,7 @@ export LG_BUDDY_SKIP_SYSTEMD_ACTIONS="1" export LG_BUDDY_TV_IP="192.168.1.10" export LG_BUDDY_TV_MAC="aa:bb:cc:dd:ee:ff" export LG_BUDDY_INPUT="HDMI_2" +export LG_BUDDY_TV_PLATFORM="bscpylgtv" export LG_BUDDY_SCREEN_BACKEND="auto" export LG_BUDDY_SYSTEM_SLEEP_WAKE_POLICY="enabled" export PIP_DISABLE_PIP_VERSION_CHECK="1" @@ -449,12 +484,19 @@ python3 "$SCRIPT_DIR/release_bundle_manifest.py" validate \ --binary "$INSTALLED_BINARY" \ "${MANIFEST_EXPECTATIONS[@]}" -# Existing profiles without the platform key remain on bscpylgtv. Materialize -# that choice through settings, then use a controlled raw-config fixture to -# prove an unpaired native shutdown skips immediately without contacting a TV. +# Existing profiles without the platform key remain on bscpylgtv. Rewriting +# one through configure.sh materializes that choice instead of applying the +# fresh-profile default. Then use a controlled raw-config fixture to prove an +# unpaired native shutdown skips immediately without contacting a TV. sed -i '/^tvs_primary_platform=/d' "$CONFIG_FILE" "$INSTALLED_BINARY" settings get tv.platform | grep -q '^bscpylgtv$' -"$INSTALLED_BINARY" settings set tv.platform bscpylgtv +LEGACY_MISSING_CONFIGURE_OUTPUT="$WORK_DIR/legacy-missing-configure.output" +( + unset LG_BUDDY_TV_PLATFORM + cd "$BUNDLE_DIR" + ./configure.sh >"$LEGACY_MISSING_CONFIGURE_OUTPUT" 2>&1 +) +grep -F -q 'TV Platform: bscpylgtv' "$LEGACY_MISSING_CONFIGURE_OUTPUT" grep -q '^tvs_primary_platform=bscpylgtv$' "$CONFIG_FILE" sed -i 's/^tvs_primary_platform=bscpylgtv$/tvs_primary_platform=lg_webos/' "$CONFIG_FILE" @@ -494,10 +536,11 @@ grep -q '^updates_channel=prerelease$' "$CONFIG_FILE" # Configure should read inline-commented platform values with the same value # semantics as the Rust config parser, then persist the sanitized choice. sed -i 's/^tvs_primary_platform=bscpylgtv$/ tvs_primary_platform = bscpylgtv # legacy/' "$CONFIG_FILE" -printf '%s\n' 'tvs_primary_platform = lg_webos # experimental' >>"$CONFIG_FILE" +printf '%s\n' 'tvs_primary_platform = lg_webos # native' >>"$CONFIG_FILE" LEGACY_CONFIGURE_OUTPUT="$WORK_DIR/legacy-configure.output" ( + unset LG_BUDDY_TV_PLATFORM unset LG_BUDDY_SCREEN_BACKEND unset LG_BUDDY_SCREEN_IDLE_TIMEOUT unset LG_BUDDY_SCREEN_RESTORE_POLICY