14 | ├── images ├── cec.png ├── back.png ├── front.png ├── zoom.png ├── casting.png ├── homepage.png ├── remote.png ├── notification.png ├── stylesheets.png ├── activity_cursor.png └── profile_wizard.png ├── config_files └── home │ └── tv │ ├── .config │ ├── after_suspend.sh │ ├── before_suspend.sh │ ├── systemd │ │ ├── user │ │ │ ├── casting.service │ │ │ └── dunst.service │ │ └── system │ │ │ └── after-suspend.service │ ├── dunst │ │ └── dunstrc │ └── sway │ │ ├── config │ │ └── base │ ├── earlgreytv │ ├── images │ │ ├── cataas.png │ │ ├── logo.png │ │ ├── wttrin.png │ │ ├── background.webp │ │ ├── shortcut.svg │ │ ├── folder.svg │ │ ├── drtv.svg │ │ ├── drlyd.svg │ │ └── watchlist.svg │ └── earlgreytv.html │ ├── .mozilla │ └── firefox │ │ └── earlgreytv │ │ └── chrome │ │ └── userChrome.css │ └── casting_server.py ├── download_logos.sh ├── .gitignore ├── apply.sh ├── logos.txt ├── sync.sh ├── LICENSE └── README.md /images/cec.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carltheperson/earlgreytv/HEAD/images/cec.png -------------------------------------------------------------------------------- /images/back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carltheperson/earlgreytv/HEAD/images/back.png -------------------------------------------------------------------------------- /images/front.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carltheperson/earlgreytv/HEAD/images/front.png -------------------------------------------------------------------------------- /images/zoom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carltheperson/earlgreytv/HEAD/images/zoom.png -------------------------------------------------------------------------------- /config_files/home/tv/.config/after_suspend.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo 'on 0' | cec-client -s -d 1 -------------------------------------------------------------------------------- /images/casting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carltheperson/earlgreytv/HEAD/images/casting.png -------------------------------------------------------------------------------- /images/homepage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carltheperson/earlgreytv/HEAD/images/homepage.png -------------------------------------------------------------------------------- /images/remote.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carltheperson/earlgreytv/HEAD/images/remote.png -------------------------------------------------------------------------------- /config_files/home/tv/.config/before_suspend.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo 'standby 0' | cec-client -s -d 1 -------------------------------------------------------------------------------- /images/notification.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carltheperson/earlgreytv/HEAD/images/notification.png -------------------------------------------------------------------------------- /images/stylesheets.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carltheperson/earlgreytv/HEAD/images/stylesheets.png -------------------------------------------------------------------------------- /images/activity_cursor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carltheperson/earlgreytv/HEAD/images/activity_cursor.png -------------------------------------------------------------------------------- /images/profile_wizard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carltheperson/earlgreytv/HEAD/images/profile_wizard.png -------------------------------------------------------------------------------- /download_logos.sh: -------------------------------------------------------------------------------- 1 | while IFS=' ' read -r name url || [ -n "$name" ]; do curl -o "config_files/home/tv/earlgreytv/images/${name}" "${url}"; done < logos.txt -------------------------------------------------------------------------------- /config_files/home/tv/earlgreytv/images/cataas.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carltheperson/earlgreytv/HEAD/config_files/home/tv/earlgreytv/images/cataas.png -------------------------------------------------------------------------------- /config_files/home/tv/earlgreytv/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carltheperson/earlgreytv/HEAD/config_files/home/tv/earlgreytv/images/logo.png -------------------------------------------------------------------------------- /config_files/home/tv/earlgreytv/images/wttrin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carltheperson/earlgreytv/HEAD/config_files/home/tv/earlgreytv/images/wttrin.png -------------------------------------------------------------------------------- /config_files/home/tv/earlgreytv/images/background.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carltheperson/earlgreytv/HEAD/config_files/home/tv/earlgreytv/images/background.webp -------------------------------------------------------------------------------- /config_files/home/tv/.config/systemd/user/casting.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Run casting server 3 | 4 | [Service] 5 | ExecStart=/home/tv/casting_server.py 6 | Restart=always 7 | 8 | [Install] 9 | WantedBy=default.target -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Yes I'm scared 2 | config_files/home/tv/earlgreytv/images/yt.png 3 | config_files/home/tv/earlgreytv/images/netflix.jpg 4 | config_files/home/tv/earlgreytv/images/hn.png 5 | config_files/home/tv/earlgreytv/images/max.png 6 | -------------------------------------------------------------------------------- /config_files/home/tv/.config/systemd/system/after-suspend.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=On Resume 3 | After=suspend.target 4 | 5 | [Service] 6 | Type=oneshot 7 | ExecStart=/home/tv/.config/after_suspend.sh 8 | 9 | [Install] 10 | WantedBy=suspend.target -------------------------------------------------------------------------------- /config_files/home/tv/.config/systemd/user/dunst.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Dunst user-level notification daemon 3 | 4 | [Service] 5 | ExecStart=/usr/bin/dunst 6 | Environment="DISPLAY=:0" 7 | Restart=always 8 | 9 | [Install] 10 | WantedBy=default.target -------------------------------------------------------------------------------- /apply.sh: -------------------------------------------------------------------------------- 1 | 2 | ssh tv << 'EOF' 3 | export SWAYSOCK=$(find /run/user/1000/ -name "sway-ipc.1000*.sock" -print -quit) 4 | swaymsg reload 5 | systemctl --user daemon-reload 6 | systemctl --user enable dunst.service casting.service 7 | systemctl --user restart dunst.service casting.service 8 | EOF 9 | 10 | ssh tv -t "su -c 'systemctl link /home/tv/.config/systemd/system/after-suspend.service; systemctl daemon-reload; systemctl enable after-suspend'" -------------------------------------------------------------------------------- /logos.txt: -------------------------------------------------------------------------------- 1 | yt.png https://upload.wikimedia.org/wikipedia/commons/e/ef/Youtube_logo.png 2 | max.png https://play-lh.googleusercontent.com/VODqBhdZXQIkQlcv_A2nAq1gPNO7fwfDlUO3UZcgcMy6jAVx05CSU-vFuVFsr9gFUuo=w240-h480 3 | netflix.jpg https://media1.popsugar-assets.com/files/thumbor/vrEDAyWceXXFdGYA_5jmRwsIZpk=/2500x2500/filters:format_auto():quality(85):extract_cover()/2019/02/01/911/n/1922283/94a9fe8d5c54b1a22eaf19.91477690_.jpg 4 | hn.png https://cdn2.iconfinder.com/data/icons/social-flat-buttons-3/512/hacker_news-512.png -------------------------------------------------------------------------------- /config_files/home/tv/.mozilla/firefox/earlgreytv/chrome/userChrome.css: -------------------------------------------------------------------------------- 1 | /* https://support.mozilla.org/en-US/questions/1324666 */ 2 | #fullscr-toggler { 3 | display: none !important; 4 | } 5 | 6 | /* https://support.mozilla.org/fy-NL/questions/1323490 */ 7 | /* hide 'overlink' messages */ 8 | #statuspanel[type="overLink"] { 9 | opacity: 0 !important; 10 | } 11 | 12 | /* hide all status messages */ 13 | #statuspanel { 14 | opacity: 0 !important; 15 | } 16 | 17 | /* disable fullscreen transition */ 18 | #navigator-toolbox[fullscreenShouldAnimate] { 19 | transition: none !important; 20 | } -------------------------------------------------------------------------------- /sync.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Make sure you have a host with this name in your ~/.ssh/config 4 | SERVER_ALIAS="tv" 5 | 6 | # User 7 | rsync -avz -e ssh --chown=root:root --exclude '.git' "$(pwd)/config_files/" $SERVER_ALIAS:/ 8 | 9 | # Root 10 | # A crazy solution because I want to be promted for the root password and won't create ssh keys for user root 11 | # ROOT_SCRIPT_CONTENT=$(cat "$(pwd)/config_files/usr/lib/systemd/system-sleep/custom-suspend.sh") 12 | # ROOT_SCRIPT_DEST="/usr/lib/systemd/system-sleep/custom-suspend.sh" 13 | # ssh $SERVER_ALIAS -t "echo '$ROOT_SCRIPT_CONTENT' | sudo tee $ROOT_SCRIPT_DEST > /dev/null; sudo chmod +x $ROOT_SCRIPT_DEST" -------------------------------------------------------------------------------- /config_files/home/tv/earlgreytv/images/shortcut.svg: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /config_files/home/tv/.config/dunst/dunstrc: -------------------------------------------------------------------------------- 1 | [global] 2 | width = (0, 1500) 3 | height = 950 4 | origin = top-right 5 | offset = 30x30 6 | progress_bar = true 7 | progress_bar_height = 36 8 | progress_bar_frame_width = 4 9 | progress_bar_width = 600 10 | separator_height = 4 11 | padding = 10 12 | horizontal_padding = 30 13 | frame_width = 6 14 | gap_size = 0 15 | idle_threshold = 30 16 | font = Monospace 24 17 | line_height = 20 18 | markup = full 19 | format = "%s\n%b\n" 20 | alignment = left 21 | vertical_alignment = center 22 | word_wrap = no 23 | ellipsize = end 24 | ignore_newline = no 25 | show_indicators = yes 26 | icon_position = right 27 | corner_radius = 6 28 | max_icon_size = 160 29 | 30 | 31 | [urgency_low] 32 | background = "#ffffff" 33 | foreground = "#2E3440" 34 | frame_color = "#444444" 35 | highlight = "#444444" 36 | timeout = 2 37 | icon = /home/tv/earlgreytv/images/logo.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Carl Riis 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /config_files/home/tv/earlgreytv/images/folder.svg: -------------------------------------------------------------------------------- 1 | 5 | -------------------------------------------------------------------------------- /config_files/home/tv/earlgreytv/images/drtv.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /config_files/home/tv/earlgreytv/images/drlyd.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /config_files/home/tv/earlgreytv/images/watchlist.svg: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /config_files/home/tv/.config/sway/config: -------------------------------------------------------------------------------- 1 | 2 | include /home/tv/.config/sway/base 3 | 4 | # Removing Sway UI stuff 5 | titlebar_border_thickness 0 6 | gaps inner 0 7 | default_border pixel 0 8 | hide_edge_borders both 9 | 10 | # Making cursor big 11 | seat seat0 xcursor_theme "capitaine-cursors" 64 12 | set $gnome-schema org.gnome.desktop.interface 13 | exec_always { 14 | gsettings set $gnome-schema cursor-theme 'capitaine-cursors' 15 | gsettings set $gnome-schema cursor-size 64 16 | } 17 | 18 | # Airmouse 19 | input "9639:292:ZYSD.Ltd_HCY_MIC_RC_Mouse" { 20 | pointer_accel 1 21 | } 22 | 23 | # Screen 24 | output HDMI-A-1 position 0,0 25 | output eDP-1 disable 26 | workspace 1 output HDMI-A-1 27 | 28 | # Audio 29 | bindsym XF86AudioMute exec pactl set-sink-mute @DEFAULT_SINK@ toggle 30 | bindsym XF86AudioLowerVolume exec pactl set-sink-volume @DEFAULT_SINK@ -5% && dunstify -u low -h int:value:"$(pamixer --sink @DEFAULT_SINK@ --get-volume)" "Volume: $(pamixer --sink @DEFAULT_SINK@ --get-volume)" -r 1 31 | bindsym XF86AudioRaiseVolume exec pactl set-sink-volume @DEFAULT_SINK@ +5% && dunstify -u low -h int:value:"$(pamixer --sink @DEFAULT_SINK@ --get-volume)" "Volume: $(pamixer --sink @DEFAULT_SINK@ --get-volume)" -r 1 32 | 33 | # Controls for the Airmouse 34 | bindsym Menu exec wtype -M alt -P home -m alt -p home 35 | bindsym Up exec xdotool click 4 # Not all sites support scroll with arrow keys. This scrolls 36 | bindsym Down exec xdotool click 5 37 | bindsym Left exec ydotool mousemove 1000 500 # This is the middle of the screen for me 38 | bindsym XF86VoiceCommand exec /home/tv/.config/before_suspend.sh && systemctl suspend 39 | 40 | # Run on startup 41 | exec dunstify -u low "Starting system" -r 1 && sleep 5 && dunstctl close-all 42 | exec sleep 5 && swaymsg workspace number 1 && firefox -url file:///home/tv/earlgreytv/earlgreytv.html & sleep 1 && xdotool search --sync --onlyvisible --class "Firefox" windowactivate key F11 43 | exec systemctl --user daemon-reload && systemctl --user enable dunst.service wakeup.service && systemctl --user restart wakeup.service dunst.service -------------------------------------------------------------------------------- /config_files/home/tv/casting_server.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | from http.server import SimpleHTTPRequestHandler, HTTPServer 4 | import hashlib 5 | import subprocess as sp 6 | import os 7 | 8 | env = os.environ.copy() 9 | env["WAYLAND_DISPLAY"] = "wayland-1" 10 | 11 | MACHINE_ID_PATH = "/etc/machine-id" 12 | EXTRA_SECRET_PATH = "/home/tv/extra_secret.txt" 13 | 14 | with open(MACHINE_ID_PATH, 'r') as file: 15 | machine_id = file.read() 16 | extra = "" 17 | if os.path.exists(EXTRA_SECRET_PATH): 18 | with open(EXTRA_SECRET_PATH) as file: 19 | extra = file.read() 20 | secret = hashlib.sha256((machine_id + extra + "earlcasting").encode()).hexdigest() 21 | print("Secret for checksum:", secret) 22 | 23 | def clean_url(url): 24 | # I want no funny business in my URLs 25 | allowed_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789:-_/#&?=.," 26 | for char in url: 27 | if char not in allowed_chars: 28 | raise ValueError(f"Invalid character found: {char}") 29 | if len(url) > 300: 30 | raise ValueError(f"Too long URL") 31 | return url 32 | 33 | 34 | class CastingHandler(SimpleHTTPRequestHandler): 35 | def do_GET(self): 36 | checksum = self.path[1: (64 + 1)] 37 | url = self.path[(64 + 1):] 38 | 39 | expected_checksum = hashlib.sha256((secret + url).encode()).hexdigest() 40 | if checksum != expected_checksum: 41 | self.send_response(400) 42 | return 43 | url = clean_url(url) 44 | 45 | # Most of these delays and commands aren't needed. 46 | # I have them because rare cases without delay don't work. 47 | command = [ 48 | 'wtype', 49 | '-M', 'ctrl', 50 | '-P', 'l', 51 | '-m', 'ctrl', 52 | '-p', 'l', 53 | '-s', '1000', 54 | '-M', 'ctrl', 55 | '-P', 'l', 56 | '-m', 'ctrl', 57 | '-p', 'l', 58 | '-s', '1000', 59 | url, 60 | '-s', '1000', 61 | '-k', 'KP_Enter', 62 | '-s', '2500', 63 | '-k', 'Escape', 64 | '-s', '1000', 65 | '-M', 'ctrl', 66 | '-P', 'r', 67 | '-m', 'ctrl', 68 | '-p', 'r', 69 | ] 70 | try: 71 | result = sp.run(command, check=True, text=True, capture_output=True, env=env) 72 | print(result.stdout) 73 | print(result.stderr) 74 | except Exception as e: 75 | print(e.stdout) 76 | print(e.stderr) 77 | self.send_response(500) 78 | return 79 | 80 | 81 | self.send_response(200) 82 | self.send_header('Content-type', 'text/plain') 83 | self.end_headers() 84 | self.wfile.write("ok".encode()) 85 | 86 | 87 | port = 3000 88 | httpd = HTTPServer(('0.0.0.0', port), CastingHandler) 89 | print("Server started on port", port) 90 | httpd.serve_forever() -------------------------------------------------------------------------------- /config_files/home/tv/.config/sway/base: -------------------------------------------------------------------------------- 1 | ### Variables 2 | 3 | set $mod Mod4 4 | set $left h 5 | set $down j 6 | set $up k 7 | set $right l 8 | set $term gnome-terminal 9 | set $menu dmenu_path | dmenu | xargs swaymsg exec -- 10 | 11 | ### System configs 12 | 13 | include /etc/sway/config-vars.d/* 14 | include /etc/sway/config.d/* 15 | 16 | ### Key bindings 17 | # 18 | # Basics: 19 | # 20 | # Start a terminal 21 | bindsym $mod+Return exec $term 22 | # Kill focused window 23 | bindsym $mod+Shift+q kill 24 | # Start your launcher 25 | bindsym $mod+d exec $menu 26 | # Drag floating windows by holding down $mod and left mouse button. 27 | floating_modifier $mod normal 28 | # Reload the configuration file 29 | bindsym $mod+Shift+c reload 30 | # Exit sway (logs you out of your Wayland session) 31 | bindsym $mod+Shift+e exec swaynag -t warning -m 'You pressed the exit shortcut. Do you really want to exit sway? This will end your Wayland session.' -B 'Yes, exit sway' 'swaymsg exit' 32 | # 33 | # Moving around: 34 | # 35 | # Move your focus around 36 | bindsym $mod+$left focus left 37 | bindsym $mod+$down focus down 38 | bindsym $mod+$up focus up 39 | bindsym $mod+$right focus right 40 | # Or use $mod+[up|down|left|right] 41 | bindsym $mod+Left focus left 42 | bindsym $mod+Down focus down 43 | bindsym $mod+Up focus up 44 | bindsym $mod+Right focus right 45 | 46 | # Move the focused window with the same, but add Shift 47 | bindsym $mod+Shift+$left move left 48 | bindsym $mod+Shift+$down move down 49 | bindsym $mod+Shift+$up move up 50 | bindsym $mod+Shift+$right move right 51 | # Ditto, with arrow keys 52 | bindsym $mod+Shift+Left move left 53 | bindsym $mod+Shift+Down move down 54 | bindsym $mod+Shift+Up move up 55 | bindsym $mod+Shift+Right move right 56 | # 57 | # Workspaces: 58 | # 59 | # Switch to workspace 60 | bindsym $mod+1 workspace number 1 61 | bindsym $mod+2 workspace number 2 62 | bindsym $mod+3 workspace number 3 63 | bindsym $mod+4 workspace number 4 64 | bindsym $mod+5 workspace number 5 65 | bindsym $mod+6 workspace number 6 66 | bindsym $mod+7 workspace number 7 67 | bindsym $mod+8 workspace number 8 68 | bindsym $mod+9 workspace number 9 69 | bindsym $mod+0 workspace number 10 70 | # Move focused container to workspace 71 | bindsym $mod+Shift+1 move container to workspace number 1 72 | bindsym $mod+Shift+2 move container to workspace number 2 73 | bindsym $mod+Shift+3 move container to workspace number 3 74 | bindsym $mod+Shift+4 move container to workspace number 4 75 | bindsym $mod+Shift+5 move container to workspace number 5 76 | bindsym $mod+Shift+6 move container to workspace number 6 77 | bindsym $mod+Shift+7 move container to workspace number 7 78 | bindsym $mod+Shift+8 move container to workspace number 8 79 | bindsym $mod+Shift+9 move container to workspace number 9 80 | bindsym $mod+Shift+0 move container to workspace number 10 81 | # Layout stuff: 82 | bindsym $mod+v split toggle 83 | bindsym $mod+s layout stacking 84 | bindsym $mod+w layout tabbed 85 | bindsym $mod+e layout toggle split 86 | bindsym $mod+f fullscreen 87 | bindsym $mod+Shift+space floating toggle 88 | bindsym $mod+space focus mode_toggle 89 | bindsym $mod+a focus parent 90 | # 91 | # Resizing containers: 92 | # 93 | mode "resize" { 94 | bindsym $left resize shrink width 10px 95 | bindsym $down resize grow height 10px 96 | bindsym $up resize shrink height 10px 97 | bindsym $right resize grow width 10px 98 | bindsym Left resize shrink width 10px 99 | bindsym Down resize grow height 10px 100 | bindsym Up resize shrink height 10px 101 | bindsym Right resize grow width 10px 102 | bindsym Return mode "default" 103 | bindsym Escape mode "default" 104 | } 105 | bindsym $mod+r mode "resize" -------------------------------------------------------------------------------- /config_files/home/tv/earlgreytv/earlgreytv.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 | 7 |
14 | This repository explains how I run my smart TV setup EarlGreyTV.
3 | 4 |
6 |