[shelly] Minor bugfixes from hardening (#13200)

* Minor bugfixes from hardening

Signed-off-by: Markus Michels <markus7017@gmail.com>

* review changes applied

Signed-off-by: Markus Michels <markus7017@gmail.com>

* controlProfile is type String

Signed-off-by: Markus Michels <markus7017@gmail.com>
This commit is contained in:
Markus Michels 2022-08-04 08:42:29 +02:00 committed by GitHub
parent 22f7dfe309
commit 0d73ed8150
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 302 additions and 252 deletions

View File

@ -906,12 +906,12 @@ You should calibrate the valve using the device Web UI or Shelly App before star
|control |targetTemp |Number |no |Temperature in °C: 4=Low/Min; 5..30=target temperature;31=Hi/Max | |control |targetTemp |Number |no |Temperature in °C: 4=Low/Min; 5..30=target temperature;31=Hi/Max |
| |position |Dimmer |no |Set valve to manual mode (0..100%) disables auto-temp) | | |position |Dimmer |no |Set valve to manual mode (0..100%) disables auto-temp) |
| |mode |String |no |Switch between manual and automatic mode | | |mode |String |no |Switch between manual and automatic mode |
| |profile |Number |no |Select profile: 0=disable, 1-n: profile index from Shelly Web App | | |selectedProfile|String |no |Select profile: Profile name or 0=disable, 1-n: profile index |
| |boost |Number |no |Enable/disable boost mode (full heating power) | | |boost |Number |no |Enable/disable boost mode (full heating power) |
| |boostTimer |Number |no |Number of minutes to heat at full power while boost mode is enabled | | |boostTimer |Number |no |Number of minutes to heat at full power while boost mode is enabled |
| |schedule |Switch |yes |ON: Schedule is active |
|battery |batteryLevel |Number |yes |Battery Level in % | |battery |batteryLevel |Number |yes |Battery Level in % |
| |batteryAlert |Switch |yes |Low battery alert | | |batteryAlert |Switch |yes |Low battery alert |
|device |schedule |Switch |yes |ON: Schedule is active |
### Shelly Button 1 or 2 (thing-type: shellybutton1 / shellybutton2) ### Shelly Button 1 or 2 (thing-type: shellybutton1 / shellybutton2)

View File

