gurumitts / pylutron-caseta

Apache License 2.0
153 stars 97 forks source link

Add Lumaris Tape Light and Ketra color support #154

Closed eclair4151 closed 9 months ago

eclair4151 commented 10 months ago

Here's my initial attempt at adding ketra color support. Includes unit tests, and supports zone status for retrieving the color of the lamp. Let me know if you think something should be changed. Tested and works great on my Ketra bulbs, but can use further confirmation. See issue #153

Corresponding Home Assistant feature branch: https://github.com/eclair4151/home-assistant-core/tree/ketra_lumaris_color_support

Once you checkout the HA branch, youll need to manually install the new version of pylutron-caseta as well. Open a terminal in your Home Assistant Core, or the terminal tab in vscode docker, and run

pip uninstall pylutron-caseta
pip install git+https://github.com/eclair4151/pylutron-caseta.git@dev

Video of this working in Home Assistant https://photos.app.goo.gl/VAziwYKXhbo8fo4z7

eclair4151 commented 10 months ago

Usage:

ketra_device_id = "XYZ"

color_value = WarmCoolColorValue(2700)
# color_value = FullColorValue(RGBColorParameter(r=0, g=250, b=10))
# color_value = FullColorValue(HueSaturationColorParameter(hue=50, saturation=20))
# color_value = WarmDimmingColorValue(True)
# color_value = VibrancyColorValue(50)

await bridge.set_value(ketra_device_id,  color_value=color_value)
# or with value in the same command
await bridge.set_value(ketra_device_id,  value=50, color_value=color_value)
eclair4151 commented 10 months ago

@mdonoughe @cbw @gurumitts Any initial thoughts on the direction of this PR? Let me know if there's anything obvious I'm missing or needs work. Much appreciated :)

eclair4151 commented 10 months ago

Merged in #155 since there was overlap in the ketra and lumaris color support.

eclair4151 commented 10 months ago

@RBaragona @uberjay My initial version of the home assistant branch is ready to test. I only have ketra bulbs, so let me know if the lumaris works correctly. it should only show the warm cool scale on the device, and show the correct kelvin limits.

https://github.com/eclair4151/home-assistant-core/tree/ketra_lumaris_color_support

Once you checkout this branch, youll need to manually install the new version of pylutron-caseta as well. Open a terminal in your Home Assistant Core, or the terminal tab in vscode docker, and run

pip uninstall pylutron-caseta    
pip install git+https://github.com/eclair4151/pylutron-caseta.git@dev 
eclair4151 commented 10 months ago

HA also supports setting a color changing bulb to "white mode" which I have mapped to warm dim. So you should be able to click here to go into warm dim mode.

Screenshot 2023-12-23 at 11 03 31 PM
RBaragona commented 10 months ago

I should be able to test this out later today. I'll let you know how it goes.

RBaragona commented 10 months ago

My ketra fixtures are working well, but I'm having issues with the Lumaris fixtures. I'll try and pull some logs and look at the code.

eclair4151 commented 10 months ago

Ah hmm, what happens?

RBaragona commented 10 months ago

I enabled diagnostics and was able to capture the following when controlling a Lumaris tape:

