[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 |
| |position |Dimmer |no |Set valve to manual mode (0..100%) disables auto-temp) |
| |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) |
| |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 % |
| |batteryAlert |Switch |yes |Low battery alert |
|device |schedule |Switch |yes |ON: Schedule is active |
### 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_ERROR = "lastError";
public static final String CHANNEL_CONTROL_SETTEMP = "targetTemp"; // Shelly TRV: target temp
public static final String CHANNEL_CONTROL_POSITION = "position"; // Shelly TRV: Valve position
public static final String CHANNEL_CONTROL_MODE = "mode"; // Shelly TRV
public static final String CHANNEL_CONTROL_PROFILE = "profile"; // Shelly TRV
public static final String CHANNEL_CONTROL_BCONTROL = "boost"; // Shelly TRV
public static final String CHANNEL_CONTROL_BTIMER = "boostTimer"; // Shelly TRV
// TRV
public static final String CHANNEL_CONTROL_SETTEMP = "targetTemp";
public static final String CHANNEL_CONTROL_POSITION = "position";
public static final String CHANNEL_CONTROL_MODE = "mode";
public static final String CHANNEL_CONTROL_PROFILE = "selectedProfile";
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
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_VOLTAGE = "supplyVoltage";
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_POWER_DISABLE = "powerLed";

View File

