Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support light transition on LED strips #404

Open
lupusbytes opened this issue Mar 1, 2024 · 7 comments
Open

Support light transition on LED strips #404

lupusbytes opened this issue Mar 1, 2024 · 7 comments
Labels
bug Something isn't working enhancement New feature or request

Comments

@lupusbytes
Copy link

I am an owner of two msl320cp LED strips.
In the Meross app, when I change the brightness or color, the lights smoothly transition from one state to the other.
Using meross_lan, it instantly switches from one state to the other.
Automations cannot take advantage of the transitions, like:

  - service: light.turn_on
    data:
      entity_id: light.bedroom_1
      brightness: 75
      transition: 300

Looking around for clues in the code, I found lines like this in the emulator_traces.
2021/10/12 - 19:19:31 http GETACK Appliance.System.All {"all": {"system": {"hardware": {"type": "msl320cpr", "subType": "un", "version": "4.0.0", "chipType": "rtl8710cm", ... "light": {"capacity": 6, "channel": 0, "rgb": 255, "temperature": 46, "luminance": 100, "transform": 0}}}}

Is it possible that the transform property is the secret to smooth transitions?

The device is certainly capable of creating smooth transitions, with the Meross app.
If you use the effects tab, and choose Party, it goes crazy with flashing all the colors of the rainbow, and you can even set the Flash speed and mode.
This is another thing that is not supported by meross_lan, but not a big deal for me; You can see the list of effects in homeassistant, but not trigger them...

@krahabb krahabb added bug Something isn't working enhancement New feature or request labels Mar 2, 2024
@krahabb
Copy link
Owner

krahabb commented Mar 2, 2024

Hello @lupusbytes,
Yep, you're pointing right into one of the 'long standing misunderstands' in my knowledge of lights. Namely 'effects' and 'transitions'.
I've seen too that "transform" key in some lights traces here and there but I hadn't any chance to understand its behavior so, this might be the time to dig into it.
You might use the meross_lan.request service to manually craft and send your own commands in order to investigate this.
In the end, you might even use this feature in your automations once you understand which payload to send but, nevertheless, we could insert support for transitions in core meross_lan too.

In order to use the service, beside the obvious fields (following the developer service panel UI) you'll have to setup a meaningful payload. To start off you might query the actual light state with:

service: meross_lan.request
data:
  protocol: auto
  method: GET
  namespace: Appliance.Control.Light
  payload: "{}"
  device_id: YOUR_DEVICE_UUID

then, in order to send a command just switch the method to SET and put a valid/experimental payload in it. In yaml the payload would need to be escaped (for the ") a bit so it might be easier to use the visual UI to fill that service field. The paylaod for a Light command is usually a kind of

{ "light": {
      "capacity": 6,
      "channel": 0,
      "rgb": 16739924,
      "temperature": 71,
      "luminance": 81
      }
}

Keep in mind, some lights automatically turn on when receiving any Light payload some other not (they need a Togglex command), some other need an "onoff" key (with value set to 0/1) in the payload.
The "capacity" field sets the mode (rgb, temperature, effect) and is a bit field value - you'll be able to lookup the constants in code)

As for effects, that's a mess since meross_lan tries to implement sending the command for setting an effect but there were reports it wasn't working (#232) and I had never received any hint or help to try fix it.
Basically, in my understanding, the selection of an effect is carried by sending the "effect" key in the payload together with an index into the list of effects available (which meross_lan queries and is able to show in the HA effects list) but this is failing. It maybe worked on legacy firmwares but at this point I'm not sure)
The fact is I've also seen traces where the "effect" key was carrying a string instead of an integer index (that's why there's some euristics in code trying to overcome this when sending the command).
There's also the fact that, when an effect is in place, the "capacity" key carries the bit-field value 8 (together with some other flags maybe) and so it might be needed to also set this when trying to setup an effect.

I hope you could experiment this a bit so that we can (finally) fix this long standing issue and maybe add support for transitions too ;)

@lupusbytes
Copy link
Author

lupusbytes commented Apr 21, 2024

Dear @krahabb
Sorry for the late reply. I have been toying a bit with the service: meross_lan.request like you suggested.
My finding is that the transform property does nothing at all. But it is possible that I'm just using it in a wrong way... Perhaps it's used in effects?

After trying to call the API in many different ways I think that I can conclude that it doesn't support transitions in the traditional sense. The effects and "transition" that I am seeing in the app is simply a smooth brightness transition that takes around 1 second and cannot be configured. I uploaded a video of the Meross "Party" Effect for you to see: https://www.youtube.com/shorts/IZyXmvV1zys
They seem to just crank the brightness up, then down, then change color and repeat.
I can replicate this with behaviour with meross_lan.request

alias: Meross Test
sequence:
  - service: meross_lan.request
    data:
      method: SET
      namespace: Appliance.Control.Light
      payload: |-
        {
          "light": {
            "capacity": 4,
            "onoff": 1,
            "channel": 0,
            "luminance": 10
          }
        }
      host: 172.16.0.21
  - delay:
      hours: 0
      minutes: 0
      seconds: 3
      milliseconds: 0
  - service: meross_lan.request
    data:
      method: SET
      namespace: Appliance.Control.Light
      payload: |-
        {
          "light": {
            "capacity": 4,
            "onoff": 1,
            "channel": 0,
            "luminance": 100
          }
        }
      host: 172.16.0.21

This will smoothly turn down the brightness over 1 second, wait 3 seconds and then smoothly turn it up again.
The script is very simple. I am wondering what meross_lan is doing different when it's changing the brightness that makes it instant change brightness.
I have been looking into your source code for clues again for why this is, but I don't understand all the bitwise operations related to capacity.

I found another repo that was easier for me to read: https://github.com/bwp91/homebridge-meross/blob/latest/lib/device/light-rgb.js
Here they (also?) keep track of the current light mode and send the correct capacity in payload when they change a value

CAPACITIES
1 - rgb to rgb
2 - cct to cct
4 - brightness
5 - cct to rgb
6 - rgb to cct

Could it be related to this?

While this feature may not enable us to use the normal HomeAssistant transition function, implementing the smooth change would still allow us to script much smoother (longer) transitions ourselves, so that not every brightness step is as instant and noticeable.

EDIT: The effects tab actually lets you define the flash speed on a slider, which can be turned very far down to make it extremely slow... 🤔

@lupusbytes
Copy link
Author

I have been trying for hours to use meross_lan.request to set the luminance in a loop.

The following script works on my meross LED strips, but the brightness steps are instant and disturbing and this is why I started researching this thing 😄

alias: Dynamic Transition
action:
  - repeat:
      count: "{{ (end_brightness - start_brightness) / step }}"
      sequence:
        - service: light.turn_on
          target:
            entity_id: "{{ light_entities }}"
          data_template:
            brightness_pct: "{{ start_brightness + repeat.index * step }}"
        - delay:
            seconds: >-
              {{ transition_time / ((end_brightness - start_brightness) / step)
              }}
variables:
  start_brightness: 1
  end_brightness: 70
  step: 2
  transition_time: 60
  light_entities:
    - light.smart_light_2104062991838990848548e1e969c8ca

I tried so many variations, but I can't make this work with meross_lan.request
I can't figure out how to use variables inside the json payload:

alias: Dynamic Transition Meross
action:
  - repeat:
      count: "{{ (end_brightness - start_brightness) / step }}"
      sequence:
        - service: meross_lan.request
          data:
            method: SET
            namespace: Appliance.Control.Light
            payload: |-
              {
                "light": {
                  "capacity": 4,
                  "onoff": 1,
                  "channel": 0,
                  "luminance": "{{ start_brightness + repeat.index * step }}"
                }
              }
            host: 172.16.0.21
        - delay:
            seconds: >-
              {{ transition_time / ((end_brightness - start_brightness) / step)
              }}
variables:
  start_brightness: 1
  end_brightness: 70
  step: 2
  transition_time: 60

@krahabb
Copy link
Owner

krahabb commented May 9, 2024

Hello @lupusbytes,
Sorry for not being active tracking this but I was trying to finish off support for msl320 effects and I've finally released a preview

Meross lights are driving me mad (still) since, even if they (different models/fw) all expose the kind-of-same interface, their behavior is so erratic...

I've bought an msl320 Pro and, beside being finally able to implement effect selection I'm now more concerned than before (more on this later)

Back to your findings:
In my knowledge, the capacity represents the 'working mode' of the light. It is composed of those bit-fields (1: rgb, 2: temperature, 4: luminance) you've found and it usually works like this:

  • if light only supports intensity (luminance) then it only supports capacity=4 and the luminance key value (from 1 to 100 usually - but I guess I've seen some tracelogs reporting 0 meaning the light was off...don't know really - my lights only report/accept 1-100)
  • if light supports temperature then the capacity uses the bit value '2' to carry the mode. You usually also control luminance and that's why you issue commands with capacity=6 (4+2) to carry both 'temperature' (ranging from 1 to 100) and 'luminance'
  • if light supports rgb then the capacity uses the bit value '1' to carry the mode. Again you'll use 5 (4+1) to carry the 'rgb' and 'luminance' values
  • if an effect is in place then things start to get weird since the 'capacity' now carries the bit '8' (which is the flag stating the light is reproducing an effect) and the payload will usually carry an 'effect' key with the index of the current effect (the light has its own memory of effects). The other bit fields in this scenario are just 'dirty' which means they have the value they had before activating the effect. Also the 'rgb', 'temperature' and 'luminance' fields are still the (previous) static light settings and not what the current effect is proposing actually (at least this is what happens on my msl320cp). Going deeper on this, meross_lan now allows to control the luminance of the current effect (when it is active) by reprogramming the definition of the effect itself and not using the 'luminance' key of the 'light' payload. This is the same as editing the effect in the App.

I've tried issuing commands with capacity=1 to just control the rgb value but in the end it is like issuing a command with capacity=5 and replying the current luminance (which is the approach used in meross_lan - I always (re)send the current 'cached' luminance if it is not changed through an HA UI request

If I send capacity=7 (which should mean both rgb and cct active at the same time) the light just flashes-out something and then ignores the command actually rejecting this combo as a valid working mode.

I'll try experiment a bit more on these capacity mess to see if there are some combo/transitions available but I'm not so confident.

As for why your script using meross_lan.request doesn't work, at first sight I see those double-quotes (") around the template calculation for the "luminance" key value which might be producing a string instead of an int for the json value to send and this is likely rejected by the device. I'm not sure about the syntax for the script but you should ensure you're sending an int (correctly bounded though) for the "luminance"...again, not sure if this is the issue

@krahabb
Copy link
Owner

krahabb commented May 9, 2024

I think I've found while you can't invoke the service with the template...fact is, the payload is accepted as a string parameter in the service interface and then converted to json inside the service call...
This leads to the template not being rendered by the HA template engine but just being passed in as a simple string

@krahabb
Copy link
Owner

krahabb commented May 9, 2024

I was able to finally make it work...
The script I've used is:

alias: New Script (Duplicate)
sequence:
  - repeat:
      count: "{{ (end_brightness - start_brightness) / step }}"
      sequence:
        - service: meross_lan.request
          data:
            method: SET
            namespace: Appliance.Control.Light
            payload: 
              {
                "light": {
                  "capacity": 4,
                  "channel": 0,
                  "luminance": "{{ start_brightness + repeat.index * step }}"
                }
              }
              
            device_id: 2104293378215590850648e1e96ec013
        - delay:
            seconds: >-
              {{ transition_time / ((end_brightness - start_brightness) / step)
              }}
variables:
  start_brightness: 10
  end_brightness: 50
  step: 1
  transition_time: 5
mode: restart

but it currently doesn't work on the published release since, as I've stated before, the current/old implementation always tries to interpret the passed in payload parameter as a string since in my experiments/tests the UI always passed in a string (even if it contained a json structured value)
Now I've fixed it to be prepared for 'real' dict parameters when the service invocation passes in data this way so I'm going to quickly push the fix (if you like you could directly pull the commit

@lupusbytes
Copy link
Author

This sounds very exciting.
Did you also notice a difference of how the light changes more smoothly, like I did, when changing luminance with a crafted meross_lan.request compared to "regular" methods?

I'm still kind of new to Home Assistant. I'm running it in a docker container and not sure how to use git to pull integration updates, instead of the normal release channels, but I am very excited to try the feature out!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants