├── 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 | 2 | 3 | 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 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /config_files/home/tv/earlgreytv/images/drtv.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | 9 | 10 | 12 | 13 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /config_files/home/tv/earlgreytv/images/drlyd.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /config_files/home/tv/earlgreytv/images/watchlist.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 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 | Earl Grey TV 8 | 9 | 10 | 11 |
12 |
13 | 14 |

EarlGreyTV

15 |
16 |
17 |
18 | 51 | 174 | 175 | 176 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

EarlGreyTV

2 |

This repository explains how I run my smart TV setup EarlGreyTV.

3 | 4 |
5 | TV 6 |
7 | 8 |
9 | 10 | > [!TIP] 11 | > I wrote a [blog post](https://carltheperson.com/posts/earlgreytv) about this too. And recorded a quick [YouTube video](https://www.youtube.com/watch?v=836VlTPxnFc). 12 | 13 | Summary: I use a Linux-running laptop (which I spilled tea on) to render a browser in constant fullscreen. I then have a homepage with a smart TV-like look. The "apps" are simply links to websites. 14 | 15 | Warning: This is mostly a collection of my notes. I haven't tested if these steps work perfectly on a fresh install. You should be able to debug things yourself if anything goes wrong. 16 | 17 | I encourage you to customize this as much as possible. Please make it *your* perfect setup, not mine. 18 | 19 | 20 | # Configuring and Running Locally 21 | 22 | The main "homepage" of EarlGreyTV is the `earlgreytv.html` file found in `config_files/home/tv/earlgreytv/`. This is where you tweak the homepage appearance and, importantly, configure the "apps". 23 | 24 | In this repo, I have my own example with my apps. Maybe I'm paranoid, but I'm too scared of copyright to have logo files hosted here. I made this script so you can download them yourself: 25 | 26 | ```sh 27 | # Download the logos in the logos.txt file 28 | # E.g Netflix logo 29 | ./download_logos.sh 30 | ``` 31 | 32 | Now you should be able to open the HTML file in a browser. e.g.: 33 | 34 | ``` 35 | firefox config_files/home/tv/earlgreytv/earlgreytv.html 36 | ``` 37 | 38 | ## Adding Your Own Apps 39 | 40 | The apps are defined in the JavaScript portion of the `earlgreytv.html` file. Specify your app name, URL, and logo (which should exist in the `images/` folder). 41 | 42 | ```js 43 | const APPS = [ 44 | { name: "App name", url: "https://example.org", image: "app.png" }, 45 | /* ... */ 46 | ] 47 | ``` 48 | Tip: You can add a shortcut icon in the top-right corner by specifying `shortcut: true`. 49 | 50 | # Hardware Setup Notes 51 | 52 | ## TV 53 | 54 | Get yourself something you can use as a screen for your setup. Ideally, it should be as dumb as possible - simply showing the output of the computer you plug into it. 55 | 56 | I couldn't find a dumb TV, so I got a smart TV and used settings to dumb it down a bit. 57 | 58 | I got a `SAMSUNG 55" 4K UHD LED TV TU55CU7105KXXC` (3840x2160), and connected it with HDMI. 59 | 60 | I did this setting change: 61 | 62 | - \> All Settings 63 | - \> General & Privacy 64 | - \> Start Screen Options 65 | - "Autorun Last App" 66 | 67 | My HDMI connection is considered an "App". 68 | 69 | Side note: Some TVs have hidden menus that can allow you to dumb them down even more, [example](https://youtu.be/LGngUs30dh0). The regular settings seemed to work okay for me, though. 70 | 71 | ## Computer 72 | 73 | You need a computer to power your setup. An old used laptop is perfect. 74 | 75 | I used a 5-year-old Lenovo laptop with a broken keyboard (because I spilled Earl Grey tea into it). 76 | 77 | ![Back of TV with computer](images/back.png) 78 | 79 | ## Remote 80 | 81 | Since this setup is a regular desktop setup disguised as a smart TV, you need something to be your mouse. You also need a keyboard too (although maybe you can trigger an on-screen keyboard). 82 | 83 | I chose the `WECHIP W3 2.4G 3-i-1 2.4G Air Mouse`. It uses a small gyroscope as input for the mouse and has a small keyboard on the back. It's good but a bit flimsy. 84 | 85 | ![Remote front and back](images/remote.png) 86 | 87 | ## Optional Extras 88 | 89 | ### HDMI USB CEC Adapter 90 | 91 | I use [CEC](https://en.wikipedia.org/wiki/Consumer_Electronics_Control) to allow my computer to suspend my TV. I need an adapter because the laptop doesn't support the CEC protocol itself. 92 | 93 | I use the `Pulse Eight USB-CEC Adapter`. 94 | 95 | ![CEC adapter](images/cec.png) 96 | 97 | ### USB Extender 98 | 99 | For your mouse, you'll likely get a very small USB receiver, [example](https://upload.wikimedia.org/wikipedia/commons/7/7d/Logitech_unifying_receiver.jpg). Depending on where you place your laptop, you might need to extend the receiver. 100 | 101 | I use a generic USB extender, [example](https://commons.wikimedia.org/wiki/File:USB_extender_cable.jpg). 102 | 103 | # Software Setup Notes 104 | 105 | ## Operating System 106 | 107 | I went with [Debian Linux](https://www.debian.org/). I named my user `tv`. 108 | 109 | ## Desktop Environment 110 | 111 | I chose [Sway](https://swaywm.org/). I needed something I could easily configure with a config file and that would be highly minimal. 112 | 113 | In retrospect, maybe regular [i3](https://i3wm.org/) would have been better. The automation tools that exist for the [X Window System](https://en.wikipedia.org/wiki/X_Window_System) are more documented. 114 | 115 | Run: 116 | 117 | ```sh 118 | sudo apt install sway -y 119 | ``` 120 | 121 | Then log out of GNOME and log into Sway. 122 | 123 | ## Packages 124 | 125 | These are the packages I could gather from my notes. There may be a couple I forgot, and some that are not needed. 126 | 127 | ```Sh 128 | sudo apt install rsync pulseaudio pulseaudio-utils playerctl pamixer xdotool ydotool input-util xbindkeys wtype udev cec-utils -y 129 | ``` 130 | 131 | ## SSH notes 132 | 133 | The next steps depend on an SSH connection to your TV. I recommend adding your TV computer to your `~/.ssh/config`. This is how I connect to my TV: 134 | 135 | ```sh 136 | ssh tv 137 | ``` 138 | 139 | **Tip: Sway Commands Over SSH:** 140 | 141 | Interacting with Sway over SSH might give you an error like this: `swaymsg/main.c:419] Unable to retrieve socket path`. 142 | 143 | This is my fix: 144 | 145 | ```sh 146 | export SWAYSOCK=$(find /run/user/1000/ -name "sway-ipc.1000*.sock" -print -quit) 147 | # Now you should be able to e.g. reload Sway: 148 | swaymsg reload 149 | ``` 150 | 151 | ## Firefox Manual Settings 152 | 153 | ### Set Zoom 154 | 155 | You likely need some extra zoom on the website you visit. How much depends on how big your screen is. 200% looks good on my TV. 156 | 157 | Navigate to `about:preferences`. 158 | 159 | Set "Default zoom" to 200%. 160 | 161 | ![Zoom](images/zoom.png) 162 | 163 | ### Create Profile 164 | 165 | The profile is used for a predictable path to place a config file. 166 | 167 | Navigate to `about:profiles`. 168 | 169 | Make sure to click "Choose Folder..." and set it to `/home/tv/.mozilla/firefox/earlgreytv`. 170 | 171 | ![profile wizard](images/profile_wizard.png) 172 | 173 | "Default Profile" should be set to "Yes" 174 | 175 | ### Set Homepage 176 | 177 | Navigate to `about:preferences#home`. 178 | 179 | For the homepage, pick "Custom URLs...". Then set it to `file:///home/tv/earlgreytv/earlgreytv.html`. 180 | 181 | ![home page settings](images/homepage.png) 182 | 183 | ### Enable Stylesheets 184 | 185 | Navigate to `about:config`, and set `toolkit.legacyUserProfileCustomizations.stylesheets` to `true`. 186 | 187 | ![stylesheets option](images/stylesheets.png) 188 | 189 | ### Enable Activity Cursor 190 | 191 | This will indicate on the cursor if the page is loading. 192 | 193 | Navigate to `about:config`, and set `ui.use_activity_cursor` to `true`. 194 | 195 | ![activity cursor](images/activity_cursor.png) 196 | 197 | 198 | ## Sync Configs 199 | 200 | Run `./sync.sh` in this directory, to sync files needed for the EarlgreyTV setup. 201 | 202 | ```sh 203 | # Make sure you have rsync 204 | sudo apt install rsync -y 205 | # Sync files 206 | ./sync.sh 207 | ``` 208 | 209 | ## Apply Configs 210 | 211 | Run `./apply.sh` to restart any services that use config files. It doesn't restart Firefox. 212 | 213 | ```sh 214 | ./apply.sh 215 | ``` 216 | 217 | ## Casting via iPhone Shortcut 218 | 219 | EarlGreyTV implements a very simple casting mechanism. A server will receive a URL from an HTTP request, and paste it into the Firefox address bar. The script for the casting server can be found here: `config_files/home/tv/casting_server.py`. 220 | 221 | To make calls to the casting server easily from my iPhone, I created a shortcut that shows up in the Sharing Sheet when sharing a link from any app. 222 | 223 | If you want to recreate the shortcut yourself, you can refer to these screenshots: 224 | 225 | ![Casting shortcut](images/casting.png) 226 | 227 | 228 | ## Further Notes 229 | 230 | - I ran into some issues with `ydotool`. This [solution](https://github.com/ReimuNotMoe/ydotool/issues/25#issuecomment-535842993) helped. 231 | - Your TV screen might not be primary on the initial Debian login screen. [This](https://askubuntu.com/a/1467005) can correct that. 232 | 233 | # Thanks To 234 | 235 | - Cup drawing logo: [BorogoveLM](https://www.deviantart.com/borogovelm/gallery), https://www.deviantart.com/borogovelm/art/Not-My-Cup-Of-Tea-297646401 236 | - Background drawing: [Rebecca](https://krita-artists.org/u/rebecca/summary), https://krita-artists.org/t/drawing-with-water-soluble-ink/3069 237 | - Web SVG icon by HASTA ICON from Noun Project (CC BY 3.0) 238 | - Folder SVG icon by Waldiz Production from Noun Project (CC BY 3.0) 239 | - Document SVG icon by Adiyogi from Noun Project (CC BY 3.0) 240 | - Send/shortcut SVG icon by HideMaru from Noun Project (CC BY 3.0) 241 | - [Kevin Balicot](https://github.com/kevinbalicot) for making https://cataas.com (used as example "app") 242 | - [Igor Chubin](https://github.com/chubin) for making https://wttr.in (used as example "app") 243 | 244 | # License 245 | 246 | This project is licensed under the MIT License - see the [LICENSE](./LICENSE) file for details --------------------------------------------------------------------------------