Giter Club home page Giter Club logo

golifx's Introduction

Build Status GoDoc License-MIT

Note: This library is at a moderately early stage - functionality is quite solid, but the V2 protocol implementation needs documentation and tests.

v1.0.0 breaks the API for subscriptions - all subscription targets now just embed common.SubscriptionProvider, see the documentation for those methods below.

You may find binaries for a trivial CLI application that allows querying and controlling your LIFX devices under releases.

Alternatively, if you have Go installed, you may install the lifx command from source like so:

go get -u github.com/pdf/golifx/cmd/lifx

The lifx command will be available at ${GOPATH}/bin/lifx

golifx

-- import "github.com/pdf/golifx"

Package golifx provides a simple Go interface to the LIFX LAN protocol.

Based on the protocol documentation available at: http://lan.developer.lifx.com/

Also included in cmd/lifx is a small CLI utility that allows interacting with your LIFX devices on the LAN.

In various parts of this package you may find references to a Device or a Light. The LIFX protocol makes room for future non-light devices by making a light a superset of a device, so a Light is a Device, but a Device is not necessarily a Light. At this stage, LIFX only produces lights though, so they are the only type of device you will interact with.

Usage

const (
	// VERSION of this library
	VERSION = "1.0.4"
)

func SetLogger

func SetLogger(logger common.Logger)

SetLogger allows assigning a custom levelled logger that conforms to the common.Logger interface. To capture logs generated during client creation, this should be called before creating a Client. Defaults to common.StubLogger, which does no logging at all.

type Client

type Client struct {
	common.SubscriptionProvider
	sync.RWMutex
}

Client provides a simple interface for interacting with LIFX devices. Client can not be instantiated manually or it will not function - always use NewClient() to obtain a Client instance.

func NewClient

func NewClient(p common.Protocol) (*Client, error)

NewClient returns a pointer to a new Client and any error that occurred initializing the client, using the protocol p. It also kicks off a discovery run.

func (*Client) Close

func (c *Client) Close() error

Close signals the termination of this client, and cleans up resources

func (*Client) GetDeviceByID

func (c *Client) GetDeviceByID(id uint64) (common.Device, error)

GetDeviceByID looks up a device by its id and returns a common.Device. May return a common.ErrNotFound error if the lookup times out without finding the device.

func (*Client) GetDeviceByLabel

func (c *Client) GetDeviceByLabel(label string) (common.Device, error)

GetDeviceByLabel looks up a device by its label and returns a common.Device. May return a common.ErrNotFound error if the lookup times out without finding the device.

func (*Client) GetDevices

func (c *Client) GetDevices() (devices []common.Device, err error)

GetDevices returns a slice of all devices known to the client, or common.ErrNotFound if no devices are currently known.

func (*Client) GetGroupByID

func (c *Client) GetGroupByID(id string) (common.Group, error)

GetGroupByID looks up a group by its id and returns a common.Group. May return a common.ErrNotFound error if the lookup times out without finding the group.

func (*Client) GetGroupByLabel

func (c *Client) GetGroupByLabel(label string) (common.Group, error)

GetGroupByLabel looks up a group by its label and returns a common.Group. May return a common.ErrNotFound error if the lookup times out without finding the group.

func (*Client) GetGroups

func (c *Client) GetGroups() (groups []common.Group, err error)

GetGroups returns a slice of all groups known to the client, or common.ErrNotFound if no groups are currently known.

func (*Client) GetLightByID

func (c *Client) GetLightByID(id uint64) (light common.Light, err error)

GetLightByID looks up a light by its id and returns a common.Light. May return a common.ErrNotFound error if the lookup times out without finding the light, or common.ErrDeviceInvalidType if the device exists but is not a light.

func (*Client) GetLightByLabel

func (c *Client) GetLightByLabel(label string) (common.Light, error)

GetLightByLabel looks up a light by its label and returns a common.Light. May return a common.ErrNotFound error if the lookup times out without finding the light, or common.ErrDeviceInvalidType if the device exists but is not a light.

func (*Client) GetLights

