Coder Social home page Coder Social logo

lifxlan's Introduction

lifxlan

lifxlan is a Python 3 module for locally controlling LIFX devices (such as lightbulbs) over a LAN. It implements the LIFX LAN Protocol specification. Supports white, color, multizone (LIFX Z, LIFX Beam), infrared (LIFX+), and chain (LIFX Tile) capabilities. Also supports group-based control of arbitrary sets of lights. Supports Unicode characters in names, groups, and locations.

How to Install

To get the latest stable release:

sudo pip install lifxlan

However, to be guaranteed to get the most recent features and fixes you can install from source with:

sudo python setup.py install

Run

See the examples folder for example scripts that use lifxlan.

To be as generic as possible, the examples use automatic device discovery to find individual bulbs, which causes a short but noticeable delay. To avoid device discovery, you can either instantiate Light objects directly using their MAC address and IP address (which you can learn by running examples/hello_world.py), or you can use the broadcast methods provided in the LifxLAN API. In the examples folder, broadcast_on.py, broadcast_off.py, and broadcast_color.py will allow you to send commands to all lights quickly from the command line without doing device discovery.

Overview

You can do several things with this library:

  • Control LIFX devices using the package's high-level API (see the examples folder and the following API sections).
  • Build your own high-level API on top of the low-level networking messages.
  • Build virtual LIFX devices in software (think adapters for Philips Hue bulbs, etc).

High-Level API:

  • lifxlan.py - Provides the LifxLAN API, and low-level API for sending broadcast LIFX packets to the LAN.
  • device.py - Provides the Device API, and low-level API for sending unicast LIFX packets to a Device.
  • light.py - Provides the Light API. Subclass of Device.
  • multizonelight.py - Provides the MultiZoneLight API. Subclass of Light.
  • tilechain.py - Provides the TileChain API. Subclass of Light.
  • group.py - Provides the Group API. Allows you to perform synchronized actions on groups of devices.
LifxLAN API

You can create a LifxLAN object to represent the local network:

lan = LifxLAN()
lan = LifxLAN(num_lights)   #this will make discovery go faster if all lights are responsive

LifxLAN objects have the following methods:

# power can be "on"/"off", True/False, 0/1, or 0/65535
# color is a list of HSBK values: [hue (0-65535), saturation (0-65535), brightness (0-65535), Kelvin (2500-9000)]
# duration is the transition time in milliseconds
# rapid is True/False. If True, don't wait for successful confirmation, just send multiple packets and move on
# NOTE: rapid is meant for super-fast light shows with lots of changes. You should't need it for normal use.
# arguments in [square brackets] are optional
# name is the string label for the light, such as "Right Lamp"
# names is a list of name strings, such as ["Left Lamp", "Right Lamp"]
# group is a string label for a group, such as "Living Room"
# location is the string label for a location, such as "My Home"

get_lights()                                                                                 # returns list of Light objects
get_color_lights()                                                                           # returns list of Light objects that support color functionality
get_infrared_lights()                                                                        # returns list of Light objects that support infrared functionality
get_multizone_lights()                                                                       # returns list of MultiZoneLight objects that support multizone functionality
get_tilechain_lights()                                                                       # returns a list of TileChain objects that support chain functionality
get_device_by_name(name)                                                                     # returns a Device object (instantiated as the most specific Device subclass possible, such as MultiZoneLight)
get_devices_by_name(names)                                                                   # returns a Group object
get_devices_by_group(group)                                                                  # returns a Group object
get_devices_by_location(location)                                                            # returns a Group object
set_power_all_lights(power, [duration], [rapid])                                             # set power for all lights on LAN
set_color_all_lights(color, [duration], [rapid])                                             # set color for all lights on LAN
set_waveform_all_lights(is_transient, color, period, cycles, duty_cycle, waveform, [rapid])  # see the Light API for more details
get_power_all_lights()                                                                       # returns dict of Light, power pairs
get_color_all_lights()                                                                       # returns dict of Light, color pairs
Device API

In keeping with the LIFX protocol, all lights are devices, and so support the following methods:

# label is a string, 32 char max
# power can be "on"/"off", True/False, 0/1, or 0/65535
# rapid is True/False. If True, don't wait for successful confirmation, just send multiple packets and move on
# NOTE: rapid is meant for super-fast light shows with lots of changes. You should't need it for normal use.
# arguments in [square brackets] are optional

set_label(label)
set_power(power, [rapid])
get_mac_addr()
get_ip_addr()
get_service()                       # returns int, 1 = UDP
get_port()
get_label()
get_power()                         # returns 0 for off, 65535 for on
get_host_firmware_tuple()           # returns (build_timestamp (in nanoseconds), version)
get_host_firmware_build_timestamp()
get_host_firmware_version()
get_wifi_info_tuple()               # returns (wifi_signal_mw, wifi_tx_bytes, wifi_rx_bytes)
get_wifi_signal_mw()
get_wifi_tx_bytes()
get_wifi_rx_bytes()
get_wifi_firmware_tuple()           # returns (build_timestamp (in nanoseconds), version)
get_wifi_firmware_build_timestamp()
get_wifi_firmware_version()
get_version_tuple()                 # returns (vendor, product, version)
get_location()                      # Returns location id (bytearray length 16)
get_location_tuple()                # Returns a tuple of location(bytearray lenght 16), location_label(string), and location_updated_at(unsigned 64 bit epoch timestamp)
get_location_label()                # Returns location_label string
get_location_updated_at             # Returns location_updated_at unsigned 64 bit int -> epoch timestamp
get_group()                         # Returns group id (bytearray length 16)
get_group_tuple()                   # Returns a tuple of group(bytearray lenght 16), group_label(string), and group_updated_at(unsigned 64 bit epoch timestamp)
get_group_label()                   # Returns group_label(string)
get_group_updated_at                # Returns group_updated_at unsigned 64 bit int -> epoch timestamp
get_vendor()
get_product()
get_version()
get_info_tuple()                    # returns (time (current timestamp in ns), uptime (in ns), downtime (in ns, +/- 5 seconds))
get_time()
get_uptime()
get_downtime()
is_light()                          # returns True if device is some kind of light product
supports_color()                    # returns True if product features include color
supports_temperature()              # returns True if product features include white color temperature
supports_multizone()                # returns True if product features include multizone functionality
supports_infrared()                 # returns True if product features include infrared functionality
Light API

