sift

package module
v0.0.0-...-b0e10ae Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 26, 2016 License: Apache-2.0 Imports: 21 Imported by: 0

README

SIFT (Simple Interface for Functional Things)

Godoc Build Status

SIFT is a framework that enables developers to control connected devices without having to understand their implementation details.

A SIFT server:

  • Regularly scans available networks to discover new connected devices
  • Once discovered, actively gathers the state of connected devices to produce a synchronized internal collection of device states
  • Allows developers to query the state of connected devices, and to subscribe to notifications when they are updated or removed
  • Allows developers to control controllable devices

Note: SIFT is in early development. The API may change in future updates.

Getting Started

SIFT requires Golang 1.4+ and python 2.7.

# install
go get github.com/upwrd/sift
pip install -r $GOPATH/github.com/upwrd/sift/requirements.txt

# run interactive setup & example apps:
go run github.com/upward/sift/main/interactive.go

Examples

Movies and Chill

Automatically turn down the lights when watching a movie

package main

import (
  "github.com/upwrd/sift"
  "github.com/upwrd/sift/types"
  "github.com/upwrd/sift/notif"
)

func main() {
  // Create a SIFT server at the default path ("sift.db")
  server, _ := sift.NewServer(sift.DefaultDBFilepath) // err ignored
  server.AddDefaults() // err ignored
  go server.Serve() // starts discovering and syncing devices

  token := server.Login() // authenticate with SIFT

  // Subscribe to receive notifications when media players or lights are added
  // or removed
  mediaFilter := notif.ComponentFilter{Type: types.ComponentTypeMediaPlayer}
  lightsFilter := notif.ComponentFilter{Type: types.ComponentTypeLightEmitter}
  listener := server.Listen(token, lightsFilter, mediaFilter) // this starts listening

  // Each time a light or media player is added or changed...
  for range listener {
    // ...determine which lights should be dim and which should be bright...
    db, _ := server.DB()
    lightsQuery := `
    			SELECT c1.device_id, c1.name, num_playing_mpls_in_room
    			FROM light_emitter_state l1
    			JOIN component c1 ON l1.id=c1.id
    			JOIN device d1 ON c1.device_id=d1.id
    			JOIN (SELECT d.location_id loc,
    				SUM (CASE WHEN m.play_state="PLAYING" THEN 1 ELSE 0 END) as num_playing_mpls_in_room
    				FROM media_player_state m
    				JOIN component c ON m.id=c.id
    				JOIN device d ON c.device_id=d.id
    				GROUP BY d.location_id)
    				ON d1.location_id=loc;`
    type result struct {
    	DeviceID                     int64 `db:"device_id"`
    	Name                         string
    	NumPlayingMediaPlayersInRoom int `db:"num_playing_mpls_in_room"`
    }
    results := []result{}
    db.Select(&results, lightsQuery) // err ignored

    // ...then tell the SIFT server to make it so
  	for _, result := range results {
  		var intent types.SetLightEmitterIntent // SIFT uses intents to control things
  		if result.NumPlayingMediaPlayersInRoom > 0 { // a movie is playing in the room ...
  			intent.BrightnessInPercent = lightsLow // ...bring the lights down low
  		} else { // no movies in this room...
  			intent.BrightnessInPercent = lightsHigh // ...bring the lights up
  		}
  		target := types.ComponentID{
  			DeviceID: types.DeviceID(result.DeviceID),
  			Name:     result.Name,
  		}
  		// Send the intent to the SIFT server, which will try to make it real
  		server.EnactIntent(target, intent) // err ignored
    }
  }
}

Data Model

To learn more about the SIFT data types and structure, see: https://github.com/upwrd/sift/tree/master/types

Compatibility

