123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174 |
- package sonoff_s2x
- import (
- "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 Handler
- func 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,
- }
- }
|