La télécommande du téléviseur Sony suspend l'ordinateur portable via le contrôle réseau
tvpowered
(La TV contrôle l'alimentation de l'ordinateur) est un bash script qui suspend automatiquement l'ordinateur portable lorsque la TV Sony Bravia est éteinte. En outre, il va :
- Éteignez l'image du téléviseur pour économiser de l'énergie lorsque vous ne regardez pas de films sur le téléviseur.
- Affiche une bulle contextuelle lorsque vous modifiez le volume du téléviseur utilisé pour écouter de la musique.
Veuillez noter que fonctionne uniquement avec les téléviseurs Sony Bravia .
Vue d'ensemble
Lorsque le téléviseur est éteint tvpowered
suspend, met en veille prolongée ou éteint automatiquement l'ordinateur portable. Modifiez le paramètre de SCTL
variable globale pour contrôler l'action à entreprendre.
tvpowered
doit être exécuté en tant qu'utilisateur normal et appelé dans les applications de démarrage.
La conception du programme est simple :
- Attendez que le téléviseur soit sous tension.
- Commencer une opération entièrement active.
- Vérifiez si le téléviseur est éteint. Si elle est éteinte, passez à l'étape 5.
- Dormez pendant 3 secondes et répétez l'étape 3.
- Suspendre ou mettre hors tension le système lorsque le téléviseur est éteint.
- Si vous reprenez à partir d'une suspension, retournez à l'étape 1.
Entre ces étapes, des bulles d'information apparaissent sur le bureau et sont également enregistrées dans le journal de bord. journalctl
:
$ journalctl -xe | grep tvpower
Jun 11 18:11:20 tvpowered[27398]: TV is powered on. 'tvpowered' is now waiting for TV to power off.
Jun 11 18:11:47 tvpowered[28229]: TV Powered off. 'systemctl suspend' being called.
Jun 11 18:11:47 tvpowered[28238]: System powered back up. Checking if TV powered on. './tvpowered'.
Jun 11 18:12:26 tvpowered[31672]: TV is powered on. 'tvpowered' is now waiting for TV to power off.
tvpowered
script
Copiez et collez le script dans un fichier sur votre ordinateur et marquez-le comme exécutable avec :
chmod a+x /path/to/tvpowered
Où /path/to/
est le nom du répertoire dans lequel vous avez créé le fichier.
Vous pouvez également utiliser votre gestionnaire de fichiers (comme Nautilus) pour rendre le fichier exécutable.
Dans le script ci-dessous, il y a quelques constantes que vous devrez définir :
SCTL=suspend # systemctl paramater: suspend or poweroff
IP=192.168.0.16 # IP address for Sony TV
PWRD=123 # Password for Sony TV IP Connect
tvpowered
compléter bash script
#!/bin/bash
# NAME: tvpowered
#
# Original name slave2tv announced as politically incorrect after one day:
# https://www.rt.com/news/491343-microsoft-coding-blacklists-slaves/
#
# DESC: When TV is powered off automatically suspend the laptop.
# DATE: June 9, 2020. Modified December 31, 2020
#
# NOTE: Written for Ask Ubuntu question:
# https://askubuntu.com/questions/1247484/
# 4-clicks-to-shut-down-ubuntu-can-we-reduce-this
# UPDT: Jun 10 2020: Make name politically correct for Microsoft guidelines.
# Change name from 'slave2tv' to 'tvpowered'. Abandon approach of polling
# i2c, drm, i915, nvidia, xrandr, etc to see if monitor turned off. Setup
# WiFi on TV instead and use Sony REST API to communicate TV status.
# Jun 11 2020: Add pop-up bubble status messages. Add dependencies.
# Add TenMinuteSpam. Add WaitUserSignOn. Add $SCTL constant. Convert
# in-line code to mainline format.
# Oct 03 2020: If ethernet disconnected we don't want to suspend.
# Add TenMinuteSpam. Add WaitUserSignOn. Add $SCTL constant. Convert
# in-line code to mainline format.
# Oct 18 2020: If WiFi disconnected we don't want to suspend.
# Dec 23 2020: After resume turn off picture with power savings.
# Dec 31 2020: Fast popping bubble messages when volume changes.
# Sources:
# https://gist.github.com/kalleth/e10e8f3b8b7cb1bac21463b0073a65fb#cec-sonycec
# https://pro-bravia.sony.net/develop/integrate/rest-api/spec/service/audio/v1_0/setAudioVolume/index.html
# https://developer.sony.com/develop/audio-control-api/get-started/http-example#tutorial-step-2
# https://en.wikipedia.org/wiki/CURL
# https://stackoverflow.com/questions/7172784/how-do-i-post-json-data-with-curl
# https://stackoverflow.com/questions/2829613/how-do-you-tell-if-a-string-contains-another-string-in-posix-sh
SCTL=suspend # systemctl paramater: suspend or poweroff
IP=192.168.0.16 # IP address for Sony TV
PWRD=123 # Password for Sony TV IP Connect (Pre-Shared key)
# Must have curl package.
command -v curl >/dev/null 2>&1 || { echo >&2 \
"'curl' package required but it is not installed. Aborting."; \
exit 2; }
# Must have notify-send from libnotify-bin package
command -v notify-send >/dev/null 2>&1 || { echo >&2 \
"libnotify-bin package required but it is not installed. Aborting."; \
exit 3; }
cURLit () {
# $1 = JSON String in pretty format converted to file for cURL --data.
# $2 = Sony subsystem to talk to, eg accessControl, audio, system, etc.
# 3 = variable name to receive reply from TV
local TEMP Result ReturnState
# Declare mathres as reference to argument 3 provided (Bash 4.3 or greater)
declare -n Result=$3 # ERROR: declare: `': not a valid identifier
# Create temporary file in RAM for curl command
TEMP=$(mktemp --tmpdir json.XXXXXXXX)
echo "$1" > "$TEMP"
# -s = silent
Result=$(curl -s -H "Content-Type: application/json; charset=UTF-8" \
-H "X-Auth-PSK: $PWRD" \
--data @"$TEMP" \
http://$IP/sony/"$2")
#echo "Result: $Result" # Remove leading # for debugging
ReturnState="$?"
# TO-DO: check $? and if non-zero pop up dialog with $TEMP contents
rm "$TEMP"
} # cURLit
GetPowerStatus () {
local Reply ReturnState
# Copy and paste JSON strings from Sony website:
# https://pro-bravia.sony.net/develop/integrate/rest-api/spec/service/system/v1_0/getPowerStatus/index.html
JSONstr='{
"method": "getPowerStatus",
"id": 50,
"params": [],
"version": "1.0"
}'
cURLit "$JSONstr" "system" Reply # No $ for Reply variable! pass pointer
ReturnState="$?"
#echo "Reply: $Reply" # Remove leading # for debugging
# Reply: {"result":[{"status":"active"}],"id":50}
# or: {"result":[{"status":"standby"}],"id":50}
# Does 'active' substring exist in TV's reply?
[[ "${Reply#*active}" != "$Reply" ]] && return 0
# TV is turned off
# Might want timer tests to make sure we aren't repeatedly turning off
return 1
} # GetPowerStatus
GetVolume () {
# Copy and paste JSON strings from Sony website:
# https://pro-bravia.sony.net/develop/integrate/rest-api/spec/service/audio/v1_0/getVolumeInformation/index.html
JSONstr='{
"method": "getVolumeInformation",
"id": 33,
"params": [],
"version": "1.0"
}'
# Then pass string to cURL for execution
cURLit "$JSONstr" "audio" Reply
# Sample output:
# Volume:, {"result":[[{"target":"speaker","volume":44,"mute":false,
# "maxVolume":100,"minVolume":0},{"target":"headphone","volume":15,
# "mute":false,"maxVolume":100,"minVolume":0}]],"id":33}
Start="${Reply:41:4}"
Volume=${Start%,*}
return $Volume
} # GetVolume
log () {
logger --id=$$ -t "tvpowered" "$1"
} # log
WaitForSignOn () {
# tvpowered might be loaded during boot. The user name is required
# for sending popup bubble messages and dialogs to screen. We must
# wait until user signs on to get .Xauthority file settings.
# code lifted from eyesome.sh
SpamOn=10 # Causes 10 iterations of 2 second sleep
SpamContext="Login"
TotalWait=0
# Wait for user to sign on then get Xserver access for xrandr calls
UserName=""
while [[ $UserName == "" ]]; do
# Find UserName currently logged in.
UserName="$(who -u | grep -F '(:0)' | head -n 1 | awk '{print $1}')"
[[ $UserName != "" ]] && break
sleep "$SpamLength"
TotalWait=$(( TotalWait + SpamLength ))
done
if [[ $TotalWait != "0" ]] ; then
log "Waited $TotalWait seconds for $UserName to login."
xhost local:root
export XAUTHORITY="/home/$UserName/.Xauthority"
fi
} # WaitForSignOn
LastVolume=0
CurrVolume=0
TenMinuteSpam () {
# If TV not powered up Spam user for 10 minutes that 'tvpowered' is running
# and will shut down / suspend system
WaitForSignOn # Might be called by root during boot before user signed on.
Cnt=0
while : ; do
GetPowerStatus
if [[ "$?" == "0" ]] ; then
log "TV is powered on. 'tvpowered' is now waiting for TV to power off."
break
else
# Spam user every 60 seconds
(( $(( Cnt % 20 )) == 0 )) && \
notify-send --urgency=critical "tvpowered" \
-h string:x-canonical-private-synchronous:startup \
--icon=/usr/share/icons/gnome/48x48/devices/display.png \
"TV not communicating.\n Checking TV again..."
fi
sleep 3
(( Cnt++ ))
done
GetVolume
LastVolume="$?"
notify-send --urgency=critical "tvpowered" \
--icon=/usr/share/icons/gnome/48x48/devices/display.png \
"Fully activated.\n System will $SCTL when TV powered off. Volume: $LastVolume"
return 0
} # TenMinuteSpam
###################################
# MAINLINE #
###################################
Main () {
echo "$0: Initialization. Ensuring TV is powered on before starting."
TenMinuteSpam
echo "$0: Fully activated. Waiting for TV to powered off and then $SCTL."
echo "$0: LastVolume: $LastVolume"
Cnt=0
FirstTime=true
VolumeCnt=0 # TV Remote changed volume, so shrorter sleep
while : ; do
#etherup=$(cat /sys/class/net/e*/carrier) # Returns 1 even disconnected
#wifi_up=$(cat /sys/class/net/w*/carrier)
#if [[ $etherup <> "1" && $wifi_up <> "1" ]] ; then
state=$(nmcli -f STATE -t g) # Network manager takes .5 CPU
if [[ $state == disconnected ]] ; then
# Spam user every 60 * Cot seconds
notify-send --urgency=critical "tvpowered" \
-h string:x-canonical-private-synchronous:startup \
--icon=/usr/share/icons/gnome/48x48/devices/display.png \
"Internet not up.\nChecking Ethernet and/or WiFi state again..."
sleep $((Cnt * 60))
(( Cnt++ ))
continue
else
Cnt=0 # Reset timer for next loop
fi
GetPowerStatus
if [[ "$?" != "0" ]] ; then
state=$(nmcli -f STATE -t g) # Network manager takes .5 CPU
if [[ $state == disconnected ]] ; then
echo "Unexpected disconnect, aborting suspend."
else
log "TV Powered off. 'systemctl $SCTL' being called."
systemctl "$SCTL"
log "System powered back up. Checking if TV powered on. '$0'."
sleep 10 # Give system time to wake from suspend
TenMinuteSpam
/home/rick/sony/pictureoff.sh
fi
fi
GetVolume
CurrVolume="$?"
# echo CurrVolume: $CurrVolume LastVolume: $LastVolume
if [[ "$CurrVolume" != "$LastVolume" ]] ; then
# Ask Ubuntu: https://askubuntu.com/a/871207/307523
notify-send --urgency=critical "tvpowered" \
-h string:x-canonical-private-synchronous:volume \
--icon=/usr/share/icons/gnome/48x48/devices/audio-speakers.png \
"Volume: $CurrVolume"
LastVolume=$CurrVolume
VolumeCnt=10
# TODO: Process VolumeCnt internally in loop instead of larger loop
fi
if [[ $VolumeCnt > 0 ]]; then
(( VolumeCnt-- ))
SleepTime=.01
else
SleepTime=2.5
fi
sleep $SleepTime
# Next iteration
FirstTime=false
done
exit 0
} # Main
Main "$@"
Résumé
J'ai été inspiré par la question de l'OP et je n'avais jamais réalisé à quel point mon processus de suspension de fin de journée était lourd et fastidieux :
- Trouvez l'endroit où se trouve le curseur sur l'un des trois moniteurs.
- Naviguez en haut à droite de n'importe quel moniteur et cliquez à gauche sur le menu Cog.
- Tirez la souris vers le bas pour suspendre l'option
- Cliquez sur suspendre (en faisant attention de ne pas cliquer sur arrêter à côté !).
- Éteindre le téléviseur Sony
- Éteindre le téléviseur Toshiba
tvpowered
a éliminé les fastidieuses étapes 1. à 4.
Bonus - Tour de lumière derrière la télévision
Pour le visionnage de nuit, il y a une lumière derrière le téléviseur. Chaque fois que l'ordinateur portable se met en veille, il éteint d'abord la lumière.
Créer le script /etc/NetworkManager/dispatcher.d/pre-down.d/smartplug_off
et le placer dedans :
#!/bin/bash
# NAME: smartplug_off
# PATH: /etc/NetworkManager/dispatcher.d/pre-down.d
# DESC: Turn off smartplug light power for TV light
# DATE: March 7, 2020.
# CALL: Called by Network Manager before going down. Network manager in turn
# is called by systemd during suspend/hibernate/shutdown
# NOTE: myisp.sh and hs100.sh must be installed for hs100 tp-link power plug.
# https://developer.gnome.org/NetworkManager/stable/NetworkManager.html
PlugName="192.168.0.15"
status=$(hs100.sh -i "$PlugName" check | cut -f2)
if [ $status == "OFF" ] ; then
: # Nothing to do already off
elif [ $status == "ON" ] ; then
hs100.sh -i "$PlugName" off
else
echo Error hs100.sh not responding check connection and IP "$PlugName".
fi