Simple MJPEG streamer for ESPHome cameras.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

115 lines
2.4 KiB

package main
import (
"sync"
"time"
"github.com/juju/errors"
"github.com/sirupsen/logrus"
"maze.io/x/esphome"
)
type Manager struct {
sync.RWMutex
Device map[string]*Device
config *Config
}
func NewManager(config *Config) *Manager {
m := &Manager{
Device: make(map[string]*Device),
config: config,
}
go m.keepalive()
return m
}
func (m *Manager) keepalive() {
m.discoverDevices()
m.checkDevicesAlive()
ticker := time.NewTicker(m.config.Scan.Interval)
for {
<-ticker.C
m.discoverDevices()
m.checkDevicesAlive()
}
}
func (m *Manager) discoverDevices() {
log.Debugln("scanning for devices")
devices := make(chan *esphome.Device, 32)
defer close(devices)
go m.addDiscoveredDevices(devices)
if err := esphome.DiscoverService(devices, m.config.Scan.Service, m.config.Scan.Domain, m.config.Scan.Timeout); err != nil {
log.Printf("manager: error discovering: %v", err)
}
}
func (m *Manager) addDiscoveredDevices(devices <-chan *esphome.Device) {
for discovered := range devices {
log := log.WithFields(logrus.Fields{
"name": discovered.Name,
"addr": discovered.Addr(),
})
log.Debug("mDNS response from device")
m.RLock()
device, ok := m.Device[discovered.Name]
m.RUnlock()
if !ok {
log.Info("discovered new device")
device = NewDevice(discovered.Name, discovered.Addr())
device.Version = discovered.Version
m.Lock()
m.Device[device.Name] = device
m.Unlock()
}
device.Last = time.Now()
if device.IsAvailable() {
continue
}
if err := device.Connect(m.config.GetSecret(discovered.Name), m.config.Scan.Timeout); err != nil {
if err == esphome.ErrPassword {
err = errors.Wrap(err, errors.New("update device password in config"))
}
log.WithError(err).Warn("error connecting")
} else {
log.Info("connected")
}
}
}
func (m *Manager) checkDevicesAlive() {
m.RLock()
for _, device := range m.Device {
// We're not polling devices that have no camera, they are mainly
// listed for brevity.
if device.IsAvailable() && device.Camera != nil {
m.checkDeviceAlive(device)
}
}
m.RUnlock()
}
func (m *Manager) checkDeviceAlive(device *Device) {
var (
idle = time.Since(device.client.LastMessage())
log = log.WithFields(logrus.Fields{
"name": device.Name,
"idle": idle,
})
)
log.Debug("check alive")
if idle > m.config.Scan.Interval {
log.Warn("device is unresponsive, scheduling reconnect")
_ = device.client.Close()
device.client = nil
}
}