[shelly] Improved documentation, support for UNI, 1L, Color Bulb (#9508)
* This PR merges the 2.5 code base with new features, a bunch of bug fixes and improved documentation. Parts of the code has been re-factored and vaious issues are fixed/optimized along the way. Signed-off-by: Markus Michels <markus7017@gmail.com> * Minor fixed, support for Shelly Color Bulb, Shelly Motion Signed-off-by: Markus Michels <markus7017@gmail.com> * Review changes applied Signed-off-by: Markus Michels <markus7017@gmail.com> * review change applied Signed-off-by: Markus Michels <markus7017@gmail.com> * review changes Signed-off-by: Markus Michels <markus7017@gmail.com> * Review changes Signed-off-by: Markus Michels <markus7017@gmail.com> * review changes Signed-off-by: Markus Michels <markus7017@gmail.com> * README updated Signed-off-by: Markus Michels <markus7017@gmail.com> * review change Signed-off-by: Markus Michels <markus7017@gmail.com> * review change Signed-off-by: Markus Michels <markus7017@gmail.com>pull/10046/head
parent
bc5ffb26e2
commit
c4a3b1e6ba
|
@ -7,19 +7,20 @@ Allterco provides a rich set of smart home devices. All of them are WiFi enabled
|
|||
The binding is officially acknowledged by Allterco and openHAB is listed as a reference and directly supports the openHAB community.
|
||||
|
||||
The binding controls the devices independently from the Allterco Shelly Cloud (in fact it can be disabled).
|
||||
The binding co-exists with Shelly App for Smartphones, Shelly Web App, Shelly Cloud, mqqt and other 3rd party Apps.
|
||||
The binding co-exists with Shelly App for Smartphones, Shelly Device Web UI, Shelly Cloud, MQTT and other 3rd party Apps.
|
||||
|
||||
The binding focuses on reporting the device status and device control.
|
||||
Initial setup and device configuration has to be performed using the Shelly Apps (Web or Smartphone App).
|
||||
Initial setup and device configuration has to be performed using the Shelly Apps (Web UI or Smartphone App).
|
||||
The binding gets in sync with the next status refresh.
|
||||
|
||||
Refer to [Advanced Users](doc/AdvancedUsers.md) for more information on openHAB Shelly integration, e.g. firmware update, network communication or log filtering.
|
||||
|
||||
## Supported Devices
|
||||
|
||||
| Thing Type | Model | Vendor ID |
|
||||
| thing-type | Model | Vendor ID |
|
||||
|--------------------|--------------------------------------------------------|-----------|
|
||||
| shelly1 | Shelly 1 Single Relay Switch | SHSW-1 |
|
||||
| shelly1l | Shelly 1L Single Relay Switch | SHSW-L |
|
||||
| shelly1pm | Shelly Single Relay Switch with integrated Power Meter | SHSW-PM |
|
||||
| shelly2-relay | Shelly Double Relay Switch in relay mode | SHSW-21 |
|
||||
| shelly2-roller | Shelly2 in Roller Mode | SHSW-21 |
|
||||
|
@ -35,11 +36,14 @@ Refer to [Advanced Users](doc/AdvancedUsers.md) for more information on openHAB
|
|||
| shellyem3 | Shelly 3EM with 3 integrated Power Meter | SHEM-3 |
|
||||
| shellyrgbw2 | Shelly RGB Controller | SHRGBW2 |
|
||||
| shellybulb | Shelly Bulb in Color or White Mode | SHBLB-1 |
|
||||
| shellybulbduo | Shelly Duo (White Mode) | SHBDUO-1 |
|
||||
| shellybulbduo | Shelly Duo White | SHBDUO-1 |
|
||||
| shellybulbduo | Shelly Duo White G10 | SHBDUO-1 |
|
||||
| shellycolorbulb | Shelly Duo Color G10 | SHCB-1 |
|
||||
| shellyvintage | Shelly Vintage (White Mode) | SHVIN-1 |
|
||||
| shellyht | Shelly Sensor (temp+humidity) | SHHT-1 |
|
||||
| shellyflood | Shelly Flood Sensor | SHWT-1 |
|
||||
| shellysmoke | Shelly Smoke Sensor | SHSM-1 |
|
||||
| shellymotion | Shelly Motion Sensor | SHMOS-01 |
|
||||
| shellygas | Shelly Gas Sensor | SHGS-1 |
|
||||
| shellydw | Shelly Door/Window | SHDW-1 |
|
||||
| shellydw2 | Shelly Door/Window 2 | SHDW-2 |
|
||||
|
@ -115,6 +119,14 @@ For those open the case, press that button and the LED starts flashing.
|
|||
Wait a moment and then start the discovery. The device should show up in the Inbox and can be added.
|
||||
Sometimes you need to run the discovery multiple times.
|
||||
|
||||
### Roller Favorites
|
||||
|
||||
Firmware 1.9.2 for Shelly 2.5 in roller mode supports so called favorites for positions.
|
||||
You could use the Shelly App to setup 4 different positions (percentage) and assign id 1-4.
|
||||
The channel `roller#rollerFav` allows to select those from openHAB and the roller moves to the desired position.
|
||||
In the Thing configuration you could also configure an id when the `roller#control` channel receives UP or DOWN.
|
||||
Values 1-4 are selecting the corresponding favorite id in the Shelly App, 0 means no favorite.
|
||||
|
||||
### Thing Status
|
||||
|
||||
The binding sets the following Thing status depending on the device status:
|
||||
|
@ -168,6 +180,8 @@ You could also create a rule to catch those status changes or device alarms (see
|
|||
|eventsSensorReport|true: register event "posted updated sensor data" | no |true for sensor devices |
|
||||
|eventsCoIoT |true: Listen for CoIoT/COAP events | no |true for battery devices, false for others |
|
||||
|eventsRoller |true: register event "trigger" when the roller updates status | no |true for roller devices |
|
||||
|favoriteUP |0-4: Favorite id for UP (see Roller Favorites) | no |0 = no favorite id |
|
||||
|favoriteDOWN |0-4: Favorite id for DOWN (see Roller Favorites) | no |0 = no favorite id |
|
||||
|
||||
|
||||
### General Notes
|
||||
|
@ -189,6 +203,7 @@ Every device has a channel group `device` with the following channels:
|
|||
| |updateAvailable |Switch |yes |ON: A firmware update is available |
|
||||
| |statusLed |Switch |r/w |ON: Status LED is disabled, OFF: LED enabled |
|
||||
| |powerLed |Switch |r/w |ON: Power LED is disabled, OFF: LED enabled |
|
||||
| |charger |Switch |yes |ON: USB charging cable is connected external power supply activated. |
|
||||
|
||||
Availability of channels is depending on the device type.
|
||||
The binding detects many of those channels on-the-fly (when Thing changes to ONLINE state) and adjusts the Thing's channel structure.
|
||||
|
@ -296,6 +311,8 @@ Depending on the device type and firmware release channels might be not availabl
|
|||
| |outputName |String |yes |Logical name of this relay output as configured in the Shelly App |
|
||||
| |input |Switch |yes |ON: Input/Button is powered, see general notes on channels |
|
||||
| |button |Trigger |yes |Event trigger with payload, see SHORT_PRESSED or LONG_PRESSED |
|
||||
| |lastEvent |String |yes |Last event type (S/SS/SSS/L) |
|
||||
| |eventCount |Number |yes |Counter gets incremented every time the device issues a button event. |
|
||||
| |autoOn |Number |r/w |Relay #1: Sets a timer to turn the device ON after every OFF command; in seconds|
|
||||
| |autoOff |Number |r/w |Relay #1: Sets a timer to turn the device OFF after every ON command; in seconds|
|
||||
| |timerActive |Switch |yes |Relay #1: ON: An auto-on/off timer is active |
|
||||
|
@ -304,6 +321,34 @@ Depending on the device type and firmware release channels might be not availabl
|
|||
| |temperature3 |Number |yes |Temperature value of external sensor #3 (if connected to temp/hum addon) |
|
||||
| |humidity |Number |yes |Humidity in percent (if connected to temp/hum addon) |
|
||||
|
||||
### Shelly 1L (thing-type: shelly1l)
|
||||
|
||||
|Group |Channel |Type |read-only|Description |
|
||||
|----------|-------------|---------|---------|---------------------------------------------------------------------------------|
|
||||
|relay |output |Switch |r/w |Controls the relay's output channel (on/off) |
|
||||
| |outputName |String |yes |Logical name of this relay output as configured in the Shelly App |
|
||||
| |input1 |Switch |yes |ON: Input/Button for input 1 is powered, see general notes on channels |
|
||||
| |button1 |Trigger |yes |Event trigger, see section Button Events |
|
||||
| |lastEvent1 |String |yes |Last event type (S/SS/SSS/L) for input 1 |
|
||||
| |eventCount1 |Number |yes |Counter gets incremented every time the device issues a button event. |
|
||||
| |input2 |Switch |yes |ON: Input/Button for channel 2 is powered, see general notes on channels |
|
||||
| |button2 |Trigger |yes |Event trigger, see section Button Events |
|
||||
| |lastEvent2 |String |yes |Last event type (S/SS/SSS/L) for input 2 |
|
||||
| |eventCount2 |Number |yes |Counter gets incremented every time the device issues a button event. |
|
||||
| |autoOn |Number |r/w |Relay #1: Sets a timer to turn the device ON after every OFF command; in seconds|
|
||||
| |autoOff |Number |r/w |Relay #1: Sets a timer to turn the device OFF after every ON command; in seconds|
|
||||
| |timerActive |Switch |yes |Relay #1: ON: An auto-on/off timer is active |
|
||||
|meter |currentWatts |Number |yes |Current power consumption in Watts |
|
||||
| |lastUpdate |DateTime |yes |Timestamp of the last measurement |
|
||||
|sensors |temperature1 |Number |yes |Temperature value of external sensor #1 (if connected to temp/hum addon) |
|
||||
| |temperature2 |Number |yes |Temperature value of external sensor #2 (if connected to temp/hum addon) |
|
||||
| |temperature3 |Number |yes |Temperature value of external sensor #3 (if connected to temp/hum addon) |
|
||||
| |humidity |Number |yes |Humidity in percent (if connected to temp/hum addon) |
|
||||
|
||||
Note: The `meter`for the Shelly 1L is kind of fake.
|
||||
It doesn't have a real power meter, but you could setup an estimated consumption in the Shelly App, e.g. 60W if you have attached a good old light bulb to the output channel.
|
||||
In this case the is no real measurement based on power consumption, but the Shelly reports the configured value when the relay is ON.
|
||||
|
||||
### Shelly 1PM (thing-type: shelly1pm)
|
||||
|
||||
|Group |Channel |Type |read-only|Description |
|
||||
|
@ -330,6 +375,8 @@ Depending on the device type and firmware release channels might be not availabl
|
|||
| |outputName |String |yes |Logical name of this relay output as configured in the Shelly App |
|
||||
| |input |Switch |yes |ON: Input/Button is powered, see general notes on channels |
|
||||
| |button |Trigger |yes |Event trigger, see section Button Events |
|
||||
| |lastEvent |String |yes |Last event type (S/SS/SSS/L) |
|
||||
| |eventCount |Number |yes |Counter gets incremented every time the device issues a button event. |
|
||||
| |autoOn |Number |r/w |Relay #1: Sets a timer to turn the device ON after every OFF command; in seconds|
|
||||
| |autoOff |Number |r/w |Relay #1: Sets a timer to turn the device OFF after every ON command; in seconds|
|
||||
| |timerActive |Switch |yes |Relay #1: ON: An auto-on/off timer is active |
|
||||
|
@ -357,6 +404,8 @@ The Thing id is derived from the service name, so that's the reason why the Thin
|
|||
| |outputName |String |yes |Logical name of this relay output as configured in the Shelly App |
|
||||
| |input |Switch |yes |ON: Input/Button is powered, see general notes on channels |
|
||||
| |button |Trigger |yes |Event trigger, see section Button Events |
|
||||
| |lastEvent |String |yes |Last event type (S/SS/SSS/L) |
|
||||
| |eventCount |Number |yes |Counter gets incremented every time the device issues a button event. |
|
||||
| |autoOn |Number |r/w |Relay #1: Sets a timer to turn the device ON after every OFF command; in seconds|
|
||||
| |autoOff |Number |r/w |Relay #1: Sets a timer to turn the device OFF after every ON command; in seconds|
|
||||
| |timerActive |Switch |yes |Relay #1: ON: An auto-on/off timer is active |
|
||||
|
@ -419,6 +468,7 @@ The Thing id is derived from the service name, so that's the reason why the Thin
|
|||
| |input |Switch |yes |ON: Input/Button is powered, see General Notes on Channels |
|
||||
| |event |Trigger |yes |Roller event/trigger with payload ROLLER_OPEN / ROLLER_CLOSE / ROLLER_STOP |
|
||||
| |rollerpos |Number |r/w |Roller position: 100%=open...0%=closed; gets updated when the roller stops, see Notes |
|
||||
| |rollerFav |Number |r/w |Select roller position favorite (1-4, 0=no), see Notes |
|
||||
| |state |String |yes |Roller state: open/close/stop |
|
||||
| |stopReason |String |yes |Last stop reasons: normal, safety_switch or obstacle |
|
||||
| |safety |Switch |yes |Indicates status of the Safety Switch, ON=problem detected, powered off |
|
||||
|
@ -495,8 +545,13 @@ The Shelly 4Pro provides 4 relays and 4 power meters.
|
|||
|relay |brightness |Dimmer |r/w |Currently selected brightness. |
|
||||
| |outputName |String |yes |Logical name of this relay output as configured in the Shelly App |
|
||||
| |input1 |Switch |yes |ON: Input/Button for input 1 is powered, see general notes on channels |
|
||||
| |input2 |Switch |yes |ON: Input/Button for input 1 is powered, see general notes on channels |
|
||||
| |button |Trigger |yes |Event trigger, see section Button Events |
|
||||
| |button1 |Trigger |yes |Event trigger, see section Button Events |
|
||||
| |lastEvent1 |String |yes |Last event type (S/SS/SSS/L) for input 1 |
|
||||
| |eventCount1 |Number |yes |Counter gets incremented every time the device issues a button event. |
|
||||
| |input2 |Switch |yes |ON: Input/Button for channel 2 is powered, see general notes on channels |
|
||||
| |button2 |Trigger |yes |Event trigger, see section Button Events |
|
||||
| |lastEvent2 |String |yes |Last event type (S/SS/SSS/L) for input 2 |
|
||||
| |eventCount2 |Number |yes |Counter gets incremented every time the device issues a button event. |
|
||||
| |autoOn |Number |r/w |Relay #1: Sets a timer to turn the device ON after every OFF command; in seconds|
|
||||
| |autoOff |Number |r/w |Relay #1: Sets a timer to turn the device OFF after every ON command; in seconds|
|
||||
| |timerActive |Switch |yes |Relay #1: ON: An auto-on/off timer is active |
|
||||
|
@ -521,7 +576,7 @@ Using the Thing configuration option `brightnessAutoOn` you could decide if the
|
|||
| |input3 |Switch |yes |State of Input 3 |
|
||||
| |button |Trigger |yes |Event trigger: Event trigger, see section Button Events |
|
||||
| |lastEvent |String |yes |S/SS/SSS for 1/2/3x Shortpush or L for Longpush |
|
||||
| |eventCount |Number |yes |Number of button events |
|
||||
| |eventCount |Number |yes |Counter gets incremented every time the device issues a button event. |
|
||||
|
||||
### Shelly Bulb (thing-type: shellybulb)
|
||||
|
||||
|
@ -533,7 +588,7 @@ Using the Thing configuration option `brightnessAutoOn` you could decide if the
|
|||
| |autoOff |Number |r/w |Sets a timer to turn the device OFF after every ON: in sec |
|
||||
| |timerActive |Switch |yes |ON: An auto-on/off timer is active |
|
||||
|color | | | |Color settings: only valid in COLOR mode |
|
||||
| |hsb |HSB |r/w |Represents the color picker (HSBType), control r/g/b, bight not white |
|
||||
| |hsb |HSB |r/w |Represents the color picker (HSBType), control r/g/b, but 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% or 0..255 (control only the red channel) |
|
||||
|
@ -547,9 +602,16 @@ Using the Thing configuration option `brightnessAutoOn` you could decide if the
|
|||
|white | | | |Color settings: only valid in WHITE mode |
|
||||
| |temperature |Number |r/w |color temperature (K): 0..100% or 3000..6500 |
|
||||
| |brightness |Dimmer | |Brightness: 0..100% or 0..100 |
|
||||
|
||||
|
||||
Note: The openHAB color picker has only values for red/green/blue (RGB), not for white as supported by the RGBW2.
|
||||
Beside channel `hsb` the binding also offers the `white` channel (hsb as only RGB values).
|
||||
Or control each color separately with channels `red`, `blue`, `green` (those are advanced channels).
|
||||
|
||||
|
||||
#### Shelly Duo (thing-type: shellybulbduo)
|
||||
|
||||
This information applies to the Shelly Duo-1 as well as the Duo White for the G10 socket.
|
||||
|
||||
|Group |Channel |Type |read-only|Description |
|
||||
|----------|-------------|---------|---------|-----------------------------------------------------------------------|
|
||||
|control |autoOn |Number |r/w |Sets a timer to turn the device ON after every OFF; in sec |
|
||||
|
@ -569,16 +631,44 @@ Using the Thing configuration option `brightnessAutoOn` you could decide if the
|
|||
|----------|-------------|---------|---------|-----------------------------------------------------------------------|
|
||||
|control |autoOn |Number |r/w |Sets a timer to turn the device ON after every OFF; in sec |
|
||||
| |autoOff |Number |r/w |Sets a timer to turn the device OFF after every ON: in sec |
|
||||
| |timerActive |Switch |yes |ON: An auto-on/off timer is active |
|
||||
| |timerActive |Switch |yes |ON: An auto-on/off timer is active |
|
||||
|white | | | |Color settings: only valid in WHITE mode |
|
||||
| |brightness |Dimmer | |Brightness: 0..100% or 0..100 |
|
||||
|meter |currentWatts |Number |yes |Current power consumption in Watts |
|
||||
| |lastPower1 |Number |yes |Energy consumption for a round minute, 1 minute ago |
|
||||
| |totalKWH |Number |yes |Total energy consumption in Watts since the device powered up (resets on restart)|
|
||||
| |totalKWH |Number |yes |Total energy consumption in kWh since the device powered up (resets on restart)|
|
||||
| |lastUpdate |DateTime |yes |Timestamp of the last measurement |
|
||||
|
||||
## Shelly Duo Color (thing-type: shellyduocolor-color)
|
||||
|
||||
## Shelly RGBW2 in Color Mode (thing-type: shellyrgbw2-color)
|
||||
|Group |Channel |Type |read-only|Description |
|
||||
|----------|-------------|---------|---------|-----------------------------------------------------------------------|
|
||||
|control |power |Switch |r/w |Switch light ON/OFF |
|
||||
| |button |Trigger |yes |Event trigger, see section Button Events |
|
||||
| |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 | | | |Color settings: only valid in COLOR mode |
|
||||
| |hsb |HSB |r/w |Represents the color picker (HSBType), control r/g/b, but 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% or 0..255 (control only the red channel) |
|
||||
| |green |Dimmer |r/w |Green brightness: 0..100% or 0..255 (control only the green channel) |
|
||||
| |blue |Dimmer |r/w |Blue brightness: 0..100% or 0..255 (control only the blue channel) |
|
||||
| |white |Dimmer |r/w |White brightness: 0..100% or 0..255 (control only the white channel) |
|
||||
| |gain |Dimmer |r/w |Gain setting: 0..100% or 0..100 |
|
||||
| |effect |Number |r/w |Puts the light into effect mode: 0=No effect, 1=Meteor Shower, 2=Gradual Change, 3=Flash |
|
||||
|white | | | |Color settings: only valid in WHITE mode |
|
||||
| |temperature |Number |r/w |color temperature (K): 0..100% or 3000..6500 |
|
||||
| |brightness |Dimmer | |Brightness: 0..100% or 0..100 |
|
||||
|meter |currentWatts |Number |yes |Current power consumption in Watts |
|
||||
|
||||
Using the Thing configuration option `brightnessAutoOn` you could decide if the light is turned on when a brightness > 0 is set.
|
||||
`true`: Brightness will be set and device output is powered = light turns on with the new brightness
|
||||
`false`: Brightness will be set, but output stays unchanged so light will not be switched on when it's currently off.
|
||||
|
||||
|
||||
## Shelly Duo RGBW Color Bulb (thing-type: shellycolorbulb)
|
||||
|
||||
|Group |Channel |Type |read-only|Description |
|
||||
|----------|-------------|---------|---------|-----------------------------------------------------------------------|
|
||||
|
@ -600,6 +690,8 @@ Using the Thing configuration option `brightnessAutoOn` you could decide if the
|
|||
| | | | |0=No effect, 1=Meteor Shower, 2=Gradual Change, 3=Flash |
|
||||
|meter |currentWatts |Number |yes |Current power consumption in Watts |
|
||||
|
||||
Channels in group `color`or `white`apply depending on the selected mode - they are not active at the same time.
|
||||
|
||||
Using the Thing configuration option `brightnessAutoOn` you could decide if the light is turned on when a brightness > 0 is set.
|
||||
`true`: Brightness will be set and device output is powered = light turns on with the new brightness
|
||||
`false`: Brightness will be set, but output stays unchanged so light will not be switched on when it's currently off.
|
||||
|
@ -624,7 +716,7 @@ Using the Thing configuration option `brightnessAutoOn` you could decide if the
|
|||
| |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 |
|
||||
|channel4 |brightness |Dimmer |r/w |Channel 5: Brightness: 0..100, control power state with ON/OFF |
|
||||
|channel4 |brightness |Dimmer |r/w |Channel 4: Brightness: 0..100, control power state with ON/OFF |
|
||||
| |button |Trigger |yes |Event trigger, see section Button Events |
|
||||
| |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|
|
||||
|
@ -651,12 +743,13 @@ You can define 2 items (1 Switch, 1 Number) mapping to the same channel, see exa
|
|||
|----------|-------------|---------|---------|-----------------------------------------------------------------------|
|
||||
|sensors |temperature |Number |yes |Temperature, unit is reported by tempUnit |
|
||||
| |humidity |Number |yes |Relative humidity in % |
|
||||
| |charger |Number |yes |ON: USB charging cable is |
|
||||
| |lastUpdate |DateTime |yes |Timestamp of the last update (any sensor value changed) |
|
||||
|battery |batteryLevel |Number |yes |Battery Level in % |
|
||||
| |lowBattery |Switch |yes |Low battery alert (< 20%) |
|
||||
|
||||
### Shelly Flood (thing type: shellyflood)
|
||||
`Please Note:` If you have connected an USB cable to the H&T, but channel charger is off make sure that "Use external power supply" settings is activated in the Shelly App's device settings.
|
||||
|
||||
### Shelly Flood (thing-type: shellyflood)
|
||||
|
||||
|Group |Channel |Type |read-only|Description |
|
||||
|----------|-------------|---------|---------|-----------------------------------------------------------------------|
|
||||
|
@ -666,7 +759,7 @@ You can define 2 items (1 Switch, 1 Number) mapping to the same channel, see exa
|
|||
|battery |batteryLevel |Number |yes |Battery Level in % |
|
||||
| |lowBattery |Switch |yes |Low battery alert (< 20%) |
|
||||
|
||||
### Shelly Door/Window (thing type: shellydw)
|
||||
### Shelly Door/Window (thing-type: shellydw, shellydw2)
|
||||
|
||||
|Group |Channel |Type |read-only|Description |
|
||||
|----------|-------------|---------|---------|-----------------------------------------------------------------------|
|
||||
|
@ -680,12 +773,26 @@ You can define 2 items (1 Switch, 1 Number) mapping to the same channel, see exa
|
|||
|battery |batteryLevel |Number |yes |Battery Level in % |
|
||||
| |lowBattery |Switch |yes |Low battery alert (< 20%) |
|
||||
|
||||
### Shelly Button 1 (thing type: shellybutton1)
|
||||
### Shelly Motion (thing-type: shellymotion)
|
||||
|
||||
|Group |Channel |Type |read-only|Description |
|
||||
|----------|---------------|---------|---------|---------------------------------------------------------------------|
|
||||
|sensors |motion |Switch |yes |ON: Motion was detected |
|
||||
| |motionTimestamp|DateTime |yes |Time when motion started/was detected |
|
||||
| |lux |Number |yes |Brightness in Lux |
|
||||
| |illumination |String |yes |Current illumination: dark/twilight/bright |
|
||||
| |vibration |Switch |yes |ON: Vibration detected |
|
||||
| |charger |Switch |yes |ON: USB charging cable is connected external power supply activated. |
|
||||
| |lastUpdate |DateTime |yes |Timestamp of the last update (any sensor value changed) |
|
||||
|battery |batteryLevel |Number |yes |Battery Level in % |
|
||||
| |lowBattery |Switch |yes |Low battery alert (< 20%) |
|
||||
|
||||
### Shelly Button 1 (thing-type: shellybutton1)
|
||||
|
||||
|Group |Channel |Type |read-only|Description |
|
||||
|----------|-------------|---------|---------|-----------------------------------------------------------------------|
|
||||
|status |lastEvent |String |yes |S/SS/SSS for 1/2/3x Shortpush or L for Longpush |
|
||||
| |eventCount |Number |yes |Number of button events |
|
||||
| |eventCount |Number |yes |Counter gets incremented every time the device issues a button event. |
|
||||
| |input |Switch |yes |ON: Input/Button is powered, see General Notes on Channels |
|
||||
| |button |Trigger |yes |Event trigger with payload SHORT_PRESSED, DOUBLE_PRESSED... |
|
||||
| |lastUpdate |DateTime |yes |Timestamp of the last update (any value changed) |
|
||||
|
@ -694,7 +801,7 @@ You can define 2 items (1 Switch, 1 Number) mapping to the same channel, see exa
|
|||
|
||||
You should calibrate the sensor using the Shelly App to get information on the tilt status.
|
||||
|
||||
### Shelly Smoke(thing type: shellysmoke)
|
||||
### Shelly Smoke (thing-type: shellysmoke)
|
||||
|
||||
|Group |Channel |Type |read-only|Description |
|
||||
|----------|-------------|---------|---------|-----------------------------------------------------------------------|
|
||||
|
@ -705,7 +812,7 @@ You should calibrate the sensor using the Shelly App to get information on the t
|
|||
|battery |batteryLevel |Number |yes |Battery Level in % |
|
||||
| |lowBattery |Switch |yes |Low battery alert (< 20%) |
|
||||
|
||||
### Shelly Smoke(thing type: shellygas)
|
||||
### Shelly Smoke(thing-type: shellysmoke)
|
||||
|
||||
|Group |Channel |Type |read-only|Description |
|
||||
|----------|-------------|---------|---------|-----------------------------------------------------------------------|
|
||||
|
@ -866,6 +973,7 @@ end
|
|||
#### Control CCT LED stripes
|
||||
|
||||
Usage & Requirements:
|
||||
|
||||
- 4 Items per Thing required. Example:
|
||||
|
||||
```
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
This section provides information for advanced use cases.
|
||||
|
||||
## Additiona Resources
|
||||
## Additional Resources
|
||||
|
||||
There are additional resources available providing more information on Shelly devices and how to integrate those into openHAB:
|
||||
|
||||
|
@ -29,18 +29,33 @@ However, if this doesn't work (sometimes there are issues) you could use the [Sh
|
|||
|
||||
|
||||
There are 3 options available to perform the upgrade
|
||||
- The Shelly App usually detects when a new version becomes available and offers to do the upgrade within the UI (Web or App)
|
||||
- Alterco provides the [Shelly Firmware Archive Link Generator](http://archive.shelly-faq.de).
|
||||
This can be used to generate the upgrade link, which could be easily used to perform the upgrade on the cli-level having an Internet connection on that terminal (Shelly device doesn't require an Internet access).
|
||||
|
||||
### Using Shelly Web UI or Smartphone App
|
||||
|
||||
The Apps usually detect when a new version becomes available and offers to do the upgrade to the latest release or beta version.
|
||||
|
||||
### Trigger device update
|
||||
|
||||
The [Shelly Firmware Archive Link Generator](http://archive.shelly-faq.de) is provided by the community (not official, but works like charm).
|
||||
This can be used to generate the update link, which could be easily used to perform the upgrade on the cli-level having an Internet connection on that terminal (Shelly device doesn't require an Internet access).
|
||||
|
||||
You specify the device's IP and device model SHSW-25 and the page will generate you the link for the firmware download using the OTA of the device.
|
||||
Then you run "curl -s [-u user:password] >generated link>" from the terminal.
|
||||
|
||||
Then you run
|
||||
```
|
||||
curl -s [-u user:password] <generated link>
|
||||
```
|
||||
from the command line.
|
||||
|
||||
This should show a JSON result, make sure that it shows "status:updating".
|
||||
Wait 15sec and access the device's Web UI, go to Settings:Firmware Upgrade and make sure than the new version was installed successful.
|
||||
- Manual download and installation of the firmware
|
||||
Manually pick the download link from the [Shelly Firmware Repository](https://api.shelly.cloud/files/firmware) and get the release or beta link.
|
||||
Once you downloaded the file you need to copy it to an http server.
|
||||
Open the following url http://<shelly ip>/ota?url=http://<web server>/<path>/<zip-file>
|
||||
Again, make sure that the file is downloaded and installed properly.
|
||||
|
||||
### Manual download and installation of the firmware
|
||||
|
||||
- Manually pick the download link from the [Shelly Firmware Repository](https://api.shelly.cloud/files/firmware) and get the release or beta link.
|
||||
- Once you downloaded the file you need to copy it to an http server.
|
||||
- Open the following url http://<shelly ip>/ota?url=http://<web server>/<path>/<zip-file>
|
||||
- Again, make sure that the file is downloaded and installed properly.
|
||||
|
||||
## Trouble Shooting
|
||||
|
||||
|
@ -86,10 +101,23 @@ Use a list of items to reduce logging.
|
|||
`Please note:` Once events are filtered they are not show anymore in the logfile, you can’t find them later.
|
||||
|
||||
|
||||
The configuration format of openHAB 3 is in xml format.
|
||||
- openHAB 2.5.x
|
||||
A configuration is added as a new section to `openhab2-userdata/etc/org.ops4j.pax.logging.cfg`
|
||||
|
||||
```
|
||||
# custom filtering rules
|
||||
log4j2.appender.event.filter.uselessevents.type = RegexFilter
|
||||
log4j2.appender.event.filter.uselessevents.regex = .*(heartBeat|LastUpdate|lastUpdate|LetzteAktualisierung|Uptime|Laufzeit|ZuletztGesehen).*
|
||||
log4j2.appender.event.filter.uselessevents.onMatch = DENY
|
||||
log4j2.appender.event.filter.uselessevents.onMisMatch = NEUTRAL
|
||||
```
|
||||
|
||||
- openHAB 3.0
|
||||
|
||||
The configuration format of openHAB 3.0 is in xml format.
|
||||
- Open the file `userdata/etc/log4j2.xml`
|
||||
- Search for tag 'RollingFile'
|
||||
- and add a tag `<RegexFilter>...</RegExFilter>`
|
||||
- Search for tag RollingFile
|
||||
- and add a tag `<RegexF,ilter>...</RegExFilter>`
|
||||
|
||||
The attribute `regex` of this tag defines the regular expression, `onMatch="DENY"` the the logger to discard those lines
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ To implement this use case you need
|
|||
For this how-to a roller with an electrical motor is required and will be controlled using A/C and a momentary switch.
|
||||
The same solutions works with other switch types (DC/AC, 2 button switch etc.).
|
||||
In this case you need to look into details and user a different Shelly configuration or adapt technical installation.
|
||||
Refer to the Alterco Shelly documentation and make sure to use proper technical installation and wiring.
|
||||
Refer to the Allterco Shelly documentation and make sure to use proper technical installation and wiring.
|
||||
Important: Electrical installation should be performed only be people knowing what they do - failures could harm you!!
|
||||
|
||||
- Shelly 2.5 to control the roller with firmware 1.9.2+
|
||||
|
@ -63,7 +63,7 @@ Latest version is always available [here](https://github.com/markus7017/myfiles/
|
|||
Ideally the upgrade could be performed on a Raspberry with
|
||||
```
|
||||
openhab-cli stop
|
||||
openhab-clu clean-cache
|
||||
openhab-cli clean-cache
|
||||
apt-get update
|
||||
apt-get upgrade
|
||||
openhab-cli start
|
||||
|
@ -133,7 +133,7 @@ It simplifies to identify the roller when you have a ton of Shelly things (belie
|
|||
|
||||
### Shelly setup - Positioning Favorites
|
||||
|
||||
With version 1.9 Alterco introduced an interesting feature called Favorites.
|
||||
With version 1.9 Allterco introduced an interesting feature called Favorites.
|
||||
Those allow to store up to 4 pre-defined positions in the device, e.g. 15%, 50%, 65%, 98%.
|
||||
Once defined you have kind of a short-cut to bring the roller to that position and those favorites are also supported by the binding (see below).
|
||||
|
||||
|
@ -171,7 +171,7 @@ There is also a dedicated channel (roller#rollerFav), which accepts this ids and
|
|||
|
||||
### Device events
|
||||
|
||||
As you might know the binding supports the Alterco Shelly CoIoT protocol.
|
||||
As you might know the binding supports the Allterco Shelly CoIoT protocol.
|
||||
The device supports so called I/O URL Actions, which are kind of a callback to an application for certain events.
|
||||
Whenever possible you should prefer CoIoT events, because they are triggered near realtime and provide way more information compared to the Action URLs.
|
||||
The binding uses those CoIoT updates as triggers, but also to update the channel data.
|
||||
|
|
|
@ -35,6 +35,7 @@ public class ShellyBindingConstants {
|
|||
|
||||
// Type names
|
||||
public static final String THING_TYPE_SHELLY1_STR = "shelly1";
|
||||
public static final String THING_TYPE_SHELLY1L_STR = "shelly1l";
|
||||
public static final String THING_TYPE_SHELLY1PM_STR = "shelly1pm";
|
||||
public static final String THING_TYPE_SHELLYEM_STR = "shellyem";
|
||||
public static final String THING_TYPE_SHELLY3EM_STR = "shellyem3"; // bad: misspelled product name, it's 3EM
|
||||
|
@ -55,8 +56,9 @@ public class ShellyBindingConstants {
|
|||
public static final String THING_TYPE_SHELLYDUO_STR = "shellybulbduo";
|
||||
public static final String THING_TYPE_SHELLYVINTAGE_STR = "shellyvintage";
|
||||
public static final String THING_TYPE_SHELLYRGBW2_PREFIX = "shellyrgbw2";
|
||||
public static final String THING_TYPE_SHELLYRGBW2_COLOR_STR = "shellyrgbw2-color";
|
||||
public static final String THING_TYPE_SHELLYRGBW2_WHITE_STR = "shellyrgbw2-white";
|
||||
public static final String THING_TYPE_SHELLYRGBW2_COLOR_STR = THING_TYPE_SHELLYRGBW2_PREFIX + "-color";
|
||||
public static final String THING_TYPE_SHELLYRGBW2_WHITE_STR = THING_TYPE_SHELLYRGBW2_PREFIX + "-white";
|
||||
public static final String THING_TYPE_SHELLYDUORGBW_STR = "shellycolorbulb";
|
||||
public static final String THING_TYPE_SHELLYHT_STR = "shellyht";
|
||||
public static final String THING_TYPE_SHELLYSMOKE_STR = "shellysmoke";
|
||||
public static final String THING_TYPE_SHELLYGAS_STR = "shellygas";
|
||||
|
@ -65,13 +67,16 @@ public class ShellyBindingConstants {
|
|||
public static final String THING_TYPE_SHELLYDOORWIN2_STR = "shellydw2";
|
||||
public static final String THING_TYPE_SHELLYEYE_STR = "shellyseye";
|
||||
public static final String THING_TYPE_SHELLYSENSE_STR = "shellysense";
|
||||
public static final String THING_TYPE_SHELLYMOTION_STR = "shellymotion";
|
||||
public static final String THING_TYPE_SHELLYBUTTON1_STR = "shellybutton1";
|
||||
public static final String THING_TYPE_SHELLYUNI_STR = "shellyuni";
|
||||
public static final String THING_TYPE_SHELLYPROTECTED_STR = "shellydevice";
|
||||
public static final String THING_TYPE_SHELLYUNKNOWN_STR = "shellyunknown";
|
||||
|
||||
// Device Types
|
||||
public static final String SHELLYDT_1 = "SHSW-1";
|
||||
public static final String SHELLYDT_1PM = "SHSW-PM";
|
||||
public static final String SHELLYDT_1L = "SHSW-L";
|
||||
public static final String SHELLYDT_SHPLG = "SHPLG-1";
|
||||
public static final String SHELLYDT_SHPLG_S = "SHPLG-S";
|
||||
public static final String SHELLYDT_SHPLG_U1 = "SHPLG-U1";
|
||||
|
@ -84,18 +89,22 @@ public class ShellyBindingConstants {
|
|||
public static final String SHELLYDT_DW = "SHDW-1";
|
||||
public static final String SHELLYDT_DW2 = "SHDW-2";
|
||||
public static final String SHELLYDT_SENSE = "SHSEN-1";
|
||||
public static final String SHELLYDT_MOTION = "SHMOS-01";
|
||||
public static final String SHELLYDT_GAS = "SHGS-1";
|
||||
public static final String SHELLYDT_DIMMER = "SHDM-1";
|
||||
public static final String SHELLYDT_DIMMER2 = "SHDM-2";
|
||||
public static final String SHELLYDT_IX3 = "SHIX3-1";
|
||||
public static final String SHELLYDT_BULB = "SHBLB-1";
|
||||
public static final String SHELLYDT_DUO = "SHBDUO-1";
|
||||
public static final String SHELLYDT_DUORGBW = "SHCB-1";
|
||||
public static final String SHELLYDT_VINTAGE = "SHVIN-1";
|
||||
public static final String SHELLYDT_RGBW2 = "SHRGBW2";
|
||||
public static final String SHELLYDT_BUTTON1 = "SHBTN-1";
|
||||
public static final String SHELLYDT_UNI = "SHUNI-1";
|
||||
|
||||
// List of all Thing Type UIDs
|
||||
public static final ThingTypeUID THING_TYPE_SHELLY1 = new ThingTypeUID(BINDING_ID, THING_TYPE_SHELLY1_STR);
|
||||
public static final ThingTypeUID THING_TYPE_SHELLY1L = new ThingTypeUID(BINDING_ID, THING_TYPE_SHELLY1L_STR);
|
||||
public static final ThingTypeUID THING_TYPE_SHELLY1PM = new ThingTypeUID(BINDING_ID, THING_TYPE_SHELLY1PM_STR);
|
||||
public static final ThingTypeUID THING_TYPE_SHELLYEM = new ThingTypeUID(BINDING_ID, THING_TYPE_SHELLYEM_STR);
|
||||
public static final ThingTypeUID THING_TYPE_SHELLY3EM = new ThingTypeUID(BINDING_ID, THING_TYPE_SHELLY3EM_STR);
|
||||
|
@ -112,6 +121,7 @@ public class ShellyBindingConstants {
|
|||
public static final ThingTypeUID THING_TYPE_SHELLYPLUGS = new ThingTypeUID(BINDING_ID, THING_TYPE_SHELLYPLUGS_STR);
|
||||
public static final ThingTypeUID THING_TYPE_SHELLYPLUGU1 = new ThingTypeUID(BINDING_ID,
|
||||
THING_TYPE_SHELLYPLUGU1_STR);
|
||||
public static final ThingTypeUID THING_TYPE_SHELLYUNI = new ThingTypeUID(BINDING_ID, THING_TYPE_SHELLYUNI_STR);
|
||||
public static final ThingTypeUID THING_TYPE_SHELLYDIMMER = new ThingTypeUID(BINDING_ID,
|
||||
THING_TYPE_SHELLYDIMMER_STR);
|
||||
public static final ThingTypeUID THING_TYPE_SHELLYDIMMER2 = new ThingTypeUID(BINDING_ID,
|
||||
|
@ -121,6 +131,8 @@ public class ShellyBindingConstants {
|
|||
public static final ThingTypeUID THING_TYPE_SHELLYDUO = new ThingTypeUID(BINDING_ID, THING_TYPE_SHELLYDUO_STR);
|
||||
public static final ThingTypeUID THING_TYPE_SHELLYVINTAGE = new ThingTypeUID(BINDING_ID,
|
||||
THING_TYPE_SHELLYVINTAGE_STR);
|
||||
public static final ThingTypeUID THING_TYPE_SHELLYDUORGBW = new ThingTypeUID(BINDING_ID,
|
||||
THING_TYPE_SHELLYDUORGBW_STR);
|
||||
public static final ThingTypeUID THING_TYPE_SHELLYHT = new ThingTypeUID(BINDING_ID, THING_TYPE_SHELLYHT_STR);
|
||||
public static final ThingTypeUID THING_TYPE_SHELLYSENSE = new ThingTypeUID(BINDING_ID, THING_TYPE_SHELLYSENSE_STR);
|
||||
public static final ThingTypeUID THING_TYPE_SHELLYSMOKE = new ThingTypeUID(BINDING_ID, THING_TYPE_SHELLYSMOKE_STR);
|
||||
|
@ -133,6 +145,7 @@ public class ShellyBindingConstants {
|
|||
public static final ThingTypeUID THING_TYPE_SHELLYBUTTON1 = new ThingTypeUID(BINDING_ID,
|
||||
THING_TYPE_SHELLYBUTTON1_STR);
|
||||
public static final ThingTypeUID THING_TYPE_SHELLYEYE = new ThingTypeUID(BINDING_ID, THING_TYPE_SHELLYEYE_STR);
|
||||
public static final ThingTypeUID THING_TYPE_SHELLMOTION = new ThingTypeUID(BINDING_ID, THING_TYPE_SHELLYMOTION_STR);
|
||||
public static final ThingTypeUID THING_TYPE_SHELLYRGBW2_COLOR = new ThingTypeUID(BINDING_ID,
|
||||
THING_TYPE_SHELLYRGBW2_COLOR_STR);
|
||||
public static final ThingTypeUID THING_TYPE_SHELLYRGBW2_WHITE = new ThingTypeUID(BINDING_ID,
|
||||
|
@ -142,16 +155,17 @@ public class ShellyBindingConstants {
|
|||
public static final ThingTypeUID THING_TYPE_SHELLYUNKNOWN = new ThingTypeUID(BINDING_ID,
|
||||
THING_TYPE_SHELLYUNKNOWN_STR);
|
||||
|
||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections.unmodifiableSet(
|
||||
Stream.of(THING_TYPE_SHELLY1, THING_TYPE_SHELLY1PM, THING_TYPE_SHELLYEM, THING_TYPE_SHELLY3EM,
|
||||
THING_TYPE_SHELLY2_RELAY, THING_TYPE_SHELLY2_ROLLER, THING_TYPE_SHELLY25_RELAY,
|
||||
THING_TYPE_SHELLY25_ROLLER, THING_TYPE_SHELLY4PRO, THING_TYPE_SHELLYPLUG, THING_TYPE_SHELLYPLUGS,
|
||||
THING_TYPE_SHELLYPLUGU1, THING_TYPE_SHELLYDIMMER, THING_TYPE_SHELLYDIMMER2, THING_TYPE_SHELLYIX3,
|
||||
THING_TYPE_SHELLYBULB, THING_TYPE_SHELLYDUO, THING_TYPE_SHELLYVINTAGE, THING_TYPE_SHELLYRGBW2_COLOR,
|
||||
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Collections
|
||||
.unmodifiableSet(Stream.of(THING_TYPE_SHELLY1, THING_TYPE_SHELLY1L, THING_TYPE_SHELLY1PM,
|
||||
THING_TYPE_SHELLYEM, THING_TYPE_SHELLY3EM, THING_TYPE_SHELLY2_RELAY, THING_TYPE_SHELLY2_ROLLER,
|
||||
THING_TYPE_SHELLY25_RELAY, THING_TYPE_SHELLY25_ROLLER, THING_TYPE_SHELLY4PRO, THING_TYPE_SHELLYPLUG,
|
||||
THING_TYPE_SHELLYPLUGS, THING_TYPE_SHELLYPLUGU1, THING_TYPE_SHELLYUNI, THING_TYPE_SHELLYDIMMER,
|
||||
THING_TYPE_SHELLYDIMMER2, THING_TYPE_SHELLYIX3, THING_TYPE_SHELLYBULB, THING_TYPE_SHELLYDUO,
|
||||
THING_TYPE_SHELLYVINTAGE, THING_TYPE_SHELLYDUORGBW, THING_TYPE_SHELLYRGBW2_COLOR,
|
||||
THING_TYPE_SHELLYRGBW2_WHITE, THING_TYPE_SHELLYHT, THING_TYPE_SHELLYSENSE, THING_TYPE_SHELLYEYE,
|
||||
THING_TYPE_SHELLYSMOKE, THING_TYPE_SHELLYGAS, THING_TYPE_SHELLYFLOOD, THING_TYPE_SHELLYDOORWIN,
|
||||
THING_TYPE_SHELLYDOORWIN2, THING_TYPE_SHELLYBUTTON1, THING_TYPE_SHELLYPROTECTED,
|
||||
THING_TYPE_SHELLYUNKNOWN).collect(Collectors.toSet()));
|
||||
THING_TYPE_SHELLYDOORWIN2, THING_TYPE_SHELLYBUTTON1, /* THING_TYPE_SHELLMOTION, */
|
||||
THING_TYPE_SHELLYPROTECTED, THING_TYPE_SHELLYUNKNOWN).collect(Collectors.toSet()));
|
||||
|
||||
// Thing Configuration Properties
|
||||
public static final String CONFIG_DEVICEIP = "deviceIp";
|
||||
|
@ -199,9 +213,11 @@ public class ShellyBindingConstants {
|
|||
public static final String CHANNEL_GROUP_ROL_CONTROL = "roller";
|
||||
public static final String CHANNEL_ROL_CONTROL_CONTROL = "control";
|
||||
public static final String CHANNEL_ROL_CONTROL_POS = "rollerpos";
|
||||
public static final String CHANNEL_ROL_CONTROL_FAV = "rollerFav";
|
||||
public static final String CHANNEL_ROL_CONTROL_TIMER = "timer";
|
||||
public static final String CHANNEL_ROL_CONTROL_STATE = "state";
|
||||
public static final String CHANNEL_ROL_CONTROL_STOPR = "stopReason";
|
||||
public static final String CHANNEL_ROL_CONTROL_SAFETY = "safety";
|
||||
|
||||
// Dimmer
|
||||
public static final String CHANNEL_GROUP_DIMMER_CONTROL = CHANNEL_GROUP_RELAY_CONTROL;
|
||||
|
@ -223,6 +239,7 @@ public class ShellyBindingConstants {
|
|||
public static final String CHANNEL_SENSOR_HUM = "humidity";
|
||||
public static final String CHANNEL_SENSOR_LUX = "lux";
|
||||
public static final String CHANNEL_SENSOR_PPM = "ppm";
|
||||
public static final String CHANNEL_SENSOR_VOLTAGE = "voltage";
|
||||
public static final String CHANNEL_SENSOR_ILLUM = "illumination";
|
||||
public static final String CHANNEL_SENSOR_VIBRATION = "vibration";
|
||||
public static final String CHANNEL_SENSOR_TILT = "tilt";
|
||||
|
@ -233,6 +250,7 @@ public class ShellyBindingConstants {
|
|||
public static final String CHANNEL_SENSOR_SSTATE = "status"; // Shelly Gas
|
||||
public static final String CHANNEL_SENSOR_ALARM_STATE = "alarmState";
|
||||
public static final String CHANNEL_SENSOR_MOTION = "motion";
|
||||
public static final String CHANNEL_SENSOR_MOTION_TS = "motionTimestamp";
|
||||
public static final String CHANNEL_SENSOR_ERROR = "lastError";
|
||||
|
||||
// External sensors for Shelly1/1PM
|
||||
|
@ -290,12 +308,18 @@ public class ShellyBindingConstants {
|
|||
// Button/xi3
|
||||
public static final String CHANNEL_GROUP_STATUS = "status";
|
||||
public static final String CHANNEL_STATUS_EVENTTYPE = "lastEvent";
|
||||
public static final String CHANNEL_STATUS_EVENTTYPE1 = CHANNEL_STATUS_EVENTTYPE + "1";
|
||||
public static final String CHANNEL_STATUS_EVENTTYPE2 = CHANNEL_STATUS_EVENTTYPE + "2";
|
||||
public static final String CHANNEL_STATUS_EVENTCOUNT = "eventCount";
|
||||
public static final String CHANNEL_STATUS_EVENTCOUNT1 = CHANNEL_STATUS_EVENTCOUNT + "1";
|
||||
public static final String CHANNEL_STATUS_EVENTCOUNT2 = CHANNEL_STATUS_EVENTCOUNT + "2";
|
||||
|
||||
// General
|
||||
public static final String CHANNEL_LAST_UPDATE = "lastUpdate";
|
||||
public static final String CHANNEL_EVENT_TRIGGER = "event";
|
||||
public static final String CHANNEL_BUTTON_TRIGGER = "button";
|
||||
public static final String CHANNEL_BUTTON_TRIGGER1 = CHANNEL_BUTTON_TRIGGER + "1";
|
||||
public static final String CHANNEL_BUTTON_TRIGGER2 = CHANNEL_BUTTON_TRIGGER + "2";
|
||||
|
||||
public static final String SERVICE_TYPE = "_http._tcp.local.";
|
||||
public static final String SHELLY_API_MIN_FWVERSION = "v1.5.7";// v1.5.7+
|
||||
|
@ -324,11 +348,11 @@ public class ShellyBindingConstants {
|
|||
|
||||
// Formatting: Number of scaling digits
|
||||
public static final int DIGITS_NONE = 0;
|
||||
public static final int DIGITS_WATT = 1;
|
||||
public static final int DIGITS_WATT = 2;
|
||||
public static final int DIGITS_KWH = 3;
|
||||
public static final int DIGITS_VOLT = 1;
|
||||
public static final int DIGITS_TEMP = 1;
|
||||
public static final int DIGITS_LUX = 1;
|
||||
public static final int DIGITS_LUX = 0;
|
||||
public static final int DIGITS_PERCENT = 1;
|
||||
|
||||
public static final int SHELLY_API_TIMEOUT_MS = 5000;
|
||||
|
|
|
@ -16,21 +16,19 @@ import static org.openhab.binding.shelly.internal.ShellyBindingConstants.*;
|
|||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.eclipse.jetty.util.ConcurrentHashSet;
|
||||
import org.openhab.binding.shelly.internal.coap.ShellyCoapServer;
|
||||
import org.openhab.binding.shelly.internal.config.ShellyBindingConfiguration;
|
||||
import org.openhab.binding.shelly.internal.handler.ShellyBaseHandler;
|
||||
import org.openhab.binding.shelly.internal.handler.ShellyLightHandler;
|
||||
import org.openhab.binding.shelly.internal.handler.ShellyProtectedHandler;
|
||||
import org.openhab.binding.shelly.internal.handler.ShellyRelayHandler;
|
||||
import org.openhab.binding.shelly.internal.util.ShellyTranslationProvider;
|
||||
import org.openhab.binding.shelly.internal.provider.ShellyTranslationProvider;
|
||||
import org.openhab.binding.shelly.internal.util.ShellyUtils;
|
||||
import org.openhab.core.i18n.LocaleProvider;
|
||||
import org.openhab.core.i18n.TranslationProvider;
|
||||
import org.openhab.core.io.net.http.HttpClientFactory;
|
||||
import org.openhab.core.net.HttpServiceUtil;
|
||||
import org.openhab.core.net.NetworkAddressService;
|
||||
|
@ -60,7 +58,7 @@ public class ShellyHandlerFactory extends BaseThingHandlerFactory {
|
|||
private final HttpClient httpClient;
|
||||
private final ShellyTranslationProvider messages;
|
||||
private final ShellyCoapServer coapServer;
|
||||
private final Set<ShellyBaseHandler> deviceListeners = new ConcurrentHashSet<>();
|
||||
private final Set<ShellyBaseHandler> deviceListeners = ConcurrentHashMap.newKeySet();
|
||||
private static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = ShellyBindingConstants.SUPPORTED_THING_TYPES_UIDS;
|
||||
private ShellyBindingConfiguration bindingConfig = new ShellyBindingConfiguration();
|
||||
private String localIP = "";
|
||||
|
@ -75,14 +73,18 @@ public class ShellyHandlerFactory extends BaseThingHandlerFactory {
|
|||
*/
|
||||
@Activate
|
||||
public ShellyHandlerFactory(@Reference NetworkAddressService networkAddressService,
|
||||
@Reference LocaleProvider localeProvider, @Reference TranslationProvider i18nProvider,
|
||||
@Reference HttpClientFactory httpClientFactory, ComponentContext componentContext,
|
||||
Map<String, Object> configProperties) {
|
||||
@Reference ShellyTranslationProvider translationProvider, @Reference HttpClientFactory httpClientFactory,
|
||||
ComponentContext componentContext, Map<String, Object> configProperties) {
|
||||
logger.debug("Activate Shelly HandlerFactory");
|
||||
super.activate(componentContext);
|
||||
messages = translationProvider;
|
||||
// Save bindingConfig & pass it to all registered listeners
|
||||
bindingConfig.updateFromProperties(configProperties);
|
||||
|
||||
messages = new ShellyTranslationProvider(bundleContext.getBundle(), i18nProvider, localeProvider);
|
||||
localIP = ShellyUtils.getString(networkAddressService.getPrimaryIpv4HostAddress());
|
||||
localIP = bindingConfig.localIP;
|
||||
if (localIP.isEmpty()) {
|
||||
localIP = ShellyUtils.getString(networkAddressService.getPrimaryIpv4HostAddress());
|
||||
}
|
||||
if (localIP.isEmpty()) {
|
||||
logger.warn("{}", messages.get("message.init.noipaddress"));
|
||||
}
|
||||
|
@ -95,9 +97,6 @@ public class ShellyHandlerFactory extends BaseThingHandlerFactory {
|
|||
logger.debug("Using OH HTTP port {}", httpPort);
|
||||
|
||||
this.coapServer = new ShellyCoapServer();
|
||||
|
||||
// Save bindingConfig & pass it to all registered listeners
|
||||
bindingConfig.updateFromProperties(configProperties);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -116,9 +115,10 @@ public class ShellyHandlerFactory extends BaseThingHandlerFactory {
|
|||
thingTypeUID.toString());
|
||||
handler = new ShellyProtectedHandler(thing, messages, bindingConfig, coapServer, localIP, httpPort,
|
||||
httpClient);
|
||||
} else if (thingType.equals(THING_TYPE_SHELLYBULB.getId()) || thingType.equals(THING_TYPE_SHELLYDUO.getId())
|
||||
|| thingType.equals(THING_TYPE_SHELLYRGBW2_COLOR.getId())
|
||||
|| thingType.equals(THING_TYPE_SHELLYRGBW2_WHITE.getId())) {
|
||||
} else if (thingType.equals(THING_TYPE_SHELLYBULB_STR) || thingType.equals(THING_TYPE_SHELLYDUO_STR)
|
||||
|| thingType.equals(THING_TYPE_SHELLYRGBW2_COLOR_STR)
|
||||
|| thingType.equals(THING_TYPE_SHELLYRGBW2_WHITE_STR)
|
||||
|| thingType.equals(THING_TYPE_SHELLYDUORGBW_STR)) {
|
||||
logger.debug("{}: Create new thing of type {} using ShellyLightHandler", thing.getLabel(),
|
||||
thingTypeUID.toString());
|
||||
handler = new ShellyLightHandler(thing, messages, bindingConfig, coapServer, localIP, httpPort, httpClient);
|
||||
|
|
|
@ -92,7 +92,8 @@ public class ShellyApiException extends Exception {
|
|||
public boolean isTimeout() {
|
||||
Class<?> extype = !isEmpty() ? getCauseClass() : null;
|
||||
return (extype != null) && ((extype == TimeoutException.class) || (extype == ExecutionException.class)
|
||||
|| (extype == InterruptedException.class) || getMessage().toLowerCase().contains("timeout"));
|
||||
|| (extype == InterruptedException.class)
|
||||
|| nonNullString(getMessage()).toLowerCase().contains("timeout"));
|
||||
}
|
||||
|
||||
public boolean isHttpAccessUnauthorized() {
|
||||
|
@ -125,7 +126,7 @@ public class ShellyApiException extends Exception {
|
|||
|
||||
private Class<?> getCauseClass() {
|
||||
Throwable cause = getCause();
|
||||
if (getCause() != null) {
|
||||
if (cause != null) {
|
||||
return cause.getClass();
|
||||
}
|
||||
return ShellyApiException.class;
|
||||
|
|
|
@ -110,6 +110,7 @@ public class ShellyApiJsonDTO {
|
|||
public static final String SHELLY_BTNT_MOMENTARY = "momentary";
|
||||
public static final String SHELLY_BTNT_MOM_ON_RELEASE = "momentary_on_release";
|
||||
public static final String SHELLY_BTNT_ONE_BUTTON = "one_button";
|
||||
public static final String SHELLY_BTNT_TWO_BUTTON = "dual_button";
|
||||
public static final String SHELLY_BTNT_TOGGLE = "toggle";
|
||||
public static final String SHELLY_BTNT_EDGE = "edge";
|
||||
public static final String SHELLY_BTNT_DETACHED = "detached";
|
||||
|
@ -334,6 +335,10 @@ public class ShellyApiJsonDTO {
|
|||
public String defaultState; // Accepted values: off, on, last, switch
|
||||
@SerializedName("btn_type")
|
||||
public String btnType; // Accepted values: momentary, toggle, edge, detached - // see SHELLY_BTNT_xxx
|
||||
@SerializedName("btn1_type") // Shelly 1L
|
||||
public String btnType1;
|
||||
@SerializedName("btn2_type") // Shelly 1L
|
||||
public String btnType2;
|
||||
@SerializedName("has_timer")
|
||||
public Boolean hasTimer; // Whether a timer is currently armed for this channel
|
||||
@SerializedName("auto_on")
|
||||
|
@ -390,6 +395,10 @@ public class ShellyApiJsonDTO {
|
|||
public String pushShortUrl; // short push button event
|
||||
@SerializedName("btn_type")
|
||||
public String btnType; // Accepted values: momentary, toggle, edge, detached - // see SHELLY_BTNT_xxx
|
||||
@SerializedName("btn1_type")
|
||||
public String btnType1; // Accepted values: momentary, toggle, edge, detached - // see SHELLY_BTNT_xxx
|
||||
@SerializedName("btn2_type")
|
||||
public String btnType2; // Accepted values: momentary, toggle, edge, detached - // see SHELLY_BTNT_xxx
|
||||
@SerializedName("swap_inputs")
|
||||
public Integer swapInputs; // 0=no
|
||||
}
|
||||
|
@ -438,6 +447,32 @@ public class ShellyApiJsonDTO {
|
|||
public Boolean positioning;
|
||||
}
|
||||
|
||||
public static class ShellySettingsRgbwLight {
|
||||
public String name;
|
||||
public Boolean ison; // true: output is ON
|
||||
public Integer brightness;
|
||||
public Integer transition;
|
||||
public String default_state;
|
||||
@SerializedName("auto_on")
|
||||
public Double autoOn; // Automatic flip back timer, seconds. Will engage after turning Shelly1 OFF.
|
||||
@SerializedName("auto_off")
|
||||
public Double autoOff; // Automatic flip back timer, seconds. Will engage after turning Shelly1 ON.
|
||||
public Boolean schedule;
|
||||
@SerializedName("btn_type")
|
||||
public String btnType; // Accepted values: momentary, toggle, edge, detached - // see SHELLY_BTNT_xxx
|
||||
@SerializedName("btn_reverse")
|
||||
public Integer btnReverse; // Accepted values: momentary, toggle, edge, detached - // see SHELLY_BTNT_xxx
|
||||
@SerializedName("out_on_url")
|
||||
public String outOnUrl; // output is activated
|
||||
@SerializedName("out_off_url")
|
||||
public String outOffUrl; // output is deactivated
|
||||
}
|
||||
|
||||
public static class ShellyFavPos { // FW 1.9.2+ in roller mode
|
||||
public String name;
|
||||
public Integer pos;
|
||||
}
|
||||
|
||||
public static class ShellyInputState {
|
||||
public Integer input;
|
||||
|
||||
|
@ -506,11 +541,6 @@ public class ShellyApiJsonDTO {
|
|||
@SerializedName("sleep_mode")
|
||||
public ShellySensorSleepMode sleepMode; // FW 1.6
|
||||
|
||||
// @SerializedName("ext_temperature")
|
||||
// public ShellyStatusSensor.ShellyExtTemperature extTemperature; // Shelly 1/1PM: sensor values
|
||||
// @SerializedName("ext_humidity")
|
||||
// public ShellyStatusSensor.ShellyExtHumidity extHumidity; // Shelly 1/1PM: sensor values
|
||||
|
||||
public String timezone;
|
||||
public Double lat;
|
||||
public Double lng;
|
||||
|
@ -531,6 +561,7 @@ public class ShellyApiJsonDTO {
|
|||
|
||||
public ArrayList<ShellySettingsRelay> relays;
|
||||
public ArrayList<ShellySettingsDimmer> dimmers;
|
||||
public ArrayList<ShellySettingsRgbwLight> lights;
|
||||
public ArrayList<ShellySettingsEMeter> emeters;
|
||||
public ArrayList<ShellySettingsInput> inputs; // ix3
|
||||
|
||||
|
@ -580,6 +611,11 @@ public class ShellyApiJsonDTO {
|
|||
public String alarmMidUrl; // URL reports middle alarm
|
||||
@SerializedName("alarm_heavy_url")
|
||||
public String alarmHeavyfUrl; // URL reports heavy alarm
|
||||
|
||||
// Roller with FW 1.9.2+
|
||||
@SerializedName("favorites_enabled")
|
||||
public Boolean favoritesEnabled;
|
||||
public ArrayList<ShellyFavPos> favorites;
|
||||
}
|
||||
|
||||
public static class ShellySettingsAttributes {
|
||||
|
@ -651,7 +687,7 @@ public class ShellyApiJsonDTO {
|
|||
@SerializedName("btn_type")
|
||||
public String btnType;
|
||||
|
||||
// included attributes not yet processed
|
||||
// attributes not yet processed
|
||||
// public String name;
|
||||
// @SerializedName("btn_reverse")
|
||||
// public Integer btnReverse;
|
||||
|
@ -727,6 +763,7 @@ public class ShellyApiJsonDTO {
|
|||
public String mac; // MAC
|
||||
public ArrayList<ShellyShortStatusRelay> relays; // relay status
|
||||
public ArrayList<ShellySettingsMeter> meters; // current meter value
|
||||
public ArrayList<ShellyInputState> inputs; // Firmware 1.5.6+
|
||||
|
||||
@SerializedName("ext_temperature")
|
||||
public ShellyStatusSensor.ShellyExtTemperature extTemperature; // Shelly 1/1PM: sensor values
|
||||
|
@ -838,6 +875,14 @@ public class ShellyApiJsonDTO {
|
|||
@SerializedName("is_valid")
|
||||
public Boolean isValid; // whether the internal sensor is operating properly
|
||||
public String state; // Shelly Door/Window
|
||||
|
||||
// Shelly Motion
|
||||
public Boolean motion;
|
||||
public Boolean vibration;
|
||||
@SerializedName("timestamp")
|
||||
public Long motionTimestamp;
|
||||
@SerializedName("active")
|
||||
public Boolean motionActive;
|
||||
}
|
||||
|
||||
public static class ShellySensorLux {
|
||||
|
@ -880,13 +925,17 @@ public class ShellyApiJsonDTO {
|
|||
public ShellyShortHum sensor1;
|
||||
}
|
||||
|
||||
public static class ShellyADC {
|
||||
public Double voltage;
|
||||
}
|
||||
|
||||
public ShellySensorTmp tmp;
|
||||
public ShellySensorHum hum;
|
||||
public ShellySensorLux lux;
|
||||
public ShellySensorAccel accel;
|
||||
public ShellySensorBat bat;
|
||||
@SerializedName("sensor")
|
||||
public ShellySensorState contact;
|
||||
public ShellySensorState sensor;
|
||||
public Boolean smoke; // SHelly Smoke
|
||||
public Boolean flood; // Shelly Flood: true = flood condition detected
|
||||
@SerializedName("rain_sensor")
|
||||
|
@ -914,6 +963,9 @@ public class ShellyApiJsonDTO {
|
|||
@SerializedName("connect_retries")
|
||||
public Integer connectRetries;
|
||||
public ArrayList<ShellyInputState> inputs; // Firmware 1.5.6+
|
||||
|
||||
// Shelly UNI FW 1.9+
|
||||
public ArrayList<ShellyADC> adcs;
|
||||
}
|
||||
|
||||
public static class ShellySettingsSmoke {
|
||||
|
@ -985,10 +1037,14 @@ public class ShellyApiJsonDTO {
|
|||
public Boolean ison;
|
||||
public Double power;
|
||||
public Boolean overpower;
|
||||
@SerializedName("auto_on")
|
||||
public Double autoOn; // see above
|
||||
@SerializedName("auto_off")
|
||||
public Double autoOff; // see above
|
||||
@SerializedName("has_timer")
|
||||
public Boolean hasTimer;
|
||||
@SerializedName("timer_started")
|
||||
public Integer timerStarted;
|
||||
@SerializedName("timer_duration")
|
||||
public Integer timerDuration;
|
||||
@SerializedName("timer_remaining")
|
||||
public Integer timerRemaining;
|
||||
|
||||
public Integer red; // red brightness, 0..255, applies in mode="color"
|
||||
public Integer green; // green brightness, 0..255, applies in mode="color"
|
||||
|
@ -1003,18 +1059,10 @@ public class ShellyApiJsonDTO {
|
|||
|
||||
public static class ShellyStatusLight {
|
||||
public Boolean ison; // Whether output channel is on or off
|
||||
public ArrayList<ShellyStatusLightChannel> lights;
|
||||
public ArrayList<ShellySettingsMeter> meters;
|
||||
public Integer input;
|
||||
|
||||
// not yet used:
|
||||
// public String mode; // COLOR or WHITE
|
||||
// public Boolean has_update;
|
||||
// public ShellySettingsUpdate update;
|
||||
// public ShellySettingsWiFiNetwork wifi_sta; // WiFi client configuration. See
|
||||
// /settings/sta for details
|
||||
// public ShellyStatusCloud cloud;
|
||||
// public ShellyStatusMqtt mqtt;
|
||||
public ArrayList<ShellyStatusLightChannel> lights;
|
||||
public ArrayList<ShellySettingsMeter> meters;
|
||||
}
|
||||
|
||||
public static class ShellySenseKeyCode {
|
||||
|
|
|
@ -18,19 +18,18 @@ import static org.openhab.binding.shelly.internal.util.ShellyUtils.*;
|
|||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellySettingsDimmer;
|
||||
import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellySettingsGlobal;
|
||||
import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellySettingsInput;
|
||||
import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellySettingsRelay;
|
||||
import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellySettingsRgbwLight;
|
||||
import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellySettingsStatus;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
|
||||
/**
|
||||
* The {@link ShellyDeviceProfile} creates a device profile based on the settings returned from the API's /settings
|
||||
|
@ -68,6 +67,7 @@ public class ShellyDeviceProfile {
|
|||
public int numRollers = 0; // number of Rollers, usually 1
|
||||
public boolean isRoller = false; // true for Shelly2 in roller mode
|
||||
public boolean isDimmer = false; // true for a Shelly Dimmer (SHDM-1)
|
||||
public int numInputs = 0; // number of inputs
|
||||
|
||||
public int numMeters = 0;
|
||||
public boolean isEMeter = false; // true for ShellyEM/3EM
|
||||
|
@ -101,14 +101,10 @@ public class ShellyDeviceProfile {
|
|||
|
||||
initialized = false;
|
||||
|
||||
try {
|
||||
initFromThingType(thingType);
|
||||
settingsJson = json;
|
||||
settings = Objects.requireNonNull(gson.fromJson(json, ShellySettingsGlobal.class));
|
||||
} catch (IllegalArgumentException | JsonSyntaxException e) {
|
||||
throw new ShellyApiException(
|
||||
thingName + ": Unable to transform settings JSON " + e.toString() + ", json='" + json + "'", e);
|
||||
}
|
||||
initFromThingType(thingType);
|
||||
settingsJson = json;
|
||||
ShellySettingsGlobal gs = fromJson(gson, json, ShellySettingsGlobal.class);
|
||||
settings = gs; // only update when no exception
|
||||
|
||||
// General settings
|
||||
deviceType = getString(settings.device.type);
|
||||
|
@ -131,8 +127,10 @@ public class ShellyDeviceProfile {
|
|||
numRelays = 0;
|
||||
}
|
||||
isDimmer = deviceType.equalsIgnoreCase(SHELLYDT_DIMMER) || deviceType.equalsIgnoreCase(SHELLYDT_DIMMER2);
|
||||
isRoller = mode.equalsIgnoreCase(SHELLY_MODE_ROLLER);
|
||||
hasRelays = (numRelays > 0) || isDimmer;
|
||||
numRollers = getInteger(settings.device.numRollers);
|
||||
numInputs = settings.inputs != null ? settings.inputs.size() : hasRelays ? isRoller ? 2 : 1 : 0;
|
||||
|
||||
isEMeter = settings.emeters != null;
|
||||
numMeters = !isEMeter ? getInteger(settings.device.numMeters) : getInteger(settings.device.numEMeters);
|
||||
|
@ -140,18 +138,17 @@ public class ShellyDeviceProfile {
|
|||
// RGBW2 doesn't report, but has one
|
||||
numMeters = inColor ? 1 : getInteger(settings.device.numOutputs);
|
||||
}
|
||||
isRoller = mode.equalsIgnoreCase(SHELLY_MODE_ROLLER);
|
||||
|
||||
if (settings.sleepMode != null) {
|
||||
// Sensor, usally 12h
|
||||
// Sensor, usually 12h, H&T in USB mode 10min
|
||||
updatePeriod = getString(settings.sleepMode.unit).equalsIgnoreCase("m") ? settings.sleepMode.period * 60 // minutes
|
||||
: settings.sleepMode.period * 3600; // hours
|
||||
updatePeriod += 600; // give 10min extra
|
||||
updatePeriod += 60; // give 1min extra
|
||||
} else if ((settings.coiot != null) && (settings.coiot.updatePeriod != null)) {
|
||||
// Derive from CoAP update interval, usually 2*15+5s=50sec -> 70sec
|
||||
updatePeriod = Math.max(UPDATE_SETTINGS_INTERVAL_SECONDS, 3 * getInteger(settings.coiot.updatePeriod)) + 10;
|
||||
// Derive from CoAP update interval, usually 2*15+10s=40sec -> 70sec
|
||||
updatePeriod = Math.max(UPDATE_SETTINGS_INTERVAL_SECONDS, 2 * getInteger(settings.coiot.updatePeriod)) + 10;
|
||||
} else {
|
||||
updatePeriod = 2 * UPDATE_SETTINGS_INTERVAL_SECONDS + 10;
|
||||
updatePeriod = UPDATE_SETTINGS_INTERVAL_SECONDS + 10;
|
||||
}
|
||||
|
||||
initialized = true;
|
||||
|
@ -178,7 +175,8 @@ public class ShellyDeviceProfile {
|
|||
}
|
||||
|
||||
isBulb = thingType.equals(THING_TYPE_SHELLYBULB_STR);
|
||||
isDuo = thingType.equals(THING_TYPE_SHELLYDUO_STR) || thingType.equals(THING_TYPE_SHELLYVINTAGE_STR);
|
||||
isDuo = thingType.equals(THING_TYPE_SHELLYDUO_STR) || thingType.equals(THING_TYPE_SHELLYVINTAGE_STR)
|
||||
|| thingType.equals(THING_TYPE_SHELLYDUORGBW_STR);
|
||||
isRGBW2 = thingType.startsWith(THING_TYPE_SHELLYRGBW2_PREFIX);
|
||||
isLight = isBulb || isDuo || isRGBW2;
|
||||
if (isLight) {
|
||||
|
@ -189,14 +187,29 @@ public class ShellyDeviceProfile {
|
|||
boolean isFlood = thingType.equals(THING_TYPE_SHELLYFLOOD_STR);
|
||||
boolean isSmoke = thingType.equals(THING_TYPE_SHELLYSMOKE_STR);
|
||||
boolean isGas = thingType.equals(THING_TYPE_SHELLYGAS_STR);
|
||||
boolean isUNI = thingType.equals(THING_TYPE_SHELLYUNI_STR);
|
||||
boolean isMotion = thingType.equals(THING_TYPE_SHELLYMOTION_STR);
|
||||
isHT = thingType.equals(THING_TYPE_SHELLYHT_STR);
|
||||
isDW = thingType.equals(THING_TYPE_SHELLYDOORWIN_STR) || thingType.equals(THING_TYPE_SHELLYDOORWIN2_STR);
|
||||
isSense = thingType.equals(THING_TYPE_SHELLYSENSE_STR);
|
||||
isIX3 = thingType.equals(THING_TYPE_SHELLYIX3_STR);
|
||||
isButton = thingType.equals(THING_TYPE_SHELLYBUTTON1_STR);
|
||||
isSensor = isHT || isFlood || isDW || isSmoke || isGas || isButton || isSense;
|
||||
hasBattery = isHT || isFlood || isDW || isSmoke || isButton; // we assume that Sense is connected to // the
|
||||
// charger
|
||||
isSensor = isHT || isFlood || isDW || isSmoke || isGas || isButton || isUNI || isSense;
|
||||
hasBattery = isHT || isFlood || isDW || isSmoke || isButton || isMotion; // we assume that Sense is connected to
|
||||
// the charger
|
||||
}
|
||||
|
||||
public void updateFromStatus(ShellySettingsStatus status) {
|
||||
if (hasRelays) {
|
||||
// Dimmer-2 doesn't report inputs under /settings, only on /status, we need to update that info after
|
||||
// initialization
|
||||
if (status.inputs != null) {
|
||||
numInputs = status.inputs.size();
|
||||
}
|
||||
} else if (status.input != null) {
|
||||
// RGBW2
|
||||
numInputs = 1;
|
||||
}
|
||||
}
|
||||
|
||||
public String getControlGroup(int i) {
|
||||
|
@ -208,11 +221,13 @@ public class ShellyDeviceProfile {
|
|||
if (isDimmer) {
|
||||
return CHANNEL_GROUP_DIMMER_CONTROL;
|
||||
} else if (isRoller) {
|
||||
return numRollers == 1 ? CHANNEL_GROUP_ROL_CONTROL : CHANNEL_GROUP_ROL_CONTROL + idx;
|
||||
return numRollers <= 1 ? CHANNEL_GROUP_ROL_CONTROL : CHANNEL_GROUP_ROL_CONTROL + idx;
|
||||
} else if (isDimmer) {
|
||||
return CHANNEL_GROUP_RELAY_CONTROL;
|
||||
} else if (hasRelays) {
|
||||
return numRelays == 1 ? CHANNEL_GROUP_RELAY_CONTROL : CHANNEL_GROUP_RELAY_CONTROL + idx;
|
||||
return numRelays <= 1 ? CHANNEL_GROUP_RELAY_CONTROL : CHANNEL_GROUP_RELAY_CONTROL + idx;
|
||||
} else if (isLight) {
|
||||
return numRelays == 1 ? CHANNEL_GROUP_LIGHT_CONTROL : CHANNEL_GROUP_LIGHT_CONTROL + idx;
|
||||
return numRelays <= 1 ? CHANNEL_GROUP_LIGHT_CONTROL : CHANNEL_GROUP_LIGHT_CONTROL + idx;
|
||||
} else if (isButton) {
|
||||
return CHANNEL_GROUP_STATUS;
|
||||
} else if (isSensor) {
|
||||
|
@ -239,14 +254,17 @@ public class ShellyDeviceProfile {
|
|||
}
|
||||
}
|
||||
|
||||
public String getInputChannel(int i) {
|
||||
public String getInputSuffix(int i) {
|
||||
int idx = i + 1; // channel names are 1-based
|
||||
if (isRGBW2 || isIX3) {
|
||||
return CHANNEL_INPUT; // RGBW2 has only 1 channel
|
||||
return ""; // RGBW2 has only 1 channel
|
||||
} else if (isRoller || isDimmer) {
|
||||
// Roller has 2 relays, but it will be mapped to 1 roller with 2 inputs
|
||||
return String.valueOf(idx);
|
||||
} else if (hasRelays) {
|
||||
return CHANNEL_INPUT + idx;
|
||||
return (numRelays) == 1 && (numInputs >= 2) ? String.valueOf(idx) : "";
|
||||
}
|
||||
return CHANNEL_INPUT;
|
||||
return "";
|
||||
}
|
||||
|
||||
public boolean inButtonMode(int idx) {
|
||||
|
@ -257,25 +275,43 @@ public class ShellyDeviceProfile {
|
|||
String btnType = "";
|
||||
if (isButton) {
|
||||
return true;
|
||||
} else if (isIX3) {
|
||||
if ((settings.inputs != null) && (idx >= 0) && (idx < settings.inputs.size())) {
|
||||
ShellySettingsInput input = settings.inputs.get(idx);
|
||||
btnType = input.btnType;
|
||||
}
|
||||
} else if (isIX3 && (settings.inputs != null) && (idx < settings.inputs.size())) {
|
||||
ShellySettingsInput input = settings.inputs.get(idx);
|
||||
btnType = getString(input.btnType);
|
||||
} else if (isDimmer) {
|
||||
if ((settings.dimmers != null) && (idx >= 0) && (idx < settings.dimmers.size())) {
|
||||
ShellySettingsDimmer dimmer = settings.dimmers.get(idx);
|
||||
if (settings.dimmers != null) {
|
||||
ShellySettingsDimmer dimmer = settings.dimmers.get(0);
|
||||
btnType = dimmer.btnType;
|
||||
}
|
||||
} else if ((settings.relays != null) && (idx >= 0) && (idx < settings.relays.size())) {
|
||||
ShellySettingsRelay relay = settings.relays.get(idx);
|
||||
btnType = relay.btnType;
|
||||
} else if (settings.relays != null) {
|
||||
if (numRelays == 1) {
|
||||
ShellySettingsRelay relay = settings.relays.get(0);
|
||||
if (relay.btnType != null) {
|
||||
btnType = getString(relay.btnType);
|
||||
} else {
|
||||
// Shelly 1L has 2 inputs
|
||||
btnType = idx == 0 ? getString(relay.btnType1) : getString(relay.btnType2);
|
||||
}
|
||||
} else if (idx < settings.relays.size()) {
|
||||
// only one input channel
|
||||
ShellySettingsRelay relay = settings.relays.get(idx);
|
||||
btnType = getString(relay.btnType);
|
||||
}
|
||||
} else if (isRGBW2 && (settings.lights != null) && (idx < settings.lights.size())) {
|
||||
ShellySettingsRgbwLight light = settings.lights.get(idx);
|
||||
btnType = light.btnType;
|
||||
}
|
||||
|
||||
if (btnType.equals(SHELLY_BTNT_MOMENTARY) || btnType.equals(SHELLY_BTNT_MOM_ON_RELEASE)
|
||||
|| btnType.equals(SHELLY_BTNT_DETACHED) || btnType.equals(SHELLY_BTNT_ONE_BUTTON)) {
|
||||
return true;
|
||||
logger.trace("{}: Checking for trigger, button-type[{}] is {}", thingName, idx, btnType);
|
||||
return btnType.equalsIgnoreCase(SHELLY_BTNT_MOMENTARY) || btnType.equalsIgnoreCase(SHELLY_BTNT_MOM_ON_RELEASE)
|
||||
|| btnType.equalsIgnoreCase(SHELLY_BTNT_ONE_BUTTON) || btnType.equalsIgnoreCase(SHELLY_BTNT_TWO_BUTTON);
|
||||
}
|
||||
|
||||
public int getRollerFav(int id) {
|
||||
if ((id >= 0) && getBool(settings.favoritesEnabled) && (settings.favorites != null)
|
||||
&& (id < settings.favorites.size())) {
|
||||
return settings.favorites.get(id).pos;
|
||||
}
|
||||
return false;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -87,7 +87,7 @@ public class ShellyEventServlet extends HttpServlet {
|
|||
}
|
||||
|
||||
try {
|
||||
path = request.getRequestURI().toLowerCase();
|
||||
path = getString(request.getRequestURI()).toLowerCase();
|
||||
String ipAddress = request.getHeader("HTTP_X_FORWARDED_FOR");
|
||||
if (ipAddress == null) {
|
||||
ipAddress = request.getRemoteAddr();
|
||||
|
|
|
@ -135,9 +135,9 @@ public class ShellyHttpApi {
|
|||
try {
|
||||
json = request(SHELLY_URL_STATUS);
|
||||
// Dimmer2 returns invalid json type for loaderror :-(
|
||||
json = json.replace("\"loaderror\":0,", "\"loaderror\":false,");
|
||||
json = json.replace("\"loaderror\":1,", "\"loaderror\":true,");
|
||||
ShellySettingsStatus status = gson.fromJson(json, ShellySettingsStatus.class);
|
||||
json = getString(json.replace("\"loaderror\":0,", "\"loaderror\":false,"));
|
||||
json = getString(json.replace("\"loaderror\":1,", "\"loaderror\":true,"));
|
||||
ShellySettingsStatus status = fromJson(gson, json, ShellySettingsStatus.class);
|
||||
status.json = json;
|
||||
return status;
|
||||
} catch (JsonSyntaxException e) {
|
||||
|
@ -188,9 +188,9 @@ public class ShellyHttpApi {
|
|||
status.tmp.tC = status.tmp.units.equals(SHELLY_TEMP_CELSIUS) ? status.tmp.value
|
||||
: ImperialUnits.FAHRENHEIT.getConverterTo(SIUnits.CELSIUS).convert(getDouble(status.tmp.value))
|
||||
.doubleValue();
|
||||
status.tmp.tF = status.tmp.units.equals(SHELLY_TEMP_FAHRENHEIT) ? status.tmp.value
|
||||
: SIUnits.CELSIUS.getConverterTo(ImperialUnits.FAHRENHEIT).convert(getDouble(status.tmp.value))
|
||||
.doubleValue();
|
||||
double f = (double) SIUnits.CELSIUS.getConverterTo(ImperialUnits.FAHRENHEIT)
|
||||
.convert(getDouble(status.tmp.value));
|
||||
status.tmp.tF = status.tmp.units.equals(SHELLY_TEMP_FAHRENHEIT) ? status.tmp.value : f;
|
||||
}
|
||||
if ((status.charger == null) && (status.externalPower != null)) {
|
||||
// SHelly H&T uses external_power, Sense uses charger
|
||||
|
@ -286,11 +286,12 @@ public class ShellyHttpApi {
|
|||
keyList = keyList.replaceAll(java.util.regex.Pattern.quote("["), "{ \"id\":");
|
||||
keyList = keyList.replaceAll(java.util.regex.Pattern.quote("]"), "} ");
|
||||
String json = "{\"key_codes\" : [" + keyList + "] }";
|
||||
|
||||
ShellySendKeyList codes = gson.fromJson(json, ShellySendKeyList.class);
|
||||
ShellySendKeyList codes = fromJson(gson, json, ShellySendKeyList.class);
|
||||
Map<String, String> list = new HashMap<>();
|
||||
for (ShellySenseKeyCode key : codes.keyCodes) {
|
||||
list.put(key.id, key.name);
|
||||
if (key != null) {
|
||||
list.put(key.id, key.name);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
@ -318,9 +319,6 @@ public class ShellyHttpApi {
|
|||
url = url + "&" + "id=" + keyCode;
|
||||
} else if (type.equals(SHELLY_IR_CODET_PRONTO)) {
|
||||
String code = Base64.getEncoder().encodeToString(keyCode.getBytes(StandardCharsets.UTF_8));
|
||||
if (code == null) {
|
||||
throw new IllegalArgumentException("Unable to BASE64 encode the pronto code: " + keyCode);
|
||||
}
|
||||
url = url + "&" + SHELLY_IR_CODET_PRONTO + "=" + code;
|
||||
} else if (type.equals(SHELLY_IR_CODET_PRONTO_HEX)) {
|
||||
url = url + "&" + SHELLY_IR_CODET_PRONTO_HEX + "=" + keyCode;
|
||||
|
@ -470,12 +468,8 @@ public class ShellyHttpApi {
|
|||
* @param uri: URI (e.g. "/settings")
|
||||
*/
|
||||
public <T> T callApi(String uri, Class<T> classOfT) throws ShellyApiException {
|
||||
try {
|
||||
String json = request(uri);
|
||||
return gson.fromJson(json, classOfT);
|
||||
} catch (JsonSyntaxException e) {
|
||||
throw new ShellyApiException("Unable to convert JSON", e);
|
||||
}
|
||||
String json = request(uri);
|
||||
return fromJson(gson, json, classOfT);
|
||||
}
|
||||
|
||||
private String request(String uri) throws ShellyApiException {
|
||||
|
@ -503,7 +497,7 @@ public class ShellyHttpApi {
|
|||
logger.debug("{}: API Timeout, retry #{} ({})", thingName, timeoutErrors, e.toString());
|
||||
}
|
||||
}
|
||||
throw new ShellyApiException("Inconsistent API result or Timeout"); // successful
|
||||
throw new ShellyApiException("API Timeout or inconsistent result"); // successful
|
||||
}
|
||||
|
||||
private ShellyApiResult innerRequest(HttpMethod method, String uri) throws ShellyApiException {
|
||||
|
@ -533,7 +527,7 @@ public class ShellyHttpApi {
|
|||
if (contentResponse.getStatus() != HttpStatus.OK_200) {
|
||||
throw new ShellyApiException(apiResult);
|
||||
}
|
||||
if (response == null || response.isEmpty() || !response.startsWith("{") && !response.startsWith("[")) {
|
||||
if (response.isEmpty() || !response.startsWith("{") && !response.startsWith("[")) {
|
||||
throw new ShellyApiException("Unexpected response: " + response);
|
||||
}
|
||||
} catch (ExecutionException | InterruptedException | TimeoutException | IllegalArgumentException e) {
|
||||
|
|
|
@ -16,9 +16,11 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.shelly.internal.coap.ShellyCoapJSonDTO.CoIotDescrBlk;
|
||||
import org.openhab.binding.shelly.internal.coap.ShellyCoapJSonDTO.CoIotDescrSen;
|
||||
import org.openhab.binding.shelly.internal.coap.ShellyCoapJSonDTO.CoIotSensor;
|
||||
import org.openhab.binding.shelly.internal.handler.ShellyColorUtils;
|
||||
import org.openhab.core.types.State;
|
||||
|
||||
/**
|
||||
|
@ -30,8 +32,12 @@ import org.openhab.core.types.State;
|
|||
public interface ShellyCoIoTInterface {
|
||||
public int getVersion();
|
||||
|
||||
public CoIotDescrSen fixDescription(CoIotDescrSen sen, Map<String, CoIotDescrBlk> blkMap);
|
||||
public CoIotDescrSen fixDescription(@Nullable CoIotDescrSen sen, Map<String, CoIotDescrBlk> blkMap);
|
||||
|
||||
public boolean handleStatusUpdate(List<CoIotSensor> sensorUpdates, CoIotDescrSen sen, CoIotSensor s,
|
||||
Map<String, State> updates);
|
||||
public void completeMissingSensorDefinition(Map<String, CoIotDescrSen> sensorMap);
|
||||
|
||||
public boolean handleStatusUpdate(List<CoIotSensor> sensorUpdates, CoIotDescrSen sen, int serial, CoIotSensor s,
|
||||
Map<String, State> updates, ShellyColorUtils col);
|
||||
|
||||
public String getLastWakeup();
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.shelly.internal.api.ShellyDeviceProfile;
|
||||
import org.openhab.binding.shelly.internal.coap.ShellyCoapJSonDTO.CoIotDescrBlk;
|
||||
import org.openhab.binding.shelly.internal.coap.ShellyCoapJSonDTO.CoIotDescrSen;
|
||||
|
@ -34,6 +35,10 @@ import org.openhab.core.types.State;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
|
||||
/**
|
||||
* The {@link ShellyCoIoTProtocol} implements common functions for the CoIoT implementations
|
||||
*
|
||||
|
@ -47,12 +52,14 @@ public class ShellyCoIoTProtocol {
|
|||
protected final ShellyDeviceProfile profile;
|
||||
protected final Map<String, CoIotDescrBlk> blkMap;
|
||||
protected final Map<String, CoIotDescrSen> sensorMap;
|
||||
private final Gson gson = new GsonBuilder().create();
|
||||
|
||||
// Due to the fact that the device reports only the current/last status, but no real events, we need to distinguish
|
||||
// between a real update or just a repeated status on periodic updates
|
||||
protected int lastCfgCount = -1;
|
||||
protected int[] lastEventCount = { -1, -1, -1, -1, -1, -1, -1, -1 }; // 4Pro has 4 relays, so 8 should be fine
|
||||
protected String[] inputEvent = { "", "", "", "", "", "", "", "" };
|
||||
protected String lastWakeup = "";
|
||||
|
||||
public ShellyCoIoTProtocol(String thingName, ShellyBaseHandler thingHandler, Map<String, CoIotDescrBlk> blkMap,
|
||||
Map<String, CoIotDescrSen> sensorMap) {
|
||||
|
@ -64,7 +71,7 @@ public class ShellyCoIoTProtocol {
|
|||
}
|
||||
|
||||
protected boolean handleStatusUpdate(List<CoIotSensor> sensorUpdates, CoIotDescrSen sen, CoIotSensor s,
|
||||
Map<String, State> updates) {
|
||||
Map<String, State> updates, ShellyColorUtils col) {
|
||||
// Process status information and convert into channel updates
|
||||
// Integer rIndex = Integer.parseInt(sen.links) + 1;
|
||||
// String rGroup = getProfile().numRelays <= 1 ? CHANNEL_GROUP_RELAY_CONTROL
|
||||
|
@ -72,6 +79,7 @@ public class ShellyCoIoTProtocol {
|
|||
int rIndex = getIdFromBlk(sen);
|
||||
String rGroup = getProfile().numRelays <= 1 ? CHANNEL_GROUP_RELAY_CONTROL
|
||||
: CHANNEL_GROUP_RELAY_CONTROL + rIndex;
|
||||
|
||||
switch (sen.type.toLowerCase()) {
|
||||
case "b": // BatteryLevel +
|
||||
updateChannel(updates, CHANNEL_GROUP_BATTERY, CHANNEL_SENSOR_BAT_LEVEL,
|
||||
|
@ -131,22 +139,27 @@ public class ShellyCoIoTProtocol {
|
|||
break;
|
||||
// RGBW2/Bulb
|
||||
case "red":
|
||||
col.setRed((int) s.value);
|
||||
updateChannel(updates, CHANNEL_GROUP_COLOR_CONTROL, CHANNEL_COLOR_RED,
|
||||
ShellyColorUtils.toPercent((int) s.value));
|
||||
break;
|
||||
case "green":
|
||||
col.setGreen((int) s.value);
|
||||
updateChannel(updates, CHANNEL_GROUP_COLOR_CONTROL, CHANNEL_COLOR_GREEN,
|
||||
ShellyColorUtils.toPercent((int) s.value));
|
||||
break;
|
||||
case "blue":
|
||||
col.setBlue((int) s.value);
|
||||
updateChannel(updates, CHANNEL_GROUP_COLOR_CONTROL, CHANNEL_COLOR_BLUE,
|
||||
ShellyColorUtils.toPercent((int) s.value));
|
||||
break;
|
||||
case "white":
|
||||
col.setWhite((int) s.value);
|
||||
updateChannel(updates, CHANNEL_GROUP_COLOR_CONTROL, CHANNEL_COLOR_WHITE,
|
||||
ShellyColorUtils.toPercent((int) s.value));
|
||||
break;
|
||||
case "gain":
|
||||
col.setGain((int) s.value);
|
||||
updateChannel(updates, CHANNEL_GROUP_COLOR_CONTROL, CHANNEL_COLOR_GAIN,
|
||||
ShellyColorUtils.toPercent((int) s.value, SHELLY_MIN_GAIN, SHELLY_MAX_GAIN));
|
||||
break;
|
||||
|
@ -167,7 +180,7 @@ public class ShellyCoIoTProtocol {
|
|||
return true;
|
||||
}
|
||||
|
||||
protected boolean updateChannel(Map<String, State> updates, String group, String channel, State value) {
|
||||
public static boolean updateChannel(Map<String, State> updates, String group, String channel, State value) {
|
||||
updates.put(mkChannelId(group, channel), value);
|
||||
return true;
|
||||
}
|
||||
|
@ -175,26 +188,32 @@ public class ShellyCoIoTProtocol {
|
|||
protected void handleInput(CoIotDescrSen sen, CoIotSensor s, String rGroup, Map<String, State> updates) {
|
||||
int idx = getSensorNumber(sen.desc, sen.id) - 1;
|
||||
String iGroup = profile.getInputGroup(idx);
|
||||
String iChannel = profile.getInputChannel(idx);
|
||||
String iChannel = CHANNEL_INPUT + profile.getInputSuffix(idx);
|
||||
updateChannel(updates, iGroup, iChannel, s.value == 0 ? OnOffType.OFF : OnOffType.ON);
|
||||
}
|
||||
|
||||
protected void handleInputEvent(CoIotDescrSen sen, String type, Integer count, Map<String, State> updates) {
|
||||
protected void handleInputEvent(CoIotDescrSen sen, String type, int count, int serial, Map<String, State> updates) {
|
||||
int idx = getSensorNumber(sen.desc, sen.id) - 1;
|
||||
String group = profile.getInputGroup(idx);
|
||||
if (count == -1) {
|
||||
// event type
|
||||
updateChannel(updates, group, CHANNEL_STATUS_EVENTTYPE, new StringType(type));
|
||||
updateChannel(updates, group, CHANNEL_STATUS_EVENTTYPE + profile.getInputSuffix(idx), new StringType(type));
|
||||
inputEvent[idx] = type;
|
||||
} else {
|
||||
// event count
|
||||
updateChannel(updates, group, CHANNEL_STATUS_EVENTCOUNT, getDecimal(count));
|
||||
if (profile.inButtonMode(idx) && ((profile.hasBattery && (count == 1)) || (count != lastEventCount[idx]))) {
|
||||
if (profile.isButton || (lastEventCount[idx] != -1)) { // skip the first one if binding was restarted
|
||||
thingHandler.triggerButton(group, inputEvent[idx]);
|
||||
updateChannel(updates, group, CHANNEL_STATUS_EVENTCOUNT + profile.getInputSuffix(idx), getDecimal(count));
|
||||
logger.trace(
|
||||
"{}: Check button[{}] for event trigger (isButtonMode={}, isButton={}, hasBattery={}, serial={}, count={}, lastEventCount[{}]={}",
|
||||
thingName, idx, profile.inButtonMode(idx), profile.isButton, profile.hasBattery, serial, count, idx,
|
||||
lastEventCount[idx]);
|
||||
if (profile.inButtonMode(idx) && ((profile.hasBattery && (count == 1))
|
||||
|| ((lastEventCount[idx] != -1) && (count != lastEventCount[idx])))) {
|
||||
if (!profile.isButton || (profile.isButton && (serial != 0x200))) { // skip duplicate on wake-up
|
||||
logger.debug("{}: Trigger event {}", thingName, inputEvent[idx]);
|
||||
thingHandler.triggerButton(group, idx, inputEvent[idx]);
|
||||
}
|
||||
lastEventCount[idx] = count;
|
||||
}
|
||||
lastEventCount[idx] = count;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -225,25 +244,25 @@ public class ShellyCoIoTProtocol {
|
|||
} else if (profile.isDimmer) {
|
||||
group = CHANNEL_GROUP_RELAY_CONTROL;
|
||||
} else if (profile.isRGBW2) {
|
||||
checkL = String.valueOf(id); // String.valueOf(id - 1); // id is 1-based, L is 0-based
|
||||
group = CHANNEL_GROUP_LIGHT_CHANNEL + id;
|
||||
checkL = String.valueOf(id - 1); // id is 1-based, L is 0-based
|
||||
logger.trace("{}: updatePower() for L={}", thingName, checkL);
|
||||
}
|
||||
|
||||
// We need to update brigthtess and on/off state at the same time to avoid "flipping brightness slider" in
|
||||
// We need to update brightness and on/off state at the same time to avoid "flipping brightness slider" in
|
||||
// the UI
|
||||
Double brightness = -1.0;
|
||||
Double power = -1.0;
|
||||
double brightness = -1.0;
|
||||
double power = -1.0;
|
||||
for (CoIotSensor update : allUpdates) {
|
||||
CoIotDescrSen d = fixDescription(sensorMap.getOrDefault(update.id, new CoIotDescrSen()), blkMap);
|
||||
CoIotDescrSen d = fixDescription(sensorMap.get(update.id), blkMap);
|
||||
if (!checkL.isEmpty() && !d.links.equals(checkL)) {
|
||||
// continue until we find the correct one
|
||||
continue;
|
||||
}
|
||||
if (d.desc.equalsIgnoreCase("brightness")) {
|
||||
brightness = new Double(update.value);
|
||||
brightness = update.value;
|
||||
} else if (d.desc.equalsIgnoreCase("output") || d.desc.equalsIgnoreCase("state")) {
|
||||
power = new Double(update.value);
|
||||
power = update.value;
|
||||
}
|
||||
}
|
||||
if (power != -1) {
|
||||
|
@ -298,11 +317,11 @@ public class ShellyCoIoTProtocol {
|
|||
|
||||
protected int getIdFromBlk(CoIotDescrSen sen) {
|
||||
int idx = -1;
|
||||
if (blkMap.containsKey(sen.links)) {
|
||||
CoIotDescrBlk blk = blkMap.get(sen.links);
|
||||
CoIotDescrBlk blk = blkMap.get(sen.links);
|
||||
if (blk != null) {
|
||||
String desc = blk.desc.toLowerCase();
|
||||
if (desc.startsWith(SHELLY_CLASS_RELAY) || desc.startsWith(SHELLY_CLASS_ROLLER)
|
||||
|| desc.startsWith(SHELLY_CLASS_EMETER)) {
|
||||
|| desc.startsWith(SHELLY_CLASS_LIGHT) || desc.startsWith(SHELLY_CLASS_EMETER)) {
|
||||
if (desc.contains("_")) { // CoAP v2
|
||||
idx = Integer.parseInt(substringAfter(desc, "_"));
|
||||
} else { // CoAP v1
|
||||
|
@ -349,7 +368,28 @@ public class ShellyCoIoTProtocol {
|
|||
return profile;
|
||||
}
|
||||
|
||||
public CoIotDescrSen fixDescription(CoIotDescrSen sen, Map<String, CoIotDescrBlk> blkMap) {
|
||||
return sen;
|
||||
public CoIotDescrSen fixDescription(@Nullable CoIotDescrSen sen, Map<String, CoIotDescrBlk> blkMap) {
|
||||
return sen != null ? sen : new CoIotDescrSen();
|
||||
}
|
||||
|
||||
public void completeMissingSensorDefinition(Map<String, CoIotDescrSen> sensorMap) {
|
||||
}
|
||||
|
||||
protected void addSensor(Map<String, CoIotDescrSen> sensorMap, String key, String json) {
|
||||
try {
|
||||
if (!sensorMap.containsKey(key)) {
|
||||
CoIotDescrSen sen = gson.fromJson(json, CoIotDescrSen.class);
|
||||
if (sen != null) {
|
||||
sensorMap.put(key, sen);
|
||||
}
|
||||
}
|
||||
} catch (JsonSyntaxException e) {
|
||||
// should never happen
|
||||
logger.trace("Unable to parse sensor definition: {}", json, e);
|
||||
}
|
||||
}
|
||||
|
||||
public String getLastWakeup() {
|
||||
return lastWakeup;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.shelly.internal.coap.ShellyCoapJSonDTO.CoIotDescrBlk;
|
||||
import org.openhab.binding.shelly.internal.coap.ShellyCoapJSonDTO.CoIotDescrSen;
|
||||
import org.openhab.binding.shelly.internal.coap.ShellyCoapJSonDTO.CoIotSensor;
|
||||
|
@ -63,10 +64,10 @@ public class ShellyCoIoTVersion1 extends ShellyCoIoTProtocol implements ShellyCo
|
|||
* ignored.
|
||||
*/
|
||||
@Override
|
||||
public boolean handleStatusUpdate(List<CoIotSensor> sensorUpdates, CoIotDescrSen sen, CoIotSensor s,
|
||||
Map<String, State> updates) {
|
||||
public boolean handleStatusUpdate(List<CoIotSensor> sensorUpdates, CoIotDescrSen sen, int serial, CoIotSensor s,
|
||||
Map<String, State> updates, ShellyColorUtils col) {
|
||||
// first check the base implementation
|
||||
if (super.handleStatusUpdate(sensorUpdates, sen, s, updates)) {
|
||||
if (super.handleStatusUpdate(sensorUpdates, sen, s, updates, col)) {
|
||||
// process by the base class
|
||||
return true;
|
||||
}
|
||||
|
@ -162,10 +163,10 @@ public class ShellyCoIoTVersion1 extends ShellyCoIoTProtocol implements ShellyCo
|
|||
toQuantityType(pos, Units.PERCENT));
|
||||
break;
|
||||
case "input event": // Shelly Button 1
|
||||
handleInputEvent(sen, getString(s.valueStr), -1, updates);
|
||||
handleInputEvent(sen, getString(s.valueStr), -1, serial, updates);
|
||||
break;
|
||||
case "input event counter": // Shelly Button 1/ix3
|
||||
handleInputEvent(sen, "", getInteger((int) s.value), updates);
|
||||
handleInputEvent(sen, "", getInteger((int) s.value), serial, updates);
|
||||
break;
|
||||
case "flood":
|
||||
updateChannel(updates, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_FLOOD,
|
||||
|
@ -227,7 +228,7 @@ public class ShellyCoIoTVersion1 extends ShellyCoIoTProtocol implements ShellyCo
|
|||
* @return fixed Sensor description (sen)
|
||||
*/
|
||||
@Override
|
||||
public CoIotDescrSen fixDescription(CoIotDescrSen sen, Map<String, CoIotDescrBlk> blkMap) {
|
||||
public CoIotDescrSen fixDescription(@Nullable CoIotDescrSen sen, Map<String, CoIotDescrBlk> blkMap) {
|
||||
// Shelly1: reports null descr+type "Switch" -> map to S
|
||||
// Shelly1PM: reports null descr+type "Overtemp" -> map to O
|
||||
// Shelly1PM: reports null descr+type "W" -> add description
|
||||
|
@ -238,6 +239,9 @@ public class ShellyCoIoTVersion1 extends ShellyCoIoTProtocol implements ShellyCo
|
|||
// Shelly Sense: Motion is reported with Desc "battery", but type "H" instead of "B"
|
||||
// Shelly Bulb: Colors are coded with Type="Red" etc. rather than Type="S" and color as Descr
|
||||
// Shelly RGBW2 is reporting Brightness, Power, VSwitch for each channel, but all with L=0
|
||||
if (sen == null) {
|
||||
throw new IllegalArgumentException("sen should not be null!");
|
||||
}
|
||||
if (sen.desc == null) {
|
||||
sen.desc = "";
|
||||
}
|
||||
|
@ -255,8 +259,10 @@ public class ShellyCoIoTVersion1 extends ShellyCoIoTProtocol implements ShellyCo
|
|||
CoIotDescrBlk blk = new CoIotDescrBlk();
|
||||
CoIotDescrBlk blk0 = blkMap.get("0"); // blk 0 is always there
|
||||
blk.id = sen.links;
|
||||
blk.desc = blk0.desc + "_" + blk.id;
|
||||
blkMap.put(blk.id, blk);
|
||||
if (blk0 != null) {
|
||||
blk.desc = blk0.desc + "_" + blk.id;
|
||||
blkMap.put(blk.id, blk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,10 +25,12 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.shelly.internal.coap.ShellyCoapJSonDTO.CoIotDescrBlk;
|
||||
import org.openhab.binding.shelly.internal.coap.ShellyCoapJSonDTO.CoIotDescrSen;
|
||||
import org.openhab.binding.shelly.internal.coap.ShellyCoapJSonDTO.CoIotSensor;
|
||||
import org.openhab.binding.shelly.internal.handler.ShellyBaseHandler;
|
||||
import org.openhab.binding.shelly.internal.handler.ShellyColorUtils;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.OpenClosedType;
|
||||
import org.openhab.core.library.unit.SIUnits;
|
||||
|
@ -60,17 +62,15 @@ public class ShellyCoIoTVersion2 extends ShellyCoIoTProtocol implements ShellyCo
|
|||
* Process CoIoT status update message. If a status update is received, but the device description has not been
|
||||
* received yet a GET is send to query device description.
|
||||
*
|
||||
* @param devId device id included in the status packet
|
||||
* @param payload CoAP payload (Json format), example: {"G":[[0,112,0]]}
|
||||
* @param serial Serial for this request. If this the the same as last serial
|
||||
* the update was already sent and processed so this one gets
|
||||
* ignored.
|
||||
* @param sensorUpdates Complete list of sensor updates
|
||||
* @param sen The specific sensor update to handle
|
||||
* @param updates Resulting updates (new updates will be added to input list)
|
||||
*/
|
||||
@Override
|
||||
public boolean handleStatusUpdate(List<CoIotSensor> sensorUpdates, CoIotDescrSen sen, CoIotSensor s,
|
||||
Map<String, State> updates) {
|
||||
public boolean handleStatusUpdate(List<CoIotSensor> sensorUpdates, CoIotDescrSen sen, int serial, CoIotSensor s,
|
||||
Map<String, State> updates, ShellyColorUtils col) {
|
||||
// first check the base implementation
|
||||
if (super.handleStatusUpdate(sensorUpdates, sen, s, updates)) {
|
||||
if (super.handleStatusUpdate(sensorUpdates, sen, s, updates, col)) {
|
||||
// process by the base class
|
||||
return true;
|
||||
}
|
||||
|
@ -80,7 +80,7 @@ public class ShellyCoIoTVersion2 extends ShellyCoIoTProtocol implements ShellyCo
|
|||
int rIndex = getIdFromBlk(sen);
|
||||
String rGroup = getProfile().numRelays <= 1 ? CHANNEL_GROUP_RELAY_CONTROL
|
||||
: CHANNEL_GROUP_RELAY_CONTROL + rIndex;
|
||||
String mGroup = profile.numMeters == 1 ? CHANNEL_GROUP_METER
|
||||
String mGroup = profile.numMeters <= 1 ? CHANNEL_GROUP_METER
|
||||
: CHANNEL_GROUP_METER + (profile.isEMeter ? getIdFromBlk(sen) : rIndex);
|
||||
|
||||
boolean processed = true;
|
||||
|
@ -89,14 +89,13 @@ public class ShellyCoIoTVersion2 extends ShellyCoIoTProtocol implements ShellyCo
|
|||
switch (sen.id) {
|
||||
case "3103": // H, humidity, 0-100 percent, unknown 999
|
||||
case "3106": // L, luminosity, lux, U32, -1
|
||||
case "3109": // S, tilt, 0-180deg, -1
|
||||
case "3110": // S, luminosityLevel, dark/twilight/bright, "unknown"=unknown
|
||||
case "3111": // B, battery, 0-100%, unknown -1
|
||||
case "3112": // S, charger, 0/1
|
||||
case "3115": // S, sensorError, 0/1
|
||||
case "5101": // S, brightness, 1-100%
|
||||
// processed by base handler
|
||||
break;
|
||||
|
||||
case "6109": // P, overpowerValue, W, U32
|
||||
case "9101":
|
||||
// Relay: S, mode, relay/roller or
|
||||
|
@ -113,7 +112,7 @@ public class ShellyCoIoTVersion2 extends ShellyCoIoTProtocol implements ShellyCo
|
|||
case "1103": // roller_0: S, rollerPos, 0-100, unknown -1
|
||||
int pos = Math.max(SHELLY_MIN_ROLLER_POS, Math.min((int) value, SHELLY_MAX_ROLLER_POS));
|
||||
updateChannel(updates, CHANNEL_GROUP_ROL_CONTROL, CHANNEL_ROL_CONTROL_CONTROL,
|
||||
toQuantityType(new Double(SHELLY_MAX_ROLLER_POS - pos), Units.PERCENT));
|
||||
toQuantityType((double) (SHELLY_MAX_ROLLER_POS - pos), Units.PERCENT));
|
||||
break;
|
||||
case "1105": // S, valvle, closed/opened/not_connected/failure/closing/opening/checking or unbknown
|
||||
updateChannel(updates, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_VALVE, getStringType(s.valueStr));
|
||||
|
@ -129,13 +128,13 @@ public class ShellyCoIoTVersion2 extends ShellyCoIoTProtocol implements ShellyCo
|
|||
case "2202": // Input_1: EV, inputEvent
|
||||
case "2302": // Input_2: EV, inputEvent
|
||||
case "2402": // Input_3: EV, inputEvent
|
||||
handleInputEvent(sen, getString(s.valueStr), -1, updates);
|
||||
handleInputEvent(sen, getString(s.valueStr), -1, serial, updates);
|
||||
break;
|
||||
case "2103": // EVC, inputEventCnt, U16
|
||||
case "2203": // EVC, inputEventCnt, U16
|
||||
case "2303": // EVC, inputEventCnt, U16
|
||||
case "2403": // EVC, inputEventCnt, U16
|
||||
handleInputEvent(sen, "", getInteger((int) s.value), updates);
|
||||
handleInputEvent(sen, "", getInteger((int) s.value), serial, updates);
|
||||
break;
|
||||
case "3101": // sensor_0: T, extTemp, C, -55/125; unknown 999
|
||||
case "3201": // sensor_1: T, extTemp, C, -55/125; unknown 999
|
||||
|
@ -172,6 +171,10 @@ public class ShellyCoIoTVersion2 extends ShellyCoIoTProtocol implements ShellyCo
|
|||
logger.debug("{}: Sensor error reported, check device, battery and installation", thingName);
|
||||
}
|
||||
break;
|
||||
case "3109": // S, tilt, 0-180deg, -1
|
||||
updateChannel(updates, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_TILT,
|
||||
toQuantityType(s.value, DIGITS_NONE, Units.DEGREE_ANGLE));
|
||||
break;
|
||||
case "3113": // S, sensorOp, warmup/normal/fault
|
||||
updateChannel(updates, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_SSTATE, getStringType(s.valueStr));
|
||||
break;
|
||||
|
@ -181,19 +184,27 @@ public class ShellyCoIoTVersion2 extends ShellyCoIoTProtocol implements ShellyCo
|
|||
case "3117": // S, extInput, 0/1
|
||||
handleInput(sen, s, rGroup, updates);
|
||||
break;
|
||||
case "3118":
|
||||
updateChannel(updates, mGroup, CHANNEL_SENSOR_VOLTAGE,
|
||||
toQuantityType(getDouble(s.value), DIGITS_VOLT, Units.VOLT));
|
||||
break;
|
||||
|
||||
case "4101": // relay_0: P, power, W
|
||||
case "4201": // relay_1: P, power, W
|
||||
case "4301": // relay_2: P, power, W
|
||||
case "4401": // relay_3: P, power, W
|
||||
case "4101": // relay_0/light_0: P, power, W
|
||||
case "4201": // relay_1/light_1: P, power, W
|
||||
case "4301": // relay_2/light_2: P, power, W
|
||||
case "4401": // relay_3/light_3: P, power, W
|
||||
case "4105": // emeter_0: P, power, W
|
||||
case "4205": // emeter_1: P, power, W
|
||||
case "4305": // emeter_2: P, power, W
|
||||
case "4102": // roller_0: P, rollerPower, W, 0-2300, unknown -1
|
||||
case "4202": // roller_1: P, rollerPower, W, 0-2300, unknown -1
|
||||
logger.debug("{}: Updating {}:currentWatts with {}", thingName, mGroup, s.value);
|
||||
updateChannel(updates, mGroup, CHANNEL_METER_CURRENTWATTS,
|
||||
toQuantityType(s.value, DIGITS_WATT, Units.WATT));
|
||||
updateChannel(updates, mGroup, CHANNEL_LAST_UPDATE, getTimestamp());
|
||||
if (!profile.isRGBW2 && !profile.isRoller) {
|
||||
// only for regular, not-aggregated meters
|
||||
updateChannel(updates, mGroup, CHANNEL_LAST_UPDATE, getTimestamp());
|
||||
}
|
||||
break;
|
||||
|
||||
case "4103": // relay_0: E, energy, Wmin, U32
|
||||
|
@ -237,6 +248,16 @@ public class ShellyCoIoTVersion2 extends ShellyCoIoTProtocol implements ShellyCo
|
|||
updateChannel(updates, rGroup, CHANNEL_EMETER_PFACTOR, getDecimal(s.value));
|
||||
break;
|
||||
|
||||
case "5101": // {"I":5101,"T":"S","D":"brightness","R":"0/100","L":1},
|
||||
case "5102": // {"I":5102,"T":"S","D":"gain","R":"0/100","L":1},
|
||||
case "5103": // {"I":5103,"T":"S","D":"colorTemp","U":"K","R":"3000/6500","L":1},
|
||||
case "5105": // {"I":5105,"T":"S","D":"red","R":"0/255","L":1},
|
||||
case "5106": // {"I":5106,"T":"S","D":"green","R":"0/255","L":1},
|
||||
case "5107": // {"I":5107,"T":"S","D":"blue","R":"0/255","L":1},
|
||||
case "5108": // {"I":5108,"T":"S","D":"white","R":"0/255","L":1},
|
||||
// already covered by base handler
|
||||
break;
|
||||
|
||||
case "6101": // A, overtemp, 0/1
|
||||
if (s.value == 1) {
|
||||
thingHandler.postEvent(ALARM_TYPE_OVERTEMP, true);
|
||||
|
@ -268,6 +289,21 @@ public class ShellyCoIoTVersion2 extends ShellyCoIoTProtocol implements ShellyCo
|
|||
updateChannel(updates, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_FLOOD,
|
||||
value == 1 ? OnOffType.ON : OnOffType.OFF);
|
||||
break;
|
||||
|
||||
case "6107": // A, motion, 0/1, -1
|
||||
// {"I":6107,"T":"A","D":"motion","R":["0/1","-1"],"L":1},
|
||||
updateChannel(updates, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_MOTION,
|
||||
value == 1 ? OnOffType.ON : OnOffType.OFF);
|
||||
break;
|
||||
case "3119": // Motion timestamp
|
||||
// {"I":3119,"T":"S","D":"timestamp","U":"s","R":["U32","-1"],"L":1},
|
||||
updateChannel(updates, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_MOTION_TS,
|
||||
getTimestamp(getString(profile.settings.timezone), (long) s.value));
|
||||
break;
|
||||
case "3120": // motionActive
|
||||
// {"I":3120,"T":"S","D":"motionActive","R":["0/1","-1"],"L":1},
|
||||
break;
|
||||
|
||||
case "6108": // A, gas, none/mild/heavy/test or unknown
|
||||
updateChannel(updates, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_ALARM_STATE, getStringType(s.valueStr));
|
||||
break;
|
||||
|
@ -276,7 +312,10 @@ public class ShellyCoIoTVersion2 extends ShellyCoIoTProtocol implements ShellyCo
|
|||
value == 1 ? OnOffType.ON : OnOffType.OFF);
|
||||
break;
|
||||
case "9102": // EV, wakeupEvent, battery/button/periodic/poweron/sensor/ext_power, "unknown"=unknown
|
||||
thingHandler.updateWakeupReason(s.valueArray);
|
||||
if (s.valueArray.size() > 0) {
|
||||
thingHandler.updateWakeupReason(s.valueArray);
|
||||
lastWakeup = (String) s.valueArray.get(0);
|
||||
}
|
||||
break;
|
||||
case "9103": // EVC, cfgChanged, U16
|
||||
if ((lastCfgCount != -1) && (lastCfgCount != s.value)) {
|
||||
|
@ -292,7 +331,19 @@ public class ShellyCoIoTVersion2 extends ShellyCoIoTProtocol implements ShellyCo
|
|||
}
|
||||
|
||||
@Override
|
||||
public CoIotDescrSen fixDescription(CoIotDescrSen sen, Map<String, CoIotDescrBlk> blkMap) {
|
||||
return sen;
|
||||
public CoIotDescrSen fixDescription(@Nullable CoIotDescrSen sen, Map<String, CoIotDescrBlk> blkMap) {
|
||||
return super.fixDescription(sen, blkMap);
|
||||
}
|
||||
|
||||
private static final String ID_4101_DESCR = "{ \"I\":4101, \"T\":\"P\", \"D\":\"power\", \"U\": \"W\", \"R\":\"0/3500\", \"L\": 1}";
|
||||
private static final String ID_4103_DESCR = "{ \"I\":4103, \"T\":\"E\", \"D\":\"energy\", \"U\": \"Wmin\", \"R\":\"U32\", \"L\": 1}";
|
||||
|
||||
@Override
|
||||
public void completeMissingSensorDefinition(Map<String, CoIotDescrSen> sensorMap) {
|
||||
if (profile.isDuo && profile.inColor) {
|
||||
addSensor(sensorMap, "4101", ID_4101_DESCR);
|
||||
addSensor(sensorMap, "4103", ID_4103_DESCR);
|
||||
}
|
||||
super.completeMissingSensorDefinition(sensorMap);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,8 +16,12 @@ import static org.openhab.binding.shelly.internal.ShellyBindingConstants.*;
|
|||
import static org.openhab.binding.shelly.internal.coap.ShellyCoapJSonDTO.*;
|
||||
import static org.openhab.binding.shelly.internal.util.ShellyUtils.*;
|
||||
|
||||
import java.net.SocketException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.*;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import org.eclipse.californium.core.CoapClient;
|
||||
import org.eclipse.californium.core.coap.CoAP.Code;
|
||||
|
@ -28,6 +32,7 @@ import org.eclipse.californium.core.coap.Option;
|
|||
import org.eclipse.californium.core.coap.OptionNumberRegistry;
|
||||
import org.eclipse.californium.core.coap.Request;
|
||||
import org.eclipse.californium.core.coap.Response;
|
||||
import org.eclipse.californium.core.network.Endpoint;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.shelly.internal.api.ShellyApiException;
|
||||
|
@ -41,6 +46,8 @@ import org.openhab.binding.shelly.internal.coap.ShellyCoapJSonDTO.CoIotSensor;
|
|||
import org.openhab.binding.shelly.internal.coap.ShellyCoapJSonDTO.CoIotSensorTypeAdapter;
|
||||
import org.openhab.binding.shelly.internal.config.ShellyThingConfiguration;
|
||||
import org.openhab.binding.shelly.internal.handler.ShellyBaseHandler;
|
||||
import org.openhab.binding.shelly.internal.handler.ShellyColorUtils;
|
||||
import org.openhab.core.library.unit.Units;
|
||||
import org.openhab.core.types.State;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -79,18 +86,18 @@ public class ShellyCoapHandler implements ShellyCoapListener {
|
|||
private String lastPayload = "";
|
||||
private Map<String, CoIotDescrBlk> blkMap = new LinkedHashMap<>();
|
||||
private Map<String, CoIotDescrSen> sensorMap = new LinkedHashMap<>();
|
||||
private final ShellyDeviceProfile profile;
|
||||
private ShellyDeviceProfile profile;
|
||||
|
||||
public ShellyCoapHandler(ShellyBaseHandler thingHandler, ShellyCoapServer coapServer) {
|
||||
this.thingHandler = thingHandler;
|
||||
this.thingName = thingHandler.thingName;
|
||||
this.profile = thingHandler.getProfile();
|
||||
this.coapServer = coapServer;
|
||||
this.coiot = new ShellyCoIoTVersion1(thingName, thingHandler, blkMap, sensorMap); // Default
|
||||
this.coiot = new ShellyCoIoTVersion2(thingName, thingHandler, blkMap, sensorMap); // Default: V2
|
||||
|
||||
gsonBuilder.registerTypeAdapter(CoIotDevDescription.class, new CoIotDevDescrTypeAdapter());
|
||||
gsonBuilder.registerTypeAdapter(CoIotGenericSensorList.class, new CoIotSensorTypeAdapter());
|
||||
gson = gsonBuilder.create();
|
||||
profile = thingHandler.getProfile();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -104,6 +111,7 @@ public class ShellyCoapHandler implements ShellyCoapListener {
|
|||
try {
|
||||
this.thingName = thingName;
|
||||
this.config = config;
|
||||
this.profile = thingHandler.getProfile();
|
||||
if (isStarted()) {
|
||||
logger.trace("{}: CoAP Listener was already started", thingName);
|
||||
stop();
|
||||
|
@ -113,9 +121,21 @@ public class ShellyCoapHandler implements ShellyCoapListener {
|
|||
coapServer.start(config.localIp, this);
|
||||
statusClient = new CoapClient(completeUrl(config.deviceIp, COLOIT_URI_DEVSTATUS))
|
||||
.setTimeout((long) SHELLY_API_TIMEOUT_MS).useNONs().setEndpoint(coapServer.getEndpoint());
|
||||
@Nullable
|
||||
Endpoint endpoint = null;
|
||||
if (statusClient != null) {
|
||||
endpoint = statusClient.getEndpoint();
|
||||
}
|
||||
if ((endpoint == null) || !endpoint.isStarted()) {
|
||||
logger.warn("{}: Unable to initialize CoAP access (network error)", thingName);
|
||||
throw new ShellyApiException("Network initialization failed");
|
||||
}
|
||||
discover();
|
||||
} catch (SocketException e) {
|
||||
logger.warn("{}: Unable to initialize CoAP access (socket exception) - {}", thingName, e.getMessage());
|
||||
throw new ShellyApiException("Network error", e);
|
||||
} catch (UnknownHostException e) {
|
||||
logger.debug("{}: CoAP Exception", thingName, e);
|
||||
logger.info("{}: CoAP Exception (Unknown Host)", thingName, e);
|
||||
throw new ShellyApiException("Unknown Host: " + config.deviceIp, e);
|
||||
}
|
||||
}
|
||||
|
@ -142,7 +162,6 @@ public class ShellyCoapHandler implements ShellyCoapListener {
|
|||
String payload = "";
|
||||
String devId = "";
|
||||
String uri = "";
|
||||
// int validity = 0;
|
||||
int serial = -1;
|
||||
try {
|
||||
if (logger.isDebugEnabled()) {
|
||||
|
@ -208,8 +227,8 @@ public class ShellyCoapHandler implements ShellyCoapListener {
|
|||
|
||||
// The device changes the serial on every update, receiving a message with the same serial is a
|
||||
// duplicate, excep for battery devices! Those reset the serial every time when they wake-up
|
||||
if ((serial == lastSerial) && payload.equals(lastPayload)
|
||||
&& (!profile.hasBattery || ((serial & 0xFF) != 0))) {
|
||||
if ((serial == lastSerial) && payload.equals(lastPayload) && (!profile.hasBattery
|
||||
|| coiot.getLastWakeup().equalsIgnoreCase("ext_power") || ((serial & 0xFF) != 0))) {
|
||||
logger.debug("{}: Serial {} was already processed, ignore update", thingName, serial);
|
||||
return;
|
||||
}
|
||||
|
@ -217,11 +236,16 @@ public class ShellyCoapHandler implements ShellyCoapListener {
|
|||
// fixed malformed JSON :-(
|
||||
payload = fixJSON(payload);
|
||||
|
||||
if (uri.equalsIgnoreCase(COLOIT_URI_DEVDESC) || (uri.isEmpty() && payload.contains(COIOT_TAG_BLK))) {
|
||||
handleDeviceDescription(devId, payload);
|
||||
} else if (uri.equalsIgnoreCase(COLOIT_URI_DEVSTATUS)
|
||||
|| (uri.isEmpty() && payload.contains(COIOT_TAG_GENERIC))) {
|
||||
handleStatusUpdate(devId, payload, serial);
|
||||
try {
|
||||
if (uri.equalsIgnoreCase(COLOIT_URI_DEVDESC)
|
||||
|| (uri.isEmpty() && payload.contains(COIOT_TAG_BLK))) {
|
||||
handleDeviceDescription(devId, payload);
|
||||
} else if (uri.equalsIgnoreCase(COLOIT_URI_DEVSTATUS)
|
||||
|| (uri.isEmpty() && payload.contains(COIOT_TAG_GENERIC))) {
|
||||
handleStatusUpdate(devId, payload, serial);
|
||||
}
|
||||
} catch (ShellyApiException e) {
|
||||
logger.debug("{}: Unable to process CoIoT message: {}", thingName, e.toString());
|
||||
}
|
||||
} else {
|
||||
// error handling
|
||||
|
@ -249,14 +273,14 @@ public class ShellyCoapHandler implements ShellyCoapListener {
|
|||
* @param payload Device desciption in JSon format, example:
|
||||
* {"blk":[{"I":0,"D":"Relay0"}],"sen":[{"I":112,"T":"Switch","R":"0/1","L":0}],"act":[{"I":211,"D":"Switch","L":0,"P":[{"I":2011,"D":"ToState","R":"0/1"}]}]}
|
||||
*/
|
||||
private void handleDeviceDescription(String devId, String payload) {
|
||||
private void handleDeviceDescription(String devId, String payload) throws ShellyApiException {
|
||||
logger.debug("{}: CoIoT Device Description for {}: {}", thingName, devId, payload);
|
||||
|
||||
try {
|
||||
boolean valid = true;
|
||||
|
||||
// Decode Json
|
||||
CoIotDevDescription descr = gson.fromJson(payload, CoIotDevDescription.class);
|
||||
CoIotDevDescription descr = fromJson(gson, payload, CoIotDevDescription.class);
|
||||
for (int i = 0; i < descr.blk.size(); i++) {
|
||||
CoIotDescrBlk blk = descr.blk.get(i);
|
||||
logger.debug("{}: id={}: {}", thingName, blk.id, blk.desc);
|
||||
|
@ -288,12 +312,13 @@ public class ShellyCoapHandler implements ShellyCoapListener {
|
|||
valid &= addSensor(descr.sen.get(i));
|
||||
}
|
||||
}
|
||||
coiot.completeMissingSensorDefinition(sensorMap);
|
||||
|
||||
if (!valid) {
|
||||
logger.debug(
|
||||
"{}: Incompatible device description detected for CoIoT version {} (id length mismatch), discarding!",
|
||||
thingName, coiot.getVersion());
|
||||
thingHandler.updateProperties(PROPERTY_COAP_DESCR, "");
|
||||
|
||||
discover();
|
||||
return;
|
||||
}
|
||||
|
@ -319,6 +344,7 @@ public class ShellyCoapHandler implements ShellyCoapListener {
|
|||
int vers = coiot.getVersion();
|
||||
if (((vers == COIOT_VERSION_1) && (sen.id.length() > 3))
|
||||
|| ((vers >= COIOT_VERSION_2) && (sen.id.length() < 4))) {
|
||||
logger.debug("{}: Invalid format for sensor defition detected, id={}", thingName, sen.id);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -346,8 +372,9 @@ public class ShellyCoapHandler implements ShellyCoapListener {
|
|||
* @param serial Serial for this request. If this the the same as last serial
|
||||
* the update was already sent and processed so this one gets
|
||||
* ignored.
|
||||
* @throws ShellyApiException
|
||||
*/
|
||||
private void handleStatusUpdate(String devId, String payload, int serial) {
|
||||
private void handleStatusUpdate(String devId, String payload, int serial) throws ShellyApiException {
|
||||
logger.debug("{}: CoIoT Sensor data {} (serial={})", thingName, payload, serial);
|
||||
if (blkMap.isEmpty()) {
|
||||
// send discovery packet
|
||||
|
@ -367,7 +394,7 @@ public class ShellyCoapHandler implements ShellyCoapListener {
|
|||
}
|
||||
|
||||
// Parse Json,
|
||||
CoIotGenericSensorList list = gson.fromJson(fixJSON(payload), CoIotGenericSensorList.class);
|
||||
CoIotGenericSensorList list = fromJson(gson, fixJSON(payload), CoIotGenericSensorList.class);
|
||||
if (list.generic == null) {
|
||||
logger.debug("{}: Sensor list has invalid format! Payload: {}", devId, payload);
|
||||
return;
|
||||
|
@ -377,33 +404,29 @@ public class ShellyCoapHandler implements ShellyCoapListener {
|
|||
Map<String, State> updates = new TreeMap<String, State>();
|
||||
logger.debug("{}: {} CoAP sensor updates received", thingName, sensorUpdates.size());
|
||||
int failed = 0;
|
||||
ShellyColorUtils col = new ShellyColorUtils();
|
||||
for (int i = 0; i < sensorUpdates.size(); i++) {
|
||||
try {
|
||||
CoIotSensor s = sensorUpdates.get(i);
|
||||
if (!sensorMap.containsKey(s.id)) {
|
||||
logger.debug("{}: Invalid id in sensor description: {}, index {}", thingName, s.id, i);
|
||||
failed++;
|
||||
continue;
|
||||
}
|
||||
CoIotDescrSen sen = sensorMap.get(s.id);
|
||||
Objects.requireNonNull(sen);
|
||||
if (sen == null) {
|
||||
logger.debug("{}: Unable to sensor definition for id={}, payload={}", thingName, s.id, payload);
|
||||
continue;
|
||||
}
|
||||
// find matching sensor definition from device description, use the Link ID as index
|
||||
CoIotDescrBlk element = null;
|
||||
sen = coiot.fixDescription(sen, blkMap);
|
||||
if (!blkMap.containsKey(sen.links)) {
|
||||
logger.debug("{}: Invalid CoAP description: sen.links({}", thingName, getString(sen.links));
|
||||
element = blkMap.get(sen.links);
|
||||
if (element == null) {
|
||||
logger.debug("{}: Unable to find BLK for link {} from sen.id={}, payload={}", thingName, sen.links,
|
||||
sen.id, payload);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!blkMap.containsKey(sen.links)) {
|
||||
logger.debug("{}: Unable to find BLK for link {} from sen.id={}", thingName, sen.links, sen.id);
|
||||
continue;
|
||||
}
|
||||
CoIotDescrBlk element = blkMap.get(sen.links);
|
||||
logger.trace("{}: Sensor value[{}]: id={}, Value={} ({}, Type={}, Range={}, Link={}: {})", thingName,
|
||||
i, s.id, getString(s.valueStr).isEmpty() ? s.value : s.valueStr, sen.desc, sen.type, sen.range,
|
||||
sen.links, element.desc);
|
||||
|
||||
if (!coiot.handleStatusUpdate(sensorUpdates, sen, s, updates)) {
|
||||
if (!coiot.handleStatusUpdate(sensorUpdates, sen, serial, s, updates, col)) {
|
||||
logger.debug("{}: CoIoT data for id {}, type {}/{} not processed, value={}; payload={}", thingName,
|
||||
sen.id, sen.type, sen.desc, s.value, payload);
|
||||
}
|
||||
|
@ -418,7 +441,8 @@ public class ShellyCoapHandler implements ShellyCoapListener {
|
|||
if (!updates.isEmpty()) {
|
||||
int updated = 0;
|
||||
for (Map.Entry<String, State> u : updates.entrySet()) {
|
||||
updated += thingHandler.updateChannel(u.getKey(), u.getValue(), false) ? 1 : 0;
|
||||
String key = u.getKey();
|
||||
updated += thingHandler.updateChannel(key, u.getValue(), false) ? 1 : 0;
|
||||
}
|
||||
if (updated > 0) {
|
||||
logger.debug("{}: {} channels updated from CoIoT status, serial={}", thingName, updated, serial);
|
||||
|
@ -428,6 +452,40 @@ public class ShellyCoapHandler implements ShellyCoapListener {
|
|||
}
|
||||
}
|
||||
|
||||
if (profile.isLight && profile.inColor && col.isRgbValid()) {
|
||||
// Update color picker from single values
|
||||
if (col.isRgbValid()) {
|
||||
thingHandler.updateChannel(mkChannelId(CHANNEL_GROUP_COLOR_CONTROL, CHANNEL_COLOR_PICKER),
|
||||
col.toHSB(), false);
|
||||
}
|
||||
}
|
||||
|
||||
if ((profile.isRGBW2 && !profile.inColor) || profile.isRoller) {
|
||||
// Aggregate Meter Data from different Coap updates
|
||||
int i = 1;
|
||||
double totalCurrent = 0.0;
|
||||
double totalKWH = 0.0;
|
||||
boolean updateMeter = false;
|
||||
while (i <= thingHandler.getProfile().numMeters) {
|
||||
String meter = CHANNEL_GROUP_METER + i;
|
||||
double current = thingHandler.getChannelDouble(meter, CHANNEL_METER_CURRENTWATTS);
|
||||
double total = thingHandler.getChannelDouble(meter, CHANNEL_METER_TOTALKWH);
|
||||
logger.debug("{}: {}#{}={}, total={}", thingName, meter, CHANNEL_METER_CURRENTWATTS, current,
|
||||
totalCurrent);
|
||||
totalCurrent += current >= 0 ? current : 0;
|
||||
totalKWH += total >= 0 ? total : 0;
|
||||
updateMeter |= current >= 0 | total >= 0;
|
||||
i++;
|
||||
}
|
||||
logger.debug("{}: totalCurrent={}, totalKWH={}, update={}", thingName, totalCurrent, totalKWH,
|
||||
updateMeter);
|
||||
if (updateMeter) {
|
||||
thingHandler.updateChannel(CHANNEL_GROUP_METER, CHANNEL_METER_CURRENTWATTS,
|
||||
toQuantityType(totalCurrent, DIGITS_WATT, Units.WATT));
|
||||
thingHandler.updateChannel(CHANNEL_GROUP_METER, CHANNEL_LAST_UPDATE, getTimestamp());
|
||||
}
|
||||
}
|
||||
|
||||
// Old firmware release are lacking various status values, which are not updated using CoIoT.
|
||||
// In this case we keep a refresh so it gets polled using REST. Beginning with Firmware 1.6 most
|
||||
// of the values are available
|
||||
|
|
|
@ -67,7 +67,7 @@ public class ShellyCoapJSonDTO {
|
|||
@SerializedName("I")
|
||||
String id; // ID
|
||||
@SerializedName("D")
|
||||
String desc; // Description
|
||||
String desc = ""; // Description
|
||||
@SerializedName("T")
|
||||
public String type; // Type
|
||||
@SerializedName("R")
|
||||
|
|
|
@ -16,8 +16,10 @@ import static org.openhab.binding.shelly.internal.coap.ShellyCoapJSonDTO.COIOT_P
|
|||
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.eclipse.californium.core.CoapResource;
|
||||
import org.eclipse.californium.core.CoapServer;
|
||||
|
@ -32,7 +34,6 @@ import org.eclipse.californium.core.network.config.NetworkConfig;
|
|||
import org.eclipse.californium.elements.UdpMulticastConnector;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.eclipse.jetty.util.ConcurrentHashSet;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -49,7 +50,7 @@ public class ShellyCoapServer {
|
|||
private CoapEndpoint statusEndpoint = new CoapEndpoint.Builder().build();
|
||||
private @Nullable UdpMulticastConnector statusConnector;
|
||||
private final CoapServer server = new CoapServer(NetworkConfig.getStandard(), COIOT_PORT);;
|
||||
private final Set<ShellyCoapListener> coapListeners = new ConcurrentHashSet<>();
|
||||
private final Set<ShellyCoapListener> coapListeners = ConcurrentHashMap.newKeySet();
|
||||
|
||||
protected class ShellyStatusListener extends CoapResource {
|
||||
private ShellyCoapServer listener;
|
||||
|
@ -76,7 +77,8 @@ public class ShellyCoapServer {
|
|||
}
|
||||
}
|
||||
|
||||
public synchronized void start(String localIp, ShellyCoapListener listener) throws UnknownHostException {
|
||||
public synchronized void start(String localIp, ShellyCoapListener listener)
|
||||
throws UnknownHostException, SocketException {
|
||||
if (!started) {
|
||||
logger.debug("Initializing CoIoT listener (local IP={}:{})", localIp, COIOT_PORT);
|
||||
NetworkConfig nc = NetworkConfig.getStandard();
|
||||
|
|
|
@ -31,17 +31,16 @@ public class ShellyBindingConfiguration {
|
|||
// Binding Configuration Properties
|
||||
public static final String CONFIG_DEF_HTTP_USER = "defaultUserId";
|
||||
public static final String CONFIG_DEF_HTTP_PWD = "defaultPassword";
|
||||
public static final String CONFIG_LOCAL_IP = "localIP";
|
||||
public static final String CONFIG_AUTOCOIOT = "autoCoIoT";
|
||||
|
||||
public String defaultUserId = ""; // default for http basic user id
|
||||
public String defaultPassword = ""; // default for http basic auth password
|
||||
public String localIP = ""; // default:use OH network config
|
||||
public boolean autoCoIoT = true;
|
||||
|
||||
public void updateFromProperties(Map<String, Object> properties) {
|
||||
for (Map.Entry<String, Object> e : properties.entrySet()) {
|
||||
if (e.getValue() == null) {
|
||||
continue;
|
||||
}
|
||||
switch (e.getKey()) {
|
||||
case CONFIG_DEF_HTTP_USER:
|
||||
defaultUserId = (String) e.getValue();
|
||||
|
@ -49,6 +48,9 @@ public class ShellyBindingConfiguration {
|
|||
case CONFIG_DEF_HTTP_PWD:
|
||||
defaultPassword = (String) e.getValue();
|
||||
break;
|
||||
case CONFIG_LOCAL_IP:
|
||||
localIP = (String) e.getValue();
|
||||
break;
|
||||
case CONFIG_AUTOCOIOT:
|
||||
autoCoIoT = (boolean) e.getValue();
|
||||
break;
|
||||
|
|
|
@ -29,6 +29,9 @@ public class ShellyThingConfiguration {
|
|||
public int lowBattery = 15; // threshold for battery value
|
||||
public boolean brightnessAutoOn = true; // true: turn on device if brightness > 0 is set
|
||||
|
||||
public int favoriteUP = 0; // Roller position favorite when control channel receives ON, 0=none
|
||||
public int favoriteDOWN = 0; // Roller position favorite when control channel receives ON, 0=none
|
||||
|
||||
public boolean eventsButton = false; // true: register for Relay btn_xxx events
|
||||
public boolean eventsSwitch = true; // true: register for device out_xxx events
|
||||
public boolean eventsPush = true; // true: register for short/long push events
|
||||
|
|
|
@ -33,12 +33,11 @@ import org.openhab.binding.shelly.internal.api.ShellyHttpApi;
|
|||
import org.openhab.binding.shelly.internal.config.ShellyBindingConfiguration;
|
||||
import org.openhab.binding.shelly.internal.config.ShellyThingConfiguration;
|
||||
import org.openhab.binding.shelly.internal.handler.ShellyBaseHandler;
|
||||
import org.openhab.binding.shelly.internal.util.ShellyTranslationProvider;
|
||||
import org.openhab.binding.shelly.internal.provider.ShellyTranslationProvider;
|
||||
import org.openhab.core.config.discovery.DiscoveryResult;
|
||||
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
|
||||
import org.openhab.core.config.discovery.mdns.MDNSDiscoveryParticipant;
|
||||
import org.openhab.core.i18n.LocaleProvider;
|
||||
import org.openhab.core.i18n.TranslationProvider;
|
||||
import org.openhab.core.io.net.http.HttpClientFactory;
|
||||
import org.openhab.core.thing.ThingTypeUID;
|
||||
import org.openhab.core.thing.ThingUID;
|
||||
|
@ -66,20 +65,13 @@ public class ShellyDiscoveryParticipant implements MDNSDiscoveryParticipant {
|
|||
private final HttpClient httpClient;
|
||||
private final ConfigurationAdmin configurationAdmin;
|
||||
|
||||
/**
|
||||
* OSGI Service Activation
|
||||
*
|
||||
* @param componentContext
|
||||
* @param localeProvider
|
||||
*/
|
||||
@Activate
|
||||
public ShellyDiscoveryParticipant(@Reference ConfigurationAdmin configurationAdmin,
|
||||
@Reference HttpClientFactory httpClientFactory, @Reference LocaleProvider localeProvider,
|
||||
@Reference TranslationProvider i18nProvider, ComponentContext componentContext) {
|
||||
@Reference ShellyTranslationProvider translationProvider, ComponentContext componentContext) {
|
||||
logger.debug("Activating ShellyDiscovery service");
|
||||
this.configurationAdmin = configurationAdmin;
|
||||
this.messages = new ShellyTranslationProvider(componentContext.getBundleContext().getBundle(), i18nProvider,
|
||||
localeProvider);
|
||||
this.messages = translationProvider;
|
||||
this.httpClient = httpClientFactory.getCommonHttpClient();
|
||||
bindingConfig.updateFromProperties(componentContext.getProperties());
|
||||
}
|
||||
|
@ -168,7 +160,6 @@ public class ShellyDiscoveryParticipant implements MDNSDiscoveryParticipant {
|
|||
thingUID = ShellyThingCreator.getThingUID(name, model, mode, true);
|
||||
} else {
|
||||
logger.info("{}: {}", name, messages.get("discovery.failed", address, e.toString()));
|
||||
logger.debug("{}: Discovery failed", name, e);
|
||||
}
|
||||
} catch (IllegalArgumentException e) { // maybe some format description was buggy
|
||||
logger.debug("{}: Discovery failed!", name, e);
|
||||
|
|
|
@ -34,6 +34,7 @@ public class ShellyThingCreator {
|
|||
static {
|
||||
// mapping by device type id
|
||||
THING_TYPE_MAPPING.put(SHELLYDT_1PM, THING_TYPE_SHELLY1PM_STR);
|
||||
THING_TYPE_MAPPING.put(SHELLYDT_1L, THING_TYPE_SHELLY1L_STR);
|
||||
THING_TYPE_MAPPING.put(SHELLYDT_1, THING_TYPE_SHELLY1_STR);
|
||||
THING_TYPE_MAPPING.put(SHELLYDT_3EM, THING_TYPE_SHELLY3EM_STR);
|
||||
THING_TYPE_MAPPING.put(SHELLYDT_EM, THING_TYPE_SHELLYEM_STR);
|
||||
|
@ -43,22 +44,27 @@ public class ShellyThingCreator {
|
|||
THING_TYPE_MAPPING.put(SHELLYDT_DW, THING_TYPE_SHELLYDOORWIN_STR);
|
||||
THING_TYPE_MAPPING.put(SHELLYDT_DW2, THING_TYPE_SHELLYDOORWIN2_STR);
|
||||
THING_TYPE_MAPPING.put(SHELLYDT_DUO, THING_TYPE_SHELLYDUO_STR);
|
||||
THING_TYPE_MAPPING.put(SHELLYDT_DUORGBW, THING_TYPE_SHELLYDUORGBW_STR);
|
||||
THING_TYPE_MAPPING.put(SHELLYDT_BULB, THING_TYPE_SHELLYBULB_STR);
|
||||
THING_TYPE_MAPPING.put(SHELLYDT_VINTAGE, THING_TYPE_SHELLYVINTAGE_STR);
|
||||
THING_TYPE_MAPPING.put(SHELLYDT_DIMMER, THING_TYPE_SHELLYDIMMER_STR);
|
||||
THING_TYPE_MAPPING.put(SHELLYDT_DIMMER2, THING_TYPE_SHELLYDIMMER2_STR);
|
||||
THING_TYPE_MAPPING.put(SHELLYDT_IX3, THING_TYPE_SHELLYIX3_STR);
|
||||
THING_TYPE_MAPPING.put(SHELLYDT_BUTTON1, THING_TYPE_SHELLYBUTTON1_STR);
|
||||
THING_TYPE_MAPPING.put(SHELLYDT_UNI, THING_TYPE_SHELLYUNI_STR);
|
||||
THING_TYPE_MAPPING.put(SHELLYDT_HT, THING_TYPE_SHELLYHT_STR);
|
||||
|
||||
// mapping by thing type
|
||||
THING_TYPE_MAPPING.put(THING_TYPE_SHELLY1_STR, THING_TYPE_SHELLY1_STR);
|
||||
THING_TYPE_MAPPING.put(THING_TYPE_SHELLY1PM_STR, THING_TYPE_SHELLY1PM_STR);
|
||||
THING_TYPE_MAPPING.put(THING_TYPE_SHELLY1L_STR, THING_TYPE_SHELLY1L_STR);
|
||||
THING_TYPE_MAPPING.put(THING_TYPE_SHELLY4PRO_STR, THING_TYPE_SHELLY4PRO_STR);
|
||||
THING_TYPE_MAPPING.put(THING_TYPE_SHELLYDIMMER2_STR, THING_TYPE_SHELLYDIMMER2_STR);
|
||||
THING_TYPE_MAPPING.put(THING_TYPE_SHELLYDIMMER_STR, THING_TYPE_SHELLYDIMMER_STR);
|
||||
THING_TYPE_MAPPING.put(THING_TYPE_SHELLYIX3_STR, THING_TYPE_SHELLYIX3_STR);
|
||||
THING_TYPE_MAPPING.put(THING_TYPE_SHELLY3EM_STR, THING_TYPE_SHELLY3EM_STR);
|
||||
THING_TYPE_MAPPING.put(THING_TYPE_SHELLYEM_STR, THING_TYPE_SHELLYEM_STR);
|
||||
THING_TYPE_MAPPING.put(THING_TYPE_SHELLYDUORGBW_STR, THING_TYPE_SHELLYDUORGBW_STR);
|
||||
THING_TYPE_MAPPING.put(THING_TYPE_SHELLYDUO_STR, THING_TYPE_SHELLYDUO_STR);
|
||||
THING_TYPE_MAPPING.put(THING_TYPE_SHELLYVINTAGE_STR, THING_TYPE_SHELLYVINTAGE_STR);
|
||||
THING_TYPE_MAPPING.put(THING_TYPE_SHELLYBULB_STR, THING_TYPE_SHELLYBULB_STR);
|
||||
|
@ -72,6 +78,7 @@ public class ShellyThingCreator {
|
|||
THING_TYPE_MAPPING.put(THING_TYPE_SHELLYSENSE_STR, THING_TYPE_SHELLYSENSE_STR);
|
||||
THING_TYPE_MAPPING.put(THING_TYPE_SHELLYEYE_STR, THING_TYPE_SHELLYEYE_STR);
|
||||
THING_TYPE_MAPPING.put(THING_TYPE_SHELLYBUTTON1_STR, THING_TYPE_SHELLYBUTTON1_STR);
|
||||
THING_TYPE_MAPPING.put(THING_TYPE_SHELLYUNI_STR, THING_TYPE_SHELLYUNI_STR);
|
||||
|
||||
THING_TYPE_MAPPING.put(THING_TYPE_SHELLYPROTECTED_STR, THING_TYPE_SHELLYPROTECTED_STR);
|
||||
}
|
||||
|
@ -97,7 +104,7 @@ public class ShellyThingCreator {
|
|||
String name = hostname.toLowerCase();
|
||||
String type = substringBefore(name, "-").toLowerCase();
|
||||
String devid = substringAfterLast(name, "-");
|
||||
if ((devid == null) || (type == null)) {
|
||||
if (devid.isEmpty() || type.isEmpty()) {
|
||||
throw new IllegalArgumentException("Invalid device name format: " + hostname);
|
||||
}
|
||||
|
||||
|
@ -125,12 +132,15 @@ public class ShellyThingCreator {
|
|||
|
||||
// Check general mapping
|
||||
if (!deviceType.isEmpty()) {
|
||||
String str = THING_TYPE_MAPPING.get(deviceType);
|
||||
if (str != null) {
|
||||
return str;
|
||||
String res = THING_TYPE_MAPPING.get(deviceType);
|
||||
if (res != null) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
return THING_TYPE_MAPPING.getOrDefault(type, THING_TYPE_SHELLYUNKNOWN_STR);
|
||||
String res = THING_TYPE_MAPPING.get(type);
|
||||
if (res != null) {
|
||||
return res;
|
||||
}
|
||||
return THING_TYPE_SHELLYUNKNOWN_STR;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,6 @@ import java.util.TreeMap;
|
|||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.apache.commons.lang.time.StopWatch;
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
|
@ -43,11 +42,14 @@ import org.openhab.binding.shelly.internal.coap.ShellyCoapServer;
|
|||
import org.openhab.binding.shelly.internal.config.ShellyBindingConfiguration;
|
||||
import org.openhab.binding.shelly.internal.config.ShellyThingConfiguration;
|
||||
import org.openhab.binding.shelly.internal.discovery.ShellyThingCreator;
|
||||
import org.openhab.binding.shelly.internal.provider.ShellyChannelDefinitions;
|
||||
import org.openhab.binding.shelly.internal.provider.ShellyTranslationProvider;
|
||||
import org.openhab.binding.shelly.internal.util.ShellyChannelCache;
|
||||
import org.openhab.binding.shelly.internal.util.ShellyTranslationProvider;
|
||||
import org.openhab.binding.shelly.internal.util.ShellyVersionDTO;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.OpenClosedType;
|
||||
import org.openhab.core.library.types.QuantityType;
|
||||
import org.openhab.core.thing.Channel;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
|
@ -72,7 +74,7 @@ import org.slf4j.LoggerFactory;
|
|||
@NonNullByDefault
|
||||
public class ShellyBaseHandler extends BaseThingHandler implements ShellyDeviceListener {
|
||||
protected final Logger logger = LoggerFactory.getLogger(ShellyBaseHandler.class);
|
||||
protected final ShellyChannelDefinitionsDTO channelDefinitions;
|
||||
protected final ShellyChannelDefinitions channelDefinitions;
|
||||
|
||||
public String thingName = "";
|
||||
public String thingType = "";
|
||||
|
@ -84,14 +86,14 @@ public class ShellyBaseHandler extends BaseThingHandler implements ShellyDeviceL
|
|||
private final ShellyCoapHandler coap;
|
||||
public boolean autoCoIoT = false;
|
||||
|
||||
private final ShellyTranslationProvider messages;
|
||||
public final ShellyTranslationProvider messages;
|
||||
protected boolean stopping = false;
|
||||
private boolean channelsCreated = false;
|
||||
|
||||
private long lastUptime = 0;
|
||||
private long lastAlarmTs = 0;
|
||||
private long lastTimeoutErros = -1;
|
||||
private final StopWatch watchdog = new StopWatch();
|
||||
private long watchdog = now();
|
||||
|
||||
private @Nullable ScheduledFuture<?> statusJob;
|
||||
public int scheduledUpdates = 0;
|
||||
|
@ -127,7 +129,7 @@ public class ShellyBaseHandler extends BaseThingHandler implements ShellyDeviceL
|
|||
|
||||
this.messages = translationProvider;
|
||||
this.cache = new ShellyChannelCache(this);
|
||||
this.channelDefinitions = new ShellyChannelDefinitionsDTO(messages);
|
||||
this.channelDefinitions = new ShellyChannelDefinitions(messages);
|
||||
this.bindingConfig = bindingConfig;
|
||||
|
||||
this.localIP = localIP;
|
||||
|
@ -154,7 +156,8 @@ public class ShellyBaseHandler extends BaseThingHandler implements ShellyDeviceL
|
|||
"{}: Configured Events: Button: {}, Switch (on/off): {}, Push: {}, Roller: {}, Sensor: {}, CoIoT: {}, Enable AutoCoIoT: {}",
|
||||
thingName, config.eventsButton, config.eventsSwitch, config.eventsPush, config.eventsRoller,
|
||||
config.eventsSensorReport, config.eventsCoIoT, bindingConfig.autoCoIoT);
|
||||
updateStatus(ThingStatus.UNKNOWN);
|
||||
updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.CONFIGURATION_PENDING,
|
||||
messages.get("status.unknown.initializing"));
|
||||
start = initializeThing();
|
||||
} catch (ShellyApiException e) {
|
||||
ShellyApiResult res = e.getApiResult();
|
||||
|
@ -253,6 +256,7 @@ public class ShellyBaseHandler extends BaseThingHandler implements ShellyDeviceL
|
|||
|
||||
// update thing properties
|
||||
ShellySettingsStatus status = api.getStatus();
|
||||
tmpPrf.updateFromStatus(status);
|
||||
updateProperties(tmpPrf, status);
|
||||
checkVersion(tmpPrf, status);
|
||||
if (autoCoIoT) {
|
||||
|
@ -289,11 +293,16 @@ public class ShellyBaseHandler extends BaseThingHandler implements ShellyDeviceL
|
|||
public void handleCommand(ChannelUID channelUID, Command command) {
|
||||
try {
|
||||
if (command instanceof RefreshType) {
|
||||
String channelId = channelUID.getId();
|
||||
State value = cache.getValue(channelId);
|
||||
if (value != UnDefType.NULL) {
|
||||
updateState(channelId, value);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!profile.isInitialized()) {
|
||||
logger.debug("{}: {}", thingName, messages.get("message.command.init", command));
|
||||
logger.debug("{}: {}", thingName, messages.get("command.init", command));
|
||||
initializeThing();
|
||||
} else {
|
||||
profile = getProfile(false);
|
||||
|
@ -350,7 +359,6 @@ public class ShellyBaseHandler extends BaseThingHandler implements ShellyDeviceL
|
|||
|
||||
skipUpdate++;
|
||||
ThingStatus thingStatus = getThing().getStatus();
|
||||
|
||||
if (refreshSettings || (scheduledUpdates > 0) || (skipUpdate % skipCount == 0)) {
|
||||
if (!profile.isInitialized() || ((thingStatus == ThingStatus.OFFLINE))
|
||||
|| (thingStatus == ThingStatus.UNKNOWN)) {
|
||||
|
@ -362,6 +370,7 @@ public class ShellyBaseHandler extends BaseThingHandler implements ShellyDeviceL
|
|||
|
||||
logger.trace("{}: Updating status", thingName);
|
||||
ShellySettingsStatus status = api.getStatus();
|
||||
profile.updateFromStatus(status);
|
||||
|
||||
// If status update was successful the thing must be online
|
||||
setThingOnline();
|
||||
|
@ -370,14 +379,9 @@ public class ShellyBaseHandler extends BaseThingHandler implements ShellyDeviceL
|
|||
updateChannel(CHANNEL_GROUP_DEV_STATUS, CHANNEL_DEVST_NAME, getStringType(profile.settings.name));
|
||||
updated |= this.updateDeviceStatus(status);
|
||||
updated |= ShellyComponents.updateDeviceStatus(this, status);
|
||||
// if (!channelsCreated || !cache.isEnabled() || (coap.getVersion() <
|
||||
// ShellyCoapJSonDTO.COIOT_VERSION_2)) {
|
||||
updated |= updateInputs(status);
|
||||
updated |= updateMeters(this, status);
|
||||
updated |= updateSensors(this, status);
|
||||
updated |= updateInputs(status);
|
||||
// } else {
|
||||
// logger.debug("Skipping Meter/Sensor/Input updates, because device is running CoIoT version 2");
|
||||
// }
|
||||
|
||||
// All channels must be created after the first cycle
|
||||
channelsCreated = true;
|
||||
|
@ -397,8 +401,6 @@ public class ShellyBaseHandler extends BaseThingHandler implements ShellyDeviceL
|
|||
if (isWatchdogStarted()) {
|
||||
if (!isWatchdogExpired()) {
|
||||
logger.debug("{}: Ignore API Timeout, retry later", thingName);
|
||||
} else if (profile.hasBattery) {
|
||||
logger.debug("{}: Ignore API Timeout for battery powered device", thingName);
|
||||
} else {
|
||||
logger.debug("{}: Watchdog expired after {}sec,", thingName, profile.updatePeriod);
|
||||
if (isThingOnline()) {
|
||||
|
@ -410,6 +412,11 @@ public class ShellyBaseHandler extends BaseThingHandler implements ShellyDeviceL
|
|||
} else if (e.isJSONException()) {
|
||||
status = "offline.status-error-unexpected-api-result";
|
||||
logger.debug("{}: Unable to parse API response: {}; json={}", thingName, res.getUrl(), res.response, e);
|
||||
} else if (res.isHttpTimeout()) {
|
||||
// Watchdog not started, e.g. device in sleep mode
|
||||
if (isThingOnline()) { // ignore when already offline
|
||||
status = "offline.status-error-watchdog";
|
||||
}
|
||||
} else {
|
||||
status = "offline.status-error-unexpected-api-result";
|
||||
logger.debug("{}: Unexpected API result: {}", thingName, res.response, e);
|
||||
|
@ -454,30 +461,30 @@ public class ShellyBaseHandler extends BaseThingHandler implements ShellyDeviceL
|
|||
if (!isThingOffline()) {
|
||||
logger.info("{}: Thing goes OFFLINE: {}", thingName, messages.get(messageKey));
|
||||
updateStatus(ThingStatus.OFFLINE, detail, "@text/" + messageKey);
|
||||
watchdog.reset();
|
||||
watchdog = 0;
|
||||
channelsCreated = false; // check for new channels after devices gets re-initialized (e.g. new
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void restartWatchdog() {
|
||||
watchdog = now();
|
||||
updateChannel(CHANNEL_GROUP_DEV_STATUS, CHANNEL_DEVST_HEARTBEAT, getTimestamp());
|
||||
watchdog.reset();
|
||||
watchdog.start();
|
||||
logger.trace("{}: Watchdog restarted (expires in {} sec)", thingName, profile.updatePeriod);
|
||||
}
|
||||
|
||||
private boolean isWatchdogExpired() {
|
||||
return watchdog.getTime() > profile.updatePeriod;
|
||||
long timeout = profile.hasBattery ? profile.updatePeriod : profile.updatePeriod;
|
||||
long delta = now() - watchdog;
|
||||
if ((watchdog > 0) && (delta > timeout)) {
|
||||
logger.trace("{}: Watchdog expired after {}sec (started={}, now={}", thingName, delta, watchdog, now());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isWatchdogStarted() {
|
||||
try {
|
||||
if (isThingOnline()) {
|
||||
watchdog.getStartTime();
|
||||
}
|
||||
return true;
|
||||
} catch (IllegalStateException e) {
|
||||
return false;
|
||||
}
|
||||
logger.trace("{}: Watchdog is {}", thingName, watchdog > 0 ? "started" : "inactive");
|
||||
return watchdog > 0;
|
||||
}
|
||||
|
||||
public void reinitializeThing() {
|
||||
|
@ -494,8 +501,8 @@ public class ShellyBaseHandler extends BaseThingHandler implements ShellyDeviceL
|
|||
ShellyComponents.updateDeviceStatus(this, status);
|
||||
|
||||
if (api.isInitialized() && (lastTimeoutErros != api.getTimeoutErrors())) {
|
||||
propertyUpdates.put(PROPERTY_STATS_TIMEOUTS, new Integer(api.getTimeoutErrors()).toString());
|
||||
propertyUpdates.put(PROPERTY_STATS_TRECOVERED, new Integer(api.getTimeoutsRecovered()).toString());
|
||||
propertyUpdates.put(PROPERTY_STATS_TIMEOUTS, String.valueOf(api.getTimeoutErrors()));
|
||||
propertyUpdates.put(PROPERTY_STATS_TRECOVERED, String.valueOf(api.getTimeoutsRecovered()));
|
||||
lastTimeoutErros = api.getTimeoutErrors();
|
||||
}
|
||||
|
||||
|
@ -585,8 +592,8 @@ public class ShellyBaseHandler extends BaseThingHandler implements ShellyDeviceL
|
|||
case SHELLY_EVENT_TRIPLE_SHORTPUSH:
|
||||
case SHELLY_EVENT_LONGPUSH:
|
||||
if (isButton) {
|
||||
triggerButton(group, mapButtonEvent(event));
|
||||
channel = CHANNEL_BUTTON_TRIGGER;
|
||||
triggerButton(group, idx, mapButtonEvent(event));
|
||||
channel = CHANNEL_BUTTON_TRIGGER + profile.getInputSuffix(idx);
|
||||
payload = ShellyApiJsonDTO.mapButtonEvent(event);
|
||||
} else {
|
||||
logger.debug("{}: Relay button is not in memontary or detached mode, ignore SHORT/LONGPUSH",
|
||||
|
@ -705,7 +712,7 @@ public class ShellyBaseHandler extends BaseThingHandler implements ShellyDeviceL
|
|||
config.deviceIp = saddr;
|
||||
}
|
||||
} catch (UnknownHostException e) {
|
||||
logger.debug("{}: Unable to resolehostname {}", thingName, config.deviceIp);
|
||||
logger.debug("{}: Unable to resolve hostname {}", thingName, config.deviceIp);
|
||||
}
|
||||
|
||||
config.localIp = localIP;
|
||||
|
@ -722,7 +729,20 @@ public class ShellyBaseHandler extends BaseThingHandler implements ShellyDeviceL
|
|||
config.updateInterval = UPDATE_MIN_DELAY;
|
||||
}
|
||||
|
||||
// Try to get updatePeriod from properties
|
||||
// For battery devinities the REST call to get the settings will most likely fail, because the device is in
|
||||
// sleep mode. Therefore we use the last saved property value as default. Will be overwritten, when device is
|
||||
// initialized successfully by the REST call.
|
||||
String lastPeriod = getString(properties.get(PROPERTY_UPDATE_PERIOD));
|
||||
if (!lastPeriod.isEmpty()) {
|
||||
int period = Integer.parseInt(lastPeriod);
|
||||
if (period > 0) {
|
||||
profile.updatePeriod = period;
|
||||
}
|
||||
}
|
||||
|
||||
skipCount = config.updateInterval / UPDATE_STATUS_INTERVAL_SECONDS;
|
||||
logger.trace("{}: updateInterval = {}s -> skipCount = {}", thingName, config.updateInterval, skipCount);
|
||||
}
|
||||
|
||||
private void checkVersion(ShellyDeviceProfile prf, ShellySettingsStatus status) {
|
||||
|
@ -743,13 +763,13 @@ public class ShellyBaseHandler extends BaseThingHandler implements ShellyDeviceL
|
|||
}
|
||||
autoCoIoT = true;
|
||||
}
|
||||
if (status.update.hasUpdate && !version.checkBeta(getString(prf.fwVersion))) {
|
||||
logger.info("{}: {}", thingName,
|
||||
messages.get("versioncheck.update", status.update.oldVersion, status.update.newVersion));
|
||||
}
|
||||
} catch (NullPointerException e) { // could be inconsistant format of beta version
|
||||
logger.debug("{}: {}", thingName, messages.get("versioncheck.failed", prf.fwVersion));
|
||||
}
|
||||
if (status.update.hasUpdate) {
|
||||
logger.info("{}: {}", thingName,
|
||||
messages.get("versioncheck.update", status.update.oldVersion, status.update.newVersion));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -841,57 +861,34 @@ public class ShellyBaseHandler extends BaseThingHandler implements ShellyDeviceL
|
|||
* @param status Shelly device status
|
||||
* @return true: one or more inputs were updated
|
||||
*/
|
||||
public boolean updateInputs(String groupName, ShellySettingsStatus status, int index) {
|
||||
if ((status.input == null)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean updated = false;
|
||||
if (index == 0) {
|
||||
// RGBW2: a single int rather than an array
|
||||
updated |= updateChannel(groupName, CHANNEL_INPUT,
|
||||
getInteger(status.input) == 0 ? OnOffType.OFF : OnOffType.ON);
|
||||
} else {
|
||||
if (profile.isDimmer || profile.isRoller) {
|
||||
ShellyInputState state1 = status.inputs.get(0);
|
||||
ShellyInputState state2 = status.inputs.get(1);
|
||||
logger.trace("{}: Updating {}#input1 with {}, input2 with {}", thingName, groupName,
|
||||
getOnOff(state1.input), getOnOff(state2.input));
|
||||
updated |= updateChannel(groupName, CHANNEL_INPUT + "1", getOnOff(state1.input));
|
||||
updated |= updateChannel(groupName, CHANNEL_INPUT + "2", getOnOff(state2.input));
|
||||
} else {
|
||||
if (index < status.inputs.size()) {
|
||||
ShellyInputState state = status.inputs.get(index);
|
||||
updated |= updateChannel(groupName, CHANNEL_INPUT, getOnOff(state.input));
|
||||
} else {
|
||||
logger.debug("{}: Unable to update input, index is out of range ({}/{}", thingName, index,
|
||||
status.inputs.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
return updated;
|
||||
}
|
||||
|
||||
public boolean updateInputs(ShellySettingsStatus status) {
|
||||
boolean updated = false;
|
||||
String groupName = "";
|
||||
|
||||
if (status.input != null) {
|
||||
// RGBW2: a single int rather than an array
|
||||
return updateChannel(groupName, CHANNEL_INPUT,
|
||||
getInteger(status.input) == 0 ? OnOffType.OFF : OnOffType.ON);
|
||||
}
|
||||
if (status.inputs != null) {
|
||||
int idx = 0;
|
||||
boolean multiInput = status.inputs.size() >= 2; // device has multiple SW (inputs)
|
||||
for (ShellyInputState input : status.inputs) {
|
||||
String group = profile.getControlGroup(idx);
|
||||
updated |= updateChannel(group, CHANNEL_INPUT, getOnOff(input.input));
|
||||
String suffix = multiInput ? profile.getInputSuffix(idx) : "";
|
||||
|
||||
if (!areChannelsCreated()) {
|
||||
updateChannelDefinitions(
|
||||
ShellyChannelDefinitions.createInputChannels(thing, profile, status, group));
|
||||
}
|
||||
|
||||
updated |= updateChannel(group, CHANNEL_INPUT + suffix, getOnOff(input.input));
|
||||
if (input.event != null) {
|
||||
updated |= updateChannel(group, CHANNEL_STATUS_EVENTTYPE, getStringType(input.event));
|
||||
updated |= updateChannel(group, CHANNEL_STATUS_EVENTCOUNT, getDecimal(input.eventCount));
|
||||
updated |= updateChannel(group, CHANNEL_STATUS_EVENTTYPE + suffix, getStringType(input.event));
|
||||
updated |= updateChannel(group, CHANNEL_STATUS_EVENTCOUNT + suffix, getDecimal(input.eventCount));
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
} else {
|
||||
if (status.input != null) {
|
||||
// RGBW2: a single int rather than an array
|
||||
return updateChannel(profile.getControlGroup(0), CHANNEL_INPUT,
|
||||
getInteger(status.input) == 0 ? OnOffType.OFF : OnOffType.ON);
|
||||
}
|
||||
}
|
||||
return updated;
|
||||
}
|
||||
|
@ -911,14 +908,16 @@ public class ShellyBaseHandler extends BaseThingHandler implements ShellyDeviceL
|
|||
return changed;
|
||||
}
|
||||
|
||||
public void triggerButton(String group, String value) {
|
||||
public void triggerButton(String group, int idx, String value) {
|
||||
String trigger = mapButtonEvent(value);
|
||||
if (trigger.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
logger.debug("{}: Update button state with {}/{}", thingName, value, trigger);
|
||||
triggerChannel(group, CHANNEL_BUTTON_TRIGGER, trigger);
|
||||
triggerChannel(group,
|
||||
profile.isRoller ? CHANNEL_EVENT_TRIGGER : CHANNEL_BUTTON_TRIGGER + profile.getInputSuffix(idx),
|
||||
trigger);
|
||||
updateChannel(group, CHANNEL_LAST_UPDATE, getTimestamp());
|
||||
if (!profile.hasBattery) {
|
||||
// refresh status of the input channel
|
||||
|
@ -927,8 +926,9 @@ public class ShellyBaseHandler extends BaseThingHandler implements ShellyDeviceL
|
|||
}
|
||||
|
||||
public void publishState(String channelId, State value) {
|
||||
if (!stopping) {
|
||||
updateState(channelId.contains("$") ? substringBefore(channelId, "$") : channelId, value);
|
||||
String id = channelId.contains("$") ? substringBefore(channelId, "$") : channelId;
|
||||
if (!stopping && isLinked(id)) {
|
||||
updateState(id, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -937,14 +937,26 @@ public class ShellyBaseHandler extends BaseThingHandler implements ShellyDeviceL
|
|||
}
|
||||
|
||||
public boolean updateChannel(String channelId, State value, boolean force) {
|
||||
return !stopping && (channelId.contains("$") || isLinked(channelId))
|
||||
&& cache.updateChannel(channelId, value, force);
|
||||
return !stopping && cache.updateChannel(channelId, value, force);
|
||||
}
|
||||
|
||||
public State getChannelValue(String group, String channel) {
|
||||
return cache.getValue(group, channel);
|
||||
}
|
||||
|
||||
public double getChannelDouble(String group, String channel) {
|
||||
State value = getChannelValue(group, channel);
|
||||
if (value != UnDefType.NULL) {
|
||||
if (value instanceof QuantityType) {
|
||||
return ((QuantityType<?>) value).toBigDecimal().doubleValue();
|
||||
}
|
||||
if (value instanceof DecimalType) {
|
||||
return ((DecimalType) value).doubleValue();
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update Thing's channels according to available status information from the API
|
||||
*
|
||||
|
@ -1078,14 +1090,11 @@ public class ShellyBaseHandler extends BaseThingHandler implements ShellyDeviceL
|
|||
properties.put(PROPERTY_NUM_RELAYS, String.valueOf(profile.numRelays));
|
||||
properties.put(PROPERTY_NUM_ROLLERS, String.valueOf(profile.numRollers));
|
||||
properties.put(PROPERTY_NUM_METER, String.valueOf(profile.numMeters));
|
||||
properties.put(PROPERTY_UPDATE_PERIOD, String.valueOf(profile.updatePeriod));
|
||||
if (!profile.hwRev.isEmpty()) {
|
||||
properties.put(PROPERTY_HWREV, profile.hwRev);
|
||||
properties.put(PROPERTY_HWBATCH, profile.hwBatchId);
|
||||
}
|
||||
|
||||
if (profile.updatePeriod >= 0) {
|
||||
properties.put(PROPERTY_UPDATE_PERIOD, String.valueOf(profile.updatePeriod));
|
||||
}
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
|
|
|
@ -1,393 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) 2010-2021 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.handler;
|
||||
|
||||
import static org.openhab.binding.shelly.internal.ShellyBindingConstants.*;
|
||||
import static org.openhab.binding.shelly.internal.util.ShellyUtils.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellyControlRoller;
|
||||
import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellySettingsEMeter;
|
||||
import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellySettingsMeter;
|
||||
import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellySettingsRelay;
|
||||
import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellySettingsStatus;
|
||||
import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellyStatusRelay;
|
||||
import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellyStatusSensor;
|
||||
import org.openhab.binding.shelly.internal.api.ShellyDeviceProfile;
|
||||
import org.openhab.binding.shelly.internal.util.ShellyTranslationProvider;
|
||||
import org.openhab.core.thing.Channel;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.binding.builder.ChannelBuilder;
|
||||
import org.openhab.core.thing.type.ChannelTypeUID;
|
||||
|
||||
/**
|
||||
* The {@link ShellyCHANNEL_DEFINITIONSDTO} defines channel information for dynamically created channels. Those will be
|
||||
* added on the first thing status update
|
||||
*
|
||||
* @author Markus Michels - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ShellyChannelDefinitionsDTO {
|
||||
|
||||
private static final ChannelMap CHANNEL_DEFINITIONS = new ChannelMap();
|
||||
|
||||
// shortcuts to avoid line breaks (make code more readable)
|
||||
private static final String CHGR_DEVST = CHANNEL_GROUP_DEV_STATUS;
|
||||
private static final String CHGR_RELAY = CHANNEL_GROUP_RELAY_CONTROL;
|
||||
private static final String CHGR_ROLLER = CHANNEL_GROUP_ROL_CONTROL;
|
||||
private static final String CHGR_STATUS = CHANNEL_GROUP_STATUS;
|
||||
private static final String CHGR_METER = CHANNEL_GROUP_METER;
|
||||
private static final String CHGR_SENSOR = CHANNEL_GROUP_SENSOR;
|
||||
private static final String CHGR_BAT = CHANNEL_GROUP_BATTERY;
|
||||
|
||||
public static final String ITEM_TYPE_NUMBER = "Number";
|
||||
public static final String ITEM_TYPE_STRING = "String";
|
||||
public static final String ITEM_TYPE_SWITCH = "Switch";
|
||||
public static final String ITEM_TYPE_CONTACT = "Contact";
|
||||
public static final String ITEM_TYPE_DATETIME = "DateTime";
|
||||
public static final String ITEM_TYPE_TEMP = "Number:Temperature";
|
||||
public static final String ITEM_TYPE_LUX = "Number:Illuminance";
|
||||
public static final String ITEM_TYPE_POWER = "Number:Power";
|
||||
public static final String ITEM_TYPE_ENERGY = "Number:Energy";
|
||||
public static final String ITEM_TYPE_VOLT = "Number:ElectricPotential";
|
||||
public static final String ITEM_TYPE_AMP = "Number:ElectricPotential";
|
||||
public static final String ITEM_TYPE_PERCENT = "Number:Dimensionless";
|
||||
public static final String ITEM_TYPE_ANGLE = "Number:Angle";
|
||||
|
||||
public static final String PREFIX_GROUP = "definitions.shelly.group.";
|
||||
public static final String PREFIX_CHANNEL = "channel-type.shelly.";
|
||||
public static final String SUFFIX_LABEL = ".label";
|
||||
public static final String SUFFIX_DESCR = ".description";
|
||||
|
||||
public ShellyChannelDefinitionsDTO(ShellyTranslationProvider m) {
|
||||
// Device: Internal Temp
|
||||
CHANNEL_DEFINITIONS
|
||||
// Device
|
||||
.add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_DEVST_NAME, "deviceName", ITEM_TYPE_STRING))
|
||||
.add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_DEVST_ITEMP, "deviceTemp", ITEM_TYPE_TEMP))
|
||||
.add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_DEVST_WAKEUP, "sensorWakeup", ITEM_TYPE_STRING))
|
||||
.add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_DEVST_ACCUWATTS, "meterAccuWatts", ITEM_TYPE_POWER))
|
||||
.add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_DEVST_ACCUTOTAL, "meterAccuTotal", ITEM_TYPE_POWER))
|
||||
.add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_DEVST_ACCURETURNED, "meterAccuReturned", ITEM_TYPE_POWER))
|
||||
.add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_DEVST_CHARGER, "charger", ITEM_TYPE_SWITCH))
|
||||
.add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_LED_STATUS_DISABLE, "ledStatusDisable", ITEM_TYPE_SWITCH))
|
||||
.add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_LED_POWER_DISABLE, "ledPowerDisable", ITEM_TYPE_SWITCH))
|
||||
.add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_DEVST_SELFTTEST, "selfTest", ITEM_TYPE_STRING))
|
||||
.add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_DEVST_UPTIME, "uptime", ITEM_TYPE_NUMBER))
|
||||
.add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_DEVST_HEARTBEAT, "heartBeat", ITEM_TYPE_DATETIME))
|
||||
.add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_DEVST_UPDATE, "updateAvailable", ITEM_TYPE_SWITCH))
|
||||
|
||||
// Relay
|
||||
.add(new ShellyChannel(m, CHGR_RELAY, CHANNEL_OUTPUT_NAME, "outputName", ITEM_TYPE_STRING))
|
||||
|
||||
// Roller
|
||||
.add(new ShellyChannel(m, CHGR_ROLLER, CHANNEL_ROL_CONTROL_STATE, "rollerState", ITEM_TYPE_STRING))
|
||||
|
||||
// RGBW2
|
||||
.add(new ShellyChannel(m, CHANNEL_GROUP_LIGHT_CONTROL, CHANNEL_INPUT, "inputState", ITEM_TYPE_SWITCH))
|
||||
|
||||
// Power Meter
|
||||
.add(new ShellyChannel(m, CHGR_METER, CHANNEL_METER_CURRENTWATTS, "meterWatts", ITEM_TYPE_POWER))
|
||||
.add(new ShellyChannel(m, CHGR_METER, CHANNEL_METER_TOTALKWH, "meterTotal", ITEM_TYPE_ENERGY))
|
||||
.add(new ShellyChannel(m, CHGR_METER, CHANNEL_METER_LASTMIN1, "lastPower1", ITEM_TYPE_ENERGY))
|
||||
.add(new ShellyChannel(m, CHGR_METER, CHANNEL_LAST_UPDATE, "lastUpdate", ITEM_TYPE_DATETIME))
|
||||
|
||||
// EMeter
|
||||
.add(new ShellyChannel(m, CHGR_METER, CHANNEL_EMETER_TOTALRET, "meterReturned", ITEM_TYPE_ENERGY))
|
||||
.add(new ShellyChannel(m, CHGR_METER, CHANNEL_EMETER_REACTWATTS, "meterReactive", ITEM_TYPE_POWER))
|
||||
.add(new ShellyChannel(m, CHGR_METER, CHANNEL_EMETER_VOLTAGE, "meterVoltage", ITEM_TYPE_VOLT))
|
||||
.add(new ShellyChannel(m, CHGR_METER, CHANNEL_EMETER_CURRENT, "meterCurrent", ITEM_TYPE_AMP))
|
||||
.add(new ShellyChannel(m, CHGR_METER, CHANNEL_EMETER_PFACTOR, "meterPowerFactor", ITEM_TYPE_NUMBER))
|
||||
|
||||
// Sensors
|
||||
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_TEMP, "sensorTemp", ITEM_TYPE_TEMP))
|
||||
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_HUM, "sensorHumidity", ITEM_TYPE_PERCENT))
|
||||
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_LUX, "sensorLux", ITEM_TYPE_LUX))
|
||||
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_ILLUM, "sensorIllumination", ITEM_TYPE_STRING))
|
||||
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_CONTACT, "sensorContact", ITEM_TYPE_CONTACT))
|
||||
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_SSTATE, "sensorState", ITEM_TYPE_STRING))
|
||||
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_VIBRATION, "sensorVibration", ITEM_TYPE_SWITCH))
|
||||
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_TILT, "sensorTilt", ITEM_TYPE_ANGLE))
|
||||
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_MOTION, "sensorMotion", ITEM_TYPE_SWITCH))
|
||||
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_FLOOD, "sensorFlood", ITEM_TYPE_SWITCH))
|
||||
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_SMOKE, "sensorSmoke", ITEM_TYPE_SWITCH))
|
||||
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_PPM, "sensorPPM", ITEM_TYPE_NUMBER))
|
||||
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_VALVE, "sensorValve", ITEM_TYPE_STRING))
|
||||
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_ALARM_STATE, "alarmState", ITEM_TYPE_STRING))
|
||||
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_ERROR, "sensorError", ITEM_TYPE_STRING))
|
||||
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_LAST_UPDATE, "lastUpdate", ITEM_TYPE_DATETIME))
|
||||
|
||||
// Button/ix3
|
||||
.add(new ShellyChannel(m, CHGR_STATUS, CHANNEL_INPUT, "inputState", ITEM_TYPE_SWITCH))
|
||||
.add(new ShellyChannel(m, CHGR_STATUS, CHANNEL_STATUS_EVENTTYPE, "eventType", ITEM_TYPE_STRING))
|
||||
.add(new ShellyChannel(m, CHGR_STATUS, CHANNEL_STATUS_EVENTCOUNT, "eventCount", ITEM_TYPE_NUMBER))
|
||||
.add(new ShellyChannel(m, CHGR_STATUS, CHANNEL_BUTTON_TRIGGER, "system.button", ITEM_TYPE_STRING))
|
||||
.add(new ShellyChannel(m, CHGR_STATUS, CHANNEL_LAST_UPDATE, "lastUpdate", ITEM_TYPE_DATETIME))
|
||||
|
||||
// Addon with external sensors
|
||||
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_ESENDOR_TEMP1, "sensorExtTemp", ITEM_TYPE_TEMP))
|
||||
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_ESENDOR_TEMP2, "sensorExtTemp", ITEM_TYPE_TEMP))
|
||||
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_ESENDOR_TEMP3, "sensorExtTemp", ITEM_TYPE_TEMP))
|
||||
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_ESENDOR_HUMIDITY, "sensorExtHum", ITEM_TYPE_PERCENT))
|
||||
|
||||
// Battery
|
||||
.add(new ShellyChannel(m, CHGR_BAT, CHANNEL_SENSOR_BAT_LEVEL, "system:battery-level",
|
||||
ITEM_TYPE_PERCENT))
|
||||
.add(new ShellyChannel(m, CHGR_BAT, CHANNEL_SENSOR_BAT_LOW, "system:low-battery", ITEM_TYPE_SWITCH));
|
||||
}
|
||||
|
||||
public static ShellyChannel getDefinition(String channelName) throws IllegalArgumentException {
|
||||
String group = substringBefore(channelName, "#");
|
||||
String channel = substringAfter(channelName, "#");
|
||||
if (group.contains(CHANNEL_GROUP_METER)) {
|
||||
group = CHANNEL_GROUP_METER; // map meter1..n to meter
|
||||
} else if (group.contains(CHANNEL_GROUP_RELAY_CONTROL)) {
|
||||
group = CHANNEL_GROUP_RELAY_CONTROL; // map meter1..n to meter
|
||||
} else if (group.contains(CHANNEL_GROUP_LIGHT_CHANNEL)) {
|
||||
group = CHANNEL_GROUP_LIGHT_CHANNEL;
|
||||
} else if (group.contains(CHANNEL_GROUP_STATUS)) {
|
||||
group = CHANNEL_GROUP_STATUS; // map status1..n to meter
|
||||
}
|
||||
String channelId = group + "#" + channel;
|
||||
return CHANNEL_DEFINITIONS.get(channelId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Auto-create relay channels depending on relay type/mode
|
||||
*
|
||||
* @return ArrayList<Channel> of channels to be added to the thing
|
||||
*/
|
||||
public static Map<String, Channel> createDeviceChannels(final Thing thing, final ShellyDeviceProfile profile,
|
||||
final ShellySettingsStatus status) {
|
||||
Map<String, Channel> add = new LinkedHashMap<>();
|
||||
|
||||
addChannel(thing, add, profile.settings.name != null, CHGR_DEVST, CHANNEL_DEVST_NAME);
|
||||
|
||||
if (!profile.isSensor) {
|
||||
// Only some devices report the internal device temp
|
||||
addChannel(thing, add, (status.tmp != null) || (status.temperature != null), CHGR_DEVST,
|
||||
CHANNEL_DEVST_ITEMP);
|
||||
}
|
||||
|
||||
// RGBW2
|
||||
addChannel(thing, add, status.input != null, CHANNEL_GROUP_LIGHT_CONTROL, CHANNEL_INPUT);
|
||||
|
||||
// If device has more than 1 meter the channel accumulatedWatts receives the accumulated value
|
||||
boolean accuChannel = (((status.meters != null) && (status.meters.size() > 1) && !profile.isRoller
|
||||
&& !profile.isRGBW2) || ((status.emeters != null && status.emeters.size() > 1)));
|
||||
addChannel(thing, add, accuChannel, CHGR_DEVST, CHANNEL_DEVST_ACCUWATTS);
|
||||
addChannel(thing, add, accuChannel, CHGR_DEVST, CHANNEL_DEVST_ACCUTOTAL);
|
||||
addChannel(thing, add, accuChannel && (status.emeters != null), CHGR_DEVST, CHANNEL_DEVST_ACCURETURNED);
|
||||
addChannel(thing, add, true, CHGR_DEVST, CHANNEL_DEVST_UPDATE);
|
||||
addChannel(thing, add, true, CHGR_DEVST, CHANNEL_DEVST_UPTIME);
|
||||
addChannel(thing, add, true, CHGR_DEVST, CHANNEL_DEVST_HEARTBEAT);
|
||||
|
||||
if (profile.settings.ledPowerDisable != null) {
|
||||
addChannel(thing, add, true, CHGR_DEVST, CHANNEL_LED_POWER_DISABLE);
|
||||
}
|
||||
if (profile.settings.ledStatusDisable != null) {
|
||||
addChannel(thing, add, true, CHGR_DEVST, CHANNEL_LED_STATUS_DISABLE); // WiFi status LED
|
||||
}
|
||||
return add;
|
||||
}
|
||||
|
||||
/**
|
||||
* Auto-create relay channels depending on relay type/mode
|
||||
*
|
||||
* @return ArrayList<Channel> of channels to be added to the thing
|
||||
*/
|
||||
public static Map<String, Channel> createRelayChannels(final Thing thing, final ShellyDeviceProfile profile,
|
||||
final ShellyStatusRelay relay, int idx) {
|
||||
Map<String, Channel> add = new LinkedHashMap<>();
|
||||
String group = profile.getControlGroup(idx);
|
||||
|
||||
ShellySettingsRelay rs = profile.settings.relays.get(idx);
|
||||
addChannel(thing, add, rs.name != null, group, CHANNEL_OUTPUT_NAME);
|
||||
|
||||
// Shelly 1/1PM Addon
|
||||
if (relay.extTemperature != null) {
|
||||
addChannel(thing, add, relay.extTemperature.sensor1 != null, CHGR_SENSOR, CHANNEL_ESENDOR_TEMP1);
|
||||
addChannel(thing, add, relay.extTemperature.sensor2 != null, CHGR_SENSOR, CHANNEL_ESENDOR_TEMP2);
|
||||
addChannel(thing, add, relay.extTemperature.sensor3 != null, CHGR_SENSOR, CHANNEL_ESENDOR_TEMP3);
|
||||
}
|
||||
if (relay.extHumidity != null) {
|
||||
addChannel(thing, add, relay.extHumidity.sensor1 != null, CHGR_SENSOR, CHANNEL_ESENDOR_HUMIDITY);
|
||||
}
|
||||
|
||||
return add;
|
||||
}
|
||||
|
||||
public static Map<String, Channel> createRollerChannels(Thing thing, final ShellyControlRoller roller) {
|
||||
Map<String, Channel> add = new LinkedHashMap<>();
|
||||
addChannel(thing, add, roller.state != null, CHGR_ROLLER, CHANNEL_ROL_CONTROL_STATE);
|
||||
return add;
|
||||
}
|
||||
|
||||
public static Map<String, Channel> createMeterChannels(Thing thing, final ShellySettingsMeter meter, String group) {
|
||||
Map<String, Channel> newChannels = new LinkedHashMap<>();
|
||||
addChannel(thing, newChannels, meter.power != null, group, CHANNEL_METER_CURRENTWATTS);
|
||||
addChannel(thing, newChannels, meter.total != null, group, CHANNEL_METER_TOTALKWH);
|
||||
if (meter.counters != null) {
|
||||
addChannel(thing, newChannels, meter.counters[0] != null, group, CHANNEL_METER_LASTMIN1);
|
||||
}
|
||||
addChannel(thing, newChannels, meter.timestamp != null, group, CHANNEL_LAST_UPDATE);
|
||||
return newChannels;
|
||||
}
|
||||
|
||||
public static Map<String, Channel> createEMeterChannels(final Thing thing, final ShellySettingsEMeter emeter,
|
||||
String group) {
|
||||
Map<String, Channel> newChannels = new LinkedHashMap<>();
|
||||
addChannel(thing, newChannels, emeter.power != null, group, CHANNEL_METER_CURRENTWATTS);
|
||||
addChannel(thing, newChannels, emeter.total != null, group, CHANNEL_METER_TOTALKWH);
|
||||
addChannel(thing, newChannels, emeter.totalReturned != null, group, CHANNEL_EMETER_TOTALRET);
|
||||
addChannel(thing, newChannels, emeter.reactive != null, group, CHANNEL_EMETER_REACTWATTS);
|
||||
addChannel(thing, newChannels, emeter.voltage != null, group, CHANNEL_EMETER_VOLTAGE);
|
||||
addChannel(thing, newChannels, emeter.current != null, group, CHANNEL_EMETER_CURRENT);
|
||||
addChannel(thing, newChannels, emeter.pf != null, group, CHANNEL_EMETER_PFACTOR);
|
||||
addChannel(thing, newChannels, true, group, CHANNEL_LAST_UPDATE);
|
||||
return newChannels;
|
||||
}
|
||||
|
||||
public static Map<String, Channel> createSensorChannels(final Thing thing, final ShellyDeviceProfile profile,
|
||||
final ShellyStatusSensor sdata) {
|
||||
Map<String, Channel> newChannels = new LinkedHashMap<>();
|
||||
|
||||
// Sensor data
|
||||
addChannel(thing, newChannels, sdata.tmp != null, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_TEMP);
|
||||
addChannel(thing, newChannels, sdata.hum != null, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_HUM);
|
||||
addChannel(thing, newChannels, sdata.lux != null, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_LUX);
|
||||
if (sdata.accel != null) {
|
||||
addChannel(thing, newChannels, sdata.accel.vibration != null, CHANNEL_GROUP_SENSOR,
|
||||
CHANNEL_SENSOR_VIBRATION);
|
||||
addChannel(thing, newChannels, sdata.accel.tilt != null, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_TILT);
|
||||
}
|
||||
addChannel(thing, newChannels, sdata.flood != null, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_FLOOD);
|
||||
addChannel(thing, newChannels, sdata.smoke != null, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_FLOOD);
|
||||
addChannel(thing, newChannels, sdata.lux != null && sdata.lux.illumination != null, CHANNEL_GROUP_SENSOR,
|
||||
CHANNEL_SENSOR_ILLUM);
|
||||
addChannel(thing, newChannels, sdata.contact != null && sdata.contact.state != null, CHANNEL_GROUP_SENSOR,
|
||||
CHANNEL_SENSOR_CONTACT);
|
||||
addChannel(thing, newChannels, sdata.motion != null, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_MOTION);
|
||||
addChannel(thing, newChannels, sdata.charger != null, CHGR_DEVST, CHANNEL_DEVST_CHARGER);
|
||||
addChannel(thing, newChannels, sdata.sensorError != null, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_ERROR);
|
||||
addChannel(thing, newChannels, sdata.actReasons != null, CHGR_DEVST, CHANNEL_DEVST_WAKEUP);
|
||||
|
||||
// Gas
|
||||
if (sdata.gasSensor != null) {
|
||||
addChannel(thing, newChannels, sdata.gasSensor.selfTestState != null, CHGR_DEVST, CHANNEL_DEVST_SELFTTEST);
|
||||
addChannel(thing, newChannels, sdata.gasSensor.sensorState != null, CHANNEL_GROUP_SENSOR,
|
||||
CHANNEL_SENSOR_SSTATE);
|
||||
addChannel(thing, newChannels, sdata.concentration != null && sdata.concentration.ppm != null,
|
||||
CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_PPM);
|
||||
addChannel(thing, newChannels, sdata.valves != null, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_VALVE);
|
||||
addChannel(thing, newChannels, sdata.gasSensor.sensorState != null, CHANNEL_GROUP_SENSOR,
|
||||
CHANNEL_SENSOR_ALARM_STATE);
|
||||
}
|
||||
|
||||
// Battery
|
||||
if (sdata.bat != null) {
|
||||
addChannel(thing, newChannels, sdata.bat.value != null, CHANNEL_GROUP_BATTERY, CHANNEL_SENSOR_BAT_LEVEL);
|
||||
addChannel(thing, newChannels, sdata.bat.value != null, CHANNEL_GROUP_BATTERY, CHANNEL_SENSOR_BAT_LOW);
|
||||
}
|
||||
|
||||
addChannel(thing, newChannels, true, profile.getControlGroup(0), CHANNEL_LAST_UPDATE);
|
||||
return newChannels;
|
||||
}
|
||||
|
||||
private static void addChannel(Thing thing, Map<String, Channel> newChannels, boolean supported, String group,
|
||||
String channelName) throws IllegalArgumentException {
|
||||
if (supported) {
|
||||
final String channelId = group + "#" + channelName;
|
||||
final ShellyChannel channelDef = getDefinition(channelId);
|
||||
final ChannelUID channelUID = new ChannelUID(thing.getUID(), channelId);
|
||||
final ChannelTypeUID channelTypeUID = channelDef.typeId.contains("system:")
|
||||
? new ChannelTypeUID(channelDef.typeId)
|
||||
: new ChannelTypeUID(BINDING_ID, channelDef.typeId);
|
||||
Channel channel = ChannelBuilder.create(channelUID, channelDef.itemType).withType(channelTypeUID).build();
|
||||
newChannels.put(channelId, channel);
|
||||
}
|
||||
}
|
||||
|
||||
public class ShellyChannel {
|
||||
private final ShellyTranslationProvider messages;
|
||||
public String group = "";
|
||||
public String groupLabel = "";
|
||||
public String groupDescription = "";
|
||||
|
||||
public String channel = "";
|
||||
public String label = "";
|
||||
public String description = "";
|
||||
public String itemType = "";
|
||||
public String typeId = "";
|
||||
public String category = "";
|
||||
public Set<String> tags = new HashSet<>();
|
||||
|
||||
public ShellyChannel(ShellyTranslationProvider messages, String group, String channel, String typeId,
|
||||
String itemType, String... category) {
|
||||
this.messages = messages;
|
||||
this.group = group;
|
||||
this.channel = channel;
|
||||
this.itemType = itemType;
|
||||
this.typeId = typeId;
|
||||
|
||||
groupLabel = getText(PREFIX_GROUP + group + SUFFIX_LABEL);
|
||||
groupDescription = getText(PREFIX_GROUP + group + SUFFIX_DESCR);
|
||||
label = getText(PREFIX_CHANNEL + channel + SUFFIX_LABEL);
|
||||
description = getText(PREFIX_CHANNEL + channel + SUFFIX_DESCR);
|
||||
}
|
||||
|
||||
public String getChanneId() {
|
||||
return group + "#" + channel;
|
||||
}
|
||||
|
||||
private String getText(String key) {
|
||||
String text = messages.get(key);
|
||||
return text != null ? text : "";
|
||||
}
|
||||
}
|
||||
|
||||
public static class ChannelMap {
|
||||
private final Map<String, ShellyChannel> map = new LinkedHashMap<>();
|
||||
|
||||
private ChannelMap add(ShellyChannel def) {
|
||||
map.put(def.getChanneId(), def);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ShellyChannel get(String channelName) throws IllegalArgumentException {
|
||||
ShellyChannel def = null;
|
||||
if (channelName.contains("#")) {
|
||||
def = map.get(channelName);
|
||||
}
|
||||
for (HashMap.Entry<String, ShellyChannel> entry : map.entrySet()) {
|
||||
if (entry.getValue().channel.contains("#" + channelName)) {
|
||||
def = entry.getValue();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (def == null) {
|
||||
throw new IllegalArgumentException("Channel definition for " + channelName + " not found!");
|
||||
}
|
||||
|
||||
return def;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -65,16 +65,16 @@ public class ShellyColorUtils {
|
|||
setTemp(col.temp);
|
||||
}
|
||||
|
||||
void setMode(String mode) {
|
||||
public void setMode(String mode) {
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
void setMinMaxTemp(int min, int max) {
|
||||
public void setMinMaxTemp(int min, int max) {
|
||||
minTemp = min;
|
||||
maxTemp = max;
|
||||
}
|
||||
|
||||
boolean setRGBW(int red, int green, int blue, int white) {
|
||||
public boolean setRGBW(int red, int green, int blue, int white) {
|
||||
setRed(red);
|
||||
setGreen(green);
|
||||
setBlue(blue);
|
||||
|
@ -82,56 +82,56 @@ public class ShellyColorUtils {
|
|||
return true;
|
||||
}
|
||||
|
||||
boolean setRed(int value) {
|
||||
public boolean setRed(int value) {
|
||||
boolean changed = red != value;
|
||||
red = value;
|
||||
percentRed = toPercent(red);
|
||||
return changed;
|
||||
}
|
||||
|
||||
boolean setGreen(int value) {
|
||||
public boolean setGreen(int value) {
|
||||
boolean changed = green != value;
|
||||
green = value;
|
||||
percentGreen = toPercent(green);
|
||||
return changed;
|
||||
}
|
||||
|
||||
boolean setBlue(int value) {
|
||||
public boolean setBlue(int value) {
|
||||
boolean changed = blue != value;
|
||||
blue = value;
|
||||
percentBlue = toPercent(blue);
|
||||
return changed;
|
||||
}
|
||||
|
||||
boolean setWhite(int value) {
|
||||
public boolean setWhite(int value) {
|
||||
boolean changed = white != value;
|
||||
white = value;
|
||||
percentWhite = toPercent(white);
|
||||
return changed;
|
||||
}
|
||||
|
||||
boolean setBrightness(int value) {
|
||||
public boolean setBrightness(int value) {
|
||||
boolean changed = brightness != value;
|
||||
brightness = value;
|
||||
percentBrightness = toPercent(brightness, SHELLY_MIN_BRIGHTNESS, SHELLY_MAX_BRIGHTNESS);
|
||||
return changed;
|
||||
}
|
||||
|
||||
boolean setGain(int value) {
|
||||
public boolean setGain(int value) {
|
||||
boolean changed = gain != value;
|
||||
gain = value;
|
||||
percentGain = toPercent(gain, SHELLY_MIN_GAIN, SHELLY_MAX_GAIN);
|
||||
return changed;
|
||||
}
|
||||
|
||||
boolean setTemp(int value) {
|
||||
public boolean setTemp(int value) {
|
||||
boolean changed = temp != value;
|
||||
temp = value;
|
||||
percentTemp = toPercent(temp, minTemp, maxTemp);
|
||||
return changed;
|
||||
}
|
||||
|
||||
boolean setEffect(int value) {
|
||||
public boolean setEffect(int value) {
|
||||
boolean changed = effect != value;
|
||||
effect = value;
|
||||
return changed;
|
||||
|
@ -168,18 +168,22 @@ public class ShellyColorUtils {
|
|||
return values;
|
||||
}
|
||||
|
||||
public boolean isRgbValid() {
|
||||
return (red != -1) && (blue != -1) && (green != -1);
|
||||
}
|
||||
|
||||
public static PercentType toPercent(Integer value) {
|
||||
return toPercent(value, 0, SHELLY_MAX_COLOR);
|
||||
}
|
||||
|
||||
public static PercentType toPercent(Integer _value, Integer min, Integer max) {
|
||||
Double range = max.doubleValue() - min.doubleValue();
|
||||
Double value = _value.doubleValue();
|
||||
double range = max.doubleValue() - min.doubleValue();
|
||||
double value = _value.doubleValue();
|
||||
value = value < min ? min.doubleValue() : value;
|
||||
value = value > max ? max.doubleValue() : value;
|
||||
Double percent = 0.0;
|
||||
double percent = 0.0;
|
||||
if (range > 0) {
|
||||
percent = new Double(Math.round((value - min) / range * 100));
|
||||
percent = Math.round((value - min) / range * 100);
|
||||
}
|
||||
return new PercentType(new BigDecimal(percent));
|
||||
}
|
||||
|
|
|
@ -16,15 +16,15 @@ import static org.openhab.binding.shelly.internal.ShellyBindingConstants.*;
|
|||
import static org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.*;
|
||||
import static org.openhab.binding.shelly.internal.util.ShellyUtils.*;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.shelly.internal.api.ShellyApiException;
|
||||
import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellySettingsEMeter;
|
||||
import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellySettingsMeter;
|
||||
import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellySettingsStatus;
|
||||
import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellyStatusSensor;
|
||||
import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellyStatusSensor.ShellyADC;
|
||||
import org.openhab.binding.shelly.internal.api.ShellyDeviceProfile;
|
||||
import org.openhab.binding.shelly.internal.provider.ShellyChannelDefinitions;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
import org.openhab.core.library.types.OpenClosedType;
|
||||
import org.openhab.core.library.unit.ImperialUnits;
|
||||
|
@ -48,13 +48,13 @@ public class ShellyComponents {
|
|||
*/
|
||||
public static boolean updateDeviceStatus(ShellyBaseHandler thingHandler, ShellySettingsStatus status) {
|
||||
if (!thingHandler.areChannelsCreated()) {
|
||||
thingHandler.updateChannelDefinitions(ShellyChannelDefinitionsDTO
|
||||
.createDeviceChannels(thingHandler.getThing(), thingHandler.getProfile(), status));
|
||||
thingHandler.updateChannelDefinitions(ShellyChannelDefinitions.createDeviceChannels(thingHandler.getThing(),
|
||||
thingHandler.getProfile(), status));
|
||||
}
|
||||
|
||||
Integer rssi = getInteger(status.wifiSta.rssi);
|
||||
thingHandler.updateChannel(CHANNEL_GROUP_DEV_STATUS, CHANNEL_DEVST_UPTIME,
|
||||
toQuantityType(new Double(getLong(status.uptime)), DIGITS_NONE, Units.SECOND));
|
||||
toQuantityType((double) getLong(status.uptime), DIGITS_NONE, Units.SECOND));
|
||||
thingHandler.updateChannel(CHANNEL_GROUP_DEV_STATUS, CHANNEL_DEVST_RSSI, mapSignalStrength(rssi));
|
||||
if (status.tmp != null) {
|
||||
thingHandler.updateChannel(CHANNEL_GROUP_DEV_STATUS, CHANNEL_DEVST_ITEMP,
|
||||
|
@ -109,7 +109,7 @@ public class ShellyComponents {
|
|||
if (!thingHandler.areChannelsCreated()) {
|
||||
// skip for Shelly Bulb: JSON has a meter, but values don't get updated
|
||||
if (!profile.isBulb) {
|
||||
thingHandler.updateChannelDefinitions(ShellyChannelDefinitionsDTO
|
||||
thingHandler.updateChannelDefinitions(ShellyChannelDefinitions
|
||||
.createMeterChannels(thingHandler.getThing(), meter, groupName));
|
||||
}
|
||||
}
|
||||
|
@ -141,7 +141,7 @@ public class ShellyComponents {
|
|||
String groupName = profile.numMeters > 1 ? CHANNEL_GROUP_METER + meterIndex.toString()
|
||||
: CHANNEL_GROUP_METER;
|
||||
if (!thingHandler.areChannelsCreated()) {
|
||||
thingHandler.updateChannelDefinitions(ShellyChannelDefinitionsDTO
|
||||
thingHandler.updateChannelDefinitions(ShellyChannelDefinitions
|
||||
.createEMeterChannels(thingHandler.getThing(), emeter, groupName));
|
||||
}
|
||||
|
||||
|
@ -197,7 +197,7 @@ public class ShellyComponents {
|
|||
}
|
||||
// Create channels for 1 Meter
|
||||
if (!thingHandler.areChannelsCreated()) {
|
||||
thingHandler.updateChannelDefinitions(ShellyChannelDefinitionsDTO
|
||||
thingHandler.updateChannelDefinitions(ShellyChannelDefinitions
|
||||
.createMeterChannels(thingHandler.getThing(), status.meters.get(0), groupName));
|
||||
}
|
||||
|
||||
|
@ -237,7 +237,7 @@ public class ShellyComponents {
|
|||
* @param profile ShellyDeviceProfile
|
||||
* @param status Last ShellySettingsStatus
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws ShellyApiException
|
||||
*/
|
||||
public static boolean updateSensors(ShellyBaseHandler thingHandler, ShellySettingsStatus status)
|
||||
throws ShellyApiException {
|
||||
|
@ -250,20 +250,20 @@ public class ShellyComponents {
|
|||
if (!thingHandler.areChannelsCreated()) {
|
||||
thingHandler.logger.trace("{}: Create missing sensor channel(s)", thingHandler.thingName);
|
||||
thingHandler.updateChannelDefinitions(
|
||||
ShellyChannelDefinitionsDTO.createSensorChannels(thingHandler.getThing(), profile, sdata));
|
||||
ShellyChannelDefinitions.createSensorChannels(thingHandler.getThing(), profile, sdata));
|
||||
}
|
||||
|
||||
updated |= thingHandler.updateWakeupReason(sdata.actReasons);
|
||||
|
||||
if ((sdata.contact != null) && sdata.contact.isValid) {
|
||||
if ((sdata.sensor != null) && sdata.sensor.isValid) {
|
||||
// Shelly DW: “sensor”:{“state”:“open”, “is_valid”:true},
|
||||
updated |= thingHandler.updateChannel(CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_CONTACT,
|
||||
getString(sdata.contact.state).equalsIgnoreCase(SHELLY_API_DWSTATE_OPEN) ? OpenClosedType.OPEN
|
||||
getString(sdata.sensor.state).equalsIgnoreCase(SHELLY_API_DWSTATE_OPEN) ? OpenClosedType.OPEN
|
||||
: OpenClosedType.CLOSED);
|
||||
boolean changed = thingHandler.updateChannel(CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_ERROR,
|
||||
getStringType(sdata.sensorError));
|
||||
if (changed) {
|
||||
thingHandler.postEvent(sdata.sensorError, true);
|
||||
thingHandler.postEvent(getString(sdata.sensorError), true);
|
||||
}
|
||||
updated |= changed;
|
||||
}
|
||||
|
@ -320,7 +320,11 @@ public class ShellyComponents {
|
|||
updated |= thingHandler.updateChannel(CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_PPM,
|
||||
getDecimal(sdata.concentration.ppm));
|
||||
}
|
||||
|
||||
if ((sdata.adcs != null) && (sdata.adcs.size() > 0)) {
|
||||
ShellyADC adc = sdata.adcs.get(0);
|
||||
updated |= thingHandler.updateChannel(CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_VOLTAGE,
|
||||
getDecimal(adc.voltage));
|
||||
}
|
||||
if (sdata.bat != null) { // no update for Sense
|
||||
thingHandler.logger.trace("{}: Updating battery", thingHandler.thingName);
|
||||
updated |= thingHandler.updateChannel(CHANNEL_GROUP_BATTERY, CHANNEL_SENSOR_BAT_LEVEL,
|
||||
|
@ -332,10 +336,18 @@ public class ShellyComponents {
|
|||
thingHandler.postEvent(ALARM_TYPE_LOW_BATTERY, false);
|
||||
}
|
||||
}
|
||||
if (sdata.motion != null) {
|
||||
if (sdata.motion != null) { // Shelly Sense
|
||||
updated |= thingHandler.updateChannel(CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_MOTION,
|
||||
getOnOff(sdata.motion));
|
||||
}
|
||||
if (sdata.sensor != null) { // Shelly Motion
|
||||
updated |= thingHandler.updateChannel(CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_MOTION,
|
||||
getOnOff(sdata.sensor.motion));
|
||||
updated |= thingHandler.updateChannel(CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_MOTION_TS,
|
||||
getTimestamp(getString(profile.settings.timezone), sdata.sensor.motionTimestamp));
|
||||
updated |= thingHandler.updateChannel(CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_VIBRATION,
|
||||
getOnOff(sdata.sensor.vibration));
|
||||
}
|
||||
if (sdata.charger != null) {
|
||||
updated |= thingHandler.updateChannel(CHANNEL_GROUP_DEV_STATUS, CHANNEL_DEVST_CHARGER,
|
||||
getOnOff(sdata.charger));
|
||||
|
|
|
@ -22,6 +22,7 @@ import java.util.TreeMap;
|
|||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.openhab.binding.shelly.internal.api.ShellyApiException;
|
||||
import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellySettingsRgbwLight;
|
||||
import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellySettingsStatus;
|
||||
import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellyShortLightStatus;
|
||||
import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellyStatusLight;
|
||||
|
@ -29,7 +30,8 @@ import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellyStatusLigh
|
|||
import org.openhab.binding.shelly.internal.api.ShellyDeviceProfile;
|
||||
import org.openhab.binding.shelly.internal.coap.ShellyCoapServer;
|
||||
import org.openhab.binding.shelly.internal.config.ShellyBindingConfiguration;
|
||||
import org.openhab.binding.shelly.internal.util.ShellyTranslationProvider;
|
||||
import org.openhab.binding.shelly.internal.provider.ShellyChannelDefinitions;
|
||||
import org.openhab.binding.shelly.internal.provider.ShellyTranslationProvider;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.HSBType;
|
||||
import org.openhab.core.library.types.IncreaseDecreaseType;
|
||||
|
@ -146,9 +148,8 @@ public class ShellyLightHandler extends ShellyBaseHandler {
|
|||
col.power = getOnOff(light.ison);
|
||||
col.setBrightness(light.brightness);
|
||||
updateChannel(CHANNEL_COLOR_WHITE, CHANNEL_BRIGHTNESS + "$Switch", col.power);
|
||||
updateChannel(CHANNEL_COLOR_WHITE, CHANNEL_BRIGHTNESS + "$Value",
|
||||
toQuantityType(new Double(col.power == OnOffType.ON ? col.brightness : 0), DIGITS_NONE,
|
||||
Units.PERCENT));
|
||||
updateChannel(CHANNEL_COLOR_WHITE, CHANNEL_BRIGHTNESS + "$Value", toQuantityType(
|
||||
(double) (col.power == OnOffType.ON ? col.brightness : 0), DIGITS_NONE, Units.PERCENT));
|
||||
update = false;
|
||||
break;
|
||||
}
|
||||
|
@ -264,17 +265,15 @@ public class ShellyLightHandler extends ShellyBaseHandler {
|
|||
logger.debug("{}: {} brightness by {}", thingName, command, SHELLY_DIM_STEPSIZE);
|
||||
PercentType percent = (PercentType) super.getChannelValue(CHANNEL_GROUP_COLOR_CONTROL,
|
||||
CHANNEL_BRIGHTNESS);
|
||||
if (percent != null) {
|
||||
int currentBrightness = percent.intValue() * SHELLY_MAX_BRIGHTNESS;
|
||||
int newBrightness = currentBrightness;
|
||||
if (command == IncreaseDecreaseType.DECREASE) {
|
||||
newBrightness = Math.max(currentBrightness - SHELLY_DIM_STEPSIZE, 0);
|
||||
} else {
|
||||
newBrightness = Math.min(currentBrightness + SHELLY_DIM_STEPSIZE, SHELLY_MAX_BRIGHTNESS);
|
||||
}
|
||||
col.brightness = newBrightness;
|
||||
updated = currentBrightness != newBrightness;
|
||||
int currentBrightness = percent.intValue() * SHELLY_MAX_BRIGHTNESS;
|
||||
int newBrightness = currentBrightness;
|
||||
if (command == IncreaseDecreaseType.DECREASE) {
|
||||
newBrightness = Math.max(currentBrightness - SHELLY_DIM_STEPSIZE, 0);
|
||||
} else {
|
||||
newBrightness = Math.min(currentBrightness + SHELLY_DIM_STEPSIZE, SHELLY_MAX_BRIGHTNESS);
|
||||
}
|
||||
col.brightness = newBrightness;
|
||||
updated = currentBrightness != newBrightness;
|
||||
}
|
||||
}
|
||||
return updated;
|
||||
|
@ -303,14 +302,13 @@ public class ShellyLightHandler extends ShellyBaseHandler {
|
|||
}
|
||||
|
||||
private ShellyColorUtils getCurrentColors(int lightId) {
|
||||
ShellyColorUtils col;
|
||||
if (!channelColors.containsKey(lightId)) {
|
||||
ShellyColorUtils col = channelColors.get(lightId);
|
||||
if (col == null) {
|
||||
col = new ShellyColorUtils(); // create a new entry
|
||||
col.setMinMaxTemp(profile.minTemp, profile.maxTemp);
|
||||
channelColors.put(lightId, col);
|
||||
logger.trace("{}: Colors entry created for lightId {}", thingName, lightId);
|
||||
} else {
|
||||
col = channelColors.get(lightId);
|
||||
logger.trace(
|
||||
"{}: Colors loaded for lightId {}: power={}, RGBW={}/{}/{}/{}, gain={}, brightness={}, color temp={} (min={}, max={}",
|
||||
thingName, lightId, col.power, col.red, col.green, col.blue, col.white, col.gain, col.brightness,
|
||||
|
@ -339,6 +337,7 @@ public class ShellyLightHandler extends ShellyBaseHandler {
|
|||
for (ShellyStatusLightChannel light : status.lights) {
|
||||
Integer channelId = lightId + 1;
|
||||
String controlGroup = buildControlGroupName(profile, channelId);
|
||||
createLightChannels(light, lightId);
|
||||
// The bulb has a combined channel set for color or white mode
|
||||
// The RGBW2 uses 2 different thing types: color=1 channel, white=4 channel
|
||||
if (profile.isBulb) {
|
||||
|
@ -349,11 +348,12 @@ public class ShellyLightHandler extends ShellyBaseHandler {
|
|||
col.power = getOnOff(light.ison);
|
||||
|
||||
// Channel control/timer
|
||||
// ShellyStatusLightChannel light = status.lights.get(i);
|
||||
ShellySettingsRgbwLight ls = profile.settings.lights.get(lightId);
|
||||
updated |= updateChannel(controlGroup, CHANNEL_TIMER_AUTOON, getDecimal(ls.autoOn));
|
||||
updated |= updateChannel(controlGroup, CHANNEL_TIMER_AUTOOFF, getDecimal(ls.autoOff));
|
||||
updated |= updateChannel(controlGroup, CHANNEL_LIGHT_POWER, col.power);
|
||||
updated |= updateChannel(controlGroup, CHANNEL_TIMER_AUTOON, getDecimal(light.autoOn));
|
||||
updated |= updateChannel(controlGroup, CHANNEL_TIMER_AUTOOFF, getDecimal(light.autoOff));
|
||||
updated |= updateInputs(controlGroup, genericStatus, lightId);
|
||||
updated |= updateChannel(controlGroup, CHANNEL_TIMER_ACTIVE, getOnOff(light.hasTimer));
|
||||
|
||||
if (getBool(light.overpower)) {
|
||||
postEvent(ALARM_TYPE_OVERPOWER, false);
|
||||
}
|
||||
|
@ -386,8 +386,8 @@ public class ShellyLightHandler extends ShellyBaseHandler {
|
|||
col.setBrightness(getInteger(light.brightness));
|
||||
updated |= updateChannel(whiteGroup, CHANNEL_BRIGHTNESS + "$Switch", col.power);
|
||||
updated |= updateChannel(whiteGroup, CHANNEL_BRIGHTNESS + "$Value",
|
||||
toQuantityType(col.power == OnOffType.ON ? col.percentBrightness.doubleValue() : new Double(0),
|
||||
DIGITS_NONE, Units.PERCENT));
|
||||
toQuantityType(col.power == OnOffType.ON ? col.percentBrightness.doubleValue() : 0, DIGITS_NONE,
|
||||
Units.PERCENT));
|
||||
|
||||
if ((profile.isBulb || profile.isDuo) && (light.temp != null)) {
|
||||
col.setTemp(getInteger(light.temp));
|
||||
|
@ -403,14 +403,20 @@ public class ShellyLightHandler extends ShellyBaseHandler {
|
|||
return updated;
|
||||
}
|
||||
|
||||
private void createLightChannels(ShellyStatusLightChannel status, int idx) {
|
||||
if (!areChannelsCreated()) {
|
||||
updateChannelDefinitions(ShellyChannelDefinitions.createLightChannels(getThing(), profile, status, idx));
|
||||
}
|
||||
}
|
||||
|
||||
private Integer setColor(Integer lightId, String colorName, Command command, Integer minValue, Integer maxValue)
|
||||
throws ShellyApiException, IllegalArgumentException {
|
||||
Integer value = -1;
|
||||
logger.debug("{}: Set {} to {} ({})", thingName, colorName, command, command.getClass());
|
||||
if (command instanceof PercentType) {
|
||||
PercentType percent = (PercentType) command;
|
||||
Double v = new Double(maxValue) * percent.doubleValue() / 100.0;
|
||||
value = v.intValue();
|
||||
double v = (double) maxValue * percent.doubleValue() / 100.0;
|
||||
value = (int) v;
|
||||
logger.debug("{}: Value for {} is in percent: {}%={}", thingName, colorName, percent, value);
|
||||
} else if (command instanceof DecimalType) {
|
||||
value = ((DecimalType) command).intValue();
|
||||
|
@ -499,14 +505,13 @@ public class ShellyLightHandler extends ShellyBaseHandler {
|
|||
lightId, col.red, col.green, col.blue, col.white, col.gain, col.brightness, col.temp);
|
||||
}
|
||||
|
||||
private Integer getColorFromHSB(PercentType colorPercent) {
|
||||
return getColorFromHSB(colorPercent, new Double(SATURATION_FACTOR));
|
||||
private int getColorFromHSB(PercentType colorPercent) {
|
||||
return getColorFromHSB(colorPercent, SATURATION_FACTOR);
|
||||
}
|
||||
|
||||
private Integer getColorFromHSB(PercentType colorPercent, Double factor) {
|
||||
Double value = new Double(Math.round(colorPercent.doubleValue() * factor));
|
||||
logger.trace("{}: convert {}% into {}/{} (factor={})", thingName, colorPercent, value, value.intValue(),
|
||||
factor);
|
||||
return value.intValue();
|
||||
private int getColorFromHSB(PercentType colorPercent, double factor) {
|
||||
double value = Math.round(colorPercent.doubleValue() * factor);
|
||||
logger.trace("{}: convert {}% into {}/{} (factor={})", thingName, colorPercent, value, (int) value, factor);
|
||||
return (int) value;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,10 +16,8 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
|
|||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.openhab.binding.shelly.internal.coap.ShellyCoapServer;
|
||||
import org.openhab.binding.shelly.internal.config.ShellyBindingConfiguration;
|
||||
import org.openhab.binding.shelly.internal.util.ShellyTranslationProvider;
|
||||
import org.openhab.binding.shelly.internal.provider.ShellyTranslationProvider;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The {@link ShellyProtectedHandler} implements a dummy handler for password protected devices.
|
||||
|
@ -28,8 +26,6 @@ import org.slf4j.LoggerFactory;
|
|||
*/
|
||||
@NonNullByDefault
|
||||
public class ShellyProtectedHandler extends ShellyBaseHandler {
|
||||
private final Logger logger = LoggerFactory.getLogger(ShellyProtectedHandler.class);
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
|
|
|
@ -30,7 +30,8 @@ import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellyShortStatu
|
|||
import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellyStatusRelay;
|
||||
import org.openhab.binding.shelly.internal.coap.ShellyCoapServer;
|
||||
import org.openhab.binding.shelly.internal.config.ShellyBindingConfiguration;
|
||||
import org.openhab.binding.shelly.internal.util.ShellyTranslationProvider;
|
||||
import org.openhab.binding.shelly.internal.provider.ShellyChannelDefinitions;
|
||||
import org.openhab.binding.shelly.internal.provider.ShellyTranslationProvider;
|
||||
import org.openhab.core.library.types.DecimalType;
|
||||
import org.openhab.core.library.types.IncreaseDecreaseType;
|
||||
import org.openhab.core.library.types.OnOffType;
|
||||
|
@ -118,6 +119,19 @@ public class ShellyRelayHandler extends ShellyBaseHandler {
|
|||
requestUpdates(autoCoIoT ? 1 : 45 / UPDATE_STATUS_INTERVAL_SECONDS, false);
|
||||
break;
|
||||
|
||||
case CHANNEL_ROL_CONTROL_FAV:
|
||||
if (command instanceof Number) {
|
||||
int id = ((Number) command).intValue() - 1;
|
||||
int pos = profile.getRollerFav(id);
|
||||
if (pos > 0) {
|
||||
logger.debug("{}: Selecting favorite {}, position = {}", thingName, id, pos);
|
||||
api.setRollerPos(rIndex, pos);
|
||||
break;
|
||||
}
|
||||
}
|
||||
logger.debug("{}: Invalid favorite index: {}", thingName, command);
|
||||
break;
|
||||
|
||||
case CHANNEL_TIMER_AUTOON:
|
||||
logger.debug("{}: Set Auto-ON timer to {}", thingName, command);
|
||||
api.setTimer(rIndex, SHELLY_TIMER_AUTOON, getNumber(command));
|
||||
|
@ -152,7 +166,7 @@ public class ShellyRelayHandler extends ShellyBaseHandler {
|
|||
return;
|
||||
} else if (command instanceof IncreaseDecreaseType) {
|
||||
ShellyShortLightStatus light = api.getLightStatus(index);
|
||||
if (((IncreaseDecreaseType) command).equals(IncreaseDecreaseType.INCREASE)) {
|
||||
if (command == IncreaseDecreaseType.INCREASE) {
|
||||
value = Math.min(light.brightness + DIM_STEPSIZE, 100);
|
||||
} else {
|
||||
value = Math.max(light.brightness - DIM_STEPSIZE, 0);
|
||||
|
@ -172,14 +186,16 @@ public class ShellyRelayHandler extends ShellyBaseHandler {
|
|||
}
|
||||
|
||||
private void updateBrightnessChannel(int lightId, OnOffType power, int brightness) throws ShellyApiException {
|
||||
updateChannel(CHANNEL_COLOR_WHITE, CHANNEL_BRIGHTNESS + "$Switch", power);
|
||||
if (brightness > 0) {
|
||||
api.setBrightness(lightId, brightness, config.brightnessAutoOn);
|
||||
} else {
|
||||
api.setRelayTurn(lightId, power == OnOffType.ON ? SHELLY_API_ON : SHELLY_API_OFF);
|
||||
if (brightness >= 0) { // ignore -1
|
||||
updateChannel(CHANNEL_COLOR_WHITE, CHANNEL_BRIGHTNESS + "$Value",
|
||||
toQuantityType((double) (power == OnOffType.ON ? brightness : 0), DIGITS_NONE, Units.PERCENT));
|
||||
}
|
||||
}
|
||||
updateChannel(CHANNEL_COLOR_WHITE, CHANNEL_BRIGHTNESS + "$Switch", power);
|
||||
updateChannel(CHANNEL_COLOR_WHITE, CHANNEL_BRIGHTNESS + "$Value",
|
||||
toQuantityType(new Double(power == OnOffType.ON ? brightness : 0), DIGITS_NONE, Units.PERCENT));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -209,10 +225,9 @@ public class ShellyRelayHandler extends ShellyBaseHandler {
|
|||
ShellyControlRoller rstatus = api.getRollerStatus(index);
|
||||
|
||||
if (!getString(rstatus.state).isEmpty() && !getString(rstatus.state).equals(SHELLY_ALWD_ROLLER_TURN_STOP)) {
|
||||
boolean up = command instanceof UpDownType && (UpDownType) command == UpDownType.UP;
|
||||
boolean down = command instanceof UpDownType && (UpDownType) command == UpDownType.DOWN;
|
||||
if ((up && getString(rstatus.state).equals(SHELLY_ALWD_ROLLER_TURN_OPEN))
|
||||
|| (down && getString(rstatus.state).equals(SHELLY_ALWD_ROLLER_TURN_CLOSE))) {
|
||||
if ((command == UpDownType.UP && getString(rstatus.state).equals(SHELLY_ALWD_ROLLER_TURN_OPEN))
|
||||
|| (command == UpDownType.DOWN
|
||||
&& getString(rstatus.state).equals(SHELLY_ALWD_ROLLER_TURN_CLOSE))) {
|
||||
logger.debug("{}: Roller is already moving ({}), ignore command {}", thingName,
|
||||
getString(rstatus.state), command);
|
||||
requestUpdates(1, false);
|
||||
|
@ -220,20 +235,31 @@ public class ShellyRelayHandler extends ShellyBaseHandler {
|
|||
}
|
||||
}
|
||||
|
||||
if (((command instanceof UpDownType) && UpDownType.UP.equals(command))
|
||||
|| ((command instanceof OnOffType) && OnOffType.ON.equals(command))) {
|
||||
if ((command == UpDownType.UP) || (command == OnOffType.ON)) {
|
||||
logger.debug("{}: Open roller", thingName);
|
||||
api.setRollerTurn(index, SHELLY_ALWD_ROLLER_TURN_OPEN);
|
||||
position = SHELLY_MAX_ROLLER_POS;
|
||||
|
||||
}
|
||||
if (((command instanceof UpDownType) && UpDownType.DOWN.equals(command))
|
||||
|| ((command instanceof OnOffType) && OnOffType.OFF.equals(command))) {
|
||||
int pos = profile.getRollerFav(config.favoriteUP - 1);
|
||||
position = pos > 0 ? pos : SHELLY_MAX_ROLLER_POS;
|
||||
if (pos > 0) {
|
||||
logger.debug("{}: Use favoriteUP id {} for positioning roller({}%)", thingName, config.favoriteUP,
|
||||
pos);
|
||||
}
|
||||
} else if ((command == UpDownType.DOWN) || (command == OnOffType.OFF)) {
|
||||
logger.debug("{}: Closing roller", thingName);
|
||||
api.setRollerTurn(index, SHELLY_ALWD_ROLLER_TURN_CLOSE);
|
||||
position = SHELLY_MIN_ROLLER_POS;
|
||||
int pos = profile.getRollerFav(config.favoriteDOWN - 1);
|
||||
if (pos > 0) {
|
||||
// use favorite position
|
||||
if (pos > 0) {
|
||||
logger.debug("{}: Use favoriteDOWN id {} for positioning roller ({}%)", thingName,
|
||||
config.favoriteDOWN, pos);
|
||||
}
|
||||
api.setRollerPos(index, pos);
|
||||
} else {
|
||||
api.setRollerTurn(index, SHELLY_ALWD_ROLLER_TURN_CLOSE);
|
||||
}
|
||||
position = SHELLY_MAX_ROLLER_POS - pos;
|
||||
}
|
||||
} else if ((command instanceof StopMoveType) && StopMoveType.STOP.equals(command)) {
|
||||
} else if (command == StopMoveType.STOP) {
|
||||
logger.debug("{}: Stop roller", thingName);
|
||||
api.setRollerTurn(index, SHELLY_ALWD_ROLLER_TURN_STOP);
|
||||
} else {
|
||||
|
@ -246,7 +272,7 @@ public class ShellyRelayHandler extends ShellyBaseHandler {
|
|||
position = d.intValue();
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"Invalid value type for roller control/posiution" + command.getClass().toString());
|
||||
"Invalid value type for roller control/position" + command.getClass().toString());
|
||||
}
|
||||
|
||||
// take position from RollerShutter control and map to Shelly positon (OH:
|
||||
|
@ -274,13 +300,19 @@ public class ShellyRelayHandler extends ShellyBaseHandler {
|
|||
*/
|
||||
private void createRelayChannels(ShellyStatusRelay relay, int idx) {
|
||||
if (!areChannelsCreated()) {
|
||||
updateChannelDefinitions(ShellyChannelDefinitionsDTO.createRelayChannels(getThing(), profile, relay, idx));
|
||||
updateChannelDefinitions(ShellyChannelDefinitions.createRelayChannels(getThing(), profile, relay, idx));
|
||||
}
|
||||
}
|
||||
|
||||
private void createDimmerChannels(ShellySettingsStatus dstatus, int idx) {
|
||||
if (!areChannelsCreated()) {
|
||||
updateChannelDefinitions(ShellyChannelDefinitions.createDimmerChannels(getThing(), profile, dstatus, idx));
|
||||
}
|
||||
}
|
||||
|
||||
private void createRollerChannels(ShellyControlRoller roller) {
|
||||
if (!areChannelsCreated()) {
|
||||
updateChannelDefinitions(ShellyChannelDefinitionsDTO.createRollerChannels(getThing(), roller));
|
||||
updateChannelDefinitions(ShellyChannelDefinitions.createRollerChannels(getThing(), roller));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -343,16 +375,11 @@ public class ShellyRelayHandler extends ShellyBaseHandler {
|
|||
updated |= updateChannel(groupName, CHANNEL_TIMER_AUTOOFF,
|
||||
toQuantityType(getDouble(rsettings.autoOff), Units.SECOND));
|
||||
}
|
||||
|
||||
// Update input(s) state
|
||||
updated |= updateInputs(groupName, status, i);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for Relay in Roller Mode
|
||||
if (profile.hasRelays && profile.isRoller && (status.rollers != null)) {
|
||||
} else if (profile.hasRelays && profile.isRoller && (status.rollers != null)) {
|
||||
// Check for Relay in Roller Mode
|
||||
logger.trace("{}: Updating {} rollers", thingName, profile.numRollers);
|
||||
int i = 0;
|
||||
|
||||
|
@ -373,15 +400,15 @@ public class ShellyRelayHandler extends ShellyBaseHandler {
|
|||
if (state.equals(SHELLY_ALWD_ROLLER_TURN_STOP)) { // only valid in stop state
|
||||
int pos = Math.max(SHELLY_MIN_ROLLER_POS, Math.min(control.currentPos, SHELLY_MAX_ROLLER_POS));
|
||||
updated |= updateChannel(groupName, CHANNEL_ROL_CONTROL_CONTROL,
|
||||
toQuantityType(new Double(SHELLY_MAX_ROLLER_POS - pos), Units.PERCENT));
|
||||
toQuantityType((double) (SHELLY_MAX_ROLLER_POS - pos), Units.PERCENT));
|
||||
updated |= updateChannel(groupName, CHANNEL_ROL_CONTROL_POS,
|
||||
toQuantityType(new Double(pos), Units.PERCENT));
|
||||
toQuantityType((double) pos, Units.PERCENT));
|
||||
scheduledUpdates = 1; // one more poll and then stop
|
||||
}
|
||||
|
||||
updated |= updateChannel(groupName, CHANNEL_ROL_CONTROL_STATE, new StringType(state));
|
||||
updated |= updateChannel(groupName, CHANNEL_ROL_CONTROL_STOPR, getStringType(control.stopReason));
|
||||
updated |= updateInputs(groupName, status, i);
|
||||
updated |= updateChannel(groupName, CHANNEL_ROL_CONTROL_SAFETY, getOnOff(control.safetySwitch));
|
||||
|
||||
i++;
|
||||
}
|
||||
|
@ -406,7 +433,7 @@ public class ShellyRelayHandler extends ShellyBaseHandler {
|
|||
// the same structure as lights[] from Bulb,RGBW2 and Duo. The tag gets replaced by dimmers[] so that Gson
|
||||
// maps to a different structure (ShellyShortLight).
|
||||
Gson gson = new Gson();
|
||||
ShellySettingsStatus dstatus = gson.fromJson(ShellyApiJsonDTO.fixDimmerJson(orgStatus.json),
|
||||
ShellySettingsStatus dstatus = fromJson(gson, ShellyApiJsonDTO.fixDimmerJson(orgStatus.json),
|
||||
ShellySettingsStatus.class);
|
||||
|
||||
logger.trace("{}: Updating {} dimmers(s)", thingName, dstatus.dimmers.size());
|
||||
|
@ -416,17 +443,19 @@ public class ShellyRelayHandler extends ShellyBaseHandler {
|
|||
String groupName = profile.numRelays <= 1 ? CHANNEL_GROUP_DIMMER_CONTROL
|
||||
: CHANNEL_GROUP_DIMMER_CONTROL + r.toString();
|
||||
|
||||
createDimmerChannels(dstatus, l);
|
||||
|
||||
// On a status update we map a dimmer.ison = false to brightness 0 rather than the device's brightness
|
||||
// and send a OFF status to the same channel.
|
||||
// When the device's brightness is > 0 we send the new value to the channel and a ON command
|
||||
if (dimmer.ison) {
|
||||
updated |= updateChannel(groupName, CHANNEL_BRIGHTNESS + "$Switch", OnOffType.ON);
|
||||
updated |= updateChannel(groupName, CHANNEL_BRIGHTNESS + "$Value",
|
||||
toQuantityType(new Double(getInteger(dimmer.brightness)), DIGITS_NONE, Units.PERCENT));
|
||||
toQuantityType((double) getInteger(dimmer.brightness), DIGITS_NONE, Units.PERCENT));
|
||||
} else {
|
||||
updated |= updateChannel(groupName, CHANNEL_BRIGHTNESS + "$Switch", OnOffType.OFF);
|
||||
updated |= updateChannel(groupName, CHANNEL_BRIGHTNESS + "$Value",
|
||||
toQuantityType(new Double(0), DIGITS_NONE, Units.PERCENT));
|
||||
toQuantityType(0.0, DIGITS_NONE, Units.PERCENT));
|
||||
}
|
||||
|
||||
ShellySettingsDimmer dsettings = profile.settings.dimmers.get(l);
|
||||
|
@ -437,7 +466,6 @@ public class ShellyRelayHandler extends ShellyBaseHandler {
|
|||
toQuantityType(getDouble(dsettings.autoOff), Units.SECOND));
|
||||
}
|
||||
|
||||
updated |= updateInputs(groupName, orgStatus, l);
|
||||
l++;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,608 @@
|
|||
/**
|
||||
* Copyright (c) 2010-2021 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.provider;
|
||||
|
||||
import static org.openhab.binding.shelly.internal.ShellyBindingConstants.*;
|
||||
import static org.openhab.binding.shelly.internal.util.ShellyUtils.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.measure.Unit;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellyControlRoller;
|
||||
import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellyInputState;
|
||||
import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellySettingsDimmer;
|
||||
import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellySettingsEMeter;
|
||||
import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellySettingsGlobal;
|
||||
import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellySettingsMeter;
|
||||
import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellySettingsRelay;
|
||||
import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellySettingsRgbwLight;
|
||||
import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellySettingsStatus;
|
||||
import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellyStatusLightChannel;
|
||||
import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellyStatusRelay;
|
||||
import org.openhab.binding.shelly.internal.api.ShellyApiJsonDTO.ShellyStatusSensor;
|
||||
import org.openhab.binding.shelly.internal.api.ShellyDeviceProfile;
|
||||
import org.openhab.binding.shelly.internal.handler.ShellyBaseHandler;
|
||||
import org.openhab.core.thing.Channel;
|
||||
import org.openhab.core.thing.ChannelUID;
|
||||
import org.openhab.core.thing.Thing;
|
||||
import org.openhab.core.thing.binding.builder.ChannelBuilder;
|
||||
import org.openhab.core.thing.type.ChannelKind;
|
||||
import org.openhab.core.thing.type.ChannelTypeUID;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Reference;
|
||||
|
||||
/**
|
||||
* The {@link ShellyCHANNEL_DEFINITIONSDTO} defines channel information for dynamically created channels. Those will be
|
||||
* added on the first thing status update
|
||||
*
|
||||
* @author Markus Michels - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(service = ShellyChannelDefinitions.class)
|
||||
public class ShellyChannelDefinitions {
|
||||
|
||||
public static final String ITEMT_STRING = "String";
|
||||
public static final String ITEMT_NUMBER = "Number";
|
||||
public static final String ITEMT_SWITCH = "Switch";
|
||||
public static final String ITEMT_CONTACT = "Contact";
|
||||
public static final String ITEMT_ROLLER = "Rollershutter";
|
||||
public static final String ITEMT_DIMMER = "Dimmer";
|
||||
public static final String ITEMT_LOCATION = "Location";
|
||||
public static final String ITEMT_DATETIME = "DateTime";
|
||||
public static final String ITEMT_TEMP = "Number:Temperature";
|
||||
public static final String ITEMT_LUX = "Number:Illuminance";
|
||||
public static final String ITEMT_POWER = "Number:Power";
|
||||
public static final String ITEMT_ENERGY = "Number:Energy";
|
||||
public static final String ITEMT_VOLT = "Number:ElectricPotential";
|
||||
public static final String ITEMT_AMP = "Number:ElectricPotential";
|
||||
public static final String ITEMT_ANGLE = "Number:Angle";
|
||||
public static final String ITEMT_DISTANCE = "Number:Length";
|
||||
public static final String ITEMT_SPEED = "Number:Speed";
|
||||
public static final String ITEMT_VOLUME = "Number:Volume";
|
||||
public static final String ITEMT_TIME = "Number:Time";
|
||||
public static final String ITEMT_PERCENT = "Number:Dimensionless";
|
||||
|
||||
// shortcuts to avoid line breaks (make code more readable)
|
||||
private static final String CHGR_DEVST = CHANNEL_GROUP_DEV_STATUS;
|
||||
private static final String CHGR_RELAY = CHANNEL_GROUP_RELAY_CONTROL;
|
||||
private static final String CHGR_ROLLER = CHANNEL_GROUP_ROL_CONTROL;
|
||||
private static final String CHGR_LIGHT = CHANNEL_GROUP_LIGHT_CONTROL;
|
||||
private static final String CHGR_STATUS = CHANNEL_GROUP_STATUS;
|
||||
private static final String CHGR_METER = CHANNEL_GROUP_METER;
|
||||
private static final String CHGR_SENSOR = CHANNEL_GROUP_SENSOR;
|
||||
private static final String CHGR_BAT = CHANNEL_GROUP_BATTERY;
|
||||
|
||||
public static final String PREFIX_GROUP = "group-type." + BINDING_ID + ".";
|
||||
public static final String PREFIX_CHANNEL = "channel-type." + BINDING_ID + ".";
|
||||
|
||||
private static final ChannelMap CHANNEL_DEFINITIONS = new ChannelMap();
|
||||
|
||||
@Activate
|
||||
public ShellyChannelDefinitions(@Reference ShellyTranslationProvider translationProvider) {
|
||||
ShellyTranslationProvider m = translationProvider;
|
||||
|
||||
// Device: Internal Temp
|
||||
CHANNEL_DEFINITIONS
|
||||
// Device
|
||||
.add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_DEVST_NAME, "deviceName", ITEMT_STRING))
|
||||
.add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_DEVST_ITEMP, "deviceTemp", ITEMT_TEMP))
|
||||
.add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_DEVST_WAKEUP, "sensorWakeup", ITEMT_STRING))
|
||||
.add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_DEVST_ACCUWATTS, "meterAccuWatts", ITEMT_POWER))
|
||||
.add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_DEVST_ACCUTOTAL, "meterAccuTotal", ITEMT_POWER))
|
||||
.add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_DEVST_ACCURETURNED, "meterAccuReturned", ITEMT_POWER))
|
||||
.add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_DEVST_CHARGER, "charger", ITEMT_SWITCH))
|
||||
.add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_LED_STATUS_DISABLE, "ledStatusDisable", ITEMT_SWITCH))
|
||||
.add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_LED_POWER_DISABLE, "ledPowerDisable", ITEMT_SWITCH))
|
||||
.add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_DEVST_SELFTTEST, "selfTest", ITEMT_STRING))
|
||||
.add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_DEVST_UPTIME, "uptime", ITEMT_NUMBER))
|
||||
.add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_DEVST_HEARTBEAT, "heartBeat", ITEMT_DATETIME))
|
||||
.add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_DEVST_UPDATE, "updateAvailable", ITEMT_SWITCH))
|
||||
|
||||
// Relay
|
||||
.add(new ShellyChannel(m, CHGR_RELAY, CHANNEL_OUTPUT_NAME, "outputName", ITEMT_STRING))
|
||||
.add(new ShellyChannel(m, CHGR_RELAY, CHANNEL_OUTPUT, "system:power", ITEMT_SWITCH))
|
||||
.add(new ShellyChannel(m, CHGR_RELAY, CHANNEL_INPUT, "inputState", ITEMT_SWITCH))
|
||||
.add(new ShellyChannel(m, CHGR_RELAY, CHANNEL_BUTTON_TRIGGER, "system:button", ITEMT_STRING))
|
||||
.add(new ShellyChannel(m, CHGR_RELAY, CHANNEL_STATUS_EVENTTYPE, "lastEvent", ITEMT_STRING))
|
||||
.add(new ShellyChannel(m, CHGR_RELAY, CHANNEL_STATUS_EVENTCOUNT, "eventCount", ITEMT_NUMBER))
|
||||
.add(new ShellyChannel(m, CHGR_RELAY, CHANNEL_TIMER_AUTOON, "timerAutoOn", ITEMT_TIME))
|
||||
.add(new ShellyChannel(m, CHGR_RELAY, CHANNEL_TIMER_AUTOOFF, "timerAutoOff", ITEMT_TIME))
|
||||
.add(new ShellyChannel(m, CHGR_RELAY, CHANNEL_TIMER_ACTIVE, "timerActive", ITEMT_SWITCH))
|
||||
|
||||
// Dimmer
|
||||
.add(new ShellyChannel(m, CHANNEL_GROUP_DIMMER_CONTROL, CHANNEL_BRIGHTNESS, "dimmerBrightness",
|
||||
ITEMT_DIMMER))
|
||||
|
||||
// Roller
|
||||
.add(new ShellyChannel(m, CHGR_ROLLER, CHANNEL_ROL_CONTROL_CONTROL, "rollerShutter", ITEMT_ROLLER))
|
||||
.add(new ShellyChannel(m, CHGR_ROLLER, CHANNEL_ROL_CONTROL_POS, "rollerPosition", ITEMT_DIMMER))
|
||||
.add(new ShellyChannel(m, CHGR_ROLLER, CHANNEL_ROL_CONTROL_FAV, "rollerFavorite", ITEMT_NUMBER))
|
||||
.add(new ShellyChannel(m, CHGR_ROLLER, CHANNEL_ROL_CONTROL_STATE, "rollerState", ITEMT_STRING))
|
||||
.add(new ShellyChannel(m, CHGR_ROLLER, CHANNEL_ROL_CONTROL_STOPR, "rollerStop", ITEMT_STRING))
|
||||
.add(new ShellyChannel(m, CHGR_ROLLER, CHANNEL_ROL_CONTROL_SAFETY, "rollerSafety", ITEMT_SWITCH))
|
||||
.add(new ShellyChannel(m, CHGR_ROLLER, CHANNEL_INPUT, "inputState", ITEMT_SWITCH))
|
||||
.add(new ShellyChannel(m, CHGR_ROLLER, CHANNEL_STATUS_EVENTTYPE, "lastEvent", ITEMT_STRING))
|
||||
.add(new ShellyChannel(m, CHGR_ROLLER, CHANNEL_STATUS_EVENTCOUNT, "eventCount", ITEMT_NUMBER))
|
||||
.add(new ShellyChannel(m, CHGR_ROLLER, CHANNEL_EVENT_TRIGGER, "system:button", "system:button"))
|
||||
|
||||
// RGBW2
|
||||
.add(new ShellyChannel(m, CHGR_LIGHT, CHANNEL_LIGHT_POWER, "system:power", ITEMT_SWITCH))
|
||||
.add(new ShellyChannel(m, CHGR_LIGHT, CHANNEL_INPUT, "inputState", ITEMT_SWITCH))
|
||||
.add(new ShellyChannel(m, CHGR_LIGHT, CHANNEL_BUTTON_TRIGGER, "system:button", ITEMT_STRING))
|
||||
.add(new ShellyChannel(m, CHGR_LIGHT, CHANNEL_STATUS_EVENTTYPE, "lastEvent", ITEMT_STRING))
|
||||
.add(new ShellyChannel(m, CHGR_LIGHT, CHANNEL_STATUS_EVENTCOUNT, "eventCount", ITEMT_NUMBER))
|
||||
.add(new ShellyChannel(m, CHGR_LIGHT, CHANNEL_TIMER_AUTOON, "timerAutoOn", ITEMT_TIME))
|
||||
.add(new ShellyChannel(m, CHGR_LIGHT, CHANNEL_TIMER_AUTOOFF, "timerAutoOff", ITEMT_TIME))
|
||||
.add(new ShellyChannel(m, CHGR_LIGHT, CHANNEL_TIMER_ACTIVE, "timerActive", ITEMT_SWITCH))
|
||||
|
||||
// Power Meter
|
||||
.add(new ShellyChannel(m, CHGR_METER, CHANNEL_METER_CURRENTWATTS, "meterWatts", ITEMT_POWER))
|
||||
.add(new ShellyChannel(m, CHGR_METER, CHANNEL_METER_TOTALKWH, "meterTotal", ITEMT_ENERGY))
|
||||
.add(new ShellyChannel(m, CHGR_METER, CHANNEL_METER_LASTMIN1, "lastPower1", ITEMT_ENERGY))
|
||||
.add(new ShellyChannel(m, CHGR_METER, CHANNEL_LAST_UPDATE, "lastUpdate", ITEMT_DATETIME))
|
||||
|
||||
// EMeter
|
||||
.add(new ShellyChannel(m, CHGR_METER, CHANNEL_EMETER_TOTALRET, "meterReturned", ITEMT_ENERGY))
|
||||
.add(new ShellyChannel(m, CHGR_METER, CHANNEL_EMETER_REACTWATTS, "meterReactive", ITEMT_POWER))
|
||||
.add(new ShellyChannel(m, CHGR_METER, CHANNEL_EMETER_VOLTAGE, "meterVoltage", ITEMT_VOLT))
|
||||
.add(new ShellyChannel(m, CHGR_METER, CHANNEL_EMETER_CURRENT, "meterCurrent", ITEMT_AMP))
|
||||
.add(new ShellyChannel(m, CHGR_METER, CHANNEL_EMETER_PFACTOR, "meterPowerFactor", ITEMT_NUMBER))
|
||||
|
||||
// Sensors
|
||||
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_TEMP, "sensorTemp", ITEMT_TEMP))
|
||||
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_HUM, "sensorHumidity", ITEMT_PERCENT))
|
||||
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_LUX, "sensorLux", ITEMT_LUX))
|
||||
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_ILLUM, "sensorIllumination", ITEMT_STRING))
|
||||
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_VOLTAGE, "sensorADC", ITEMT_VOLT))
|
||||
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_CONTACT, "sensorContact", ITEMT_CONTACT))
|
||||
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_SSTATE, "sensorState", ITEMT_STRING))
|
||||
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_VIBRATION, "sensorVibration", ITEMT_SWITCH))
|
||||
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_TILT, "sensorTilt", ITEMT_ANGLE))
|
||||
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_MOTION, "sensorMotion", ITEMT_SWITCH))
|
||||
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_MOTION_TS, "motionTimestamp", ITEMT_DATETIME))
|
||||
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_FLOOD, "sensorFlood", ITEMT_SWITCH))
|
||||
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_SMOKE, "sensorSmoke", ITEMT_SWITCH))
|
||||
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_PPM, "sensorPPM", ITEMT_NUMBER))
|
||||
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_VALVE, "sensorValve", ITEMT_STRING))
|
||||
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_ALARM_STATE, "alarmState", ITEMT_STRING))
|
||||
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_ERROR, "sensorError", ITEMT_STRING))
|
||||
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_LAST_UPDATE, "lastUpdate", ITEMT_DATETIME))
|
||||
|
||||
// Button/ix3
|
||||
.add(new ShellyChannel(m, CHGR_STATUS, CHANNEL_INPUT, "inputState", ITEMT_SWITCH))
|
||||
.add(new ShellyChannel(m, CHGR_STATUS, CHANNEL_STATUS_EVENTTYPE, "lastEvent", ITEMT_STRING))
|
||||
.add(new ShellyChannel(m, CHGR_STATUS, CHANNEL_STATUS_EVENTCOUNT, "eventCount", ITEMT_NUMBER))
|
||||
.add(new ShellyChannel(m, CHGR_STATUS, CHANNEL_BUTTON_TRIGGER, "system:button", ITEMT_STRING))
|
||||
.add(new ShellyChannel(m, CHGR_STATUS, CHANNEL_LAST_UPDATE, "lastUpdate", ITEMT_DATETIME))
|
||||
|
||||
// Addon with external sensors
|
||||
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_ESENDOR_TEMP1, "sensorExtTemp", ITEMT_TEMP))
|
||||
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_ESENDOR_TEMP2, "sensorExtTemp", ITEMT_TEMP))
|
||||
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_ESENDOR_TEMP3, "sensorExtTemp", ITEMT_TEMP))
|
||||
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_ESENDOR_HUMIDITY, "sensorExtHum", ITEMT_PERCENT))
|
||||
|
||||
// Battery
|
||||
.add(new ShellyChannel(m, CHGR_BAT, CHANNEL_SENSOR_BAT_LEVEL, "system:battery-level", ITEMT_PERCENT))
|
||||
.add(new ShellyChannel(m, CHGR_BAT, CHANNEL_SENSOR_BAT_LOW, "system:low-battery", ITEMT_SWITCH));
|
||||
}
|
||||
|
||||
public static @Nullable ShellyChannel getDefinition(String channelName) throws IllegalArgumentException {
|
||||
String group = substringBefore(channelName, "#");
|
||||
String channel = substringAfter(channelName, "#");
|
||||
|
||||
if (group.contains(CHANNEL_GROUP_METER)) {
|
||||
group = CHANNEL_GROUP_METER; // map meter1..n to meter
|
||||
} else if (group.contains(CHANNEL_GROUP_RELAY_CONTROL)) {
|
||||
group = CHANNEL_GROUP_RELAY_CONTROL; // map meter1..n to meter
|
||||
} else if (group.contains(CHANNEL_GROUP_LIGHT_CHANNEL)) {
|
||||
group = CHANNEL_GROUP_LIGHT_CHANNEL;
|
||||
} else if (group.contains(CHANNEL_GROUP_STATUS)) {
|
||||
group = CHANNEL_GROUP_STATUS; // map status1..n to meter
|
||||
}
|
||||
|
||||
if (channel.startsWith(CHANNEL_INPUT)) {
|
||||
channel = CHANNEL_INPUT;
|
||||
} else if (channel.startsWith(CHANNEL_BUTTON_TRIGGER)) {
|
||||
channel = CHANNEL_BUTTON_TRIGGER;
|
||||
} else if (channel.startsWith(CHANNEL_STATUS_EVENTTYPE)) {
|
||||
channel = CHANNEL_STATUS_EVENTTYPE;
|
||||
} else if (channel.startsWith(CHANNEL_STATUS_EVENTCOUNT)) {
|
||||
channel = CHANNEL_STATUS_EVENTCOUNT;
|
||||
}
|
||||
|
||||
String channelId = group + "#" + channel;
|
||||
return CHANNEL_DEFINITIONS.get(channelId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Auto-create relay channels depending on relay type/mode
|
||||
*
|
||||
* @return ArrayList<Channel> of channels to be added to the thing
|
||||
*/
|
||||
public static Map<String, Channel> createDeviceChannels(final Thing thing, final ShellyDeviceProfile profile,
|
||||
final ShellySettingsStatus status) {
|
||||
Map<String, Channel> add = new LinkedHashMap<>();
|
||||
|
||||
addChannel(thing, add, profile.settings.name != null, CHGR_DEVST, CHANNEL_DEVST_NAME);
|
||||
|
||||
if (!profile.isSensor) {
|
||||
// Only some devices report the internal device temp
|
||||
addChannel(thing, add, (status.tmp != null) || (status.temperature != null), CHGR_DEVST,
|
||||
CHANNEL_DEVST_ITEMP);
|
||||
}
|
||||
|
||||
// If device has more than 1 meter the channel accumulatedWatts receives the accumulated value
|
||||
boolean accuChannel = (((status.meters != null) && (status.meters.size() > 1) && !profile.isRoller
|
||||
&& !profile.isRGBW2) || ((status.emeters != null && status.emeters.size() > 1)));
|
||||
addChannel(thing, add, accuChannel, CHGR_DEVST, CHANNEL_DEVST_ACCUWATTS);
|
||||
addChannel(thing, add, accuChannel, CHGR_DEVST, CHANNEL_DEVST_ACCUTOTAL);
|
||||
addChannel(thing, add, accuChannel && (status.emeters != null), CHGR_DEVST, CHANNEL_DEVST_ACCURETURNED);
|
||||
addChannel(thing, add, true, CHGR_DEVST, CHANNEL_DEVST_UPDATE);
|
||||
addChannel(thing, add, true, CHGR_DEVST, CHANNEL_DEVST_UPTIME);
|
||||
addChannel(thing, add, true, CHGR_DEVST, CHANNEL_DEVST_HEARTBEAT);
|
||||
|
||||
if (profile.settings.ledPowerDisable != null) {
|
||||
addChannel(thing, add, true, CHGR_DEVST, CHANNEL_LED_POWER_DISABLE);
|
||||
}
|
||||
if (profile.settings.ledStatusDisable != null) {
|
||||
addChannel(thing, add, true, CHGR_DEVST, CHANNEL_LED_STATUS_DISABLE); // WiFi status LED
|
||||
}
|
||||
return add;
|
||||
}
|
||||
|
||||
/**
|
||||
* Auto-create relay channels depending on relay type/mode
|
||||
*
|
||||
* @return ArrayList<Channel> of channels to be added to the thing
|
||||
*/
|
||||
public static Map<String, Channel> createRelayChannels(final Thing thing, final ShellyDeviceProfile profile,
|
||||
final ShellyStatusRelay relay, int idx) {
|
||||
Map<String, Channel> add = new LinkedHashMap<>();
|
||||
String group = profile.getControlGroup(idx);
|
||||
|
||||
ShellySettingsRelay rs = profile.settings.relays.get(idx);
|
||||
addChannel(thing, add, rs.ison != null, group, CHANNEL_OUTPUT);
|
||||
addChannel(thing, add, rs.name != null, group, CHANNEL_OUTPUT_NAME);
|
||||
addChannel(thing, add, rs.autoOn != null, group, CHANNEL_TIMER_AUTOON);
|
||||
addChannel(thing, add, rs.autoOff != null, group, CHANNEL_TIMER_AUTOOFF);
|
||||
addChannel(thing, add, rs.hasTimer != null, group, CHANNEL_TIMER_ACTIVE);
|
||||
|
||||
// Shelly 1/1PM Addon
|
||||
if (relay.extTemperature != null) {
|
||||
addChannel(thing, add, relay.extTemperature.sensor1 != null, CHGR_SENSOR, CHANNEL_ESENDOR_TEMP1);
|
||||
addChannel(thing, add, relay.extTemperature.sensor2 != null, CHGR_SENSOR, CHANNEL_ESENDOR_TEMP2);
|
||||
addChannel(thing, add, relay.extTemperature.sensor3 != null, CHGR_SENSOR, CHANNEL_ESENDOR_TEMP3);
|
||||
}
|
||||
if (relay.extHumidity != null) {
|
||||
addChannel(thing, add, relay.extHumidity.sensor1 != null, CHGR_SENSOR, CHANNEL_ESENDOR_HUMIDITY);
|
||||
}
|
||||
|
||||
return add;
|
||||
}
|
||||
|
||||
public static Map<String, Channel> createDimmerChannels(final Thing thing, final ShellyDeviceProfile profile,
|
||||
final ShellySettingsStatus dstatus, int idx) {
|
||||
Map<String, Channel> add = new LinkedHashMap<>();
|
||||
String group = profile.getControlGroup(idx);
|
||||
|
||||
// Shelly Dimmer has an additional brightness channel
|
||||
addChannel(thing, add, profile.isDimmer, group, CHANNEL_BRIGHTNESS);
|
||||
|
||||
ShellySettingsDimmer ds = profile.settings.dimmers.get(idx);
|
||||
addChannel(thing, add, ds.autoOn != null, group, CHANNEL_TIMER_AUTOON);
|
||||
addChannel(thing, add, ds.autoOff != null, group, CHANNEL_TIMER_AUTOOFF);
|
||||
return add;
|
||||
}
|
||||
|
||||
public static Map<String, Channel> createLightChannels(final Thing thing, final ShellyDeviceProfile profile,
|
||||
final ShellyStatusLightChannel status, int idx) {
|
||||
Map<String, Channel> add = new LinkedHashMap<>();
|
||||
String group = profile.getControlGroup(idx);
|
||||
|
||||
ShellySettingsRgbwLight light = profile.settings.lights.get(idx);
|
||||
// The is no brightness channel in color mode, so we need a power channel
|
||||
addChannel(thing, add, profile.inColor, group, CHANNEL_LIGHT_POWER);
|
||||
|
||||
addChannel(thing, add, light.autoOn != null, group, CHANNEL_TIMER_AUTOON);
|
||||
addChannel(thing, add, light.autoOff != null, group, CHANNEL_TIMER_AUTOOFF);
|
||||
addChannel(thing, add, status.hasTimer != null, group, CHANNEL_TIMER_ACTIVE);
|
||||
return add;
|
||||
}
|
||||
|
||||
public static Map<String, Channel> createInputChannels(final Thing thing, final ShellyDeviceProfile profile,
|
||||
final ShellySettingsStatus status, String group) {
|
||||
Map<String, Channel> add = new LinkedHashMap<>();
|
||||
if (status.inputs != null) {
|
||||
// Create channels per input. For devices with more than 1 input (Dimmer, 1L) multiple channel sets are
|
||||
// created by adding the index to the channel name
|
||||
boolean multi = ((profile.numRelays == 1) || profile.isDimmer || profile.isRoller)
|
||||
&& (profile.numInputs >= 2);
|
||||
for (int i = 0; i < profile.numInputs; i++) {
|
||||
String suffix = multi ? String.valueOf(i + 1) : "";
|
||||
ShellyInputState input = status.inputs.get(i);
|
||||
addChannel(thing, add, true, group, CHANNEL_INPUT + suffix);
|
||||
if (profile.inButtonMode(i)) {
|
||||
addChannel(thing, add, input.event != null, group, CHANNEL_STATUS_EVENTTYPE + suffix);
|
||||
addChannel(thing, add, input.eventCount != null, group, CHANNEL_STATUS_EVENTCOUNT + suffix);
|
||||
}
|
||||
addChannel(thing, add, true, group,
|
||||
(!profile.isRoller ? CHANNEL_BUTTON_TRIGGER + suffix : CHANNEL_EVENT_TRIGGER));
|
||||
}
|
||||
} else if (status.input != null) {
|
||||
// old RGBW2 firmware
|
||||
addChannel(thing, add, true, group, CHANNEL_INPUT);
|
||||
addChannel(thing, add, true, group, CHANNEL_BUTTON_TRIGGER);
|
||||
}
|
||||
return add;
|
||||
}
|
||||
|
||||
public static Map<String, Channel> createRollerChannels(Thing thing, final ShellyControlRoller roller) {
|
||||
Map<String, Channel> add = new LinkedHashMap<>();
|
||||
addChannel(thing, add, true, CHGR_ROLLER, CHANNEL_ROL_CONTROL_CONTROL);
|
||||
addChannel(thing, add, true, CHGR_ROLLER, CHANNEL_ROL_CONTROL_STATE);
|
||||
addChannel(thing, add, true, CHGR_ROLLER, CHANNEL_EVENT_TRIGGER);
|
||||
addChannel(thing, add, roller.currentPos != null, CHGR_ROLLER, CHANNEL_ROL_CONTROL_POS);
|
||||
addChannel(thing, add, roller.stopReason != null, CHGR_ROLLER, CHANNEL_ROL_CONTROL_STOPR);
|
||||
addChannel(thing, add, roller.safetySwitch != null, CHGR_ROLLER, CHANNEL_ROL_CONTROL_SAFETY);
|
||||
|
||||
ShellyBaseHandler handler = (ShellyBaseHandler) thing.getHandler();
|
||||
if (handler != null) {
|
||||
ShellySettingsGlobal settings = handler.getProfile().settings;
|
||||
if (getBool(settings.favoritesEnabled) && (settings.favorites != null)) {
|
||||
addChannel(thing, add, roller.currentPos != null, CHGR_ROLLER, CHANNEL_ROL_CONTROL_FAV);
|
||||
}
|
||||
}
|
||||
return add;
|
||||
}
|
||||
|
||||
public static Map<String, Channel> createMeterChannels(Thing thing, final ShellySettingsMeter meter, String group) {
|
||||
Map<String, Channel> newChannels = new LinkedHashMap<>();
|
||||
addChannel(thing, newChannels, meter.power != null, group, CHANNEL_METER_CURRENTWATTS);
|
||||
addChannel(thing, newChannels, meter.total != null, group, CHANNEL_METER_TOTALKWH);
|
||||
addChannel(thing, newChannels, (meter.counters != null) && (meter.counters[0] != null), group,
|
||||
CHANNEL_METER_LASTMIN1);
|
||||
addChannel(thing, newChannels, meter.timestamp != null, group, CHANNEL_LAST_UPDATE);
|
||||
return newChannels;
|
||||
}
|
||||
|
||||
public static Map<String, Channel> createEMeterChannels(final Thing thing, final ShellySettingsEMeter emeter,
|
||||
String group) {
|
||||
Map<String, Channel> newChannels = new LinkedHashMap<>();
|
||||
addChannel(thing, newChannels, emeter.power != null, group, CHANNEL_METER_CURRENTWATTS);
|
||||
addChannel(thing, newChannels, emeter.total != null, group, CHANNEL_METER_TOTALKWH);
|
||||
addChannel(thing, newChannels, emeter.totalReturned != null, group, CHANNEL_EMETER_TOTALRET);
|
||||
addChannel(thing, newChannels, emeter.reactive != null, group, CHANNEL_EMETER_REACTWATTS);
|
||||
addChannel(thing, newChannels, emeter.voltage != null, group, CHANNEL_EMETER_VOLTAGE);
|
||||
addChannel(thing, newChannels, emeter.current != null, group, CHANNEL_EMETER_CURRENT);
|
||||
addChannel(thing, newChannels, emeter.pf != null, group, CHANNEL_EMETER_PFACTOR);
|
||||
addChannel(thing, newChannels, true, group, CHANNEL_LAST_UPDATE);
|
||||
return newChannels;
|
||||
}
|
||||
|
||||
public static Map<String, Channel> createSensorChannels(final Thing thing, final ShellyDeviceProfile profile,
|
||||
final ShellyStatusSensor sdata) {
|
||||
Map<String, Channel> newChannels = new LinkedHashMap<>();
|
||||
|
||||
// Sensor data
|
||||
addChannel(thing, newChannels, sdata.tmp != null, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_TEMP);
|
||||
addChannel(thing, newChannels, sdata.hum != null, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_HUM);
|
||||
addChannel(thing, newChannels, sdata.lux != null, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_LUX);
|
||||
addChannel(thing, newChannels, sdata.lux != null && sdata.lux.illumination != null, CHANNEL_GROUP_SENSOR,
|
||||
CHANNEL_SENSOR_ILLUM);
|
||||
addChannel(thing, newChannels, sdata.flood != null, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_FLOOD);
|
||||
addChannel(thing, newChannels, sdata.smoke != null, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_FLOOD);
|
||||
addChannel(thing, newChannels, sdata.charger != null, CHGR_DEVST, CHANNEL_DEVST_CHARGER);
|
||||
addChannel(thing, newChannels, sdata.motion != null, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_MOTION);
|
||||
if (sdata.sensor != null) { // DW2 or Motion
|
||||
addChannel(thing, newChannels, sdata.sensor.state != null, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_CONTACT); // DW/DW2
|
||||
addChannel(thing, newChannels, sdata.sensor.motionTimestamp != null, CHANNEL_GROUP_SENSOR, // Motion
|
||||
CHANNEL_SENSOR_MOTION_TS);
|
||||
addChannel(thing, newChannels, sdata.sensor.vibration != null, CHANNEL_GROUP_SENSOR,
|
||||
CHANNEL_SENSOR_VIBRATION);
|
||||
}
|
||||
if (sdata.accel != null) { // DW2
|
||||
addChannel(thing, newChannels, sdata.accel.vibration != null, CHANNEL_GROUP_SENSOR,
|
||||
CHANNEL_SENSOR_VIBRATION);
|
||||
addChannel(thing, newChannels, sdata.accel.tilt != null, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_TILT);
|
||||
}
|
||||
|
||||
// Gas
|
||||
if (sdata.gasSensor != null) {
|
||||
addChannel(thing, newChannels, sdata.gasSensor.selfTestState != null, CHGR_DEVST, CHANNEL_DEVST_SELFTTEST);
|
||||
addChannel(thing, newChannels, sdata.gasSensor.sensorState != null, CHANNEL_GROUP_SENSOR,
|
||||
CHANNEL_SENSOR_SSTATE);
|
||||
addChannel(thing, newChannels, sdata.concentration != null && sdata.concentration.ppm != null,
|
||||
CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_PPM);
|
||||
addChannel(thing, newChannels, sdata.valves != null, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_VALVE);
|
||||
addChannel(thing, newChannels, sdata.gasSensor.sensorState != null, CHANNEL_GROUP_SENSOR,
|
||||
CHANNEL_SENSOR_ALARM_STATE);
|
||||
}
|
||||
|
||||
addChannel(thing, newChannels, sdata.adcs != null, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_VOLTAGE); // UNI
|
||||
|
||||
// Battery
|
||||
if (sdata.bat != null) {
|
||||
addChannel(thing, newChannels, sdata.bat.value != null, CHANNEL_GROUP_BATTERY, CHANNEL_SENSOR_BAT_LEVEL);
|
||||
addChannel(thing, newChannels, sdata.bat.value != null, CHANNEL_GROUP_BATTERY, CHANNEL_SENSOR_BAT_LOW);
|
||||
}
|
||||
|
||||
addChannel(thing, newChannels, sdata.sensorError != null, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_ERROR);
|
||||
addChannel(thing, newChannels, sdata.actReasons != null, CHGR_DEVST, CHANNEL_DEVST_WAKEUP);
|
||||
addChannel(thing, newChannels, true, profile.isButton ? CHANNEL_GROUP_STATUS : CHANNEL_GROUP_SENSOR,
|
||||
CHANNEL_LAST_UPDATE);
|
||||
return newChannels;
|
||||
}
|
||||
|
||||
private static void addChannel(Thing thing, Map<String, Channel> newChannels, boolean supported, String group,
|
||||
String channelName) throws IllegalArgumentException {
|
||||
if (supported) {
|
||||
String channelId = group + "#" + channelName;
|
||||
ChannelUID channelUID = new ChannelUID(thing.getUID(), channelId);
|
||||
ShellyChannel channelDef = getDefinition(channelId);
|
||||
if (channelDef != null) {
|
||||
ChannelTypeUID channelTypeUID = channelDef.typeId.contains("system:")
|
||||
? new ChannelTypeUID(channelDef.typeId)
|
||||
: new ChannelTypeUID(BINDING_ID, channelDef.typeId);
|
||||
Channel channel;
|
||||
if (channelDef.typeId.equalsIgnoreCase("system:button")) {
|
||||
channel = ChannelBuilder.create(channelUID, null).withKind(ChannelKind.TRIGGER)
|
||||
.withType(channelTypeUID).build();
|
||||
} else {
|
||||
channel = ChannelBuilder.create(channelUID, channelDef.itemType).withType(channelTypeUID).build();
|
||||
}
|
||||
newChannels.put(channelId, channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ShellyChannel {
|
||||
private final ShellyTranslationProvider messages;
|
||||
public String group = "";
|
||||
public String groupLabel = "";
|
||||
public String groupDescription = "";
|
||||
|
||||
public String channel = "";
|
||||
public String label = "";
|
||||
public String description = "";
|
||||
public String itemType = "";
|
||||
public String typeId = "";
|
||||
public String category = "";
|
||||
public Set<String> tags = new HashSet<>();
|
||||
public @Nullable Unit<?> unit;
|
||||
public Optional<Integer> min = Optional.empty();
|
||||
public Optional<Integer> max = Optional.empty();
|
||||
public Optional<Integer> step = Optional.empty();
|
||||
public Optional<String> pattern = Optional.empty();
|
||||
|
||||
public ShellyChannel(ShellyTranslationProvider messages, String group, String channel, String typeId,
|
||||
String itemType, String... category) {
|
||||
this.messages = messages;
|
||||
this.group = group;
|
||||
this.channel = channel;
|
||||
this.itemType = itemType;
|
||||
this.typeId = typeId;
|
||||
|
||||
groupLabel = getText(PREFIX_GROUP + group + ".label");
|
||||
groupDescription = getText(PREFIX_GROUP + group + ".description");
|
||||
label = getText(PREFIX_CHANNEL + channel + ".label");
|
||||
description = getText(PREFIX_CHANNEL + channel + ".description");
|
||||
}
|
||||
|
||||
public String getChanneId() {
|
||||
return group + "#" + channel;
|
||||
}
|
||||
|
||||
public String getGroupLabel() {
|
||||
return getGroupAttribute("group");
|
||||
}
|
||||
|
||||
public String getGroupDescription() {
|
||||
return getGroupAttribute("group");
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return getChannelAttribute("label");
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return getChannelAttribute("description");
|
||||
}
|
||||
|
||||
public boolean getAdvanced() {
|
||||
String attr = getChannelAttribute("advanced");
|
||||
return attr.isEmpty() ? false : Boolean.valueOf(attr);
|
||||
}
|
||||
|
||||
public boolean getReadyOnly() {
|
||||
String attr = getChannelAttribute("advanced");
|
||||
return attr.isEmpty() ? false : Boolean.valueOf(attr);
|
||||
}
|
||||
|
||||
public String getCategory() {
|
||||
return getChannelAttribute("category");
|
||||
}
|
||||
|
||||
public String getMin() {
|
||||
return getChannelAttribute("min");
|
||||
}
|
||||
|
||||
public String getMax() {
|
||||
return getChannelAttribute("max");
|
||||
}
|
||||
|
||||
public String getStep() {
|
||||
return getChannelAttribute("step");
|
||||
}
|
||||
|
||||
public String getPattern() {
|
||||
return getChannelAttribute("pattern");
|
||||
}
|
||||
|
||||
public String getGroupAttribute(String attribute) {
|
||||
String key = PREFIX_GROUP + group + "." + attribute;
|
||||
String value = messages.getText(key);
|
||||
return value != null && !value.equals(key) ? value : "";
|
||||
}
|
||||
|
||||
public String getChannelAttribute(String attribute) {
|
||||
String key = PREFIX_CHANNEL + channel + "." + attribute;
|
||||
String value = messages.getText(key);
|
||||
return value != null && !value.equals(key) ? value : "";
|
||||
}
|
||||
|
||||
private String getText(String key) {
|
||||
String text = messages.get(key);
|
||||
return text != null ? text : "";
|
||||
}
|
||||
}
|
||||
|
||||
public static class ChannelMap {
|
||||
private final Map<String, ShellyChannel> map = new HashMap<>();
|
||||
|
||||
private ChannelMap add(ShellyChannel def) {
|
||||
map.put(def.getChanneId(), def);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ShellyChannel get(String channelName) throws IllegalArgumentException {
|
||||
ShellyChannel def = null;
|
||||
if (channelName.contains("#")) {
|
||||
def = map.get(channelName);
|
||||
if (def != null) {
|
||||
return def;
|
||||
}
|
||||
}
|
||||
for (HashMap.Entry<String, ShellyChannel> entry : map.entrySet()) {
|
||||
if (entry.getValue().channel.contains("#" + channelName)) {
|
||||
def = entry.getValue();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (def == null) {
|
||||
throw new IllegalArgumentException("Channel definition for " + channelName + " not found!");
|
||||
}
|
||||
|
||||
return def;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,7 +10,7 @@
|
|||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.shelly.internal.util;
|
||||
package org.openhab.binding.shelly.internal.provider;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
|
@ -19,6 +19,10 @@ import org.eclipse.jdt.annotation.Nullable;
|
|||
import org.openhab.core.i18n.LocaleProvider;
|
||||
import org.openhab.core.i18n.TranslationProvider;
|
||||
import org.osgi.framework.Bundle;
|
||||
import org.osgi.framework.FrameworkUtil;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Reference;
|
||||
|
||||
/**
|
||||
* {@link ShellyTranslationProvider} provides i18n message lookup
|
||||
|
@ -26,24 +30,20 @@ import org.osgi.framework.Bundle;
|
|||
* @author Markus Michels - Initial contribution
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(service = ShellyTranslationProvider.class)
|
||||
public class ShellyTranslationProvider {
|
||||
|
||||
private final Bundle bundle;
|
||||
private final TranslationProvider i18nProvider;
|
||||
private final LocaleProvider localeProvider;
|
||||
|
||||
public ShellyTranslationProvider(Bundle bundle, TranslationProvider i18nProvider, LocaleProvider localeProvider) {
|
||||
this.bundle = bundle;
|
||||
@Activate
|
||||
public ShellyTranslationProvider(@Reference TranslationProvider i18nProvider,
|
||||
@Reference LocaleProvider localeProvider) {
|
||||
this.bundle = FrameworkUtil.getBundle(this.getClass());
|
||||
this.i18nProvider = i18nProvider;
|
||||
this.localeProvider = localeProvider;
|
||||
}
|
||||
|
||||
public ShellyTranslationProvider(final ShellyTranslationProvider other) {
|
||||
this.bundle = other.bundle;
|
||||
this.i18nProvider = other.i18nProvider;
|
||||
this.localeProvider = other.localeProvider;
|
||||
}
|
||||
|
||||
public @Nullable String get(String key, @Nullable Object... arguments) {
|
||||
return getText(key.contains("@text/") || key.contains(".shelly.") ? key : "message." + key, arguments);
|
||||
}
|
|
@ -93,7 +93,7 @@ public class ShellyChannelCache {
|
|||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
logger.debug("{}: Unable to update channel {} with {} (type {}): {} ({})", thingName, channelId, newValue,
|
||||
newValue.getClass(), ShellyUtils.getMessage(e), e.getClass());
|
||||
newValue.getClass(), ShellyUtils.getMessage(e), e.getClass(), e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -119,7 +119,8 @@ public class ShellyChannelCache {
|
|||
}
|
||||
|
||||
public State getValue(String channelId) {
|
||||
return channelData.getOrDefault(channelId, UnDefType.NULL);
|
||||
State st = channelData.get(channelId);
|
||||
return st != null ? st : UnDefType.NULL;
|
||||
}
|
||||
|
||||
public void resetChannel(String channelId) {
|
||||
|
|
|
@ -16,6 +16,7 @@ import static org.openhab.binding.shelly.internal.ShellyBindingConstants.*;
|
|||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.DateTimeException;
|
||||
|
@ -40,6 +41,10 @@ import org.openhab.core.types.Command;
|
|||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.types.UnDefType;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import com.google.gson.internal.Primitives;
|
||||
|
||||
/**
|
||||
* {@link ShellyUtils} provides general utility functions
|
||||
*
|
||||
|
@ -47,6 +52,49 @@ import org.openhab.core.types.UnDefType;
|
|||
*/
|
||||
@NonNullByDefault
|
||||
public class ShellyUtils {
|
||||
private final static String PRE = "Unable to create object of type ";
|
||||
|
||||
public static <T> T fromJson(Gson gson, @Nullable String json, Class<T> classOfT) throws ShellyApiException {
|
||||
@Nullable
|
||||
T o = fromJson(gson, json, classOfT, true);
|
||||
if (o == null) {
|
||||
throw new ShellyApiException("Unable to create JSON object");
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
||||
public static @Nullable <T> T fromJson(Gson gson, @Nullable String json, Class<T> classOfT, boolean exceptionOnNull)
|
||||
throws ShellyApiException {
|
||||
String className = substringAfter(classOfT.getName(), "$");
|
||||
|
||||
if (json == null) {
|
||||
if (exceptionOnNull) {
|
||||
throw new IllegalArgumentException(PRE + className + ": json is null!");
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (classOfT.isInstance(json)) {
|
||||
return Primitives.wrap(classOfT).cast(json);
|
||||
} else if (json.isEmpty()) { // update GSON might return null
|
||||
throw new ShellyApiException(PRE + className + "from empty JSON");
|
||||
} else {
|
||||
try {
|
||||
@Nullable
|
||||
T obj = gson.fromJson(json, classOfT);
|
||||
if ((obj == null) && exceptionOnNull) { // new in OH3: fromJson may return null
|
||||
throw new ShellyApiException(PRE + className + "from JSON: " + json);
|
||||
}
|
||||
return obj;
|
||||
} catch (JsonSyntaxException e) {
|
||||
throw new ShellyApiException(PRE + className + "from JSON (syntax/format error): " + json, e);
|
||||
} catch (RuntimeException e) {
|
||||
throw new ShellyApiException(PRE + className + "from JSON: " + json, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static String mkChannelId(String group, String channel) {
|
||||
return group + "#" + channel;
|
||||
}
|
||||
|
@ -171,7 +219,7 @@ public class ShellyUtils {
|
|||
return UnDefType.NULL;
|
||||
}
|
||||
BigDecimal bd = new BigDecimal(value.doubleValue());
|
||||
return toQuantityType(bd.setScale(digits, BigDecimal.ROUND_HALF_UP), unit);
|
||||
return toQuantityType(bd.setScale(digits, RoundingMode.HALF_UP), unit);
|
||||
}
|
||||
|
||||
public static State toQuantityType(@Nullable Number value, Unit<?> unit) {
|
||||
|
@ -233,7 +281,7 @@ public class ShellyUtils {
|
|||
}
|
||||
|
||||
public static String buildWhiteGroupName(ShellyDeviceProfile profile, Integer channelId) {
|
||||
return profile.isBulb || profile.isDuo && !profile.inColor ? CHANNEL_GROUP_WHITE_CONTROL
|
||||
return profile.isBulb || profile.isDuo ? CHANNEL_GROUP_WHITE_CONTROL
|
||||
: CHANNEL_GROUP_LIGHT_CHANNEL + channelId.toString();
|
||||
}
|
||||
|
||||
|
|
|
@ -5,22 +5,28 @@
|
|||
|
||||
<name>Shelly Binding</name>
|
||||
<description>This binding supports the Shelly series of devices.</description>
|
||||
<author>Markus Michels</author>
|
||||
|
||||
<config-description>
|
||||
<config-description uri="binding:shelly">
|
||||
<parameter name="defaultUserId" type="text">
|
||||
<default>admin</default>
|
||||
<label>Default User</label>
|
||||
<description>Default userId to access protected Shelly devices.</description>
|
||||
<label>@text/binding.shelly.config.defaultUserId.label</label>
|
||||
<description>@text/binding.shelly.config.defaultUserId.description</description>
|
||||
</parameter>
|
||||
<parameter name="defaultPassword" type="text">
|
||||
<default>admin</default>
|
||||
<label>Default Password</label>
|
||||
<description>Default password to access protected Shelly devices.</description>
|
||||
<label>@text/binding.shelly.config.defaultPassword.label</label>
|
||||
<description>@text/binding.shelly.config.defaultPassword.description</description>
|
||||
</parameter>
|
||||
<parameter name="localIP" type="text">
|
||||
<label>@text/binding.shelly.config.localIP.label</label>
|
||||
<description>@text/binding.shelly.config.localIP.description</description>
|
||||
<default></default>
|
||||
</parameter>
|
||||
<parameter name="autoCoIoT" type="boolean">
|
||||
<default>true</default>
|
||||
<label>Auto-enable CoIoT</label>
|
||||
<description>True: Enable CoIoT events by default when firmware 1.6+ is detected</description>
|
||||
<label>@text/binding.shelly.config.autoCoIoT.label</label>
|
||||
<description>@text/binding.shelly.config.autoCoIoT.description</description>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
|
|
|
@ -43,8 +43,8 @@
|
|||
<advanced>true</advanced>
|
||||
<default>true</default>
|
||||
</parameter>
|
||||
<parameter name="updateInterval" type="integer" required="true" unit="s">
|
||||
<label>Update Interval</label>
|
||||
<parameter name="updateInterval" type="integer" min="10" required="true" unit="s">
|
||||
<label>General Update Interval</label>
|
||||
<description>Interval to query an update from the device.</description>
|
||||
<default>60</default>
|
||||
<unitLabel>seconds</unitLabel>
|
||||
|
@ -62,6 +62,16 @@
|
|||
<description>Password for HTTP API access.</description>
|
||||
<context>password</context>
|
||||
</parameter>
|
||||
<parameter name="favoriteUP" type="integer" min="0" max="4" required="false">
|
||||
<label>Favorite Id for UP</label>
|
||||
<description>Which position favorite should be used for UP (0=none, favorites are defined in the Shelly App)</description>
|
||||
<default>0</default>
|
||||
</parameter>
|
||||
<parameter name="favoriteDOWN" type="integer" min="0" max="4" required="false">
|
||||
<label>Favorite Id for DOWN</label>
|
||||
<description>Which position favorite should be used for DOWN (0=none, favorites are defined in the Shelly App)</description>
|
||||
<default>0</default>
|
||||
</parameter>
|
||||
<parameter name="deviceIp" type="text" required="true">
|
||||
<label>Device IP Address</label>
|
||||
<description>IP-Address of the Shelly device.</description>
|
||||
|
@ -71,7 +81,7 @@
|
|||
<label>Enable Roller Events (Roller only)</label>
|
||||
<description>True if the binding should register to get Roller Events.</description>
|
||||
<advanced>true</advanced>
|
||||
<default>true</default>
|
||||
<default>false</default>
|
||||
</parameter>
|
||||
<parameter name="eventsCoIoT" type="boolean" required="false">
|
||||
<label>Enable CoIoT Events</label>
|
||||
|
@ -79,8 +89,8 @@
|
|||
<advanced>true</advanced>
|
||||
<default>true</default>
|
||||
</parameter>
|
||||
<parameter name="updateInterval" type="integer" required="true" unit="s">
|
||||
<label>Update Interval</label>
|
||||
<parameter name="updateInterval" type="integer" min="10" required="true" unit="s">
|
||||
<label>General Update Interval</label>
|
||||
<description>Interval to query an update from the device.</description>
|
||||
<default>60</default>
|
||||
<unitLabel>seconds</unitLabel>
|
||||
|
@ -104,7 +114,8 @@
|
|||
</parameter>
|
||||
<parameter name="brightnessAutoOn" type="boolean" required="false">
|
||||
<label>Brightness Auto-ON</label>
|
||||
<description>true: Turn device ON if brightness>0 is set; false: don't touch power status when brightness is set.</description>
|
||||
<description>true: Turn device ON if brightness above 0 is set; false: don't touch power status when brightness is
|
||||
set.</description>
|
||||
<default>true</default>
|
||||
</parameter>
|
||||
<parameter name="eventsButton" type="boolean" required="false">
|
||||
|
@ -131,8 +142,8 @@
|
|||
<advanced>true</advanced>
|
||||
<default>true</default>
|
||||
</parameter>
|
||||
<parameter name="updateInterval" type="integer" required="true" unit="s">
|
||||
<label>Update Interval</label>
|
||||
<parameter name="updateInterval" type="integer" min="10" required="true" unit="s">
|
||||
<label>General Update Interval</label>
|
||||
<description>Interval to query an update from the device.</description>
|
||||
<default>60</default>
|
||||
<unitLabel>seconds</unitLabel>
|
||||
|
@ -172,8 +183,8 @@
|
|||
<advanced>true</advanced>
|
||||
<default>true</default>
|
||||
</parameter>
|
||||
<parameter name="updateInterval" type="integer" required="true" unit="s">
|
||||
<label>Update Interval</label>
|
||||
<parameter name="updateInterval" type="integer" min="10" required="true" unit="s">
|
||||
<label>General Update Interval</label>
|
||||
<description>Interval to query an update from the device.</description>
|
||||
<default>60</default>
|
||||
<unitLabel>seconds</unitLabel>
|
||||
|
@ -207,8 +218,8 @@
|
|||
<advanced>true</advanced>
|
||||
<default>true</default>
|
||||
</parameter>
|
||||
<parameter name="updateInterval" type="integer" required="true" unit="s">
|
||||
<label>Update Interval</label>
|
||||
<parameter name="updateInterval" type="integer" min="10" required="true" unit="s">
|
||||
<label>General Update Interval</label>
|
||||
<description>Interval to query an update from the device.</description>
|
||||
<default>60</default>
|
||||
<unitLabel>seconds</unitLabel>
|
||||
|
@ -250,10 +261,10 @@
|
|||
<advanced>true</advanced>
|
||||
<default>true</default>
|
||||
</parameter>
|
||||
<parameter name="updateInterval" type="integer" required="true" unit="s">
|
||||
<label>Update Interval</label>
|
||||
<parameter name="updateInterval" type="integer" min="60" required="true" unit="s">
|
||||
<label>General Update Interval</label>
|
||||
<description>Interval to query an update from the device.</description>
|
||||
<default>3600</default>
|
||||
<default>900</default>
|
||||
<unitLabel>seconds</unitLabel>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
|
@ -286,10 +297,10 @@
|
|||
<advanced>true</advanced>
|
||||
<default>true</default>
|
||||
</parameter>
|
||||
<parameter name="updateInterval" type="integer" required="true" unit="s">
|
||||
<parameter name="updateInterval" type="integer" min="10" required="true" unit="s">
|
||||
<label>Update Interval</label>
|
||||
<description>Interval to query an update from the device.</description>
|
||||
<default>3600</default>
|
||||
<default>60</default>
|
||||
<unitLabel>seconds</unitLabel>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
|
|
|
@ -1,3 +1,12 @@
|
|||
binding.shelly.config.defaultUserId.label = Default UserId
|
||||
binding.shelly.config.defaultUserId.description = This default user id will be used for device access if no one is specified in the thing configuration.
|
||||
binding.shelly.config.defaultPassword.label = Default Password
|
||||
binding.shelly.config.defaultPassword.description = Default password for device access if none is specified in the thing configuration.
|
||||
binding.shelly.config.localIP.label = Host Interface IP
|
||||
binding.shelly.config.localIP.description = This interface will be used to setup CoIoT listen and build Action URLs. openHAB's network configuration will be used if this is not set (recommended)
|
||||
binding.shelly.config.autoCoIoT.label = Auto-CoIoT
|
||||
binding.shelly.config.autoCoIoT.description = If enabled CoIoT will be automatically used when the devices runs a firmware version 1.6 or newer; false: Use thing configuration to enabled/disable CoIoT events.
|
||||
|
||||
discovery.failed# Config status messages
|
||||
config-status.error.missing-device-ip=IP address of the Shelly device is missing.
|
||||
|
||||
|
@ -20,12 +29,14 @@ message.init.noipaddress = Unable to detect local IP address. Please make sure t
|
|||
message.init.protected = Device is password protected, enter correct credentials in thing configuration.
|
||||
message.command.failed = ERROR: Unable to process command {0} for channel {1}
|
||||
message.command.init = Thing not yet initialized, command {0} triggers initialization
|
||||
message.status.unknown.initializing = Initializing or device in sleep mode.
|
||||
message.statusupdate.failed = Unable to update status
|
||||
message.event.triggered = Event triggered: {0}
|
||||
message.coap.init.failed = Unable to start CoIoT: {0}
|
||||
message.discovery.disabled = Device is marked as non-discoverable -> skip
|
||||
message.discovery.protected = Device {0} reported 'Access defined' (missing userid/password or incorrect).
|
||||
message.discovery.failed = Device discovery of device with IP address {0} failed: {1}
|
||||
message.roller.favmissing = Roller position favorites are not supported by installed firmware or not configured in the Shelly App
|
||||
|
||||
# Device
|
||||
channel-type.shelly.deviceName.label = Device Name
|
||||
|
@ -42,6 +53,8 @@ channel-type.shelly.temperature3.label = Temperature 3
|
|||
channel-type.shelly.temperature3.description = Temperature of external Sensor #3
|
||||
channel-type.shelly.humidity.label = Humidity
|
||||
channel-type.shelly.humidity.description = Relative humidity (0..100%)
|
||||
channel-type.shelly.motionTimestamp.label = Last Motion
|
||||
channel-type.shelly.motionTimestamp.description = Timestamp when last motion was detected.
|
||||
|
||||
# Roller
|
||||
channel-type.shelly.rollerState.label = State
|
||||
|
|
|
@ -1,6 +1,14 @@
|
|||
# binding
|
||||
binding.shelly.name = Shelly Binding
|
||||
binding.shelly.description = Dieses Binding integriert Shelly-Komponenten, die über WiFi gesteuert werden können.
|
||||
binding.shelly.config.defaultUserId.label = Standardbenutzerkennung
|
||||
binding.shelly.config.defaultUserId.description = Sofern konfiguriert, wird diese für den Zugriff auf das Gerät verwendet, wenn in der Thing-Konfiguration keine angegeben ist.
|
||||
binding.shelly.config.defaultPassword.label = Standardpasswort
|
||||
binding.shelly.config.defaultPassword.description = Dieses Passwort wird für den Gerätezugriff verwendet, wenn in der Thing-Konfiguration keines gesetzt ist.
|
||||
binding.shelly.config.localIP.label = Host Interface IP
|
||||
binding.shelly.config.localIP.description = Lokale IP-Adresse der Netzwerk-Schnittstelle, welche für Verbindungen genutzt wird (CoIoT Listen und http-Callback). Default: Voreingestelltes Interface aus der openHAB Netzwerkkonfiguration.
|
||||
binding.shelly.config.autoCoIoT.label = Auto-CoIoT
|
||||
binding.shelly.config.autoCoIoT.description = Bei aktiviertem Auto-CoIoT wird das Protokoll aktiviert, sobald das Gerät eine Firmwareversion 1.6 oder neuer verwendet. Andernfalls wird dies über die Thing-Konfiguration gesteuert.
|
||||
|
||||
# Config status messages
|
||||
config-status.error.missing-deviceip=Die IP-Adresse des Shelly Gerätes ist nicht konfiguriert.
|
||||
|
@ -27,6 +35,7 @@ message.init.noipaddress = Es konnte keine lokale IP-Adresse ermittelt werden. B
|
|||
message.init.protected = Das Gerät ist passwortgeschützt, die Zugangsdaten müssen in der Thing Konfiguration hinterlegt werden.
|
||||
message.command.failed = FEHLER: Der Befehl {0} für Kanal {1} kann nicht verarbeitet werden
|
||||
message.command.init = Thing aktuell nicht initialisiert, der Befehl {0} führt zur Initialisierung
|
||||
message.status.unknown.initializing = Initialisierung oder Gerät im Schlafmodus.
|
||||
message.statusupdate.failed = Status konnte nicht aktualisiert werden
|
||||
message.event.triggered = Event erzeugt: {0}
|
||||
message.coap.init.failed = CoAP/CoIoT konnte nicht gestartet werden: {0}
|
||||
|
@ -34,10 +43,13 @@ message.discovery.disabled = Das Ger
|
|||
message.discovery.protected = Das Gerät mit der IP-Adresse {0} ist zugriffsgeschützt und keine Zugangsdaten konfiguriert.
|
||||
message.discovery.failed = Erkennung des Gerätes mit der IP-Adresse {0} ist fehlgeschlagen
|
||||
message.roller.calibrating = Das Gerät ist nicht kalibriert. Es ist eine Kalibrierung mit der Shelly App erforderlich.
|
||||
message.roller.favmissing = Positions-Favoriten werden von der installierten Firmwareversion nicht unterstützt (ab 1.9.2), oder sind nicht in der Shelly App konfiguriert.
|
||||
|
||||
# thing types
|
||||
thing-type.shelly.shelly1.label = Shelly1 (SHSW-1)
|
||||
thing-type.shelly.shelly1.label = Shelly 1 (SHSW-1)
|
||||
thing-type.shelly.shelly1.description = Shelly 1 (1 Relay)
|
||||
thing-type.shelly.shelly1l.label = Shelly 1L (SHSW-L)
|
||||
thing-type.shelly.shelly1l.description = Shelly 1L (1 Relay)
|
||||
thing-type.shelly.shelly1pm.label = Shelly 1PM (SHSW-PM)
|
||||
thing-type.shelly.shelly1pm.description = Shelly 1PM mit 1xRelais und Strommesser
|
||||
thing-type.shelly.shellyem.label = Shelly EM (SHEM)
|
||||
|
@ -56,8 +68,14 @@ thing-type.shelly.shelly4pro.label = Shelly4 Pro Relay (SHSW-4)
|
|||
thing-type.shelly.shelly4pro.description = Shelly 4 Pro mit 4 Relais und Strommessern
|
||||
thing-type.shelly.shellyplug.label = Shelly Plug (SHPLG)
|
||||
thing-type.shelly.shellyplug.description = Shelly Plug als schaltbare Steckdose
|
||||
thing-type.shelly.shellyplug.label = Shelly Plug (SHPLG)
|
||||
thing-type.shelly.shellyplug.description = Shelly Plug als schaltbare Steckdose
|
||||
thing-type.shelly.shellyplugu1.label = Shelly Plug US (SHPLG-U1)
|
||||
thing-type.shelly.shellyplugu1.description = Shelly Plug-US als schaltbare Steckdose (110V)
|
||||
thing-type.shelly.shellyplugs.label = Shelly Plug-S (SHPLG-S)
|
||||
thing-type.shelly.shellyplugs.description = Shelly Plug-S als schaltbare Steckdose
|
||||
thing-type.shelly.shellyuni.label = Shelly UNI (SHUNI-1)
|
||||
thing-type.shelly.shellyuni.description = Shelly UNI
|
||||
thing-type.shelly.shellydimmer.label = Shelly Dimmer (SHDM-1)
|
||||
thing-type.shelly.shellydimmer.description = Shelly mit Dimmer-Funktion
|
||||
thing-type.shelly.shellydimmer2.label = Shelly Dimmer (SHDM-2)
|
||||
|
@ -80,6 +98,8 @@ thing-type.shelly.shellybulb.label = Shelly Bulb (SHBLB-1)
|
|||
thing-type.shelly.shellybulb.description = Shelly Glühbirne weiß/Farbe
|
||||
thing-type.shelly.shellybulbduo.label = Shelly Duo (SHBDUO-1)
|
||||
thing-type.shelly.shellybulbduo.description = Shelly Duo Glühbirne
|
||||
thing-type.shelly.shellycolorbulb.label = Shelly Duo Color (SHCB-1)
|
||||
thing-type.shelly.shellycolorbulb.description = Farbige Shelly Duo Glühbirne (Farb- und Weißmodus)
|
||||
thing-type.shelly.shellyvintage.label = Shelly Vintage (SHVIN-1)
|
||||
thing-type.shelly.shellyvintage.description = Shelly Vintage Glühbirne
|
||||
thing-type.shelly.shellyrgbw2-color.label = Shelly RGBW2 Color Mode (SHRGBW2)
|
||||
|
@ -87,25 +107,61 @@ thing-type.shelly.shellyrgbw2-color.description = Shelly RGBW-Controller im Farb
|
|||
thing-type.shelly.shellyrgbw2-white.label = Shelly RGBW2 White Mode (SHRGBW2)
|
||||
thing-type.shelly.shellyrgbw2-white.description = Shelly RGBW-Controller im Weiß-Modus, 4 Streifen
|
||||
|
||||
# thing config - generic
|
||||
thing-type.config.shelly.generic.userId.label = Benutzer
|
||||
thing-type.config.shelly.generic.userId.description = Benutzerkennung für API-Zugriff
|
||||
thing-type.config.shelly.generic.password.label = Passwort
|
||||
thing-type.config.shelly.generic.password.description = Passwort für API-Zugriff
|
||||
thing-type.config.shelly.generic.deviceIp.label = IP Adresse
|
||||
thing-type.config.shelly.generic.deviceIp.description = IP Adresse der Shelly-Komponente
|
||||
thing-type.config.shelly.generic.weakSignal.label = Schwaches Signal (dBm)
|
||||
thing-type.config.shelly.generic.weakSignal.description = Ein Alarm wird ausgelöst, wenn das WiFi-Signal diesen Wert unterschreitet. Voreinstellung: -80 dBm
|
||||
thing-type.config.shelly.generic.eventsButton.label = Button Events
|
||||
thing-type.config.shelly.generic.eventsButton.description = Aktiviert die Button Action URLS
|
||||
thing-type.config.shelly.generic.eventsPush.label = Push Events
|
||||
thing-type.config.shelly.generic.eventsPush.description = Aktiviert die Push Button Action URLS
|
||||
thing-type.config.shelly.generic.eventsSwitch.label = Output Events
|
||||
thing-type.config.shelly.generic.eventsSwitch.description = Aktiviert die Output Action URLS
|
||||
thing-type.config.shelly.generic.eventsCoIoT.label = CoIoT aktivieren
|
||||
thing-type.config.shelly.generic.eventsCoIoT.description = Aktiviert CoIoT-Protokoll (CoAP-basiert)
|
||||
thing-type.config.shelly.generic.updateInterval.label = Status-Intervall
|
||||
thing-type.config.shelly.generic.updateInterval.description = Intervall für die Hintergundaktualisierung
|
||||
# thing config - relay
|
||||
thing-type.config.shelly.relay.userId.label = Benutzer
|
||||
thing-type.config.shelly.relay.userId.description = Benutzerkennung für API-Zugriff
|
||||
thing-type.config.shelly.relay.password.label = Passwort
|
||||
thing-type.config.shelly.relay.password.description = Passwort für API-Zugriff
|
||||
thing-type.config.shelly.relay.deviceIp.label = IP Adresse
|
||||
thing-type.config.shelly.relay.deviceIp.description = IP Adresse der Shelly-Komponente
|
||||
thing-type.config.shelly.relay.eventsButton.label = Button Events
|
||||
thing-type.config.shelly.relay.eventsButton.description = Aktiviert die Button Action URLS
|
||||
thing-type.config.shelly.relay.eventsPush.label = Push Events
|
||||
thing-type.config.shelly.relay.eventsPush.description = Aktiviert die Push Button Action URLS
|
||||
thing-type.config.shelly.relay.eventsSwitch.label = Output Events
|
||||
thing-type.config.shelly.relay.eventsSwitch.description = Aktiviert die Output Action URLS
|
||||
thing-type.config.shelly.relay.eventsCoIoT.label = CoIoT aktivieren
|
||||
thing-type.config.shelly.relay.eventsCoIoT.description = Aktiviert CoIoT-Protokoll (CoAP-basiert)
|
||||
thing-type.config.shelly.relay.updateInterval.label = Status-Intervall
|
||||
thing-type.config.shelly.relay.updateInterval.description = Intervall für die Hintergundaktualisierung
|
||||
|
||||
# thing config - roller
|
||||
thing-type.config.shelly.roller.userId.label = Benutzer
|
||||
thing-type.config.shelly.roller.userId.description = Benutzerkennung für API-Zugriff
|
||||
thing-type.config.shelly.roller.password.label = Passwort
|
||||
thing-type.config.shelly.roller.password.description = Passwort für API-Zugriff
|
||||
thing-type.config.shelly.roller.favoriteUP.label = Favoriten ID für UP
|
||||
thing-type.config.shelly.roller.favoriteUP.description = Gibt die Favoriten ID an, die beim Command UP auf den roller#control channel verwendet wird (Konfiguration in der Shelly App)
|
||||
thing-type.config.shelly.roller.favoriteDOWN.label = Favoriten ID für DOWN
|
||||
thing-type.config.shelly.roller.favoriteDOWN.description = Gibt die Favoriten ID an, die beim Command DOWN auf den roller#control channel verwendet wird (Konfiguration in der Shelly App)
|
||||
thing-type.config.shelly.roller.deviceIp.label = IP Adresse
|
||||
thing-type.config.shelly.roller.deviceIp.description = IP Adresse der Shelly-Komponente
|
||||
thing-type.config.shelly.roller.eventsRoller.label = Rollladen-Events
|
||||
thing-type.config.shelly.roller.eventsRoller.description = Aktiviert die Roller Action URLS
|
||||
thing-type.config.shelly.roller.eventsCoIoT.label = CoIoT aktivieren
|
||||
thing-type.config.shelly.roller.eventsCoIoT.description = Aktiviert CoIoT-Protokoll (CoAP-basiert)
|
||||
thing-type.config.shelly.roller.updateInterval.label = Status-Intervall
|
||||
thing-type.config.shelly.roller.updateInterval.description = Intervall für die Hintergundaktualisierung
|
||||
|
||||
# thing config - dimmer
|
||||
thing-type.config.shelly.dimmer.userId.label = Benutzer
|
||||
thing-type.config.shelly.dimmer.userId.description = Benutzerkennung für API-Zugriff
|
||||
thing-type.config.shelly.dimmer.password.label = Passwort
|
||||
thing-type.config.shelly.dimmer.password.description = Passwort für API-Zugriff
|
||||
thing-type.config.shelly.dimmer.deviceIp.label = IP Adresse
|
||||
thing-type.config.shelly.dimmer.deviceIp.description = IP Adresse der Shelly-Komponente
|
||||
thing-type.config.shelly.dimmer.brightnessAutoOn.label = Helligkeit Auto-EIN
|
||||
thing-type.config.shelly.dimmer.brightnessAutoOn.description = an: Licht wird eingeschaltet, wenn ein Helligkeitswert größer 0 gesetzt wird; aus: Helligkeit wird genetzt, aber das Gerät nicht eingeschaltet
|
||||
thing-type.config.shelly.dimmer.eventsButton.label = Button Events
|
||||
thing-type.config.shelly.dimmer.eventsButton.description = Aktiviert die Button Action URLS
|
||||
thing-type.config.shelly.dimmer.eventsPush.label = Push Events
|
||||
thing-type.config.shelly.dimmer.eventsPush.description = Aktiviert die Push Button Action URLS
|
||||
thing-type.config.shelly.dimmer.eventsSwitch.label = Output Events
|
||||
thing-type.config.shelly.dimmer.eventsSwitch.description = Aktiviert die Output Action URLS
|
||||
thing-type.config.shelly.dimmer.eventsCoIoT.label = CoIoT aktivieren
|
||||
thing-type.config.shelly.dimmer.eventsCoIoT.description = Aktiviert CoIoT-Protokoll (CoAP-basiert)
|
||||
thing-type.config.shelly.dimmer.updateInterval.label = Status-Intervall
|
||||
thing-type.config.shelly.dimmer.updateInterval.description = Intervall für die Hintergundaktualisierung
|
||||
|
||||
# thing config - light
|
||||
thing-type.config.shelly.light.userId.label = Benutzer
|
||||
|
@ -114,8 +170,6 @@ thing-type.config.shelly.light.password.label = Passwort
|
|||
thing-type.config.shelly.light.password.description = Passwort für API-Zugriff
|
||||
thing-type.config.shelly.light.deviceIp.label = IP Adresse
|
||||
thing-type.config.shelly.light.deviceIp.description = IP Adresse der Shelly-Komponente
|
||||
thing-type.config.shelly.light.weakSignal.label = Schwaches Signal (dBm)
|
||||
thing-type.config.shelly.light.weakSignal.description = Ein Alarm wird ausgelöst, wenn das WiFi-Signal diesen Wert unterschreitet. Voreinstellung: -80 dBm
|
||||
thing-type.config.shelly.light.brightnessAutoOn.label = Helligkeit Auto-EIN
|
||||
thing-type.config.shelly.light.brightnessAutoOn.description = AN: Setzen einer Helligkeit > 0 schaltet das Gerät automatisch ein; AUS: Gerätestatus wird nicht ge#ndert
|
||||
thing-type.config.shelly.light.eventsButton.label = Button Events
|
||||
|
@ -129,6 +183,20 @@ thing-type.config.shelly.light.eventsCoIoT.description = Aktiviert CoIoT-Protoko
|
|||
thing-type.config.shelly.light.updateInterval.label = Status-Intervall
|
||||
thing-type.config.shelly.light.updateInterval.description = Intervall für die Hintergrundaktualisierung
|
||||
|
||||
# thing config - RGBW2
|
||||
thing-type.config.shelly.rgbw2.userId.label = Benutzer
|
||||
thing-type.config.shelly.rgbw2.userId.description = Benutzerkennung für API-Zugriff
|
||||
thing-type.config.shelly.rgbw2.password.label = Passwort
|
||||
thing-type.config.shelly.rgbw2.password.description = Passwort für API-Zugriff
|
||||
thing-type.config.shelly.rgbw2.deviceIp.label = IP Adresse
|
||||
thing-type.config.shelly.rgbw2.deviceIp.description = IP Adresse der Shelly-Komponente
|
||||
thing-type.config.shelly.rgbw2.brightnessAutoOn.label = Helligkeit Auto-EIN
|
||||
thing-type.config.shelly.rgbw2.brightnessAutoOn.description = AN: Setzen einer Helligkeit > 0 schaltet das Gerät automatisch ein; AUS: Gerätestatus wird nicht ge#ndert
|
||||
thing-type.config.shelly.rgbw2.eventsCoIoT.label = CoIoT aktivieren
|
||||
thing-type.config.shelly.rgbw2.eventsCoIoT.description = Aktiviert CoIoT-Protokoll (CoAP-basiert)
|
||||
thing-type.config.shelly.rgbw2.updateInterval.label = Status-Intervall
|
||||
thing-type.config.shelly.rgbw2.updateInterval.description = Intervall für die Hintergrundaktualisierung
|
||||
|
||||
# thing config - battery
|
||||
thing-type.config.shelly.battery.userId.label = Benutzer
|
||||
thing-type.config.shelly.battery.userId.description = Benutzerkennung für API-Zugriff
|
||||
|
@ -140,8 +208,6 @@ thing-type.config.shelly.battery.eventsSensorReport.label = Sensor Events
|
|||
thing-type.config.shelly.battery.eventsSensorReport.description = Aktiviert die Sensor Action URLS
|
||||
thing-type.config.shelly.battery.eventsCoIoT.label = CoIoT aktivieren
|
||||
thing-type.config.shelly.battery.eventsCoIoT.description = Aktiviert CoIoT-Protokoll (CoAP-basiert)
|
||||
thing-type.config.shelly.battery.weakSignal.label = Schwaches Signal (dBm)
|
||||
thing-type.config.shelly.battery.weakSignal.description = Ein Alarm wird ausgelöst, wenn das WiFi-Signal diesen Wert unterschreitet. Voreinstellung: -80 dBm
|
||||
thing-type.config.shelly.battery.lowBattery.label = Batterieladung niedrig (%)
|
||||
thing-type.config.shelly.battery.lowBattery.description = Ein Alarm wird ausgelöst, wenn das Gerät eine Batterieladung kleiner diesem Schwellwert meldet. Default: 20%
|
||||
thing-type.config.shelly.battery.updateInterval.label = Status-Intervall
|
||||
|
@ -170,6 +236,14 @@ thing-type.shelly.shelly1pm.group.sensors.label = Externe Sensoren
|
|||
thing-type.shelly.shelly1pm.group.sensors.description = Werte der externen Sensoren (nur wenn angeschlossen)
|
||||
thing-type.shelly.shelly1pm.group.device.label = Gerätestatus
|
||||
thing-type.shelly.shelly1pm.group.device.description = Informationen zum Gerätestatus
|
||||
thing-type.shelly.shelly1l.group.relay.label = Relais
|
||||
thing-type.shelly.shelly1l.group.relay.description = Relais Ein-/Ausgänge und Status
|
||||
thing-type.shelly.shelly1l.group.meter.label = Verbrauch
|
||||
thing-type.shelly.shelly1l.group.meter.description = Verbrauchswerte und andere Informationen
|
||||
thing-type.shelly.shelly1l.group.sensors.label = Externe Sensoren
|
||||
thing-type.shelly.shelly1l.group.sensors.description = Temperaturwerte der externen Sensoren (nur wenn angeschlossen)
|
||||
thing-type.shelly.shelly1l.group.device.label = Gerätestatus
|
||||
thing-type.shelly.shelly1l.group.device.description = Informationen zum Gerätestatus
|
||||
thing-type.shelly.shellyem.group.relay.label = Relais
|
||||
thing-type.shelly.shellyem.group.relay.description = Relais Ein-/Ausgänge und Status
|
||||
thing-type.shelly.shellyem.group.meter1.label = Verbrauch 1
|
||||
|
@ -248,6 +322,20 @@ thing-type.shelly.shellyplugs.group.meter.label = Verbrauch
|
|||
thing-type.shelly.shellyplugs.group.meter.description = Verbrauchswerte und andere Informationen
|
||||
thing-type.shelly.shellyplugs.group.device.label = Gerätestatus
|
||||
thing-type.shelly.shellyplugs.group.device.description = Informationen zum Gerätestatus
|
||||
thing-type.shelly.shellyplugu1.group.relay.label = Relais
|
||||
thing-type.shelly.shellyplugu1.group.relay.description = Relais Ein-/Ausgänge und Status
|
||||
thing-type.shelly.shellyplugu1.group.meter.label = Verbrauch
|
||||
thing-type.shelly.shellyplugu1.group.meter.description = Verbrauchswerte und andere Informationen
|
||||
thing-type.shelly.shellyplugu1.group.device.label = Gerätestatus
|
||||
thing-type.shelly.shellyplugu1.group.device.description = Informationen zum Gerätestatus
|
||||
thing-type.shelly.shellyuni.group.relay1.label = Relais 1
|
||||
thing-type.shelly.shellyuni.group.relay1.description = Relais Ein-/Ausgänge und Status
|
||||
thing-type.shelly.shellyuni.group.relay2.label = Relais 2
|
||||
thing-type.shelly.shellyuni.group.relay2.description = Relais Ein-/Ausgänge und Status
|
||||
thing-type.shelly.shellyuni.group.sensors.label = Sensordaten
|
||||
thing-type.shelly.shellyuni.group.sensors.description = Daten der angeschlossenen Sensoren
|
||||
thing-type.shelly.shellyuni.group.device.label = Gerätestatus
|
||||
thing-type.shelly.shellyuni.group.device.description = Informationen zum Gerätestatus
|
||||
thing-type.shelly.shellydimmer.group.relay.label = Relais
|
||||
thing-type.shelly.shellydimmer.group.relay.description = Relais Ein-/Ausgänge und Status
|
||||
thing-type.shelly.shellydimmer.group.meter.label = Verbrauch
|
||||
|
@ -260,11 +348,11 @@ thing-type.shelly.shellydimmer2.group.meter.label = Verbrauch
|
|||
thing-type.shelly.shellydimmer2.group.meter.description = Verbrauchswerte und andere Informationen
|
||||
thing-type.shelly.shellydimmer2.group.device.label = Gerätestatus
|
||||
thing-type.shelly.shellydimmer2.group.device.description = Informationen zum Gerätestatus
|
||||
thing-type.shelly.shellyix3.group.status1.label = Eingang #1
|
||||
thing-type.shelly.shellyix3.group.status1.label = Eingang 1
|
||||
thing-type.shelly.shellyix3.group.status1.description = Status Informationen zum Eingang 1
|
||||
thing-type.shelly.shellyix3.group.status2.label = Eingang #2
|
||||
thing-type.shelly.shellyix3.group.status2.label = Eingang 2
|
||||
thing-type.shelly.shellyix3.group.status2.description = Status Informationen zum Eingang 2
|
||||
thing-type.shelly.shellyix3.group.status3.label = Eingang #3
|
||||
thing-type.shelly.shellyix3.group.status3.label = Eingang 3
|
||||
thing-type.shelly.shellyix3.group.status3.description = Status Informationen zum Eingang 3
|
||||
thing-type.shelly.shellyix3.group.device.label = Gerätestatus
|
||||
thing-type.shelly.shellyix3.group.device.description = Informationen zum Gerätestatus
|
||||
|
@ -290,10 +378,18 @@ thing-type.shelly.shellybulbduo.group.meter.label = Verbrauch
|
|||
thing-type.shelly.shellybulbduo.group.meter.description = Verbrauchswerte
|
||||
thing-type.shelly.shellybulbduo.group.device.label = Gerätestatus
|
||||
thing-type.shelly.shellybulbduo.group.device.description = Informationen zum Gerätestatus
|
||||
thing-type.shelly.shellycolorbulb.group.control.label = Steuerung
|
||||
thing-type.shelly.shellycolorbulb.group.control.description = Steuerung des Lichts
|
||||
thing-type.shelly.shellycolorbulb.group.white.label = Weißwerte
|
||||
thing-type.shelly.shellycolorbulb.group.white.description = Einstellungen für den Weiß-Modus
|
||||
thing-type.shelly.shellycolorbulb.group.meter.label = Verbrauch
|
||||
thing-type.shelly.shellycolorbulb.group.meter.description = Verbrauchswerte
|
||||
thing-type.shelly.shellycolorbulb.group.device.label = Gerätestatus
|
||||
thing-type.shelly.shellycolorbulb.group.device.description = Informationen zum Gerätestatus
|
||||
thing-type.shelly.shellyvintage.group.control.label = Steuerung
|
||||
thing-type.shelly.shellyvintage.group.control.description = Steuerung des Lichts
|
||||
thing-type.shelly.shellyvintage.group.white.label = Weißwerte
|
||||
thing-type.shelly.shellyvintage.group.white.description = Einstellungen für den Weiß-Modus
|
||||
thing-type.shelly.shellyvintage.group.white.label = Steuerung
|
||||
thing-type.shelly.shellyvintage.group.white.description = Geräteeinstellungen
|
||||
thing-type.shelly.shellyvintage.group.meter.label = Verbrauch
|
||||
thing-type.shelly.shellyvintage.group.meter.description = Verbrauchswerte
|
||||
thing-type.shelly.shellyvintage.group.device.label = Gerätestatus
|
||||
|
@ -358,6 +454,12 @@ thing-type.shelly.shellygas.group.sensors.label = Sensordaten
|
|||
thing-type.shelly.shellygas.group.sensors.description = Messwerte und Status des Sensors
|
||||
thing-type.shelly.shellygas.group.device.label = Gerätestatus
|
||||
thing-type.shelly.shellygas.group.device.description = Informationen zum Gerätestatus
|
||||
thing-type.shelly.shellymotion.group.sensors.label = Sensordaten
|
||||
thing-type.shelly.shellymotion.group.sensors.description = Messwerte und Status des Sensors
|
||||
thing-type.shelly.shellymotion.group.battery.label = Batteriestatus
|
||||
thing-type.shelly.shellymotion.group.battery.description = Informationen zum Akku
|
||||
thing-type.shelly.shellymotion.group.device.label = Gerätestatus
|
||||
thing-type.shelly.shellymotion.group.device.description = Informationen zum Gerätestatus
|
||||
|
||||
|
||||
# channels
|
||||
|
@ -370,17 +472,19 @@ channel-type.shelly.timerAutoOff.description = Wenn das Relais eingeschaltet wir
|
|||
channel-type.shelly.timerActive.label = Timer aktiv
|
||||
channel-type.shelly.timerActive.description = ON: Auto-On/Off Timer ist aktiv
|
||||
channel-type.shelly.temperature1.label = Temperatur 1
|
||||
channel-type.shelly.temperature1.description = Temperatur des externen Sensors #1
|
||||
channel-type.shelly.temperature1.description = Temperatur des externen Sensors 1
|
||||
channel-type.shelly.temperature2.label = Temperatur 2
|
||||
channel-type.shelly.temperature2.description = Temperatur des externen Sensors #2
|
||||
channel-type.shelly.temperature2.description = Temperatur des externen Sensors 2
|
||||
channel-type.shelly.temperature3.label = Temperatur 3
|
||||
channel-type.shelly.temperature3.description = Temperatur des externen Sensors #3
|
||||
channel-type.shelly.temperature3.description = Temperatur des externen Sensors 3
|
||||
channel-type.shelly.humidity.label = Luftfeuchtigkeit
|
||||
channel-type.shelly.humidity.description = Relative Luftfeuchtigkeit (0..100%)
|
||||
channel-type.shelly.rollerShutter.label = Steuerung (0=offen, 100=geschlossen)
|
||||
channel-type.shelly.rollerShutter.description = Steuerung für den Rollladen: UP, DOWN, STOP, Position (0=offen, 100=geschlossen)
|
||||
channel-type.shelly.rollerPosition.label = Position (100=offen, 0=zu)
|
||||
channel-type.shelly.rollerPosition.description = Invertierte Position des Rollladen: 100=offen, 0=zu
|
||||
channel-type.shelly.rollerFavorite.label = Positionsfavorit
|
||||
channel-type.shelly.rollerFavorite.description = Wählt den Positionsfavoriten 1-4, Positionen werden in der Shelly App konfiguriert; 0=undefiniert (kein Favorit gewählt)
|
||||
channel-type.shelly.rollerState.label = Status
|
||||
channel-type.shelly.rollerState.description = Zustand des Rollladen (open/closed/stopped).
|
||||
channel-type.shelly.rollerState.state.option.open = öffnet
|
||||
|
@ -402,12 +506,12 @@ channel-type.shelly.whiteBrightness.label = Helligkeit
|
|||
channel-type.shelly.whiteBrightness.description = Helligkeit (0-100%, 0=aus)
|
||||
channel-type.shelly.meterWatts.label = Leistung
|
||||
channel-type.shelly.meterWatts.description = Aktueller Stromverbrauch in Watt
|
||||
channel-type.shelly.meterAccuWatts.label = Kumulierter Verbrauch
|
||||
channel-type.shelly.meterAccuWatts.description = Kumulierter Verbrauch in Watt
|
||||
channel-type.shelly.meterAccuWatts.label = Kumulierte Verbrauch
|
||||
channel-type.shelly.meterAccuWatts.description = Kumulierterr Verbrauch in Watt
|
||||
channel-type.shelly.meterAccuTotal.label = Kumulierter Gesamtverbrauch
|
||||
channel-type.shelly.meterAccuTotal.description = Kumulierter Gesamtverbrauch in kW/h
|
||||
channel-type.shelly.meterAccuReturned.label = Kumulierter Einspeisung
|
||||
channel-type.shelly.meterAccuReturned.description = Kumulierter Einspeisung in kW/h
|
||||
channel-type.shelly.meterAccuReturned.label = Kumulierte Einspeisung
|
||||
channel-type.shelly.meterAccuReturned.description = Kumulierte Einspeisung in kW/h
|
||||
channel-type.shelly.meterCurrent.label = Stromstärke
|
||||
channel-type.shelly.meterCurrent.description = Aktuelle gemessene Stromstärke
|
||||
channel-type.shelly.meterTotal.label = Gesamtverbrauch
|
||||
|
@ -487,10 +591,16 @@ channel-type.shelly.sensorIllumination.state.option.unknown = Unbekannt
|
|||
channel-type.shelly.sensorIllumination.description = Angabe zum erkannten Tageslichtwert
|
||||
channel-type.shelly.sensorPPM.label = Gas-Konzentration
|
||||
channel-type.shelly.sensorPPM.description = Gemessene Konzentration in PPM
|
||||
channel-type.shelly.sensorADC.label = Spannung (ADC)
|
||||
channel-type.shelly.sensorADC.description = Gemessene Spannung
|
||||
channel-type.shelly.sensorTilt.label = Öffnungswinkel
|
||||
channel-type.shelly.sensorTilt.description = Öffnungswinkel in Grad (erfordert Kalibrierung in der App)
|
||||
channel-type.shelly.sensorVibration.label = Vibration
|
||||
channel-type.shelly.sensorVibration.description = ON: Sensor hat eine Vibration erkannt
|
||||
channel-type.shelly.sensorMotion.label = Bewegung
|
||||
channel-type.shelly.sensorMotion.description = ON: Es wurde eine Bewegung erkannt
|
||||
channel-type.shelly.motionTimestamp.label = Letzte Bewegung
|
||||
channel-type.shelly.motionTimestamp.description = Datum/Uhrzeit, wann die letzte Bewegung erkannt wurde.
|
||||
channel-type.shelly.sensorValve.label = Ventil
|
||||
channel-type.shelly.sensorValve.description = Gibt den Status des Ventils an, sofern eines angeschlossen ist.
|
||||
channel-type.shelly.sensorValve.state.option.closed = geschlossen
|
||||
|
|
|
@ -69,14 +69,14 @@
|
|||
<item-type>Number:ElectricPotential</item-type>
|
||||
<label>Battery Voltage</label>
|
||||
<description>Battery voltage in V</description>
|
||||
<state readOnly="true" pattern="%f %unit%">
|
||||
<state readOnly="true" pattern="%.1f %unit%">
|
||||
</state>
|
||||
</channel-type>
|
||||
<channel-type id="uptime" advanced="true">
|
||||
<item-type>Number:Time</item-type>
|
||||
<label>Uptime</label>
|
||||
<description>Number of seconds since the device was powered up</description>
|
||||
<state readOnly="true" pattern="%d %unit%">
|
||||
<state readOnly="true" pattern="%.0f %unit%">
|
||||
</state>
|
||||
</channel-type>
|
||||
<channel-type id="heartBeat" advanced="true">
|
||||
|
@ -102,7 +102,7 @@
|
|||
<tags>
|
||||
<tag>CurrentTemperature</tag>
|
||||
</tags>
|
||||
<state readOnly="true" pattern="%f %unit%">
|
||||
<state readOnly="true" pattern="%.0f %unit%">
|
||||
</state>
|
||||
</channel-type>
|
||||
<channel-type id="selfTest">
|
||||
|
|
|
@ -32,6 +32,21 @@
|
|||
<config-description-ref uri="thing-type:shelly:light"/>
|
||||
</thing-type>
|
||||
|
||||
<thing-type id="shellycolorbulb">
|
||||
<label>Shelly Duo Color Bulb (SHSCB-1)</label>
|
||||
<description>Shelly Duo Color Bulb in Color or White Mode</description>
|
||||
<channel-groups>
|
||||
<channel-group id="control" typeId="duoControl"/>
|
||||
<channel-group id="color" typeId="colorSettingsBulb"/>
|
||||
<channel-group id="white" typeId="whiteSettings"/>
|
||||
<channel-group id="meter" typeId="meter"/>
|
||||
<channel-group id="device" typeId="deviceStatus"/>
|
||||
</channel-groups>
|
||||
|
||||
<representation-property>deviceName</representation-property>
|
||||
<config-description-ref uri="thing-type:shelly:light"/>
|
||||
</thing-type>
|
||||
|
||||
<thing-type id="shellyvintage">
|
||||
<label>Shelly Vintage (SHVIN-1)</label>
|
||||
<description>Shelly Vintage Light Bulb</description>
|
||||
|
@ -109,12 +124,6 @@
|
|||
<channel-group-type id="rgbw2ColorControl">
|
||||
<label>Light Control</label>
|
||||
<description>Control your light channels</description>
|
||||
<channels>
|
||||
<channel id="power" typeId="system.power"/>
|
||||
<channel id="autoOn" typeId="timerAutoOn"/>
|
||||
<channel id="autoOff" typeId="timerAutoOff"/>
|
||||
<channel id="timerActive" typeId="timerActive"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
<channel-group-type id="rgbw2WhiteControl">
|
||||
<label>White Control</label>
|
||||
|
@ -211,7 +220,7 @@
|
|||
<description>blue, 0..255, only in Color Mode</description>
|
||||
<state min="0" max="255" step="1" readOnly="false"></state>
|
||||
</channel-type>
|
||||
<channel-type id="colorWhite" advanced="true">
|
||||
<channel-type id="colorWhite">
|
||||
<item-type>Dimmer</item-type>
|
||||
<label>White</label>
|
||||
<description>white, 0..255, applies in Color Mode</description>
|
||||
|
@ -234,6 +243,7 @@
|
|||
<label>Brightness</label>
|
||||
<description>Brightness: 0..100%</description>
|
||||
<category>DimmableLight</category>
|
||||
<state min="0" max="100" step="1" readOnly="false"></state>
|
||||
</channel-type>
|
||||
<channel-type id="colorEffectBulb">
|
||||
<item-type>Number</item-type>
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
<thing-type id="shelly1">
|
||||
<label>Shelly1 (SHSW-1)</label>
|
||||
<description>Shelly1 device with single relay</description>
|
||||
<description>Shelly1 device with a single relay</description>
|
||||
<channel-groups>
|
||||
<channel-group id="relay" typeId="relayChannel"/>
|
||||
<channel-group id="sensors" typeId="externalSensors"/>
|
||||
|
@ -17,6 +17,20 @@
|
|||
<config-description-ref uri="thing-type:shelly:relay"/>
|
||||
</thing-type>
|
||||
|
||||
<thing-type id="shelly1l">
|
||||
<label>Shelly 1L (SHSW-L)</label>
|
||||
<description>Shelly 1L device with a single relay</description>
|
||||
<channel-groups>
|
||||
<channel-group id="relay" typeId="relayChannel"/>
|
||||
<channel-group id="meter" typeId="meter"/>
|
||||
<channel-group id="sensors" typeId="externalSensors"/>
|
||||
<channel-group id="device" typeId="deviceStatus"/>
|
||||
</channel-groups>
|
||||
|
||||
<representation-property>deviceName</representation-property>
|
||||
<config-description-ref uri="thing-type:shelly:relay"/>
|
||||
</thing-type>
|
||||
|
||||
<thing-type id="shelly1pm">
|
||||
<label>Shelly1PM (SHSW-PM)</label>
|
||||
<description>Shelly1PM device with single relay and power meter</description>
|
||||
|
@ -218,6 +232,25 @@
|
|||
<config-description-ref uri="thing-type:shelly:relay"/>
|
||||
</thing-type>
|
||||
|
||||
<thing-type id="shellyuni">
|
||||
<label>Shelly UNI (SHUNI-2)</label>
|
||||
<description>Embedded Shelly device</description>
|
||||
|
||||
<channel-groups>
|
||||
<channel-group id="relay1" typeId="relayChannel">
|
||||
<label>Relay 1</label>
|
||||
</channel-group>
|
||||
<channel-group id="relay2" typeId="relayChannel">
|
||||
<label>Relay 2</label>
|
||||
</channel-group>
|
||||
<channel-group id="sensors" typeId="sensorData"/>
|
||||
<channel-group id="device" typeId="deviceStatus"/>
|
||||
</channel-groups>
|
||||
|
||||
<representation-property>deviceName</representation-property>
|
||||
<config-description-ref uri="thing-type:shelly:relay"/>
|
||||
</thing-type>
|
||||
|
||||
<thing-type id="shellydimmer">
|
||||
<label>Shelly Dimmer (SHDM-1)</label>
|
||||
<description>Shelly Dimmer</description>
|
||||
|
@ -260,62 +293,38 @@
|
|||
<config-description-ref uri="thing-type:shelly:relay"/>
|
||||
</thing-type>
|
||||
|
||||
<thing-type id="shellyuni">
|
||||
<label>Shelly UNI (SHUNI-1)</label>
|
||||
<description>Shelly UNI device</description>
|
||||
<channel-groups>
|
||||
<channel-group id="relay1" typeId="relayChannel"/>
|
||||
<channel-group id="relay2" typeId="relayChannel"/>
|
||||
<channel-group id="device" typeId="deviceStatus"/>
|
||||
</channel-groups>
|
||||
|
||||
<representation-property>deviceName</representation-property>
|
||||
<config-description-ref uri="thing-type:shelly:relay"/>
|
||||
</thing-type>
|
||||
|
||||
<channel-group-type id="relayChannel">
|
||||
<label>Relay</label>
|
||||
<description>A Shelly relay channel</description>
|
||||
<channels>
|
||||
<channel id="output" typeId="system.power"/>
|
||||
<channel id="input" typeId="inputState"/>
|
||||
<channel id="button" typeId="system.button"/>
|
||||
<channel id="autoOn" typeId="timerAutoOn"/>
|
||||
<channel id="autoOff" typeId="timerAutoOff"/>
|
||||
<channel id="timerActive" typeId="timerActive"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
<channel-group-type id="relayChannelPlug">
|
||||
<label>Relay</label>
|
||||
<description>A Shelly relay channel</description>
|
||||
<channels>
|
||||
<channel id="output" typeId="system.power"/>
|
||||
<channel id="autoOn" typeId="timerAutoOn"/>
|
||||
<channel id="autoOff" typeId="timerAutoOff"/>
|
||||
<channel id="timerActive" typeId="timerActive"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
<channel-group-type id="dimmerChannel">
|
||||
<label>Dimmer</label>
|
||||
<description>A Shelly Dimmer channel</description>
|
||||
<channels>
|
||||
<channel id="brightness" typeId="dimmerBrightness"/>
|
||||
<channel id="input1" typeId="inputState1"/>
|
||||
<channel id="input2" typeId="inputState2"/>
|
||||
<channel id="button" typeId="system.button"/>
|
||||
<channel id="autoOn" typeId="timerAutoOn"/>
|
||||
<channel id="autoOff" typeId="timerAutoOff"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
<channel-group-type id="ix3Channel">
|
||||
<label>Input</label>
|
||||
<description>Input Status</description>
|
||||
<channels>
|
||||
<channel id="input" typeId="inputState"/>
|
||||
<channel id="button" typeId="system.button"/>
|
||||
<channel id="lastEvent" typeId="lastEvent"/>
|
||||
<channel id="eventCount" typeId="eventCount"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
<channel-group-type id="rollerControl">
|
||||
<label>Roller Control</label>
|
||||
<description>Controlling the roller mode</description>
|
||||
<channels>
|
||||
<channel id="control" typeId="rollerShutter"/>
|
||||
<channel id="rollerpos" typeId="rollerPosition"/>
|
||||
<channel id="state" typeId="rollerState"/>
|
||||
<channel id="stopReason" typeId="rollerStop"/>
|
||||
<channel id="input1" typeId="inputState1"/>
|
||||
<channel id="input2" typeId="inputState2"/>
|
||||
<channel id="event" typeId="eventTrigger"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
<channel-group-type id="meter">
|
||||
<label>Power Meter</label>
|
||||
|
@ -337,13 +346,13 @@
|
|||
<item-type>Number:Time</item-type>
|
||||
<label>Auto-ON Timer</label>
|
||||
<description>ON: After the output was turned off it turns on automatically after xx seconds; 0 disables the timer</description>
|
||||
<state min="0" step="1" pattern="%d %unit%" readOnly="false"></state>
|
||||
<state min="0" step="1" pattern="%.0f %unit%" readOnly="false"></state>
|
||||
</channel-type>
|
||||
<channel-type id="timerAutoOff" advanced="true">
|
||||
<item-type>Number:Time</item-type>
|
||||
<label>Auto-OFF Timer</label>
|
||||
<description>ON: After the output was turned on it turns off automatically after xx seconds; 0 disables the timer</description>
|
||||
<state min="0" step="1" pattern="%d %unit%" readOnly="false"></state>
|
||||
<state min="0" step="1" pattern="%.0f %unit%" readOnly="false"></state>
|
||||
</channel-type>
|
||||
<channel-type id="timerActive" advanced="true">
|
||||
<item-type>Switch</item-type>
|
||||
|
@ -362,7 +371,13 @@
|
|||
<item-type>Dimmer</item-type>
|
||||
<label>Roller Position (100=open, 0=closed)</label>
|
||||
<description>Position the roller (100..0 in %, where 100%=open, 0%=closed)</description>
|
||||
<state readOnly="false" min="0" max="100"/>
|
||||
<state readOnly="false" min="0" max="100" pattern="%.0f %%"/>
|
||||
</channel-type>
|
||||
<channel-type id="rollerFavorite">
|
||||
<item-type>Number</item-type>
|
||||
<label>Position Favorite</label>
|
||||
<description>Set roller position by selecting favorite 1-4 (needs to be defined in the Shelly App, 0=n/a)</description>
|
||||
<state readOnly="false" min="0" max="4"/>
|
||||
</channel-type>
|
||||
<channel-type id="rollerState">
|
||||
<item-type>String</item-type>
|
||||
|
@ -400,6 +415,13 @@
|
|||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
<channel-type id="rollerSafety" advanced="true">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Safety Switch</label>
|
||||
<description>Status of the safety switch</description>
|
||||
<state readOnly="true">
|
||||
</state>
|
||||
</channel-type>
|
||||
<channel-type id="inputState">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Input</label>
|
||||
|
@ -431,91 +453,77 @@
|
|||
<item-type>Number:Power</item-type>
|
||||
<label>Watt</label>
|
||||
<description>Current power consumption in Watt</description>
|
||||
<state readOnly="true" pattern="%f %unit%">
|
||||
<state readOnly="true" pattern="%.2f %unit%">
|
||||
</state>
|
||||
</channel-type>
|
||||
<channel-type id="meterAccuWatts" advanced="true">
|
||||
<item-type>Number:Power</item-type>
|
||||
<label>Accumulated Watt</label>
|
||||
<description>Accumulated current power consumption in Watt from all meters</description>
|
||||
<state readOnly="true" pattern="%f %unit%">
|
||||
<state readOnly="true" pattern="%.2f %unit%">
|
||||
</state>
|
||||
</channel-type>
|
||||
<channel-type id="meterAccuTotal" advanced="true">
|
||||
<item-type>Number:Power</item-type>
|
||||
<label>Accumulated Total</label>
|
||||
<description>Accumulated total power consumption in kw/h from all meters</description>
|
||||
<state readOnly="true" pattern="%f %unit%">
|
||||
<description>Accumulated total power consumption from all meters</description>
|
||||
<state readOnly="true" pattern="%.3f %unit%">
|
||||
</state>
|
||||
</channel-type>
|
||||
<channel-type id="meterAccuReturned" advanced="true">
|
||||
<item-type>Number:Power</item-type>
|
||||
<label>Accumulated Returned</label>
|
||||
<description>Accumulated returned power consumption in kw/h from all meters</description>
|
||||
<state readOnly="true" pattern="%f %unit%">
|
||||
<description>Accumulated returned power consumption from all meters</description>
|
||||
<state readOnly="true" pattern="%.3f %unit%">
|
||||
</state>
|
||||
</channel-type>
|
||||
<channel-type id="meterReactive">
|
||||
<item-type>Number:Power</item-type>
|
||||
<label>Reactive Watt</label>
|
||||
<description>Instantaneous reactive power in Watts (W)</description>
|
||||
<state readOnly="true" pattern="%f %unit%">
|
||||
<state readOnly="true" pattern="%.3f %unit%">
|
||||
</state>
|
||||
</channel-type>
|
||||
<channel-type id="lastPower1" advanced="true">
|
||||
<item-type>Number:Energy</item-type>
|
||||
<label>Last Power #1</label>
|
||||
<description>Last power consumption #1 - one rounded minute</description>
|
||||
<state readOnly="true" pattern="%f %unit%">
|
||||
</state>
|
||||
</channel-type>
|
||||
<channel-type id="lastPower2" advanced="true">
|
||||
<item-type>Number:Energy</item-type>
|
||||
<label>Last Power #2</label>
|
||||
<description>Last power consumption #2 - one rounded minute</description>
|
||||
<state readOnly="true" pattern="%f %unit%">
|
||||
</state>
|
||||
</channel-type>
|
||||
<channel-type id="lastPower3" advanced="true">
|
||||
<item-type>Number:Energy</item-type>
|
||||
<label>Last Power #3</label>
|
||||
<description>Last power consumption #3 - one rounded minute</description>
|
||||
<state readOnly="true" pattern="%f %unit%">
|
||||
<state readOnly="true" pattern="%.3f %unit%">
|
||||
</state>
|
||||
</channel-type>
|
||||
<channel-type id="meterTotal">
|
||||
<item-type>Number:Energy</item-type>
|
||||
<label>Total Energy</label>
|
||||
<description>Total power consumption in kw/h</description>
|
||||
<state readOnly="true" pattern="%f %unit%">
|
||||
<description>Total power consumption</description>
|
||||
<state readOnly="true" pattern="%.3f %unit%">
|
||||
</state>
|
||||
</channel-type>
|
||||
<channel-type id="meterReturned">
|
||||
<item-type>Number:Energy</item-type>
|
||||
<label>Total Returned Energy (kw/h)</label>
|
||||
<description>Total returned energy in kw/h</description>
|
||||
<state readOnly="true" pattern="%f %unit%">
|
||||
<label>Total Returned Energy</label>
|
||||
<description>Total returned energy</description>
|
||||
<state readOnly="true" pattern="%.3f %unit%">
|
||||
</state>
|
||||
</channel-type>
|
||||
<channel-type id="meterVoltage">
|
||||
<item-type>Number:ElectricPotential</item-type>
|
||||
<label>Voltage</label>
|
||||
<description>RMS voltage, Volts </description>
|
||||
<state readOnly="true" pattern="%f %unit%">
|
||||
<state readOnly="true" pattern="%.3f %unit%">
|
||||
</state>
|
||||
</channel-type>
|
||||
<channel-type id="meterCurrent">
|
||||
<item-type>Number:ElectricPotential</item-type>
|
||||
<label>Current</label>
|
||||
<description>Current in A </description>
|
||||
<state readOnly="true" pattern="%f %unit%">
|
||||
<state readOnly="true" pattern="%.3f %unit%">
|
||||
</state>
|
||||
</channel-type>
|
||||
<channel-type id="meterPowerFactor">
|
||||
<item-type>Number</item-type>
|
||||
<label>Power Factor</label>
|
||||
<description></description>
|
||||
<state readOnly="true" pattern="%f %unit%">
|
||||
<state readOnly="true" pattern="%.3f %unit%">
|
||||
</state>
|
||||
</channel-type>
|
||||
<channel-type id="timestamp">
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<description>Shelly H&T Sensor</description>
|
||||
|
||||
<channel-groups>
|
||||
<channel-group id="sensors" typeId="htSensor"/>
|
||||
<channel-group id="sensors" typeId="sensorData"/>
|
||||
<channel-group id="battery" typeId="batteryStatus"/>
|
||||
<channel-group id="device" typeId="deviceStatus"/>
|
||||
</channel-groups>
|
||||
|
@ -23,7 +23,7 @@
|
|||
<description>Shelly Smoke Sensor (battery powered)</description>
|
||||
|
||||
<channel-groups>
|
||||
<channel-group id="sensors" typeId="smokeSensor"/>
|
||||
<channel-group id="sensors" typeId="sensorData"/>
|
||||
<channel-group id="battery" typeId="batteryStatus"/>
|
||||
<channel-group id="device" typeId="deviceStatus"/>
|
||||
</channel-groups>
|
||||
|
@ -37,7 +37,7 @@
|
|||
<description>Shelly Gas Sensor</description>
|
||||
|
||||
<channel-groups>
|
||||
<channel-group id="sensors" typeId="gasSensor"/>
|
||||
<channel-group id="sensors" typeId="sensorData"/>
|
||||
<channel-group id="device" typeId="deviceStatus"/>
|
||||
</channel-groups>
|
||||
|
||||
|
@ -49,7 +49,7 @@
|
|||
<label>Shelly Flood (SHWT-1)</label>
|
||||
<description>Shelly Flood Sensor (battery powered)</description>
|
||||
<channel-groups>
|
||||
<channel-group id="sensors" typeId="floodSensor"/>
|
||||
<channel-group id="sensors" typeId="sensorData"/>
|
||||
<channel-group id="battery" typeId="batteryStatus"/>
|
||||
<channel-group id="device" typeId="deviceStatus"/>
|
||||
</channel-groups>
|
||||
|
@ -63,7 +63,7 @@
|
|||
<description>Shelly Door/Window Sensor (battery powered)</description>
|
||||
|
||||
<channel-groups>
|
||||
<channel-group id="sensors" typeId="doorWinSensors"/>
|
||||
<channel-group id="sensors" typeId="sensorData"/>
|
||||
<channel-group id="battery" typeId="batteryStatus"/>
|
||||
<channel-group id="device" typeId="deviceStatus"/>
|
||||
</channel-groups>
|
||||
|
@ -77,7 +77,7 @@
|
|||
<description>Shelly Door/Window 2 Sensor (battery powered)</description>
|
||||
|
||||
<channel-groups>
|
||||
<channel-group id="sensors" typeId="doorWinSensors"/>
|
||||
<channel-group id="sensors" typeId="sensorData"/>
|
||||
<channel-group id="battery" typeId="batteryStatus"/>
|
||||
<channel-group id="device" typeId="deviceStatus"/>
|
||||
</channel-groups>
|
||||
|
@ -91,7 +91,7 @@
|
|||
<description>Shelly Sense Remote IR Controller</description>
|
||||
<channel-groups>
|
||||
<channel-group id="control" typeId="senseControl"/>
|
||||
<channel-group id="sensors" typeId="senseSensors"/>
|
||||
<channel-group id="sensors" typeId="sensorData"/>
|
||||
<channel-group id="battery" typeId="batteryStatus"/>
|
||||
<channel-group id="device" typeId="deviceStatus"/>
|
||||
</channel-groups>
|
||||
|
@ -114,33 +114,25 @@
|
|||
<config-description-ref uri="thing-type:shelly:battery"/>
|
||||
</thing-type>
|
||||
|
||||
<channel-group-type id="htSensor">
|
||||
<thing-type id="shellymotion">
|
||||
<label>Shelly Motion</label>
|
||||
<description>Shelly Motion Sensor (battery powered)</description>
|
||||
|
||||
<channel-groups>
|
||||
<channel-group id="sensors" typeId="sensorData"/>
|
||||
<channel-group id="battery" typeId="batteryStatus"/>
|
||||
<channel-group id="device" typeId="deviceStatus"/>
|
||||
</channel-groups>
|
||||
|
||||
<representation-property>deviceName</representation-property>
|
||||
<config-description-ref uri="thing-type:shelly:battery"/>
|
||||
</thing-type>
|
||||
|
||||
<channel-group-type id="sensorData">
|
||||
<label>Sensor Data</label>
|
||||
<description>Data from the HT Sensor</description>
|
||||
<description>Data from the various sensors</description>
|
||||
</channel-group-type>
|
||||
|
||||
<channel-group-type id="floodSensor">
|
||||
<label>Sensor Data</label>
|
||||
<description>Data from the Flood Sensor</description>
|
||||
</channel-group-type>
|
||||
|
||||
<channel-group-type id="smokeSensor">
|
||||
<label>Sensor Data</label>
|
||||
<description>Data from the Flood Sensor</description>
|
||||
</channel-group-type>
|
||||
<channel-group-type id="gasSensor">
|
||||
<label>Sensor Data</label>
|
||||
<description>Data from the Gas Sensor</description>
|
||||
</channel-group-type>
|
||||
<channel-group-type id="doorWinSensors">
|
||||
<label>Sensors</label>
|
||||
<description>Data from the sensors</description>
|
||||
</channel-group-type>
|
||||
|
||||
<channel-group-type id="senseSensors">
|
||||
<label>Sensors</label>
|
||||
<description>Data from the Sense sensors</description>
|
||||
</channel-group-type>
|
||||
<channel-group-type id="batteryStatus">
|
||||
<label>Battery Status</label>
|
||||
</channel-group-type>
|
||||
|
@ -154,12 +146,6 @@
|
|||
<channel-group-type id="buttonState">
|
||||
<label>Button State</label>
|
||||
<description>Status of the Button</description>
|
||||
<channels>
|
||||
<channel id="input" typeId="inputState"/>
|
||||
<channel id="button" typeId="system.button"/>
|
||||
<channel id="lastEvent" typeId="lastEvent"/>
|
||||
<channel id="eventCount" typeId="eventCount"/>
|
||||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
|
||||
|
@ -221,7 +207,7 @@
|
|||
<item-type>Number</item-type>
|
||||
<label>Event Count</label>
|
||||
<description>Event Count</description>
|
||||
<state pattern="%d" readOnly="true">
|
||||
<state pattern="%.0f" readOnly="true">
|
||||
</state>
|
||||
</channel-type>
|
||||
|
||||
|
@ -233,7 +219,7 @@
|
|||
<tags>
|
||||
<tag>CurrentTemperature</tag>
|
||||
</tags>
|
||||
<state readOnly="true" pattern="%f %unit%">
|
||||
<state readOnly="true" pattern="%.1f %unit%">
|
||||
</state>
|
||||
</channel-type>
|
||||
<channel-type id="sensorExtTemp">
|
||||
|
@ -244,7 +230,7 @@
|
|||
<tags>
|
||||
<tag>CurrentTemperature</tag>
|
||||
</tags>
|
||||
<state readOnly="true" pattern="%f %unit%">
|
||||
<state readOnly="true" pattern="%.1f %unit%">
|
||||
</state>
|
||||
</channel-type>
|
||||
<channel-type id="sensorExtHum">
|
||||
|
@ -255,7 +241,7 @@
|
|||
<tags>
|
||||
<tag>CurrentHumidity</tag>
|
||||
</tags>
|
||||
<state readOnly="true" pattern="%f %unit%">
|
||||
<state readOnly="true" pattern="%.1f %unit%">
|
||||
</state>
|
||||
</channel-type>
|
||||
<channel-type id="sensorHumidity">
|
||||
|
@ -265,7 +251,7 @@
|
|||
<tags>
|
||||
<tag>CurrentHumidity</tag>
|
||||
</tags>
|
||||
<state readOnly="true" min="0" max="100" pattern="%f %unit%"/>
|
||||
<state readOnly="true" pattern="%.1f %unit%"/>
|
||||
</channel-type>
|
||||
<channel-type id="sensorFlood">
|
||||
<item-type>Switch</item-type>
|
||||
|
@ -285,7 +271,7 @@
|
|||
<item-type>Number:Illuminance</item-type>
|
||||
<label>Lux</label>
|
||||
<description>Brightness from the sensor (Lux)</description>
|
||||
<state readOnly="true" pattern="%f %unit%">
|
||||
<state readOnly="true" pattern="%.0f %unit%">
|
||||
</state>
|
||||
</channel-type>
|
||||
<channel-type id="sensorIllumination">
|
||||
|
@ -300,6 +286,20 @@
|
|||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
<channel-type id="motionTimestamp">
|
||||
<item-type>DateTime</item-type>
|
||||
<label>Last motion</label>
|
||||
<description>Timestamp of last detected motion</description>
|
||||
<state readOnly="true">
|
||||
</state>
|
||||
</channel-type>
|
||||
<channel-type id="sensorMotion">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Motion</label>
|
||||
<description>ON: Motion detected</description>
|
||||
<state readOnly="true">
|
||||
</state>
|
||||
</channel-type>
|
||||
<channel-type id="sensorVibration">
|
||||
<item-type>Switch</item-type>
|
||||
<label>Vibration</label>
|
||||
|
@ -311,14 +311,21 @@
|
|||
<item-type>Number:Angle</item-type>
|
||||
<label>Tilt</label>
|
||||
<description>Tilt in degrees (requires calibration)</description>
|
||||
<state readOnly="true">
|
||||
<state readOnly="true" pattern="%.0f %unit%">
|
||||
</state>
|
||||
</channel-type>
|
||||
<channel-type id="sensorPPM">
|
||||
<item-type>Number:Density</item-type>
|
||||
<label>Concentration</label>
|
||||
<description>Gas concentration in ppm</description>
|
||||
<state readOnly="true" pattern="%d %unit%">
|
||||
<state readOnly="true" pattern="%.0f %unit%">
|
||||
</state>
|
||||
</channel-type>
|
||||
<channel-type id="sensorADC">
|
||||
<item-type>Number:ElectricPotential</item-type>
|
||||
<label>Voltage (ADC)</label>
|
||||
<description>ADC voltage in V</description>
|
||||
<state readOnly="true" pattern="%.0f %unit%">
|
||||
</state>
|
||||
</channel-type>
|
||||
<channel-type id="sensorValve">
|
||||
|
|
Loading…
Reference in New Issue