You can get Light objects automatically though LAN-based discovery (takes a few seconds), or by creating Light objects using a known MAC address and IP address:

lights = lan.get_lights()                              # Option 1: Discovery
light = Light("12:34:56:78:9a:bc", "192.168.1.42")     # Option 2: Direct

The Light API provides everything in the Device API, as well as:

# arguments in [square brackets] are optional
# power can be "on"/"off", True/False, 0/1, or 0/65535
# color is a HSBK list of values: [hue (0-65535), saturation (0-65535), brightness (0-65535), Kelvin (2500-9000)]
# duration is the transition time in milliseconds
# rapid is True/False. If True, don't wait for successful confirmation, just send multiple packets and move on
# is_transient is 1/0. If 1, return to the original color after the specified number of cycles. If 0, set light to specified color
# period is the length of one cycle in milliseconds
# cycles is the number of times to repeat the waveform
# duty_cycle is an integer between -32768 and 32767. Its effect is most obvious with the Pulse waveform
#     set duty_cycle to 0 to spend an equal amount of time on the original color and the new color
#     set duty_cycle to positive to spend more time on the original color
#     set duty_cycle to negative to spend more time on the new color
# waveform can be 0 = Saw, 1 = Sine, 2 = HalfSine, 3 = Triangle, 4 = Pulse (strobe)
# infrared_brightness (0-65535) - is the maximum infrared brightness when the lamp automatically turns on infrared (0 = off)

# NOTE: rapid is meant for super-fast light shows with lots of changes. You should't need it for normal use.

set_power(power, [duration], [rapid])
set_color(color, [duration], [rapid])
set_waveform(is_transient, color, period, cycles, duty_cycle, waveform)
get_power()                                                                 # returns 0 or 65535
get_color()                                                                 # returns color (HSBK list)
get_infrared()                                                              # returns infrared brightness (0 to 65535), or None if infrared is not supported
set_infrared(infrared_brightness)

The Light API also provides macros for basic colors, like RED, BLUE, GREEN, etc. Setting colors is as easy as mybulb.set_color(BLUE). See light.py for complete list of color macros.

Finally, you can set parts of the color individually using the following four methods. However, the bulbs must receive all four values in each SetColor message. That means that using one of the following methods is always slower than using set_color(color) above because it will have to call get_color() first to get the other three values.

set_hue(hue, [duration], [rapid])                  # hue in range [0-65535]
set_brightness(brightness, [duration], [rapid])    # brightness in range [0-65535]
set_saturation(saturation, [duration], [rapid])    # saturation in range [0-65535]
set_colortemp(kelvin, [duration], [rapid])         # kelvin in range [2500-9000]
MultiZoneLight API

Lights with MultiZone capability, such as the LIFX Z, have all the same methods as the Light API, and also add the following:

# start and end refer to zone indices (inclusive).
# duration is the transition time in milliseconds
# rapid is True/False. True means there is no guarantee that the bulb will receive your message.
# apply is 1/0. If 0, queue up the change until a packet with apply=1 comes by, then apply all queued changes.
# effect_type is 0 for None, 1 for Move

get_color_zones([start], [end])                                    # returns a list of [H,S,V,K] colors, one for each zone. Length of the list is the number of zones.
set_zone_color(start, end, color, [duration], [rapid], [apply])    # indices are inclusive and zero-indexed
set_zone_colors(colors, [duration], [rapid])                       # colors is a list of [H,S,V,K] colors, which will get applied to the zones in order. This makes it possible to restore the original colors easily after a display.
get_multizone_effect()                                             # returns current firmware effect status
set_multizone_effect([effect_type], [speed], [duration], [instanceid], [parameters], [rapid]) # starts the firmware effect sequence

The LIFX Z can be instantiated as either a Light or MultiZoneLight object, but to use the MultiZone API you'll need to instantiate it as a MultiZoneLight. Just like with more generic Light objects, you can instantiate a MultiZoneLight directly with light = MultiZoneLight("12:34:56:78:9a:bc", "192.168.1.23"). You can also get a list of all MultiZone lights using lights = lan.get_multizone_lights(), where lan is a LifxLAN object.

TileChain API

TileChain lights, such as the LIFX Tile, have all the same methods as the Light API, and also add the following:

# refresh_cache is a binary value. If True, send the query directly to the light to get the answer, and update the locally stored information (slower). If False, return the locally stored answer from a previous query (faster). Should almost always be False, unless the configuration of the Tiles is expected to change while the program is running (an unusual concern for most programs).
# tile_count is the number of tiles on which to replicate the command, starting from the start_index tile.
# x, y, and width will probably not be used. They allow the user to specify a rectangle of LEDs on the specified tile. The (x, y) coordinate gives the starting LED, and the width gives the width of the desired rectangle. The default values of these ((0, 0) and 8) specify the whole tile, and will probably not need to be changed.
# colors is a list of 64 HSVK tuples.
# tilechain_colors is a list of tile_count x 64 HSVK tuples, used for getting and setting the entire TileChain's colors at once.
# hsvk_matrix is a 2D list of HSVK color tuples with canvas_dimensions rows and cols. The canvas_dimensions will depend on the configuration of the tiles. The canvas can be thought of as the rectangular bounding box around the entire TileChain, where each pixel is an LED.
# effect_type is 0 for None, 2 for Morph and 3 for Flame. 1 is Reserved.

get_tile_info([refresh_cache])                                  # returns a list of Tile objects
get_tile_count([refresh_cache])                                 # returns the number of Tiles in the TileChain light
get_tile_colors(start_index, [tile_count], [x], [y], [width])   # returns colors for the specified tile(s).
set_tile_colors(start_index, colors, [duration], [tile_count], [x], [y], [width], [rapid]) # sets the colors on the specified tile(s). For tile_count > 1, the colors will be duplicated.
get_tilechain_colors()                                          # returns tilechain_colors
set_tilechain_colors(tilechain_colors, [duration], [rapid])     # sets all the colors on the whole TileChain
project_matrix(hsvk_matrix, [duration], [rapid])                # projects the given matrix of colors onto the TileChain.
get_canvas_dimensions([refresh_cache])                          # returns (x, y), representing the rows and columns of the bounding box around the entire TileChain, where each element is an individual LED position.
get_tile_effect()                                               # returns current firmware effect status
set_tile_effect([effect_type], [speed], [duration], [palette], [instanceid], [parameters], [rapid]) # starts the firmware effect sequence

