Example #1
0
func initOpenWeatherMapBackend(config map[string]interface{}, back *backend.Backend) error {

	apiKey, ok := utils.GetString("api_key", config, "")
	if !ok {
		logrus.Warnf("Backend: %s configuration option missing: 'apiKey'", back.Name())
		return nil // CHANGE
	}

	weather := weatherTracker{}

	weather.unit, _ = utils.GetString("unit", config, "C")
	language, _ := utils.GetString("language", config, "DE")
	weather.delay, _ = utils.GetInt("delay", config, 60)

	w, err := owm.NewCurrent(weather.unit, language, apiKey)
	if err != nil {
		logrus.Warnf("Backend: %s failed to update state", back.Name())
	}
	weather.current = w

	back.RegisterComponent <- &backend.Component{
		Internal:  &weather,
		Name:      "weather",
		Polling:   updateWeather,
		Type:      backend.WeatherTracker,
		StateSink: make(chan interface{}),
	}

	return nil
}
Example #2
0
// Start start the http rest api plugin
func (cli *HTTPAPIServer) Start(config map[string]interface{}) error {

	wwwroot, _ := utils.GetString("static", config, "")
	addr, _ := utils.GetString("listen", config, ":8080")
	secret, ok := utils.GetString("secret", config, randomString(128))
	if !ok {
		logrus.Warnf("Plugins: httpapi: generated new secret: %s", secret)
	}

	jwt_middleware := &jwt.JWTMiddleware{
		Key:        []byte(secret),
		Realm:      "jwt auth",
		Timeout:    time.Hour,
		MaxRefresh: time.Hour * 24,
		Authenticator: func(userId string, password string) bool {
			return userId == "admin" && password == "admin"
		}}

	api := rest.NewApi()
	api.Use(rest.DefaultDevStack...)
	api.Use(&rest.IfMiddleware{
		Condition: func(request *rest.Request) bool {
			return request.URL.Path != "/login"
		},
		IfTrue: jwt_middleware,
	})

	router, err := rest.MakeRouter(
		rest.Post("/login", jwt_middleware.LoginHandler),
		rest.Get("/refresh_token", jwt_middleware.RefreshHandler),

		rest.Get("/", getApiVersion),
		rest.Get("/sensors", getAllSensors),
		rest.Get("/sensors/:backend.:sensor", getSensor),
		rest.Get("/services", getAllServices),
		rest.Get("/services/:backend.:service", getService),
		rest.Post("/services/:backend.:service", callService),
	)
	if err != nil {
		log.Fatal(err)
	}
	api.SetApp(router)

	http.Handle("/v1/", http.StripPrefix("/v1", api.MakeHandler()))

	if wwwroot != "" {
		logrus.Infof("Plugin: httpapi: service static content from %s", wwwroot)
		http.Handle("/ui/", http.StripPrefix("/ui", http.FileServer(http.Dir(wwwroot))))
	}

	go func() {
		logrus.Infof("Plugin: httpapi: starting on %s", addr)
		manners.ListenAndServe(addr, http.DefaultServeMux)
	}()

	return nil
}
Example #3
0
/*
This backend let GoHome connect to an MPD server and retrieve information about
it's current status as well as perform some basic operations like play/pause.

In order to use this backend add something like the following to your configuration:

 backends:
  - type: mpd
	  endpoint: 192.168.0.10:6600

Data within state changes published on the eventbus will have the following
format:
 {
  "TOBEDONE": "TOBEDONE"
 }

In addition, the following service calls are exported

  <mpd-name>.play()
	<mpd-nam>.pause()


*/
func MediaPlayerDaemonBackend(config map[string]interface{}, back *backend.Backend) error {
	url, ok := utils.GetString("endpoint", config, "localhost:6600")
	if !ok {
		logrus.Warn("Backend: %s missing configuration option 'endpoint'. assuming 'localhost:6600'", back.Name())
	}

	intern := mpdClientComponent{
		url:       url,
		connected: false,
	}

	comp := &backend.Component{
		Name:      "mpd",
		Internal:  &intern,
		StateSink: make(chan interface{}),
		Polling:   updateMPD,
	}
	back.RegisterComponent <- comp

	service.ServiceRegistry.Register(comp, "pause", "Pause MPD playback", intern.pause,
		map[string]string{}, map[string]string{})
	service.ServiceRegistry.Register(comp, "play", "Resume MPD playback", intern.play,
		map[string]string{}, map[string]string{})

	go (&intern).connect()
	return nil
}
Example #4
0
/*
This backend provides a device tracker by querying Netgear routers via the
proprietary Genie protocol (see github.com/nethack42/go-netgear).

Note that only newer Netgear routers are supported. There might be ways
to query this information on older models too, but this is currently not
supported.

In order to use this backend within your GoHome domain add something like the
following to your configuration file:

 backends:
  - type: netgear
	  name: MyRouter1
		host: 192.168.0.1
		username: admin
		password: mysecretpassword
		track_only_known: y
		devices:
		 "00:00:00:00:00:11": "Bob"
		 "11:22:33:44:55:66": "Alice"

Note that none of those fields are required to be set. If obmitted, `host`
defaults to "routerlogin.net", `username` to "admin" and `password` to "".
If `track_only_known` is not set it defaults to "n" and thus all devices
connected to the router will be returned. If `track_only_known` is set to "y"
only devices with their MAC address configured in `devices` will be tracked.

Data in state changes published by this backend have the following format:

 [
   [TO_BE_DONE]
 ]


*/
func NetgearDeviceTrackerBackend(config map[string]interface{}, back *backend.Backend) error {
	deviceList := make(map[string]string)

	host, _ := utils.GetString("host", config, "routerlogin.net")
	username, _ := utils.GetString("username", config, "admin")
	password, _ := utils.GetString("password", config, "")
	trackS, _ := utils.GetString("only_known_devices", config, "y")

	track := true
	// Re do this more elegant
	for _, str := range []string{"n", "no", "0", "false"} {
		if str == trackS {
			track = false
		}
	}

	if config["devices"] != nil {
		devs := config["devices"].(map[string]interface{})
		for mac, name := range devs {
			deviceList[mac] = name.(string)
		}
	}

	component := &backend.Component{
		StateSink: make(chan interface{}),
		Polling:   updateNetgear,
		Name:      "devices",
		Type:      backend.DeviceTracker,
		Internal: &netgearComponent{
			router:         netgear.NewRouter(host, username, password),
			knownDevices:   deviceList,
			trackOnlyKnown: track,
		},
	}
	back.RegisterComponent <- component

	return nil
}
Example #5
0
/*
Backend providing a JSON based component connector via Websockets.

Configuration

In order to use this websocket based connector add something like the following
to your configuration file:

 backends:
  - type: websocket
    listen: httpapi


Description

The `listen` parameter may specifiy an endpoint the websocket server should
be bound to (e.g. localhost:8080, 0.0.0.0:6600, :9000). As a special case,
"httpapi" can be specified. If so, the websocket connector backend attaches it
self the the HTTP API plugin and will therefore share it's HTTP server. In the
latter case the access URI will be "/devices" instead of "/".


API Description

First a Websocket connection must be established. During this HTTP request,
the device name and type must be provided. Consider the following Websocket URL:

  ws://example.com/device?name=<YourDeviceName>&type=<YourDeviceType>

If this module is not used in conjunction with the HTTP API plugin the request
URL should look like:

	ws://example.com/?name=<YourDeviceName>&type=<YourDeviceType>

If both parameters are provided a websocket connection can be initialized.

Now the client can start providing data by sending JSON messages following this
format:

  {
	   "topic": "state_changed",
		 "state": <YourStateOfWhatsOEver>
  }

Everything provided in `state` will be published on the internal eventbus and
thus be available for all subscribers.

Note that a WebSocket component can only send state updates and register service
calles. It is prohibited to execute other services or send other topics then
"state_changed" and "register_service".

Invalid messages will be logged and the respective connection will be closed.

In order to register new service calles within GoHome a Websocket component could
send a service registration request with the following format:

 {
   "topic": "register_service",
   "name": "<NameOfYourService>",
   "required": {
   "param1": "This is the first and only parameter required"
  },
  "optional" {
   "param2": "This is an optional parameter which defaults to: some-value"
  }
 }

The `name`, `required` and `optional` fields are required to be set during
service registration. However, `required` and `optional` may be an empty
map/object.

Whenever a service should be executed GoHome send the following request via the
Websocket connection:

{
	"topic": "service_call",
	"method": "<NameOfYourService>",
	"params": [<ListOfParameters]
}

BUG: Currently it is NOT possible for Websocket components to return a response
to the caller! All service call will immediately return nil.

*/
func WebsocketConnectorBackend(config map[string]interface{}, back *backend.Backend) error {
	logrus.Infof("Started backend %s with name %s", config["type"].(string), back.Name())

	listen, _ := utils.GetString("listen", config, "httpapi")

	back.OnShutdown = shutdownWebsockets
	back.Internal = &websocketServer{
		clients: make(map[string]peer),
		name:    back.Name(),
		back:    back,
	}

	if listen == "httpapi" {
		logrus.Infof("Backend: %s: attaching to default HTTP server (httpapi) on URI /device", back.Name())
		http.Handle("/device", back.Internal.(*websocketServer))
	} else {
		logrus.Infof("Backend: %s: listening on %s", back.Name(), listen)
		go http.ListenAndServe(listen, back.Internal.(*websocketServer))
	}
	return nil
}
Example #6
0
func NotifyMyAndroidBackend(config map[string]interface{}, back *backend.Backend) error {
	apiKey, ok := utils.GetString("api_key", config, "")
	if !ok {
		return fmt.Errorf("Backend: %s missing configuration option 'api_key'", back.Name())
	}

	comp := &notifyMyAndroid{
		nma:     nma.New(apiKey),
		backend: back,
	}
	back.Internal = comp

	service.ServiceRegistry.Register(back, "notify", "Send a notification via Notify-My-Android",
		comp.Notify, map[string]string{
			"subject": "Subject of the notification",
			"message": "Message part of the notification",
		},
		map[string]string{
			"priority": "Priority of the notification. Defaults to 0",
		})

	return nil
}
Example #7
0
func (mgr *pluginManager) Start(options *config.Config) error {

	plugins, err := options.List("plugins")
	if err != nil {
		logrus.Warnf("Plugins: No configuration section found")
		return err
	}

	for _, plug := range plugins {
		plugin_conf := plug.(map[string]interface{})

		pluginType, ok := utils.GetString("type", plugin_conf, "")
		if !ok {
			logrus.Warnf("Plugins: failed to get type for '%s'", plugin_conf)
			continue
		}

		plugin := mgr.known_plugins[pluginType]

		if plugin != nil {
			err = plugin.Start(plugin_conf)
			if err != nil {
				logrus.Warnf("Plugins: failed to start plugin: '%s' error: '%s'", plugin.Name(), err)
				continue
			}

			mgr.plugins[plugin.Name()] = plugin
			logrus.Infof("Plugins: plugin started successfully: '%s'", plugin.Name())
		} else {
			logrus.Warnf("Plugins: plugin does not exists: '%s'", pluginType)
		}
	}

	eventbus.EventBus.Subscribe(eventbus.HOME_SHUTDOWN, mgr.Stop)

	return nil
}