// 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 }
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{}), }) }
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) }
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 }
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) }
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) }
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