func (c *Client) GetLights() (lights []common.Light, err error)

GetLights returns a slice of all lights known to the client, or common.ErrNotFound if no lights are currently known.

func (*Client) GetLocationByID

func (c *Client) GetLocationByID(id string) (common.Location, error)

GetLocationByID looks up a location by its id and returns a common.Location. May return a common.ErrNotFound error if the lookup times out without finding the location.

func (*Client) GetLocationByLabel

func (c *Client) GetLocationByLabel(label string) (common.Location, error)

GetLocationByLabel looks up a location by its label and returns a common.Location. May return a common.ErrNotFound error if the lookup times out without finding the location.

func (*Client) GetLocations

func (c *Client) GetLocations() (locations []common.Location, err error)

GetLocations returns a slice of all locations known to the client, or common.ErrNotFound if no locations are currently known.

func (*Client) GetRetryInterval

func (c *Client) GetRetryInterval() *time.Duration

GetRetryInterval returns the currently configured retry interval for operations on this client

func (*Client) GetTimeout

func (c *Client) GetTimeout() *time.Duration

GetTimeout returns the currently configured timeout period for operations on this client

func (*Client) SetColor

func (c *Client) SetColor(color common.Color, duration time.Duration) error

SetColor broadcasts a request to change the color of all devices on the network.

func (*Client) SetDiscoveryInterval

func (c *Client) SetDiscoveryInterval(interval time.Duration) error

SetDiscoveryInterval causes the client to discover devices and state every interval. You should set this to a non-zero value for any long-running process, otherwise devices will only be discovered once.

func (*Client) SetPower

func (c *Client) SetPower(state bool) error

SetPower broadcasts a request to change the power state of all devices on the network. A state of true requests power on, and a state of false requests power off.

func (*Client) SetPowerDuration

func (c *Client) SetPowerDuration(state bool, duration time.Duration) error

SetPowerDuration broadcasts a request to change the power state of all devices on the network, transitioning over the specified duration. A state of true requests power on, and a state of false requests power off. Not all device types support transitioning, so if you wish to change the state of all device types, you should use SetPower instead.

func (*Client) SetRetryInterval

func (c *Client) SetRetryInterval(retryInterval time.Duration)

SetRetryInterval sets the retry interval for operations on this client. If a timeout has been set, and the retry interval exceeds the timeout, the retry interval will be set to half the timeout

func (*Client) SetTimeout

func (c *Client) SetTimeout(timeout time.Duration)

SetTimeout sets the time that client operations wait for results before returning an error. The special value of 0 may be set to disable timeouts, and all operations will wait indefinitely, but this is not recommended.

common

-- import "github.com/pdf/golifx/common"

Package common contains common elements for the golifx client and protocols

Usage

const (
	// DefaultTimeout is the default duration after which operations time out
	DefaultTimeout = 2 * time.Second
	// DefaultRetryInterval is the default interval at which operations are
	// retried
	DefaultRetryInterval = 100 * time.Millisecond
)
var (
	// ErrNotFound not found
	ErrNotFound = errors.New(`Not found`)
	// ErrProtocol protocol error
	ErrProtocol = errors.New(`Protocol error`)
	// ErrDuplicate already exists
	ErrDuplicate = errors.New(`Already exists`)
	// ErrInvalidArgument invalid argument
	ErrInvalidArgument = errors.New(`Invalid argument`)
	// ErrClosed connection closed
	ErrClosed = errors.New(`Connection closed`)
	// ErrTimeout timed out
	ErrTimeout = errors.New(`Timed out`)
	// ErrDeviceInvalidType invalid device type
	ErrDeviceInvalidType = errors.New(`Invalid device type`)
	// ErrUnsupported operation is not supported
	ErrUnsupported = errors.New(`Operation not supported`)
)

func ColorEqual

func ColorEqual(a, b Color) bool

ColorEqual tests whether two Colors are equal

func SetLogger

func SetLogger(logger Logger)

SetLogger wraps the supplied logger with a logPrefixer to denote golifx logs

type Client

type Client interface {
	GetTimeout() *time.Duration
	GetRetryInterval() *time.Duration
}

