Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 49 additions & 1 deletion custom_components/aqara_gateway/core/gateway.py
Original file line number Diff line number Diff line change
Expand Up @@ -818,7 +818,55 @@ def send(self, device: dict, data: dict):
""" send command """
try:
payload = {}
if device['type'] == 'zigbee' or 'paring' in data:
# Check if this is the specific M1S gateway model that needs special handling
is_m1s_gateway = (device['type'] == 'gateway' and
device['model'] == 'lumi.gateway.acn01')

if is_m1s_gateway:
# For M1S gateway, try sending through ioctl/recv with rgb type
# This matches the format used by other gateways
if ATTR_HS_COLOR in data:
hs_color = data.get(ATTR_HS_COLOR, 0)
brightness = (hs_color >> 24) & 0xFF
payload = {
'cmd': 'control',
'data': {
'blue': int((hs_color & 0xFF) * brightness / 100),
'breath': 500,
'green': int(
((hs_color >> 8) & 0xFF) * brightness / 100),
'red': int(
((hs_color >> 16) & 0xFF) * brightness / 100)},
'type': 'rgb',
'rev': 1,
'from': 'ha',
'id': randint(0, 65535)
}
else:
# For simple on/off or brightness, send through ioctl/recv
brightness = 100 # Default brightness
if 'light' in data:
brightness = data['light']
elif ATTR_BRIGHTNESS in data:
brightness = data[ATTR_BRIGHTNESS]

payload = {
'cmd': 'control',
'data': {
'blue': int(255 * brightness / 100),
'breath': 500,
'green': int(255 * brightness / 100),
'red': int(255 * brightness / 100)},
'type': 'rgb',
'rev': 1,
'from': 'ha',
'id': randint(0, 65535)
}

payload = json.dumps(payload, separators=(',', ':')).encode()
self._mqttc.publish('ioctl/recv', payload)
_LOGGER.debug(f"M1S Gateway: Sent payload to ioctl/recv: {payload}")
elif device['type'] == 'zigbee' or 'paring' in data:
did = data.get('did', device['did'])
data.pop('did', '')
params = []
Expand Down
201 changes: 160 additions & 41 deletions custom_components/aqara_gateway/light.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,13 @@ def extra_state_attributes(self):

def update(self, data: dict = None):
""" update attribue in data """
# Check if this is the specific M1S gateway model that needs special handling
is_m1s_gateway = (self.device['type'] == 'gateway' and
self.device['model'] == 'lumi.gateway.acn01')

_LOGGER.debug(f"M1S Gateway light update called. Device: {self.device['model']}, Type: {self.device['type']}, is_m1s_gateway: {is_m1s_gateway}")
_LOGGER.debug(f"update data: {data}")

for key, value in data.items():
if key == CHIP_TEMPERATURE:
self._chip_temperature = value
Expand All @@ -127,14 +134,37 @@ def update(self, data: dict = None):
self._state = any(data[self._attr])
else:
self._state = data[self._attr] >= 1
if ATTR_BRIGHTNESS in data:
_LOGGER.debug(f"Updated state: {self._state}")

# For M1S gateway, handle both 'light' and 'brightness' attributes
if is_m1s_gateway and 'light' in data:
self._attr_brightness = int(data['light'] / 100.0 * 255.0)
_LOGGER.debug(f"M1S Gateway: Updated light attribute: {self._attr_brightness}")
elif is_m1s_gateway and ATTR_BRIGHTNESS in data:
self._attr_brightness = int(data[ATTR_BRIGHTNESS] / 100.0 * 255.0)
_LOGGER.debug(f"M1S Gateway: Updated brightness attribute: {self._attr_brightness}")
elif ATTR_BRIGHTNESS in data:
self._attr_brightness = int(data[ATTR_BRIGHTNESS] / 100.0 * 255.0)
if ATTR_COLOR_TEMP in data:
self._attr_color_temp_kelvin = color_util.color_temperature_mired_to_kelvin(data[ATTR_COLOR_TEMP])
if is_m1s_gateway:
# For M1S gateway, use direct color temp value
self._attr_color_temp = data[ATTR_COLOR_TEMP]
_LOGGER.debug(f"M1S Gateway: Updated color temp: {self._attr_color_temp}")
else:
# For other devices, convert to kelvin
self._attr_color_temp_kelvin = color_util.color_temperature_mired_to_kelvin(data[ATTR_COLOR_TEMP])
_LOGGER.debug(f"Other device: Updated color temp kelvin: {self._attr_color_temp_kelvin}")
if ATTR_RGB_COLOR in data:
x_val = float(data[ATTR_RGB_COLOR] / (2**16))
y_val = float(data[ATTR_RGB_COLOR] - x_val)
self._attr_rgb_color = color_util.color_xy_to_RGB(x_val, y_val)
if is_m1s_gateway:
# For M1S gateway, use direct RGB value
self._attr_rgb_color = data[ATTR_RGB_COLOR]
_LOGGER.debug(f"M1S Gateway: Updated RGB color: {self._attr_rgb_color}")
else:
# For other devices, convert to XY color space
x_val = float(data[ATTR_RGB_COLOR] / (2**16))
y_val = float(data[ATTR_RGB_COLOR] - x_val)
self._attr_rgb_color = color_util.color_xy_to_RGB(x_val, y_val)
_LOGGER.debug(f"Other device: Updated RGB color from XY: {self._attr_rgb_color}")
if ATTR_HS_COLOR in data:
if self.device['type'] == 'zigbee':
if isinstance(data[ATTR_HS_COLOR], int):
Expand All @@ -161,46 +191,135 @@ def turn_on(self, **kwargs):
"""Turn the light on."""
payload = {}

