mirror of
https://github.com/openhab/openhab-addons.git
synced 2025-01-10 15:11:59 +01:00
[netatmo] Enhance RefreshCapability (#16574)
Signed-off-by: clinique <gael@lhopital.org> Signed-off-by: gael@lhopital.org <gael@lhopital.org>
This commit is contained in:
parent
9f59d29c38
commit
b6e3b816ac
@ -83,12 +83,12 @@ Once authentication process has been done, current refreshToken is stored in `/O
|
||||
| presence | Thing | NOC | The Netatmo Smart Outdoor Camera (Presence) camera with or without siren. | id, ipAddress |
|
||||
| siren | Thing | NIS | The Netatmo Smart Indoor Siren. | id |
|
||||
| doorbell | Thing | NDB | The Netatmo Smart Video Doorbell device. | id, ipAddress |
|
||||
| weather-station | Bridge | NAMain | Main indoor module reporting temperature, humidity, pressure, air quality and sound level. | id |
|
||||
| weather-station | Bridge | NAMain | Main indoor module reporting temperature, humidity, pressure, air quality and sound level. | id, refreshInterval |
|
||||
| outdoor | Thing | NAModule1 | Outdoor module reporting temperature and humidity. | id |
|
||||
| wind | Thing | NAModule2 | Wind sensor reporting wind angle and strength. | id |
|
||||
| rain | Thing | NAModule3 | Rain Gauge measuring precipitation. | id |
|
||||
| indoor | Thing | NAModule4 | Additional indoor module reporting temperature, humidity and CO2 level. | id |
|
||||
| home-coach | Thing | NHC | Healthy home coach reporting health-index, temperature, humidity, pressure, air quality, sound level. | id |
|
||||
| home-coach | Thing | NHC | Healthy home coach reporting health-index, temperature, humidity, pressure, air quality, sound level. | id, refreshInterval |
|
||||
| plug | Thing | NAPlug | The relay connected to the boiler controlling a Thermostat and zero or more valves. | id |
|
||||
| thermostat | Thing | NATherm1 | The Thermostat device placed in a given room. | id |
|
||||
| room | Thing | NARoom | A room in your house. | id |
|
||||
@ -161,8 +161,8 @@ If you did not manually create things in the *.things file, the Netatmo Binding
|
||||
|
||||
### Weather Station Main Indoor Device
|
||||
|
||||
Weather station does not need any refreshInterval setting.
|
||||
Based on a standard update period of 10mn by Netatmo systems - it will auto adapt to stick closest as possible to last data availability.
|
||||
Weather station uses a default `refreshInterval` of 10 minutes (can be adjusted), based on a standard update period of Netatmo systems.
|
||||
It will auto-adapt to stick as closely as possible to the last data availability.
|
||||
|
||||
**Supported channels for the main indoor module:**
|
||||
|
||||
@ -330,6 +330,9 @@ All these channels are read only.
|
||||
|
||||
### Healthy Home Coach Device
|
||||
|
||||
Home Coach uses a default `refreshInterval` of 10 minutes (can be adjusted), based on a standard update period of Netatmo systems.
|
||||
It will auto-adapt to stick as closely as possible to the last data availability.
|
||||
|
||||
**Supported channels for the healthy home coach device:**
|
||||
|
||||
| Channel Group | Channel Id | Item Type | Description |
|
||||
@ -453,11 +456,12 @@ Depending on the way it is configured the behaviour will be adapted and availabl
|
||||
|
||||
The Home thing has the following configuration elements:
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| ---------- | ------ | -------- | ----------------------------------------------------------------------------------- |
|
||||
| id (1) | String | No | If you have a single type of equipment, this id is to be used for the home |
|
||||
| energyId | String | No | Id of a home holding energy control devices |
|
||||
| securityId | String | No | Id of a home holding security monitoring devices |
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------------- | ------- | -------- | ----------------------------------------------------------------------------------- |
|
||||
| id (1) | String | No | If you have a single type of equipment, this id is to be used for the home |
|
||||
| energyId | String | No | Id of a home holding energy control devices |
|
||||
| securityId | String | No | Id of a home holding security monitoring devices |
|
||||
| refreshInterval | Integer | No | Refresh interval for refreshing the data in seconds. Default 180. |
|
||||
|
||||
At least one of these parameter must be filled - at most two :
|
||||
|
||||
@ -465,7 +469,7 @@ At least one of these parameter must be filled - at most two :
|
||||
* id or energyId
|
||||
* securityId and energyId
|
||||
|
||||
(1) this parameter is only kept for backward compatibility.
|
||||
(1) this parameter is kept for backward compatibility.
|
||||
|
||||
All channels are read only.
|
||||
|
||||
|
@ -36,8 +36,11 @@ import org.openhab.binding.netatmo.internal.handler.capability.DeviceCapability;
|
||||
import org.openhab.binding.netatmo.internal.handler.capability.DoorbellCapability;
|
||||
import org.openhab.binding.netatmo.internal.handler.capability.HomeCapability;
|
||||
import org.openhab.binding.netatmo.internal.handler.capability.MeasureCapability;
|
||||
import org.openhab.binding.netatmo.internal.handler.capability.ParentUpdateCapability;
|
||||
import org.openhab.binding.netatmo.internal.handler.capability.PersonCapability;
|
||||
import org.openhab.binding.netatmo.internal.handler.capability.PresenceCapability;
|
||||
import org.openhab.binding.netatmo.internal.handler.capability.RefreshAutoCapability;
|
||||
import org.openhab.binding.netatmo.internal.handler.capability.RefreshCapability;
|
||||
import org.openhab.binding.netatmo.internal.handler.capability.RoomCapability;
|
||||
import org.openhab.binding.netatmo.internal.handler.capability.WeatherCapability;
|
||||
import org.openhab.binding.netatmo.internal.handler.channelhelper.ChannelHelper;
|
||||
@ -148,6 +151,12 @@ public class NetatmoHandlerFactory extends BaseThingHandlerFactory {
|
||||
newCap = new MeasureCapability(handler, helpers);
|
||||
} else if (capability == ChannelHelperCapability.class) {
|
||||
newCap = new ChannelHelperCapability(handler, helpers);
|
||||
} else if (capability == RefreshAutoCapability.class) {
|
||||
newCap = new RefreshAutoCapability(handler);
|
||||
} else if (capability == RefreshCapability.class) {
|
||||
newCap = new RefreshCapability(handler);
|
||||
} else if (capability == ParentUpdateCapability.class) {
|
||||
newCap = new ParentUpdateCapability(handler);
|
||||
}
|
||||
if (newCap != null) {
|
||||
handler.getCapabilities().put(newCap);
|
||||
|
@ -34,8 +34,11 @@ import org.openhab.binding.netatmo.internal.handler.capability.DeviceCapability;
|
||||
import org.openhab.binding.netatmo.internal.handler.capability.DoorbellCapability;
|
||||
import org.openhab.binding.netatmo.internal.handler.capability.HomeCapability;
|
||||
import org.openhab.binding.netatmo.internal.handler.capability.MeasureCapability;
|
||||
import org.openhab.binding.netatmo.internal.handler.capability.ParentUpdateCapability;
|
||||
import org.openhab.binding.netatmo.internal.handler.capability.PersonCapability;
|
||||
import org.openhab.binding.netatmo.internal.handler.capability.PresenceCapability;
|
||||
import org.openhab.binding.netatmo.internal.handler.capability.RefreshAutoCapability;
|
||||
import org.openhab.binding.netatmo.internal.handler.capability.RefreshCapability;
|
||||
import org.openhab.binding.netatmo.internal.handler.capability.RoomCapability;
|
||||
import org.openhab.binding.netatmo.internal.handler.capability.WeatherCapability;
|
||||
import org.openhab.binding.netatmo.internal.handler.channelhelper.AirQualityChannelHelper;
|
||||
@ -70,91 +73,100 @@ public enum ModuleType {
|
||||
new ChannelGroup(ApiBridgeChannelHelper.class, GROUP_MONITORING)),
|
||||
|
||||
HOME(FeatureArea.NONE, "NAHome", 1, "home", ACCOUNT,
|
||||
Set.of(DeviceCapability.class, HomeCapability.class, ChannelHelperCapability.class),
|
||||
Set.of(DeviceCapability.class, HomeCapability.class, ChannelHelperCapability.class,
|
||||
RefreshCapability.class),
|
||||
new ChannelGroup(SecurityChannelHelper.class, GROUP_SECURITY_EVENT, GROUP_SECURITY),
|
||||
new ChannelGroup(EnergyChannelHelper.class, GROUP_ENERGY)),
|
||||
|
||||
PERSON(FeatureArea.SECURITY, "NAPerson", 1, "virtual", HOME,
|
||||
Set.of(PersonCapability.class, ChannelHelperCapability.class),
|
||||
Set.of(PersonCapability.class, ChannelHelperCapability.class, ParentUpdateCapability.class),
|
||||
new ChannelGroup(PersonChannelHelper.class, GROUP_PERSON),
|
||||
new ChannelGroup(EventPersonChannelHelper.class, GROUP_PERSON_LAST_EVENT)),
|
||||
|
||||
WELCOME(FeatureArea.SECURITY, "NACamera", 1, "camera", HOME,
|
||||
Set.of(CameraCapability.class, ChannelHelperCapability.class), ChannelGroup.SIGNAL, ChannelGroup.EVENT,
|
||||
Set.of(CameraCapability.class, ChannelHelperCapability.class, ParentUpdateCapability.class),
|
||||
ChannelGroup.SIGNAL, ChannelGroup.EVENT,
|
||||
new ChannelGroup(CameraChannelHelper.class, GROUP_SECURITY_EVENT, GROUP_CAM_STATUS, GROUP_CAM_LIVE)),
|
||||
|
||||
TAG(FeatureArea.SECURITY, "NACamDoorTag", 1, "device", WELCOME, Set.of(ChannelHelperCapability.class),
|
||||
ChannelGroup.SIGNAL, ChannelGroup.BATTERY, ChannelGroup.TIMESTAMP,
|
||||
new ChannelGroup(DoorTagChannelHelper.class, GROUP_TAG)),
|
||||
TAG(FeatureArea.SECURITY, "NACamDoorTag", 1, "device", WELCOME,
|
||||
Set.of(ChannelHelperCapability.class, ParentUpdateCapability.class), ChannelGroup.SIGNAL,
|
||||
ChannelGroup.BATTERY, ChannelGroup.TIMESTAMP, new ChannelGroup(DoorTagChannelHelper.class, GROUP_TAG)),
|
||||
|
||||
SIREN(FeatureArea.SECURITY, "NIS", 1, "device", WELCOME, Set.of(ChannelHelperCapability.class), ChannelGroup.SIGNAL,
|
||||
SIREN(FeatureArea.SECURITY, "NIS", 1, "device", WELCOME,
|
||||
Set.of(ChannelHelperCapability.class, ParentUpdateCapability.class), ChannelGroup.SIGNAL,
|
||||
ChannelGroup.BATTERY, ChannelGroup.TIMESTAMP, new ChannelGroup(SirenChannelHelper.class, GROUP_SIREN)),
|
||||
|
||||
PRESENCE(FeatureArea.SECURITY, "NOC", 1, "camera", HOME,
|
||||
Set.of(PresenceCapability.class, ChannelHelperCapability.class), ChannelGroup.SIGNAL, ChannelGroup.EVENT,
|
||||
Set.of(PresenceCapability.class, ChannelHelperCapability.class, ParentUpdateCapability.class),
|
||||
ChannelGroup.SIGNAL, ChannelGroup.EVENT,
|
||||
new ChannelGroup(PresenceChannelHelper.class, GROUP_SECURITY_EVENT, GROUP_CAM_STATUS, GROUP_CAM_LIVE,
|
||||
GROUP_PRESENCE),
|
||||
new ChannelGroup(EventCameraChannelHelper.class, GROUP_SUB_EVENT)),
|
||||
|
||||
DOORBELL(FeatureArea.SECURITY, "NDB", 1, "camera", HOME,
|
||||
Set.of(DoorbellCapability.class, ChannelHelperCapability.class), ChannelGroup.SIGNAL,
|
||||
Set.of(DoorbellCapability.class, ChannelHelperCapability.class, ParentUpdateCapability.class),
|
||||
ChannelGroup.SIGNAL,
|
||||
new ChannelGroup(CameraChannelHelper.class, GROUP_SECURITY_EVENT, GROUP_DOORBELL_STATUS,
|
||||
GROUP_DOORBELL_LIVE),
|
||||
new ChannelGroup(EventCameraChannelHelper.class, GROUP_DOORBELL_LAST_EVENT, GROUP_DOORBELL_SUB_EVENT)),
|
||||
|
||||
WEATHER_STATION(FeatureArea.WEATHER, "NAMain", 1, "device", ACCOUNT,
|
||||
WEATHER_STATION(FeatureArea.WEATHER, "NAMain", 1, "weather", ACCOUNT,
|
||||
Set.of(DeviceCapability.class, WeatherCapability.class, MeasureCapability.class,
|
||||
ChannelHelperCapability.class),
|
||||
ChannelHelperCapability.class, RefreshAutoCapability.class),
|
||||
ChannelGroup.SIGNAL, ChannelGroup.HUMIDITY, ChannelGroup.TSTAMP_EXT, ChannelGroup.MEASURE,
|
||||
ChannelGroup.AIR_QUALITY, ChannelGroup.LOCATION, ChannelGroup.NOISE, ChannelGroup.TEMP_INSIDE_EXT,
|
||||
new ChannelGroup(PressureChannelHelper.class, MeasureClass.PRESSURE, GROUP_TYPE_PRESSURE_EXTENDED)),
|
||||
|
||||
OUTDOOR(FeatureArea.WEATHER, "NAModule1", 1, "device", WEATHER_STATION,
|
||||
Set.of(MeasureCapability.class, ChannelHelperCapability.class), ChannelGroup.SIGNAL, ChannelGroup.HUMIDITY,
|
||||
ChannelGroup.TSTAMP_EXT, ChannelGroup.MEASURE, ChannelGroup.BATTERY, ChannelGroup.TEMP_OUTSIDE_EXT),
|
||||
Set.of(MeasureCapability.class, ChannelHelperCapability.class, ParentUpdateCapability.class),
|
||||
ChannelGroup.SIGNAL, ChannelGroup.HUMIDITY, ChannelGroup.TSTAMP_EXT, ChannelGroup.MEASURE,
|
||||
ChannelGroup.BATTERY, ChannelGroup.TEMP_OUTSIDE_EXT),
|
||||
|
||||
WIND(FeatureArea.WEATHER, "NAModule2", 1, "device", WEATHER_STATION, Set.of(ChannelHelperCapability.class),
|
||||
ChannelGroup.SIGNAL, ChannelGroup.TSTAMP_EXT, ChannelGroup.BATTERY,
|
||||
new ChannelGroup(WindChannelHelper.class, GROUP_WIND)),
|
||||
WIND(FeatureArea.WEATHER, "NAModule2", 1, "device", WEATHER_STATION,
|
||||
Set.of(ChannelHelperCapability.class, ParentUpdateCapability.class), ChannelGroup.SIGNAL,
|
||||
ChannelGroup.TSTAMP_EXT, ChannelGroup.BATTERY, new ChannelGroup(WindChannelHelper.class, GROUP_WIND)),
|
||||
|
||||
RAIN(FeatureArea.WEATHER, "NAModule3", 1, "device", WEATHER_STATION,
|
||||
Set.of(MeasureCapability.class, ChannelHelperCapability.class), ChannelGroup.SIGNAL,
|
||||
ChannelGroup.TSTAMP_EXT, ChannelGroup.MEASURE, ChannelGroup.BATTERY,
|
||||
Set.of(MeasureCapability.class, ChannelHelperCapability.class, ParentUpdateCapability.class),
|
||||
ChannelGroup.SIGNAL, ChannelGroup.TSTAMP_EXT, ChannelGroup.MEASURE, ChannelGroup.BATTERY,
|
||||
new ChannelGroup(RainChannelHelper.class, MeasureClass.RAIN_QUANTITY, GROUP_RAIN)),
|
||||
|
||||
INDOOR(FeatureArea.WEATHER, "NAModule4", 1, "device", WEATHER_STATION,
|
||||
Set.of(MeasureCapability.class, ChannelHelperCapability.class), ChannelGroup.SIGNAL,
|
||||
ChannelGroup.TSTAMP_EXT, ChannelGroup.MEASURE, ChannelGroup.BATTERY, ChannelGroup.HUMIDITY,
|
||||
ChannelGroup.TEMP_INSIDE_EXT, ChannelGroup.AIR_QUALITY),
|
||||
Set.of(MeasureCapability.class, ChannelHelperCapability.class, ParentUpdateCapability.class),
|
||||
ChannelGroup.SIGNAL, ChannelGroup.TSTAMP_EXT, ChannelGroup.MEASURE, ChannelGroup.BATTERY,
|
||||
ChannelGroup.HUMIDITY, ChannelGroup.TEMP_INSIDE_EXT, ChannelGroup.AIR_QUALITY),
|
||||
|
||||
HOME_COACH(FeatureArea.AIR_CARE, "NHC", 1, "device", ACCOUNT,
|
||||
HOME_COACH(FeatureArea.AIR_CARE, "NHC", 1, "weather", ACCOUNT,
|
||||
Set.of(DeviceCapability.class, AirCareCapability.class, MeasureCapability.class,
|
||||
ChannelHelperCapability.class),
|
||||
ChannelHelperCapability.class, RefreshAutoCapability.class),
|
||||
ChannelGroup.LOCATION, ChannelGroup.SIGNAL, ChannelGroup.NOISE, ChannelGroup.HUMIDITY,
|
||||
ChannelGroup.TEMP_INSIDE, ChannelGroup.MEASURE, ChannelGroup.TSTAMP_EXT,
|
||||
new ChannelGroup(AirQualityChannelHelper.class, GROUP_TYPE_AIR_QUALITY_EXTENDED),
|
||||
new ChannelGroup(PressureChannelHelper.class, MeasureClass.PRESSURE, GROUP_PRESSURE)),
|
||||
|
||||
PLUG(FeatureArea.ENERGY, "NAPlug", 1, "device", HOME, Set.of(ChannelHelperCapability.class), ChannelGroup.SIGNAL),
|
||||
PLUG(FeatureArea.ENERGY, "NAPlug", 1, "device", HOME,
|
||||
Set.of(ChannelHelperCapability.class, ParentUpdateCapability.class), ChannelGroup.SIGNAL),
|
||||
|
||||
VALVE(FeatureArea.ENERGY, "NRV", 1, "device", PLUG, Set.of(ChannelHelperCapability.class), ChannelGroup.SIGNAL,
|
||||
VALVE(FeatureArea.ENERGY, "NRV", 1, "device", PLUG,
|
||||
Set.of(ChannelHelperCapability.class, ParentUpdateCapability.class), ChannelGroup.SIGNAL,
|
||||
ChannelGroup.BATTERY_EXT),
|
||||
|
||||
THERMOSTAT(FeatureArea.ENERGY, "NATherm1", 1, "device", PLUG, Set.of(ChannelHelperCapability.class),
|
||||
ChannelGroup.SIGNAL, ChannelGroup.BATTERY_EXT,
|
||||
new ChannelGroup(Therm1ChannelHelper.class, GROUP_TYPE_TH_PROPERTIES)),
|
||||
THERMOSTAT(FeatureArea.ENERGY, "NATherm1", 1, "device", PLUG,
|
||||
Set.of(ChannelHelperCapability.class, ParentUpdateCapability.class), ChannelGroup.SIGNAL,
|
||||
ChannelGroup.BATTERY_EXT, new ChannelGroup(Therm1ChannelHelper.class, GROUP_TYPE_TH_PROPERTIES)),
|
||||
|
||||
ROOM(FeatureArea.ENERGY, "NARoom", 1, "virtual", HOME, Set.of(RoomCapability.class, ChannelHelperCapability.class),
|
||||
ROOM(FeatureArea.ENERGY, "NARoom", 1, "virtual", HOME,
|
||||
Set.of(RoomCapability.class, ChannelHelperCapability.class, ParentUpdateCapability.class),
|
||||
new ChannelGroup(RoomChannelHelper.class, GROUP_TYPE_ROOM_PROPERTIES, GROUP_TYPE_ROOM_TEMPERATURE),
|
||||
new ChannelGroup(SetpointChannelHelper.class, GROUP_SETPOINT)),
|
||||
|
||||
SMOKE_DETECTOR(FeatureArea.SECURITY, "NSD", 1, "device", HOME,
|
||||
Set.of(AlarmEventCapability.class, ChannelHelperCapability.class), ChannelGroup.SIGNAL,
|
||||
ChannelGroup.TIMESTAMP, ChannelGroup.ALARM_LAST_EVENT),
|
||||
Set.of(AlarmEventCapability.class, ChannelHelperCapability.class, ParentUpdateCapability.class),
|
||||
ChannelGroup.SIGNAL, ChannelGroup.TIMESTAMP, ChannelGroup.ALARM_LAST_EVENT),
|
||||
|
||||
CO_DETECTOR(FeatureArea.SECURITY, "NCO", 1, "device", HOME,
|
||||
Set.of(AlarmEventCapability.class, ChannelHelperCapability.class), ChannelGroup.SIGNAL,
|
||||
ChannelGroup.TIMESTAMP, ChannelGroup.ALARM_LAST_EVENT);
|
||||
Set.of(AlarmEventCapability.class, ChannelHelperCapability.class, ParentUpdateCapability.class),
|
||||
ChannelGroup.SIGNAL, ChannelGroup.TIMESTAMP, ChannelGroup.ALARM_LAST_EVENT);
|
||||
|
||||
public static final EnumSet<ModuleType> AS_SET = EnumSet.allOf(ModuleType.class);
|
||||
|
||||
|
@ -31,6 +31,12 @@ public class HomeConfiguration extends NAThingConfiguration {
|
||||
return getIdForArea(energyId.isBlank() ? FeatureArea.SECURITY : FeatureArea.ENERGY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRefreshInterval() {
|
||||
int local = refreshInterval;
|
||||
return local == -1 ? 180 : local;
|
||||
}
|
||||
|
||||
public String getIdForArea(FeatureArea feature) {
|
||||
return FeatureArea.ENERGY.equals(feature) ? energyId.isBlank() ? id : energyId
|
||||
: FeatureArea.SECURITY.equals(feature) ? securityId.isBlank() ? id : securityId : id;
|
||||
|
@ -25,7 +25,12 @@ public class NAThingConfiguration {
|
||||
public static final String ID = "id";
|
||||
|
||||
protected String id = "";
|
||||
public int refreshInterval = -1;
|
||||
protected int refreshInterval = -1;
|
||||
|
||||
public int getRefreshInterval() {
|
||||
int local = refreshInterval;
|
||||
return local == -1 ? 600 : local;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
|
@ -12,10 +12,13 @@
|
||||
*/
|
||||
package org.openhab.binding.netatmo.internal.handler;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
@ -28,8 +31,6 @@ import org.openhab.binding.netatmo.internal.config.NAThingConfiguration;
|
||||
import org.openhab.binding.netatmo.internal.handler.capability.Capability;
|
||||
import org.openhab.binding.netatmo.internal.handler.capability.CapabilityMap;
|
||||
import org.openhab.binding.netatmo.internal.handler.capability.HomeCapability;
|
||||
import org.openhab.binding.netatmo.internal.handler.capability.ParentUpdateCapability;
|
||||
import org.openhab.binding.netatmo.internal.handler.capability.RefreshCapability;
|
||||
import org.openhab.binding.netatmo.internal.handler.capability.RestCapability;
|
||||
import org.openhab.core.thing.Bridge;
|
||||
import org.openhab.core.thing.Channel;
|
||||
@ -87,6 +88,10 @@ public interface CommonInterface {
|
||||
: null;
|
||||
}
|
||||
|
||||
default Optional<ScheduledFuture<?>> schedule(Runnable arg0, Duration delay) {
|
||||
return Optional.of(getScheduler().schedule(arg0, delay.getSeconds(), TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
default @Nullable ApiBridgeHandler getAccountHandler() {
|
||||
Bridge bridge = getBridge();
|
||||
BridgeHandler bridgeHandler = null;
|
||||
@ -221,15 +226,14 @@ public interface CommonInterface {
|
||||
setThingStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_UNINITIALIZED, null);
|
||||
} else if (!ThingStatus.ONLINE.equals(bridge.getStatus())) {
|
||||
setThingStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE, null);
|
||||
getCapabilities().remove(RefreshCapability.class);
|
||||
getCapabilities().remove(ParentUpdateCapability.class);
|
||||
getCapabilities().getParentUpdate().ifPresent(Capability::dispose);
|
||||
getCapabilities().getRefresh().ifPresent(Capability::dispose);
|
||||
} else {
|
||||
setThingStatus(ThingStatus.UNKNOWN, ThingStatusDetail.NONE, null);
|
||||
if (ModuleType.ACCOUNT.equals(getModuleType().getBridge())) {
|
||||
NAThingConfiguration config = getThing().getConfiguration().as(NAThingConfiguration.class);
|
||||
getCapabilities().put(new RefreshCapability(this, config.refreshInterval));
|
||||
}
|
||||
getCapabilities().put(new ParentUpdateCapability(this));
|
||||
getCapabilities().getParentUpdate().ifPresentOrElse(Capability::initialize, () -> {
|
||||
int interval = getThingConfigAs(NAThingConfiguration.class).getRefreshInterval();
|
||||
getCapabilities().getRefresh().ifPresent(cap -> cap.setInterval(Duration.ofSeconds(interval)));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -46,7 +46,7 @@ public abstract class CacheCapability<T extends RestManager> extends RestCapabil
|
||||
protected synchronized List<NAObject> updateReadings(T api) {
|
||||
Instant now = Instant.now();
|
||||
|
||||
if (requestTS.plus(validity).isBefore(now)) {
|
||||
if (!stillValid(now)) {
|
||||
logger.debug("{} requesting fresh data for {}", getClass().getSimpleName(), thingUID);
|
||||
List<NAObject> result = getFreshData(api);
|
||||
if (!result.isEmpty()) {
|
||||
@ -58,5 +58,9 @@ public abstract class CacheCapability<T extends RestManager> extends RestCapabil
|
||||
return lastResult;
|
||||
}
|
||||
|
||||
protected boolean stillValid(Instant ts) {
|
||||
return requestTS.plus(validity).isAfter(ts);
|
||||
}
|
||||
|
||||
protected abstract List<NAObject> getFreshData(T api);
|
||||
}
|
||||
|
@ -153,7 +153,7 @@ public class Capability {
|
||||
|
||||
public void expireData() {
|
||||
CommonInterface bridgeHandler = handler.getBridgeHandler();
|
||||
if (bridgeHandler != null && !handler.getCapabilities().containsKey(RefreshCapability.class)) {
|
||||
if (bridgeHandler != null && handler.getCapabilities().getRefresh().isEmpty()) {
|
||||
bridgeHandler.expireData();
|
||||
}
|
||||
}
|
||||
|
@ -49,4 +49,13 @@ public class CapabilityMap extends ConcurrentHashMap<Class<?>, Capability> {
|
||||
cap.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public Optional<RefreshCapability> getRefresh() {
|
||||
return values().stream().filter(RefreshCapability.class::isInstance).map(RefreshCapability.class::cast)
|
||||
.findFirst();
|
||||
}
|
||||
|
||||
public Optional<Capability> getParentUpdate() {
|
||||
return values().stream().filter(ParentUpdateCapability.class::isInstance).findFirst();
|
||||
}
|
||||
}
|
||||
|
@ -12,9 +12,9 @@
|
||||
*/
|
||||
package org.openhab.binding.netatmo.internal.handler.capability;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.netatmo.internal.handler.CommonInterface;
|
||||
@ -29,7 +29,7 @@ import org.slf4j.LoggerFactory;
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class ParentUpdateCapability extends Capability {
|
||||
private static final int DEFAULT_DELAY_S = 2;
|
||||
private static final Duration DEFAULT_DELAY = Duration.ofSeconds(2);
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(ParentUpdateCapability.class);
|
||||
private Optional<ScheduledFuture<?>> job = Optional.empty();
|
||||
@ -40,13 +40,13 @@ public class ParentUpdateCapability extends Capability {
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
job = Optional.of(handler.getScheduler().schedule(() -> {
|
||||
logger.debug("Requesting parents data update for Thing {}", handler.getId());
|
||||
job = handler.schedule(() -> {
|
||||
logger.debug("Requesting parents data update for Thing '{}'", thingUID);
|
||||
CommonInterface bridgeHandler = handler.getBridgeHandler();
|
||||
if (bridgeHandler != null) {
|
||||
bridgeHandler.expireData();
|
||||
}
|
||||
}, DEFAULT_DELAY_S, TimeUnit.SECONDS));
|
||||
}, DEFAULT_DELAY);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,80 @@
|
||||
/**
|
||||
* Copyright (c) 2010-2024 Contributors to the openHAB project
|
||||
*
|
||||
* See the NOTICE file(s) distributed with this work for additional
|
||||
* information.
|
||||
*
|
||||
* This program and the accompanying materials are made available under the
|
||||
* terms of the Eclipse Public License 2.0 which is available at
|
||||
* http://www.eclipse.org/legal/epl-2.0
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.openhab.binding.netatmo.internal.handler.capability;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.ZonedDateTime;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.netatmo.internal.api.dto.NAObject;
|
||||
import org.openhab.binding.netatmo.internal.api.dto.NAThing;
|
||||
import org.openhab.binding.netatmo.internal.handler.CommonInterface;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* {@link RefreshAutoCapability} implements probing and auto-adjusting refresh strategy
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class RefreshAutoCapability extends RefreshCapability {
|
||||
private static final Duration DEFAULT_DELAY = Duration.ofSeconds(15);
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(RefreshAutoCapability.class);
|
||||
|
||||
private Instant dataTimeStamp = Instant.MIN;
|
||||
|
||||
public RefreshAutoCapability(CommonInterface handler) {
|
||||
super(handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void expireData() {
|
||||
dataTimeStamp = Instant.MIN;
|
||||
super.expireData();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Duration calcDelay() {
|
||||
if (Instant.MIN.equals(dataTimeStamp)) {
|
||||
return PROBING_INTERVAL;
|
||||
}
|
||||
|
||||
Duration dataAge = Duration.between(dataTimeStamp, Instant.now());
|
||||
|
||||
Duration delay = dataValidity.minus(dataAge);
|
||||
if (delay.isNegative() || delay.isZero()) {
|
||||
logger.debug("{} did not update data in expected time, return to probing", thingUID);
|
||||
dataTimeStamp = Instant.MIN;
|
||||
return PROBING_INTERVAL;
|
||||
}
|
||||
|
||||
return delay.plus(DEFAULT_DELAY);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateNAThing(NAThing newData) {
|
||||
super.updateNAThing(newData);
|
||||
dataTimeStamp = newData.getLastSeen().map(ZonedDateTime::toInstant).orElse(Instant.MIN);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void afterNewData(@Nullable NAObject newData) {
|
||||
properties.put("probing", Boolean.valueOf(Instant.MIN.equals(dataTimeStamp)).toString());
|
||||
super.afterNewData(newData);
|
||||
}
|
||||
}
|
@ -12,114 +12,107 @@
|
||||
*/
|
||||
package org.openhab.binding.netatmo.internal.handler.capability;
|
||||
|
||||
import static java.time.temporal.ChronoUnit.*;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.binding.netatmo.internal.api.dto.NAThing;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
import org.openhab.binding.netatmo.internal.api.dto.NAObject;
|
||||
import org.openhab.binding.netatmo.internal.handler.CommonInterface;
|
||||
import org.openhab.core.thing.ThingStatus;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* {@link RefreshCapability} is the class used to embed the refreshing needs calculation for devices
|
||||
* {@link RefreshCapability} is the base class used to define refreshing policies
|
||||
* It implements of a fixed refresh rate strategy.
|
||||
*
|
||||
* @author Gaël L'hopital - Initial contribution
|
||||
*
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class RefreshCapability extends Capability {
|
||||
private static final Duration DEFAULT_DELAY = Duration.of(20, SECONDS);
|
||||
private static final Duration PROBING_INTERVAL = Duration.of(120, SECONDS);
|
||||
private static final Duration OFFLINE_INTERVAL = Duration.of(15, MINUTES);
|
||||
protected static final Duration ASAP = Duration.ofSeconds(2);
|
||||
protected static final Duration OFFLINE_DELAY = Duration.ofMinutes(15);
|
||||
protected static final Duration PROBING_INTERVAL = Duration.ofMinutes(2);
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(RefreshCapability.class);
|
||||
|
||||
private Duration dataValidity;
|
||||
private Instant dataTimeStamp = Instant.now();
|
||||
private Instant dataTimeStamp0 = Instant.MIN;
|
||||
protected Duration dataValidity = PROBING_INTERVAL;
|
||||
private Optional<ScheduledFuture<?>> refreshJob = Optional.empty();
|
||||
private boolean refreshConfigured;
|
||||
private boolean expiring = false;
|
||||
|
||||
public RefreshCapability(CommonInterface handler, int refreshInterval) {
|
||||
public RefreshCapability(CommonInterface handler) {
|
||||
super(handler);
|
||||
this.dataValidity = Duration.ofSeconds(Math.max(0, refreshInterval));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
this.refreshConfigured = !probing();
|
||||
freeJobAndReschedule(2);
|
||||
public void setInterval(Duration dataValidity) {
|
||||
if (dataValidity.isNegative() || dataValidity.isZero()) {
|
||||
throw new IllegalArgumentException("refreshInterval must be positive");
|
||||
}
|
||||
this.dataValidity = dataValidity;
|
||||
expireData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
freeJobAndReschedule(0);
|
||||
stopJob();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void expireData() {
|
||||
dataTimeStamp = Instant.now().minus(dataValidity);
|
||||
freeJobAndReschedule(1);
|
||||
}
|
||||
|
||||
private Duration dataAge() {
|
||||
return Duration.between(dataTimeStamp, Instant.now());
|
||||
}
|
||||
|
||||
private boolean probing() {
|
||||
return dataValidity.getSeconds() <= 0;
|
||||
}
|
||||
|
||||
private void proceedWithUpdate() {
|
||||
handler.proceedWithUpdate();
|
||||
long delay;
|
||||
if (!ThingStatus.ONLINE.equals(handler.getThing().getStatus())) {
|
||||
logger.debug("{} is not ONLINE, special refresh interval is used", thingUID);
|
||||
delay = OFFLINE_INTERVAL.toSeconds();
|
||||
if (probing()) {
|
||||
dataTimeStamp0 = Instant.MIN;
|
||||
}
|
||||
} else {
|
||||
delay = refreshConfigured ? dataValidity.getSeconds()
|
||||
: (probing() ? PROBING_INTERVAL : dataValidity.minus(dataAge()).plus(DEFAULT_DELAY)).toSeconds();
|
||||
if (!expiring) {
|
||||
expiring = true;
|
||||
rescheduleJob(ASAP);
|
||||
}
|
||||
delay = delay < 2 ? PROBING_INTERVAL.toSeconds() : delay;
|
||||
logger.debug("{} refreshed, next one in {}s", thingUID, delay);
|
||||
freeJobAndReschedule(delay);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateNAThing(NAThing newData) {
|
||||
super.updateNAThing(newData);
|
||||
newData.getLastSeen().map(ZonedDateTime::toInstant).ifPresent(tsInstant -> {
|
||||
if (probing()) {
|
||||
if (Instant.MIN.equals(dataTimeStamp0)) {
|
||||
dataTimeStamp0 = tsInstant;
|
||||
logger.debug("First data timestamp of {} is {}", thingUID, dataTimeStamp0);
|
||||
} else if (tsInstant.isAfter(dataTimeStamp0)) {
|
||||
dataValidity = Duration.between(dataTimeStamp0, tsInstant);
|
||||
refreshConfigured = true;
|
||||
logger.debug("Data validity period of {} identified to be {}", thingUID, dataValidity);
|
||||
} else {
|
||||
logger.debug("Data validity period of {} not yet found, data timestamp unchanged", thingUID);
|
||||
}
|
||||
}
|
||||
dataTimeStamp = tsInstant;
|
||||
});
|
||||
protected void afterNewData(@Nullable NAObject newData) {
|
||||
expiring = false;
|
||||
super.afterNewData(newData);
|
||||
}
|
||||
|
||||
private void freeJobAndReschedule(long delay) {
|
||||
protected Duration calcDelay() {
|
||||
return dataValidity;
|
||||
}
|
||||
|
||||
private void proceedWithUpdate() {
|
||||
Duration delay;
|
||||
handler.proceedWithUpdate();
|
||||
if (!ThingStatus.ONLINE.equals(handler.getThing().getStatus())) {
|
||||
delay = OFFLINE_DELAY;
|
||||
logger.debug("Thing '{}' is not ONLINE, using special refresh interval", thingUID);
|
||||
} else {
|
||||
delay = calcDelay();
|
||||
}
|
||||
rescheduleJob(delay);
|
||||
}
|
||||
|
||||
private void rescheduleJob(Duration delay) {
|
||||
if (refreshJob.isPresent()) {
|
||||
ScheduledFuture<?> job = refreshJob.get();
|
||||
Instant now = Instant.now();
|
||||
Instant expectedExecution = now.plus(delay);
|
||||
Instant scheduledExecution = now.plusMillis(job.getDelay(TimeUnit.MILLISECONDS));
|
||||
if (Math.abs(ChronoUnit.SECONDS.between(expectedExecution, scheduledExecution)) <= 3) {
|
||||
logger.debug("'{}' refresh as already pending roughly as the same time, will not reschedule", thingUID);
|
||||
return;
|
||||
} else {
|
||||
stopJob();
|
||||
}
|
||||
}
|
||||
logger.debug("'{}' next refresh in {}", thingUID, delay);
|
||||
refreshJob = handler.schedule(this::proceedWithUpdate, delay);
|
||||
}
|
||||
|
||||
private void stopJob() {
|
||||
refreshJob.ifPresent(job -> job.cancel(true));
|
||||
refreshJob = Optional.ofNullable(delay == 0 ? null
|
||||
: handler.getScheduler().schedule(() -> proceedWithUpdate(), delay, TimeUnit.SECONDS));
|
||||
refreshJob = Optional.empty();
|
||||
}
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ public class WeatherCapability extends CacheCapability<WeatherApi> {
|
||||
private final Logger logger = LoggerFactory.getLogger(WeatherCapability.class);
|
||||
|
||||
public WeatherCapability(CommonInterface handler) {
|
||||
super(handler, Duration.ofSeconds(2), WeatherApi.class);
|
||||
super(handler, Duration.ofSeconds(10), WeatherApi.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -103,6 +103,19 @@
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
<config-description uri="netatmo:weather">
|
||||
<parameter name="id" type="text" pattern="([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})" required="true">
|
||||
<label>@text/config.equipmentId.label</label>
|
||||
<description>@text/config.equipmentId.description</description>
|
||||
</parameter>
|
||||
<parameter name="refreshInterval" type="integer" min="120" unit="s">
|
||||
<label>@text/config.refreshInterval.label</label>
|
||||
<description>@text/config.refreshInterval.description</description>
|
||||
<default>600</default>
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
</config-description>
|
||||
|
||||
<config-description uri="netatmo:camera">
|
||||
<parameter name="id" type="text" pattern="([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})" required="true">
|
||||
<label>@text/config.equipmentId.label</label>
|
||||
@ -140,7 +153,7 @@
|
||||
<advanced>true</advanced>
|
||||
</parameter>
|
||||
|
||||
<parameter name="refreshInterval" type="integer" min="20" unit="s">
|
||||
<parameter name="refreshInterval" type="integer" min="60" unit="s">
|
||||
<label>@text/config.refreshInterval.label</label>
|
||||
<description>@text/config.refreshInterval.description</description>
|
||||
<default>180</default>
|
||||
|
@ -30,6 +30,10 @@ public class ModuleTypeTest {
|
||||
// This did not exist prior to PR #16492
|
||||
return URI.create(BINDING_ID + ":camera");
|
||||
}
|
||||
if (mt == ModuleType.WEATHER_STATION || mt == ModuleType.HOME_COACH) {
|
||||
// This did not exist prior to PR #16492
|
||||
return URI.create(BINDING_ID + ":weather");
|
||||
}
|
||||
// This was previous method for calculating configuration URI
|
||||
return URI.create(BINDING_ID + ":"
|
||||
+ (mt == ModuleType.ACCOUNT ? "api_bridge"
|
||||
|
Loading…
Reference in New Issue
Block a user