コード例 #1
0
ファイル: device.go プロジェクト: chendo/golifx
func (d *Device) SetStateLabel(pkt *packet.Packet) error {
	l := stateLabel{}
	if err := pkt.DecodePayload(&l); err != nil {
		return err
	}
	common.Log.Debugf("Got label (%v): %+v\n", d.id, l.Label)
	newLabel := stripNull(string(l.Label[:]))
	if newLabel != d.label {
		d.label = newLabel
		if err := d.publish(common.EventUpdateLabel{Label: d.label}); err != nil {
			return err
		}
	}

	return nil
}
コード例 #2
0
ファイル: device.go プロジェクト: pdf/golifx
func (d *Device) SetStateHostFirmware(pkt *packet.Packet) error {
	f := stateHostFirmware{}
	if err := pkt.DecodePayload(&f); err != nil {
		return err
	}
	common.Log.Debugf("Got firmware version (%d): %d", d.id, f.Version)
	d.RLock()
	version := d.firmwareVersion
	d.RUnlock()
	if f.Version != version {
		d.Lock()
		d.firmwareVersion = f.Version
		d.firmwareVersionString = f.String()
		d.Unlock()
	}

	return nil
}
コード例 #3
0
ファイル: device.go プロジェクト: pdf/golifx
func (d *Device) SetStateLabel(pkt *packet.Packet) error {
	l := stateLabel{}
	if err := pkt.DecodePayload(&l); err != nil {
		return err
	}
	common.Log.Debugf("Got label (%d): %v", d.id, string(l.Label[:]))
	newLabel := stripNull(string(l.Label[:]))
	if newLabel != d.CachedLabel() {
		d.Lock()
		d.label = newLabel
		d.Unlock()
		if err := d.publish(common.EventUpdateLabel{Label: newLabel}); err != nil {
			return err
		}
	}

	return nil
}
コード例 #4
0
ファイル: device.go プロジェクト: chendo/golifx
func New(addr *net.UDPAddr, requestSocket *net.UDPConn, timeout *time.Duration, retryInterval *time.Duration, reliable bool, pkt *packet.Packet) (*Device, error) {
	d := new(Device)
	d.init(addr, requestSocket, timeout, retryInterval, reliable)

	if pkt != nil {
		d.id = pkt.Target
		service := new(stateService)

		if err := pkt.DecodePayload(service); err != nil {
			return nil, err
		}

		d.address.Port = int(service.Port)
	}
	go d.handler()

	return d, nil
}
コード例 #5
0
ファイル: device.go プロジェクト: chendo/golifx
func (d *Device) SetStatePower(pkt *packet.Packet) error {
	p := statePower{}
	if err := pkt.DecodePayload(&p); err != nil {
		return err
	}
	common.Log.Debugf("Got power (%v): %+v\n", d.id, d.power)

	if d.power != p.Level {
		d.Lock()
		d.power = p.Level
		d.Unlock()
		if err := d.publish(common.EventUpdatePower{Power: d.power > 0}); err != nil {
			return err
		}
	}

	return nil
}
コード例 #6
0
ファイル: device.go プロジェクト: pdf/golifx
func (d *Device) SetStatePower(pkt *packet.Packet) error {
	p := statePower{}
	if err := pkt.DecodePayload(&p); err != nil {
		return err
	}
	common.Log.Debugf("Got power (%d): %d", d.id, d.power)

	state := p.Level > 0
	if d.CachedPower() != state {
		d.Lock()
		d.power = p.Level
		d.Unlock()
		if err := d.publish(common.EventUpdatePower{Power: state}); err != nil {
			return err
		}
	}

	return nil
}
コード例 #7
0
ファイル: group.go プロジェクト: chendo/golifx
func (g *Group) Parse(pkt *packet.Packet) error {
	var shouldUpdate, labelUpdate bool

	s := stateGroup{}
	if err := pkt.DecodePayload(&s); err != nil {
		return err
	}

	g.RLock()
	if s.UpdatedAt > g.updatedAt {
		shouldUpdate = true
	}
	g.RUnlock()

	if shouldUpdate {
		g.Lock()
		g.id = s.ID
		g.idEncoded = strings.Replace(
			base64.URLEncoding.EncodeToString(s.ID[:]),
			`=`, ``, -1,
		)
		g.updatedAt = s.UpdatedAt
		if g.label != s.Label {
			g.label = s.Label
			labelUpdate = true
		}
		g.Unlock()

		if labelUpdate {
			if err := g.publish(common.EventUpdateLabel{Label: g.GetLabel()}); err != nil {
				return err
			}
		}
	}

	return nil
}
コード例 #8
0
ファイル: light.go プロジェクト: chendo/golifx
func (l *Light) SetState(pkt *packet.Packet) error {
	s := &state{}

	if err := pkt.DecodePayload(s); err != nil {
		return err
	}
	common.Log.Debugf("Got light state (%v): %+v\n", l.id, s)

	if l.color != s.Color {
		l.Lock()
		l.color = s.Color
		l.Unlock()
		if err := l.publish(common.EventUpdateColor{Color: l.color}); err != nil {
			return err
		}
	}
	if l.power != s.Power {
		l.Lock()
		l.power = s.Power
		l.Unlock()
		if err := l.publish(common.EventUpdatePower{Power: l.power > 0}); err != nil {
			return err
		}
	}
	newLabel := stripNull(string(s.Label[:]))
	if newLabel != l.label {
		l.Lock()
		l.label = newLabel
		l.Unlock()
		if err := l.publish(common.EventUpdateLabel{Label: l.label}); err != nil {
			return err
		}
	}

	return nil
}
コード例 #9
0
ファイル: v2.go プロジェクト: chendo/golifx
func (p *V2) process(pkt *packet.Packet, addr *net.UDPAddr) {
	common.Log.Debugf("Processing packet from %v: source %v, type %v, sequence %v, target %v, tagged %v, resRequired %v, ackRequired %v: %+v\n", addr.IP, pkt.GetSource(), pkt.GetType(), pkt.GetSequence(), pkt.GetTarget(), pkt.GetTagged(), pkt.GetResRequired(), pkt.GetAckRequired(), *pkt)

	// Update device seen time for any targeted packets
	if pkt.Target != 0 {
		dev, err := p.getDevice(pkt.Target)
		if err == nil {
			dev.SetSeen(time.Now())
		}
	}

	// Broadcast packets, or packets generated by other clients
	if pkt.GetSource() != packet.ClientID {
		switch pkt.GetType() {
		case device.StatePower:
			dev, err := p.getDevice(pkt.GetTarget())
			if err != nil {
				common.Log.Debugf("Skipping StatePower packet for unknown device: source %v, type %v, sequence %v, target %v, tagged %v, resRequired %v, ackRequired %v: %+v\n", pkt.GetSource(), pkt.GetType(), pkt.GetSequence(), pkt.GetTarget(), pkt.GetTagged(), pkt.GetResRequired(), pkt.GetAckRequired(), *pkt)
				return
			}
			err = dev.SetStatePower(pkt)
			if err != nil {
				common.Log.Debugf("Failed setting StatePower on device: source %v, type %v, sequence %v, target %v, tagged %v, resRequired %v, ackRequired %v: %+v\n", pkt.GetSource(), pkt.GetType(), pkt.GetSequence(), pkt.GetTarget(), pkt.GetTagged(), pkt.GetResRequired(), pkt.GetAckRequired(), *pkt)
				return
			}
		case device.StateLabel:
			dev, err := p.getDevice(pkt.GetTarget())
			if err != nil {
				common.Log.Debugf("Skipping StateLabel packet for unknown device: source %v, type %v, sequence %v, target %v, tagged %v, resRequired %v, ackRequired %v: %+v\n", pkt.GetSource(), pkt.GetType(), pkt.GetSequence(), pkt.GetTarget(), pkt.GetTagged(), pkt.GetResRequired(), pkt.GetAckRequired(), *pkt)
				return
			}
			err = dev.SetStateLabel(pkt)
			if err != nil {
				common.Log.Debugf("Failed setting StatePower on device: source %v, type %v, sequence %v, target %v, tagged %v, resRequired %v, ackRequired %v: %+v\n", pkt.GetSource(), pkt.GetType(), pkt.GetSequence(), pkt.GetTarget(), pkt.GetTagged(), pkt.GetResRequired(), pkt.GetAckRequired(), *pkt)
				return
			}
		case device.State:
			dev, err := p.getDevice(pkt.GetTarget())
			if err != nil {
				common.Log.Debugf("Skipping State packet for unknown device: source %v, type %v, sequence %v, target %v, tagged %v, resRequired %v, ackRequired %v: %+v\n", pkt.GetSource(), pkt.GetType(), pkt.GetSequence(), pkt.GetTarget(), pkt.GetTagged(), pkt.GetResRequired(), pkt.GetAckRequired(), *pkt)
				return
			}
			light, ok := dev.(*device.Light)
			if !ok {
				common.Log.Debugf("Skipping State packet for non-light device: source %v, type %v, sequence %v, target %v, tagged %v, resRequired %v, ackRequired %v: %+v\n", pkt.GetSource(), pkt.GetType(), pkt.GetSequence(), pkt.GetTarget(), pkt.GetTagged(), pkt.GetResRequired(), pkt.GetAckRequired(), *pkt)
				return
			}
			err = light.SetState(pkt)
			if err != nil {
				common.Log.Debugf("Error setting State on device: source %v, type %v, sequence %v, target %v, tagged %v, resRequired %v, ackRequired %v: %+v\n", pkt.GetSource(), pkt.GetType(), pkt.GetSequence(), pkt.GetTarget(), pkt.GetTagged(), pkt.GetResRequired(), pkt.GetAckRequired(), *pkt)
				return
			}
		default:
			common.Log.Debugf("Skipping packet with non-local source: source %v, type %v, sequence %v, target %v, tagged %v, resRequired %v, ackRequired %v: %+v\n", pkt.GetSource(), pkt.GetType(), pkt.GetSequence(), pkt.GetTarget(), pkt.GetTagged(), pkt.GetResRequired(), pkt.GetAckRequired(), *pkt)
		}
		return
	}

	// Packets processed at the protocol level regardless of target
	switch pkt.GetType() {
	case device.StateLocation:
		p.addLocation(pkt)
	case device.StateGroup:
		p.addGroup(pkt)
	}

	// Packets processed at the protocol level or returned to target
	switch pkt.GetType() {
	case device.StateService:
		dev, err := p.getDevice(pkt.Target)
		if err != nil {
			dev, err := device.New(addr, p.socket, p.timeout, p.retryInterval, p.Reliable, pkt)
			if err != nil {
				common.Log.Errorf("Failed creating device: %v\n", err)
				return
			}
			p.addDevice(dev)
			return
		}
		// Perform state discovery on lights
		if l, ok := dev.(*device.Light); ok {
			if err := l.Get(); err != nil {
				common.Log.Debugf("Failed getting light state: %v\n", err)
			}
		}
	default:
		if pkt.GetTarget() == 0 {
			common.Log.Debugf("Skipping packet without target: %+v\n", *pkt)
			return
		}
		dev, err := p.getDevice(pkt.GetTarget())
		if err != nil {
			common.Log.Errorf("No known device with ID %v\n", pkt.GetTarget())
			return
		}
		common.Log.Debugf("Returning packet to device %v: %+v\n", dev.ID(), *pkt)
		dev.Handle(pkt)
	}
}
コード例 #10
0
ファイル: device.go プロジェクト: pdf/golifx
func (d *Device) Send(pkt *packet.Packet, ackRequired, responseRequired bool) (packet.Chan, error) {
	proxyChan := make(packet.Chan)

	// Rate limiter
	<-d.limiter.C

	// Broadcast vs direct
	broadcast := d.id == 0
	if broadcast {
		// Broadcast can't be reliable
		ackRequired = false
		pkt.SetTagged(true)
	} else {
		pkt.SetTarget(d.id)
		if ackRequired {
			pkt.SetAckRequired(true)
		}
		if responseRequired {
			pkt.SetResRequired(true)
		}
		if ackRequired || responseRequired {
			seq, res := d.addSeq()
			pkt.SetSequence(seq)

			go func() {
				defer func() {
					close(res.done)
					close(proxyChan)
				}()

				var (
					timeout <-chan time.Time
					ticker  = time.NewTicker(*d.retryInterval)
				)

				if d.timeout == nil || *d.timeout == 0 {
					timeout = make(<-chan time.Time)
				} else {
					timeout = time.After(*d.timeout)
				}

				for {
					select {
					case pktResponse, ok := <-res.ch:
						if !ok {
							return
						}
						if pktResponse.Result.GetType() == Acknowledgement {
							common.Log.Debugf("Got ACK for seq %d on device %d, cancelling retries", seq, d.ID())
							ticker.Stop()
							// Ack does not resolve outstanding request,
							// continue waiting for response
							if responseRequired {
								continue
							}
						}
						proxyChan <- pktResponse
						return
					case <-ticker.C:
						common.Log.Debugf("Retrying send for seq %d on device %d after %d milliseconds", seq, d.ID(), *d.retryInterval/time.Millisecond)
						if err := pkt.Write(); err != nil {
							proxyChan <- &packet.Response{
								Error: err,
							}
							return
						}
					case <-timeout:
						proxyChan <- &packet.Response{
							Error: common.ErrTimeout,
						}
						return
					}
				}
			}()
		}
	}

	err := pkt.Write()
	d.resetLimiter(broadcast)

	return proxyChan, err
}
コード例 #11
0
ファイル: device.go プロジェクト: chendo/golifx
func (d *Device) Send(pkt *packet.Packet, ackRequired, responseRequired bool) (packet.Chan, error) {
	proxyChan := make(packet.Chan)

	// Rate limiter
	<-d.limiter.C

	// Broadcast vs direct
	broadcast := d.id == 0
	if broadcast {
		// Broadcast can't be reliable
		ackRequired = false
		pkt.SetTagged(true)
	} else {
		pkt.SetTarget(d.id)
		if ackRequired {
			pkt.SetAckRequired(true)
		}
		if responseRequired {
			pkt.SetResRequired(true)
		}
		if ackRequired || responseRequired {
			inputChan := make(packet.Chan)
			doneChan := make(chan struct{})

			d.Lock()
			d.sequence++
			if d.sequence == 0 {
				d.sequence++
			}
			seq := d.sequence
			d.responseMap[seq] = inputChan
			d.doneMap[seq] = doneChan
			pkt.SetSequence(seq)
			d.Unlock()

			go func() {
				defer func() {
					close(doneChan)
				}()

				var (
					ok          bool
					timeout     <-chan time.Time
					pktResponse = packet.Response{}
					ticker      = time.NewTicker(*d.retryInterval)
				)

				if d.timeout == nil || *d.timeout == 0 {
					timeout = make(<-chan time.Time)
				} else {
					timeout = time.After(*d.timeout)
				}

				for {
					select {
					case pktResponse, ok = <-inputChan:
						if !ok {
							close(proxyChan)
							return
						}
						if pktResponse.Result.GetType() == Acknowledgement {
							common.Log.Debugf("Got ACK for seq %d on device %d, cancelling retries\n", seq, d.ID())
							ticker.Stop()
							if responseRequired {
								continue
							}
						}
						proxyChan <- pktResponse
						return
					case <-ticker.C:
						common.Log.Debugf("Retrying send after %d milliseconds: %+v\n", *d.retryInterval/time.Millisecond, *pkt)
						if err := pkt.Write(); err != nil {
							pktResponse.Error = err
							proxyChan <- pktResponse
							return
						}
					case <-timeout:
						pktResponse.Error = common.ErrTimeout
						proxyChan <- pktResponse
						return
					}
				}
			}()
		}
	}

	err := pkt.Write()
	d.resetLimiter(broadcast)

	return proxyChan, err
}