예제 #1
0
// Connect Builds a new ninja connection to the MQTT broker, using the given client ID
func Connect(clientID string) (*Connection, error) {

	log := logger.GetLogger(fmt.Sprintf("%s.connection", clientID))

	conn := Connection{
		log:      log,
		services: []model.ServiceAnnouncement{},
	}

	mqttURL := fmt.Sprintf("%s:%d", config.MustString("mqtt", "host"), config.MustInt("mqtt", "port"))

	log.Infof("Connecting to %s using cid:%s", mqttURL, clientID)

	conn.mqtt = bus.MustConnect(mqttURL, clientID)

	log.Infof("Connected")

	conn.rpc = rpc.NewClient(conn.mqtt, json2.NewClientCodec())
	conn.rpcServer = rpc.NewServer(conn.mqtt, json2.NewCodec())

	// Add service discovery service. Responds to queries about services exposed in this process.
	discoveryService := &discoverService{&conn}
	_, err := conn.exportService(discoveryService, "$discover", &simpleService{*discoveryService.GetServiceAnnouncement()})
	if err != nil {
		log.Fatalf("Could not expose discovery service: %s", err)
	}

	return &conn, nil
}
예제 #2
0
func (r *RestServer) listen() error {

	m := martini.Classic()

	m.Use(cors.Allow(&cors.Options{
		AllowAllOrigins: true,
	}))

	// m.Map(r.TaskModel)

	task := NewTaskRouter()
	task.scheduler = r.Scheduler

	m.Group("/rest/v1/tasks", task.Register)

	listenAddress := fmt.Sprintf(":%d", config.MustInt("app-scheduler.rest.port"))

	r.log.Infof("Listening at %s", listenAddress)

	srv := &http.Server{Addr: listenAddress, Handler: m}
	ln, err := net.Listen("tcp", listenAddress)
	if err != nil {
		return err
	}

	return srv.Serve(listener{
		TCPListener: ln.(*net.TCPListener),
		stop:        make(chan struct{}),
	})
}
예제 #3
0
func (c *client) bridgeToMaster(host net.IP, port int) {

	log.Debugf("Bridging to the master: %s:%d", host, port)

	mqttURL := fmt.Sprintf("%s:%d", host, port)

	clientID := "slave-" + config.Serial()

	log.Infof("Connecting to master %s using cid:%s", mqttURL, clientID)

	c.masterBus = bus.MustConnect(mqttURL, clientID)
	c.localBus = bus.MustConnect(fmt.Sprintf("%s:%d", config.MustString("mqtt.host"), config.MustInt("mqtt.port")), "meshing")

	log.Infof("Connected to master? %t", c.masterBus.Connected())

	if c.masterBus.Connected() {
		c.setUnorphaned()
	} else {
		c.setOrphaned()
	}

	c.masterBus.OnDisconnect(func() {
		log.Infof("Disconnected from master")
		go func() {
			time.Sleep(time.Second * 5)
			if !c.masterBus.Connected() {
				log.Infof("Still disconnected from master, setting orphaned.")
				c.setOrphaned()
			}
		}()
	})

	c.masterBus.OnConnect(func() {
		log.Infof("Connected to master")
		go func() {
			time.Sleep(time.Second * 2)
			if !c.masterBus.Connected() {
				log.Infof("Still connected to master, setting unorphaned")
				c.setUnorphaned()
			}
		}()
	})

	bridgeTopics := []string{"$discover", "$site/#", "$home/#" /*deprecated*/, "$node/#", "$thing/#", "$device/#"}

	c.bridgeMqtt(c.masterBus, c.localBus, true, bridgeTopics)
	c.bridgeMqtt(c.localBus, c.masterBus, false, bridgeTopics)
}
예제 #4
0
func UpdateSphereAvahiService(isPaired, isMaster bool) error {

	tmpl, err := template.New("avahi").Parse(src)

	if err != nil {
		return err
	}

	serviceDefinition := new(bytes.Buffer)

	err = tmpl.Execute(serviceDefinition, map[string]interface{}{
		"Serial":      config.Serial(),
		"Master":      isMaster,
		"Paired":      isPaired,
		"User":        config.String("", "userId"),
		"Site":        config.String("", "siteId"),
		"MasterNode":  config.String("", "masterNodeId"),
		"SiteUpdated": config.Int(0, "siteUpdated"),
		"RestPort":    config.MustInt("homecloud.rest.port"),
	})

	if err != nil {
		return err
	}

	log.Debugf("Saving service definition", serviceDefinition.String())

	if runtime.GOOS != "linux" {
		log.Warningf("Avahi service definition is not being saved, as platform != linux")
		return nil
	}

	if _, err := os.Stat("/data/etc/avahi/services"); err != nil {
		log.Warningf("Avahi service definition is not being saved, as /data/etc/avahi/services does not exist")
		return nil
	}

	err = ioutil.WriteFile("/data/etc/avahi/services/ninjasphere.service", []byte(serviceDefinition.String()), 0644)

	// HACK: Remove this if it doesn't fix Chris's problem
	exec.Command("service", "avahi-daemon", "restart").Output()

	return err
}
예제 #5
0
func (r *RestServer) Listen() error {

	m := martini.Classic()

	m.Use(cors.Allow(&cors.Options{
		AllowAllOrigins: true,
	}))

	m.Map(r.RoomModel)
	m.Map(r.ThingModel)
	m.Map(r.DeviceModel)
	m.Map(r.SiteModel)
	m.Map(r.Conn)
	m.Map(r.StateManager)

	m.Use(func(c martini.Context) {
		conn := r.RedisPool.Get()
		c.Map(conn)

		c.Next()

		conn.Close()
	})

	location := NewLocationRouter()
	thing := NewThingRouter()
	room := NewRoomRouter()
	site := NewSiteRouter()

	m.Group("/rest/v1/locations", location.Register)
	m.Group("/rest/v1/things", thing.Register)
	m.Group("/rest/v1/rooms", room.Register)
	m.Group("/rest/v1/sites", site.Register)

	listenAddress := fmt.Sprintf(":%d", config.MustInt("homecloud.rest.port"))

	r.log.Infof("Listening at %s", listenAddress)

	return http.ListenAndServe(listenAddress, m)
}
예제 #6
0
func (c *client) findPeers() {

	query := "_ninja-homecloud-mqtt._tcp"

	// Make a channel for results and start listening
	entriesCh := make(chan *mdns.ServiceEntry, 4)
	go func() {
		for entry := range entriesCh {

			if !strings.Contains(entry.Name, query) {
				continue
			}
			nodeInfo := parseMdnsInfo(entry.Info)

			id, ok := nodeInfo["ninja.sphere.node_id"]

			if !ok {
				log.Warningf("Found a node, but couldn't get it's node id. %v", entry)
				continue
			}

			if id == config.Serial() {
				// It's me.
				continue
			}

			user, ok := nodeInfo["ninja.sphere.user_id"]
			if !ok {
				log.Warningf("Found a node, but couldn't get it's user id. %v", entry)
				continue
			}

			site, ok := nodeInfo["ninja.sphere.site_id"]
			siteUpdated, ok := nodeInfo["ninja.sphere.site_updated"]
			masterNodeID, ok := nodeInfo["ninja.sphere.master_node_id"]

			if user == config.MustString("userId") {

				if site == config.MustString("siteId") {
					log.Infof("Found a sibling node (%s) - %s", id, entry.Addr)

					siteUpdatedInt, err := strconv.ParseInt(siteUpdated, 10, 64)

					if err != nil {
						log.Warningf("Failed to read the site_updated field (%s) on node %s - %s", siteUpdated, id, entry.Addr)
					} else {
						if int(siteUpdatedInt) > config.MustInt("siteUpdated") {

							log.Infof("Found node (%s - %s) with a newer site update time (%s).", id, entry.Addr, siteUpdated)

							info := &meshInfo{
								MasterNodeID: masterNodeID,
								SiteID:       config.MustString("siteId"),
								SiteUpdated:  int(siteUpdatedInt),
							}

							err := saveMeshInfo(info)
							if err != nil {
								log.Warningf("Failed to save updated mesh info from node: %s - %+v", err, info)
							}

							if masterNodeID == config.MustString("masterNodeId") {
								log.Infof("Updated master id is the same (%s). Moving on with our lives.", masterNodeID)
							} else {
								log.Infof("Master id has changed (was %s now %s). Rebooting", config.MustString("masterNodeId"), masterNodeID)

								reboot()
								return
							}
						}
					}

				} else {
					log.Warningf("Found a node owned by the same user (%s) but from a different site (%s) - ID:%s - %s", user, site, id, entry.Addr)
				}

			} else {
				log.Infof("Found a node owned by another user (%s) (%s) - %s", user, id, entry.Addr)
			}

			if id == config.MustString("masterNodeId") {
				log.Infof("Found the master node (%s) - %s", id, entry.Addr)

				select {
				case c.foundMaster <- true:
				default:
				}

				if !c.bridged {
					c.bridgeToMaster(entry.Addr, entry.Port)
					c.bridged = true
					c.exportNodeDevice()
				}
			}

		}
	}()

	// Start the lookup
	mdns.Lookup(query, entriesCh)
	close(entriesCh)
}
예제 #7
0
func main() {

	log.Infof("Welcome home, Ninja.")

	if config.Bool(true, "homecloud.waitForNTP") {
		waitForNTP()
	}

	// The MQTT Connection
	conn, err := ninja.Connect("sphere-go-homecloud")
	if err != nil {
		log.Fatalf("Failed to connect to sphere: %s", err)
	}

	// An MQTT Connection used for outbound syncing connections
	syncConn := &models.SyncConnection{}
	syncConn.Conn, err = ninja.Connect("sphere-go-homecloud.sync")
	if err != nil {
		log.Fatalf("Failed to connect to sphere (sync): %s", err)
	}

	// Our redis pool
	pool := &redis.Pool{
		MaxIdle:     config.MustInt("homecloud.redis.maxIdle"),
		MaxActive:   config.Int(10, "homecloud.redis.maxActive"),
		IdleTimeout: config.MustDuration("homecloud.redis.idleTimeout"),
		Wait:        true,
		Dial: func() (redis.Conn, error) {
			c, err := redis.Dial("tcp", fmt.Sprintf("%s:%d", config.String("", "homecloud.redis.host"), config.MustInt("homecloud.redis.port")))
			if err != nil {
				return nil, err
			}
			return c, err
		},
		TestOnBorrow: func(c redis.Conn, t time.Time) error {
			_, err := c.Do("PING")
			return err
		},
	}

	// Not pretty.
	rpc.RedisPool = pool

	// Wait until we connect to redis successfully.
	for {
		c := pool.Get()

		if c.Err() == nil {
			c.Close()
			break
		}
		log.Warningf("Failed to connect to redis: %s", c.Err())
		time.Sleep(time.Second)
	}

	// Build the object graph using dependency injection
	injectables := []interface{}{}

	injectables = append(injectables, pool, conn, syncConn)
	injectables = append(injectables, &homecloud.HomeCloud{}, &homecloud.TimeSeriesManager{}, &homecloud.DeviceManager{}, &homecloud.ModuleManager{})
	injectables = append(injectables, state.NewStateManager())
	injectables = append(injectables, &rest.RestServer{})
	injectables = append(injectables, models.GetInjectables()...)

	err = inject.Populate(injectables...)

	if err != nil {
		log.Fatalf("Failed to construct the object graph: %s", err)
	}

	// Run PostConstruct on any objects that have it
	for _, node := range injectables {
		if n, ok := node.(postConstructable); ok {
			go func(c postConstructable) {
				if err := c.PostConstruct(); err != nil {
					log.Fatalf("Failed PostConstruct on object %s: %s", reflect.TypeOf(c).String(), err)
				}
			}(n)
		}
	}

	support.WaitUntilSignal()
	// So long, and thanks for all the fish.
}
	"github.com/ninjasphere/gestic-tools/go-gestic-sdk"
	"github.com/ninjasphere/go-ninja/api"
	"github.com/ninjasphere/go-ninja/channels"
	"github.com/ninjasphere/go-ninja/config"
	"github.com/ninjasphere/go-ninja/devices"
	"github.com/ninjasphere/go-ninja/logger"
	"github.com/ninjasphere/sphere-go-led-controller/util"
)

var lightTapInterval = config.MustDuration("led.light.tapInterval")
var colorInterval = config.MustDuration("led.light.colorInterval")

var colorAdjustSpeed = config.MustFloat("led.light.colorSpeed")
var brightnessAdjustSpeed = config.MustFloat("led.light.brightnessSpeed")

var brightnessMinimum = uint8(config.MustInt("led.light.brightnessMinimum"))

type LightPane struct {
	log      *logger.Logger
	conn     *ninja.Connection
	onEnable chan bool

	onOffDevices    *[]*ninja.ServiceClient
	airwheelDevices *[]*ninja.ServiceClient

	onOffState bool
	lastTap    time.Time

	colorMode bool

	airWheelState         float64