Compare commits

...

27 Commits

Author SHA1 Message Date
1dc6ce6e5e fix: type conversion 2023-12-27 07:42:38 +01:00
69389d030b refactor: parameterize submit delay 2023-12-27 07:38:48 +01:00
cc7a59c109 feat triple c lick on save button archer 2023-07-04 16:42:46 +02:00
62bda54d98 feat: adjust waiting times 2023-07-02 23:56:23 +02:00
0933174ea1 fix bug 2023-07-02 23:28:38 +02:00
bf700eeeb3 feat add log date 2023-07-02 23:13:30 +02:00
bbc94e0375 polish logs 2023-07-02 23:08:07 +02:00
ad8ed58366 feat increase wait 2023-07-02 22:16:17 +02:00
db9f7495aa increase wait time after login submit 2023-07-02 22:14:31 +02:00
0ae922e58d feat: change jquery to pure js 2023-07-02 21:28:53 +02:00
fc35559ab8 feat wait a little more 2023-07-02 20:16:34 +02:00
c09933767b feat: archer add print message 2023-07-02 20:15:54 +02:00
cc9c9354bc feat: add archer ax50 2023-07-02 19:50:59 +02:00
55ed1c1b36 increase wait done 50 ms 2023-06-03 17:09:38 +02:00
8032850b0b fix nucom switcher, now it is working fine 2023-06-03 01:08:16 +02:00
5d5d90d3f6 feat: handle multiple providers 2023-06-02 03:24:12 +02:00
7742a70f6c feat: add close page 2023-03-25 08:19:57 +01:00
857aec79e3 fix: remove test line 2023-03-07 23:08:24 +01:00
10c80effce feat: add build for raspi 2023-03-07 23:07:29 +01:00
b86fc071ec fix bug missing " 2023-03-07 23:07:20 +01:00
7b06c1b782 feat: refactor code 2023-03-07 22:57:30 +01:00
e626e79882 feat: handle retries on switch wifi 2023-03-07 22:50:07 +01:00
c1b6714d57 feat: add README 2023-03-04 22:00:30 +01:00
fb48c6a9cc feat: add missing Bin on config 2023-03-04 21:56:47 +01:00
d69bfe4590 feat: browser binary path as parameter 2023-03-02 20:55:10 +01:00
abf360a126 fix: load .env file when ENV is not prod 2023-03-02 18:39:53 +01:00
cc3b454262 feat: add build for raspi and freebsd 2023-03-02 18:39:30 +01:00
10 changed files with 460 additions and 46 deletions

4
.gitignore vendored
View File