Here are some other available methods that you are much less likely to use:

# x and y below are in units of tile length, not LED rows/cols. In other words, the x and y below are not the same as the x and y above.

recenter_coordinates()                  # This will permanently shift all the tile coordinates so that they are centered around a tile at (0, 0). Sometimes the app will result in a particular axis being off (e.g., the origin tile is (0, -0.5), so all the tile coordinates are shifted by -0.5). This method isn't necessary for any functionality, but makes the tile_info a little more human-readable.
set_tile_coordinates(tile_index, x, y)  # Permanently sets the specified tile's coordinates to x and y (in tile length units) relative to the central tile. Tile coordinates are generally set through the LIFX app, and it is unlikely you will need to ever use this method unless you're doing something pretty weird. (If so, drop me a line!)
get_tile_map([refresh_cache])           # Returns a 2D list with canvas_dimensions rows and cols where each element contains either a (tile_index, color_index) tuple or 0. This maps a pixel on the canvas to the tile number and LED number on that tile that the pixel corresponds to, or 0 if there is no tile in that location.

A LIFX Tile light can be instantiated as either a Light or TileChain object, but to use the TileChain API you'll need to instantiate it as a TileChain. Just like with more generic Light objects, you can instantiate a TileChain directly with light = TileChain("12:34:56:78:9a:bc", "192.168.1.23"). You can also get a list of all tilechain lights using lights = lan.get_tilechain_lights(), where lan is a LifxLAN object.

Group API

A Group is a collection of devices. Under the covers, a Group is just a list of device objects (like Devices, Lights, MultiZoneLights) and a set of functions that send multi-threaded commands to the applicable devices in the group. The multi-threading allows changes to be made more or less simultaneously. At the very least, it is certainly faster than if you looped through each individual light one at a time. You can get a Group by group, location, or device names via the LifxLAN API. However, you can also instantiate a Group with any arbitrary list of device objects. Here are some ways to create groups:

# The following methods use discovery
lan = LifxLAN()
g = lan.get_devices_by_name(["Left Lamp", "Right Lamp"])
g = lan.get_devices_by_group("Living Room")
g = lan.get_devices_by_location("My Home")

# This method is fastest
right = Light("12:34:56:78:9a:bc", "192.168.0.2")
left = Light("cb:a9:87:65:43:21", "192.168.0.3")
g = Group([right, left])

Almost all of the Group API methods are commands. Commands will only be sent to the devices in the group that support that capability. If you want to get state information from the devices, you will need to access the list of devices and call their get methods directly.

# device_object is a Device or any of its subclasses like Light and MultiZoneLight.
# device_name is a string name of a device, like "Right Lamp"
# power can be "on"/"off", True/False, 0/1, or 0/65535
# color is a HSBK list of values: [hue (0-65535), saturation (0-65535), brightness (0-65535), Kelvin (2500-9000)]
# infrared_brightness is an integer between 0 and 65535.
# duration is the transition time in milliseconds
# rapid is True/False. If True, don't wait for successful confirmation, just send multiple packets and move on
# start and end refer to zone indices (inclusive).
# apply is 1/0. If 0, queue up the change until a packet with apply=1 comes by, then apply all queued changes.

add_device(device_object)
remove_device(device_object)
remove_device_by_name(device_name)
get_device_list()
set_power(power, [duration], [rapid])
set_color(color, [duration], [rapid])
set_hue(hue, [duration], [rapid])
set_brightness(brightness, [duration], [rapid])
set_saturation(saturation, [duration], [rapid])
set_colortemp(kelvin, [duration], [rapid])
set_infrared(infrared_brightness)
set_zone_color(start, end, color, [duration], [rapid], [apply])
set_zone_colors(colors, [duration], [rapid])

LIFX LAN Protocol Implementation:

The LIFX LAN protocol specification is officially documented here. In lifxlan, you can see the underlying stream of packets being sent and received at any time by initializing the LifxLAN object with the verbose flag set: lifx = LifxLAN(verbose = True). (See examples/verbose_lan.py.) You can also set the verbose flag if creating a Light or MultiZoneLight object directly.

The files that deal with LIFX packet construction and representation are:

  • message.py - Defines the message fields and the basic packet structure.
  • msgtypes.py - Provides subclasses for each LIFX message type, along with their payload constructors.
  • unpack.py - Creates a LIFX message object from a string of binary data (crucial for receiving messages).

Happy hacking!

lifxlan's People

Contributors

autolog avatar cabalist avatar cbeck527 avatar davidcfk avatar dismounted avatar einstein42 avatar exking avatar flyingdiver avatar grunskis avatar joakimlindbom avatar kylegoetz avatar mclarkk avatar mrstegeman avatar nickharmer avatar noblebrutus avatar ps-jay avatar psubrownie avatar samclane avatar sparrowjack63 avatar steveatinfincia avatar tacaswell avatar wbelt 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  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  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  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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

lifxlan's Issues

Linux lifxlan.device.WorkflowException

Hello

I tried to use your project.

i have error when i try to run in python2

i lauch braodcast.py is ok
python2 broadcast_on.py
or off, color work wel (turn off or on or color my lifx bulb)

after i try an exemple one bulb parameter:

python2 blink.py 1

