Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 11 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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,
Expand Down
16 changes: 9 additions & 7 deletions configure.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions crates/lg-buddy/tests/cucumber_support/steps.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand Down
7 changes: 4 additions & 3 deletions crates/lg-buddy/tests/cucumber_support/world.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand All @@ -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();
Expand Down
12 changes: 9 additions & 3 deletions crates/lg-buddy/tests/features/webos.feature
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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
Expand Down Expand Up @@ -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"
Expand Down
9 changes: 6 additions & 3 deletions docs/architecture-overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down
16 changes: 9 additions & 7 deletions docs/defaults-and-configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down
4 changes: 4 additions & 0 deletions docs/release-process.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 4 additions & 2 deletions docs/testing-strategy.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
34 changes: 28 additions & 6 deletions docs/user-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -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. |
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
1 change: 1 addition & 0 deletions scripts/test-cross-version-upgrade.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
1 change: 1 addition & 0 deletions scripts/test-production-upgrade-canary.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
59 changes: 51 additions & 8 deletions scripts/test-release-bundle.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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."
Expand All @@ -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"
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down