Device Networks Components Notes
Connected By TCP Lighting IPv4 Light Emitters Must press the sync button on the Connected By TCP Hub on first run.
Google Chromecast IPv4 Media Players Does not work with all Chromecast apps, most notably Netflix :(

To learn how to add support for new devices and device types, see https://github.com/upwrd/sift/tree/master/adapter

Licensing

Copyright 2016 Upward Automation, Inc.

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

A copy of the license is available in the repository's LICENSE file.

Documentation

Overview

Package sift - the Simple Interface of Functional Things.

SIFT makes it easy for developers to write code which understands and manipulates connected devices. A SIFT Server presents an authoritative, centralized repository of Devices (physical units) and their functional Components. SIFT Components are generically typed, meaning a developer can manipulate any number of Light Emitters or Media Players without understanding their specific implementations (e.g. Philips Hue / Google Chromecast) and their implementation details (such as wireless protocols and API specifics).

Example
// start a new SIFT server
serv, _ := sift.NewServer("") // "" indicates a random, temporary file
go serv.Serve()
defer func() {
	serv.StopAndWait(1 * time.Minute)
}()

// With no running adapters, the SIFT server should not report any connected components
components, _ := serv.GetComponents(db.ExpandAll)
fmt.Printf("len(components) == %v", len(components)) // "len(components) == 0"

// Start an example service server running on localhost port 12345. This is
// a demonstration server that you can control programatically for testing.
exServ := example.NewServer(uint16(12345))
go exServ.Serve()
defer exServ.Stop()
<-time.After(500 * time.Millisecond)

// Add a light to the example service. This code is specific to the example
// server. In a real-world application, a light would be added when it is
// plugged in and connected as defined by the manufacturer.
light1 := example.Light{
	IsOn:            true,
	OutputInPercent: 100,
}
device1 := example.Device{
	Components: map[string]example.Component{"light1": light1},
}
exServ.SetDevice("device1", device1) // This should produce an update

// Add an Adapter Factory to monitor the example service
serv.AddAdapterFactory(example.NewFactory(12345))

components, _ = serv.GetComponents(db.ExpandAll)
fmt.Printf("len(components) == %v", len(components)) // "len(components) == 1"
fmt.Printf("components:")
for id, component := range components {
	fmt.Printf("   component %v: %+v", id, component)
}
Output:

Index

Examples

Constants

View Source
const DefaultDBFilepath = "sift.db"

A suggested default database name.

Variables

View Source
var Log = logging.Log.New("pkg", "sift")

Log is used to log messages for the sift package. Logs are disabled by default; use sift/logging.SetLevel() to set log levels for all packages, or Log.SetHandler() to set a custom handler for this package (see: https://godoc.org/gopkg.in/inconshreveable/log15.v2)

Functions

func SetLogLevel

func SetLogLevel(lvlstr string)

SetLogLevel is a convenient wrapper to sift/logging.SetLevelStr() Use it to quickly set the log level for all loggers in this package. For more control over logging, manipulate logging.Log directly, as outlined by https://godoc.org/gopkg.in/inconshreveable/log15.v2#hdr-Library_Use (see also: github.com/inconshreveable/log15)

Types

type Server

type Server struct {
	// SiftDB provides direct access to the underlying sqlite database through
	// Jason Moiron's wonderful sqlx API (see: github.com/jmoiron/sqlx)
	*db.SiftDB

	auth.Authorizor // Provides login/authorize methods
	notif.Provider  // Provides notification pub/sub methods
	notif.Receiver  // Adds methods to post notifications
	// contains filtered or unexported fields
}

Server maintains the state of SIFT objects and provides methods for listening to, retrieving, and manipulating them. You should always initialize Servers with a call to NewServer()

func NewServer

func NewServer(dbpath string) (*Server, error)

NewServer constructs a new SIFT Server, using the SIFT database at the provided path (or creating a new one it does not exist). Be sure to start the Server with Serve()

func (*Server) AddAdapterFactory

func (s *Server) AddAdapterFactory(factory adapter.Factory) (string, error)

AddAdapterFactory adds an AdapterFactory to the Server. Once added, the Server will begin searching for services matching the AdapterFactory's description. If any are found, the Server will use the AdapterFactory to create an Adapter to handle the sevice.

func (*Server) AddDefaults

func (s *Server) AddDefaults() error

AddDefaults adds default Adapter Factories to the SIFT server

func (*Server) EnactIntent

func (s *Server) EnactIntent(target types.ComponentID, intent types.Intent) error

EnactIntent attempts to fulfill an intent, usually to change the state of a particular Component. For a list of possible intents, see sift/types

func (*Server) Serve

func (s *Server) Serve()

Serve starts running the SIFT server. Most of the time you'll want to call in a goroutine; or as a Suture Service (see github.com/thejerf/suture)

func (*Server) Stop

func (s *Server) Stop()

Stop stops the Server (does not block)

func (*Server) StopAndWait

func (s *Server) StopAndWait(timeout time.Duration) error

StopAndWait stops the SIFT server and waits for it to complete.

Directories

Path Synopsis
Package auth manages the authorization of SIFT users
Package auth manages the authorization of SIFT users
db
sql
network

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL