package lockserver import ( "bufio" "fmt" "net" "os" "strconv" "strings" "time" log "github.com/sirupsen/logrus" ) // Build key encoding request command for the Omnitec lock server. func (lock *OmniLockServer) BuildCommand(doorReq DoorCardRequest, checkIn, checkOut time.Time) error { const funcName = "OmniLockServer.BuildCommand" hostname, err := os.Hostname() if err != nil { return fmt.Errorf("[%s] failed to get hostname: %v", funcName, err) } // Format lockId as 4-digit zero-padded string idInt, err := strconv.Atoi(doorReq.RoomField) if err != nil { return fmt.Errorf("[%s] failed to convert lockId to integer: %v", funcName, err) } formattedLockId := fmt.Sprintf("%04d", idInt) // Format date/time parts dt := checkOut.Format("15:04") // DT = HH:mm ga := checkIn.Format("060102") // GA = ddMMyy gd := checkOut.Format("060102") // GD = ddMMyy ti := checkIn.Format("150405") // TI = HHmmss // Construct payload payload := fmt.Sprintf( "KR|KC%s|KTD|RN%s|%s|DT%s|G#75|GA%s|GD%s|KO0000|DA%s|TI%s|", lock.encoderAddr, formattedLockId, hostname, dt, ga, gd, ga, ti, ) // Assign to command field with STX and ETX lock.command = append([]byte{0x02}, append([]byte(payload), 0x03)...) return nil } // Starts link to the Omnitec lock server and perform key encoding func (lock *OmniLockServer) LockSequence() error { const funcName = "OmniLockServer.LockSequence" conn, err := InitializeServerConnection(LockServerURL) if err != nil { return err } defer conn.Close() // Start the link with the lock server regs, err := lock.linkStart(conn) if err != nil { return fmt.Errorf("[%s] linkStart failed: %v", funcName, err) } for _, reg := range regs { log.Printf("Received: %q", reg) } // Request encoding from the lock server raw, err := lock.requestEncoding(conn) if err != nil { return fmt.Errorf("[%s] request encoding failed: %v", funcName, err) } log.Infof("Encoding response: %s", raw) return nil } func (lock *OmniLockServer) linkStart(conn net.Conn) ([]string, error) { payload := fmt.Sprintf("LS|DA%s|TI%s|", time.Now().Format("060102"), time.Now().Format("150405"), ) command := append([]byte{0x02}, append([]byte(payload), 0x03)...) log.Printf("Sending command: %q", command) _, err := conn.Write(command) if err != nil { return nil, fmt.Errorf("failed to send command: %v", err) } conn.SetReadDeadline(time.Now().Add(10 * time.Second)) reader := bufio.NewReader(conn) var registers []string for { reg, err := reader.ReadString(0x03) if err != nil { if netErr, ok := err.(net.Error); ok && netErr.Timeout() { break // timeout -> assume no more registers } return nil, fmt.Errorf("error reading register: %v", err) } registers = append(registers, reg) // stop when you see the final register type, e.g. "LA|…|" if strings.HasPrefix(reg, "\x02LA|") { break } } return registers, nil } func (lock *OmniLockServer) requestEncoding(conn net.Conn) (string, error) { raw, err := sendAndReceive(conn, lock.command) if err != nil { return "", fmt.Errorf("failed to send Encoding request: %v", err) } return parseOmniResponse(raw) } func parseOmniResponse(raw string) (string, error) { clean := strings.Trim(raw, "\x02\x03") idx := strings.Index(clean, "AS") code := clean[idx+2 : idx+4] // Extract the response code code = strings.ToLower(code) // Convert to lowercase for consistency if code != "ok" { return "", fmt.Errorf("negative response code: %s", clean) } return "Success: " + clean, nil }