2023-12-25 07:46:03.165 DEBUG (MainThread) [pylutron_caseta.leap] sending b'{"CommuniqueType": "CreateRequest", "Header": {"ClientTag": "1d5325e1-588a-4ba5-a931-4602d0639229", "Url": "/zone/9172/commandprocessor"}, "Body": {"Command": {"CommandType": "GoToWhiteTuningLevel", "WhiteTuningLevelParameters": {"ColorTuningStatus": {"WhiteTuningLevel": {"Kelvin": 2878}}}}}}'
2023-12-25 07:46:03.193 DEBUG (MainThread) [pylutron_caseta.leap] received: {'CommuniqueType': 'ExceptionResponse', 'Header': {'MessageBodyType': 'ExceptionDetail', 'StatusCode': '400 BadRequest', 'Url': '/zone/9172/commandprocessor'}, 'Body': {'Message': 'The body is missing a required property: Level or WhiteTuningLevel'}}
2023-12-25 07:46:03.193 ERROR (MainThread) [homeassistant.components.websocket_api.http.connection] [140085640918848] 400 BadRequest
Traceback (most recent call last):
  File "/workspaces/Home-Assistant-Core/homeassistant/components/websocket_api/commands.py", line 238, in handle_call_service
    response = await hass.services.async_call(
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspaces/Home-Assistant-Core/homeassistant/core.py", line 2133, in async_call
    response_data = await coro
                    ^^^^^^^^^^
  File "/workspaces/Home-Assistant-Core/homeassistant/core.py", line 2170, in _execute_service
    return await target(service_call)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspaces/Home-Assistant-Core/homeassistant/helpers/entity_component.py", line 272, in handle_service
    return await service.entity_service_call(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspaces/Home-Assistant-Core/homeassistant/helpers/service.py", line 882, in entity_service_call
    single_response = await _handle_entity_call(
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspaces/Home-Assistant-Core/homeassistant/helpers/service.py", line 952, in _handle_entity_call
    result = await task
             ^^^^^^^^^^
  File "/workspaces/Home-Assistant-Core/homeassistant/components/light/__init__.py", line 624, in async_handle_light_on_service
    await light.async_turn_on(**filter_turn_on_params(light, params))
  File "/workspaces/Home-Assistant-Core/homeassistant/components/lutron_caseta/light.py", line 121, in async_turn_on
    await self._set_brightness(brightness, color, **kwargs)
  File "/workspaces/Home-Assistant-Core/homeassistant/components/lutron_caseta/light.py", line 96, in _set_brightness
    await self._smartbridge.set_value(
  File "/home/vscode/.local/lib/python3.11/site-packages/pylutron_caseta/smartbridge.py", line 343, in set_value
    await self._request(
  File "/home/vscode/.local/lib/python3.11/site-packages/pylutron_caseta/smartbridge.py", line 259, in _request
    raise BridgeResponseError(response)
pylutron_caseta.BridgeResponseError: 400 BadRequest

Here's a command that i can see does work: {"Url":"/zone/8470/commandprocessor"},"Body":{"Command":{"CommandType":"GoToWhiteTuningLevel","WhiteTuningLevelParameters":{"Level":25,"WhiteTuningLevel":{"Kelvin":3000}}}}

And here's the debug log of what is sent: {"Url":"/zone/9172/commandprocessor"},"Body":{"Command":{"CommandType":"GoToWhiteTuningLevel","WhiteTuningLevelParameters":{"ColorTuningStatus":{"WhiteTuningLevel":{"Kelvin":7133}}}}}}'

my assumption is that the color tuning status portion shouldn't be present for the Lumaris tape, but that would need to be verified as the actual issue

I can see the correct range for white tuning shows up:

"8470": {
          "device_id": "8470",
          "current_state": 4,
          "fan_speed": null,
          "zone": "8470",
          "name": "Bedroom_Headboard",
          "button_groups": null,
          "type": "WhiteTune",
          "model": null,
          "serial": null,
          "area": "8458",
          "device_name": "Headboard",
          "white_tuning_range": {
            "Min": 1800,
            "Max": 3000
          },
          "tilt": null,
          "color": {
            "__type": "<class 'pylutron_caseta.color_value.WarmCoolColorValue'>",
            "repr": "<pylutron_caseta.color_value.WarmCoolColorValue object at 0x7f684023bd50>"
          }
        },

and

"11964": {
          "device_id": "11964",
          "current_state": 0,
          "fan_speed": null,
          "zone": "11964",
          "name": "Office_Zone 01",
          "button_groups": null,
          "type": "WhiteTune",
          "model": null,
          "serial": null,
          "area": "650",
          "device_name": "Zone 01",
          "white_tuning_range": {
            "Min": 2500,
            "Max": 5000
          },
          "tilt": null
        },
eclair4151 commented 10 months ago

Ah yea you are correct, I didn't realize that part wasn't needed for white tune zones, i'm editing it now, should be an easy change

eclair4151 commented 10 months ago

Alright, after some digging around in the decompiled APK, warm dim also works completely differently on the white tuning zones, so i've updated the code to break out warm dim into its own function.

I was able to add the lumaris light strips in my designer program to my system without activating them, and that let me test with them over leap and confirmed it should be working now on my end, so let me know how it works for you now

youll need to update the HA branch, and reinstall the pip lib.

pip uninstall pylutron-caseta    
pip install git+https://github.com/eclair4151/pylutron-caseta.git@dev 
RBaragona commented 10 months ago

This works well for me now!! My only comment is about the HA core implementation (let me know if I should add this detail elsewhere), where the white

  1. temperature limits don't match the light
  2. default favorite temperatures are outside the light's capability.

Should this #155 be closed and wrap it all into #153

eclair4151 commented 10 months ago

Hmm thats odd, could you clarify what you mean and add some screenshots? I had tested it out, and the kelvin limits were being pulled in for me as expected for the light strips. Im fine with #155 being closed since I merged your branch into mine, so all changes are included. But curious to hear feedback from one of the maintainers of the repo.

RBaragona commented 10 months ago

Here's an example from one of my loads:

"8470": {
          "device_id": "8470",
          "current_state": 4,
          "fan_speed": null,
          "zone": "8470",
          "name": "Bedroom_Headboard",
          "button_groups": null,
          "type": "WhiteTune",
          "model": null,
          "serial": null,
          "area": "8458",
          "device_name": "Headboard",
          "white_tuning_range": {
            "Min": 1800,
            "Max": 3000
          },
          "tilt": null,
          "color": {
            "__type": "<class 'pylutron_caseta.color_value.WarmCoolColorValue'>",
            "repr": "<pylutron_caseta.color_value.WarmCoolColorValue object at 0x7f684023bd50>"
          }
        },

From above you can see the min/max range is 1800 to 3000k, but: image image

And the default favorite color temperatures are pulled from the debug logs: image

2023-12-29 03:57:44.582 DEBUG (MainThread) [pylutron_caseta.leap] sending b'{"CommuniqueType": "CreateRequest", "Header": {"ClientTag": "10bb5cbb-7672-4b4f-ad2b-af9af58df9b1", "Url": "/zone/8470/commandprocessor"}, "Body": {"Command": {"CommandType": "GoToWhiteTuningLevel", "WhiteTuningLevelParameters": {"WhiteTuningLevel": {"Kelvin": 1400}}}}}'
2023-12-29 03:57:44.618 DEBUG (MainThread) [pylutron_caseta.leap] received: {'CommuniqueType': 'CreateResponse', 'Header': {'MessageBodyType': 'OneZoneStatus', 'StatusCode': '201 Created', 'Url': '/zone/8470/commandprocessor'}, 'Body': {'ZoneStatus': {'href': '/zone/8470/status', 'ColorTuningStatus': {'WhiteTuningLevel': {'Kelvin': 1400}}, 'Zone': {'href': '/zone/8470'}, 'StatusAccuracy': 'Good', 'Availability': 'Available'}}}

2023-12-29 03:57:46.159 DEBUG (MainThread) [pylutron_caseta.leap] sending b'{"CommuniqueType": "CreateRequest", "Header": {"ClientTag": "64fdf5aa-c0f0-4664-a35d-79168385730b", "Url": "/zone/8470/commandprocessor"}, "Body": {"Command": {"CommandType": "GoToWhiteTuningLevel", "WhiteTuningLevelParameters": {"WhiteTuningLevel": {"Kelvin": 4267}}}}}'
2023-12-29 03:57:46.193 DEBUG (MainThread) [pylutron_caseta.leap] received: {'CommuniqueType': 'CreateResponse', 'Header': {'MessageBodyType': 'OneZoneStatus', 'StatusCode': '201 Created', 'Url': '/zone/8470/commandprocessor'}, 'Body': {'ZoneStatus': {'href': '/zone/8470/status', 'ColorTuningStatus': {'WhiteTuningLevel': {'Kelvin': 4267}}, 'Zone': {'href': '/zone/8470'}, 'StatusAccuracy': 'Good', 'Availability': 'Available'}}}
2023-12-29 03:57:46.333 DEBUG (MainThread) [pylutron_caseta.leap] received for subscription 42fd393d-356b-4d0d-a020-16ba08c27ca0: {'CommuniqueType': 'ReadResponse', 'Header': {'MessageBodyType': 'MultipleZoneStatus', 'StatusCode': '200 OK', 'Url': '/zone/status'}, 'Body': {'ZoneStatuses': [{'href': '/zone/8470/status', 'ColorTuningStatus': {'XYTuningLevel': {'X': 0.4365, 'Y': 0.4041}, 'HSVTuningLevel': {'Hue': 41, 'Saturation': 39}, 'WhiteTuningLevel': {'Kelvin': 3000}}, 'Zone': {'href': '/zone/8470'}, 'StatusAccuracy': 'Good'}]}}
2023-12-29 03:57:46.333 DEBUG (MainThread) [pylutron_caseta.smartbridge] Handling multi zone status: Response(Header=ResponseHeader(StatusCode=ResponseStatus(200, 'OK'), Url='/zone/status', MessageBodyType='MultipleZoneStatus'), CommuniqueType='ReadResponse', Body={'ZoneStatuses': [{'href': '/zone/8470/status', 'ColorTuningStatus': {'XYTuningLevel': {'X': 0.4365, 'Y': 0.4041}, 'HSVTuningLevel': {'Hue': 41, 'Saturation': 39}, 'WhiteTuningLevel': {'Kelvin': 3000}}, 'Zone': {'href': '/zone/8470'}, 'StatusAccuracy': 'Good'}]})
2023-12-29 03:57:46.333 DEBUG (MainThread) [pylutron_caseta.smartbridge] zone=8470 level=-1
2023-12-29 03:57:47.503 DEBUG (MainThread) [pylutron_caseta.leap] sending b'{"CommuniqueType": "CreateRequest", "Header": {"ClientTag": "1289fc28-beaa-46c4-bcc6-73289b0d0fd8", "Url": "/zone/8470/commandprocessor"}, "Body": {"Command": {"CommandType": "GoToWhiteTuningLevel", "WhiteTuningLevelParameters": {"WhiteTuningLevel": {"Kelvin": 7133}}}}}'
2023-12-29 03:57:47.538 DEBUG (MainThread) [pylutron_caseta.leap] received: {'CommuniqueType': 'CreateResponse', 'Header': {'MessageBodyType': 'OneZoneStatus', 'StatusCode': '201 Created', 'Url': '/zone/8470/commandprocessor'}, 'Body': {'ZoneStatus': {'href': '/zone/8470/status', 'ColorTuningStatus': {'WhiteTuningLevel': {'Kelvin': 7133}}, 'Zone': {'href': '/zone/8470'}, 'StatusAccuracy': 'Good', 'Availability': 'Available'}}}

2023-12-29 03:57:48.907 DEBUG (MainThread) [pylutron_caseta.leap] sending b'{"CommuniqueType": "CreateRequest", "Header": {"ClientTag": "829ed32a-172f-4f50-b712-bbccd1bf7260", "Url": "/zone/8470/commandprocessor"}, "Body": {"Command": {"CommandType": "GoToWhiteTuningLevel", "WhiteTuningLevelParameters": {"WhiteTuningLevel": {"Kelvin": 10000}}}}}'
2023-12-29 03:57:48.942 DEBUG (MainThread) [pylutron_caseta.leap] received: {'CommuniqueType': 'CreateResponse', 'Header': {'MessageBodyType': 'OneZoneStatus', 'StatusCode': '201 Created', 'Url': '/zone/8470/commandprocessor'}, 'Body': {'ZoneStatus': {'href': '/zone/8470/status', 'ColorTuningStatus': {'WhiteTuningLevel': {'Kelvin': 10000}}, 'Zone': {'href': '/zone/8470'}, 'StatusAccuracy': 'Good', 'Availability': 'Available'}}}
eclair4151 commented 10 months ago

Hmm that is odd. the favorties are just picked from the range, so that will fix itself, just curious its not pulling the correct range. can you confirm the contents of homeassistant/components/lutron_caseta/light.py

I want to make sure everything looks correct.

How are you setup? do you have docker setup in vscode? you should also be able to press f5 to launch in debug mode, and set a break point on line 186 of that file, and see what self._device contains

RBaragona commented 10 months ago

You're right, I was on an older Home Assistant commit. It's all working well now!

eclair4151 commented 10 months ago

Awesome! @mdonoughe let us know next steps and if you have any feedback. I know it's the new years so no rush!

uberjay commented 9 months ago

Hey there, finally getting to trying this out -- it seems to work with my simple testing. Only one string of Lumaris, but it shows up and is controllable. It's got the correct temperature range reported. Is it possible to toggle the warm-dim setting from a LEAP command? If so, it'd be nice to expose that somehow. (of course, figuring out the design for that shouldn't hold up this change!)

eclair4151 commented 9 months ago

@uberjay Warm Dim is already supported :) you can turn it on by clicking the W

https://github.com/gurumitts/pylutron-caseta/pull/154#issuecomment-1868427905

uberjay commented 9 months ago

Ah! Ok, I wondered but it didn't seem to do what I expected... but, trying it again, I see it does! Essentially, once enabled, the color temperature will be auto-scaled. If the temperature is set manually, it'll stay that way.

It appears to function that way, but should the device state color_mode be reported as white in this case? it seems to always be reported as color_temp, regardless of if warm-dim is enabled or not.

eclair4151 commented 9 months ago

i did notice that too, but I think more logic would be required to be able to tell at any point if warm dim is enabled. From my testing, the current warm dim enabled setting is not returned when the level is changed, and a color is reported as if it were set to kelvin. So it would take a bunch more work to try and keep the warm-dim setting accurate and in memory, especially when you consider multiple devices. If anyone has any simple ideas to keep the warm dim setting in sync, im down to try it out though.

uberjay commented 9 months ago

That all makes sense and I suppose will be limited by what's returned by the protocol. I don't know much about the implementation, here, so correct me if I'm wrong, but: when you talk about "the current warm dim enabled setting is not returned when the level is changed", are you talking about the LEAP exchange, or something higher-level in home-assistant? It must be the protocol exchange, but I guess some state must be cached (at least in memory) inside home-assistant?

It looks like when switching the fixture to warm-dim mode its current color temperature isn't returned, but the brightness is? Do you have any pointers on LEAP protocol documentation (what's been discovered so far, at least...)?

event_type: state_changed
data:
  entity_id: light.3rd_floor_hallway_baseboard
  old_state:
    entity_id: light.3rd_floor_hallway_baseboard
    state: "on"
    attributes:
      min_color_temp_kelvin: 1800
      max_color_temp_kelvin: 3000
      min_mireds: 333
      max_mireds: 555
      supported_color_modes:
        - brightness
        - color_temp
        - white
      color_mode: color_temp
      brightness: 107
      color_temp_kelvin: 2636
      color_temp: 379
      hs_color:
        - 28.539
        - 67.808
      rgb_color:
        - 255
        - 164
        - 82
      xy_color:
        - 0.532
        - 0.388
      device_id: "7081"
      zone_id: "7081"
      friendly_name: 3rd Floor Hallway Baseboard
      supported_features: 32
    last_changed: "2024-01-02T18:59:47.592801+00:00"
    last_updated: "2024-01-02T18:59:50.795147+00:00"
    context:
      id: 01HK5RQQQR5WW5HPAAVHB2NTEJ
      parent_id: null
      user_id: 15b3776d7a4046be8a8b2c1229641f88
  new_state:
    entity_id: light.3rd_floor_hallway_baseboard
    state: "on"
    attributes:
      min_color_temp_kelvin: 1800
      max_color_temp_kelvin: 3000
      min_mireds: 333
      max_mireds: 555
      supported_color_modes:
        - brightness
        - color_temp
        - white
      color_mode: white
      brightness: 107
      color_temp_kelvin: null
      color_temp: null
      hs_color: null
      rgb_color: null
      xy_color: null
      device_id: "7081"
      zone_id: "7081"
      friendly_name: 3rd Floor Hallway Baseboard
      supported_features: 32
    last_changed: "2024-01-02T18:59:47.592801+00:00"
    last_updated: "2024-01-02T18:59:53.218391+00:00"
    context:
      id: 01HK5RQT4BECMR94XGRTRBBFTQ
      parent_id: null
      user_id: 15b3776d7a4046be8a8b2c1229641f88
origin: LOCAL
time_fired: "2024-01-02T18:59:53.218391+00:00"
context:
  id: 01HK5RQT4BECMR94XGRTRBBFTQ
  parent_id: null
  user_id: 15b3776d7a4046be8a8b2c1229641f88
uberjay commented 9 months ago

You may already know all this (and likely do!), but when toggling warm-dim using the Lutron app, these events occur:

Turning warm-dim on:

2024-01-02 19:14:11.144 DEBUG (MainThread) [pylutron_caseta.smartbridge] Handling multi zone status: Response(Header=ResponseHeader(StatusCode=ResponseStatus(200, 'OK'), Url='/zone/status', MessageBodyType='MultipleZoneStatus'), CommuniqueType='ReadResponse', Body={'ZoneStatuses': [{'href': '/zone/7081/status', 'Level': 16, 'ColorTuningStatus': {'XYTuningLevel': {'X': 0.5258, 'Y': 0.4134}, 'HSVTuningLevel': {'Hue': 36, 'Saturation': 69}, 'WhiteTuningLevel': {'Kelvin': 2009}, 'CurveDimming': {'Curve': {'href': '/curve/1'}}}, 'Zone': {'href': '/zone/7081'}, 'StatusAccuracy': 'Good'}]})

When turning it off:

2024-01-02 19:17:15.218 DEBUG (MainThread) [pylutron_caseta.smartbridge] Handling multi zone status: Response(Header=ResponseHeader(StatusCode=ResponseStatus(200, 'OK'), Url='/zone/status', MessageBodyType='MultipleZoneStatus'), CommuniqueType='ReadResponse', Body={'ZoneStatuses': [{'href': '/zone/7081/status', 'ColorTuningStatus': {'CurveDimming': {}}, 'Zone': {'href': '/zone/7081'}, 'StatusAccuracy': 'Good'}]})

I wonder if there are any other possible CurveDimming options? Probably not for us lowly RA3 users... 🤪 .

uberjay commented 9 months ago

I think everything is working as well as it ought to, given the information available via the LEAP interface.

Thank you for making this happen! I'm excited to have this available in Homeassistant, eventually. :)

eclair4151 commented 9 months ago

Hey, Cool, sorry I didnt get too much time yesterday, but I want to take another day to look into this and see if there is a better way to do it by checking all zone status updates for Curve Dimming, so the plugin can correctly report the current bulb mode. Ill let you know my findings!

eclair4151 commented 9 months ago

Sorry for the delay. I think this would work, it just requires breaking out Curve Dimming into its own dict property of the device to track the state of. Will let you know once its ready to test.

eclair4151 commented 9 months ago

Alright both branches have been updated, give it a try. Seems to work fine for me, I am able to turn on/off warm dimming from the lutron app, and see it update in real time on HA.

once you update your home assistant branch run this to re pull the python changes

pip uninstall pylutron-caseta    
pip install git+https://github.com/eclair4151/pylutron-caseta.git@dev 

@mdonoughe i'd love some feedback on the direction we are going and any thoughts you might have. Thanks! (I still need to update the unit tests, but wanted to get your thoughts first)

uberjay commented 9 months ago

The behavior feels a lot nicer and responsive now -- it tracks the warm-dim state changes very quickly. Thanks! I'm excited :)

iCSpotRun commented 9 months ago

Curious the reason for removing Ketra from RA3.

eclair4151 commented 9 months ago

Curious the reason for removing Ketra from RA3.

I was the one that added it accidentally in the tests. The Homeworks system in the tests already have ketra bulbs in the tests, and Ketra is not available to Ra3 Systems as far as I know. If you look at the overall change log, the Ra3 system in the tests was left alone.

iCSpotRun commented 9 months ago

Curious the reason for removing Ketra from RA3.

I was the one that added it accidentally in the tests. The Homeworks system in the tests already have ketra bulbs in the tests, and Ketra is not available to Ra3 Systems as far as I know.

Ah gotcha. I assumed that this PR somehow extended Ketra support into RA3. Thanks for the clarification.

eclair4151 commented 9 months ago

@mdonoughe I think it should be good for a rereview whenever you get some time :)

mdonoughe commented 9 months ago

Manually merged in df6fe49