| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174 | package sonoff_s2ximport (	"log"	"regexp"	"strings"	"imuslab.com/arozos/mod/iot"	"imuslab.com/arozos/mod/network/mdns")/*	Sonoff S2X Module	This is a module that handles Sonoff Tasmota 6.4.1(sonoff)	Core version: 2_4_2/2.2.1(cfd48f3)	See https://github.com/arendst/Tasmota for source code	mDNS must be set to enable in order to use this scanner*/type Handler struct {	scanner      *mdns.MDNSHost	lastScanTime int64}//Create a new Sonoff S2X Protocol Handlerfunc NewProtocolHandler(scanner *mdns.MDNSHost) *Handler {	//Create a new MDNS Host	return &Handler{		scanner,		0,	}}func (h *Handler) Start() error {	log.Println("*IoT* Sonoff Tasmoto S2X 6.4 scanner loaded")	return nil}func (h *Handler) Scan() ([]*iot.Device, error) {	results := []*iot.Device{}	scannedDevices := h.scanner.Scan(10, "")	for _, dev := range scannedDevices {		if dev.Port == 80 {			//This things has web UI. Check if it is sonoff by grabbing its index			value, err := tryGet("http://" + dev.IPv4[0].String() + "/")			if err != nil {				//This things is not sonoff smart socket				log.Println(dev.HostName + " is not sonoff")				continue			}			//Check if the return value contains the keyword:			if strings.Contains(value, "Sonoff-Tasmota") {				//This is sonoff device!				//Extract its MAC Address from Web UI				info, err := tryGet("http://" + dev.IPv4[0].String() + "/in")				if err != nil {					//This things is not sonoff smart socket					log.Println(dev.HostName + " failed to extract its MAC address from /in page")					continue				}				//Try to seperate the MAC address out				//I have no idea what I am doing here				re := regexp.MustCompile("[[:alnum:]][[:alnum:]]:[[:alnum:]][[:alnum:]]:[[:alnum:]][[:alnum:]]:[[:alnum:]][[:alnum:]]:[[:alnum:]][[:alnum:]]:[[:alnum:]][[:alnum:]]")				match := re.FindStringSubmatch(info)				deviceMAC := ""				if len(match) > 0 {					deviceMAC = match[0]				} else {					//Can't find MAC address for no reason?					continue				}				//Try to get the device status				status, err := tryGet("http://" + dev.IPv4[0].String() + "/ay")				if err != nil {					continue				}				devStatus := map[string]interface{}{}				if strings.Contains(status, "ON") {					//It is on					devStatus["Power"] = "ON"				} else {					//It is off					devStatus["Power"] = "OFF"				}				toggleEndpoint := iot.Endpoint{					RelPath: "ay?o=1",					Name:    "Toggle Power",					Desc:    "Toggle the power of the smart switch",					Type:    "none",				}				results = append(results, &iot.Device{					Name:         strings.Title(strings.ReplaceAll(dev.HostName, ".local.", "")),					Port:         80,					Model:        "Sonoff S2X Smart Switch",					Version:      "",					Manufacturer: "Sonoff",					DeviceUUID:   deviceMAC,					IPAddr:           dev.IPv4[0].String(),					RequireAuth:      false,					RequireConnect:   false,					Status:           devStatus,					ControlEndpoints: []*iot.Endpoint{&toggleEndpoint},					Handler:          h,				})			} else {				continue			}		}	}	return results, nil}func (h *Handler) Connect(device *iot.Device, authInfo *iot.AuthInfo) error {	return nil}func (h *Handler) Disconnect(device *iot.Device) error {	return nil}func (h *Handler) Status(device *iot.Device) (map[string]interface{}, error) {	//Try to get the device status	status, err := tryGet("http://" + device.IPAddr + "/ay")	if err != nil {		return map[string]interface{}{}, err	}	devStatus := map[string]interface{}{}	if strings.Contains(status, "ON") {		//It is on		devStatus["Power"] = "ON"	} else {		//It is off		devStatus["Power"] = "OFF"	}	return devStatus, nil}func (h *Handler) Icon(device *iot.Device) string {	return "switch"}func (h *Handler) Execute(device *iot.Device, endpoint *iot.Endpoint, payload interface{}) (interface{}, error) {	results, err := tryGet("http://" + device.IPAddr + "/" + endpoint.RelPath)	if err != nil {		return nil, err	}	results = strings.ReplaceAll(results, "{t}", "")	return results, nil}func (h *Handler) Stats() iot.Stats {	return iot.Stats{		Name:          "Sonoff Tasmota",		Desc:          "Tasmota firmware for Sonoff S2X devices",		Version:       "1.0",		ProtocolVer:   "1.0",		Author:        "tobychui",		AuthorWebsite: "http://imuslab.com",		AuthorEmail:   "[email protected]",		ReleaseDate:   1616944405,	}}
 |