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 princial:
// 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, "[email protected]", "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!