Discovering lights...
Selected None
Traceback (most recent call last):
File "blink.py", line 72, in
main()
File "blink.py", line 29, in main
original_color = bulb.get_color()
File "/usr/local/lib/python2.7/dist-packages/lifxlan-0.2.3-py2.7.egg/lifxlan/light.py", line 71, in get_color
response = self.req_with_resp(LightGet, LightState)
File "/usr/local/lib/python2.7/dist-packages/lifxlan-0.2.3-py2.7.egg/lifxlan/device.py", line 349, in req_with_resp
response = unpack_lifx_message(data)
File "/usr/local/lib/python2.7/dist-packages/lifxlan-0.2.3-py2.7.egg/lifxlan/unpack.py", line 156, in unpack_lifx_message
message = LightState(target_addr, source_id, seq_num, payload, ack_requested, response_requested)
File "/usr/local/lib/python2.7/dist-packages/lifxlan-0.2.3-py2.7.egg/lifxlan/msgtypes.py", line 306, in init
super(LightState, self).init(MSG_IDS[LightState], target_addr, source_id, seq_num, ack_requested, response_requested)
File "/usr/local/lib/python2.7/dist-packages/lifxlan-0.2.3-py2.7.egg/lifxlan/message.py", line 43, in init
self.packed_message = self.generate_packed_message()
File "/usr/local/lib/python2.7/dist-packages/lifxlan-0.2.3-py2.7.egg/lifxlan/message.py", line 46, in generate_packed_message
self.payload = self.get_payload()
File "/usr/local/lib/python2.7/dist-packages/lifxlan-0.2.3-py2.7.egg/lifxlan/msgtypes.py", line 317, in get_payload
label = b''.join(little_endian(bitstring.pack('8', c)) for c in self.label)
File "/usr/local/lib/python2.7/dist-packages/lifxlan-0.2.3-py2.7.egg/lifxlan/msgtypes.py", line 317, in
label = b''.join(little_endian(bitstring.pack('8', c)) for c in self.label)
File "/usr/local/lib/python2.7/dist-packages/bitstring.py", line 4224, in pack
s._append(BitStream._init_with_token(name, length, value))
File "/usr/local/lib/python2.7/dist-packages/bitstring.py", line 1224, in _init_with_token
b = cls(**{name: int(value), 'length': token_length})
ValueError: invalid literal for int() with base 10: '['

I try with 2 bulb parameter:

python2 blink.py 2
Discovering lights...
Traceback (most recent call last):
File "blink.py", line 72, in
main()
File "blink.py", line 23, in main
devices = lifx.get_lights()
File "/usr/local/lib/python2.7/dist-packages/lifxlan-0.2.3-py2.7.egg/lifxlan/lifxlan.py", line 49, in get_lights
responses = self.broadcast_with_resp(GetService, StateService)
File "/usr/local/lib/python2.7/dist-packages/lifxlan-0.2.3-py2.7.egg/lifxlan/lifxlan.py", line 203, in broadcast_with_resp
raise WorkflowException('Did not receive {} in response to {}'.format(str(response_type), str(msg_type)))
lifxlan.device.WorkflowException: Did not receive <class 'lifxlan.msgtypes.StateService'> in response to <class 'lifxlan.msgtypes.GetService'>

You talk about route ...

My route table under linux is:
Destination Gateway Genmask Indic Metric Ref Use Iface
default 192.168.1.5 0.0.0.0 UG 202 0 0 eth0
192.168.1.0 * 255.255.255.0 U 202 0 0 eth0

my router is 192.168.1.5

Thanks for your help in advance

String of label includes hex zeros

If you print the label the hex zeros don't show up, but if including the label in, say, a dictionary, it displays as:

{u'name': u'Lotus Lamp\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'}

I replaced \x00 with the empty string and it was fine.

no route to bulbs on Windows

I tried installing lifxlan on a different Win7 (64bit AMD chipset) machine. Running the broadcast scripts return immediately with no output and hello_world.py returns a run-time error:

c:\Python27\lifxlan-master\lifxlan-master\examples>python broadcast_off.py

c:\Python27\lifxlan-master\lifxlan-master\examples>python broadcast_off.py

c:\Python27\lifxlan-master\lifxlan-master\examples>python broadcast_on.py

c:\Python27\lifxlan-master\lifxlan-master\examples>python hello_world.py 2
Discovering lights...
Traceback (most recent call last):
  File "hello_world.py", line 27, in <module>
    main()
  File "hello_world.py", line 21, in main
    devices = lifx.get_lights()
  File "C:\Python27\lib\site-packages\lifxlan-0.2.3-py2.7.egg\lifxlan\lifxlan.py", line 49, in get_lights
    responses = self.broadcast_with_resp(GetService, StateService)
  File "C:\Python27\lib\site-packages\lifxlan-0.2.3-py2.7.egg\lifxlan\lifxlan.py", line 203, in broadcast_with_resp
    raise WorkflowException("Did not receive {} in response to {}".format(str(response_type), str(msg_type)))
lifxlan.device.WorkflowException: Did not receive <class 'lifxlan.msgtypes.StateService'> in response to <class 'lifxlan.msgtypes.GetService'>

Example of virtual bulb

Would it be possible for you to provide an example or more hints for how to build a virtual bulb that the LIFX Android app would discover?

STDOUT WorkflowException issue

Ok... stay with me here. I have a framework that interacts with different 'node servers' via STDIN and STDOUT and some other stuff.

I'm using your LIFXLAN module inside one of these node servers and it queries the light every 30 seconds. It creates a new class for each light and runs as a daemon.

My specific issue:

lifx = lifxlan.LifxLAN(None)
devices = lifx.get_lights()
for d in devices:
    while True:
        try:
            power = True if d.get_power() == 65535 else False
        except (lifxlan.device.WorkflowException):
            print 'The device is offline'
        time.sleep(30)

When I try to catch lifxlan.device.WorkflowException, no matter what I do it ALWAYS outputs to STDOUT. This actually breaks my framework. Instead of actually catching the exception, I get this to STDOUT:

WorkflowException: Did not receive <class 'lifxlan.msgtypes.LightStatePower'> in response to <class 'lifxlan.msgtypes.LightGetPower'>

My question is how the heck can I catch your custom exception from device.py without outputting to STDOUT? I'm sure it's my ignorance, hope this is an easy question and I'm just missing something.

Fix MultiZone examples

The multizone examples need MultiZone discovery instead of relying on the light name containing the string "strips," and the multizone_chase.py lightshow logic should be changed so glitches are less obvious (i.e. to stop the whole thing from occasionally flashing red when certain packets get dropped).

Error discovering bulbs