Client defines the interface required by protocols

type Color

type Color struct {
	Hue        uint16 `json:"hue"`        // range 0 to 65535
	Saturation uint16 `json:"saturation"` // range 0 to 65535
	Brightness uint16 `json:"brightness"` // range 0 to 65535
	Kelvin     uint16 `json:"kelvin"`     // range 2500° (warm) to 9000° (cool)
}

Color is used to represent the color and color temperature of a light. The color is represented as a 48-bit HSB (Hue, Saturation, Brightness) value. The color temperature is represented in K (Kelvin) and is used to adjust the warmness / coolness of a white light, which is most obvious when saturation is close zero.

func AverageColor

func AverageColor(colors ...Color) (color Color)

AverageColor returns the average of the provided colors

type Device

type Device interface {
	// Returns the ID for the device
	ID() uint64

	// GetLabel gets the label for the device
	GetLabel() (string, error)
	// SetLabel sets the label for the device
	SetLabel(label string) error
	// GetPower requests the current power state of the device, true for on,
	// false for off
	GetPower() (bool, error)
	// CachedPower returns the last known power state of the device, true for
	// on, false for off
	CachedPower() bool
	// SetPower sets the power state of the device, true for on, false for off
	SetPower(state bool) error
	// GetFirmwareVersion returns the firmware version of the device
	GetFirmwareVersion() (string, error)
	// CachedFirmwareVersion returns the last known firmware version of the
	// device
	CachedFirmwareVersion() string
	// GetProductName returns the product name of the device
	GetProductName() (string, error)

	// Device is a SubscriptionTarget
	SubscriptionTarget
}

Device represents a generic LIFX device

type ErrNotImplemented

type ErrNotImplemented struct {
	Method string
}

ErrNotImplemented not implemented

func (*ErrNotImplemented) Error

func (e *ErrNotImplemented) Error() string

Error satisfies the error interface

type EventExpiredDevice

type EventExpiredDevice struct {
	Device Device
}

EventExpiredDevice is emitted by a Client or Group when a Device is no longer known

type EventExpiredGroup

type EventExpiredGroup struct {
	Group Group
}

EventExpiredGroup is emitted by a Client or Group when a Group is no longer known

type EventExpiredLocation

type EventExpiredLocation struct {
	Location Location
}

EventExpiredLocation is emitted by a Client or Group when a Location is no longer known

type EventNewDevice

type EventNewDevice struct {
	Device Device
}

EventNewDevice is emitted by a Client or Group when it discovers a new Device

type EventNewGroup

type EventNewGroup struct {
	Group Group
}

EventNewGroup is emitted by a Client when it discovers a new Group

type EventNewLocation

type EventNewLocation struct {
	Location Location
}

EventNewLocation is emitted by a Client when it discovers a new Location

type EventUpdateColor

type EventUpdateColor struct {
	Color Color
}

EventUpdateColor is emitted by a Light or Group when its Color is updated

type EventUpdateLabel

type EventUpdateLabel struct {
	Label string
}

EventUpdateLabel is emitted by a Device or Group when its label is updated

type EventUpdatePower

type EventUpdatePower struct {
	Power bool
}

EventUpdatePower is emitted by a Device or Group when its power state is updated

type Group

type Group interface {
	// ID returns a base64 encoding of the device ID
	ID() string

	// Label returns the label for the group
	GetLabel() string

	// Devices returns the devices in the group
	Devices() []Device

	// Lights returns the lights in the group
	Lights() []Light

	// Returns the power state of the group, true if any members are on, false
	// if all members off. Returns error on communication errors.
	GetPower() (bool, error)

	// Returns the average color of lights in the group. Returns error on
	// communication error.
	GetColor() (Color, error)

	// SetColor requests a change of color for all devices in the group that
	// support color changes, transitioning over the specified duration
	SetColor(color Color, duration time.Duration) error
	// SetPower sets the power of devices in the group that support power
	// changes, state is true for on, false for off.
	SetPower(state bool) error
	// SetPowerDuration sets the power of devices in the group that support
	// power changes, transitioning over the speficied duration, state is true
	// for on, false for off.
	SetPowerDuration(state bool, duration time.Duration) error

	// Device is a SubscriptionTarget
	SubscriptionTarget
}