@ -63,7 +63,6 @@ public class ShellyHandlerFactory extends BaseThingHandlerFactory {
private final HttpClient httpClient;
private final ShellyTranslationProvider messages;
private final Shelly1CoapServer coapServer;
private final ShellyThingTable thingTable;
private ShellyBindingConfiguration bindingConfig = new ShellyBindingConfiguration();
private String localIP = "";
@ -82,10 +81,10 @@ public class ShellyHandlerFactory extends BaseThingHandlerFactory {
@Reference HttpClientFactory httpClientFactory, ComponentContext componentContext,
Map<String, Object> configProperties) {
super.activate(componentContext);
messages = translationProvider;
// Save bindingConfig & pass it to all registered listeners
bindingConfig.updateFromProperties(configProperties);
this.messages = translationProvider;
this.thingTable = thingTable;
bindingConfig.updateFromProperties(configProperties);
localIP = bindingConfig.localIP;
if (localIP.isEmpty()) {
localIP = ShellyUtils.getString(networkAddressService.getPrimaryIpv4HostAddress());
@ -101,7 +100,6 @@ public class ShellyHandlerFactory extends BaseThingHandlerFactory {
}
logger.debug("Using OH HTTP port {}", httpPort);
this.thingTable = thingTable;
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)
|| thingType.equals(THING_TYPE_SHELLYRGBW2_COLOR_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(),
thingTypeUID.toString());
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 boolean isInitialized();
public void initialize() throws ShellyApiException;
public void setConfig(String thingName, ShellyThingConfiguration config);
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.ShellySettingsRgbwLight;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -60,6 +61,7 @@ public class ShellyDeviceProfile {
public String hostname = "";
public String name = "";
public String model = "";
public String mode = "";
public boolean discoverable = true;
public boolean auth = false;
@ -154,18 +156,6 @@ public class ShellyDeviceProfile {
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;
return this;
}
@ -244,8 +234,11 @@ public class ShellyDeviceProfile {
return CHANNEL_GROUP_RELAY_CONTROL;
} else if (hasRelays) {
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) {
return numRelays <= 1 ? CHANNEL_GROUP_LIGHT_CONTROL : CHANNEL_GROUP_LIGHT_CONTROL + idx;
return CHANNEL_GROUP_LIGHT_CONTROL;
} else if (isButton) {
return CHANNEL_GROUP_STATUS;
} else if (isSensor) {
@ -337,6 +330,20 @@ public class ShellyDeviceProfile {
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) {
if (version != null) {
// 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.List;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyStatusSensor.ShellyMotionSettings;
import org.openhab.core.thing.CommonTriggerEvents;
@ -265,8 +266,6 @@ public class Shelly1ApiJsonDTO {
public String fw;
public Boolean auth;
public Integer gen;
@SerializedName("coiot") // Shelly Motion Multicast Endpoint
public String coiot;
public Integer longid;
@ -409,10 +408,6 @@ public class Shelly1ApiJsonDTO {
public Boolean overpower;
@SerializedName("is_valid")
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 {
@ -566,22 +561,22 @@ public class Shelly1ApiJsonDTO {
public static class ShellySettingsGlobal {
// https://shelly-api-docs.shelly.cloud/#shelly1pm-settings
public ShellySettingsDevice device;
public ShellySettingsDevice device = new ShellySettingsDevice();
@SerializedName("wifi_ap")
public ShellySettingsWiFiAp wifiAp;
public ShellySettingsWiFiAp wifiAp = new ShellySettingsWiFiAp();
@SerializedName("wifi_sta")
public ShellySettingsWiFiNetwork wifiSta;
public ShellySettingsWiFiNetwork wifiSta = new ShellySettingsWiFiNetwork();
@SerializedName("wifi_sta1")
public ShellySettingsWiFiNetwork wifiSta1;
public ShellySettingsWiFiNetwork wifiSta1 = new ShellySettingsWiFiNetwork();
@SerializedName("wifirecovery_reboot_enabled")
public Boolean wifiRecoveryReboot; // FW 1.10+
@SerializedName("ap_roaming")
public ShellyApRoaming apRoaming; // FW 1.10+
public ShellySettingsMqtt mqtt; // not used for now
public ShellySettingsSntp sntp; // not used for now
public ShellySettingsCoiot coiot; // Firmware 1.6+
public ShellySettingsLogin login;
public ShellySettingsMqtt mqtt = new ShellySettingsMqtt();
public ShellySettingsSntp sntp = new ShellySettingsSntp();
public ShellySettingsCoiot coiot = new ShellySettingsCoiot();
public ShellySettingsLogin login = new ShellySettingsLogin();
@SerializedName("pin_code")
public String pinCode;
@SerializedName("coiot_execute_enable")
@ -590,8 +585,8 @@ public class Shelly1ApiJsonDTO {
public Boolean discoverable; // FW 1.6+
public String fw;
@SerializedName("build_info")
public ShellySettingsBuildInfo buildInfo;
public ShellyStatusCloud cloud;
public ShellySettingsBuildInfo buildInfo = new ShellySettingsBuildInfo();
public ShellyStatusCloud cloud = new ShellyStatusCloud();
@SerializedName("sleep_mode")
public ShellySensorSleepMode sleepMode; // FW 1.6
@SerializedName("external_power")
@ -610,19 +605,22 @@ public class Shelly1ApiJsonDTO {
@SerializedName("max_power")
public Double maxPower;
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
@SerializedName("supply_voltage")
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")
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
@SerializedName("wifi_sta")
public ShellySettingsWiFiNetwork wifiSta; // WiFi client configuration. See /settings/sta for details
public ShellyStatusCloud cloud;
public ShellyStatusMqtt mqtt;
public ShellySettingsWiFiNetwork wifiSta = new ShellySettingsWiFiNetwork();
public ShellyStatusCloud cloud = new ShellyStatusCloud();
public ShellyStatusMqtt mqtt = new ShellyStatusMqtt();
public String time;
public Integer serial = -1;
@ -717,21 +715,23 @@ public class Shelly1ApiJsonDTO {
public Integer cfgChangedCount; // FW 1.8
@SerializedName("actions_stats")
public ShellyActionsStats astats;
public ArrayList<ShellySettingsRelay> relays;
public Double voltage; // Shelly 2.5
public ArrayList<ShellySettingsRoller> rollers;
public Integer input; // RGBW2 has no JSON array
public ArrayList<ShellyInputState> inputs;
public ArrayList<ShellySettingsLight> lights;
public ArrayList<ShellySettingsRelay> relays;
public ArrayList<ShellyRollerStatus> rollers;
public ArrayList<ShellyShortLightStatus> dimmers;
public ArrayList<ShellyInputState> inputs;
public ArrayList<ShellySettingsMeter> meters;
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
public ShellySensorTmp tmp; // Shelly 1PM
public Double temperature = SHELLY_API_INVTEMP; // Shelly 2.5
public ShellySensorTmp tmp = new ShellySensorTmp(); // Shelly 1PM
public Double temperature; // Shelly 2.5
public Boolean overtemperature;
// Shelly Dimmer only
@ -742,7 +742,8 @@ public class Shelly1ApiJsonDTO {
public Boolean calibrated;
public ArrayList<ShellyThermnostat> thermostats;
public ShellySettingsUpdate update;
public ShellySettingsUpdate update = new ShellySettingsUpdate();
@SerializedName("ram_total")
public Long ramTotal;
@SerializedName("ram_free")

View File

@ -106,8 +106,9 @@ public class Shelly1CoIoTVersion2 extends Shelly1CoIoTProtocol implements Shelly
break;
case "3117": // S, mode, 0-5 (0=disabled)
value = getDouble(s.value).intValue();
updateChannel(updates, CHANNEL_GROUP_CONTROL, CHANNEL_CONTROL_PROFILE, getDecimal(value));
updateChannel(updates, CHANNEL_GROUP_DEV_STATUS, CHANNEL_DEVST_SCHEDULE, getOnOff(value > 0));
updateChannel(updates, CHANNEL_GROUP_CONTROL, CHANNEL_CONTROL_PROFILE,
getStringType(profile.getValueProfile((int) value)));
updateChannel(updates, CHANNEL_GROUP_CONTROL, CHANNEL_CONTROL_SCHEDULE, getOnOff(value > 0));
break;
case "3118": // Valve 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.ShellyHttpClient;
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.ShellySenseKeyCode;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellySettingsDevice;
@ -151,8 +152,9 @@ public class Shelly1HttpApi extends ShellyHttpClient implements ShellyApiInterfa
try {
json = httpRequest(SHELLY_URL_STATUS);
// Dimmer2 returns invalid json type for loaderror :-(
json = getString(json.replace("\"loaderror\":0,", "\"loaderror\":false,"));
json = getString(json.replace("\"loaderror\":1,", "\"loaderror\":true,"));
json = json.replace("\"loaderror\":0,", "\"loaderror\":false,")
.replace("\"loaderror\":1,", "\"loaderror\":true,")
.replace("\"tmp\":{\"value\": \"null\",", "\"tmp\":{\"value\": null,");
ShellySettingsStatus status = fromJson(gson, json, ShellySettingsStatus.class);
status.json = json;
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.ShellySettingsDevice;
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.Shelly1CoapJSonDTO;
import org.openhab.binding.shelly.internal.api1.Shelly1CoapServer;
@ -87,14 +88,14 @@ public class ShellyBaseHandler extends BaseThingHandler
protected final ShellyApiInterface api;
private final HttpClient httpClient;
protected ShellyBindingConfiguration bindingConfig;
private ShellyBindingConfiguration bindingConfig;
protected ShellyThingConfiguration config = new ShellyThingConfiguration();
protected ShellyDeviceProfile profile = new ShellyDeviceProfile(); // init empty profile to avoid NPE
protected ShellyDeviceStats stats = new ShellyDeviceStats();
private final Shelly1CoapHandler coap;
public boolean autoCoIoT = false;
public final ShellyTranslationProvider messages;
private final ShellyTranslationProvider messages;
protected boolean stopping = false;
private boolean channelsCreated = false;
@ -108,7 +109,7 @@ public class ShellyBaseHandler extends BaseThingHandler
// delay before enabling channel
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 localPort = "";
@ -225,7 +226,7 @@ public class ShellyBaseHandler extends BaseThingHandler
*
* @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
stopping = false;
refreshSettings = false;
@ -251,7 +252,7 @@ public class ShellyBaseHandler extends BaseThingHandler
// Initialize API access, exceptions will be catched by initialize()
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");
return false;
}
@ -274,11 +275,26 @@ public class ShellyBaseHandler extends BaseThingHandler
// New Shelly devices might use a different endpoint for the CoAP listener
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.status = api.getStatus();
tmpPrf.updateFromStatus(tmpPrf.status);
showThingConfig(tmpPrf);
// update thing properties
checkVersion(tmpPrf, tmpPrf.status);
if (config.eventsCoIoT && (tmpPrf.settings.coiot != null) && (tmpPrf.settings.coiot.enabled != null)) {
@ -385,14 +401,39 @@ public class ShellyBaseHandler extends BaseThingHandler
: 0;
api.setSleepTime(value);
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:
logger.debug("{}: Select profile {}", thingName, command);
int profile = (int) getNumber(command);
if (profile < 0 || profile > 5) {
logger.warn("{}: Invalid profile Id {} requested", thingName, profile);
break;
int id = -1;
if (command instanceof Number) {
id = (int) getNumber(command);
} 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;
case CHANNEL_CONTROL_MODE:
logger.debug("{}: Set mode to {}", thingName, command);
@ -567,9 +608,10 @@ public class ShellyBaseHandler extends BaseThingHandler
updateStatus(ThingStatus.ONLINE);
// 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);
}
// Restart watchdog when status update was successful (no exception)
restartWatchdog();
}
@ -695,7 +737,7 @@ public class ShellyBaseHandler extends BaseThingHandler
if (force || !lastAlarm.equals(event)
|| (lastAlarm.equals(event) && now() > stats.lastAlarmTs + HEALTH_CHECK_INTERVAL_SEC)) {
switch (event) {
switch (event.toUpperCase()) {
case "":
case "0": // DW2 1.8
case SHELLY_WAKEUPT_SENSOR:
@ -957,7 +999,7 @@ public class ShellyBaseHandler extends BaseThingHandler
* @param thingType thing type acc. to the xml definition
* @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);
if (!thingTypeUID.equals(THING_TYPE_SHELLYUNKNOWN)) {
logger.debug("{}: Changing thing type to {}", getThing().getLabel(), thingTypeUID);
@ -1034,7 +1076,7 @@ public class ShellyBaseHandler extends BaseThingHandler
int idx = 0;
boolean multiInput = status.inputs.size() >= 2; // device has multiple SW (inputs)
for (ShellyInputState input : status.inputs) {
String group = profile.getControlGroup(idx);
String group = profile.getInputGroup(idx);
String suffix = multiInput ? profile.getInputSuffix(idx) : "";
if (!areChannelsCreated()) {

View File

@ -20,8 +20,10 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.shelly.internal.api.ShellyApiException;
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.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.ShellyStatusSensor;
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.core.library.types.OnOffType;
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.SIUnits;
import org.openhab.core.library.unit.Units;
@ -86,6 +89,83 @@ public class ShellyComponents {
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
*
@ -111,8 +191,12 @@ public class ShellyComponents {
int m = 0;
if (!profile.isEMeter) {
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
// correctly in white mode
// correctly in white mode
String groupName = profile.getMeterGroup(m);
if (!thingHandler.areChannelsCreated()) {
// skip for Shelly Bulb: JSON has a meter, but values don't get updated
@ -302,7 +386,7 @@ public class ShellyComponents {
// Shelly TRV
ShellyThermnostat t = status.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);
updated |= thingHandler.updateChannel(CHANNEL_GROUP_CONTROL, CHANNEL_CONTROL_BCONTROL,
getOnOff(getInteger(t.boostMinutes) > 0));
@ -310,10 +394,11 @@ public class ShellyComponents {
toQuantityType((double) bminutes, DIGITS_NONE, Units.MINUTE));
updated |= thingHandler.updateChannel(CHANNEL_GROUP_CONTROL, CHANNEL_CONTROL_MODE,
getStringType(getBool(t.targetTemp.enabled) ? SHELLY_TRV_MODE_AUTO : SHELLY_TRV_MODE_MANUAL));
updated |= thingHandler.updateChannel(CHANNEL_GROUP_CONTROL, CHANNEL_CONTROL_PROFILE,
getDecimal(getBool(t.schedule) ? t.profile + 1 : 0));
updated |= thingHandler.updateChannel(CHANNEL_GROUP_DEV_STATUS, CHANNEL_DEVST_SCHEDULE,
int pid = getBool(t.schedule) ? getInteger(t.profile) : 0;
updated |= thingHandler.updateChannel(CHANNEL_GROUP_CONTROL, CHANNEL_CONTROL_SCHEDULE,
getOnOff(t.schedule));
updated |= thingHandler.updateChannel(CHANNEL_GROUP_CONTROL, CHANNEL_CONTROL_PROFILE,
getStringType(profile.getValueProfile(pid)));
if (t.tmp != null) {
Double temp = convertToC(t.tmp.value, getString(t.tmp.units));
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) {
// Channel control/timer
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_TIMER_AUTOON,
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_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.ShellySettingsDimmer;
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.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.config.ShellyBindingConfiguration;
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.PercentType;
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.unit.SIUnits;
import org.openhab.core.library.unit.Units;
import org.openhab.core.thing.ChannelUID;
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))
|| (command == UpDownType.DOWN
&& 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);
requestUpdates(1, false);
return;
@ -305,7 +300,7 @@ public class ShellyRelayHandler extends ShellyBaseHandler {
/**
* 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()) {
updateChannelDefinitions(ShellyChannelDefinitions.createRelayChannels(getThing(), profile, relay, idx));
}
@ -350,93 +345,23 @@ public class ShellyRelayHandler extends ShellyBaseHandler {
}
}
if (profile.hasRelays && !profile.isRoller && !profile.isDimmer) {
logger.trace("{}: Updating {} relay(s)", thingName, profile.numRelays);
int i = 0;
ShellyStatusRelay rstatus = api.getRelayStatus(i);
for (ShellyShortStatusRelay relay : rstatus.relays) {
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));
}
}
if (profile.hasRelays && !profile.isRoller) {
logger.trace("{}: Updating {} relay(s)", thingName, profile.numRelays);
for (int i = 0; i < status.relays.size(); i++) {
createRelayChannels(status.relays.get(i), i);
updated |= ShellyComponents.updateRelay(this, status, i);
i++;
}
} else if (profile.hasRelays && profile.isRoller && (status.rollers != null)) {
} else {
// Check for Relay in Roller Mode
logger.trace("{}: Updating {} rollers", thingName, profile.numRollers);
int i = 0;
for (ShellySettingsRoller roller : status.rollers) {
if (roller.isValid) {
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++;
}
for (int i = 0; i < profile.numRollers; i++) {
ShellyRollerStatus roller = status.rollers.get(i);
createRollerChannels(roller);
updated |= ShellyComponents.updateRoller(this, roller, i);
}
}
return updated;
}

View File

@ -212,11 +212,11 @@ public class ShellyManagerPage {
ShellyDeviceStats stats = th.getStats();
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();
if (p.getValue() != null) {
String value = p.getValue().toString();
properties.put(key, value);
Object o = p.getValue();
if (o != null) {
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.ShellySettingsStatus;
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.ShellyStatusRelay;
import org.openhab.binding.shelly.internal.api1.Shelly1ApiJsonDTO.ShellyStatusSensor;
import org.openhab.binding.shelly.internal.handler.ShellyThingInterface;
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_ROLLER = CHANNEL_GROUP_ROL_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_METER = CHANNEL_GROUP_METER;
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_UPDATE, "updateAvailable", 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
.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_EVENT_TRIGGER, "system:button", "system:button"))
// RGBW2
.add(new ShellyChannel(m, CHGR_LIGHT, CHANNEL_LIGHT_POWER, "system:power", ITEMT_SWITCH))
// Bulb/Duo/Vintage
.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, 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_AUTOOFF, "timerAutoOff", ITEMT_TIME))
.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
.add(new ShellyChannel(m, CHGR_METER, CHANNEL_METER_CURRENTWATTS, "meterWatts", ITEMT_POWER))
.add(new ShellyChannel(m, CHGR_METER, CHANNEL_METER_TOTALKWH, "meterTotal", ITEMT_ENERGY))
@ -217,7 +227,8 @@ public class ShellyChannelDefinitions {
// TRV
.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_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_BCONTROL, "boostControl", ITEMT_SWITCH))
.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
*/
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<>();
String group = profile.getControlGroup(idx);
if (profile.settings.relays != null) {
ShellySettingsRelay rs = profile.settings.relays.get(idx);
ShellyShortStatusRelay rstatus = relay.relays.get(idx);
boolean timer = rs.hasTimer != null || (rstatus != null && rstatus.hasTimer != null); // Dimmer 1/2 have
// has_timer under
// /status
boolean timer = rs.hasTimer != null || rstatus.hasTimer != null; // Dimmer 1/2 have
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);
@ -312,13 +320,13 @@ public class ShellyChannelDefinitions {
}
// 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 (profile.settings.extTemperature != null) {
addChannel(thing, add, profile.settings.extTemperature.sensor1 != null, CHGR_SENSOR, CHANNEL_ESENDOR_TEMP1);
addChannel(thing, add, profile.settings.extTemperature.sensor2 != null, CHGR_SENSOR, CHANNEL_ESENDOR_TEMP2);
addChannel(thing, add, profile.settings.extTemperature.sensor3 != null, CHGR_SENSOR, CHANNEL_ESENDOR_TEMP3);
}
if (relay.extHumidity != null) {
addChannel(thing, add, relay.extHumidity.sensor1 != null, CHGR_SENSOR, CHANNEL_ESENDOR_HUMIDITY);
if (profile.settings.extHumidity != null) {
addChannel(thing, add, profile.settings.extHumidity.sensor1 != null, CHGR_SENSOR, CHANNEL_ESENDOR_HUMIDITY);
}
return add;
@ -350,13 +358,15 @@ public class ShellyChannelDefinitions {
if (profile.settings.lights != null) {
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
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.autoOff != null, group, CHANNEL_TIMER_AUTOOFF);
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;
}
@ -444,11 +454,10 @@ public class ShellyChannelDefinitions {
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, (profile.settings.externalPower != null) || (sdata.charger != null), CHGR_DEVST,
addChannel(thing, newChannels, profile.settings.externalPower != null || sdata.charger != null, CHGR_DEVST,
CHANNEL_DEVST_CHARGER);
addChannel(thing, newChannels,
sdata.motion != null || ((sdata.sensor != null) && (sdata.sensor.motion != null)), CHANNEL_GROUP_SENSOR,
CHANNEL_SENSOR_MOTION);
addChannel(thing, newChannels, sdata.motion != null || (sdata.sensor != null && sdata.sensor.motion != null),
CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_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.motionActive != null, CHANNEL_GROUP_SENSOR, // Motion
@ -482,13 +491,13 @@ public class ShellyChannelDefinitions {
// TRV
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_BCONTROL);
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_MODE);
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
}

View File

@ -87,14 +87,14 @@ public class ShellyUtils {
@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);
throw new ShellyApiException(PRE + className + " from JSON: " + json);
}
return obj;
} catch (JsonSyntaxException e) {
throw new ShellyApiException(
PRE + className + "from JSON (syntax/format error: " + e.getMessage() + "): " + json, 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) {
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();
}

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.description = Output/Channel Name as configured in the Shelly App
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.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.description = ON: A timer is active, OFF: no timer active
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.state.option.manual = Manual
channel-type.shelly.controlMode.state.option.automatic = Automatic
channel-type.shelly.controlProfile.label = Profile
channel-type.shelly.controlProfile.description = Selected Profile
channel-type.shelly.controlSchedule.label = Schedule active
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.description = ON: Boost mode is activated (overwrites automatic temperature mode)
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.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.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.button.label = Event Trigger
channel-type.shelly.system.brightness.label = Brightness

View File

@ -146,12 +146,5 @@
</options>
</state>
</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>

View File

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

View File

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