-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
[shelly] Add support for Shelly Pro RGBWW PM #19227
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
base: main
Are you sure you want to change the base?
Changes from 27 commits
325de23
34f61eb
0952c67
2cdd822
dc73d59
5a9ffe4
0ea2d3a
2679637
aae8dcc
c85c811
52eed26
d1180cf
45c4d90
5e935e7
2ec7616
558366d
e717b29
2a3b20f
91f951d
9a20bb2
71ade77
875df96
b89bba4
9243f03
9703d68
d33981e
14ee34e
7ef3347
a928c4a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -116,6 +116,7 @@ See section [Discovery](#discovery) for details. | |
| | shellyplusdimmer | Shelly Plus Dimmer Gen 3 | S3DM-0A101WWL | | ||
| | shellyprodm2pm | Shelly Pro Dimmer 2PM | SPDM-002PE01EU | | ||
| | shellyplusrgbwpm | Shelly Plus RGBW PM | SNDC-0D4P10WW | | ||
| | shellyprorgbwwpm | Shelly Pro RGBWW PM | SPDC-0D5PE16EU | | ||
| | shellywalldisplay | Shelly Plus Wall Display | SAWD-0A1XX10EU1 | | ||
| | shellyblugw | Shelly BLU Gateway | SNGW-BT01 | | ||
| | shellyblugw3 | Shelly BLU Gateway 3 | S3GW-1DBT001 | | ||
|
|
@@ -1740,6 +1741,71 @@ In `light` profile (white mode), each of the 4 LED output channels is exposed as | |
| `Note`: | ||
| totalEnergy might reset on restart depending on device type and firmware version | ||
|
|
||
| ### Shelly Pro RGBWW PM (thing-type: shellyprorgbwwpm) | ||
|
|
||
| The active device profile (`light`, `rgbcct`, `cctx2` or `rgbx2light`) is selected in the Shelly App/device settings and determines which channel groups below are populated. | ||
| Changing the profile requires deleting and re-discovering the Thing. | ||
|
|
||
| In `rgbcct` or `rgbx2light` profile, the RGB component is exposed as the color component (color mode): | ||
|
|
||
| | Group | Channel | Type | read-only | Description | | ||
| | ------- | ------------- | -------- | --------- | ----------------------------------------------------------------------- | | ||
| | control | power | Switch | r/w | Switch light ON/OFF | | ||
| | | autoOn | Number | r/w | Sets a timer to turn the device ON after every OFF command; in seconds | | ||
| | | autoOff | Number | r/w | Sets a timer to turn the device OFF after every ON command; in seconds | | ||
| | | timerActive | Switch | yes | ON: An auto-on/off timer is active | | ||
| | color | hsb | HSB | r/w | Represents the color picker (HSBType); control r/g/b, not white | | ||
| | | full | String | r/w | Set Red / Green / Blue / Yellow / White mode and switch mode | | ||
| | | | | r/w | Valid settings: "red", "green", "blue", "yellow", "white" or "r,g,b,w" | | ||
| | | red | Dimmer | r/w | Red brightness: 0..100% (control only the red channel) | | ||
| | | green | Dimmer | r/w | Green brightness: 0..100% (control only the green channel) | | ||
| | | blue | Dimmer | r/w | Blue brightness: 0..100% (control only the blue channel) | | ||
| | | white | Dimmer | r/w | White brightness: 0..100% (control only the white channel) | | ||
| | meter1 | currentPower | Number | yes | Current power consumption in Watts | | ||
| | | energyAvg1Min | Number | yes | Energy consumed in the previous minute (Wh) | | ||
| | | totalEnergy | Number | yes | Total energy consumption in kWh | | ||
| | | lastUpdate | DateTime | yes | Timestamp of the last measurement | | ||
|
|
||
| `Note`: | ||
| `rgbcct` and `rgbx2light` combine the RGB component above with additional CCT (`rgbcct`) or Light | ||
| (`rgbx2light`) components. Each additional component is exposed as its own `light1`/`light2` group | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This applies only to LIGHT or RGBX2LIGHT and not to RGBCCT or CCTX2 .. also (nit) in OH markdown convention every sentence must be on a new line. |
||
| (same layout as the `light` profile below) with its own independent meter (`meter2`/`meter3`). | ||
| Since every profile has more than one meter, the device also gets the aggregated `device#accumulatedPower`/`device#totalEnergy` channels described in the general notes on channels above. | ||
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Probably you should add a whole new table of channels for RGBCCT with its own comments etc. |
||
| In `light` profile (white mode), each of the 5 LED output channels is exposed as its own group: | ||
|
|
||
| | Group | Channel | Type | read-only | Description | | ||
| | ------ | ----------- | ------ | --------- | ----------------------------------------------------------------------- | | ||
| | light1 | brightness | Dimmer | r/w | Channel 1: Brightness: 0..100, control power state with ON/OFF | | ||
| | | autoOn | Number | r/w | Sets a timer to turn the device ON after every OFF command; in seconds | | ||
| | | autoOff | Number | r/w | Sets a timer to turn the device OFF after every ON command; in seconds | | ||
| | | timerActive | Switch | yes | ON: An auto-on/off timer is active | | ||
| | light2 | | | | Same for LED channel 2 | | ||
| | light3 | | | | Same for LED channel 3 | | ||
| | light4 | | | | Same for LED channel 4 | | ||
| | light5 | | | | Same for LED channel 5 | | ||
| | meter1 | | | | Meter for LED channel 1, see meter group in color mode above | | ||
| | meter2 | | | | Meter for LED channel 2 (if configured) | | ||
| | meter3 | | | | Meter for LED channel 3 (if configured) | | ||
| | meter4 | | | | Meter for LED channel 4 (if configured) | | ||
| | meter5 | | | | Meter for LED channel 5 (if configured) | | ||
|
|
||
| In `cctx2` profile (dual color-temperature mode), the device exposes two independent CCT components (`CCT:0` and `CCT:1`), each controlling its own warm/cold white pair; they are each exposed as their own channel group, with its own independent meter: | ||
|
|
||
| | Group | Channel | Type | read-only | Description | | ||
| | ------ | ----------- | ------ | --------- | ----------------------------------------------------------------------- | | ||
| | light1 | brightness | Dimmer | r/w | CCT channel 1: Brightness: 0..100, control power state with ON/OFF | | ||
| | | colorTemp | Dimmer | r/w | CCT channel 1: Color temperature: 0..100% (2700K..6500K) | | ||
| | | autoOn | Number | r/w | Sets a timer to turn the device ON after every OFF command; in seconds | | ||
| | | autoOff | Number | r/w | Sets a timer to turn the device OFF after every ON command; in seconds | | ||
| | | timerActive | Switch | yes | ON: An auto-on/off timer is active | | ||
| | light2 | | | | Same for CCT channel 2 | | ||
| | meter1 | | | | Meter for CCT channel 1 (light1), see meter group above | | ||
| | meter2 | | | | Meter for CCT channel 2 (light2), see meter group above | | ||
|
|
||
| `Note`: | ||
| totalEnergy might reset on restart depending on device type and firmware version | ||
|
|
||
| ## Shelly Plus Mini Series | ||
|
|
||
| ### Shelly Plus 1 Mini (thing-type: shelly1mini) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -112,6 +112,7 @@ public class ShellyDevices { | |
| public static final String SHELLYDT_PRODM2PM = "SPDM-002PE01EU"; | ||
| public static final String SHELLYDT_PLUSWALLDISPLAY = "SAWD-0A1XX10EU1"; | ||
| public static final String SHELLYDT_PLUSRGBWPM = "SNDC-0D4P10WW"; | ||
| public static final String SHELLYDT_PRORGBWWPM = "SPDC-0D5PE16EU"; | ||
| public static final String SHELLYDT_PLUSSTRIP = "S4PL-00416EU"; | ||
| public static final String SHELLYDT_PLUSPLUGCPM = "S4PL-00116EU"; // Shelly Plus Plug CPM (Gen4) | ||
| public static final String SHELLYDT_PLUSEM = "S3EM-002CXCEU"; | ||
|
|
@@ -262,6 +263,7 @@ public class ShellyDevices { | |
| public static final ThingTypeUID THING_TYPE_SHELLYPLUSDIMMER = new ThingTypeUID(BINDING_ID, "shellyplusdimmer"); | ||
| public static final ThingTypeUID THING_TYPE_SHELLYPRODM2PM = new ThingTypeUID(BINDING_ID, "shellyprodm2pm"); | ||
| public static final ThingTypeUID THING_TYPE_SHELLYPLUSRGBWPM = new ThingTypeUID(BINDING_ID, "shellyplusrgbwpm"); | ||
| public static final ThingTypeUID THING_TYPE_SHELLYPRORGBWWPM = new ThingTypeUID(BINDING_ID, "shellyprorgbwwpm"); | ||
| public static final ThingTypeUID THING_TYPE_SHELLYPLUSSTRIP = new ThingTypeUID(BINDING_ID, "shellyplusstrip"); | ||
| public static final ThingTypeUID THING_TYPE_SHELLYPLUSEM = new ThingTypeUID(BINDING_ID, "shellyplusem"); | ||
| public static final ThingTypeUID THING_TYPE_SHELLYPLUS3EM63 = new ThingTypeUID(BINDING_ID, "shellyplus3em63"); | ||
|
|
@@ -321,7 +323,8 @@ public class ShellyDevices { | |
|
|
||
| // RGBW2 devices | ||
| public static final Set<ThingTypeUID> GROUP_RGBW2_THING_TYPES = Set.of( // | ||
| THING_TYPE_SHELLYRGBW2_COLOR, THING_TYPE_SHELLYRGBW2_WHITE, THING_TYPE_SHELLYPLUSRGBWPM); | ||
| THING_TYPE_SHELLYRGBW2_COLOR, THING_TYPE_SHELLYRGBW2_WHITE, THING_TYPE_SHELLYPLUSRGBWPM, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think RGBWWPM is not strictly an RGBW thing type since it has no W component. See other comments above and below |
||
| THING_TYPE_SHELLYPRORGBWWPM); | ||
|
|
||
| // All Light Bulbs / RGBW devices | ||
| public static final Set<ThingTypeUID> GROUP_LIGHT_THING_TYPES = Stream | ||
|
|
@@ -467,6 +470,7 @@ public class ShellyDevices { | |
| Map.entry(SHELLYDT_PLUSDIMMERG3, THING_TYPE_SHELLYPLUSDIMMER), | ||
| Map.entry(SHELLYDT_PRODM2PM, THING_TYPE_SHELLYPRODM2PM), | ||
| Map.entry(SHELLYDT_PLUSRGBWPM, THING_TYPE_SHELLYPLUSRGBWPM), | ||
| Map.entry(SHELLYDT_PRORGBWWPM, THING_TYPE_SHELLYPRORGBWWPM), | ||
| Map.entry(SHELLYDT_PLUSEM, THING_TYPE_SHELLYPLUSEM), | ||
| Map.entry(SHELLYDT_PLUS3EM63, THING_TYPE_SHELLYPLUS3EM63), | ||
| Map.entry(SHELLYDT_PLUSBLUGW, THING_TYPE_SHELLYPLUSBLUGW), // | ||
|
|
@@ -656,6 +660,7 @@ public class ShellyDevices { | |
| Map.entry("shellydimmerg3", THING_TYPE_SHELLYPLUSDIMMER), | ||
| Map.entry("shellyprodm2pm", THING_TYPE_SHELLYPRODM2PM), | ||
| Map.entry("shellyplusrgbwpm", THING_TYPE_SHELLYPLUSRGBWPM), | ||
| Map.entry("shellyprorgbwwpm", THING_TYPE_SHELLYPRORGBWWPM), | ||
| Map.entry("shellyplusstrip", THING_TYPE_SHELLYPLUSSTRIP), | ||
| Map.entry("shellyblugw", THING_TYPE_SHELLYPLUSBLUGW), // | ||
| Map.entry("shellyblugwg3", THING_TYPE_SHELLYPLUSBLUGW), // | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,129 @@ | ||
| /* | ||
| * Copyright (c) 2010-2026 Contributors to the openHAB project | ||
| * | ||
| * See the NOTICE file(s) distributed with this work for additional | ||
| * information. | ||
| * | ||
| * This program and the accompanying materials are made available under the | ||
| * terms of the Eclipse Public License 2.0 which is available at | ||
| * http://www.eclipse.org/legal/epl-2.0 | ||
| * | ||
| * SPDX-License-Identifier: EPL-2.0 | ||
| */ | ||
| package org.openhab.binding.shelly.internal.api; | ||
|
|
||
| import static org.openhab.binding.shelly.internal.ShellyBindingConstants.*; | ||
| import static org.openhab.binding.shelly.internal.util.ShellyUtils.substringAfter; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| import org.eclipse.jdt.annotation.NonNullByDefault; | ||
| import org.eclipse.jdt.annotation.Nullable; | ||
| import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsRgbwLight; | ||
|
|
||
| /** | ||
| * The {@link ShellyApiLightUtil} provides light-specific helpers shared by Gen1 (api1) and Gen2+ (api2) code: | ||
| * the {@code settings.lights[i].apiComponent} tag values used to identify which light API component family (RGB, | ||
| * RGBW, CCT or plain Light) a {@code ShellySettingsRgbwLight} entry belongs to, and the light channel group/id | ||
| * resolution shared across handlers. | ||
| * | ||
| * @author Markus Michels - Initial contribution | ||
| */ | ||
| @NonNullByDefault | ||
| public class ShellyApiLightUtil { | ||
| private ShellyApiLightUtil() { | ||
| } | ||
|
|
||
| /** | ||
| * The light API component family a {@code settings.lights[i]} entry belongs to. {@code NONE} marks an | ||
| * untagged entry (a Gen1 RGBW2/Bulb entry never sets apiComponent) or an out-of-range index. | ||
| * <p> | ||
| * Each index is an independently switchable/dimmable/metered physical component - not a sub-channel of one | ||
| * combined light. A hybrid Pro RGBWW PM profile (e.g. {@code rgbcct}, {@code rgbx2light}) reports its color | ||
| * component (RGB/RGBW) and its secondary CCT/Light component(s) as separate {@code settings.lights} indices, | ||
| * each with its own RPC methods and meter - never merged into a single multi-channel model. | ||
| */ | ||
| public enum ShellyLightApiComponent { | ||
| RGB, | ||
| RGBW, | ||
| CCT, | ||
| LIGHT, | ||
| NONE | ||
| } | ||
|
|
||
| /** | ||
| * True when tag identifies an RGB/RGBW color component, as opposed to a CCT/Light one. | ||
| */ | ||
| public static boolean isColorComponent(ShellyLightApiComponent apiComponent) { | ||
| return apiComponent == ShellyLightApiComponent.RGB || apiComponent == ShellyLightApiComponent.RGBW; | ||
| } | ||
|
|
||
| public static boolean isRgbComponent(ShellyLightApiComponent apiComponent) { | ||
| return apiComponent == ShellyLightApiComponent.RGB; | ||
| } | ||
|
|
||
| public static boolean isRgbwComponent(ShellyLightApiComponent apiComponent) { | ||
| return apiComponent == ShellyLightApiComponent.RGBW; | ||
| } | ||
|
|
||
| public static boolean isCctComponent(ShellyLightApiComponent apiComponent) { | ||
| return apiComponent == ShellyLightApiComponent.CCT; | ||
| } | ||
|
|
||
| public static boolean isLightComponent(ShellyLightApiComponent apiComponent) { | ||
| return apiComponent == ShellyLightApiComponent.LIGHT; | ||
| } | ||
|
|
||
| /** | ||
| * The apiComponent tag of {@code lights.get(idx)}, or {@code NONE} when lights is null, idx is out of range, | ||
| * or the entry is untagged (a Gen1 RGBW2/Bulb entry never sets apiComponent). | ||
| */ | ||
| public static ShellyLightApiComponent tagAt(@Nullable List<ShellySettingsRgbwLight> lights, int idx) { | ||
| return lights != null && idx >= 0 && idx < lights.size() ? lights.get(idx).apiComponent | ||
| : ShellyLightApiComponent.NONE; | ||
| } | ||
|
|
||
| /** | ||
| * True when any entry in lights is an RGB/RGBW color component. | ||
| */ | ||
| public static boolean hasColorComponent(List<ShellySettingsRgbwLight> lights) { | ||
| return lights.stream().map(l -> l.apiComponent).anyMatch(ShellyApiLightUtil::isColorComponent); | ||
| } | ||
|
|
||
| /** | ||
| * Reverses {@link ShellyDeviceProfile#getControlGroup(int)}: converts a channel group name back into its | ||
| * {@code settings.lights} index. On a hybrid profile (e.g. {@code rgbcct}, {@code rgbx2light}) the indexed | ||
| * groups (light1, light2, ...) start after the leading color component slot(s), so the group number alone | ||
| * is not the flat index - {@code profile.getColorComponentCount()} must be added back in. | ||
| */ | ||
| public static Integer getLightIdFromGroup(String groupName, ShellyDeviceProfile profile) { | ||
| if (groupName.startsWith(CHANNEL_GROUP_LIGHT_INDEX)) { | ||
| return Integer.parseInt(substringAfter(groupName, CHANNEL_GROUP_LIGHT_INDEX)) - 1 | ||
| + profile.getColorComponentCount(); | ||
| } | ||
| if (groupName.startsWith(CHANNEL_GROUP_LIGHT_CHANNEL)) { | ||
| return Integer.parseInt(substringAfter(groupName, CHANNEL_GROUP_LIGHT_CHANNEL)) - 1 | ||
| + profile.getColorComponentCount(); | ||
| } | ||
| return 0; // only 1 light, e.g. bulb or rgbw2 in color mode | ||
| } | ||
|
|
||
| // Gen2 RGBW PM ships on light1..n natively. A Gen1 RGBW2 Thing that already carries the | ||
| // deprecated channel1..n group (from before this Thing was migrated) keeps publishing there, | ||
| // dual-written to light1..n by ShellyBaseHandler.updateChannel(); a freshly discovered Gen1 | ||
| // RGBW2 Thing goes straight to light1..n and never gets a channel1..n group. | ||
| public static String lightChannelGroupPrefix(ShellyDeviceProfile profile) { | ||
| return profile.isGen2 || !profile.hasLegacyLightChannels ? CHANNEL_GROUP_LIGHT_INDEX | ||
| : CHANNEL_GROUP_LIGHT_CHANNEL; | ||
| } | ||
|
|
||
| // Bulb/Duo report white/temp under a dedicated group, distinct from their color group; everything else | ||
| // (including a hybrid Pro RGBWW PM profile's secondary CCT/Light component) shares the same per-component | ||
| // group resolution as the color channels, so this just delegates to getControlGroup(). | ||
| public static String buildWhiteGroupName(ShellyDeviceProfile profile, int lightId) { | ||
| if (profile.isBulb || profile.isDuo) { | ||
| return CHANNEL_GROUP_WHITE_CONTROL; | ||
| } | ||
| return profile.getControlGroup(lightId); | ||
| } | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As described here https://shelly-api-docs.shelly.cloud/gen2/Devices/Gen2/ShellyProRGBWWPM the device has 5 outputs which can be used in various combinations:
LIGHTprofileRGBX2LIGHTprofileRGBXCCTprofileCCTX2profileNote that the first group seems always to be
RGBand notRGBW.Therefore I would assume that the RGB mode will NOT expose a White component. i.e. RGB -only..
See my comments here #19227 (comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is still a concrete RGB-vs-RGBW issue here.
colorSettingsRGBreuses thecolorFullchannel type, which still offerswhiteas an option.The Pro RGBWW PM exposes an
RGBcomponent, not anRGBWcomponent. Selectingwhitetherefore makeshandleFullColor()produceRGBW = 0,0,0,255, but the RGB RPC path only sends the RGB values and ignores the white value. The device consequently receivesRGB = 0,0,0.An RGB-only component should therefore not expose the White option, unless White is explicitly translated to a valid RGB value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is fixed by my PR