Group represents a group of LIFX devices

type Light

type Light interface {
	// SetColor changes the color of the light, transitioning over the specified
	// duration
	SetColor(color Color, duration time.Duration) error
	// GetColor requests the current color of the light
	GetColor() (Color, error)
	// CachedColor returns the last known color of the light
	CachedColor() Color
	// SetPowerDuration sets the power of the light, transitioning over the
	// speficied duration, state is true for on, false for off.
	SetPowerDuration(state bool, duration time.Duration) error

	// Light is a superset of the Device interface
	Device
}

Light represents a LIFX light device

type Location

type Location interface {
	// Location is a group
	Group
}

Location represents a locality-based group of LIFX devices

type Logger

type Logger interface {
	// Debugf handles debug level messages
	Debugf(format string, args ...interface{})
	// Infof handles info level messages
	Infof(format string, args ...interface{})
	// Warnf handles warn level messages
	Warnf(format string, args ...interface{})
	// Errorf handles error level messages
	Errorf(format string, args ...interface{})
	// Fatalf handles fatal level messages, and must exit the application
	Fatalf(format string, args ...interface{})
	// Panicf handles debug level messages, and must panic the application
	Panicf(format string, args ...interface{})
}

Logger represents a minimal levelled logger

var (
	// Log holds the global logger used by golifx, can be set via SetLogger() in
	// the golifx package
	Log Logger
)

type Protocol

type Protocol interface {
	SubscriptionTarget

	// GetLocations returns a slice of all locations known to the protocol, or
	// ErrNotFound if no locations are currently known.
	GetLocations() (locations []Location, err error)
	// GetLocation looks up a location by its `id`
	GetLocation(id string) (Location, error)
	// GetGroups returns a slice of all groups known to the protocol, or
	// ErrNotFound if no locations are currently known.
	GetGroups() (locations []Group, err error)
	// GetGroup looks up a group by its `id`
	GetGroup(id string) (Group, error)
	// GetDevices returns a slice of all devices known to the protocol, or
	// ErrNotFound if no devices are currently known.
	GetDevices() (devices []Device, err error)
	// GetDevice looks up a device by its `id`
	GetDevice(id uint64) (Device, error)
	// Discover initiates device discovery, this may be a noop in some future
	// protocol versions.  This is called immediately when the client connects
	// to the protocol
	Discover() error
	// SetTimeout attaches the client timeout to the protocol
	SetTimeout(timeout *time.Duration)
	// SetRetryInterval attaches the client retry interval to the protocol
	SetRetryInterval(retryInterval *time.Duration)
	// Close closes the protocol driver, no further communication with the
	// protocol is possible
	Close() error

	// SetPower sets the power state globally, on all devices
	SetPower(state bool) error
	// SetPowerDuration sets the power state globally, on all lights, over the
	// specified duration
	SetPowerDuration(state bool, duration time.Duration) error
	// SetColor changes the color globally, on all lights, over the specified
	// duration
	SetColor(color Color, duration time.Duration) error
}

Protocol defines the interface between the Client and a protocol implementation

type StubLogger

type StubLogger struct{}

StubLogger satisfies the Logger interface, and simply does nothing with received messages

func (*StubLogger) Debugf

func (l *StubLogger) Debugf(format string, args ...interface{})

Debugf handles debug level messages

func (*StubLogger) Errorf

func (l *StubLogger) Errorf(format string, args ...interface{})

Errorf handles error level messages

func (*StubLogger) Fatalf

func (l *StubLogger) Fatalf(format string, args ...interface{})

Fatalf handles fatal level messages, exits the application

func (*StubLogger) Infof

func (l *StubLogger) Infof(format string, args ...interface{})

Infof handles info level messages

func (*StubLogger) Panicf

func (l *StubLogger) Panicf(format string, args ...interface{})

Panicf handles debug level messages, and panics the application

