-
Notifications
You must be signed in to change notification settings - Fork 0
/
samil.go
84 lines (75 loc) · 2.55 KB
/
samil.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
// Package samil provides an API for quering networked Samil Power inverters.
// Supported inverters are those that work with SolarPower Browser V3 software.
// These are: SolarRiver TD, SolarRiver TL-D and SolarLake TL inverters.
// It is only tested and confirmed for SolarRiver 4500TL-D.
package samil
import (
"net"
)
// Samil maintains a connection to an inverter.
//
// Connections are usually closed by the inverter after 20 seconds of
// inactivity. When a connection is closed, subsequent API calls will return the
// error EOF.
type Samil struct {
conn net.Conn
in chan message // Buffer for incoming messages
closed error
}
// NewConnection searches for an inverter in the network and returns the
// connection if one is found.
//
// Inverters that are already connected to a client will not initiate a new
// connection. Therefore calling this function multiple times while leaving the
// connections open will connect to different inverters.
//
// The search will return with an i/o timeout error when no inverter is found
// after a minute.
func NewConnection() (*Samil, error) {
return newConnection(net.IPv4zero)
}
// NewConnectionWithInterface behaves almost the same as the NewConnection
// function. The difference is that this function lets you specify the
// interface IP address that is used to listen on.
//
// This can be helpful if the program by default binds to the wrong IP address.
// In that case, no inverter can be found. It can then help to set the
// interface IP address to your local network IPv4 address (e.g. 192.168.1.15).
func NewConnectionWithInterface(interfaceIP net.IP) (*Samil, error) {
return newConnection(interfaceIP)
}
func newConnection(interfaceIP net.IP) (*Samil, error) {
conn, err := connect(interfaceIP)
if err != nil {
return nil, err
}
s := &Samil{
conn: conn,
in: make(chan message, 5),
closed: nil,
}
go s.readRoutine()
return s, nil
}
// Writes only after checking if we are still connected.
func (s *Samil) write(b []byte) error {
if s.closed != nil {
return s.closed
}
_, err := s.conn.Write(b)
return err
}
// RemoteAddr returns the remote network address. The Addr returned is shared by
// all invocations of RemoteAddr, so do not modify it.
func (s *Samil) RemoteAddr() net.Addr {
return s.conn.RemoteAddr()
}
// LocalAddr returns the local network address. The Addr returned is shared by
// all invocations of LocalAddr, so do not modify it.
func (s *Samil) LocalAddr() net.Addr {
return s.conn.LocalAddr()
}
// Close closes the connection.
func (s *Samil) Close() error {
return s.conn.Close()
}