@ -134,12 +134,14 @@ public class ShellyBindingConstants {
public static final String CHANNEL_SENSOR_ALARM_STATE = "alarmState"; public static final String CHANNEL_SENSOR_ALARM_STATE = "alarmState";
public static final String CHANNEL_SENSOR_ERROR = "lastError"; public static final String CHANNEL_SENSOR_ERROR = "lastError";
public static final String CHANNEL_CONTROL_SETTEMP = "targetTemp"; // Shelly TRV: target temp // TRV
public static final String CHANNEL_CONTROL_POSITION = "position"; // Shelly TRV: Valve position public static final String CHANNEL_CONTROL_SETTEMP = "targetTemp";
public static final String CHANNEL_CONTROL_MODE = "mode"; // Shelly TRV public static final String CHANNEL_CONTROL_POSITION = "position";
public static final String CHANNEL_CONTROL_PROFILE = "profile"; // Shelly TRV public static final String CHANNEL_CONTROL_MODE = "mode";
public static final String CHANNEL_CONTROL_BCONTROL = "boost"; // Shelly TRV public static final String CHANNEL_CONTROL_PROFILE = "selectedProfile";
public static final String CHANNEL_CONTROL_BTIMER = "boostTimer"; // Shelly TRV public static final String CHANNEL_CONTROL_BCONTROL = "boost";
public static final String CHANNEL_CONTROL_BTIMER = "boostTimer";
public static final String CHANNEL_CONTROL_SCHEDULE = "schedule";
// External sensors for Shelly1/1PM // External sensors for Shelly1/1PM
public static final String CHANNEL_ESENDOR_TEMP1 = CHANNEL_SENSOR_TEMP + "1"; public static final String CHANNEL_ESENDOR_TEMP1 = CHANNEL_SENSOR_TEMP + "1";
@ -192,7 +194,6 @@ public class ShellyBindingConstants {
public static final String CHANNEL_DEVST_SELFTTEST = "selfTest"; public static final String CHANNEL_DEVST_SELFTTEST = "selfTest";
public static final String CHANNEL_DEVST_VOLTAGE = "supplyVoltage"; public static final String CHANNEL_DEVST_VOLTAGE = "supplyVoltage";
public static final String CHANNEL_DEVST_CALIBRATED = "calibrated"; public static final String CHANNEL_DEVST_CALIBRATED = "calibrated";
public static final String CHANNEL_DEVST_SCHEDULE = "schedule";
public static final String CHANNEL_LED_STATUS_DISABLE = "statusLed"; public static final String CHANNEL_LED_STATUS_DISABLE = "statusLed";
public static final String CHANNEL_LED_POWER_DISABLE = "powerLed"; public static final String CHANNEL_LED_POWER_DISABLE = "powerLed";

View File

@ -63,7 +63,6 @@ public class ShellyHandlerFactory extends BaseThingHandlerFactory {
private final HttpClient httpClient; private final HttpClient httpClient;
private final ShellyTranslationProvider messages; private final ShellyTranslationProvider messages;
private final Shelly1CoapServer coapServer; private final Shelly1CoapServer coapServer;
private final ShellyThingTable thingTable; private final ShellyThingTable thingTable;
private ShellyBindingConfiguration bindingConfig = new ShellyBindingConfiguration(); private ShellyBindingConfiguration bindingConfig = new ShellyBindingConfiguration();
private String localIP = ""; private String localIP = "";
@ -82,10 +81,10 @@ public class ShellyHandlerFactory extends BaseThingHandlerFactory {
@Reference HttpClientFactory httpClientFactory, ComponentContext componentContext, @Reference HttpClientFactory httpClientFactory, ComponentContext componentContext,
Map<String, Object> configProperties) { Map<String, Object> configProperties) {
super.activate(componentContext); super.activate(componentContext);
messages = translationProvider; this.messages = translationProvider;
// Save bindingConfig & pass it to all registered listeners this.thingTable = thingTable;
bindingConfig.updateFromProperties(configProperties);
bindingConfig.updateFromProperties(configProperties);
localIP = bindingConfig.localIP; localIP = bindingConfig.localIP;
if (localIP.isEmpty()) { if (localIP.isEmpty()) {
localIP = ShellyUtils.getString(networkAddressService.getPrimaryIpv4HostAddress()); localIP = ShellyUtils.getString(networkAddressService.getPrimaryIpv4HostAddress());
@ -101,7 +100,6 @@ public class ShellyHandlerFactory extends BaseThingHandlerFactory {
} }
logger.debug("Using OH HTTP port {}", httpPort); logger.debug("Using OH HTTP port {}", httpPort);
this.thingTable = thingTable;
this.coapServer = new Shelly1CoapServer(); this.coapServer = new Shelly1CoapServer();
} }
@ -124,7 +122,8 @@ public class ShellyHandlerFactory extends BaseThingHandlerFactory {
} else if (thingType.equals(THING_TYPE_SHELLYBULB_STR) || thingType.equals(THING_TYPE_SHELLYDUO_STR) } 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_COLOR_STR)
|| thingType.equals(THING_TYPE_SHELLYRGBW2_WHITE_STR) || thingType.equals(THING_TYPE_SHELLYRGBW2_WHITE_STR)
|| thingType.equals(THING_TYPE_SHELLYDUORGBW_STR)) { || thingType.equals(THING_TYPE_SHELLYRGBW2_WHITE_STR) || thingType.equals(THING_TYPE_SHELLYDUORGBW_STR)
|| thingType.equals(THING_TYPE_SHELLYVINTAGE_STR)) {
logger.debug("{}: Create new thing of type {} using ShellyLightHandler", thing.getLabel(), logger.debug("{}: Create new thing of type {} using ShellyLightHandler", thing.getLabel(),
thingTypeUID.toString()); thingTypeUID.toString());
handler = new ShellyLightHandler(thing, messages, bindingConfig, coapServer, localIP, httpPort, httpClient); handler = new ShellyLightHandler(thing, messages, bindingConfig, coapServer, localIP, httpPort, httpClient);

View File

@ -35,6 +35,8 @@ import org.openhab.binding.shelly.internal.config.ShellyThingConfiguration;
public interface ShellyApiInterface { public interface ShellyApiInterface {
public boolean isInitialized(); public boolean isInitialized();
public void initialize() throws ShellyApiException;
public void setConfig(String thingName, ShellyThingConfiguration config); public void setConfig(String thingName, ShellyThingConfiguration config);
public ShellySettingsDevice getDeviceInfo() throws ShellyApiException; public ShellySettingsDevice getDeviceInfo() throws ShellyApiException;

View File

@ -30,6 +30,7 @@ import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettings
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsRelay; import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsRelay;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsRgbwLight; import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsRgbwLight;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsStatus; import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsStatus;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyThermnostat;
import org.openhab.binding.shelly.internal.util.ShellyVersionDTO; import org.openhab.binding.shelly.internal.util.ShellyVersionDTO;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -60,6 +61,7 @@ public class ShellyDeviceProfile {
public String hostname = ""; public String hostname = "";
public String name = ""; public String name = "";
public String model = "";
public String mode = ""; public String mode = "";
public boolean discoverable = true; public boolean discoverable = true;
public boolean auth = false; public boolean auth = false;
@ -154,18 +156,6 @@ public class ShellyDeviceProfile {
numMeters = inColor ? 1 : getInteger(settings.device.numOutputs); numMeters = inColor ? 1 : getInteger(settings.device.numOutputs);
} }
if (settings.sleepMode != null) {
// 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 += 60; // give 1min extra
} else if ((settings.coiot != null) && settings.coiot.updatePeriod != null && !isTRV) {
// 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 = UPDATE_SETTINGS_INTERVAL_SECONDS + 10;
}
initialized = true; initialized = true;
return this; return this;
} }
@ -244,8 +234,11 @@ public class ShellyDeviceProfile {
return CHANNEL_GROUP_RELAY_CONTROL; return CHANNEL_GROUP_RELAY_CONTROL;
} else if (hasRelays) { } 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 (isRGBW2) {
return settings.lights == null || settings.lights.size() <= 1 ? CHANNEL_GROUP_LIGHT_CONTROL
: CHANNEL_GROUP_LIGHT_CHANNEL + idx;
} else if (isLight) { } else if (isLight) {
return numRelays <= 1 ? CHANNEL_GROUP_LIGHT_CONTROL : CHANNEL_GROUP_LIGHT_CONTROL + idx; return CHANNEL_GROUP_LIGHT_CONTROL;
} else if (isButton) { } else if (isButton) {
return CHANNEL_GROUP_STATUS; return CHANNEL_GROUP_STATUS;
} else if (isSensor) { } else if (isSensor) {
@ -337,6 +330,20 @@ public class ShellyDeviceProfile {
return -1; return -1;
} }
public String getValueProfile(int profileId) {
int id = profileId;
if (settings.thermostats != null) {
ShellyThermnostat t = settings.thermostats.get(0);
id = profileId == 0 ? getInteger(t.profile) : profileId;
if (id <= 0) {
return "DISABLED";
}
return id <= t.profileNames.length ? getString(t.profileNames[id - 1]) : "" + id;
}
return "" + id;
}
public static String extractFwVersion(@Nullable String version) { public static String extractFwVersion(@Nullable String version) {
if (version != null) { if (version != null) {
// fix version e.g. 20210319-122304/v.1.10-Dimmer1-gfd4cc10 (with v.1. instead of v1.) // fix version e.g. 20210319-122304/v.1.10-Dimmer1-gfd4cc10 (with v.1. instead of v1.)

View File

@ -15,6 +15,7 @@ package org.openhab.binding.shelly.internal.api1;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyStatusSensor.ShellyMotionSettings; import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyStatusSensor.ShellyMotionSettings;
import org.openhab.core.thing.CommonTriggerEvents; import org.openhab.core.thing.CommonTriggerEvents;
@ -265,8 +266,6 @@ public class Shelly1ApiJsonDTO {
public String fw; public String fw;
public Boolean auth; public Boolean auth;
public Integer gen; public Integer gen;
@SerializedName("coiot") // Shelly Motion Multicast Endpoint
public String coiot; public String coiot;
public Integer longid; public Integer longid;
@ -409,10 +408,6 @@ public class Shelly1ApiJsonDTO {
public Boolean overpower; public Boolean overpower;
@SerializedName("is_valid") @SerializedName("is_valid")
public Boolean isValid; public Boolean isValid;
@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 static class ShellySettingsDimmer { public static class ShellySettingsDimmer {
@ -566,22 +561,22 @@ public class Shelly1ApiJsonDTO {
public static class ShellySettingsGlobal { public static class ShellySettingsGlobal {
// https://shelly-api-docs.shelly.cloud/#shelly1pm-settings // https://shelly-api-docs.shelly.cloud/#shelly1pm-settings
public ShellySettingsDevice device; public ShellySettingsDevice device = new ShellySettingsDevice();
@SerializedName("wifi_ap") @SerializedName("wifi_ap")
public ShellySettingsWiFiAp wifiAp; public ShellySettingsWiFiAp wifiAp = new ShellySettingsWiFiAp();
@SerializedName("wifi_sta") @SerializedName("wifi_sta")
public ShellySettingsWiFiNetwork wifiSta; public ShellySettingsWiFiNetwork wifiSta = new ShellySettingsWiFiNetwork();
@SerializedName("wifi_sta1") @SerializedName("wifi_sta1")
public ShellySettingsWiFiNetwork wifiSta1; public ShellySettingsWiFiNetwork wifiSta1 = new ShellySettingsWiFiNetwork();
@SerializedName("wifirecovery_reboot_enabled") @SerializedName("wifirecovery_reboot_enabled")
public Boolean wifiRecoveryReboot; // FW 1.10+ public Boolean wifiRecoveryReboot; // FW 1.10+
@SerializedName("ap_roaming") @SerializedName("ap_roaming")
public ShellyApRoaming apRoaming; // FW 1.10+ public ShellyApRoaming apRoaming; // FW 1.10+
public ShellySettingsMqtt mqtt; // not used for now public ShellySettingsMqtt mqtt = new ShellySettingsMqtt();
public ShellySettingsSntp sntp; // not used for now public ShellySettingsSntp sntp = new ShellySettingsSntp();
public ShellySettingsCoiot coiot; // Firmware 1.6+ public ShellySettingsCoiot coiot = new ShellySettingsCoiot();
public ShellySettingsLogin login; public ShellySettingsLogin login = new ShellySettingsLogin();
@SerializedName("pin_code") @SerializedName("pin_code")
public String pinCode; public String pinCode;
@SerializedName("coiot_execute_enable") @SerializedName("coiot_execute_enable")
@ -590,8 +585,8 @@ public class Shelly1ApiJsonDTO {
public Boolean discoverable; // FW 1.6+ public Boolean discoverable; // FW 1.6+
public String fw; public String fw;
@SerializedName("build_info") @SerializedName("build_info")
public ShellySettingsBuildInfo buildInfo; public ShellySettingsBuildInfo buildInfo = new ShellySettingsBuildInfo();
public ShellyStatusCloud cloud; public ShellyStatusCloud cloud = new ShellyStatusCloud();
@SerializedName("sleep_mode") @SerializedName("sleep_mode")
public ShellySensorSleepMode sleepMode; // FW 1.6 public ShellySensorSleepMode sleepMode; // FW 1.6
@SerializedName("external_power") @SerializedName("external_power")
@ -610,19 +605,22 @@ public class Shelly1ApiJsonDTO {
@SerializedName("max_power") @SerializedName("max_power")
public Double maxPower; public Double maxPower;
public Boolean calibrated; public Boolean calibrated;
public ArrayList<ShellySettingsRelay> relays;
public ArrayList<ShellySettingsRoller> rollers;
public ArrayList<ShellySettingsDimmer> dimmers;
public ArrayList<ShellySettingsRgbwLight> lights;
public ArrayList<ShellySettingsEMeter> emeters;
public ArrayList<ShellySettingsInput> inputs; // ix3
public ArrayList<ShellyThermnostat> thermostats; // TRV
public Double voltage; // AC voltage for Shelly 2.5 public Double voltage; // AC voltage for Shelly 2.5
@SerializedName("supply_voltage") @SerializedName("supply_voltage")
public Long supplyVoltage; // Shelly 1PM/1L: 0=110V, 1=220V public Long supplyVoltage; // Shelly 1PM/1L: 0=110V, 1=220V
public @Nullable ArrayList<ShellySettingsRelay> relays;
public @Nullable ArrayList<ShellySettingsInput> inputs; // ix3
public @Nullable ArrayList<ShellySettingsDimmer> dimmers;
public @Nullable ArrayList<ShellySettingsRoller> rollers;
public @Nullable ArrayList<ShellySettingsRgbwLight> lights;
public @Nullable ArrayList<ShellySettingsEMeter> emeters;
public @Nullable ArrayList<ShellyThermnostat> thermostats; // TRV
@SerializedName("ext_temperature")
public ShellyStatusSensor.ShellyExtTemperature extTemperature; // Shelly 1/1PM: sensor values
@SerializedName("ext_humidity")
public ShellyStatusSensor.ShellyExtHumidity extHumidity; // Shelly 1/1PM: sensor values
@SerializedName("temperature_units") @SerializedName("temperature_units")
public String temperatureUnits = "C"; // Either'C'or'F' public String temperatureUnits = "C"; // Either'C'or'F'
@ -703,9 +701,9 @@ public class Shelly1ApiJsonDTO {
public String name; // FW 1.8: Symbolic Device name is configurable public String name; // FW 1.8: Symbolic Device name is configurable
@SerializedName("wifi_sta") @SerializedName("wifi_sta")
public ShellySettingsWiFiNetwork wifiSta; // WiFi client configuration. See /settings/sta for details public ShellySettingsWiFiNetwork wifiSta = new ShellySettingsWiFiNetwork();
public ShellyStatusCloud cloud; public ShellyStatusCloud cloud = new ShellyStatusCloud();
public ShellyStatusMqtt mqtt; public ShellyStatusMqtt mqtt = new ShellyStatusMqtt();
public String time; public String time;
public Integer serial = -1; public Integer serial = -1;
@ -717,21 +715,23 @@ public class Shelly1ApiJsonDTO {
public Integer cfgChangedCount; // FW 1.8 public Integer cfgChangedCount; // FW 1.8
@SerializedName("actions_stats") @SerializedName("actions_stats")
public ShellyActionsStats astats; public ShellyActionsStats astats;
public ArrayList<ShellySettingsRelay> relays;
public Double voltage; // Shelly 2.5 public Double voltage; // Shelly 2.5
public ArrayList<ShellySettingsRoller> rollers;
public Integer input; // RGBW2 has no JSON array public Integer input; // RGBW2 has no JSON array
public ArrayList<ShellyInputState> inputs; public ArrayList<ShellySettingsRelay> relays;
public ArrayList<ShellySettingsLight> lights; public ArrayList<ShellyRollerStatus> rollers;
public ArrayList<ShellyShortLightStatus> dimmers; public ArrayList<ShellyShortLightStatus> dimmers;
public ArrayList<ShellyInputState> inputs;
public ArrayList<ShellySettingsMeter> meters; public ArrayList<ShellySettingsMeter> meters;
public ArrayList<ShellySettingsEMeter> emeters; public ArrayList<ShellySettingsEMeter> emeters;
@SerializedName("ext_temperature")
public ShellyStatusSensor.ShellyExtTemperature extTemperature; // Shelly 1/1PM: sensor values
@SerializedName("ext_humidity")
public ShellyStatusSensor.ShellyExtHumidity extHumidity; // Shelly 1/1PM: sensor values
// Internal device temp // Internal device temp
public ShellySensorTmp tmp; // Shelly 1PM public ShellySensorTmp tmp = new ShellySensorTmp(); // Shelly 1PM
public Double temperature = SHELLY_API_INVTEMP; // Shelly 2.5 public Double temperature; // Shelly 2.5
public Boolean overtemperature; public Boolean overtemperature;
// Shelly Dimmer only // Shelly Dimmer only
@ -742,7 +742,8 @@ public class Shelly1ApiJsonDTO {
public Boolean calibrated; public Boolean calibrated;
public ArrayList<ShellyThermnostat> thermostats; public ArrayList<ShellyThermnostat> thermostats;
public ShellySettingsUpdate update; public ShellySettingsUpdate update = new ShellySettingsUpdate();
@SerializedName("ram_total") @SerializedName("ram_total")
public Long ramTotal; public Long ramTotal;
@SerializedName("ram_free") @SerializedName("ram_free")

View File

@ -106,8 +106,9 @@ public class Shelly1CoIoTVersion2 extends Shelly1CoIoTProtocol implements Shelly
break; break;
case "3117": // S, mode, 0-5 (0=disabled) case "3117": // S, mode, 0-5 (0=disabled)
value = getDouble(s.value).intValue(); value = getDouble(s.value).intValue();
updateChannel(updates, CHANNEL_GROUP_CONTROL, CHANNEL_CONTROL_PROFILE, getDecimal(value)); updateChannel(updates, CHANNEL_GROUP_CONTROL, CHANNEL_CONTROL_PROFILE,
updateChannel(updates, CHANNEL_GROUP_DEV_STATUS, CHANNEL_DEVST_SCHEDULE, getOnOff(value > 0)); getStringType(profile.getValueProfile((int) value)));
updateChannel(updates, CHANNEL_GROUP_CONTROL, CHANNEL_CONTROL_SCHEDULE, getOnOff(value > 0));
break; break;
case "3118": // Valve state case "3118": // Valve state
updateChannel(updates, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_STATE, updateChannel(updates, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_STATE,

View File

@ -37,6 +37,7 @@ import org.openhab.binding.shelly.internal.api.ShellyApiResult;
import org.openhab.binding.shelly.internal.api.ShellyDeviceProfile; import org.openhab.binding.shelly.internal.api.ShellyDeviceProfile;
import org.openhab.binding.shelly.internal.api.ShellyHttpClient; import org.openhab.binding.shelly.internal.api.ShellyHttpClient;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyOtaCheckResult; import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyOtaCheckResult;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyRollerStatus;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySendKeyList; import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySendKeyList;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySenseKeyCode; import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySenseKeyCode;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsDevice; import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsDevice;
@ -151,8 +152,9 @@ public class Shelly1HttpApi extends ShellyHttpClient implements ShellyApiInterfa
try { try {
json = httpRequest(SHELLY_URL_STATUS); json = httpRequest(SHELLY_URL_STATUS);
// Dimmer2 returns invalid json type for loaderror :-( // Dimmer2 returns invalid json type for loaderror :-(
json = getString(json.replace("\"loaderror\":0,", "\"loaderror\":false,")); json = json.replace("\"loaderror\":0,", "\"loaderror\":false,")
json = getString(json.replace("\"loaderror\":1,", "\"loaderror\":true,")); .replace("\"loaderror\":1,", "\"loaderror\":true,")
.replace("\"tmp\":{\"value\": \"null\",", "\"tmp\":{\"value\": null,");
ShellySettingsStatus status = fromJson(gson, json, ShellySettingsStatus.class); ShellySettingsStatus status = fromJson(gson, json, ShellySettingsStatus.class);
status.json = json; status.json = json;
return status; return status;

View File

@ -39,6 +39,7 @@ import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyInputSta
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyOtaCheckResult; import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyOtaCheckResult;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsDevice; import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsDevice;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsStatus; import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsStatus;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyThermnostat;
import org.openhab.binding.shelly.internal.api1.Shelly1CoapHandler; import org.openhab.binding.shelly.internal.api1.Shelly1CoapHandler;
import org.openhab.binding.shelly.internal.api1.Shelly1CoapJSonDTO; import org.openhab.binding.shelly.internal.api1.Shelly1CoapJSonDTO;
import org.openhab.binding.shelly.internal.api1.Shelly1CoapServer; import org.openhab.binding.shelly.internal.api1.Shelly1CoapServer;
@ -87,14 +88,14 @@ public class ShellyBaseHandler extends BaseThingHandler
protected final ShellyApiInterface api; protected final ShellyApiInterface api;
private final HttpClient httpClient; private final HttpClient httpClient;
protected ShellyBindingConfiguration bindingConfig; private ShellyBindingConfiguration bindingConfig;
protected ShellyThingConfiguration config = new ShellyThingConfiguration(); protected ShellyThingConfiguration config = new ShellyThingConfiguration();
protected ShellyDeviceProfile profile = new ShellyDeviceProfile(); // init empty profile to avoid NPE protected ShellyDeviceProfile profile = new ShellyDeviceProfile(); // init empty profile to avoid NPE
protected ShellyDeviceStats stats = new ShellyDeviceStats(); protected ShellyDeviceStats stats = new ShellyDeviceStats();
private final Shelly1CoapHandler coap; private final Shelly1CoapHandler coap;
public boolean autoCoIoT = false; public boolean autoCoIoT = false;
public final ShellyTranslationProvider messages; private final ShellyTranslationProvider messages;
protected boolean stopping = false; protected boolean stopping = false;
private boolean channelsCreated = false; private boolean channelsCreated = false;
@ -108,7 +109,7 @@ public class ShellyBaseHandler extends BaseThingHandler
// delay before enabling channel // delay before enabling channel
private final int cacheCount = UPDATE_SETTINGS_INTERVAL_SECONDS / UPDATE_STATUS_INTERVAL_SECONDS; private final int cacheCount = UPDATE_SETTINGS_INTERVAL_SECONDS / UPDATE_STATUS_INTERVAL_SECONDS;
protected final ShellyChannelCache cache; private final ShellyChannelCache cache;
private String localIP = ""; private String localIP = "";
private String localPort = ""; private String localPort = "";
@ -225,7 +226,7 @@ public class ShellyBaseHandler extends BaseThingHandler
* *
* @throws ShellyApiException e.g. http returned non-ok response, check e.getMessage() for details. * @throws ShellyApiException e.g. http returned non-ok response, check e.getMessage() for details.
*/ */
private boolean initializeThing() throws ShellyApiException { public boolean initializeThing() throws ShellyApiException {
// Init from thing type to have a basic profile, gets updated when device info is received from API // Init from thing type to have a basic profile, gets updated when device info is received from API
stopping = false; stopping = false;
refreshSettings = false; refreshSettings = false;
@ -251,7 +252,7 @@ public class ShellyBaseHandler extends BaseThingHandler
// Initialize API access, exceptions will be catched by initialize() // Initialize API access, exceptions will be catched by initialize()
ShellySettingsDevice devInfo = api.getDeviceInfo(); ShellySettingsDevice devInfo = api.getDeviceInfo();
if (getBool(devInfo.auth) && config.userId.isEmpty()) { if (getBool(devInfo.auth) && config.password.isEmpty()) {
setThingOffline(ThingStatusDetail.CONFIGURATION_ERROR, "offline.conf-error-no-credentials"); setThingOffline(ThingStatusDetail.CONFIGURATION_ERROR, "offline.conf-error-no-credentials");
return false; return false;
} }
@ -274,11 +275,26 @@ public class ShellyBaseHandler extends BaseThingHandler
// New Shelly devices might use a different endpoint for the CoAP listener // New Shelly devices might use a different endpoint for the CoAP listener
tmpPrf.coiotEndpoint = devInfo.coiot; tmpPrf.coiotEndpoint = devInfo.coiot;
} }
if (tmpPrf.settings.sleepMode != null && !tmpPrf.isTRV) {
// Sensor, usually 12h, H&T in USB mode 10min
tmpPrf.updatePeriod = getString(tmpPrf.settings.sleepMode.unit).equalsIgnoreCase("m")
? tmpPrf.settings.sleepMode.period * 60 // minutes
: tmpPrf.settings.sleepMode.period * 3600; // hours
tmpPrf.updatePeriod += 60; // give 1min extra
} else if ((tmpPrf.settings.coiot != null) && tmpPrf.settings.coiot.updatePeriod != null) {
// Derive from CoAP update interval, usually 2*15+10s=40sec -> 70sec
tmpPrf.updatePeriod = Math.max(UPDATE_SETTINGS_INTERVAL_SECONDS,
2 * getInteger(tmpPrf.settings.coiot.updatePeriod)) + 10;
} else {
tmpPrf.updatePeriod = UPDATE_SETTINGS_INTERVAL_SECONDS + 10;
}
tmpPrf.auth = devInfo.auth; // missing in /settings tmpPrf.auth = devInfo.auth; // missing in /settings
tmpPrf.status = api.getStatus(); tmpPrf.status = api.getStatus();
tmpPrf.updateFromStatus(tmpPrf.status); tmpPrf.updateFromStatus(tmpPrf.status);
showThingConfig(tmpPrf); showThingConfig(tmpPrf);
// update thing properties
checkVersion(tmpPrf, tmpPrf.status); checkVersion(tmpPrf, tmpPrf.status);
if (config.eventsCoIoT && (tmpPrf.settings.coiot != null) && (tmpPrf.settings.coiot.enabled != null)) { if (config.eventsCoIoT && (tmpPrf.settings.coiot != null) && (tmpPrf.settings.coiot.enabled != null)) {
@ -385,14 +401,39 @@ public class ShellyBaseHandler extends BaseThingHandler
: 0; : 0;
api.setSleepTime(value); api.setSleepTime(value);
break; break;
case CHANNEL_CONTROL_SCHEDULE:
if (profile.isTRV) {
logger.debug("{}: {} Valve schedule/profile", thingName,
command == OnOffType.ON ? "Enable" : "Disable");
api.setValveProfile(0,
command == OnOffType.OFF ? 0 : profile.status.thermostats.get(0).profile);
}
break;
case CHANNEL_CONTROL_PROFILE: case CHANNEL_CONTROL_PROFILE:
logger.debug("{}: Select profile {}", thingName, command); logger.debug("{}: Select profile {}", thingName, command);
int profile = (int) getNumber(command); int id = -1;
if (profile < 0 || profile > 5) { if (command instanceof Number) {
logger.warn("{}: Invalid profile Id {} requested", thingName, profile); id = (int) getNumber(command);
break; } else {
String cmd = command.toString();
if (isDigit(cmd.charAt(0))) {
id = Integer.parseInt(cmd);
} else if (cmd.equalsIgnoreCase("DISABLED")) {
id = 0;
} else if (profile.settings.thermostats != null) {
ShellyThermnostat t = profile.settings.thermostats.get(0);
for (int i = 0; i < t.profileNames.length; i++) {
if (t.profileNames[i].equalsIgnoreCase(cmd)) {
id = i + 1;
}
}
}
}
if (id < 0 || id > 5) {
logger.warn("{}: Invalid profile Id {} requested", thingName, profile);
} else {
api.setValveProfile(0, id);
} }
api.setValveProfile(0, profile);
break; break;
case CHANNEL_CONTROL_MODE: case CHANNEL_CONTROL_MODE:
logger.debug("{}: Set mode to {}", thingName, command); logger.debug("{}: Set mode to {}", thingName, command);
@ -567,9 +608,10 @@ public class ShellyBaseHandler extends BaseThingHandler
updateStatus(ThingStatus.ONLINE); updateStatus(ThingStatus.ONLINE);
// request 3 updates in a row (during the first 2+3*3 sec) // request 3 updates in a row (during the first 2+3*3 sec)
logger.debug("{}: Thing went online, request status update", thingName);
requestUpdates(profile.alwaysOn ? 3 : 1, !channelsCreated); requestUpdates(profile.alwaysOn ? 3 : 1, !channelsCreated);
} }
// Restart watchdog when status update was successful (no exception)
restartWatchdog(); restartWatchdog();
} }
@ -695,7 +737,7 @@ public class ShellyBaseHandler extends BaseThingHandler
if (force || !lastAlarm.equals(event) if (force || !lastAlarm.equals(event)
|| (lastAlarm.equals(event) && now() > stats.lastAlarmTs + HEALTH_CHECK_INTERVAL_SEC)) { || (lastAlarm.equals(event) && now() > stats.lastAlarmTs + HEALTH_CHECK_INTERVAL_SEC)) {
switch (event) { switch (event.toUpperCase()) {
case "": case "":
case "0": // DW2 1.8 case "0": // DW2 1.8
case SHELLY_WAKEUPT_SENSOR: case SHELLY_WAKEUPT_SENSOR:
@ -957,7 +999,7 @@ public class ShellyBaseHandler extends BaseThingHandler
* @param thingType thing type acc. to the xml definition * @param thingType thing type acc. to the xml definition
* @param mode Device mode (e.g. relay, roller) * @param mode Device mode (e.g. relay, roller)
*/ */
private void changeThingType(String thingType, String mode) { protected void changeThingType(String thingType, String mode) {
ThingTypeUID thingTypeUID = ShellyThingCreator.getThingTypeUID(thingType, "", mode); ThingTypeUID thingTypeUID = ShellyThingCreator.getThingTypeUID(thingType, "", mode);
if (!thingTypeUID.equals(THING_TYPE_SHELLYUNKNOWN)) { if (!thingTypeUID.equals(THING_TYPE_SHELLYUNKNOWN)) {
logger.debug("{}: Changing thing type to {}", getThing().getLabel(), thingTypeUID); logger.debug("{}: Changing thing type to {}", getThing().getLabel(), thingTypeUID);
@ -1034,7 +1076,7 @@ public class ShellyBaseHandler extends BaseThingHandler
int idx = 0; int idx = 0;
boolean multiInput = status.inputs.size() >= 2; // device has multiple SW (inputs) boolean multiInput = status.inputs.size() >= 2; // device has multiple SW (inputs)
for (ShellyInputState input : status.inputs) { for (ShellyInputState input : status.inputs) {
String group = profile.getControlGroup(idx); String group = profile.getInputGroup(idx);
String suffix = multiInput ? profile.getInputSuffix(idx) : ""; String suffix = multiInput ? profile.getInputSuffix(idx) : "";
if (!areChannelsCreated()) { if (!areChannelsCreated()) {

View File

@ -20,8 +20,10 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.shelly.internal.api.ShellyApiException; import org.openhab.binding.shelly.internal.api.ShellyApiException;
import org.openhab.binding.shelly.internal.api.ShellyDeviceProfile; import org.openhab.binding.shelly.internal.api.ShellyDeviceProfile;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyRollerStatus;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsEMeter; import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsEMeter;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsMeter; import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsMeter;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsRelay;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsStatus; import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsStatus;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyStatusSensor; import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyStatusSensor;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyStatusSensor.ShellyADC; import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyStatusSensor.ShellyADC;
@ -29,6 +31,7 @@ import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyThermnos
import org.openhab.binding.shelly.internal.provider.ShellyChannelDefinitions; import org.openhab.binding.shelly.internal.provider.ShellyChannelDefinitions;
import org.openhab.core.library.types.OnOffType; import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.OpenClosedType; import org.openhab.core.library.types.OpenClosedType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.library.unit.ImperialUnits; import org.openhab.core.library.unit.ImperialUnits;
import org.openhab.core.library.unit.SIUnits; import org.openhab.core.library.unit.SIUnits;
import org.openhab.core.library.unit.Units; import org.openhab.core.library.unit.Units;
@ -86,6 +89,83 @@ public class ShellyComponents {
return false; // device status never triggers update return false; // device status never triggers update
} }
public static boolean updateRelay(ShellyBaseHandler thingHandler, ShellySettingsStatus status, int id) {
ShellyDeviceProfile profile = thingHandler.getProfile();
ShellySettingsRelay rsettings = profile.settings.relays.get(id);
ShellySettingsRelay relay = status.relays.get(id);
boolean updated = false;
if (relay.isValid == null || relay.isValid) {
String groupName = profile.getControlGroup(id);
updated |= thingHandler.updateChannel(groupName, CHANNEL_OUTPUT_NAME, getStringType(rsettings.name));
if (getBool(relay.overpower)) {
thingHandler.postEvent(ALARM_TYPE_OVERPOWER, false);
}
updated |= thingHandler.updateChannel(groupName, CHANNEL_OUTPUT, getOnOff(relay.ison));
updated |= thingHandler.updateChannel(groupName, CHANNEL_TIMER_ACTIVE, getOnOff(relay.hasTimer));
if (status.extTemperature != null) {
// Shelly 1/1PM support up to 3 external sensors
// for whatever reason those are not represented as an array, but 3 elements
if (status.extTemperature.sensor1 != null) {
updated |= thingHandler.updateChannel(CHANNEL_GROUP_SENSOR, CHANNEL_ESENDOR_TEMP1,
toQuantityType(getDouble(status.extTemperature.sensor1.tC), DIGITS_TEMP, SIUnits.CELSIUS));
}
if (status.extTemperature.sensor2 != null) {
updated |= thingHandler.updateChannel(CHANNEL_GROUP_SENSOR, CHANNEL_ESENDOR_TEMP2,
toQuantityType(getDouble(status.extTemperature.sensor2.tC), DIGITS_TEMP, SIUnits.CELSIUS));
}
if (status.extTemperature.sensor3 != null) {
updated |= thingHandler.updateChannel(CHANNEL_GROUP_SENSOR, CHANNEL_ESENDOR_TEMP3,
toQuantityType(getDouble(status.extTemperature.sensor3.tC), DIGITS_TEMP, SIUnits.CELSIUS));
}
}
if ((status.extHumidity != null) && (status.extHumidity.sensor1 != null)) {
updated |= thingHandler.updateChannel(CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_HUM,
toQuantityType(getDouble(status.extHumidity.sensor1.hum), DIGITS_PERCENT, Units.PERCENT));
}
// Update Auto-ON/OFF timer
updated |= thingHandler.updateChannel(groupName, CHANNEL_TIMER_AUTOON,
toQuantityType(getDouble(rsettings.autoOn), Units.SECOND));
updated |= thingHandler.updateChannel(groupName, CHANNEL_TIMER_AUTOOFF,
toQuantityType(getDouble(rsettings.autoOff), Units.SECOND));
}
return updated;
}
public static boolean updateRoller(ShellyBaseHandler thingHandler, ShellyRollerStatus control, int id)
throws ShellyApiException {
ShellyDeviceProfile profile = thingHandler.getProfile();
boolean updated = false;
if (getBool(control.isValid)) {
String groupName = profile.getControlGroup(id);
if (control.name != null) {
updated |= thingHandler.updateChannel(groupName, CHANNEL_OUTPUT_NAME, getStringType(control.name));
}
String state = getString(control.state);
if (state.equals(SHELLY_ALWD_ROLLER_TURN_STOP)) {
if (control.currentPos != null) {
// only valid in stop state
int pos = Math.max(SHELLY_MIN_ROLLER_POS, Math.min(control.currentPos, SHELLY_MAX_ROLLER_POS));
updated |= thingHandler.updateChannel(groupName, CHANNEL_ROL_CONTROL_CONTROL,
toQuantityType((double) (SHELLY_MAX_ROLLER_POS - pos), Units.PERCENT));
updated |= thingHandler.updateChannel(groupName, CHANNEL_ROL_CONTROL_POS,
toQuantityType((double) pos, Units.PERCENT));
}
}
updated |= thingHandler.updateChannel(groupName, CHANNEL_ROL_CONTROL_STATE, new StringType(state));
updated |= thingHandler.updateChannel(groupName, CHANNEL_ROL_CONTROL_STOPR,
getStringType(control.stopReason));
updated |= thingHandler.updateChannel(groupName, CHANNEL_ROL_CONTROL_SAFETY,
getOnOff(control.safetySwitch));
}
return updated;
}
/** /**
* Update Meter channel * Update Meter channel
* *
@ -111,8 +191,12 @@ public class ShellyComponents {
int m = 0; int m = 0;
if (!profile.isEMeter) { if (!profile.isEMeter) {
for (ShellySettingsMeter meter : status.meters) { for (ShellySettingsMeter meter : status.meters) {
if (m >= profile.numMeters) {
// Shelly1: reports status.meters[0].is_valid = true, but even doesn't have a meter
meter.isValid = false;
}
if (getBool(meter.isValid) || profile.isLight) { // RGBW2-white doesn't report valid flag if (getBool(meter.isValid) || profile.isLight) { // RGBW2-white doesn't report valid flag
// correctly in white mode // correctly in white mode
String groupName = profile.getMeterGroup(m); String groupName = profile.getMeterGroup(m);
if (!thingHandler.areChannelsCreated()) { if (!thingHandler.areChannelsCreated()) {
// skip for Shelly Bulb: JSON has a meter, but values don't get updated // skip for Shelly Bulb: JSON has a meter, but values don't get updated
@ -302,7 +386,7 @@ public class ShellyComponents {
// Shelly TRV // Shelly TRV
ShellyThermnostat t = status.thermostats.get(0); ShellyThermnostat t = status.thermostats.get(0);
ShellyThermnostat ps = profile.settings.thermostats.get(0); ShellyThermnostat ps = profile.settings.thermostats.get(0);
int bminutes = getInteger(t.boostMinutes) > 0 ? getInteger(t.boostMinutes) int bminutes = getInteger(t.boostMinutes) >= 0 ? getInteger(t.boostMinutes)
: getInteger(ps.boostMinutes); : getInteger(ps.boostMinutes);
updated |= thingHandler.updateChannel(CHANNEL_GROUP_CONTROL, CHANNEL_CONTROL_BCONTROL, updated |= thingHandler.updateChannel(CHANNEL_GROUP_CONTROL, CHANNEL_CONTROL_BCONTROL,
getOnOff(getInteger(t.boostMinutes) > 0)); getOnOff(getInteger(t.boostMinutes) > 0));
@ -310,10 +394,11 @@ public class ShellyComponents {
toQuantityType((double) bminutes, DIGITS_NONE, Units.MINUTE)); toQuantityType((double) bminutes, DIGITS_NONE, Units.MINUTE));
updated |= thingHandler.updateChannel(CHANNEL_GROUP_CONTROL, CHANNEL_CONTROL_MODE, updated |= thingHandler.updateChannel(CHANNEL_GROUP_CONTROL, CHANNEL_CONTROL_MODE,
getStringType(getBool(t.targetTemp.enabled) ? SHELLY_TRV_MODE_AUTO : SHELLY_TRV_MODE_MANUAL)); getStringType(getBool(t.targetTemp.enabled) ? SHELLY_TRV_MODE_AUTO : SHELLY_TRV_MODE_MANUAL));
updated |= thingHandler.updateChannel(CHANNEL_GROUP_CONTROL, CHANNEL_CONTROL_PROFILE, int pid = getBool(t.schedule) ? getInteger(t.profile) : 0;
getDecimal(getBool(t.schedule) ? t.profile + 1 : 0)); updated |= thingHandler.updateChannel(CHANNEL_GROUP_CONTROL, CHANNEL_CONTROL_SCHEDULE,
updated |= thingHandler.updateChannel(CHANNEL_GROUP_DEV_STATUS, CHANNEL_DEVST_SCHEDULE,
getOnOff(t.schedule)); getOnOff(t.schedule));
updated |= thingHandler.updateChannel(CHANNEL_GROUP_CONTROL, CHANNEL_CONTROL_PROFILE,
getStringType(profile.getValueProfile(pid)));
if (t.tmp != null) { if (t.tmp != null) {
Double temp = convertToC(t.tmp.value, getString(t.tmp.units)); Double temp = convertToC(t.tmp.value, getString(t.tmp.units));
updated |= thingHandler.updateChannel(CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_TEMP, updated |= thingHandler.updateChannel(CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_TEMP,

View File

@ -350,8 +350,11 @@ public class ShellyLightHandler extends ShellyBaseHandler {
if (profile.settings.lights != null) { if (profile.settings.lights != null) {
// Channel control/timer // Channel control/timer
ShellySettingsRgbwLight ls = profile.settings.lights.get(lightId); ShellySettingsRgbwLight ls = profile.settings.lights.get(lightId);
updated |= updateChannel(controlGroup, CHANNEL_TIMER_AUTOON, getDecimal(ls.autoOn)); updated |= updateChannel(controlGroup, CHANNEL_TIMER_AUTOON,
updated |= updateChannel(controlGroup, CHANNEL_TIMER_AUTOOFF, getDecimal(ls.autoOff)); toQuantityType(getDouble(ls.autoOn), Units.SECOND));
updated |= updateChannel(controlGroup, CHANNEL_TIMER_AUTOOFF,
toQuantityType(getDouble(ls.autoOff), Units.SECOND));
updated |= updateChannel(controlGroup, CHANNEL_LIGHT_POWER, col.power);
updated |= updateChannel(controlGroup, CHANNEL_TIMER_ACTIVE, getOnOff(light.hasTimer)); updated |= updateChannel(controlGroup, CHANNEL_TIMER_ACTIVE, getOnOff(light.hasTimer));
updated |= updateChannel(controlGroup, CHANNEL_LIGHT_POWER, col.power); updated |= updateChannel(controlGroup, CHANNEL_LIGHT_POWER, col.power);
} }

View File

@ -23,11 +23,8 @@ import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyRollerStatus; import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyRollerStatus;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsDimmer; import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsDimmer;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsRelay; import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsRelay;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsRoller;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsStatus; import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsStatus;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyShortLightStatus; import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyShortLightStatus;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyShortStatusRelay;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyStatusRelay;
import org.openhab.binding.shelly.internal.api1.Shelly1CoapServer; import org.openhab.binding.shelly.internal.api1.Shelly1CoapServer;
import org.openhab.binding.shelly.internal.config.ShellyBindingConfiguration; import org.openhab.binding.shelly.internal.config.ShellyBindingConfiguration;
import org.openhab.binding.shelly.internal.provider.ShellyChannelDefinitions; import org.openhab.binding.shelly.internal.provider.ShellyChannelDefinitions;
@ -37,9 +34,7 @@ import org.openhab.core.library.types.IncreaseDecreaseType;
import org.openhab.core.library.types.OnOffType; import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.PercentType; import org.openhab.core.library.types.PercentType;
import org.openhab.core.library.types.StopMoveType; import org.openhab.core.library.types.StopMoveType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.library.types.UpDownType; import org.openhab.core.library.types.UpDownType;
import org.openhab.core.library.unit.SIUnits;
import org.openhab.core.library.unit.Units; import org.openhab.core.library.unit.Units;
import org.openhab.core.thing.ChannelUID; import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing; import org.openhab.core.thing.Thing;
@ -228,7 +223,7 @@ public class ShellyRelayHandler extends ShellyBaseHandler {
if ((command == UpDownType.UP && getString(rstatus.state).equals(SHELLY_ALWD_ROLLER_TURN_OPEN)) if ((command == UpDownType.UP && getString(rstatus.state).equals(SHELLY_ALWD_ROLLER_TURN_OPEN))
|| (command == UpDownType.DOWN || (command == UpDownType.DOWN
&& getString(rstatus.state).equals(SHELLY_ALWD_ROLLER_TURN_CLOSE))) { && getString(rstatus.state).equals(SHELLY_ALWD_ROLLER_TURN_CLOSE))) {
logger.debug("{}: Roller is already moving ({}), ignore command {}", thingName, logger.debug("{}: Roller is already in requested position ({}), ignore command {}", thingName,
getString(rstatus.state), command); getString(rstatus.state), command);
requestUpdates(1, false); requestUpdates(1, false);
return; return;
@ -305,7 +300,7 @@ public class ShellyRelayHandler extends ShellyBaseHandler {
/** /**
* Auto-create relay channels depending on relay type/mode * Auto-create relay channels depending on relay type/mode
*/ */
private void createRelayChannels(ShellyStatusRelay relay, int idx) { private void createRelayChannels(ShellySettingsRelay relay, int idx) {
if (!areChannelsCreated()) { if (!areChannelsCreated()) {
updateChannelDefinitions(ShellyChannelDefinitions.createRelayChannels(getThing(), profile, relay, idx)); updateChannelDefinitions(ShellyChannelDefinitions.createRelayChannels(getThing(), profile, relay, idx));
} }
@ -350,93 +345,23 @@ public class ShellyRelayHandler extends ShellyBaseHandler {
} }
} }
if (profile.hasRelays && !profile.isRoller && !profile.isDimmer) { if (profile.hasRelays && !profile.isRoller) {
logger.trace("{}: Updating {} relay(s)", thingName, profile.numRelays); logger.trace("{}: Updating {} relay(s)", thingName, profile.numRelays);
int i = 0; for (int i = 0; i < status.relays.size(); i++) {
ShellyStatusRelay rstatus = api.getRelayStatus(i); createRelayChannels(status.relays.get(i), i);
for (ShellyShortStatusRelay relay : rstatus.relays) { updated |= ShellyComponents.updateRelay(this, status, i);
createRelayChannels(rstatus, i);
if ((relay.isValid == null) || relay.isValid) {
String groupName = profile.getControlGroup(i);
ShellySettingsRelay rs = profile.settings.relays.get(i);
updated |= updateChannel(groupName, CHANNEL_OUTPUT_NAME, getStringType(rs.name));
if (getBool(relay.overpower)) {
postEvent(ALARM_TYPE_OVERPOWER, false);
}
updated |= updateChannel(groupName, CHANNEL_OUTPUT, getOnOff(relay.ison));
updated |= updateChannel(groupName, CHANNEL_TIMER_ACTIVE, getOnOff(relay.hasTimer));
if (rstatus.extTemperature != null) {
// Shelly 1/1PM support up to 3 external sensors
// for whatever reason those are not represented as an array, but 3 elements
if (rstatus.extTemperature.sensor1 != null) {
updated |= updateChannel(CHANNEL_GROUP_SENSOR, CHANNEL_ESENDOR_TEMP1, toQuantityType(
getDouble(rstatus.extTemperature.sensor1.tC), DIGITS_TEMP, SIUnits.CELSIUS));
}
if (rstatus.extTemperature.sensor2 != null) {
updated |= updateChannel(CHANNEL_GROUP_SENSOR, CHANNEL_ESENDOR_TEMP2, toQuantityType(
getDouble(rstatus.extTemperature.sensor2.tC), DIGITS_TEMP, SIUnits.CELSIUS));
}
if (rstatus.extTemperature.sensor3 != null) {
updated |= updateChannel(CHANNEL_GROUP_SENSOR, CHANNEL_ESENDOR_TEMP3, toQuantityType(
getDouble(rstatus.extTemperature.sensor3.tC), DIGITS_TEMP, SIUnits.CELSIUS));
}
}
if ((rstatus.extHumidity != null) && (rstatus.extHumidity.sensor1 != null)) {
updated |= updateChannel(CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_HUM, toQuantityType(
getDouble(rstatus.extHumidity.sensor1.hum), DIGITS_PERCENT, Units.PERCENT));
}
// Update Auto-ON/OFF timer
ShellySettingsRelay rsettings = profile.settings.relays.get(i);
if (rsettings != null) {
updated |= updateChannel(groupName, CHANNEL_TIMER_AUTOON,
toQuantityType(getDouble(rsettings.autoOn), Units.SECOND));
updated |= updateChannel(groupName, CHANNEL_TIMER_AUTOOFF,
toQuantityType(getDouble(rsettings.autoOff), Units.SECOND));
}
}
i++; i++;
} }
} else if (profile.hasRelays && profile.isRoller && (status.rollers != null)) { } else {
// Check for Relay in Roller Mode // Check for Relay in Roller Mode
logger.trace("{}: Updating {} rollers", thingName, profile.numRollers); logger.trace("{}: Updating {} rollers", thingName, profile.numRollers);
int i = 0; for (int i = 0; i < profile.numRollers; i++) {
ShellyRollerStatus roller = status.rollers.get(i);
for (ShellySettingsRoller roller : status.rollers) { createRollerChannels(roller);
if (roller.isValid) { updated |= ShellyComponents.updateRoller(this, roller, i);
ShellyRollerStatus control = api.getRollerStatus(i);
Integer relayIndex = i + 1;
String groupName = profile.numRollers > 1 ? CHANNEL_GROUP_ROL_CONTROL + relayIndex.toString()
: CHANNEL_GROUP_ROL_CONTROL;
createRollerChannels(control);
if (control.name != null) {
updated |= updateChannel(groupName, CHANNEL_OUTPUT_NAME, getStringType(control.name));
}
String state = getString(control.state);
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));
logger.debug("{}: REST Update roller position: control={}, position={}", thingName,
SHELLY_MAX_ROLLER_POS - pos, pos);
updated |= updateChannel(groupName, CHANNEL_ROL_CONTROL_CONTROL,
toQuantityType((double) (SHELLY_MAX_ROLLER_POS - pos), Units.PERCENT));
updated |= updateChannel(groupName, CHANNEL_ROL_CONTROL_POS,
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 |= updateChannel(groupName, CHANNEL_ROL_CONTROL_SAFETY, getOnOff(control.safetySwitch));
i++;
}
} }
} }
return updated; return updated;
} }

View File

@ -212,11 +212,11 @@ public class ShellyManagerPage {
ShellyDeviceStats stats = th.getStats(); ShellyDeviceStats stats = th.getStats();
properties.putAll(stats.asProperties()); properties.putAll(stats.asProperties());
for (Map.Entry<String, Object> p : thing.getConfiguration().getProperties().entrySet()) { for (Map.Entry<String, @Nullable Object> p : thing.getConfiguration().getProperties().entrySet()) {
String key = p.getKey(); String key = p.getKey();
if (p.getValue() != null) { Object o = p.getValue();
String value = p.getValue().toString(); if (o != null) {
properties.put(key, value); properties.put(key, o.toString());
} }
} }

View File

@ -38,9 +38,7 @@ import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettings
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsRgbwLight; import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsRgbwLight;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsStatus; import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsStatus;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyShortLightStatus; import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyShortLightStatus;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyShortStatusRelay;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyStatusLightChannel; import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyStatusLightChannel;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyStatusRelay;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyStatusSensor; import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyStatusSensor;
import org.openhab.binding.shelly.internal.handler.ShellyThingInterface; import org.openhab.binding.shelly.internal.handler.ShellyThingInterface;
import org.openhab.core.thing.Channel; import org.openhab.core.thing.Channel;
@ -89,6 +87,7 @@ public class ShellyChannelDefinitions {
private static final String CHGR_RELAY = CHANNEL_GROUP_RELAY_CONTROL; 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_ROLLER = CHANNEL_GROUP_ROL_CONTROL;
private static final String CHGR_LIGHT = CHANNEL_GROUP_LIGHT_CONTROL; private static final String CHGR_LIGHT = CHANNEL_GROUP_LIGHT_CONTROL;
private static final String CHGR_LIGHTCH = CHANNEL_GROUP_LIGHT_CHANNEL;
private static final String CHGR_STATUS = CHANNEL_GROUP_STATUS; private static final String CHGR_STATUS = CHANNEL_GROUP_STATUS;
private static final String CHGR_METER = CHANNEL_GROUP_METER; private static final String CHGR_METER = CHANNEL_GROUP_METER;
private static final String CHGR_SENSOR = CHANNEL_GROUP_SENSOR; private static final String CHGR_SENSOR = CHANNEL_GROUP_SENSOR;
@ -122,7 +121,6 @@ public class ShellyChannelDefinitions {
.add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_DEVST_HEARTBEAT, "heartBeat", ITEMT_DATETIME)) .add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_DEVST_HEARTBEAT, "heartBeat", ITEMT_DATETIME))
.add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_DEVST_UPDATE, "updateAvailable", ITEMT_SWITCH)) .add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_DEVST_UPDATE, "updateAvailable", ITEMT_SWITCH))
.add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_DEVST_CALIBRATED, "calibrated", ITEMT_SWITCH)) .add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_DEVST_CALIBRATED, "calibrated", ITEMT_SWITCH))
.add(new ShellyChannel(m, CHGR_DEVST, CHANNEL_DEVST_SCHEDULE, "deviceSchedule", ITEMT_SWITCH))
// Relay // Relay
.add(new ShellyChannel(m, CHGR_RELAY, CHANNEL_OUTPUT_NAME, "outputName", ITEMT_STRING)) .add(new ShellyChannel(m, CHGR_RELAY, CHANNEL_OUTPUT_NAME, "outputName", ITEMT_STRING))
@ -151,16 +149,28 @@ public class ShellyChannelDefinitions {
.add(new ShellyChannel(m, CHGR_ROLLER, CHANNEL_STATUS_EVENTCOUNT, "eventCount", ITEMT_NUMBER)) .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")) .add(new ShellyChannel(m, CHGR_ROLLER, CHANNEL_EVENT_TRIGGER, "system:button", "system:button"))
// RGBW2 // Bulb/Duo/Vintage
.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_INPUT, "inputState", ITEMT_SWITCH))
.add(new ShellyChannel(m, CHGR_LIGHT, CHANNEL_BUTTON_TRIGGER, "system:button", ITEMT_STRING)) .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_EVENTTYPE, "lastEvent", ITEMT_STRING))
.add(new ShellyChannel(m, CHGR_LIGHT, CHANNEL_STATUS_EVENTCOUNT, "eventCount", ITEMT_NUMBER)) .add(new ShellyChannel(m, CHGR_LIGHT, CHANNEL_STATUS_EVENTCOUNT, "eventCount", ITEMT_NUMBER))
.add(new ShellyChannel(m, CHANNEL_GROUP_WHITE_CONTROL, CHANNEL_BRIGHTNESS, "whiteBrightness",
ITEMT_DIMMER))
.add(new ShellyChannel(m, CHANNEL_GROUP_WHITE_CONTROL, CHANNEL_COLOR_TEMP, "whiteTemp", ITEMT_DIMMER))
// RGBW2-color
.add(new ShellyChannel(m, CHGR_LIGHT, CHANNEL_LIGHT_POWER, "system:power", ITEMT_SWITCH))
.add(new ShellyChannel(m, CHGR_LIGHT, CHANNEL_TIMER_AUTOON, "timerAutoOn", ITEMT_TIME)) .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_AUTOOFF, "timerAutoOff", ITEMT_TIME))
.add(new ShellyChannel(m, CHGR_LIGHT, CHANNEL_TIMER_ACTIVE, "timerActive", ITEMT_SWITCH)) .add(new ShellyChannel(m, CHGR_LIGHT, CHANNEL_TIMER_ACTIVE, "timerActive", ITEMT_SWITCH))
// RGBW2-white
.add(new ShellyChannel(m, CHGR_LIGHTCH, CHANNEL_BRIGHTNESS, "whiteBrightness", ITEMT_DIMMER))
.add(new ShellyChannel(m, CHGR_LIGHTCH, CHANNEL_TIMER_AUTOON, "timerAutoOn", ITEMT_TIME))
.add(new ShellyChannel(m, CHGR_LIGHTCH, CHANNEL_TIMER_AUTOOFF, "timerAutoOff", ITEMT_TIME))
.add(new ShellyChannel(m, CHGR_LIGHTCH, CHANNEL_TIMER_ACTIVE, "timerActive", ITEMT_SWITCH))
// RGBW2-color
.add(new ShellyChannel(m, CHGR_LIGHT, CHANNEL_LIGHT_POWER, "system:power", ITEMT_SWITCH))
// Power Meter // Power Meter
.add(new ShellyChannel(m, CHGR_METER, CHANNEL_METER_CURRENTWATTS, "meterWatts", ITEMT_POWER)) .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_TOTALKWH, "meterTotal", ITEMT_ENERGY))
@ -217,7 +227,8 @@ public class ShellyChannelDefinitions {
// TRV // TRV
.add(new ShellyChannel(m, CHGR_CONTROL, CHANNEL_CONTROL_POSITION, "sensorPosition", ITEMT_DIMMER)) .add(new ShellyChannel(m, CHGR_CONTROL, CHANNEL_CONTROL_POSITION, "sensorPosition", ITEMT_DIMMER))
.add(new ShellyChannel(m, CHGR_CONTROL, CHANNEL_CONTROL_MODE, "controlMode", ITEMT_STRING)) .add(new ShellyChannel(m, CHGR_CONTROL, CHANNEL_CONTROL_MODE, "controlMode", ITEMT_STRING))
.add(new ShellyChannel(m, CHGR_CONTROL, CHANNEL_CONTROL_PROFILE, "controlProfile", ITEMT_NUMBER)) .add(new ShellyChannel(m, CHGR_CONTROL, CHANNEL_CONTROL_SCHEDULE, "controlSchedule", ITEMT_SWITCH))
.add(new ShellyChannel(m, CHGR_CONTROL, CHANNEL_CONTROL_PROFILE, "controlProfile", ITEMT_STRING))
.add(new ShellyChannel(m, CHGR_CONTROL, CHANNEL_CONTROL_SETTEMP, "targetTemp", ITEMT_TEMP)) .add(new ShellyChannel(m, CHGR_CONTROL, CHANNEL_CONTROL_SETTEMP, "targetTemp", ITEMT_TEMP))
.add(new ShellyChannel(m, CHGR_CONTROL, CHANNEL_CONTROL_BCONTROL, "boostControl", ITEMT_SWITCH)) .add(new ShellyChannel(m, CHGR_CONTROL, CHANNEL_CONTROL_BCONTROL, "boostControl", ITEMT_SWITCH))
.add(new ShellyChannel(m, CHGR_CONTROL, CHANNEL_CONTROL_BTIMER, "boostTimer", ITEMT_TIME)); .add(new ShellyChannel(m, CHGR_CONTROL, CHANNEL_CONTROL_BTIMER, "boostTimer", ITEMT_TIME));
@ -294,16 +305,13 @@ public class ShellyChannelDefinitions {
* @return ArrayList<Channel> of channels to be added to the thing * @return ArrayList<Channel> of channels to be added to the thing
*/ */
public static Map<String, Channel> createRelayChannels(final Thing thing, final ShellyDeviceProfile profile, public static Map<String, Channel> createRelayChannels(final Thing thing, final ShellyDeviceProfile profile,
final ShellyStatusRelay relay, int idx) { final ShellySettingsRelay rstatus, int idx) {
Map<String, Channel> add = new LinkedHashMap<>(); Map<String, Channel> add = new LinkedHashMap<>();
String group = profile.getControlGroup(idx); String group = profile.getControlGroup(idx);
if (profile.settings.relays != null) { if (profile.settings.relays != null) {
ShellySettingsRelay rs = profile.settings.relays.get(idx); ShellySettingsRelay rs = profile.settings.relays.get(idx);
ShellyShortStatusRelay rstatus = relay.relays.get(idx); boolean timer = rs.hasTimer != null || rstatus.hasTimer != null; // Dimmer 1/2 have
boolean timer = rs.hasTimer != null || (rstatus != null && rstatus.hasTimer != null); // Dimmer 1/2 have
// has_timer under
// /status
addChannel(thing, add, rs.ison != null, group, CHANNEL_OUTPUT); addChannel(thing, add, rs.ison != null, group, CHANNEL_OUTPUT);
addChannel(thing, add, rs.name != null, group, CHANNEL_OUTPUT_NAME); addChannel(thing, add, rs.name != null, group, CHANNEL_OUTPUT_NAME);
addChannel(thing, add, rs.autoOn != null, group, CHANNEL_TIMER_AUTOON); addChannel(thing, add, rs.autoOn != null, group, CHANNEL_TIMER_AUTOON);
@ -312,13 +320,13 @@ public class ShellyChannelDefinitions {
} }
// Shelly 1/1PM Addon // Shelly 1/1PM Addon
if (relay.extTemperature != null) { if (profile.settings.extTemperature != null) {
addChannel(thing, add, relay.extTemperature.sensor1 != null, CHGR_SENSOR, CHANNEL_ESENDOR_TEMP1); addChannel(thing, add, profile.settings.extTemperature.sensor1 != null, CHGR_SENSOR, CHANNEL_ESENDOR_TEMP1);
addChannel(thing, add, relay.extTemperature.sensor2 != null, CHGR_SENSOR, CHANNEL_ESENDOR_TEMP2); addChannel(thing, add, profile.settings.extTemperature.sensor2 != null, CHGR_SENSOR, CHANNEL_ESENDOR_TEMP2);
addChannel(thing, add, relay.extTemperature.sensor3 != null, CHGR_SENSOR, CHANNEL_ESENDOR_TEMP3); addChannel(thing, add, profile.settings.extTemperature.sensor3 != null, CHGR_SENSOR, CHANNEL_ESENDOR_TEMP3);
} }
if (relay.extHumidity != null) { if (profile.settings.extHumidity != null) {
addChannel(thing, add, relay.extHumidity.sensor1 != null, CHGR_SENSOR, CHANNEL_ESENDOR_HUMIDITY); addChannel(thing, add, profile.settings.extHumidity.sensor1 != null, CHGR_SENSOR, CHANNEL_ESENDOR_HUMIDITY);
} }
return add; return add;
@ -350,13 +358,15 @@ public class ShellyChannelDefinitions {
if (profile.settings.lights != null) { if (profile.settings.lights != null) {
ShellySettingsRgbwLight light = profile.settings.lights.get(idx); ShellySettingsRgbwLight light = profile.settings.lights.get(idx);
String whiteGroup = profile.isRGBW2 ? group : CHANNEL_GROUP_WHITE_CONTROL;
// Create power channel in color mode and brightness channel in white mode // Create power channel in color mode and brightness channel in white mode
addChannel(thing, add, profile.inColor, group, CHANNEL_LIGHT_POWER); addChannel(thing, add, profile.inColor, group, CHANNEL_LIGHT_POWER);
addChannel(thing, add, !profile.inColor, group, CHANNEL_BRIGHTNESS);
addChannel(thing, add, light.temp != null, CHANNEL_GROUP_WHITE_CONTROL, CHANNEL_COLOR_TEMP);
addChannel(thing, add, light.autoOn != null, group, CHANNEL_TIMER_AUTOON); addChannel(thing, add, light.autoOn != null, group, CHANNEL_TIMER_AUTOON);
addChannel(thing, add, light.autoOff != null, group, CHANNEL_TIMER_AUTOOFF); addChannel(thing, add, light.autoOff != null, group, CHANNEL_TIMER_AUTOOFF);
addChannel(thing, add, status.hasTimer != null, group, CHANNEL_TIMER_ACTIVE); addChannel(thing, add, status.hasTimer != null, group, CHANNEL_TIMER_ACTIVE);
addChannel(thing, add, light.brightness != null, whiteGroup, CHANNEL_BRIGHTNESS);
addChannel(thing, add, light.temp != null, whiteGroup, CHANNEL_COLOR_TEMP);
} }
return add; return add;
} }
@ -444,11 +454,10 @@ public class ShellyChannelDefinitions {
CHANNEL_SENSOR_ILLUM); CHANNEL_SENSOR_ILLUM);
addChannel(thing, newChannels, sdata.flood != null, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_FLOOD); 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.smoke != null, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_FLOOD);
addChannel(thing, newChannels, (profile.settings.externalPower != null) || (sdata.charger != null), CHGR_DEVST, addChannel(thing, newChannels, profile.settings.externalPower != null || sdata.charger != null, CHGR_DEVST,
CHANNEL_DEVST_CHARGER); CHANNEL_DEVST_CHARGER);
addChannel(thing, newChannels, addChannel(thing, newChannels, sdata.motion != null || (sdata.sensor != null && sdata.sensor.motion != null),
sdata.motion != null || ((sdata.sensor != null) && (sdata.sensor.motion != null)), CHANNEL_GROUP_SENSOR, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_MOTION);
CHANNEL_SENSOR_MOTION);
if (sdata.sensor != null) { // DW, Sense or Motion if (sdata.sensor != null) { // DW, Sense or Motion
addChannel(thing, newChannels, sdata.sensor.state != null, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_STATE); // DW/DW2 addChannel(thing, newChannels, sdata.sensor.state != null, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_STATE); // DW/DW2
addChannel(thing, newChannels, sdata.sensor.motionActive != null, CHANNEL_GROUP_SENSOR, // Motion addChannel(thing, newChannels, sdata.sensor.motionActive != null, CHANNEL_GROUP_SENSOR, // Motion
@ -482,13 +491,13 @@ public class ShellyChannelDefinitions {
// TRV // TRV
if (profile.isTRV) { if (profile.isTRV) {
addChannel(thing, newChannels, true, CHANNEL_GROUP_DEV_STATUS, CHANNEL_DEVST_SCHEDULE);
addChannel(thing, newChannels, true, CHANNEL_GROUP_CONTROL, CHANNEL_CONTROL_SETTEMP); addChannel(thing, newChannels, true, CHANNEL_GROUP_CONTROL, CHANNEL_CONTROL_SETTEMP);
addChannel(thing, newChannels, true, CHANNEL_GROUP_CONTROL, CHANNEL_CONTROL_BCONTROL); addChannel(thing, newChannels, true, CHANNEL_GROUP_CONTROL, CHANNEL_CONTROL_BCONTROL);
addChannel(thing, newChannels, true, CHANNEL_GROUP_CONTROL, CHANNEL_CONTROL_BTIMER); addChannel(thing, newChannels, true, CHANNEL_GROUP_CONTROL, CHANNEL_CONTROL_BTIMER);
addChannel(thing, newChannels, true, CHANNEL_GROUP_CONTROL, CHANNEL_CONTROL_POSITION); addChannel(thing, newChannels, true, CHANNEL_GROUP_CONTROL, CHANNEL_CONTROL_POSITION);
addChannel(thing, newChannels, true, CHANNEL_GROUP_CONTROL, CHANNEL_CONTROL_MODE); addChannel(thing, newChannels, true, CHANNEL_GROUP_CONTROL, CHANNEL_CONTROL_MODE);
addChannel(thing, newChannels, true, CHANNEL_GROUP_CONTROL, CHANNEL_CONTROL_PROFILE); addChannel(thing, newChannels, true, CHANNEL_GROUP_CONTROL, CHANNEL_CONTROL_PROFILE);
addChannel(thing, newChannels, true, CHANNEL_GROUP_CONTROL, CHANNEL_CONTROL_SCHEDULE);
addChannel(thing, newChannels, true, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_STATE); // TRV addChannel(thing, newChannels, true, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_STATE); // TRV
} }

View File

@ -87,14 +87,14 @@ public class ShellyUtils {
@Nullable @Nullable
T obj = gson.fromJson(json, classOfT); T obj = gson.fromJson(json, classOfT);
if ((obj == null) && exceptionOnNull) { // new in OH3: fromJson may return null if ((obj == null) && exceptionOnNull) { // new in OH3: fromJson may return null
throw new ShellyApiException(PRE + className + "from JSON: " + json); throw new ShellyApiException(PRE + className + " from JSON: " + json);
} }
return obj; return obj;
} catch (JsonSyntaxException e) { } catch (JsonSyntaxException e) {
throw new ShellyApiException( throw new ShellyApiException(
PRE + className + "from JSON (syntax/format error: " + e.getMessage() + "): " + json, e); PRE + className + "from JSON (syntax/format error: " + e.getMessage() + "): " + json, e);
} catch (RuntimeException e) { } catch (RuntimeException e) {
throw new ShellyApiException(PRE + className + "from JSON: " + json, e); throw new ShellyApiException(PRE + className + " from JSON: " + json, e);
} }
} }
} }
@ -321,7 +321,7 @@ public class ShellyUtils {
} }
public static String buildControlGroupName(ShellyDeviceProfile profile, Integer channelId) { public static String buildControlGroupName(ShellyDeviceProfile profile, Integer channelId) {
return profile.isBulb || profile.isDuo || profile.inColor ? CHANNEL_GROUP_LIGHT_CONTROL return !profile.isRGBW2 || profile.inColor ? CHANNEL_GROUP_LIGHT_CONTROL
: CHANNEL_GROUP_LIGHT_CHANNEL + channelId.toString(); : CHANNEL_GROUP_LIGHT_CHANNEL + channelId.toString();
} }

View File

@ -200,9 +200,9 @@ channel-group-type.shelly.externalSensors.description = Temperatures from extern
channel-type.shelly.outputName.label = Output Name channel-type.shelly.outputName.label = Output Name
channel-type.shelly.outputName.description = Output/Channel Name as configured in the Shelly App channel-type.shelly.outputName.description = Output/Channel Name as configured in the Shelly App
channel-type.shelly.timerAutoOn.label = Auto-ON Timer channel-type.shelly.timerAutoOn.label = Auto-ON Timer
channel-type.shelly.timerAutoOn.description = When the output of the relay is switched on, it will be switched off automatically after n seconds channel-type.shelly.timerAutoOn.description = When the output of the relay is switched off, it will be switched off automatically after n seconds
channel-type.shelly.timerAutoOff.label = Auto-OFF Timer channel-type.shelly.timerAutoOff.label = Auto-OFF Timer
channel-type.shelly.timerAutoOff.description = When the output of the relay is switched off, it will be switched on automatically after n seconds channel-type.shelly.timerAutoOff.description = When the output of the relay is switched on, it will be switched on automatically after n seconds
channel-type.shelly.timerActive.label = Auto ON/OFF timer active channel-type.shelly.timerActive.label = Auto ON/OFF timer active
channel-type.shelly.timerActive.description = ON: A timer is active, OFF: no timer active channel-type.shelly.timerActive.description = ON: A timer is active, OFF: no timer active
channel-type.shelly.temperature.label = Temperature channel-type.shelly.temperature.label = Temperature
@ -388,8 +388,10 @@ channel-type.shelly.controlMode.label = Mode
channel-type.shelly.controlMode.description = Sensor/Control Mode channel-type.shelly.controlMode.description = Sensor/Control Mode
channel-type.shelly.controlMode.state.option.manual = Manual channel-type.shelly.controlMode.state.option.manual = Manual
channel-type.shelly.controlMode.state.option.automatic = Automatic channel-type.shelly.controlMode.state.option.automatic = Automatic
channel-type.shelly.controlProfile.label = Profile channel-type.shelly.controlSchedule.label = Schedule active
channel-type.shelly.controlProfile.description = Selected Profile channel-type.shelly.controlSchedule.description = ON: A scheduled program is active
channel-type.shelly.controlProfile.label = Selected Profile
channel-type.shelly.controlProfile.description = Selected Profile configured in the Shelly App
channel-type.shelly.boostControl.label = Boost Mode channel-type.shelly.boostControl.label = Boost Mode
channel-type.shelly.boostControl.description = ON: Boost mode is activated (overwrites automatic temperature mode) channel-type.shelly.boostControl.description = ON: Boost mode is activated (overwrites automatic temperature mode)
channel-type.shelly.boostTimer.label = Boost Timer channel-type.shelly.boostTimer.label = Boost Timer
@ -466,8 +468,6 @@ channel-type.shelly.sensorError.label = Last Error
channel-type.shelly.sensorError.description = Only valid in case of error channel-type.shelly.sensorError.description = Only valid in case of error
channel-type.shelly.sensorSleepTime.label = Sensor Sleep Time channel-type.shelly.sensorSleepTime.label = Sensor Sleep Time
channel-type.shelly.sensorSleepTime.description = The sensor will not send notifications and will not perform actions until the specified time expires (0=disable) channel-type.shelly.sensorSleepTime.description = The sensor will not send notifications and will not perform actions until the specified time expires (0=disable)
channel-type.shelly.deviceSchedule.label = Schedule active
channel-type.shelly.deviceSchedule.description = ON: A scheduled program is active
channel-type.shelly.system.power.label = Power channel-type.shelly.system.power.label = Power
channel-type.shelly.system.button.label = Event Trigger channel-type.shelly.system.button.label = Event Trigger
channel-type.shelly.system.brightness.label = Brightness channel-type.shelly.system.brightness.label = Brightness

View File

@ -146,12 +146,5 @@
</options> </options>
</state> </state>
</channel-type> </channel-type>
<channel-type id="deviceSchedule" advanced="true">
<item-type>Switch</item-type>
<label>@text/channel-type.shelly.deviceSchedule.label</label>
<description>@text/channel-type.shelly.deviceSchedule.description</description>
<state readOnly="true">
</state>
</channel-type>
</thing:thing-descriptions> </thing:thing-descriptions>

View File

@ -56,7 +56,7 @@
<category>Lightbulb</category> <category>Lightbulb</category>
<channel-groups> <channel-groups>
<channel-group id="control" typeId="duoControl"/> <channel-group id="control" typeId="duoControl"/>
<channel-group id="white" typeId="whiteSettingsSimple"/> <channel-group id="white" typeId="whiteSettings"/>
<channel-group id="meter" typeId="meter"/> <channel-group id="meter" typeId="meter"/>
<channel-group id="device" typeId="deviceStatus"/> <channel-group id="device" typeId="deviceStatus"/>
</channel-groups> </channel-groups>
@ -112,20 +112,12 @@
<channels> <channels>
<channel id="power" typeId="system.power"/> <channel id="power" typeId="system.power"/>
<channel id="mode" typeId="colorMode"/> <channel id="mode" typeId="colorMode"/>
<channel id="autoOn" typeId="timerAutoOn"/>
<channel id="autoOff" typeId="timerAutoOff"/>
<channel id="timerActive" typeId="timerActive"/>
</channels> </channels>
</channel-group-type> </channel-group-type>
<channel-group-type id="duoControl"> <channel-group-type id="duoControl">
<label>@text/channel-group-type.shelly.duoControl.label</label> <label>@text/channel-group-type.shelly.duoControl.label</label>
<description>@text/channel-group-type.shelly.duoControl.description</description> <description>@text/channel-group-type.shelly.duoControl.description</description>
<channels>
<channel id="autoOn" typeId="timerAutoOn"/>
<channel id="autoOff" typeId="timerAutoOff"/>
<channel id="timerActive" typeId="timerActive"/>
</channels>
</channel-group-type> </channel-group-type>
<channel-group-type id="rgbw2ColorControl"> <channel-group-type id="rgbw2ColorControl">
@ -171,32 +163,13 @@
<channel-group-type id="whiteSettings"> <channel-group-type id="whiteSettings">
<label>@text/channel-group-type.shelly.whiteSettings.label</label> <label>@text/channel-group-type.shelly.whiteSettings.label</label>
<description>@text/channel-group-type.shelly.whiteSettings.description</description> <description>@text/channel-group-type.shelly.whiteSettings.description</description>
<channels>
<channel id="brightness" typeId="whiteBrightness"/>
<channel id="temperature" typeId="whiteTemp"/>
</channels>
</channel-group-type>
<channel-group-type id="whiteSettingsSimple">
<label>@text/channel-group-type.shelly.whiteSettingsSimple.label</label>
<description>@text/channel-group-type.shelly.whiteSettingsSimple.description</description>
<channels>
<channel id="brightness" typeId="whiteBrightness"/>
</channels>
</channel-group-type> </channel-group-type>
<channel-group-type id="rgbw2Channel"> <channel-group-type id="rgbw2Channel">
<label>@text/channel-group-type.shelly.rgbw2Channel.label</label> <label>@text/channel-group-type.shelly.rgbw2Channel.label</label>
<description>@text/channel-group-type.shelly.rgbw2Channel.description</description> <description>@text/channel-group-type.shelly.rgbw2Channel.description</description>
<channels>
<channel id="autoOn" typeId="timerAutoOn"/>
<channel id="autoOff" typeId="timerAutoOff"/>
<channel id="timerActive" typeId="timerActive"/>
<channel id="brightness" typeId="whiteBrightness"/>
</channels>
</channel-group-type> </channel-group-type>
<channel-type id="colorMode"> <channel-type id="colorMode">
<item-type>Switch</item-type> <item-type>Switch</item-type>
<label>@text/channel-type.shelly.colorMode.label</label> <label>@text/channel-type.shelly.colorMode.label</label>
@ -298,5 +271,4 @@
</options> </options>
</state> </state>
</channel-type> </channel-type>
</thing:thing-descriptions> </thing:thing-descriptions>

View File

@ -171,10 +171,10 @@
</channel-type> </channel-type>
<channel-type id="controlProfile"> <channel-type id="controlProfile">
<item-type>Number</item-type> <item-type>String</item-type>
<label>@text/channel-type.shelly.controlProfile.label</label> <label>@text/channel-type.shelly.controlProfile.label</label>
<description>@text/channel-type.shelly.controlProfile.description</description> <description>@text/channel-type.shelly.controlProfile.description</description>
<state min="0" max="5" step="1" readOnly="false"></state> <state readOnly="false"></state>
</channel-type> </channel-type>
<channel-type id="targetTemp"> <channel-type id="targetTemp">
@ -194,6 +194,7 @@
<label>@text/channel-type.shelly.boostControl.label</label> <label>@text/channel-type.shelly.boostControl.label</label>
<description>@text/channel-type.shelly.boostControl.description</description> <description>@text/channel-type.shelly.boostControl.description</description>
</channel-type> </channel-type>
<channel-type id="boostTimer"> <channel-type id="boostTimer">
<item-type>Number:Time</item-type> <item-type>Number:Time</item-type>
<label>@text/channel-type.shelly.boostTimer.label</label> <label>@text/channel-type.shelly.boostTimer.label</label>
@ -202,6 +203,13 @@
<state min="0" step="1" pattern="%.0f %unit%" readOnly="false"></state> <state min="0" step="1" pattern="%.0f %unit%" readOnly="false"></state>
</channel-type> </channel-type>
<channel-type id="controlSchedule" advanced="true">
<item-type>Switch</item-type>
<label>@text/channel-type.shelly.controlSchedule.label</label>
<description>@text/channel-type.shelly.controlSchedule.description</description>
<state readOnly="false"></state>
</channel-type>
<channel-group-type id="sensorData"> <channel-group-type id="sensorData">
<label>@text/channel-group-type.shelly.sensorData.label</label> <label>@text/channel-group-type.shelly.sensorData.label</label>