func (*StubLogger) Warnf

func (l *StubLogger) Warnf(format string, args ...interface{})

Warnf handles warn level messages

type Subscription

type Subscription struct {
	sync.Mutex
}

Subscription exposes an event channel for consumers, and attaches to a SubscriptionTarget, that will feed it with events

func (*Subscription) Close

func (s *Subscription) Close() error

Close cleans up resources and notifies the provider that the subscription should no longer be used. It is important to close subscriptions when you are done with them to avoid blocking operations.

func (*Subscription) Events

func (s *Subscription) Events() <-chan interface{}

Events returns a chan reader for reading events published to this subscription

type SubscriptionProvider

type SubscriptionProvider struct {
	sync.RWMutex
}

SubscriptionProvider provides an embedable subscription factory

func (*SubscriptionProvider) Close

func (s *SubscriptionProvider) Close() (err error)

Close all subscriptions

func (*SubscriptionProvider) Notify

func (s *SubscriptionProvider) Notify(event interface{})

Notify sends the provided event to all subscribers

func (*SubscriptionProvider) Subscribe

func (s *SubscriptionProvider) Subscribe() *Subscription

Subscribe returns a new Subscription for this provider

type SubscriptionTarget

type SubscriptionTarget interface {
	Subscribe() *Subscription
	Notify(event interface{})
}

SubscriptionTarget generally embeds a SubscriptionProvider

golifx's People

Contributors

brutella avatar pdf avatar pmaene avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

golifx's Issues

Memory Leak

There appears to be a pretty big memory leak when using this library in the hklifxd HomeKit bridge. After spending some time looking into this, it appears that golifx is the culprit. I'm not a go developer so it's a bit out of my league to investigate the leak.

null pointer error when sending a command to a light

I am getting the following error when I send a command to the light.

`./lifx light color -H 65535 -S 0 -B 65535 -K 5500 -l Testing
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x177e60]

goroutine 129 [running]:
panic(0x21b640, 0x10626008)
/usr/local/go/src/runtime/panic.go:500 +0x33c
github.com/pdf/golifx/protocol/v2/device.(*Device).Send.func1(0x106f0a40, 0x106f7580, 0x1071c000, 0x106, 0x106ec5a0)
/Users/Pancho/workspace/src/github.com/pdf/golifx/protocol/v2/device/device.go:644 +0x180
created by github.com/pdf/golifx/protocol/v2/device.(*Device).Send
/Users/Pancho/workspace/src/github.com/pdf/golifx/protocol/v2/device/device.go:670 +0x240`

I have compiled the utility for arm and running the command in a raspberry pi.
This happens sometimes and sometimes it doesn't and also happens in the Go code I am developing, but seems to randomly be doing it.

Let me know if you need more information. By the way, the command seems to be reaching the light because it is turning on or off or sending the color right. but I get this panic that prevents my Go code to keep running.

Powerless and wireless light switches

Am planning to replace all wall light switches with radio based ones.

Do you know any ?

This is so that the lights can be controlled by and app as well as a wall light switch.

It can also be powered because it's a renovation so I could just power the radio light switch via the 230 volts running behind each switch

importing golifx/common errors

upon using hklifx, $ get go errors due to import of golifx/common --> can be found here

Error:

# hklifx
./hklifxd.go:14: inconsistent definition for type common.SubscriptionTarget during import
    interface { CloseSubscription(*common.Subscription) error; NewSubscription() (*common.Subscription, error) } (in "github.com/pdf/golifx")
    interface { CloseSubscription(*<T>) error; NewSubscription() (*common.Subscription, error) } (in "github.com/pdf/golifx/common")

I have also filed an issue to note this on hklifx: brutella/hklifx#15

Import of github.com/prometheus/log is deprecated and breaks dep in projects that already use logrus

https://github.com/prometheus/log is deprecated and currently redirects to https://github.com/prometheus-junkyard/log but this package is imported by subscription_provider.go:

https://github.com/pdf/golifx/blob/master/common/subscription_provider.go#L6

