Transformei o meu Fire TV Stick em um servidor WOL com Go
Motivação
Recentemente eu fiquei curioso sobre como a internet das coisas funciona e como eu desenvolveria algumas soluções, foi ai que pensei em criar uma forma de ligar meu PC de fora de casa. Sim eu sei, a Alexa possui essa skill (mesmo que seja bugada), mas e se fosse para você criar, como o faria?
Otimização de recursos de hardware e software
Eu comecei a revisar as possibilidades, e então eu encontrei 3 possibilidades iniciais:
- Fire TV Stick (que utilizo na minha TV)
- Notebook antigo
- Roteador com OpenWrt
Então eu comecei a investigar a possibilidade de utilizar o Fire TV.
Eu decidi utilizar o Go porque eu já estava arranjando uma desculpa para testar a linguagem já faz um tempo, a minha pergunta após o uso dela foi: por que não utilizei antes?
Eu utilizo muito node.js e .NET no dia a dia, mas ambos em comparação ao Go perdem em um ponto importante: footprint enxuto.
- .NET
- Footprint pode ser pequeno para .NET Framework 4.8, mas aqui é Windows Based
- Há problemas de compilação cruzada e seu footprint com .NET 8, mas não vou adentrar muito nesse assunto pois não é o objetivo
- Node.js
- Lindo uso no dia a dia, funciona muito bem no Termux com arch ARM, inclusive utilizo muito no meu celular com Termux também
- Uso de memória excessivo para pequenas operações
- Footprint grande pois depende do binário do Node.js
Implementação
Primeiramente, gostaria de reconsiderar que o Fire TV Stick nunca desliga de fato, ele gerencia os app's para controle de memória e de energia, mas sempre se mantém ativo. Você mesmo pode fazer este teste realizando o ping do ip na rede, caso possua um.
Estrutura da solução
- Termux no FireTV (leve e lhe dá acesso ao Linux capado)
- Habilitar via terminal:
$ termux-wake-lock
- Para forçar com que o Termux não perca prioridade na CPU (isso não afeta o uso do FireTV)
- Projeto em GO
- Inicia um server http para realizar as operações remotas
- Teste de ping (não vi tanta utilidade, então eu removi)
- Chamada de execução do processo de WOL para ligar o computador
- Aqui é possível implementar via biblioteca no GO, mas ai me bateu a preguiça pois fazer com a chamada do serviço (binário) estava mais na mão e funciona igual.
- Faz a chamada para um SSH client with reverse tunneling com NGROK
- Faz o espelhamento da porta do server http para o Ngrok
- Aqui eu tive que incluir na plataforma do Ngrok a chave pública gerada que você utiliza para conexões SSH
- Inicia um server http para realizar as operações remotas
- Habilitar via terminal:
- Ativação do Wake on Lan no computador
- AnyDesk para acesso remoto (esse me atende bem, mas com o pc ligado as possibilidades exponenciam de uma maneira muito maior..)
Código
Controle da execução principal:
// main.go
package main
import (
"fmt"
"homec/connection"
"homec/handlers"
"net/http"
"os"
)
func main() {
go connection.LocalhostRun()
portService := fmt.Sprintf("%d", connection.PORT_SERVICE)
http.HandleFunc("/wake-pc", handlers.HandlerWakePc)
http.HandleFunc("/ping-pc", handlers.HandlerPingPc)
http.HandleFunc("/test", handlers.HandlerTest)
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/test", http.StatusSeeOther)
})
if err := http.ListenAndServe("localhost:"+portService, nil); err != nil {
fmt.Println("Erro ao iniciar o servidor:", err)
os.Exit(1)
}
}
A comunicação com o serviço do Ngrok:
// connection/localhostrun.go
package connection
import (
"bufio"
"context"
"fmt"
"os/exec"
"strings"
"time"
)
var CountManyConnects int = 0
var PORT_SERVICE int = 8042
func LocalhostRun() {
for {
portService := fmt.Sprintf("%d", PORT_SERVICE)
cmd := exec.Command("ssh", "-R", "0:localhost:"+portService, "v2@connect.ngrok-agent.com", "http")
connected := false
stdout, _ := cmd.StdoutPipe()
// stderr, _ := cmd.StderrPipe()
CountManyConnects++;
if err := cmd.Start(); err != nil {
fmt.Println("Erro ao iniciar o comando:", err)
connected = false
} else {
connected = true
}
cmd.Wait()
if connected {
fmt.Println("SSH encerrado...")
time.Sleep(1 * time.Second)
} else {
fmt.Println("Nova tentativa SSH encerrada...")
time.Sleep(2 * time.Second)
}
}
}
A implementação com o pacote wol do Termux no endpoint /wake-pc (instalação com pkg install wol
):
// handlers/wake-pc.go
package handlers
import (
"net/http"
"os/exec"
"strings"
)
func HandlerWakePc(w http.ResponseWriter, r *http.Request) {
if r.Method != "GET" {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
cmd := exec.Command("wol", "00:00:00:00:00:00")
output, err := cmd.CombinedOutput()
if err != nil {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte("It was not possible to wake the device"))
return
}
if !strings.Contains(string(output), "Waking up") {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte("It was not possible to wake the device"))
return
}
w.WriteHeader(http.StatusOK)
}
Abaixo o handler do endpoint /test
, coloquei poucas informações somente para testar se o serviço está comunicando:
// handlers/test.go
package handlers
import (
"fmt"
"homec/connection"
"net/http"
)
func HandlerTest(w http.ResponseWriter, r *http.Request) {
if r.Method != "GET" {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
w.WriteHeader(http.StatusOK)
w.Write([]byte("Ok!\n"))
w.Write([]byte(fmt.Sprintf("CountManyConnects: %d", connection.CountManyConnects)))
}
Ressalvas importantes
Problemas na compilação cruzada
Utilizando o Windows 11 não foi tão difícil compilar para arm7 (arquitetura do FireTV), o problema foi que de alguma forma o comportamento não foi o mesmo. Tive problemas com a parte de comunicação e DNS, nada novo aqui, já encontrei esse problemas outras vezes. O problema é que a implementação do Termux não é exatamente igual a um Linux padrão, o projeto possui diversas modificações para tornar o projeto possível.
Qual foi a solução? compilei o projeto diretamente no FireTV.
Um pouco de SSH, SCP e poucos comandos depois o projeto já estava pronto.
O gadget não foi projetado para uso intenso
Além de ocupar pouco espaço, o Fire TV Stick possui pouca memória e processamento, então aqui a escolha do Go foi primordial para a efetividade do projeto.
É importante evitar disparar muitos comandos ou intensificar o uso da CPU e RAM para não crashar o app:
~ $ free -h
total used free shared buff/cache available
Mem: 899Mi 548Mi 48Mi 0.0Ki 302Mi 269Mi
Swap: 383Mi 251Mi 132Mi
Configuração inicial do Termux no Fire TV Stick
Eu precisei baixar um app Downloader, habilitar nas configurações para que esse app instale aplicativos e então, instalar o Termux.
Com o Termux em mãos, eu precisei instalar o OpenSSH com SSHD, para iniciar o SSHD (é possível iniciar junto com o terminal para evitar a input do comando caso precise reiniciar).
É simplesmente inviável utilizar terminal pelo controle da TV kkkk
Não contempla quedas de energia
Infelizmente não consegui configurar o Termux:Boot no Fire TV, mas creio que isso seja possível. Até lá, toda vez que ocorre quedas de energia o serviço fica fora pois o Fire TV não faz o Reboot (pelo menos o meu não faz).
Mas há uma possível solução, há algumas placas mães que tem a possibilidade de na queda de energia ela automaticamente bootar, é uma opção, mesmo assim não resolve o problema da inicialização do serviço
Conclusão
Foi um bom teste para avaliar Go e os limites do Fire TV Stick.
Agora eu quero testar o Termux em um SmartWatch com Android, embora as reclamações de uso de bateria nesse gadget, vale a pena testar o que dá para fazer nele kkk Preciso de um pouco de estudo para entender se isso é possível.
Outro ponto importante é que já subestimei o Termux, mas a grande verdade é que você não precisa de acesso Root para a maior parte das coisas, pelo menos eu não precisei.
Algumas outras implementações que consegui com o Termux:
- Syncar o Obsidian do celular com multiplos devices
- Acessar o shell do celular de qualquer lugar com Ngrok (e consome pouca internet)
- Utilizar o code-server (VsCode para servers) para editar projetos no celular (somente por curiosidade, mas é possível fazer)
Tem mais coisa que não lembro agora, é isso!
Fera... d+! porém eu particularmente não usaria a fire tv para isso. Usaria uma esp32 com o protocolo mqtt... ou até mesmo uma esp8266 ligada diretamente no jumper de power switch da placa mãe...
Muito bacana usar Go nesse cenário.
Caraca, muito interessante. Parabéns pelo projeto. Sou iniciante ainda na computação, então não compreendi profundamente vários ponto, todavia, achei-os incríveis. Recentemente eu utilizei o Temux para controlar a minha IPTV via ADB, achei bem legal. Eu poderia fazer algumas automações interessantes com isso.
muito bom meu mano! também tô pensando em usar o termux pra configurar uns relógios digitais aqui
o termux, mesmo que seja limitado em alguns aspectos, com criatividade muitos projetos podem se beneficiar do uso do mesmo; eu já utilizei o termux no meu celular para instalar o LunarVim e escrever alguns scripts durante as aulas, era divertido, só ligar um teclado e começar a codar.
seu projeto me fez lembrar da existência de um raspberry model b que tá guardado na gaveta do meu quarto, estou pensando no que fazer com ele, recentemente cancelei um serviço de armazenamento em nuvem e essa era a desculpa que eu precisava para começar a brincar com ele, ainda tô pesquisando a respeito desse assunto e separando os dispositivos de armazenamento que vou utilizar, acredito que será um projeto bem divertido.
Caraca! não conhecia Ngrok haha.
Seu artigo foi muito bom pra mim. Tenho um projeto de automação domestica e o Ngrok pode servir para alguma coisa.. sem falar que estou estudando o Go pra fazer isso e seu projeto deu uma inspirada nas minhas ideias.
Nossa muito obrigado! Estava procurando uma forma de ligar meu servidor NAS remotamente e você caiu como um anjo hahahah.
Interessante!!