Although I can get hello_world.py to work on my Mac (Python 2.7 OS X 10.10.3), I can't get it to work on Windows . I am using Python 2.7 on win7-64bit Lenovo Thinkpad machine. I've added 3 seconds to DEFAULT_TIMEOUT but still get an error when attempting to discovering bulbs. Any ideas?

C:\Python27\lifxlan-master\lifxlan-master\examples>python hello_world.py 3
Discovering lights...
Traceback (most recent call last):
  File "hello_world.py", line 27, in <module>
    main()
  File "hello_world.py", line 21, in main
    devices = lifx.get_lights()
  File "c:\python27\lib\site-packages\lifxlan\lifxlan.py", line 49, in get_lights
    responses = self.broadcast_with_resp(GetService, StateService)
  File "c:\python27\lib\site-packages\lifxlan\lifxlan.py", line 203, in broadcast_with_resp
    raise WorkflowException("Did not receive {} in response to {}".format(str(response_type), str(msg_type)))
lifxlan.device.WorkflowException: Did not receive <class 'lifxlan.msgtypes.StateService'> in response to <class 'lifxlan.msgtypes.GetService'>

C:\Python27\lifxlan-master\lifxlan-master\examples>

Discovery fails behind VPN network lock

First off thanks for generously sharing your work with us. Second, this is not really a bug report but rather an attempt to get two things to work together properly.

I just lately installed this plugin https://github.com/sanghviharshit/script.kodi.lifx.ambilight for my Kodi installation on Ubuntu. It uses lifxlan to communicate with the LIFX bulbs. Now I do have a VPN client running on this server that uses IP-table rules to offer a feature called network lock (Leak protection). Unfortunately as soon as I activate the network lock feature the bulb discovery by lifxlan fails. My technical background is not good enough to understand why this is happening. I was told something about DNS and hostnames being probably responsible for the issue but have no idea what to do with that information tbh.

If you have any idea what causes this behavior and even better a possible hint for a solution I'd be very grateful.

Intermittent problem with all zero color zones (possibly a bulb issue)

Sometimes get_color_zones() returns zones colors where random segments of zones are all zeros. Today I ran into this a lot while testing multizone_chase.py and trying to save the original colors and restore them at the end. It's weird, because it doesn't seem to be from out-of-order packets or anything, and it's not consistent. I'm wondering if the light strips themselves are having intermittent problems.

Error printing labels

I have three bulbs but I know that they all have labels. I also know that they are running firmware 2.0

In verbose_lan.py. When I run it on my Mac using (python verbose_lan.py) , I get several pages of verbose message dumps and then finally:

Found Bulbs:
Traceback (most recent call last):
  File "verbose_lan.py", line 29, in <module>
    main()
  File "verbose_lan.py", line 26, in main
    print("  " + label)
TypeError: cannot concatenate 'str' and 'NoneType' objects

I tried changing that line to:

print(" %s " % label) 

instead but that ended up giving me:

Found Bulbs:
 None 
 None 
 None 

I also tried hello_world.py and also ran into trouble printing the bulbs:

$ python hello_world.py 3
Discovering lights...

Found 3 light(s):

Did not receive <class 'lifxlan.msgtypes.LightStatePower'> in response to <class 'lifxlan.msgtypes.LightGetPower'>
Traceback (most recent call last):
  File "hello_world.py", line 26, in <module>
    main()
  File "hello_world.py", line 23, in main
    print(d)
  File "/Library/Python/2.7/site-packages/lifxlan/light.py", line 88, in __str__
    s = self.device_characteristics_str(indent)
  File "/Library/Python/2.7/site-packages/lifxlan/device.py", line 253, in device_characteristics_str
    s += indent + "Power: {}\n".format(STR_MAP[self.power_level])
KeyError: None

device.supports_multizone() or get_multizone_lights()

lifx: File "/home/pi/dev/lifx-nodeserver/lifxlan/device.py", line 338, in supports_multizone
lifx: return self.product_features['multizone']
lifx: TypeError: 'NoneType' object has no attribute 'getitem'

I have 1 group and 2 lights. Tried just get_lights() and get_multizone_lights(). Both fail same error, same line.

Issue with python3 pip

Hi, I wanted to notice you that the installation of lifxlan on python3 with pip seems to be broken because it collects the data, executes the setup, but then every file of the library is empty and the instatiation of the class responds with:

'lifxlan has no attribute LifxLAN'

The Python2/pip2 version works correctly

Simultaneous color change by group?

Apologies if this is the wrong forum in which to ask -- but I was wondering if anyone has figured out a way using this library to make synchronized changes across groups of lights?

In the cloud API you can send a command to a group and the change appears to be synchronized (e.g. all lights change at once).

With this library, I'm using asynchronous python to send a command to multiple bulbs in the same room at once, but you can easily see that the change doesn't take place fully in sync (some bulbs change quicker than others). I assume this is because there's no way in the library to address a "group" instead of a "device".

Has anyone found a clever way using this python library (or something similar on the LAN?) to send a change to multiple devices (e.g. group) and have it take place in sync on the lights?

Thanks!
Matt

Firmware Representation

App displays firmware version as 2.1.

Messages return:

Host Firmware Build Version: 131073
Wifi Firmware Build Version: 131073

That's because 131073 is the integer interpretation of:

0000000000000010 0000000000000001

Or, 2 and 1. Need to change representation to match app.

Bulb not responding when instantiating Light object with MAC and IP addresses

I have 2 LIFX colour 1000 bulbs. Both work with the Android app and both work when using the example scripts under lifxlan/examples (these scripts all use device discovery). This includes the scripts which read the bulb colour and power etc.

However when I instantiate a Light object with a MAC and IP addresses, one bulb responds and one doesn't. I can control both bulbs (set colour, power, etc), but do not receive acknowledgements.

Since the example scripts work, I don't think I have a hardware problem. But I don't understand how my very basic scripts work with one bulb and not the other. Yes I have triple checked the obvious - MAC addr and IP addr.

E.g.
~/lifxlan/examples$ ./get_color_all.py

Discovery will go much faster if you provide the number of lights on your LAN:
python ./get_color_all.py

Discovering lights...
Right (d0:73:d5:14:23:af 192.168.2.30) HSBK: (29814, 0, 65535, 5500)
Left (d0:73:d5:14:1e:ac 192.168.2.29) HSBK: (0, 0, 24575, 3000)

