Closed avaidyam closed 8 years ago
I saw this on reddit today. Can't wait to work on integrating it with the strip under my kitchen cabinets :)
I just gotta get one first.
Just ordered it! Should be here in a day or two, so I'll let you all know how it goes. For now, I'm sure a scripting bridge from HA would do the trick to the Ruby API.
I've been able to confirm that the API works from the given ruby scripts. Any interest in porting it?
Someone needs the device to properly do something like that.
@maddox I was indicating that I had the device, and was willing to attempt porting it... 😎 Was wondering if anyone would be interested in the port.
@avaidyam AHH. Yes OF COURSE. Who wouldn't be interested in having this heh.
Good luck!
Thanks! Have you ordered yours? It's pretty handy, and for 30$ + 15$ for a 5m light strip, it's an amazing Philips Hue replacement.
I'm just struggling to have banana clips stick properly in the thing.
Milight/Limitless LED also supports LED strips. Already has an HA component as well.
@maddox I've managed to get the ruby script successfully working, so I'll be porting that tonight. @happyleavesaoc I can probably model the component the same way then.
I've ported the ruby scripts as best I could to python. Any issues here?
API_PORT = 5577
class LimitlessLED(Light):
def __init__(self, device_address, reuse_connection=False, max_retries=3):
self.device_address = device_address
self.reuse_connection = reuse_connection
self.max_retries = max_retries
@property
def is_on(self):
return (status.bytes[13] & 0x01) == 1
@property
def turn_on(self):
send_bytes_action(0x71, 0x23, 0x0F, 0xA3)
return True
@property
def turn_off(self):
send_bytes_action(0x71, 0x24 ,0x0F, 0xA4)
return True
def update_color(self, r, g, b):
checksum = (r + g + b + 0x3F) % 0x100
send_bytes_action(0x31, r, g, b, 0xFF, 0, 0x0F, checksum)
def current_color(self):
status.bytes[6, 3]
def reconnect(self):
create_socket()
# Example response:
# [129, 4, 35, 97, 33, 9, 11, 22, 33, 255, 3, 0, 0, 119]
# R G B WW ^--- LSB indicates on/off
def status(self):
socket_action do
send_bytes(0x81, 0x8A, 0x8B, 0x96)
flush_response(14)
def flush_response(self, msg_length):
self.socket.recv(msg_length, socket.MSG_WAITALL)
def send_bytes(self, *b):
self.socket.write(b.pack('c*'))
def send_bytes_action(self, *b):
socket_action { send_bytes(self, *b) }
def create_socket(self):
if not ((self.socket is None) or (self.socket.closed)):
self.socket.close()
self.socket = socket()
self.socket.connect(self.device_address, API_PORT)
def socket_action(self):
tries = 0
try:
if not ((self.socket is None) or (self.socket.closed)):
create_socket()
yield
catch Errno::EPIPE, IOError => e:
tries += 1
if tries <= self.max_retries
reconnect()
retry
else
throw e
finally:
if not (self.socket.closed or self.reuse_connection)
self.socket.close()
def discover_devices(expected_devices=1, timeout=5):
send_socket = socket(AF_INET, SOCK_DGRAM)
send_socket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
send_socket.setsockopt(SOL_SOCKET, SO_BROADCAST, 1)
send_socket.sendto('HF-A11ASSISTHREAD', ('<broadcast>', 48899))
discovered_devices = []
try:
Timeout::timeout(timeout) do:
while true:
data = send_socket.recv(1024)
discovered_devices.push(data.split(','))
if discovered_devices.count >= expected_devices
throw Timeout::Error
except Timeout::Error:
# expected
finally:
send_socket.close()
return discovered_devices
@tomduijf Do you think you could proofread the Python translation here?
Looks ok as a stand-alone module. Don't have such a device, so cant comment on it workings.
For HASS integration it'll probably need some more work. So this works with some kind of broadcast discovery? Perhaps it's already natively discovered by netdisco.
I think i'll order me one of these ledenet devices, looks fun :)
The wifi module the LEDENET devices use (documentation) supports discovery via UDP broadcast. It doesn't seem like a standard discovery protocol.
@avaidyam -- maybe good to link to my repo in this code so there's a sensible place to report issues?
@sidoh Hey! Of course, that's a great idea, will do that. Could you help out with the Python translation into a single component?
@tomduijf Like @sidoh said, it's a non-standard UDP broadcast discovery, so unless we explicitly bake that support in, it doesn't seem likely.
My Python's not strong enough to be of much help with that. What you have looks enough like the Ruby that I suspect it wouldn't work. Might be best to restructure it a bit in a way that's better suited for Python.
@sidoh @maddox I've spent a little time in getting a good port working here.
Essentially, brightness can be achieved with (RGBW * % brightness)
, and a kelvin scale for plain RGB strips can be achieved using the ktorgb()
function, and you can also adjust the brightness from there.
I've only barely tested it, but it should have API-parity with @sidoh's stuff, and it works on my strip. I'll work on getting a component in for this for HA.
RGB*%brightness is destructive, right? If you make something darker, you can't make it brighter again?
I ended up doing RGB -> HSL, adjusting luminosity, and converting back to RGB. That seemed to work pretty well. Code here:
https://github.com/sidoh/ha_gateway/blob/master/ha_gateway.rb#L41
@sidoh That's a smart idea, I'll try that. Have you taken a look at the additional Kelvin scale? It's a fake incandescent lighting, but it does the job alright.
@sidoh @maddox Here's what I've got for the LEDENET component. It's not working that well, and I can't figure out how to display an RGB color selector in the frontend...
import logging
from ledenet import *
from colorsys import *
from homeassistant.components.light import (
Light, ATTR_BRIGHTNESS, ATTR_RGB_COLOR)
_LOGGER = logging.getLogger(__name__)
DEPENDENCIES = []
#REQUIREMENTS = ['https://github.com/avaidyam/LEDENET/']
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
""" Find and return LEDENET lights. """
bulbs = LEDENETDevice.discover_devices(expected_devices=999, timeout=5)
add_devices_callback([LEDENETLight(n) for n in bulbs])
class LEDENETLight(Light):
""" Provides an LEDENET bulb. """
def __init__(self, bulb):
self._bulb = bulb
self._name = self._bulb.ip.decode('utf-8')
@property
def should_poll(self):
return False
@property
def name(self):
""" Returns the name of the device if any. """
return self._name
@property
def brightness(self):
""" Brightness of this light between 0..255. """
rgb = self._bulb.get_color()
hls = rgb_to_hls(rgb[0] / 255.0, rgb[1] / 255.0, rgb[2] / 255.0)
return int(hls[1] * 255)
@property
def rgb_color(self):
""" rgb color value. """
return self._bulb.get_color()
@property
def is_on(self):
""" True if device is on. """
return self._bulb.is_on()
def turn_on(self, **kwargs):
""" Turn the device on. """
self._bulb.set_on(False)
bright = 1.0
if ATTR_BRIGHTNESS in kwargs:
bright = float(kwargs[ATTR_BRIGHTNESS]) / 255.0
rgb = [1.0, 1.0, 1.0]
if ATTR_RGB_COLOR in kwargs:
rgb = kwargs[ATTR_RGB_COLOR]
hls = rgb_to_hls(rgb[0], rgb[1], rgb[2])
hls[1] = bright
rgb = hls_to_rgb(hls[0], hls[1], hls[2])
self._bulb.set_color(rgb[0] * 255, rgb[1] * 255, rgb[2] * 255, 0x00)
self.update_ha_state()
def turn_off(self, **kwargs):
""" Turn the device off. """
self._bulb.set_on(False)
self.update_ha_state()
For this platform, I think you want to enable polling.
@balloob Gotcha, thanks. It's not really working right to begin with, so I'll continue debugging it.
Any updates on support for this ? I have a similar LEDNET wifi strip adapter, trying to integrate somehow as well. Best I've found so far is a python script that sends commands as hex codes. For now I can turn on/ off with a command line strip, but would like a bit more control over it eventually.
Here's a python project for Lednet bulbs. https://github.com/beville/flux_led This may be a good place to start. I've only used it with a single bulb
Unfortunately, the code here is as-is. I've switched away from this platform, which I found fairly unreliable, to a SmartThings hub.
This LED controller API makes the combination of a 30$ controller and a 15$ LED strip much more appealing than a Philips Hue LED strip for more than double the cost. @sidoh has already reverse engineered its API and provided a very simple wrapper. It should be simple to integrate. Any thoughts?
The following Ruby files are the pertinent ones: api.rb and device_discovery.rb, and if translated to Python would be simple to plug in.