Пример #1
0
func refreshMeshInfo() (*meshInfo, error) {

	nodes, err := getNodes()
	if err != nil {
		return nil, err
	}

	sites, err := getSites()
	if err != nil {
		return nil, err
	}

	node, ok := nodes[config.Serial()]
	if !ok {
		return nil, errors.New("Could not find our node in the cloud. (race condition?)")
	}

	site, ok := sites[node.SiteID]
	if !ok {
		return nil, errors.New("Could not find our node in the cloud. (race condition?)")
	}

	if config.Bool(false, "forceMaster") {
		site.MasterNodeID = config.Serial()
	}

	meshInfo := &meshInfo{
		SiteID:       site.ID,
		MasterNodeID: site.MasterNodeID,
		SiteUpdated:  int(time.Time(site.Updated).UnixNano() / int64(time.Second)),
	}

	return meshInfo, saveMeshInfo(meshInfo)
}
Пример #2
0
// bridgeMqtt connects one mqtt broker to another. Shouldn't probably be doing this. But whatever.
func (c *client) bridgeMqtt(from, to bus.Bus, masterToSlave bool, topics []string) {

	onMessage := func(topic string, payload []byte) {

		if masterToSlave {
			// This is a message from master, clear the timeout
			c.masterReceiveTimeout.Reset(orphanTimeout)
		}

		if payload[0] != '{' {
			log.Warningf("Invalid payload (should be a json-rpc object): %s", payload)
			return
		}

		var msg meshMessage
		json.Unmarshal(payload, &msg)

		interesting := false

		if masterToSlave {
			// Interesting if it's from the master or one of the other slaves
			interesting = msg.Source == nil || (*msg.Source != config.Serial())
		} else {
			// Interesting if it's from me
			interesting = msg.Source == nil
		}

		log.Infof("Mesh master2slave:%t topic:%s interesting:%t", masterToSlave, topic, interesting)

		if interesting {

			if msg.Source == nil {
				if masterToSlave {
					payload = addMeshSource(config.MustString("masterNodeId"), payload)
				} else {
					payload = addMeshSource(config.Serial(), payload)
				}
			}

			to.Publish(topic, payload)
		}

	}

	for _, topic := range topics {
		_, err := from.Subscribe(topic, onMessage)
		if err != nil {
			log.Fatalf("Failed to subscribe to topic %s when bridging to master: %s", topic, err)
		}
	}

}
func runTasks() {

	// Find this sphere's location, if we care..
	if sameRoomOnly {
		for _, thing := range allThings {
			if thing.Type == "node" && thing.Device != nil && thing.Device.NaturalID == config.Serial() {
				if thing.Location != nil && (roomID == nil || *roomID != *thing.Location) {
					// Got it.
					log.Infof("Got this sphere's location: %s", thing.Location)
					roomID = thing.Location
					select {
					case foundLocation <- true:
					default:
					}

					break
				}
			}
		}
	}

	for _, task := range tasks {
		go func(t *request) {
			t.cb(getChannelServices(t.thingType, t.protocol, t.filter))
		}(task)
	}

}
Пример #4
0
// ExportDriver Exports a driver using the 'driver' protocol, and announces it
func (c *Connection) ExportDriver(driver Driver) error {

	time.Sleep(config.Duration(time.Second*3, "drivers.startUpDelay"))

	topic := fmt.Sprintf("$node/%s/driver/%s", config.Serial(), driver.GetModuleInfo().ID)

	announcement := driver.GetModuleInfo()

	announcement.ServiceAnnouncement = model.ServiceAnnouncement{
		Schema: "http://schema.ninjablocks.com/service/driver",
	}

	_, err := c.exportService(driver, topic, announcement)

	if err != nil {
		return err
	}

	if config.Bool(false, "autostart") {
		err := c.GetServiceClient(topic).Call("start", struct{}{}, nil, time.Second*20)
		if err != nil {
			c.log.Fatalf("Failed to autostart driver: %s", err)
		}
	}

	return nil
}
Пример #5
0
// ExportApp Exports an app using the 'app' protocol, and announces it
func (c *Connection) ExportApp(app App) error {

	if app.GetModuleInfo().ID == "" {
		panic("You must provide an ID in the package.json")
	}
	topic := fmt.Sprintf("$node/%s/app/%s", config.Serial(), app.GetModuleInfo().ID)

	announcement := app.GetModuleInfo()

	announcement.ServiceAnnouncement = model.ServiceAnnouncement{
		Schema: "http://schema.ninjablocks.com/service/app",
	}

	_, err := c.exportService(app, topic, announcement)

	if err != nil {
		return err
	}

	if config.Bool(false, "autostart") {
		err := c.GetServiceClient(topic).Call("start", struct{}{}, nil, time.Second*20)
		if err != nil {
			c.log.Fatalf("Failed to autostart app: %s", err)
		}
	}

	return nil
}
Пример #6
0
func (c *HomeCloud) AutoStartModules() {

	do := func(name string, task string) error {
		return c.Conn.SendNotification("$node/"+config.Serial()+"/module/"+task, name)
	}

	interval := config.MustDuration("homecloud.autoStart.interval")

	for _, name := range c.findAutoStartModules() {
		log.Infof("-- (Re)starting '%s'", name)

		err := do(name, "stop")
		if err != nil {
			log.Fatalf("Failed to send %s stop message! %s", name, err)
		}

		time.Sleep(interval)

		err = do(name, "start")
		if err != nil {
			log.Fatalf("Failed to send %s start message! %s", name, err)
		}
	}

}
func NewSystemPane(conn *ninja.Connection) Pane {

	pane := &SystemPane{
		log:   logger.GetLogger("SystemPane"),
		code:  "0000",
		color: "green",
	}

	status := conn.GetServiceClient("$device/:deviceId/component/:componentId")
	status.OnEvent("status", func(statusEvent *StatusEvent, values map[string]string) bool {
		if deviceId, ok := values["deviceId"]; !ok {
			return true
		} else if deviceId != config.Serial() {
			return true
		} else {
			params, _ := json.Marshal(statusEvent)
			pane.log.Infof("$device/%s/component/%s - %s", deviceId, values["componentId"], params)
			pane.code = statusEvent.Code
			pane.color = statusEvent.Color
			if pane.color == "" {
				pane.color = "green"
			}
			return true
		}
	})

	return pane
}
Пример #8
0
func activate(client *http.Client, url string) (*credentials, error) {

	log.Debugf("Requesting url: %s", url)

	resp, err := client.Get(url)
	if err != nil {
		return nil, err
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	if resp.StatusCode == http.StatusRequestTimeout {
		return nil, nil
	}

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("Failed to activate: %s - %s", resp.Status, body)
	}

	log.Debugf("Got response: %s", body)

	var response nodeClaimResponse
	err = json.Unmarshal(body, &response)

	if err != nil {
		log.Fatalf("Failed to unmarshal credentials from cloud: %s (%s)", body, err)
	}

	if response.Data.NodeID != config.Serial() {
		log.Fatalf("Incorrect node id returned from pairing! Expected %s got %s", config.Serial(), response.Data.NodeID)
	}

	if response.Data.UserID == "" || response.Data.Token == "" || response.Data.SphereNetworkKey == "" {
		log.Fatalf("Invalid credentials (missing value): %s", body)
	}

	return &credentials{
		UserID:           response.Data.UserID,
		Token:            response.Data.Token,
		SphereNetworkKey: response.Data.SphereNetworkKey,
	}, err
}
Пример #9
0
func (d *NodeDevice) GetDeviceInfo() *model.Device {
	if d.modelDevice == nil {
		name := "Spheramid " + config.Serial()
		sphereVersion := config.SphereVersion()
		d.modelDevice = &model.Device{
			NaturalID:     config.Serial(),
			NaturalIDType: "node",
			Name:          &name,
			Signatures: &map[string]string{
				"ninja:manufacturer":  "Ninja Blocks Inc.",
				"ninja:productName":   "Spheramid",
				"ninja:thingType":     "node",
				"ninja:sphereVersion": sphereVersion,
			},
		}
	}
	return d.modelDevice
}
func NewLedController(conn *ninja.Connection) (*LedController, error) {

	s, err := util.GetLEDConnection()

	if err != nil {
		log.Fatalf("Failed to get connection to LED matrix: %s", err)
	}

	// Send a blank image to the led matrix
	util.WriteLEDMatrix(image.NewRGBA(image.Rect(0, 0, 16, 16)), s)

	controller := &LedController{
		conn:          conn,
		pairingLayout: ui.NewPairingLayout(),
		serial:        s,
		waiting:       make(chan bool),
	}

	conn.MustExportService(controller, "$node/"+config.Serial()+"/led-controller", &model.ServiceAnnouncement{
		Schema: "/service/led-controller",
	})

	conn.MustExportService(controller, "$home/led-controller", &model.ServiceAnnouncement{
		Schema: "/service/led-controller",
	})

	if config.HasString("siteId") {
		log.Infof("Have a siteId, checking if homecloud is running")
		// If we have just started, and we have a site, and homecloud is running... enable control!
		go func() {
			siteModel := conn.GetServiceClient("$home/services/SiteModel")
			for {

				if controller.commandReceived {
					log.Infof("Command has been received, stopping search for homecloud.")
					break
				}

				err := siteModel.Call("fetch", config.MustString("siteId"), nil, time.Second*5)

				if err != nil {
					log.Infof("Fetched site to enableControl. Got err: %s", err)
				} else if err == nil && !controller.commandReceived {
					controller.EnableControl()
					break
				}
				time.Sleep(time.Second * 5)
			}
		}()
	}

	return controller, nil
}
Пример #11
0
func showPane() {

	led := driver.conn.GetServiceClient("$node/" + config.Serial() + "/led-controller")
	fmt.Println(led)
	err := led.Call("displayIcon", ledmodel.IconRequest{
		Icon: "orphaned.gif",
	}, nil, 0)

	if err != nil {
		fmt.Println("========")
		fmt.Println(err)
		fmt.Println("========")
	}
}
Пример #12
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)
}
Пример #13
0
func Start() {

	log.Infof("Starting client on Node: %s", config.Serial())

	conn, err := ninja.Connect("client")
	if err != nil {
		log.Fatalf("Failed to connect to sphere: %s", err)
	}

	client := &client{
		conn:        conn,
		led:         conn.GetServiceClient("$home/led-controller"),
		foundMaster: make(chan bool),
	}

	if !config.IsPaired() {
		err = UpdateSphereAvahiService(false, false)
		if err != nil {
			log.Fatalf("Failed to update avahi service: %s", err)
		}
	}

	client.updatePairingLight("black", false)

	conn.SubscribeRaw("$sphere/bridge/status", client.onBridgeStatus)

	client.start()

	log.Infof("Client started.")

	err = UpdateSphereAvahiService(true, client.master)
	if err != nil {
		log.Fatalf("Failed to update avahi service: %s", err)
	}

	if runtime.GOOS == "linux" {
		go func() {
			err := client.ensureTimezoneIsSet()
			if err != nil {
				log.Warningf("Could not save timezone: %s", err)
			}
		}()
	}

	listenToSiteUpdates(conn)
}
Пример #14
0
func (c *client) pair() error {

	var boardType string
	if config.HasString("boardType") {
		boardType = config.MustString("boardType")
	} else {
		boardType = fmt.Sprintf("custom-%s-%s", runtime.GOOS, runtime.GOARCH)
	}

	log.Debugf("Board type: %s", boardType)

	client := &http.Client{
		Timeout: time.Second * 60, // It's 20sec on the server so this *should* be ok
	}

	if config.Bool(false, "cloud", "allowSelfSigned") {
		log.Warningf("Allowing self-signed certificate (should only be used to connect to development cloud)")
		client.Transport = &http.Transport{
			TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
		}
	}

	var creds *credentials

	for {
		url := fmt.Sprintf(config.MustString("cloud", "activation"), config.Serial(), getLocalIP(), boardType)

		log.Debugf("Activating at URL: %s", url)

		var err error
		creds, err = activate(client, url)

		if err != nil {
			log.Warningf("Activation error : %s", err)
			log.Warningf("Sleeping for 3sec")
			time.Sleep(time.Second * 3)
		} else if creds != nil {
			break
		}
	}

	log.Infof("Got credentials. User: %s", creds.UserID)

	return saveCreds(creds)
}
Пример #15
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
}
Пример #16
0
func getUser(driver *HueDriver, bridge *hue.Bridge) *hue.User {
	var user *hue.User
	var err error
	noUser := true
	retries := 0
	serial := config.Serial()
	username := serial + serial //username must be long 10-40 characters
	isvaliduser, err := bridge.IsValidUser(username)
	if err != nil {
		log.Warningf("Problem determining if hue user is valid")
	}

	var notificationTime time.Time

	if isvaliduser {
		user = hue.NewUserWithBridge(username, bridge)
	} else {
		for noUser {
			user, err = bridge.CreateUser("ninjadevice", username)
			if err != nil {
				if strings.Contains(err.Error(), "101") { // there's probably a nicer way to check this
					retries++
					log.Debugf("Couldn't make user, push link button. Retry: %d", retries)

					if time.Since(notificationTime) > time.Minute*5 {
						notificationTime = time.Now()
						driver.sendEvent("notification", pushButtonNotification)
					}

					time.Sleep(time.Second * 2) //this sucks

				} else {
					log.Debugf("Error creating user %s", err)
					time.Sleep(time.Second * 20)
				}
			}

			if user != nil {
				noUser = false
			}
		}
	}
	return user
}
Пример #17
0
func NewLight(driver *Driver, D int, name string, port *arduino.Arduino) error {
	light, err := devices.CreateLightDevice(driver, &model.Device{
		NaturalID:     fmt.Sprintf("%s-%d", config.Serial(), D),
		NaturalIDType: "block-arduino",
		Name:          &name,
		Signatures: &map[string]string{
			"ninja:manufacturer": "Ninja Blocks Inc",
			"ninja:productType":  "Light",
			"ninja:thingType":    "light",
		},
	}, driver.Conn)

	if err != nil {
		log.FatalError(err, "Could not create light device")
	}

	if err := light.EnableOnOffChannel(); err != nil {
		log.FatalError(err, "Could not enable hue on-off channel")
	}

	if err := light.EnableBrightnessChannel(); err != nil {
		log.FatalError(err, "Could not enable hue brightness channel")
	}

	if err := light.EnableColorChannel("temperature", "hue"); err != nil {
		log.FatalError(err, "Could not enable color channel")
	}

	port.OnDeviceData(func(data arduino.DeviceData) {
		if data.D == D {
			spew.Dump("Light Data!", data)
		}
	})

	return nil
}
Пример #18
0
func (b *TinyBus) connect() {

	b.connecting.Add(1)
	defer func() {
		b.connecting.Done()

		b.publish(&proto.Publish{
			Header: proto.Header{
				Retain: true,
			},
			TopicName: fmt.Sprintf("node/%s/module/%s/state/connected", config.Serial(), b.id),
			Payload:   proto.BytesPayload([]byte("true")),
		})
	}()

	var conn wrappedConn
	for {
		tcpConn, err := net.Dial("tcp", b.host)
		if err == nil {
			conn = wrappedConn{
				Conn: tcpConn,
				done: make(chan bool, 1),
			}
			break
		}

		//log.Warningf("Failed to connect to: %s", err)
		time.Sleep(time.Millisecond * 500)
	}

	if b.mqtt != nil {
		log.Infof("Reconnected to mqtt server")
	}

	mqtt := mqtt.NewClientConn(conn)
	mqtt.ClientId = b.id

	err := mqtt.ConnectCustom(&proto.Connect{
		WillFlag:    true,
		WillQos:     0,
		WillRetain:  true,
		WillTopic:   fmt.Sprintf("$node/%s/module/%s/state/connected", config.Serial(), b.id),
		WillMessage: "false",
	})

	if err != nil {
		log.Fatalf("MQTT Failed to connect to: %s", err)
	}

	b.mqtt = mqtt

	b.connected()

	for _, s := range b.subscriptions {
		if !s.cancelled {
			b.subscribe(s)
		}
	}

	go func() {
		for m := range mqtt.Incoming {
			b.onIncoming(m)
		}
	}()

	go func() {
		<-conn.done
		b.disconnected()
		if !b.destroyed {
			b.connect()
		}
	}()
}
Пример #19
0
package rest