But if I run the following script snippet:
def test():
left = lifxlan.Light("d0:73:d5:14:1e:ac", "192.168.2.29")
print left.get_color()
right = lifxlan.Light("d0:73:d5:14:23:af", "192.168.2.30")
print right.get_color()

I get:
~/lifxlan/examples$ ./test.py
(0, 0, 24575, 3000)
WorkflowException: Did not receive [<class 'lifxlan.msgtypes.LightState'>] from d0:73:d5:14:23:af (Name: None) in response to <class 'lifxlan.msgtypes.LightGet'>
None

Do you have any suggestions?

Thanks.

Multiple Interfaces

On a Raspberry Pi 3 I have an interface that links to the internet (192.168.2.3 eth0) and its own WiFi AP (192.168.42.1 wlan0) for which my lightbulbs (Lifx and TP-Link LB130) connect to. I can do an ARP ping on the 192.168.42.1 wlan0 interface's subnet with scapy in Python and get the MAC Addresses for all of them. However, LifxLAN's device discovery by UDP broadcast doesn't seem to pick up anything.

Generation 3 Lights not discovering and cannot control them

Appears that at the same time as the firmware update to 2.13 on the Generation 3 devices, I can no longer discover them with lifxlan. Gen1 and 2 are still discovered ok.

Trying to toggle the power on a light by creating a bulb using:
bulb = Light(mac,ip)
bulb.set_power("off",duration)

Gives the error:

File "/usr/local/lib/python2.7/dist-packages/lifxlan/light.py", line 40, in get_power
response = self.req_with_resp(LightGetPower, LightStatePower)
File "/usr/local/lib/python2.7/dist-packages/lifxlan/device.py", line 490, in req_with_resp
raise WorkflowException("WorkflowException: Did not receive {} from {} (Name: {}) in response to {}".format(str(response_type), str(self.mac_addr), str(self.label), str(msg_type)))
lifxlan.errors.WorkflowException: WorkflowException: Did not receive [<class 'lifxlan.msgtypes.LightStatePower'>] from d0:73:d5:20:67:a1 (Name: None) in response to <class 'lifxlan.msgtypes.LightGetPower'>

Found Devices: none

I am testing on Windows and not finding devices. verbose_lan shows packets found device none

Set hue/sat/brightness/kelvin individually

This is unfortunately not supported at all by the low-level LAN protocol that the LIFX bulbs use. The bulbs require that all four values be present in a SetColor message. However, I could fake the ability to set hue/saturation/brightness/kelvin by getting the current state of the bulbs first and then supplying the other values based on what the bulb said, but it would add some latency. I'm not sure how much -- I might be able to do it quickly enough. It's worth investigating.

Preset RED not correct

Run ./broadcast_color.py red, and then check with ./get_color_all.py
Lights are set to HSBK: (62978, 65535, 65535, 3500) instead of HSBK: (65535, 65535, 65535, 3500) as specified in the Lights.py.

Edit: Should have also mentioned I installed with pip, and the RED preset is incorrect in that Lights.py.

Skip Discovery using IP

Should be able to get data directly from a specific bulb using either a given IP address or MAC address, no discovery necessary.

ord exception in msgtypes

Hi,
I want to notice that the line 388 in msgtypes.py gets faulty when the light label is not handled correctly.

Sometimes short labels have trailing \x00 in self.label and the 'join' used to generate the label crashes because 'ord(c)' expects a char while \x00 is considered an int

Odd-length string error during discovery

e.g.

Discovering lights...
Traceback (most recent call last):
  File "get_color_all.py", line 27, in <module>
    main()
  File "get_color_all.py", line 24, in main
    print("{} ({}) HSBK: {}".format(d.get_label(), d.mac_addr, d.get_color()))
  File "/Library/Python/2.7/site-packages/lifxlan-0.2.3-py2.7.egg/lifxlan/light.py", line 71, in get_color
    response = self.req_with_resp(LightGet, LightState)
  File "/Library/Python/2.7/site-packages/lifxlan-0.2.3-py2.7.egg/lifxlan/device.py", line 352, in req_with_resp
    response = unpack_lifx_message(data)
  File "/Library/Python/2.7/site-packages/lifxlan-0.2.3-py2.7.egg/lifxlan/unpack.py", line 154, in unpack_lifx_message
    label = binascii.unhexlify("".join(["%x" % (b & 0x000000ff) for b in struct.unpack("b"*32, payload_str[12:44])]))
TypeError: Odd-length string

WorkflowException

Thanks for all your work on this project.

I wrote an interface to allow a home automation system to control the LiFX bulbs with your module. It works great most of the time, however quite often I get WorkflowException: Did not receive [<class 'lifxlan.msgtypes.X'>]

X obviously being the class type.

I create a queue.Queue() for each device to make sure I don't send multiple commands to a device at a time. Also after discovery I create a class for each bulb and set self.device = lifxlan.Light(self.mac, self.ip) That way it has it's own direct connection.

Then every 30 seconds I do three queries to each bulb.

get_color / get_color_zones depending on if its a multizone bulb
get_power()
get_uptime()

About 10% (sometimes less, sometimes up to 50%) of the time, at least one of those fails. I've tried increasing your DEFAULT_TIMEOUT from your device class, I've tried time.sleep between calls, pretty much anything I can think of and no luck. I can't pin down to anything specific. I have a wifi router 5 feet from these bulbs (I've moved it further and closer to see if it made any difference).

It's not a huge deal since I just query it again 30 seconds later, but I was hoping there might be something I'm doing wrong.

Thanks for your time.

Support MultiZone

Is there any chance you could add support for the new LIFX light strip's multi-zone capability?

The protocol documentation is here
The product link is here

Windows 10 - No Devices Found

I downloaded the lifxlan repository and installed it on Win 10. https://github.com/mclarkk/lifxlan

However, when I run the hello_world it says found 0 lights.

When I use the 'sniffer' though it picks up packets from my light?

I can see the light in both the Win10 and Android apps.

Cheers.

Addendum: I also checked the IP addresses and confirmed they are on the same subnet.

Getting status of individual bulb

Hi,