if ATTR_BRIGHTNESS in kwargs:
self._attr_brightness = int(kwargs[ATTR_BRIGHTNESS] / 255.0 * 100.0)
payload[ATTR_BRIGHTNESS] = self._attr_brightness

if ATTR_COLOR_TEMP_KELVIN in kwargs:
self._attr_color_temp_kelvin = kwargs[ATTR_COLOR_TEMP_KELVIN]
payload[ATTR_COLOR_TEMP] = color_util.color_temperature_kelvin_to_mired(self._attr_color_temp_kelvin)

if ATTR_RGB_COLOR in kwargs:
self._attr_rgb_color = kwargs[ATTR_RGB_COLOR]

if ATTR_HS_COLOR in kwargs:
self._attr_hs_color = kwargs[ATTR_HS_COLOR]

if (ATTR_HS_COLOR in kwargs or ATTR_BRIGHTNESS in kwargs):
if self._attr_hs_color:
payload[ATTR_HS_COLOR] = color_util.color_temperature_kelvin_to_mired(self._attr_color_temp_kelvin)
rgb = color_util.color_hs_to_RGB(*self._attr_hs_color)
rgba = (self._attr_brightness,) + rgb
if isinstance(self._attr_brightness, int):
rgbhex = binascii.hexlify(
struct.pack("BBBB", *rgba)).decode("ASCII")
rgbhex = int(rgbhex, 16)
if self.device['type'] == 'zigbee':
# Check if this is the specific M1S gateway model that needs special handling
is_m1s_gateway = (self.device['type'] == 'gateway' and
self.device['model'] == 'lumi.gateway.acn01')

_LOGGER.debug(f"M1S Gateway light turn_on called. Device: {self.device['model']}, Type: {self.device['type']}, is_m1s_gateway: {is_m1s_gateway}")
_LOGGER.debug(f"turn_on kwargs: {kwargs}")

if is_m1s_gateway:
# For M1S gateway, use 'light' attribute as in version 0.2.9
if ATTR_BRIGHTNESS in kwargs:
self._attr_brightness = int(kwargs[ATTR_BRIGHTNESS] / 255.0 * 100.0)
payload['light'] = self._attr_brightness
_LOGGER.debug(f"M1S Gateway: Setting light attribute: {self._attr_brightness}")

# Preserve current color when changing brightness
if hasattr(self, '_attr_rgb_color') and self._attr_rgb_color:
rgb = self._attr_rgb_color
rgba = (self._attr_brightness,) + rgb
if isinstance(self._attr_brightness, int):
rgbhex = binascii.hexlify(
struct.pack("BBBB", *rgba)).decode("ASCII")
rgbhex = int(rgbhex, 16)
payload[ATTR_HS_COLOR] = rgbhex
else:
_LOGGER.debug(f"M1S Gateway: Preserving RGB color with new brightness {self._attr_brightness}: {rgbhex}")

if ATTR_COLOR_TEMP in kwargs:
self._attr_color_temp = kwargs[ATTR_COLOR_TEMP]
payload[ATTR_COLOR_TEMP] = self._attr_color_temp
_LOGGER.debug(f"M1S Gateway: Setting color temp: {self._attr_color_temp}")
if ATTR_COLOR_TEMP_KELVIN in kwargs:
self._attr_color_temp_kelvin = kwargs[ATTR_COLOR_TEMP_KELVIN]
payload[ATTR_COLOR_TEMP] = color_util.color_temperature_kelvin_to_mired(self._attr_color_temp_kelvin)
if ATTR_RGB_COLOR in kwargs:
self._attr_rgb_color = kwargs[ATTR_RGB_COLOR]
payload[ATTR_RGB_COLOR] = self._attr_rgb_color
_LOGGER.debug(f"M1S Gateway: Setting RGB color: {self._attr_rgb_color}")

# Convert RGB to HS color for M1S gateway
rgb = self._attr_rgb_color
hs_color = color_util.color_RGB_to_hs(*rgb)
if hs_color:
# Use current brightness if not provided in kwargs
brightness = self._attr_brightness if hasattr(self, '_attr_brightness') else 100
rgba = (brightness,) + rgb
if isinstance(brightness, int):
rgbhex = binascii.hexlify(
struct.pack("BBBB", *rgba)).decode("ASCII")
rgbhex = int(rgbhex, 16)
payload[ATTR_HS_COLOR] = rgbhex
_LOGGER.debug(f"M1S Gateway: RGB color converted to HEX with brightness {brightness}: {rgbhex}")

