Skip to content

Commit

Permalink
Merge branch 'AlexxIT:master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
dguihal authored Oct 16, 2023
2 parents 329f9df + 74cd74c commit 9f034f9
Show file tree
Hide file tree
Showing 9 changed files with 208 additions and 37 deletions.
14 changes: 13 additions & 1 deletion custom_components/sonoff/core/devices.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
XLightGroup,
XLightL1,
XLightL3,
XT5Light,
)
from ..number import XPulseWidth
from ..remote import XRemote
Expand All @@ -51,6 +52,7 @@
XEnergySensorDualR3,
XEnergySensorPOWR3,
XEnergyTotal,
XT5Action,
)
from ..switch import (
XSwitch,
Expand Down Expand Up @@ -289,7 +291,13 @@ def spec(cls, base: str = None, enabled: bool = None, **kwargs) -> type:
136: [spec(XLightB05B, min_ct=0, max_ct=100), RSSI], # Sonoff B05-BL
137: [XLightL1, RSSI],
# https://github.com/AlexxIT/SonoffLAN/issues/623#issuecomment-1365841454
138: [Switch1, LED, RSSI, XDetach], # MINIR3, MINIR4
138: [
Switch1,
LED,
RSSI,
XDetach,
spec(XRemoteButton, param="action"),
], # MINIR3, MINIR4
# https://github.com/AlexxIT/SonoffLAN/issues/808
154: [XWiFiDoor, Battery, RSSI], # DW2-Wi-Fi-L
162: SPEC_3CH, # https://github.com/AlexxIT/SonoffLAN/issues/659
Expand Down Expand Up @@ -335,6 +343,10 @@ def spec(cls, base: str = None, enabled: bool = None, **kwargs) -> type:
], # Sonoff POWR3
# https://github.com/AlexxIT/SonoffLAN/issues/984
195: [XTemperatureTH], # NSPanel Pro
# https://github.com/AlexxIT/SonoffLAN/issues/1183
209: [Switch1, XT5Light, XT5Action], # T5-1C-86
210: [Switch1, Switch2, XT5Light, XT5Action], # T5-2C-86
211: [Switch1, Switch2, Switch3, XT5Light, XT5Action], # T5-3C-86
1000: [XRemoteButton, Battery], # zigbee_ON_OFF_SWITCH_1000
1256: [spec(XSwitch, base="light")], # ZCL_HA_DEVICEID_ON_OFF_LIGHT
1257: [spec(XLightD1, base="light")], # ZigbeeWhiteLight
Expand Down
4 changes: 2 additions & 2 deletions custom_components/sonoff/core/ewelink/cloud.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,9 +214,9 @@ async def send(
log += f"{params} | "

# protect cloud from DDoS (it can break connection)
while time.time() - self.last_ts < 0.1:
while (delay := self.last_ts + 0.1 - time.time()) > 0:
log += "DDoS | "
await asyncio.sleep(0.1)
await asyncio.sleep(delay)
self.last_ts = time.time()

if sequence is None:
Expand Down
2 changes: 2 additions & 0 deletions custom_components/sonoff/core/ewelink/local.py
Original file line number Diff line number Diff line change
Expand Up @@ -249,4 +249,6 @@ def decrypt_msg(msg: dict, devicekey: str = None) -> dict:
# Fix Sonoff RF Bridge sintax bug
if data and data.startswith(b'{"rf'):
data = data.replace(b'"="', b'":"')
# fix https://github.com/AlexxIT/SonoffLAN/issues/1160
data = data.rstrip(b"\x02")
return json.loads(data)
124 changes: 99 additions & 25 deletions custom_components/sonoff/light.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import time

from homeassistant.components.light import (
COLOR_MODE_BRIGHTNESS,
COLOR_MODE_COLOR_TEMP,
COLOR_MODE_ONOFF,
COLOR_MODE_RGB,
SUPPORT_EFFECT,
SUPPORT_TRANSITION,
LightEntity,
)
from homeassistant.util import color
Expand Down Expand Up @@ -45,6 +48,7 @@ class XLight(XEntity, LightEntity):
# support on/off and brightness
_attr_color_mode = COLOR_MODE_BRIGHTNESS
_attr_supported_color_modes = {COLOR_MODE_BRIGHTNESS}
_attr_supported_features = SUPPORT_TRANSITION

def set_state(self, params: dict):
if self.param in params:
Expand All @@ -61,17 +65,22 @@ async def async_turn_on(
xy_color=None,
hs_color=None,
effect: str = None,
**kwargs
transition: float = None,
**kwargs,
) -> None:
if brightness == 0:
await self.async_turn_off()
return

if xy_color:
rgb_color = color.color_xy_to_RGB(*xy_color)
elif hs_color:
rgb_color = color.color_hs_to_RGB(*hs_color)

if transition:
await self.transiton(brightness, color_temp, rgb_color, transition)
return

if brightness == 0:
await self.async_turn_off()
return

if brightness or color_temp or rgb_color or effect:
params = self.get_params(brightness, color_temp, rgb_color, effect)
else:
Expand All @@ -83,15 +92,50 @@ async def async_turn_on(
await self.ewelink.send(
self.device, {self.param: "on"}, query_cloud=False
)

await self.ewelink.send(
self.device, params, {"cmd": "dimmable", **params}, cmd_lan="dimmable"
self.device,
params,
{"cmd": "dimmable", **params},
cmd_lan="dimmable",
query_cloud=kwargs.get("query_cloud", True),
)
else:
await self.ewelink.send(self.device, {self.param: "on"})

async def async_turn_off(self, **kwargs) -> None:
await self.ewelink.send(self.device, {self.param: "off"})

async def transiton(
self,
brightness: int,
color_temp: int,
rgb_color,
transition: float,
):
br0 = self.brightness or 0
br1 = brightness
ct0 = self.color_temp or self.min_mireds
ct1 = color_temp
rgb0 = self.rgb_color or [0, 0, 0]
rgb1 = rgb_color

t0 = time.time()

while (k := (time.time() - t0) / transition) < 1:
if br1 is not None:
brightness = br0 + round((br1 - br0) * k)
if ct1 is not None:
color_temp = ct0 + round((ct1 - ct0) * k)
if rgb1 is not None:
rgb_color = [rgb0[i] + round((rgb1[i] - rgb0[i]) * k) for i in range(3)]

await self.async_turn_on(
brightness, color_temp, rgb_color, query_cloud=False
)

await self.async_turn_on(br1, ct1, rgb1)


# noinspection PyAbstractClass, UIID36
class XDimmer(XLight):
Expand Down Expand Up @@ -199,7 +243,7 @@ class XLightB1(XLight):
_attr_effect_list = list(UIID22_MODES.keys())
# support on/off, brightness, color_temp and RGB
_attr_supported_color_modes = {COLOR_MODE_COLOR_TEMP, COLOR_MODE_RGB}
_attr_supported_features = SUPPORT_EFFECT
_attr_supported_features = SUPPORT_EFFECT | SUPPORT_TRANSITION

def set_state(self, params: dict):
XLight.set_state(self, params)
Expand Down Expand Up @@ -295,7 +339,7 @@ class XLightL1(XLight):

# support on/off, brightness, RGB
_attr_supported_color_modes = {COLOR_MODE_RGB}
_attr_supported_features = SUPPORT_EFFECT
_attr_supported_features = SUPPORT_EFFECT | SUPPORT_TRANSITION

def set_state(self, params: dict):
XLight.set_state(self, params)
Expand All @@ -314,23 +358,23 @@ def set_state(self, params: dict):
)

def get_params(self, brightness, color_temp, rgb_color, effect) -> dict:
params = {}
if effect:
return self.modes.get(effect)
if brightness or rgb_color:
# support bright and color in one command
params = {"mode": 1}
if brightness:
params["bright"] = conv(brightness, 1, 255, 1, 100)
if rgb_color:
params.update(
{
"colorR": rgb_color[0],
"colorG": rgb_color[1],
"colorB": rgb_color[2],
"light_type": 1,
}
)
return params
params.update(self.modes[effect])
if brightness:
params.setdefault("mode", 1)
params["bright"] = conv(brightness, 1, 255, 1, 100)
if rgb_color:
params.setdefault("mode", 1)
params.update(
{
"colorR": rgb_color[0],
"colorG": rgb_color[1],
"colorB": rgb_color[2],
"light_type": 1,
}
)
return params


# noinspection PyAbstractClass
Expand Down Expand Up @@ -729,7 +773,7 @@ class XLightB02(XLight):
_attr_effect_list = list(B02_MODE_PAYLOADS.keys())
# support on/off, brightness and color_temp
_attr_supported_color_modes = {COLOR_MODE_COLOR_TEMP}
_attr_supported_features = SUPPORT_EFFECT
_attr_supported_features = SUPPORT_EFFECT | SUPPORT_TRANSITION

# ewelink specs
min_br = 1
Expand Down Expand Up @@ -1027,3 +1071,33 @@ async def async_turn_on(

async def async_turn_off(self, **kwargs) -> None:
await self.ewelink.send(self.device, {"lightswitch": 0})


class XT5Light(XEntity, LightEntity):
params = {"lightSwitch", "lightMode"}

_attr_effect_list = ["0", "1", "2", "3", "4", "5", "6", "7"]
_attr_supported_features = SUPPORT_EFFECT

def set_state(self, params: dict):
if "lightSwitch" in params:
self._attr_is_on = params["lightSwitch"] == "on"

if "lightMode" in params:
self._attr_effect = str(params["lightMode"])

async def async_turn_on(
self, brightness: int = None, effect: str = None, **kwargs
) -> None:
params = {}

if effect and effect != "0":
params["lightMode"] = int(effect)

if not params:
params["lightSwitch"] = "on"

await self.ewelink.send(self.device, params)

async def async_turn_off(self, **kwargs) -> None:
await self.ewelink.send(self.device, {"lightSwitch": "off"})
12 changes: 6 additions & 6 deletions custom_components/sonoff/manifest.json
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
{
"domain": "sonoff",
"name": "Sonoff",
"config_flow": true,
"documentation": "https://github.com/AlexxIT/SonoffLAN",
"issue_tracker": "https://github.com/AlexxIT/SonoffLAN/issues",
"codeowners": [
"@AlexxIT"
],
"config_flow": true,
"dependencies": [
"http",
"zeroconf"
],
"documentation": "https://github.com/AlexxIT/SonoffLAN",
"iot_class": "local_push",
"issue_tracker": "https://github.com/AlexxIT/SonoffLAN/issues",
"requirements": [
"pycryptodome>=3.6.6"
],
"version": "3.5.2",
"iot_class": "local_push"
}
"version": "3.5.3"
}
26 changes: 25 additions & 1 deletion custom_components/sonoff/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -305,10 +305,11 @@ def internal_available(self) -> bool:


class XRemoteButton(XEntity, SensorEntity):
_attr_native_value = ""

def __init__(self, ewelink: XRegistry, device: dict):
XEntity.__init__(self, ewelink, device)
self.params = {"key"}
self._attr_native_value = ""

def set_state(self, params: dict):
button = params.get("outlet")
Expand All @@ -324,6 +325,29 @@ async def clear_state(self):
self._async_write_ha_state()


class XT5Action(XEntity, SensorEntity):
uid = "action"
_attr_native_value = ""

def __init__(self, ewelink: XRegistry, device: dict):
XEntity.__init__(self, ewelink, device)
self.params = {"triggerType", "slide"}

def set_state(self, params: dict):
if params.get("triggerType") == 2:
self._attr_native_value = "touch"
asyncio.create_task(self.clear_state())

if slide := params.get("slide"):
self._attr_native_value = f"slide_{slide}"
asyncio.create_task(self.clear_state())

async def clear_state(self):
await asyncio.sleep(0.5)
self._attr_native_value = ""
self._async_write_ha_state()


class XUnknown(XEntity, SensorEntity):
_attr_device_class = SensorDeviceClass.TIMESTAMP

Expand Down
2 changes: 1 addition & 1 deletion tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def init(device: dict, config: dict = None) -> (XRegistry, List[XEntity]):
reg.dispatcher_connect(SIGNAL_ADD_ENTITIES, lambda x: entities.extend(x))
entities += reg.setup_devices(devices)

hass = HomeAssistant()
hass = HomeAssistant("")
for entity in entities:
if not isinstance(entity, Entity):
continue
Expand Down
Loading

0 comments on commit 9f034f9

Please sign in to comment.