Thanks for writing lifxlan. I am currently playing around with it and trying to understand how to get the status of an individual bulb. Sending on and off works as expected, but it seems like it won't report status back unless I do full broadcast. Am I missing something?

#!/usr/bin/env python
from lifxlan import *
import sys
from time import sleep

def main():
    num_lights = None
    if len(sys.argv) != 3:
        print("\nDiscovery will go much faster if you provide the number of lights on your LAN:")
        print("  python {} <number of lights on LAN>\n".format(sys.argv[0]))
    else:
        ip = sys.argv[1]
        mac_address = sys.argv[2]

    print("IP: " + ip)
    print("MAC: " + mac_address)
    # instantiate LifxLAN client, num_lights may be None (unknown).
    # In fact, you don't need to provide LifxLAN with the number of bulbs at all.
    # lifx = LifxLAN() works just as well. Knowing the number of bulbs in advance
    # simply makes initial bulb discovery faster.
    #    print("Discovering lights...")
    #lifx = LifxLAN(1)

    # get devices
    bulb = Light(mac_address, ip)
    print(bulb.__dict__)
    bulb.set_power("on")
    print(bulb.__dict__)
    bulb.get_power()

multizone - line 43

The Multizone code responds with

for i in range(((upper_8_aligned - lower_8_aligned) / 8) + 1):
TypeError: 'float' object cannot be interpreted as an integer

