Skip to content

Commit

Permalink
Merge pull request #19 from jfarmer08/Dev
Browse files Browse the repository at this point in the history
Support for E11-G14
  • Loading branch information
jfarmer08 authored Aug 25, 2020
2 parents 8266359 + d48ccd5 commit 141860d
Show file tree
Hide file tree
Showing 13 changed files with 1,049 additions and 1,100 deletions.
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

This is a custom component to allow control of Sengled Bulbs in Homeassistant using the unofficial Sengled API. Please note this mimics the Sengled app and therefore Sengled may cut off access at anytime.

# Supported Bulbs from Sengled
You can find [Supported Product from Sengled](https://github.com/jfarmer08/ha-sengledapi/wiki/Supported-Product-from-Sengled.) here. If you have other bulbs that are not on this list and they do work, you can create a pull request to have the wiki updated.

### Highlights of what **SengledApi** can do

* Control Sengled Bulbs as lights through HA
Expand All @@ -13,6 +16,19 @@ This is a custom component to allow control of Sengled Bulbs in Homeassistant us
* I only have Element Classic A19 Kit (Light bulbs + Hub) to test https://us.sengled.com/products/element-classic-kit
* An update may break them without my knowledge. **Please use the betas as they become avaliable**

**In this Update**
* Wifi bulbs are now supported by adding ```wifi: true``` to your configuration. All functions at this time are not supported.
# Only 2 wifi bulbs are supported at this time
1. Sengled Smart Wi-Fi LED Multicolor A19 Bulb : wificolora19
2. Sengled Smart Wi-Fi LED Daylight A19 Bulb : wifia19

# Supported Functions of Wifi Bulbs
1. View bulbs on/off state
2. Control bulbs on/off state
3. View Brightness
4. View Color Temp for supported bulbs
5. View Color for supported bulbs

## Installation (HACS) - Highly Recommended

1. Have HACS installed, this will allow you to easily update
Expand Down Expand Up @@ -43,6 +59,7 @@ sengledapi:
username: <email for sengled>
password: <password for sengled>
country: <country>
wifi: true
```
## Usage
Expand Down
129 changes: 57 additions & 72 deletions custom_components/sengledapi/light.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
"""Platform for light integration."""

import logging
from datetime import timedelta

from .sengledapi.sengledapi import SengledApi
from .const import ATTRIBUTION, DOMAIN

Expand All @@ -22,6 +24,8 @@
LightEntity,
)

# Add to support quicker update time. Is this to Fast?
SCAN_INTERVAL = timedelta(seconds=5)

_LOGGER = logging.getLogger(__name__)

Expand All @@ -31,8 +35,13 @@ async def async_setup_platform(hass, config, add_entities, discovery_info=None):
_LOGGER.debug("""Creating new Sengled light component""")
# Add devices
add_entities(
SengledBulb(light)
for light in await hass.data[DOMAIN]["sengledapi_account"].async_list_bulbs()
[
SengledBulb(light)
for light in await hass.data[DOMAIN][
"sengledapi_account"
].async_list_bulbs()
],
True,
)


Expand All @@ -45,107 +54,70 @@ def __init__(self, light):
self._name = light._friendly_name
self._state = light._state
self._brightness = light._brightness
self._avaliable = True
self._avaliable = light._avaliable
self._device_mac = light._device_mac
self._device_model = light._device_model
self._color_temperature = light._color_temperature
self._color = light._color
self._device_rssi = light._device_rssi

@property
def name(self):
"""Return the display name of this light."""
# pylint:disable=logging-not-lazy
_LOGGER.debug(
"SengledApi Light "
+ self._name
+ " State "
+ str(self._state)
+ " Brightness "
+ str(self._brightness)
+ " Avaliable "
+ str(self._avaliable)
+ " Devive Mac "
+ str(self._device_mac)
+ " Device Model "
+ str(self._device_model)
)
return self._name

@property
def unique_id(self):
# pylint:disable=logging-not-lazy
_LOGGER.debug(
"SengledApi Light "
+ self._name
+ " State "
+ str(self._state)
+ " Brightness "
+ str(self._brightness)
+ " Avaliable "
+ str(self._avaliable)
+ " Devive Mac "
+ str(self._device_mac)
+ " Device Model "
+ str(self._device_model)
)
return self._device_mac

@property
def available(self):
# pylint:disable=logging-not-lazy
"""Return the connection status of this light"""
_LOGGER.debug(
"SengledApi Light "
+ self._name
+ " State "
+ str(self._state)
+ " Brightness "
+ str(self._brightness)
+ " Avaliable "
+ str(self._avaliable)
+ " Devive Mac "
+ str(self._device_mac)
+ " Device Model "
+ str(self._device_model)
)
return self._avaliable

@property
def device_state_attributes(self):
# pylint:disable=logging-not-lazy
"""Return device attributes of the entity."""
_LOGGER.debug(
"SengledApi Light "
+ self._name
+ " State "
+ str(self._state)
+ " Brightness "
+ str(self._brightness)
+ " Avaliable "
+ str(self._avaliable)
+ " Devive Mac "
+ str(self._device_mac)
+ " Device Model "
+ str(self._device_model)
)
return {
ATTR_ATTRIBUTION: ATTRIBUTION,
"state": self._state,
"available": self._avaliable,
"device model": self._device_model,
"device rssi": self._device_rssi,
"mac": self._device_mac,
}

@property
def color_temp(self):
"""Return the color_temp of the light."""
color_temp = self._color_temperature
color_temp = int(self._color_temperature)
if color_temp is None:
return 1
return color_temp
return None
return colorutil.color_temperature_kelvin_to_mired(color_temp)

@property
def hs_color(self):
"""Return the hs_color of the light."""
_LOGGER.debug("FARMER::::::: %s", str(self._color))
a, b, c = self._color.split(":")
_LOGGER.debug(a)
_LOGGER.debug(b)
_LOGGER.debug(c)
return colorutil.color_RGB_to_hs(int(a), int(b), int(c))

# @property
# def min_mireds(self):
# """Return color temperature min mireds."""
# return colorutil.color_temperature_kelvin_to_mired(2000)

# @property
## def max_mireds(self):
# """Return color temperature max mireds."""
# return colorutil.color_temperature_kelvin_to_mired(6500)

@property
def brightness(self):
# pylint:disable=logging-not-lazy
"""Return the brightness of the light.
This method is optional. Removing it indicates to Home Assistant
Expand All @@ -169,15 +141,27 @@ def supported_features(self):

async def async_turn_on(self, **kwargs):
"""Instruct the light to turn on. """
if self._device_model != "wificolora19":
self._light._brightness = kwargs.get(ATTR_BRIGHTNESS)
# self._light._colortemp = kwargs.get(ATTR_COLOR_TEMP)
"""Turn on or control the light."""
if (
ATTR_BRIGHTNESS not in kwargs
and ATTR_HS_COLOR not in kwargs
and ATTR_COLOR_TEMP not in kwargs
):
await self._light.async_turn_on()
if ATTR_BRIGHTNESS in kwargs:
await self._light.async_turn_on()
await self._light.set_brightness(kwargs[ATTR_BRIGHTNESS])
if ATTR_HS_COLOR in kwargs:
self._light.set_color(kwargs[ATTR_HS_COLOR])
if ATTR_COLOR_TEMP in kwargs:
color_temp = colorutil.color_temperature_mired_to_kelvin(
kwargs[ATTR_COLOR_TEMP]
)
await self._light.set_color_temp(color_temp)

async def async_turn_off(self, **kwargs):
"""Instruct the light to turn off."""
if self._device_model != "wificolora19":
await self._light.async_turn_off()
await self._light.async_turn_off()

async def async_update(self):
"""Fetch new state data for this light.
Expand All @@ -187,4 +171,5 @@ async def async_update(self):
self._state = self._light.is_on()
self._avaliable = self._light._avaliable
self._brightness = self._light._brightness
self._color_temperature = self._color_temperature
self._color_temperature = self._light._color_temperature
self._color = self._light._color
2 changes: 1 addition & 1 deletion custom_components/sengledapi/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"documentation": "https://github.com/jfarmer08/ha-sengledapi",
"dependencies": [],
"codeowners": ["@jfarmer08"],
"requirements": [],
"requirements": ["paho-mqtt==1.5.0"],
"icons": [
"https://camo.githubusercontent.com/4e40f86270556ef2922b12694496746b7d96d6fd/68747470733a2f2f7a69676265652e626c616b61646465722e636f6d2f6173736574732f696d616765732f646576696365732f53656e676c65645f4531312d4731332e6a7067"
],
Expand Down
134 changes: 134 additions & 0 deletions custom_components/sengledapi/sengledapi/bulbs/sengled_bulb.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
"""Sengled Bulb Integration."""

import asyncio
import logging

_LOGGER = logging.getLogger(__name__)
_LOGGER.debug("SengledApi: Initializing Sengled Bulb")


class SengledBulb:
def __init__(
self,
api,
device_mac,
friendly_name,
state,
device_model,
brightness,
device_rssi,
isonline,
jsession_id,
country,
):
_LOGGER.debug("SengledApi: Bulb %s initializing.", friendly_name)

self._api = api
self._device_mac = device_mac
self._friendly_name = friendly_name
self._state = state
self._avaliable = isonline
self._just_changed_state = False
self._device_model = device_model
self._brightness = int(brightness)
self._color = None
self._jsession_id = jsession_id
self._country = country
self._color_temperature = None
self._device_rssi = self.translate(int(device_rssi), 0, 5, 0, -100)

async def async_turn_on(self):
_LOGGER.debug(
"SengledApi: Bulb %s %s turning on.", self._friendly_name, self._device_mac
)

if self._brightness is not None:
url = (
"https://"
+ self._country
+ "-elements.cloud.sengled.com/zigbee/device/deviceSetBrightness.json"
)

if self._brightness:
brightness = self._brightness

payload = {"deviceUuid": self._device_mac, "brightness": brightness}

else:
url = (
"https://"
+ self._country
+ "-elements.cloud.sengled.com/zigbee/device/deviceSetOnOff.json"
)

payload = {"deviceUuid": self._device_mac, "onoff": "1"}

loop = asyncio.get_running_loop()
# loop.create_task(SengledRequest(url, payload).async_get_response(self._jsession_id))
loop.create_task(self._api.async_do_request(url, payload, self._jsession_id))

self._state = True
self._just_changed_state = True

async def async_turn_off(self):
_LOGGER.debug(
"SengledApi: Bulb %s %s turning off.", self._friendly_name, self._device_mac
)
url = (
"https://"
+ self._country
+ "-elements.cloud.sengled.com/zigbee/device/deviceSetOnOff.json"
)
payload = {"deviceUuid": self._device_mac, "onoff": "0"}

loop = asyncio.get_running_loop()
# loop.create_task(SengledRequest(url, payload).async_get_response(self._jsession_id))
loop.create_task(self._api.async_do_request(url, payload, self._jsession_id))

self._state = False
self._just_changed_state = True

def is_on(self):
return self._state

async def async_update(self):
_LOGGER.debug(
"Sengled Bulb "
+ self._friendly_name
+ " "
+ self._device_mac
+ " updating."
)
if self._just_changed_state:
self._just_changed_state = False
else:
url = (
"https://element.cloud.sengled.com/zigbee/device/getDeviceDetails.json"
)
payload = {}

data = await self._api.async_do_request(url, payload, self._jsession_id)

_LOGGER.debug("Light " + self._friendly_name + " updating.")
for item in data["deviceInfos"]:
for items in item["lampInfos"]:
if items["deviceUuid"] == self._device_mac:
self._friendly_name = items["attributes"]["name"]
self._brightness = int(items["attributes"]["brightness"])
self._state = (
True if int(items["attributes"]["onoff"]) == 1 else False
)
self._avaliable = (
False if int(items["attributes"]["isOnline"]) == 0 else True
)

def translate(self, value, leftMin, leftMax, rightMin, rightMax):
# Figure out how 'wide' each range is
leftSpan = leftMax - leftMin
rightSpan = rightMax - rightMin

# Convert the left range into a 0-1 range (float)
valueScaled = float(value - leftMin) / float(leftSpan)

# Convert the 0-1 range into a value in the right range.
return rightMin + (valueScaled * rightSpan)
Loading

0 comments on commit 141860d

Please sign in to comment.