import (
	"encoding/json"
	"io/ioutil"
	"net/http"

	"github.com/ninjasphere/go-ninja/config"
	"github.com/ninjasphere/go-ninja/logger"
)

var wrapped = true
var log = logger.GetLogger("HomeCloud.Router")

// doesn't change so we cache it.
var NodeID = config.Serial()

// ResponseWrapper used to wrap responses from the API
type ResponseWrapper struct {
	Type string      `json:"type,omitempty"`
	Data interface{} `json:"data,omitempty"`
}

// WriteServerErrorResponse Builds the wrapped error for the client
func WriteServerErrorResponse(msg string, code int, w http.ResponseWriter) {

	body, err := json.Marshal(&ResponseWrapper{Type: "error", Data: msg})

	if err != nil {
		log.Errorf("Unable to serialise response: %s", err)
	}
Пример #20
0
func (c *client) start() {

	if !config.IsPaired() {
		log.Infof("Client is unpaired. Attempting to pair.")
		if err := c.pair(); err != nil {
			log.Fatalf("An error occurred while pairing. Restarting. error: %s", err)
		}

		log.Infof("Pairing was successful.")
		// We reload the config so the creds can be picked up
		config.MustRefresh()

		if !config.IsPaired() {
			log.Fatalf("Pairing appeared successful, but I did not get the credentials. Restarting.")
		}

	}

	log.Infof("Client is paired. User: %s", config.MustString("userId"))

	if !config.NoCloud() {

		mesh, err := refreshMeshInfo()

		if err == errorUnauthorised {
			log.Warningf("UNAUTHORISED! Unpairing.")
			c.unpair()
			return
		}

		if err != nil {
			log.Warningf("Failed to refresh mesh info: %s", err)
		} else {
			log.Debugf("Got mesh info: %+v", mesh)
		}

		config.MustRefresh()

		if !config.HasString("masterNodeId") {
			log.Warningf("We don't have any mesh information. Which is unlikely. But we can't do anything without it, so restarting client.")
			time.Sleep(time.Second * 10)
			os.Exit(0)
		}

	}

	if config.MustString("masterNodeId") == config.Serial() {
		log.Infof("I am the master, starting HomeCloud.")

		cmd := exec.Command("start", "sphere-homecloud")
		cmd.Output()
		go c.exportNodeDevice()

		c.master = true
	} else {
		log.Infof("I am a slave. The master is %s", config.MustString("masterNodeId"))

		// TODO: Remove this when we are running drivers on slaves
		cmd := exec.Command("stop", "sphere-director")
		cmd.Output()

		c.masterReceiveTimeout = time.AfterFunc(orphanTimeout, func() {
			c.setOrphaned()
		})

	}

	go func() {

		log.Infof("Starting search for peers")

		for {
			c.findPeers()
			time.Sleep(time.Second * 30)
		}
	}()
}
Пример #21
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)
}
Пример #22
0
func (c *client) unpair() {
	log.Infof("Unpairing")
	c.conn.SendNotification(fmt.Sprintf("$node/%s/unpair", config.Serial()), nil)
}