Needs int division operator so for i in range(((upper_8_aligned - lower_8_aligned) // 8) + 1): works.

Issue when using direct Light object

This used to work so I'm not entirely sure why it stopped. When I create a Light object directly, the light api doesn't get responses. If I discover lights they are Device objects and the light api works no problem. Verbose output shows that device objects have the source_id set where as Light objects set this to 0. Setting the source_id in the Light class fixes this forcing the response back to unicast rather than broadcast.

Can you help me?

I don't know why it doesn't work. (
root@debian:/usr/src/lifxlan/examples# python rainbow_all.py

Discovery will go much faster if you provide the number of lights on your LAN:
python rainbow_all.py

Discovering lights...
Traceback (most recent call last):
File "rainbow_all.py", line 49, in
main()
File "rainbow_all.py", line 20, in main
original_colors = lifx.get_color_all_lights()
File "/usr/local/lib/python2.7/dist-packages/lifxlan/lifxlan.py", line 88, in get_color_all_lights
responses = self.broadcast_with_resp(LightGet, LightState)
File "/usr/local/lib/python2.7/dist-packages/lifxlan/lifxlan.py", line 186, in broadcast_with_resp
response = unpack_lifx_message(data)
File "/usr/local/lib/python2.7/dist-packages/lifxlan/unpack.py", line 153, in unpack_lifx_message
label = "".join([chr(b) for b in struct.unpack("b"*32, payload_str[12:44])])
ValueError: chr() arg not in range(256)

Light object and MultiZone

is it just me, or does MultiZone not work with Light object?

I have a known MAC and IP of my Lighstrip.... so I'm tying to use a Light object to control the zones....

I can control the entire light with the Light object...

Crash since 1.1.2 update - z lights

Hello sorry to say that the Hello world example is showing errors now when listing lights.
Specifically the Z it seems as it was listing the original bulbs but not Z

Traceback (most recent call last): File "c:/Python/lifxlan-master/examples/hello_world.py", line 30, in <module> main() File "c:/Python/lifxlan-master/examples/hello_world.py", line 27, in main print(d) File "C:\Python36\lib\site-packages\lifxlan\light.py", line 178, in __str__ self.refresh() File "C:\Python36\lib\site-packages\lifxlan\device.py", line 93, in refresh self.location = self.get_location() File "C:\Python36\lib\site-packages\lifxlan\device.py", line 138, in get_location response = self.req_with_resp(GetLocation, StateLocation) File "C:\Python36\lib\site-packages\lifxlan\device.py", line 492, in req_with_resp raise WorkflowException("WorkflowException: Did not receive {} from {} (Name: {}) in response to {}".format(str(response_type), str(sel f.mac_addr), str(self.label), str(msg_type))) lifxlan.errors.WorkflowException: WorkflowException: Did not receive [<class 'lifxlan.msgtypes.StateLocation'>] from d0:73:d5:14:f0:a8 (Nam e: None) in response to <class 'lifxlan.msgtypes.GetLocation'>

Get_color_zones needs extra logic to return all requested zones

The get_color_zones function will need to do extra logic and send multiple packets in order to get the state of all the zones on the device/all zones in the requested range instead of just eight from the first StateMultiZone packet that comes back (or perhaps just a single zone from StateZone).

What happens if you make a non-aligned request? That is, what happens if you request zones 2-9? Will color[0], color[1]...color[7] in the response correspond to zone 2, zone 3...zone 9? Or will color[0] be zone 1, color[1] is zone 2, and zone 9 is in some other packet?

UDP_BROADCAST_IP_ADDRS incorrect

get_broadcast_addrs in device.py looks like it assumes the network is a /24 by replacing the last octet with 255 to create broadcast addresses. My network is a /16 and thus recent versions of this code fail to find any of my devices which are on a different segment.

It looks like you're using netifaces to retrieve IP information and it looks like the ni.ifaddresses(iface)[ni.AF_INET][0] contains the broadcast address.

{'peer': u'127.0.0.1', 'netmask': u'255.0.0.0', 'addr': u'127.0.0.1'}
{'broadcast': u'10.10.255.255', 'netmask': u'255.255.0.0', 'addr': u'10.10.3.30'}

Any reason not to use that?

Need more detailed installation instructions

I created a virtualenv, loaded it, then ran "pip install lifxlan." It installed correctly. However, when I try to import lifxlan, I get:

Python 3.4.0 (v3.4.0:04f714765c13, Mar 15 2014, 23:02:41)
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.

import lifxlan
Traceback (most recent call last):
File "", line 1, in
File "/Users/kylegoetz/Documents/Workspaces/lifx/venv/lib/python3.4/site-packages/lifxlan-0.2.3-py3.4.egg/lifxlan/init.py", line 1, in
from .lifxlan import LifxLAN
File "/Users/kylegoetz/Documents/Workspaces/lifx/venv/lib/python3.4/site-packages/lifxlan-0.2.3-py3.4.egg/lifxlan/lifxlan.py", line 5, in
from message import BROADCAST_MAC, BROADCAST_SOURCE_ID
ImportError: No module named 'message'

Cloning the repo and executing hello_world.py sample script I get the same problem:

Traceback (most recent call last):
File "hello_world.py", line 2, in
from lifxlan import *
File "/Users/kylegoetz/Documents/Workspaces/lifx/venv/lib/python3.4/site-packages/lifxlan-0.2.3-py3.4.egg/lifxlan/init.py", line 1, in
from .lifxlan import LifxLAN
File "/Users/kylegoetz/Documents/Workspaces/lifx/venv/lib/python3.4/site-packages/lifxlan-0.2.3-py3.4.egg/lifxlan/lifxlan.py", line 5, in
from message import BROADCAST_MAC, BROADCAST_SOURCE_ID
ImportError: No module named 'message'

Even by doing the python setup.py install the problem persists.

SetWaveform doesn't work like it should (likely a bulb firmware issue)

NOTE: Set Waveform is currently an experimental undocumented feature of the LIFX bulbs.

is_transient=1 is supposed to set the light back to its original color after the waveform according to some pull requests for the LIFX LAN protocol docs: LIFX/lifx-protocol-docs@8a38d3d

Instead, currently is_transient=1 results in bulbs staying on the last color of the waveform instead of original color. At this point I'm 99.9% sure this is a problem with the bulbs, not the code, but I'm submitting it here as an issue to document the behavior and keep an eye on it as LIFX develops their protocol.

GetVersion error [LIFX Z Firmware issue]

Hi,

I'm trying to get the code working for the first time, I've tried both pip versions and a checkout locally today.

Using the following code:

from lifxlan import LifxLAN
from lifxlan import MultiZoneLight
num_lights = 2
print("Starting up lights...")
lifx = LifxLAN(num_lights, False)
multizone_lights = lifx.get_multizone_lights()

I get:

Traceback (most recent call last):
File "party_mode.py", line 9, in
multizone_lights = lifx.get_multizone_lights()
File "/srv/hass/lib/python3.4/site-packages/lifxlan/lifxlan.py", line 74, in get_multizone_lights
if l.supports_multizone():
File "/srv/hass/lib/python3.4/site-packages/lifxlan/device.py", line 369, in supports_multizone
self.product_features = self.get_product_features()
File "/srv/hass/lib/python3.4/site-packages/lifxlan/device.py", line 277, in get_product_features
self.vendor, self.product, self.version = self.get_version_tuple()
File "/srv/hass/lib/python3.4/site-packages/lifxlan/device.py", line 258, in get_version_tuple
response = self.req_with_resp(GetVersion, StateVersion)
File "/srv/hass/lib/python3.4/site-packages/lifxlan/device.py", line 508, in req_with_resp
raise WorkflowException("WorkflowException: Did not receive {} from {} (Name: {}) in response to {}".format(str(response_type), str(self.mac_addr), str(self.label), str(msg_type)))
lifxlan.errors.WorkflowException: WorkflowException: Did not receive [<class 'lifxlan.msgtypes.StateVersion'>] from MYMACADDRESS (Name: None) in response to <class 'lifxlan.msgtypes.GetVersion'>

(MAC Address is masked)

I have two LIFX Z strips on my network, I also tried using MultiZoneLight(...,...) but get the same error.

Any ideas what the problem is?

Cheers
Nathan

Do MultiZone lights need their own send/recv functions?

The protocol says that the response to MultiZone::GetColorZones can be either MultiZone::StateZone OR MultiZone::StateMultiZone. Does that mean that I should potentially expect multiple response packets? If so, MultiZoneLights will need to have their own send/recv functions to deal with the fact that a device might send multiple messages in response, which sounds terrible unless you know beforehand how many to wait for.

https://lan.developer.lifx.com/docs/multizone-light-control

Synchronizing command execution on/off

Like the title states, is it possible to synchronize command execution? I have two lights and using the following code to toggle them on/off they execute in sequence (the first Light() in the list executes faster than the second Light() in the list). Is there a way to let them execute the commands in parallel? I've tried applying the following threading, but alas it didn't work:
https://pymotw.com/2/threading/

My code:

    power = self.lights[0].get_power() 
    for light in self.lights:
       try:
           light.set_power("on" if power == 0 else "off")
       except:
           print(sys.exc_info()[0])  # TODO replace with logging

Unicode Support

Code doesn't currently support Unicode names (e.g., Cyrillic characters).

Unable to run scripts in Python 3 IDLE - Import error

I've been able to launch the modules with no problem when using Python 2 IDLE, but on Python 3 i get an error anytime i try to import from lifxlan. Even the given examples give errors. They work fine with called from terminal though. Forgive me if i'm missing something simple. I ask because i want to use code with Python 3 web framework and want to make sure i wont have issues with Python 3.

Simple Power Toggle

I have very limited coding knowledge and would like to write a script that does a simple power toggle - when lights are on, turn them off and vice versa - so I can control the bulbs from network events. I did investigate all the provided example files and tried to adapt them for my needs with no success. I'm sure this is very simple but still couldn't figure it out. any help is appreciated!

thank you for your help!

Python3 not working (solved)

Hi Meghan,

First thanks for sharing your work. I used it with python 2.7 and it was working great. But now I want python 3.5.

I was trying to use lifxlan with python 3.5 and it was not working. So I made it work whilst keeping python 2 (2.7.12) working too. All the examples that is, there may still be some problems lurking around. The "git diff" file is attached

I any case it seem to indicate some issue with the way you handle the labels as a string and as a bytes string....but I did not investigate too deeply... it may be due to the Python rev.

Next I'll see how to get it to work with asyncio and see if I can get something in the IPv6 domain...since I can ping6 my bulbs.

Cheers,
François

lifxlan-diff.txt

Fails to detect any devices on my network

It was working for about a week, and then everything stopped suddenly. I'm not sure why. I can't detect any lights on my network via lifxlan. The official android app, and windows client can still detect lights. I've tried lifxlan on windows 10 and ubuntu (arm64), neither are working anymore, while both were working a week ago.

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.