Because of an issue with logrus - sirupsen/logrus#451 - this causes dependency problems with projects that already import logrus. Due to the deprecation of prometheus/log it looks like that project won't fix it. It looks like github.com/prometheus/common/log is the correct import now.

Refresh groups/locations with discovery

We currently don't correctly track group/location changes for known devices after initial discovery. Need to determine the optimal method of handling this.

closed channel

panic: close of closed channel

goroutine 424 [running]:
github.com/pdf/golifx/protocol/v2/device.(*Device).delSeq(0xc420164120, 0xc42016e008)
	/Users/adam/Scripts/go/src/github.com/pdf/golifx/protocol/v2/device/device.go:827 +0xa2
github.com/pdf/golifx/protocol/v2/device.(*Device).handler.func1(0xc420164120, 0xc4202b8020, 0xc420145000, 0x8)
	/Users/adam/Scripts/go/src/github.com/pdf/golifx/protocol/v2/device/device.go:787 +0x1f0
created by github.com/pdf/golifx/protocol/v2/device.(*Device).handler
	/Users/adam/Scripts/go/src/github.com/pdf/golifx/protocol/v2/device/device.go:768 +0x368
exit status 2

also:

panic: send on closed channel

goroutine 453 [running]:
github.com/pdf/golifx/protocol/v2/device.(*Device).handler.func1(0xc420114120, 0xc420241100, 0xc4202822e0, 0x8)
	/Users/adam/Scripts/go/src/github.com/pdf/golifx/protocol/v2/device/device.go:776 +0x166
created by github.com/pdf/golifx/protocol/v2/device.(*Device).handler
	/Users/adam/Scripts/go/src/github.com/pdf/golifx/protocol/v2/device/device.go:768 +0x368
exit status 2

planning on looking at these myself, creating an issue to remember.

reproduce code is very simple:

func main() {
	client, err := golifx.NewClient(&protocol.V2{Reliable: true, Port: 56700})
	if err != nil {
		panic(err)
	}

	logger := log.New()
	logger.Level = log.InfoLevel
	golifx.SetLogger(logger)
	client.SetDiscoveryInterval(time.Second * 10)

	sig := make(chan os.Signal, 1)
	signal.Notify(sig, os.Interrupt, syscall.SIGTERM)
	<-sig
}

Crash on concurrent map access

Occasionally I get the following crash

fatal error: concurrent map read and map write

goroutine 25054 [running]:
runtime.throw(0x616060, 0x21)
    /usr/local/Cellar/go/1.6/libexec/src/runtime/panic.go:530 +0x90 fp=0xc820218be8 sp=0xc820218bd0
runtime.mapaccess2(0x4b1e00, 0xc8201470e0, 0xc820218c67, 0xc82000a2a0, 0x2)
    /usr/local/Cellar/go/1.6/libexec/src/runtime/hashmap.go:343 +0x5a fp=0xc820218c30 sp=0xc820218be8
github.com/pdf/golifx/protocol/v2/device.(*Device).handler(0xc8203b4c40)
    /Users/mah/Source/Code/Go/src/github.com/pdf/golifx/protocol/v2/device/device.go:634 +0x4c1 fp=0xc820218fa8 sp=0xc820218c30
runtime.goexit()
    /usr/local/Cellar/go/1.6/libexec/src/runtime/asm_amd64.s:1998 +0x1 fp=0xc820218fb0 sp=0xc820218fa8
created by github.com/pdf/golifx/protocol/v2/device.New
    /Users/mah/Source/Code/Go/src/github.com/pdf/golifx/protocol/v2/device/device.go:689 +0x165

New infrared commands support

Hi, I saw that there are new bulbs that support infrared light, are you planning to add the infrared support to your library?
The infrared packets are documented in lifx website. I don't have bulbs that support infrared yet but I will have some soon.
I am getting familiar how the library is built and I can try to contribute adding that support, but I don't know how you handle changes in the library and posting those changes.
Something that would also be useful is to get the information of the wifi config, specially the signal strength.
Sorry if this is not the right place to communicate but I couldn't find another way.
Thanks
Francisco

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.