# Ensure brightness is set in the payload to update the UI
payload['light'] = brightness
_LOGGER.debug(f"M1S Gateway: Ensuring brightness is set to {brightness} in payload for UI update")

# Update the brightness attribute in the entity to reflect the current brightness
self._attr_brightness = brightness
_LOGGER.debug(f"M1S Gateway: Updated brightness attribute to {brightness} for UI consistency")

if ATTR_HS_COLOR in kwargs:
self._attr_hs_color = kwargs[ATTR_HS_COLOR]
_LOGGER.debug(f"M1S Gateway: Setting HS color: {self._attr_hs_color}")

if self._attr_hs_color:
# Convert HS to RGB and then to HEX format
rgb = color_util.color_hs_to_RGB(*self._attr_hs_color)
rgba = (self._attr_brightness,) + rgb
if isinstance(self._attr_brightness, int):
rgbhex = binascii.hexlify(
struct.pack("BBBB", *rgba)).decode("ASCII")
rgbhex = int(rgbhex, 16)
payload[ATTR_HS_COLOR] = rgbhex
if ATTR_RGB_COLOR in kwargs and self._attr_rgb_color:
x_val, y_val = color_util.color_RGB_to_xy(*kwargs[ATTR_RGB_COLOR])
payload[ATTR_RGB_COLOR] = int(x_val * 65535) * (2 ** 16) + int(y_val * 65535)
_LOGGER.debug(f"M1S Gateway: HS color converted to HEX: {rgbhex}")
else:
# For other devices, use the standard logic
if ATTR_BRIGHTNESS in kwargs:
self._attr_brightness = int(kwargs[ATTR_BRIGHTNESS] / 255.0 * 100.0)
payload[ATTR_BRIGHTNESS] = self._attr_brightness
_LOGGER.debug(f"Other device: Setting brightness: {self._attr_brightness}")

if ATTR_COLOR_TEMP in kwargs:
self._attr_color_temp = kwargs[ATTR_COLOR_TEMP]
payload[ATTR_COLOR_TEMP] = self._attr_color_temp
_LOGGER.debug(f"Other device: Setting color temp: {self._attr_color_temp}")

if ATTR_COLOR_TEMP_KELVIN in kwargs:
self._attr_color_temp_kelvin = kwargs[ATTR_COLOR_TEMP_KELVIN]
payload[ATTR_COLOR_TEMP] = color_util.color_temperature_kelvin_to_mired(self._attr_color_temp_kelvin)

if ATTR_RGB_COLOR in kwargs:
self._attr_rgb_color = kwargs[ATTR_RGB_COLOR]
x_val, y_val = color_util.color_RGB_to_xy(*kwargs[ATTR_RGB_COLOR])
payload[ATTR_RGB_COLOR] = int(x_val * 65535) * (2 ** 16) + int(y_val * 65535)
_LOGGER.debug(f"Other device: Using XY color space: {payload[ATTR_RGB_COLOR]}")

if ATTR_HS_COLOR in kwargs:
self._attr_hs_color = kwargs[ATTR_HS_COLOR]
_LOGGER.debug(f"Other device: Setting HS color: {self._attr_hs_color}")

if self._attr_hs_color:
payload[ATTR_HS_COLOR] = color_util.color_temperature_kelvin_to_mired(self._attr_color_temp_kelvin)
rgb = color_util.color_hs_to_RGB(*self._attr_hs_color)
rgba = (self._attr_brightness,) + rgb
if isinstance(self._attr_brightness, int):
rgbhex = binascii.hexlify(
struct.pack("BBBB", *rgba)).decode("ASCII")
rgbhex = int(rgbhex, 16)
if self.device['type'] == 'zigbee':
payload[ATTR_HS_COLOR] = rgbhex
else:
payload[ATTR_HS_COLOR] = rgbhex
_LOGGER.debug(f"Other device: HS color converted to HEX: {rgbhex}")

if not payload:
payload[self._attr] = 1

try:
if self.gateway.send(self.device, payload):
self._state = True
self.schedule_update_ha_state()
except:
_LOGGER.warn(f"send payload {payload} to gateway failed")
if is_m1s_gateway:
# For M1S gateway, send brightness=100 to turn on with default brightness
payload['brightness'] = 100
_LOGGER.debug(f"M1S Gateway: No specific attributes, setting default brightness payload: {payload}")
else:
payload[self._attr] = 1
_LOGGER.debug(f"Other device: No specific attributes, setting default payload: {payload}")

_LOGGER.debug(f"Final payload to send: {payload}")
if self.gateway.send(self.device, payload):
self._state = True
self.schedule_update_ha_state()
_LOGGER.debug(f"Successfully sent payload to gateway")
else:
_LOGGER.error(f"Failed to send payload to gateway")

def turn_off(self, **kwargs):
"""Turn the light off."""
Expand Down