[shelly] Vibration event for DW2 (#10618)

* Adapted to new checkstyle

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

* stylecheck cnages; fix CoIoT update for vibration, url encode user+pw on
"set login credentials"

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

* #10617 Vibration event added for DW2

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

* brackets

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

* some unused stuff/code removed

Signed-off-by: Markus Michels <markus7017@gmail.com>
This commit is contained in:
Markus Michels 2021-05-09 20:21:35 +02:00 committed by GitHub
parent 892221ccad
commit a2084e746a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 119 additions and 177 deletions

View File

@ -17,6 +17,7 @@ The binding gets in sync with the next status refresh.
Refer to [Advanced Users](doc/AdvancedUsers.md) for more information on openHAB Shelly integration, e.g. firmware update, network communication or log filtering.
Also check out the [Shelly Manager](doc/ShellyManager.md), which
- provides detailed information on your Shellys
- helps to diagnose WiFi issues or device instabilities
- includes some common actions and
@ -276,6 +277,7 @@ Check the channel definitions for the various devices to see if the device suppo
You could use the Shelly App to set the timing for those events.
If you want to use those events triggering a rule:
- If a physical switch is connected to the Shelly use the input channel(`input` or `input1`/`input2`) to trigger a rule
- For a momentary button use the `button` trigger channel as trigger, channels `lastEvent` and `eventCount` will provide details on the event
@ -309,7 +311,7 @@ A new alarm will be triggered on a new condition or every 5 minutes if the condi
|BATTERY |Device reported an update to the battery status. |
|TEMP_UNDER |Below "temperature under" threshold |
|TEMP_OVER |Above "temperature over" threshold |
|VIBRATION |A vibration/tamper was detected (DW2 only) |
Refer to section [Full Example:shelly.rules](#shelly-rules) for examples how to catch alarm triggers in openHAB rules
@ -803,6 +805,7 @@ You can define 2 items (1 Switch, 1 Number) mapping to the same channel, see exa
| |lastError |String |yes |Last device error. |
|battery |batteryLevel |Number |yes |Battery Level in % |
| |lowBattery |Switch |yes |Low battery alert (< 20%) |
|device |alarm |Trigger |yes |Will receive trigger VIBRATION if DW2 detects vibration |
### Shelly Motion (thing-type: shellymotion)
@ -1090,8 +1093,8 @@ when
Channel "shelly:shelly25-roller:XXXXXX:device#alarm" triggered
then
if (receivedEvent !== null) { // A (channel) event triggered the rule
eventSource = receivedEvent.getChannel().asString
eventType = receivedEvent.getEvent()
eventSource = triggeredChannel
eventType = receivedEvent
...
}
end

View File

@ -191,8 +191,6 @@ public class ShellyBindingConstants {
public static final String PROPERTY_UPDATE_NEW_VERS = "updateNewVersion";
public static final String PROPERTY_COAP_DESCR = "coapDeviceDescr";
public static final String PROPERTY_COAP_VERSION = "coapVersion";
public static final String PROPERTY_STATS_TIMEOUTS = "statsTimeoutErrors";
public static final String PROPERTY_STATS_TRECOVERED = "statsTimeoutsRecovered";
public static final String PROPERTY_COIOTAUTO = "coiotAutoEnable";
// Relay
@ -337,6 +335,7 @@ public class ShellyBindingConstants {
public static final String ALARM_TYPE_LOADERR = "LOAD_ERROR";
public static final String ALARM_TYPE_SENSOR_ERROR = "SENSOR_ERROR";
public static final String ALARM_TYPE_LOW_BATTERY = "LOW_BATTERY";
public static final String EVENT_TYPE_VIBRATION = "VIBRATION";
// Event types
public static final String EVENT_TYPE_RELAY = "relay";
@ -364,4 +363,5 @@ public class ShellyBindingConstants {
public static final int UPDATE_MIN_DELAY = 15;// update every x triggers or when a key was pressed
public static final int UPDATE_SETTINGS_INTERVAL_SECONDS = 60; // check for updates every x sec
public static final int HEALTH_CHECK_INTERVAL_SEC = 300; // Health check interval, 5min
public static final int VIBRATION_FILTER_SEC = 5; // Absore duplicate vibration events for xx sec
}

View File

@ -34,7 +34,7 @@ public class ShellyApiException extends Exception {
private static final long serialVersionUID = -5809459454769761821L;
private ShellyApiResult apiResult = new ShellyApiResult();
private static String NONE = "none";
private static final String NONE = "none";
public ShellyApiException(Exception exception) {
super(exception);
@ -117,7 +117,7 @@ public class ShellyApiException extends Exception {
}
private boolean isEmpty() {
return nonNullString(super.getMessage()).equals(NONE);
return NONE.equals(nonNullString(super.getMessage()));
}
private static String nonNullString(@Nullable String s) {

View File

@ -379,9 +379,6 @@ public class ShellyApiJsonDTO {
public String pushLongUrl; // to access when roller stopped
@SerializedName("shortpush_url")
public String pushShortUrl; // to access when roller stopped
public Boolean schedule;
// ArrayList<ShellySettingsScheduleRules> schedule_rules;
}
public static class ShellySettingsDimmer {
@ -442,8 +439,6 @@ public class ShellyApiJsonDTO {
public Boolean isValid;
@SerializedName("safety_switch")
public Boolean safetySwitch;
public Boolean schedule;
// ArrayList<ShellySettingsScheduleRules> schedule_rules; // not used for now
@SerializedName("obstacle_mode")
public String obstaclMode; // SHELLY_OBSTMODE_
@SerializedName("obstacle_action")
@ -468,7 +463,8 @@ public class ShellyApiJsonDTO {
public Boolean ison; // true: output is ON
public Integer brightness;
public Integer transition;
public String default_state;
@SerializedName("default_state")
public String defaultState;
@SerializedName("auto_on")
public Double autoOn; // Automatic flip back timer, seconds. Will engage after turning Shelly1 OFF.
@SerializedName("auto_off")
@ -565,20 +561,14 @@ public class ShellyApiJsonDTO {
public ShellySensorSleepMode sleepMode; // FW 1.6
@SerializedName("external_power")
public Integer externalPower; // H&T FW 1.6, seems to be the same like charger for the Sense
public Boolean debug_enable; // FW 1.10+
@SerializedName("debug_enable") // FW 1.10+
public Boolean debugEnable;
public String timezone;
public Double lat;
public Double lng;
public Boolean tzautodetect;
public String time;
// @SerializedName("tz_utc_offset")
// public Integer tzUTCOoffset; // FW 1.6+
// @SerializedName("tz_dst")
// public Boolean tzDdst; // FW 1.6+
// @SerializedName("tz_dst_auto")
// public Boolean tzDstAuto; // FW 1.6+
// public Long unixtime; // FW 1.6+
public ShellySettingsHwInfo hwinfo;
public String mode;
@ -619,15 +609,6 @@ public class ShellyApiJsonDTO {
@SerializedName("vibration_url")
public String vibrationUrl; // URL reports when DW detects vibration FW 1.6.5+
// @SerializedName("tilt_enabled")
// public Boolean tiltEnabled; // Whether tilt monitoring is activated
// @SerializedName("tilt_calibrated")
// public Boolean tiltCalibrated; // Whether calibration data is valid
// @SerializedName("vibration_enabled")
// public Boolean vibrationEnabled; // Whether vibration monitoring is activated
// @SerializedName("reverse_open_close")
// public Boolean reverseOpenClose; // Whether to reverse which position the sensor consideres "open"
// Gas FW 1.7
@SerializedName("set_volume")
public Integer volume; // Speaker volume for alarm
@ -700,8 +681,6 @@ public class ShellyApiJsonDTO {
public Integer input; // RGBW2 has no JSON array
public ArrayList<ShellyInputState> inputs;
public ArrayList<ShellySettingsLight> lights;
// @SerializedName("night_mode") // FW 1.5.7+
// public ShellySettingsNightMode nightMode;
public ArrayList<ShellyShortLightStatus> dimmers;
public ArrayList<ShellySettingsMeter> meters;
public ArrayList<ShellySettingsEMeter> emeters;
@ -735,27 +714,6 @@ public class ShellyApiJsonDTO {
public static class ShellySettingsInput {
@SerializedName("btn_type")
public String btnType;
// attributes not yet processed
// public String name;
// @SerializedName("btn_reverse")
// public Integer btnReverse;
// @SerializedName("btn_on_url")
// public String btnOnUrl;
// @SerializedName("btn_off_url")
// public String btnOffUrl;
// @SerializedName("shortpush_url")
// public String shortpushUrl;
// @SerializedName("longpush_url")
// public String longpushUrl;
// @SerializedName("double_shortpush_url")
// public String doubleShortpushUrl;
// @SerializedName("triple_shortpush_url")
// public String tripleShortpushUrl;
// @SerializedName("shortpush_longpush_url")
// public String shortpushLongpushUrl;
// @SerializedName("longpush_shortpush_url")
// public String longpushShortpushUrl;
}
public static class ShellyControlRelay {
@ -791,12 +749,6 @@ public class ShellyApiJsonDTO {
public Boolean ison; // Whether output channel is on or off
public String mode; // color or white - valid only for Bulb and RGBW2 even Dimmer returns it also
public Integer brightness; // brightness: 0.100%
// @SerializedName("has_timer")
// public Boolean hasTimer; // Whether a timer is currently armed for this channel
// @SerializedName("timer_remaining")
// public Integer timerRemaining;
// public Integer wgite;
// public Integer temp; // light temp
}
public static class ShellyStatusRelay {
@ -804,10 +756,7 @@ public class ShellyApiJsonDTO {
@SerializedName("wifi_sta")
public ShellySettingsWiFiNetwork wifiSta; // WiFi status
// public ShellyStatusCloud cloud; // Cloud status
// public ShellyStatusMqtt mqtt; // mqtt status
public ShellySettingsCoiot coiot; // Firmware 1.6+
// public String time; // current time
public Integer serial;
public String mac; // MAC
public ArrayList<ShellyShortStatusRelay> relays; // relay status
@ -821,30 +770,11 @@ public class ShellyApiJsonDTO {
public Double temperature; // device temp acc. on the selected temp unit
public ShellyStatusSensor.ShellySensorTmp tmp;
@SerializedName("has_update")
public Boolean hasUpdate; // If a newer firmware version is available
public ShellySettingsUpdate update; // /status/firmware value
@SerializedName("ram_total")
public Integer ramTotal; // Total and available amount of system memory in bytes
@SerializedName("ram_free")
public Integer ramFree;
@SerializedName("fs_size")
public Integer fsSize;
@SerializedName("fs_free")
public Integer fsFree; // Total and available amount of file system space in bytes
public Integer uptime; // econds elapsed since boot
}
public static class ShellyStatusDimmer {
@SerializedName("wifi_sta")
public ShellySettingsWiFiNetwork wifiSta; // WiFi status
// public ShellyStatusCloud cloud; // Cloud status
// public ShellyStatusMqtt mqtt; // mqtt status
public String time; // current time
public Integer serial;
public String mac; // MAC
public ArrayList<ShellyShortLightStatus> lights; // relay status
public ArrayList<ShellySettingsMeter> meters; // current meter value
@ -853,21 +783,6 @@ public class ShellyApiJsonDTO {
public Boolean loaderror;
public Boolean overload;
@SerializedName("has_update")
public Boolean hasUpdate; // If a newer firmware version is available
public ShellySettingsUpdate update; // /status/firmware value
@SerializedName("ram_total")
public Integer ramTotal; // Total and available amount of system memory in
// bytes
@SerializedName("ram_free")
public Integer ramFree;
@SerializedName("fs_size")
public Integer fsSize;
@SerializedName("fs_free")
public Integer fsFree; // Total and available amount of file system space in bytes
public Integer uptime; // seconds elapsed since boot
}
public static class ShellyControlRoller {

View File

@ -45,7 +45,7 @@ import com.google.gson.Gson;
@NonNullByDefault
public class ShellyDeviceProfile {
private final Logger logger = LoggerFactory.getLogger(ShellyDeviceProfile.class);
private final static Pattern VERSION_PATTERN = Pattern.compile("v\\d+\\.\\d+\\.\\d+(-[a-z0-9]*)?");
private static final Pattern VERSION_PATTERN = Pattern.compile("v\\d+\\.\\d+\\.\\d+(-[a-z0-9]*)?");
public boolean initialized = false; // true when initialized
@ -131,6 +131,7 @@ public class ShellyDeviceProfile {
extFeatures = version.compare(fwVersion, SHELLY_API_FW_110) >= 0;
discoverable = (settings.discoverable == null) || settings.discoverable;
isRoller = mode.equalsIgnoreCase(SHELLY_MODE_ROLLER);
inColor = isLight && mode.equalsIgnoreCase(SHELLY_MODE_COLOR);
numRelays = !isLight ? getInteger(settings.device.numOutputs) : 0;
@ -184,8 +185,6 @@ public class ShellyDeviceProfile {
}
isDimmer = deviceType.equalsIgnoreCase(SHELLYDT_DIMMER) || deviceType.equalsIgnoreCase(SHELLYDT_DIMMER2);
isRoller = mode.equalsIgnoreCase(SHELLY_MODE_ROLLER);
isBulb = thingType.equals(THING_TYPE_SHELLYBULB_STR);
isDuo = thingType.equals(THING_TYPE_SHELLYDUO_STR) || thingType.equals(THING_TYPE_SHELLYVINTAGE_STR)
|| thingType.equals(THING_TYPE_SHELLYDUORGBW_STR);

View File

@ -246,8 +246,8 @@ public class ShellyHttpApi {
}
public ShellySettingsLogin setLoginCredentials(String user, String password) throws ShellyApiException {
return callApi(SHELLY_URL_SETTINGS + "/login?enabled=yes&username=" + user + "&password=" + password,
ShellySettingsLogin.class);
return callApi(SHELLY_URL_SETTINGS + "/login?enabled=yes&username=" + urlEncode(user) + "&password="
+ urlEncode(password), ShellySettingsLogin.class);
}
public String getCoIoTDescription() throws ShellyApiException {

View File

@ -76,9 +76,6 @@ public class ShellyCoIoTProtocol {
protected boolean handleStatusUpdate(List<CoIotSensor> sensorUpdates, CoIotDescrSen sen, CoIotSensor s,
Map<String, State> updates, ShellyColorUtils col) {
// Process status information and convert into channel updates
// Integer rIndex = Integer.parseInt(sen.links) + 1;
// String rGroup = getProfile().numRelays <= 1 ? CHANNEL_GROUP_RELAY_CONTROL
// : CHANNEL_GROUP_RELAY_CONTROL + rIndex;
int rIndex = getIdFromBlk(sen);
String rGroup = getProfile().numRelays <= 1 ? CHANNEL_GROUP_RELAY_CONTROL
: CHANNEL_GROUP_RELAY_CONTROL + rIndex;
@ -130,8 +127,10 @@ public class ShellyCoIoTProtocol {
s.value == 1 ? OnOffType.ON : OnOffType.OFF);
break;
case "vibration": // DW with FW1.6.5+
updateChannel(updates, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_VIBRATION,
s.value == 1 ? OnOffType.ON : OnOffType.OFF);
if (s.value == 1) {
thingHandler.triggerChannel(CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_ALARM_STATE,
EVENT_TYPE_VIBRATION);
}
break;
case "luminositylevel": // +
updateChannel(updates, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_ILLUM, getStringType(s.valueStr));
@ -166,7 +165,7 @@ public class ShellyCoIoTProtocol {
updateChannel(updates, CHANNEL_GROUP_COLOR_CONTROL, CHANNEL_COLOR_GAIN,
ShellyColorUtils.toPercent((int) s.value, SHELLY_MIN_GAIN, SHELLY_MAX_GAIN));
break;
case "sensorerror": // +
case "sensorerror":
updateChannel(updates, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_ERROR, getStringType(s.valueStr));
break;
default:

View File

@ -177,8 +177,15 @@ public class ShellyCoIoTVersion1 extends ShellyCoIoTProtocol implements ShellyCo
toQuantityType(s.value, DIGITS_NONE, Units.DEGREE_ANGLE));
break;
case "vibration": // DW with FW1.6.5+
updateChannel(updates, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_VIBRATION,
s.value == 1 ? OnOffType.ON : OnOffType.OFF);
if (profile.isMotion) {
// handle as status
updateChannel(updates, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_VIBRATION,
s.value == 1 ? OnOffType.ON : OnOffType.OFF);
} else if (s.value == 1) {
// handle as event
thingHandler.triggerChannel(CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_ALARM_STATE,
EVENT_TYPE_VIBRATION);
}
break;
case "temp": // Shelly Bulb
case "colortemperature": // Shelly Duo

View File

@ -103,7 +103,10 @@ public class ShellyCoIoTVersion2 extends ShellyCoIoTProtocol implements ShellyCo
// skip, could check against thing mode...
break;
case "1101": // S, output, 0/1
case "1101": // relay_0: output, 0/1
case "1201": // relay_1: output, 0/1
case "1301": // relay_2: output, 0/1
case "1401": // relay_3: output, 0/1
updatePower(profile, updates, rIndex, sen, s, sensorUpdates);
break;
case "1102": // roler_0: S, roller, open/close/stop -> roller state
@ -316,8 +319,14 @@ public class ShellyCoIoTVersion2 extends ShellyCoIoTProtocol implements ShellyCo
updateChannel(updates, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_ALARM_STATE, getStringType(s.valueStr));
break;
case "6110": // A, vibration, 0/1, -1=unknown
updateChannel(updates, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_VIBRATION,
value == 1 ? OnOffType.ON : OnOffType.OFF);
if (profile.isMotion) {
// handle as status
updateChannel(updates, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_VIBRATION,
s.value == 1 ? OnOffType.ON : OnOffType.OFF);
} else if (s.value == 1) {
// handle as event
thingHandler.triggerChannel(CHANNEL_GROUP_DEV_STATUS, CHANNEL_DEVST_ALARM, EVENT_TYPE_VIBRATION);
}
break;
case "9102": // EV, wakeupEvent, battery/button/periodic/poweron/sensor/ext_power, "unknown"=unknown
if (s.valueArray.size() > 0) {

View File

@ -133,8 +133,9 @@ public class ShellyCoapHandler implements ShellyCoapListener {
.setTimeout((long) SHELLY_API_TIMEOUT_MS).useNONs().setEndpoint(coapServer.getEndpoint());
@Nullable
Endpoint endpoint = null;
if (statusClient != null) {
endpoint = statusClient.getEndpoint();
CoapClient client = statusClient;
if (client != null) {
endpoint = client.getEndpoint();
}
if ((endpoint == null) || !endpoint.isStarted()) {
logger.warn("{}: Unable to initialize CoAP access (network error)", thingName);

View File

@ -115,8 +115,11 @@ public class ShellyDiscoveryParticipant implements MDNSDiscoveryParticipant {
Map<String, Object> properties = new TreeMap<>();
name = service.getName().toLowerCase();
address = service.getHostAddress();
if ((address == null) || address.isEmpty()) {
String[] hostAddresses = service.getHostAddresses();
if ((hostAddresses != null) && (hostAddresses.length > 0)) {
address = hostAddresses[0];
}
if (address.isEmpty()) {
logger.trace("{}: Shelly device discovered with empty IP address (service-name={})", name, service);
return null;
}

View File

@ -101,8 +101,6 @@ public class ShellyBaseHandler extends BaseThingHandler implements ShellyDeviceL
private int skipUpdate = 0;
private boolean refreshSettings = false;
private @Nullable ScheduledFuture<?> asyncButtonRelease;
// delay before enabling channel
private final int cacheCount = UPDATE_SETTINGS_INTERVAL_SECONDS / UPDATE_STATUS_INTERVAL_SECONDS;
protected final ShellyChannelCache cache;
@ -111,6 +109,7 @@ public class ShellyBaseHandler extends BaseThingHandler implements ShellyDeviceL
private String localPort = "";
private String lastWakeupReason = "";
private int vibrationFilter = 0;
/**
* Constructor
@ -274,9 +273,7 @@ public class ShellyBaseHandler extends BaseThingHandler implements ShellyDeviceL
logger.debug("{}: Unable to set CoIoT peer: {}", thingName, e.toString());
}
} else if (!devpeer.isEmpty() && !devpeer.equals(ourpeer)) {
logger.warn("{}: CoIoT peer in device settings does not point this to this host, disabling CoIoT",
thingName);
config.eventsCoIoT = autoCoIoT = false;
logger.warn("{}: CoIoT peer in device settings does not point this to this host", thingName);
}
}
if (autoCoIoT) {
@ -385,6 +382,12 @@ public class ShellyBaseHandler extends BaseThingHandler implements ShellyDeviceL
try {
boolean updated = false;
if (vibrationFilter > 0) {
vibrationFilter--;
logger.debug("{}: Vibration events are absorbed for {} more seconds", thingName,
vibrationFilter * UPDATE_STATUS_INTERVAL_SECONDS);
}
skipUpdate++;
ThingStatus thingStatus = getThing().getStatus();
if (refreshSettings || (scheduledUpdates > 0) || (skipUpdate % skipCount == 0)) {
@ -483,7 +486,7 @@ public class ShellyBaseHandler extends BaseThingHandler implements ShellyDeviceL
updateStatus(ThingStatus.ONLINE);
// request 3 updates in a row (during the first 2+3*3 sec)
requestUpdates(profile.alwaysOn ? 3 : 1, channelsCreated == false);
requestUpdates(profile.alwaysOn ? 3 : 1, !channelsCreated);
}
restartWatchdog();
}
@ -575,8 +578,9 @@ public class ShellyBaseHandler extends BaseThingHandler implements ShellyDeviceL
*/
private boolean checkRestarted(ShellySettingsStatus status) {
if (profile.isInitialized() && (status.uptime < stats.lastUptime || !profile.status.update.oldVersion.isEmpty()
&& !status.update.oldVersion.equals(profile.status.update.oldVersion))) {
if (profile.isInitialized() && profile.alwaysOn /* exclude battery powered devices */
&& (status.uptime < stats.lastUptime || !profile.status.update.oldVersion.isEmpty()
&& !status.update.oldVersion.equals(profile.status.update.oldVersion))) {
updateProperties(profile, status);
return true;
}
@ -706,13 +710,10 @@ public class ShellyBaseHandler extends BaseThingHandler implements ShellyDeviceL
updateChannel(group, CHANNEL_SENSOR_ILLUM, getStringType(event));
break;
case SHELLY_EVENT_VIBRATION:
updateChannel(group, CHANNEL_SENSOR_VIBRATION, OnOffType.ON);
break;
case SHELLY_EVENT_ALARM_MILD: // Shelly Gas
case SHELLY_EVENT_ALARM_HEAVY:
case SHELLY_EVENT_ALARM_OFF:
case SHELLY_EVENT_VIBRATION: // DW2
channel = CHANNEL_SENSOR_ALARM_STATE;
payload = event.toUpperCase();
break;
@ -1195,7 +1196,21 @@ public class ShellyBaseHandler extends BaseThingHandler implements ShellyDeviceL
}
public void triggerChannel(String group, String channel, String payload) {
triggerChannel(mkChannelId(group, channel), payload);
String triggerCh = mkChannelId(group, channel);
logger.debug("{}: Send event {} to channel {}", thingName, triggerCh, payload);
if (EVENT_TYPE_VIBRATION.contentEquals(payload)) {
if (vibrationFilter == 0) {
vibrationFilter = VIBRATION_FILTER_SEC / UPDATE_STATUS_INTERVAL_SECONDS + 1;
logger.debug("{}: Duplicate vibration events will be absorbed for the next {} sec", thingName,
vibrationFilter * UPDATE_STATUS_INTERVAL_SECONDS);
} else {
logger.debug("{}: Vibration event absorbed, {} sec remaining", thingName,
vibrationFilter * UPDATE_STATUS_INTERVAL_SECONDS);
return;
}
}
triggerChannel(triggerCh, payload);
}
public void stop() {
@ -1206,11 +1221,6 @@ public class ShellyBaseHandler extends BaseThingHandler implements ShellyDeviceL
statusJob = null;
logger.debug("{}: Shelly statusJob stopped", thingName);
}
job = asyncButtonRelease;
if (job != null) {
job.cancel(true);
asyncButtonRelease = null;
}
coap.stop();
profile.initialized = false;

View File

@ -300,8 +300,6 @@ public class ShellyComponents {
if (sdata.accel != null) {
updated |= thingHandler.updateChannel(CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_TILT,
toQuantityType(getDouble(sdata.accel.tilt.doubleValue()), DIGITS_NONE, Units.DEGREE_ANGLE));
updated |= thingHandler.updateChannel(CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_VIBRATION,
getInteger(sdata.accel.vibration) == 1 ? OnOffType.ON : OnOffType.OFF);
}
if (sdata.flood != null) {
updated |= thingHandler.updateChannel(CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_FLOOD,
@ -365,8 +363,6 @@ public class ShellyComponents {
updated |= thingHandler.updateChannel(CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_MOTION_TS,
getTimestamp(getString(profile.settings.timezone), timestamp));
}
updated |= thingHandler.updateChannel(CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_VIBRATION,
getOnOff(sdata.sensor.vibration));
}
updated |= thingHandler.updateInputs(status);

View File

@ -34,11 +34,9 @@ import org.osgi.service.cm.ConfigurationAdmin;
@NonNullByDefault
public class ShellyManager {
private final Map<String, ShellyManagerPage> pages = new LinkedHashMap<>();
private final ShellyHandlerFactory handlerFactory;
public ShellyManager(ConfigurationAdmin configurationAdmin, ShellyTranslationProvider translationProvider,
HttpClient httpClient, String localIp, int localPort, ShellyHandlerFactory handlerFactory) {
this.handlerFactory = handlerFactory;
pages.put(SHELLY_MGR_OVERVIEW_URI, new ShellyManagerOverviewPage(configurationAdmin, translationProvider,
httpClient, localIp, localPort, handlerFactory));
pages.put(SHELLY_MGR_ACTION_URI, new ShellyManagerActionPage(configurationAdmin, translationProvider,

View File

@ -92,7 +92,7 @@ public class ShellyManagerActionPage extends ShellyManagerPage {
refreshTimer = 3;
break;
case ACTION_RESTART:
if (update.equalsIgnoreCase("yes")) {
if ("yes".equalsIgnoreCase(update)) {
message = getMessageP("action.restart.info", MCINFO);
actionButtonLabel = "Ok";
new Thread(() -> { // schedule asynchronous reboot
@ -116,7 +116,7 @@ public class ShellyManagerActionPage extends ShellyManagerPage {
break;
}
if (!update.equalsIgnoreCase("yes")) {
if (!"yes".equalsIgnoreCase(update)) {
ShellySettingsLogin status = api.getLoginSettings();
message = getMessage("action.protect.status", getBool(status.enabled) ? "enabled" : "disabled",
status.username)
@ -137,7 +137,7 @@ public class ShellyManagerActionPage extends ShellyManagerPage {
}
String peer = getString(profile.settings.coiot.peer);
boolean mcast = peer.isEmpty() || peer.equalsIgnoreCase(SHELLY_COIOT_MCAST);
boolean mcast = peer.isEmpty() || SHELLY_COIOT_MCAST.equalsIgnoreCase(peer);
String newPeer = mcast ? localIp + ":" + ShellyCoapJSonDTO.COIOT_PORT : SHELLY_COIOT_MCAST;
String displayPeer = mcast ? newPeer : "Multicast";
@ -147,7 +147,7 @@ public class ShellyManagerActionPage extends ShellyManagerPage {
break;
}
if (!update.equalsIgnoreCase("yes")) {
if (!"yes".equalsIgnoreCase(update)) {
message = getMessageP("coiot.current-peer", MCMESSAGE, mcast ? "Multicast" : peer)
+ getMessageP("coiot.new-peer", MCINFO, displayPeer)
+ getMessageP(mcast ? "coiot.mode-peer" : "coiot.mode-mcast", MCMESSAGE);
@ -176,7 +176,7 @@ public class ShellyManagerActionPage extends ShellyManagerPage {
refreshTimer = 20;
break;
case ACTION_RESET:
if (!update.equalsIgnoreCase("yes")) {
if (!"yes".equalsIgnoreCase(update)) {
message = getMessageP("action.reset.warning", MCWARNING, serviceName);
actionUrl = buildActionUrl(uid, action);
} else {
@ -204,8 +204,8 @@ public class ShellyManagerActionPage extends ShellyManagerPage {
break;
case ACTION_ENDEBUG:
case ACTION_DISDEBUG:
boolean enable = action.equalsIgnoreCase(ACTION_ENDEBUG);
if (!update.equalsIgnoreCase("yes")) {
boolean enable = ACTION_ENDEBUG.equalsIgnoreCase(action);
if (!"yes".equalsIgnoreCase(update)) {
message = getMessage(enable ? "action.debug-enable" : "action.debug-disable");
actionUrl = buildActionUrl(uid, action);
} else {
@ -222,7 +222,7 @@ public class ShellyManagerActionPage extends ShellyManagerPage {
}
break;
case ACTION_RESSTA:
if (!update.equalsIgnoreCase("yes")) {
if (!"yes".equalsIgnoreCase(update)) {
message = getMessage("action.resetsta-info");
actionUrl = buildActionUrl(uid, action);
} else {
@ -237,8 +237,8 @@ public class ShellyManagerActionPage extends ShellyManagerPage {
break;
case ACTION_ENWIFIREC:
case ACTION_DISWIFIREC:
enable = action.equalsIgnoreCase(ACTION_ENWIFIREC);
if (!update.equalsIgnoreCase("yes")) {
enable = ACTION_ENWIFIREC.equalsIgnoreCase(action);
if (!"yes".equalsIgnoreCase(update)) {
message = getMessage(enable ? "action.setwifirec-enable" : "action.setwifirec-disable");
actionUrl = buildActionUrl(uid, action);
} else {
@ -254,8 +254,8 @@ public class ShellyManagerActionPage extends ShellyManagerPage {
case ACTION_ENAPROAMING:
case ACTION_DISAPROAMING:
enable = action.equalsIgnoreCase(ACTION_ENAPROAMING);
if (!update.equalsIgnoreCase("yes")) {
enable = ACTION_ENAPROAMING.equalsIgnoreCase(action);
if (!"yes".equalsIgnoreCase(update)) {
message = getMessage(enable ? "action.aproaming-enable" : "action.aproaming-disable");
actionUrl = buildActionUrl(uid, action);
} else {
@ -272,7 +272,7 @@ public class ShellyManagerActionPage extends ShellyManagerPage {
case ACTION_GETDEB:
case ACTION_GETDEB1:
try {
message = api.getDebugLog(action.equalsIgnoreCase(ACTION_GETDEB) ? "log" : "log1");
message = api.getDebugLog(ACTION_GETDEB.equalsIgnoreCase(action) ? "log" : "log1");
message = message.replaceAll("[\r]", "").replaceAll("[\r\n]", "<br>");
} catch (ShellyApiException e) {
message = getMessage("action.getdebug-failed", e.toString());
@ -308,7 +308,7 @@ public class ShellyManagerActionPage extends ShellyManagerPage {
if ((profile.settings.coiot != null) && (profile.settings.coiot.peer != null) && !profile.isMotion) {
boolean mcast = profile.settings.coiot.peer.isEmpty()
|| profile.settings.coiot.peer.equalsIgnoreCase(SHELLY_COIOT_MCAST);
|| SHELLY_COIOT_MCAST.equalsIgnoreCase(profile.settings.coiot.peer);
list.put(mcast ? ACTION_SETCOIOT_PEER : ACTION_SETCOIOT_MCAST,
mcast ? "Set CoIoT Peer Mode" : "Set CoIoT Multicast Mode");
}
@ -332,7 +332,7 @@ public class ShellyManagerActionPage extends ShellyManagerPage {
list.put(ACTION_RESET, "-Factory Reset");
if (profile.extFeatures) {
list.put(ACTION_OTACHECK, "Check for Update");
boolean debug_enable = getBool(profile.settings.debug_enable);
boolean debug_enable = getBool(profile.settings.debugEnable);
list.put(!debug_enable ? ACTION_ENDEBUG : ACTION_DISDEBUG,
!debug_enable ? "Enable Debug" : "Disable Debug");
if (debug_enable) {

View File

@ -115,7 +115,7 @@ public class ShellyManagerOtaPage extends ShellyManagerPage {
properties.put(ATTRIBUTE_UPDATE_URL, "http://" + getDeviceIp(properties) + "/ota?" + updateUrl);
properties.put(URLPARM_CONNECTION, connection);
if (update.equalsIgnoreCase("yes")) {
if ("yes".equalsIgnoreCase(update)) {
// do the update
th.setThingOffline(ThingStatusDetail.FIRMWARE_UPDATING, "offline.status-error-fwupgrade");
html += loadHTML(FWUPDATE2_HTML, properties);
@ -199,9 +199,9 @@ public class ShellyManagerOtaPage extends ShellyManagerPage {
} else {
// convert prod/beta to full url
FwRepoEntry fw = getFirmwareRepoEntry(deviceType, mode);
String url = getString(prod ? fw.url : fw.beta_url);
logger.debug("ShellyManager: Map {} release to url {}, version {}", url,
prod ? fw.url : fw.beta_url, prod ? fw.version : fw.beta_ver);
String url = getString(prod ? fw.url : fw.betaUrl);
logger.debug("ShellyManager: Map {} release to url {}, version {}", url, prod ? fw.url : fw.betaUrl,
prod ? fw.version : fw.betaVer);
return url;
}
default: // Update from firmware archive

View File

@ -148,7 +148,7 @@ public class ShellyManagerOverviewPage extends ShellyManagerPage {
html += "\t\t\t\t\t<option value=\"" + updateUrl + "&" + URLPARM_VERSION + "=" + FWPROD + "\">Release "
+ pVersion + "</option>\n";
}
bVersion = extractFwVersion(fw.beta_ver);
bVersion = extractFwVersion(fw.betaVer);
if (!bVersion.isEmpty()) {
html += "\t\t\t\t\t<option value=\"" + updateUrl + "&" + URLPARM_VERSION + "=" + FWBETA + "\">Beta "
+ bVersion + "</option>\n";

View File

@ -63,6 +63,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
import com.google.gson.annotations.SerializedName;
/**
* {@link ShellyManagerOtaPage} implements the Shelly Manager's page template
@ -136,8 +137,10 @@ public class ShellyManagerPage {
public @Nullable String url; // prod
public @Nullable String version;
public @Nullable String beta_url; // beta version if avilable
public @Nullable String beta_ver;
@SerializedName("beta_url")
public @Nullable String betaUrl; // beta version if avilable
@SerializedName("beta_ver")
public @Nullable String betaVer;
}
public ShellyManagerPage(ConfigurationAdmin configurationAdmin, ShellyTranslationProvider translationProvider,
@ -242,7 +245,7 @@ public class ShellyManagerPage {
addAttribute(properties, th, CHANNEL_GROUP_DEV_STATUS, CHANNEL_DEVST_ALARM);
addAttribute(properties, th, CHANNEL_GROUP_DEV_STATUS, CHANNEL_DEVST_CHARGER);
properties.put(ATTRIBUTE_DEBUG_MODE, getOption(profile.settings.debug_enable));
properties.put(ATTRIBUTE_DEBUG_MODE, getOption(profile.settings.debugEnable));
properties.put(ATTRIBUTE_DISCOVERABLE, String.valueOf(getBool(profile.settings.discoverable)));
properties.put(ATTRIBUTE_WIFI_RECOVERY, String.valueOf(getBool(profile.settings.wifiRecoveryReboot)));
properties.put(ATTRIBUTE_APR_MODE,
@ -414,9 +417,9 @@ public class ShellyManagerPage {
fw.url = url;
logger.debug("ShellyManager: Release Split-URL for device type {} is {}", deviceType, url);
}
url = substringBefore(fw.beta_url, ".zip") + "-" + mode + ".zip";
url = substringBefore(fw.betaUrl, ".zip") + "-" + mode + ".zip";
if (testUrl(url)) {
fw.beta_url = url;
fw.betaUrl = url;
logger.debug("ShellyManager: Beta Split-URL for device type {} is {}", deviceType, url);
}
}

View File

@ -125,9 +125,11 @@ public class ShellyManagerServlet extends HttpServlet {
} else {
// binary data
byte[] data = (byte[]) output.data;
response.setContentLength(data.length);
bin = response.getOutputStream();
bin.write(data, 0, data.length);
if (data != null) {
response.setContentLength(data.length);
bin = response.getOutputStream();
bin.write(data, 0, data.length);
}
}
} catch (ShellyApiException | RuntimeException e) {
logger.debug("{}: Exception uri={}, parameters={}", className, path, request.getParameterMap().toString(),

View File

@ -175,7 +175,6 @@ public class ShellyChannelDefinitions {
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_VOLTAGE, "sensorADC", ITEMT_VOLT))
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_CONTACT, "sensorContact", ITEMT_CONTACT))
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_SSTATE, "sensorState", ITEMT_STRING))
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_VIBRATION, "sensorVibration", ITEMT_SWITCH))
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_TILT, "sensorTilt", ITEMT_ANGLE))
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_MOTION, "sensorMotion", ITEMT_SWITCH))
.add(new ShellyChannel(m, CHGR_SENSOR, CHANNEL_SENSOR_MOTION_TS, "motionTimestamp", ITEMT_DATETIME))
@ -424,8 +423,6 @@ public class ShellyChannelDefinitions {
CHANNEL_SENSOR_VIBRATION);
}
if (sdata.accel != null) { // DW2
addChannel(thing, newChannels, sdata.accel.vibration != null, CHANNEL_GROUP_SENSOR,
CHANNEL_SENSOR_VIBRATION);
addChannel(thing, newChannels, sdata.accel.tilt != null, CHANNEL_GROUP_SENSOR, CHANNEL_SENSOR_TILT);
}
@ -564,18 +561,17 @@ public class ShellyChannelDefinitions {
public String getGroupAttribute(String attribute) {
String key = PREFIX_GROUP + group + "." + attribute;
String value = messages.getText(key);
return value != null && !value.equals(key) ? value : "";
return !value.equals(key) ? value : "";
}
public String getChannelAttribute(String attribute) {
String key = PREFIX_CHANNEL + channel + "." + attribute;
String value = messages.getText(key);
return value != null && !value.equals(key) ? value : "";
return !value.equals(key) ? value : "";
}
private String getText(String key) {
String text = messages.get(key);
return text != null ? text : "";
return messages.get(key);
}
}

View File

@ -52,8 +52,8 @@ import com.google.gson.JsonSyntaxException;
*/
@NonNullByDefault
public class ShellyUtils {
private final static String PRE = "Unable to create object of type ";
public final static DateTimeFormatter DATE_TIME = DateTimeFormatter.ofPattern(DateTimeType.DATE_PATTERN);
private static final String PRE = "Unable to create object of type ";
public static final DateTimeFormatter DATE_TIME = DateTimeFormatter.ofPattern(DateTimeType.DATE_PATTERN);
public static <T> T fromJson(Gson gson, @Nullable String json, Class<T> classOfT) throws ShellyApiException {
@Nullable
@ -96,6 +96,7 @@ public class ShellyUtils {
}
}
@SuppressWarnings("unchecked")
private static <T> Class<T> wrap(Class<T> type) {
if (type == int.class) {
return (Class<T>) Integer.class;