@@ -2,4 +2,6 @@
coverage/*
test_monitor.txt
bin
.vscode
.vscode
.notes
bin

View File

@@ -16,4 +16,14 @@ test-coverage:
build:
rm -rf ${BINARY_DIR}
mkdir ${BINARY_DIR}
env GOOS=linux CGO_ENABLED=0 GOARCH=amd64 go build -o ./${BINARY_DIR}/${BINARY_NAME} main.go
env GOOS=linux CGO_ENABLED=0 GOARCH=amd64 go build -o ./${BINARY_DIR}/${BINARY_NAME} main.go
build_raspi:
rm -rf ${BINARY_DIR}
mkdir ${BINARY_DIR}
env GOOS=linux CGO_ENABLED=0 GOARCH=arm64 go build -o ./${BINARY_DIR}/${BINARY_NAME} main.go
build_freebsd:
rm -rf ${BINARY_DIR}
mkdir ${BINARY_DIR}
env GOOS=freebsd CGO_ENABLED=0 GOARCH=arm64 ENV=prod go build -o ./${BINARY_DIR}/${BINARY_NAME} main.go
list_build_types:
go tool dist list | grep arm

24
README.md Normal file
View File

@@ -0,0 +1,24 @@
# go-wifi-switcher
## Huawei B2368-66
This software will turn wifi on/off based on current state.
Example: If wifi is **on** then it will turn **off**.
## How does it works
It uses web-scrapping thanks to amazing **[go-rod web scrapper](github.com/go-rod/rod)**
## How to use
You must build it using the `go build` command that better fits with the OS where this software will be consumed.
I'm using it on debian OS so I've created some build commands inside **[Makefile](https://gitea.urkob.com/urko/go-wifi-switcher/src/branch/main/Makefile)**
You can build and copy into your OS in `/usr/local/bin` for example, then create a cron task to automatically run this binary.
## Result
You can configure to switch **off** during **night** and **on** at **morning**. So you can get free of radiation while you sleep and don't have to manually switch on :)
## How to install and how to use
TODO
## Tests
TODO

View File

@@ -8,10 +8,12 @@ import (
)
type Config struct {
Page string `required:"true" split_words:"true"`
AdminUser string `required:"true" split_words:"true"`
Password string `required:"true" split_words:"true"`
LogFile bool `required:"true" split_words:"true"`
Page string `required:"true" split_words:"true"`
AdminUser string `required:"true" split_words:"true"`
Password string `required:"true" split_words:"true"`
LogFile bool `required:"true" split_words:"true"`
Bin string `required:"true" split_words:"true"`
SubmitDelay int `default:"7500" required:"false" split_words:"true"`
}
func NewConfig(envFilePath string) *Config {

View File

@@ -0,0 +1,108 @@
package providers
import (
"fmt"
"log"
"time"
"gitea.urkob.com/urko/go-wifi-switcher/pkg/provider"
"github.com/go-rod/rod"
)
type ArcherAx50 struct {
passwordID string
loginButtonID string
wirelessTab string
chkWifi2gID string
saveButtonID string
page *rod.Page
}
func NewArcherAx50(page *rod.Page) ArcherAx50 {
return ArcherAx50{
passwordID: "#login-password",
loginButtonID: "#login-btn",
wirelessTab: "wireless",
chkWifi2gID: "chk_enable_wireless_24g",
saveButtonID: "#total_save",
page: page,
}
}
func (p ArcherAx50) Login(user, pass string) error {
time.Sleep(time.Millisecond * 5550)
p.page.MustEvaluate(&rod.EvalOptions{
JS: `() => {
document.querySelectorAll("input.password-text").forEach((v, i) => {
v.click();
v.focus();
v.value="` + pass + `";
})
return '';
}`,
})
time.Sleep(time.Millisecond * 550)
log.Println(p.passwordID, "DONE")
login, err := p.page.Element(p.loginButtonID)
if err != nil {
return fmt.Errorf("page.Element %s: %s", p.loginButtonID, err)
}
login.MustClick()
log.Println(p.loginButtonID, "DONE")
time.Sleep(time.Millisecond * 4550)
return nil
}
func (p ArcherAx50) SwitchWIFI(cfg provider.SwitchConfig) error {
pageURL := p.page.MustInfo().URL
log.Println("p.page.MustInfo().URL", pageURL)
wirelessTab, err := p.page.Element(`[name="` + p.wirelessTab + `"]`)
if err != nil {
return fmt.Errorf("page.Element %s: %s", p.wirelessTab, err)
}
wirelessTab.MustClick()
log.Println(p.wirelessTab, "DONE")
time.Sleep(time.Millisecond * 1850)
checked := p.page.MustEvaluate(&rod.EvalOptions{
JS: `() => {
return document.getElementById('` + p.chkWifi2gID + `').checked;
}`,
})
isChecked := checked.Value.Bool()
log.Println(getCheckedMessage(isChecked))
p.page.MustEvaluate(&rod.EvalOptions{
JS: `() => {
document.getElementById('` + p.chkWifi2gID + `').checked=` + fmt.Sprint(!isChecked) + `;
return '';
}`,
})
time.Sleep(time.Millisecond * 550)
log.Println(p.chkWifi2gID, "DONE")
saveButton, err := p.page.Element(p.saveButtonID)
if err != nil {
return fmt.Errorf("page.Element %s: %s", p.saveButtonID, err)
}
saveButton.MustClick()
saveButton.MustClick()
saveButton.MustClick()
log.Println(p.saveButtonID, "DONE")
time.Sleep(time.Millisecond * time.Duration(cfg.SubmitDelay))
return nil
}
func getCheckedMessage(isChecked bool) string {
if isChecked {
return "Time to sleep, let's switch off"
}
return "Time to laborate, let's switch WIFI on"
}

View File

@@ -0,0 +1,113 @@
package providers
import (
"fmt"
"log"
"time"
"gitea.urkob.com/urko/go-wifi-switcher/pkg/provider"
"github.com/go-rod/rod"
)
type Huawei struct {
usernameID string
userpasswordID string
loginText string
skipText string
netID string
netWlanID string
mainFrameID string
wlanEnableID string
page *rod.Page
}
func NewHuawei(page *rod.Page) Huawei {
return Huawei{
usernameID: "#username",
userpasswordID: "#userpassword",
loginText: "Login",
skipText: "Skip",
netID: "#Net",
netWlanID: "#Net-WLAN",
mainFrameID: "#mainFrame",
wlanEnableID: "#wlanEnable",
page: page,
}
}
func (p Huawei) Login(user, pass string) error {
username, err := p.page.Element(p.usernameID)
if err != nil {
return fmt.Errorf("page.Element %s: %s", p.userpasswordID, err)
}
username.MustInput(user)
log.Println(p.usernameID, "DONE")
userpassword, err := p.page.Element(p.userpasswordID)
if err != nil {
return fmt.Errorf("page.Element %s: %s", p.userpasswordID, err)
}
userpassword.MustInput(pass)
log.Println(p.userpasswordID, "DONE")
login, err := p.page.Element(`input[value="` + p.loginText + `"]`)
if err != nil {
return fmt.Errorf("page.Element %s: %s", p.loginText, err)
}
login.MustClick()
log.Println(p.loginText, "DONE")
skip, err := p.page.Element(`input[value="` + p.skipText + `"]`)
if err != nil {
return fmt.Errorf("page.Element %s: %s", p.skipText, err)
}
skip.MustClick()
log.Println(p.skipText, "DONE")
return nil
}
func (p Huawei) SwitchWIFI(cfg provider.SwitchConfig) error {
net, err := p.page.Element(p.netID)
if err != nil {
return fmt.Errorf("page.Element %s: %s", p.netID, err)
}
net.MustClick()
log.Println(p.netID, "DONE")
netWlan, err := p.page.Element(p.netWlanID)
if err != nil {
return fmt.Errorf("page.Element %s: %s", p.netWlanID, err)
}
netWlan.MustClick()
log.Println(p.netWlanID, "DONE")
frameElement, err := p.page.Element(p.mainFrameID)
if err != nil {
return fmt.Errorf("page.Element %s: %s", p.mainFrameID, err)
}
mainFrame, err := frameElement.Frame()
if err != nil {
return fmt.Errorf("frameElement.Frame: %s", err)
}
wlanEnable, err := mainFrame.Element(p.wlanEnableID)
if err != nil {
return fmt.Errorf("mainFrame.Element %s: %s", p.wlanEnableID, err)
}
wlanEnable.MustClick()
log.Println(p.wlanEnableID, "DONE")
sysSubmitID := "#sysSubmit"
sysSubmit, err := mainFrame.Element(sysSubmitID)
if err != nil {
return fmt.Errorf("mainFrame.Element %s: %s", sysSubmitID, err)
}
sysSubmit.MustClick()
log.Println(sysSubmitID, "DONE")
// TODO: improve this and wait until something should happens (refresh)
time.Sleep(time.Second * 3)
return nil
}

105
internal/providers/nucom.go Normal file
View File

@@ -0,0 +1,105 @@
package providers
import (
"fmt"
"log"
"time"
"gitea.urkob.com/urko/go-wifi-switcher/pkg/provider"
"github.com/go-rod/rod"
)
type Nucom struct {
usernameName string
passwordName string
loginSubmitID string
wirelessTabID string
wirelessMenuText string
configureWarningID string
wifiRadioID string
saveID string
radioOff string
radioOn string
page *rod.Page
}
func NewNucom(page *rod.Page) Nucom {
return Nucom{
usernameName: "loginUser",
passwordName: "loginPass",
loginSubmitID: "btnApply",
wirelessTabID: "menu_wireless",
wirelessMenuText: "Básico",
configureWarningID: "Configurar ahora",
wifiRadioID: "radiohiddenButton",
radioOff: "Apagada",
radioOn: "Encendida",
saveID: "basicApply",
page: page,
}
}
func (p Nucom) Login(user, pass string) error {
username, err := p.page.Element(`input[name="` + p.usernameName + `"]`)
if err != nil {
return fmt.Errorf("page.Element %s: %s", p.passwordName, err)
}
username.MustInput(user)
log.Println(p.usernameName, "DONE")
userpassword, err := p.page.Element(`input[name="` + p.passwordName + `"]`)
if err != nil {
return fmt.Errorf("page.Element %s: %s", p.passwordName, err)
}
userpassword.MustInput(pass)
log.Println(p.passwordName, "DONE")
login, err := p.page.Element(`input[id="` + p.loginSubmitID + `"]`)
if err != nil {
return fmt.Errorf("page.Element %s: %s", p.loginSubmitID, err)
}
login.MustClick()
log.Println(p.loginSubmitID, "DONE")
return nil
}
func (p Nucom) SwitchWIFI(cfg provider.SwitchConfig) error {
wirelessMenu, err := p.page.Element(`a[id="` + p.wirelessTabID + `"]`)
if err != nil {
return fmt.Errorf("page.Element %s: %s", p.wirelessTabID, err)
}
wirelessMenu.MustClick()
log.Println(p.wirelessTabID, "DONE")
time.Sleep(time.Millisecond * 500)
log.Println(p.page.MustInfo().URL)
offWifiRadio, err := p.page.Element(`input[type="radio"][name="radiohiddenButton"][value="0"]`)
if err != nil {
return fmt.Errorf("page.Element %s: %s", p.wifiRadioID, err)
}
offChecked := offWifiRadio.MustProperty("checked")
if offChecked.Bool() {
log.Println("it is off, let's switch on")
onRadio, err := p.page.Element(`input[type="radio"][name="radiohiddenButton"][value="1"]`)
if err != nil {
return fmt.Errorf("page.Element %s: %s", p.wifiRadioID, err)
}
onRadio.MustClick()
} else {
log.Println("it is on, time to switch off")
offWifiRadio.MustClick()
}
log.Println(p.wifiRadioID, "DONE")
time.Sleep(time.Millisecond * 150)
saveButton, err := p.page.Element(`input[id="` + p.saveID + `"]`)
if err != nil {
return fmt.Errorf("page.Element %s: %s", p.saveID, err)
}
saveButton.MustClick()
log.Println(p.saveID, "DONE")
time.Sleep(time.Millisecond * 150)
return nil
}

View File

@@ -0,0 +1,49 @@
package switcher
import (
"fmt"
"gitea.urkob.com/urko/go-wifi-switcher/cfg"
"gitea.urkob.com/urko/go-wifi-switcher/pkg/provider"
"github.com/go-rod/rod"
)
type Switcher struct {
Browser *rod.Browser
Page *rod.Page
config *cfg.Config
}
func NewSwitcher(remoteControlBrowserUrl string, config *cfg.Config) Switcher {
browser := rod.New().
ControlURL(remoteControlBrowserUrl).
MustConnect().
MustIgnoreCertErrors(true)
page := browser.MustPage(config.Page)
return Switcher{
Browser: browser,
Page: page,
config: config,
}
}
func (s Switcher) SwitchWIFI(prv provider.ProviderIface) error {
defer s.Browser.MustClose()
defer s.Page.Close()
if err := prv.Login(s.config.AdminUser, s.config.Password); err != nil {
return fmt.Errorf("prv.Login %w", err)
}
switchConfig := provider.SwitchConfig{
SubmitDelay: s.config.SubmitDelay,
}
if err := prv.SwitchWIFI(switchConfig); err != nil {
return fmt.Errorf("prv.SwitchWIFI %w", err)
}
return nil
}

71
main.go
View File

@@ -8,15 +8,21 @@ import (
"time"
"gitea.urkob.com/urko/go-wifi-switcher/cfg"
"github.com/go-rod/rod"
"gitea.urkob.com/urko/go-wifi-switcher/internal/providers"
"gitea.urkob.com/urko/go-wifi-switcher/internal/switcher"
"github.com/go-rod/rod/lib/launcher"
"github.com/go-rod/rod/lib/utils"
"github.com/ysmood/leakless"
)
func main() {
config := cfg.NewConfig("./.env")
envFile := ""
if os.Getenv("ENV") != "prod" {
envFile = "./.env"
}
config := cfg.NewConfig(envFile)
log.SetFlags(log.Ldate)
log.SetFlags(log.Lmicroseconds)
if config.LogFile {
logFileName := fmt.Sprintf("%s.txt", time.Now().Format(strings.ReplaceAll(time.RFC1123Z, ":", "_")))
@@ -29,48 +35,33 @@ func main() {
log.SetOutput(f)
}
// get the browser executable path
path := launcher.NewBrowser().MustGet()
args := launcher.New().FormatArgs()
var lc *launcher.Launcher
var remoteControlBrowserURL string
cmd := leakless.New().Command(path, args...)
log.Println("config", config)
if config.Bin != "" {
lc = launcher.New().Bin(config.Bin)
remoteControlBrowserURL = lc.MustLaunch()
log.Println("u = lc.MustLaunch()", remoteControlBrowserURL)
} else {
lc = launcher.New()
path := launcher.NewBrowser().MustGet()
args := lc.FormatArgs()
parser := launcher.NewURLParser()
cmd.Stderr = parser
utils.E(cmd.Start())
u := launcher.MustResolveURL(<-parser.URL)
browser := rod.New().
ControlURL(u).
MustConnect().
MustIgnoreCertErrors(true)
cmd := leakless.New().Command(path, args...)
defer browser.MustClose()
parser := launcher.NewURLParser()
cmd.Stderr = parser
utils.E(cmd.Start())
remoteControlBrowserURL = launcher.MustResolveURL(<-parser.URL)
}
page := browser.MustPage(config.Page)
page.MustElement(`#username`).MustInput(config.AdminUser)
page.MustElement(`#userpassword`).MustInput(config.Password)
log.Println("INPUT DONE")
page.MustElement(`input[value="Login"]`).MustClick()
log.Println("LOGIN DONE")
page.MustElement(`input[value="Skip"]`).MustClick()
log.Println("SKIP DONE")
page.MustElement("#Net").MustClick()
log.Println("Net DONE")
page.MustElement("#Net-WLAN").MustClick()
log.Println("Net-WLAN DONE")
mainFrame := page.MustElement("#mainFrame").MustFrame()
mainFrame.MustElement("#wlanEnable").MustClick()
log.Println("wlanEnable DONE")
mainFrame.MustElement("#sysSubmit").MustClick()
// TODO: improve this and wait until something should happens (refresh)
time.Sleep(time.Second * 3)
sw := switcher.NewSwitcher(remoteControlBrowserURL, config)
prv := providers.NewArcherAx50(sw.Page)
err := sw.SwitchWIFI(prv)
if err != nil {
panic(err)
}
log.Println("task completed")
}

10
pkg/provider/provider.go Normal file
View File

@@ -0,0 +1,10 @@
package provider
type ProviderIface interface {
Login(string, string) error
SwitchWIFI(SwitchConfig) error
}
type SwitchConfig struct {
SubmitDelay int
}