diff --git a/bundles/org.openhab.binding.netatmo/README.md b/bundles/org.openhab.binding.netatmo/README.md
index f0def84b7..a2151ff80 100644
--- a/bundles/org.openhab.binding.netatmo/README.md
+++ b/bundles/org.openhab.binding.netatmo/README.md
@@ -10,6 +10,7 @@ The Netatmo binding integrates the following Netatmo products:
- _Doorbell_
- _Smoke Detector_
- _Smart Door Sensor_
+- _Carbon Monoxide Detector_
See for details on their product.
@@ -94,6 +95,8 @@ Now that you have got your bridge _ONLINE_ you can now start a scan with the bin
| room | Thing | NARoom | A room in your house. | id |
| valve | Thing | NRV | A valve controlling a radiator. | id |
| tag | Thing | NACamDoorTag | A door / window sensor | id |
+| smoke-detector | Thing | NSD | A Smoke Detector | id |
+| co-detector | Thing | NCO | A Carbon Monoxide Alarm | id |
### Webhook
@@ -642,6 +645,22 @@ All these channels are read only.
| last-event | subtype | String | Sub-type of event |
| last-event | message | String | Last event message from this person |
+### Netatmo Smart Carbon Monoxide Detector
+
+All these channels are read only.
+
+**Supported channels for the Carbon Monoxide Detector thing:**
+
+| Channel Group | Channel Id | Item Type | Description |
+| ------------- | ---------- | ------------ | ------------------------------------------------ |
+| signal | strength | Number | Signal strength (0 for no signal, 1 for weak...) |
+| signal | value | Number:Power | Signal strength in dBm |
+| timestamp | last-seen | DateTime | Last time the module reported its presence |
+| last-event | type | String | Type of event |
+| last-event | time | DateTime | Moment of the last event for this detector |
+| last-event | subtype | String | Sub-type of event |
+| last-event | message | String | Last event message from this detector |
+
## Configuration Examples
### things/netatmo.things
diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/NetatmoBindingConstants.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/NetatmoBindingConstants.java
index a728889a9..2f8ef0636 100644
--- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/NetatmoBindingConstants.java
+++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/NetatmoBindingConstants.java
@@ -68,9 +68,9 @@ public class NetatmoBindingConstants {
public static final String OPTION_PERSON = "-person";
public static final String OPTION_ROOM = "-room";
public static final String OPTION_THERMOSTAT = "-thermostat";
- public static final String OPTION_SMOKE = "-smoke";
+ public static final String OPTION_ALARM = "-alarm";
public static final Set GROUP_VARIATIONS = Set.of(OPTION_EXTENDED, OPTION_OUTSIDE, OPTION_DOORBELL,
- OPTION_PERSON, OPTION_ROOM, OPTION_THERMOSTAT, OPTION_SMOKE);
+ OPTION_PERSON, OPTION_ROOM, OPTION_THERMOSTAT, OPTION_ALARM);
public static final String GROUP_TYPE_TIMESTAMP_EXTENDED = GROUP_TIMESTAMP + OPTION_EXTENDED;
public static final String GROUP_TYPE_BATTERY_EXTENDED = GROUP_BATTERY + OPTION_EXTENDED;
@@ -83,7 +83,7 @@ public class NetatmoBindingConstants {
public static final String GROUP_DOORBELL_LAST_EVENT = GROUP_LAST_EVENT + OPTION_DOORBELL;
public static final String GROUP_DOORBELL_SUB_EVENT = GROUP_SUB_EVENT + OPTION_DOORBELL;
public static final String GROUP_PERSON_LAST_EVENT = GROUP_LAST_EVENT + OPTION_PERSON;
- public static final String GROUP_SMOKE_LAST_EVENT = GROUP_LAST_EVENT + OPTION_SMOKE;
+ public static final String GROUP_ALARM_LAST_EVENT = GROUP_LAST_EVENT + OPTION_ALARM;
public static final String GROUP_TYPE_ROOM_TEMPERATURE = GROUP_TEMPERATURE + OPTION_ROOM;
public static final String GROUP_TYPE_ROOM_PROPERTIES = GROUP_PROPERTIES + OPTION_ROOM;
public static final String GROUP_TYPE_TH_PROPERTIES = GROUP_PROPERTIES + OPTION_THERMOSTAT;
diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/NetatmoHandlerFactory.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/NetatmoHandlerFactory.java
index 4053372bf..3d85c3e1f 100644
--- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/NetatmoHandlerFactory.java
+++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/NetatmoHandlerFactory.java
@@ -19,6 +19,7 @@ import java.util.Map;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.client.HttpClient;
+import org.openhab.binding.netatmo.internal.api.data.ChannelGroup;
import org.openhab.binding.netatmo.internal.api.data.ModuleType;
import org.openhab.binding.netatmo.internal.config.BindingConfiguration;
import org.openhab.binding.netatmo.internal.deserialization.NADeserializer;
@@ -27,6 +28,7 @@ import org.openhab.binding.netatmo.internal.handler.CommonInterface;
import org.openhab.binding.netatmo.internal.handler.DeviceHandler;
import org.openhab.binding.netatmo.internal.handler.ModuleHandler;
import org.openhab.binding.netatmo.internal.handler.capability.AirCareCapability;
+import org.openhab.binding.netatmo.internal.handler.capability.AlarmEventCapability;
import org.openhab.binding.netatmo.internal.handler.capability.CameraCapability;
import org.openhab.binding.netatmo.internal.handler.capability.Capability;
import org.openhab.binding.netatmo.internal.handler.capability.ChannelHelperCapability;
@@ -37,7 +39,6 @@ import org.openhab.binding.netatmo.internal.handler.capability.MeasureCapability
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.RoomCapability;
-import org.openhab.binding.netatmo.internal.handler.capability.SmokeCapability;
import org.openhab.binding.netatmo.internal.handler.capability.WeatherCapability;
import org.openhab.binding.netatmo.internal.handler.channelhelper.ChannelHelper;
import org.openhab.binding.netatmo.internal.providers.NetatmoDescriptionProvider;
@@ -113,8 +114,8 @@ public class NetatmoHandlerFactory extends BaseThingHandlerFactory {
CommonInterface handler = moduleType.isABridge() ? new DeviceHandler((Bridge) thing) : new ModuleHandler(thing);
List helpers = new ArrayList<>();
- moduleType.channelGroups
- .forEach(channelGroup -> channelGroup.getHelperInstance().ifPresent(helper -> helpers.add(helper)));
+
+ helpers.addAll(moduleType.channelGroups.stream().map(ChannelGroup::getHelperInstance).toList());
moduleType.capabilities.forEach(capability -> {
Capability newCap = null;
@@ -134,8 +135,8 @@ public class NetatmoHandlerFactory extends BaseThingHandlerFactory {
newCap = new PersonCapability(handler, stateDescriptionProvider, helpers);
} else if (capability == CameraCapability.class) {
newCap = new CameraCapability(handler, stateDescriptionProvider, helpers);
- } else if (capability == SmokeCapability.class) {
- newCap = new SmokeCapability(handler, stateDescriptionProvider, helpers);
+ } else if (capability == AlarmEventCapability.class) {
+ newCap = new AlarmEventCapability(handler, stateDescriptionProvider, helpers);
} else if (capability == PresenceCapability.class) {
newCap = new PresenceCapability(handler, stateDescriptionProvider, helpers);
} else if (capability == MeasureCapability.class) {
diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/data/ChannelGroup.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/data/ChannelGroup.java
index 0b3fa8ab0..c3cc59c24 100644
--- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/data/ChannelGroup.java
+++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/data/ChannelGroup.java
@@ -14,7 +14,6 @@ package org.openhab.binding.netatmo.internal.api.data;
import static org.openhab.binding.netatmo.internal.NetatmoBindingConstants.*;
-import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
@@ -32,8 +31,6 @@ import org.openhab.binding.netatmo.internal.handler.channelhelper.SignalChannelH
import org.openhab.binding.netatmo.internal.handler.channelhelper.TemperatureChannelHelper;
import org.openhab.binding.netatmo.internal.handler.channelhelper.TimestampChannelHelper;
import org.openhab.binding.netatmo.internal.providers.NetatmoThingTypeProvider;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
/**
* The {@link ChannelGroup} makes the link between a channel helper and some group types. It also
@@ -66,8 +63,9 @@ public class ChannelGroup {
GROUP_NOISE);
public static final ChannelGroup HUMIDITY = new ChannelGroup(HumidityChannelHelper.class, MeasureClass.HUMIDITY,
GROUP_HUMIDITY);
+ public static final ChannelGroup ALARM_LAST_EVENT = new ChannelGroup(EventChannelHelper.class,
+ GROUP_ALARM_LAST_EVENT);
- private final Logger logger = LoggerFactory.getLogger(ChannelGroup.class);
private final Class extends ChannelHelper> helper;
public final Set groupTypes;
public final Set extensions;
@@ -86,13 +84,13 @@ public class ChannelGroup {
this.extensions = extensions;
}
- public Optional getHelperInstance() {
+ public ChannelHelper getHelperInstance() {
try {
- return Optional.of(helper.getConstructor(Set.class).newInstance(
- groupTypes.stream().map(NetatmoThingTypeProvider::toGroupName).collect(Collectors.toSet())));
+ return helper.getConstructor(Set.class).newInstance(
+ groupTypes.stream().map(NetatmoThingTypeProvider::toGroupName).collect(Collectors.toSet()));
} catch (ReflectiveOperationException e) {
- logger.warn("Error creating or initializing helper class : {}", e.getMessage());
+ throw new IllegalArgumentException(
+ "Error creating or initializing helper class : %s".formatted(e.getMessage()));
}
- return Optional.empty();
}
}
diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/data/EventSubType.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/data/EventSubType.java
index 7c937b979..7cf497e66 100644
--- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/data/EventSubType.java
+++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/data/EventSubType.java
@@ -32,7 +32,7 @@ public enum EventSubType {
SD_CARD_INCOMPATIBLE_SPEED(6, EventType.SD),
SD_CARD_INSUFFICIENT_SPACE(7, EventType.SD),
- // Alimentation sub events
+ // Power sub events
ALIM_INCORRECT_POWER(1, EventType.ALIM),
ALIM_CORRECT_POWER(2, EventType.ALIM),
@@ -47,6 +47,12 @@ public enum EventSubType {
SOUND_TEST_ERROR(1, EventType.SOUND_TEST),
DETECTOR_READY(0, EventType.TAMPERED),
DETECTOR_TAMPERED(1, EventType.TAMPERED),
+
+ // Carbon Monoxide Alarm
+ CO_OK(0, EventType.CO_DETECTED),
+ CO_PRE_ALARM(1, EventType.CO_DETECTED),
+ CO_ALARM(2, EventType.CO_DETECTED),
+
WIFI_STATUS_OK(1, EventType.WIFI_STATUS),
WIFI_STATUS_ERROR(0, EventType.WIFI_STATUS),
diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/data/EventType.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/data/EventType.java
index 5e383efae..dfb8de510 100644
--- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/data/EventType.java
+++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/data/EventType.java
@@ -111,22 +111,25 @@ public enum EventType {
SMOKE(ModuleType.SMOKE_DETECTOR),
@SerializedName("tampered") // When smoke detector is ready or tampered
- TAMPERED(ModuleType.SMOKE_DETECTOR),
+ TAMPERED(ModuleType.SMOKE_DETECTOR, ModuleType.CO_DETECTOR),
@SerializedName("wifi_status") // When wifi status is updated
- WIFI_STATUS(ModuleType.SMOKE_DETECTOR),
+ WIFI_STATUS(ModuleType.SMOKE_DETECTOR, ModuleType.CO_DETECTOR),
@SerializedName("battery_status") // When battery status is too low
- BATTERY_STATUS(ModuleType.SMOKE_DETECTOR),
+ BATTERY_STATUS(ModuleType.SMOKE_DETECTOR, ModuleType.CO_DETECTOR),
@SerializedName("detection_chamber_status") // When the detection chamber is dusty or clean
DETECTION_CHAMBER_STATUS(ModuleType.SMOKE_DETECTOR),
@SerializedName("sound_test") // Sound test result
- SOUND_TEST(ModuleType.SMOKE_DETECTOR),
+ SOUND_TEST(ModuleType.SMOKE_DETECTOR, ModuleType.CO_DETECTOR),
@SerializedName("new_device")
- NEW_DEVICE(ModuleType.HOME);
+ NEW_DEVICE(ModuleType.HOME),
+
+ @SerializedName("co_detected")
+ CO_DETECTED(ModuleType.CO_DETECTOR);
public static final EnumSet AS_SET = EnumSet.allOf(EventType.class);
diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/data/ModuleType.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/data/ModuleType.java
index 2314d1092..de5e14d9b 100644
--- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/data/ModuleType.java
+++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/data/ModuleType.java
@@ -18,14 +18,15 @@ import static org.openhab.binding.netatmo.internal.api.data.NetatmoConstants.*;
import java.net.URI;
import java.util.EnumSet;
import java.util.List;
+import java.util.Optional;
import java.util.Set;
-import java.util.stream.Collectors;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.netatmo.internal.api.data.NetatmoConstants.FeatureArea;
import org.openhab.binding.netatmo.internal.api.data.NetatmoConstants.MeasureClass;
import org.openhab.binding.netatmo.internal.handler.capability.AirCareCapability;
+import org.openhab.binding.netatmo.internal.handler.capability.AlarmEventCapability;
import org.openhab.binding.netatmo.internal.handler.capability.CameraCapability;
import org.openhab.binding.netatmo.internal.handler.capability.Capability;
import org.openhab.binding.netatmo.internal.handler.capability.ChannelHelperCapability;
@@ -36,14 +37,12 @@ import org.openhab.binding.netatmo.internal.handler.capability.MeasureCapability
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.RoomCapability;
-import org.openhab.binding.netatmo.internal.handler.capability.SmokeCapability;
import org.openhab.binding.netatmo.internal.handler.capability.WeatherCapability;
import org.openhab.binding.netatmo.internal.handler.channelhelper.AirQualityChannelHelper;
import org.openhab.binding.netatmo.internal.handler.channelhelper.ApiBridgeChannelHelper;
import org.openhab.binding.netatmo.internal.handler.channelhelper.CameraChannelHelper;
import org.openhab.binding.netatmo.internal.handler.channelhelper.DoorTagChannelHelper;
import org.openhab.binding.netatmo.internal.handler.channelhelper.EnergyChannelHelper;
-import org.openhab.binding.netatmo.internal.handler.channelhelper.EventChannelHelper;
import org.openhab.binding.netatmo.internal.handler.channelhelper.EventDoorbellChannelHelper;
import org.openhab.binding.netatmo.internal.handler.channelhelper.EventPersonChannelHelper;
import org.openhab.binding.netatmo.internal.handler.channelhelper.PersonChannelHelper;
@@ -59,13 +58,14 @@ import org.openhab.binding.netatmo.internal.handler.channelhelper.WindChannelHel
import org.openhab.core.thing.ThingTypeUID;
/**
- * This enum all handled Netatmo modules and devices along with their capabilities
+ * This enum describes all Netatmo modules and devices along with their capabilities.
*
* @author Gaël L'hopital - Initial contribution
*/
@NonNullByDefault
public enum ModuleType {
UNKNOWN(FeatureArea.NONE, "", null, Set.of()),
+
ACCOUNT(FeatureArea.NONE, "", null, Set.of(), new ChannelGroup(ApiBridgeChannelHelper.class, GROUP_MONITORING)),
HOME(FeatureArea.NONE, "NAHome", ACCOUNT,
@@ -140,13 +140,15 @@ public enum ModuleType {
new ChannelGroup(RoomChannelHelper.class, GROUP_TYPE_ROOM_PROPERTIES, GROUP_TYPE_ROOM_TEMPERATURE),
new ChannelGroup(SetpointChannelHelper.class, GROUP_SETPOINT)),
- SMOKE_DETECTOR(FeatureArea.SECURITY, "NSD", HOME, Set.of(SmokeCapability.class, ChannelHelperCapability.class),
- ChannelGroup.SIGNAL, ChannelGroup.TIMESTAMP,
- new ChannelGroup(EventChannelHelper.class, GROUP_SMOKE_LAST_EVENT));
+ SMOKE_DETECTOR(FeatureArea.SECURITY, "NSD", HOME, Set.of(AlarmEventCapability.class, ChannelHelperCapability.class),
+ ChannelGroup.SIGNAL, ChannelGroup.TIMESTAMP, ChannelGroup.ALARM_LAST_EVENT),
+
+ CO_DETECTOR(FeatureArea.SECURITY, "NCO", HOME, Set.of(AlarmEventCapability.class, ChannelHelperCapability.class),
+ ChannelGroup.SIGNAL, ChannelGroup.TIMESTAMP, ChannelGroup.ALARM_LAST_EVENT);
public static final EnumSet AS_SET = EnumSet.allOf(ModuleType.class);
- private final @Nullable ModuleType bridgeType;
+ private final Optional bridgeType;
public final Set channelGroups;
public final Set> capabilities;
public final ThingTypeUID thingTypeUID;
@@ -155,7 +157,7 @@ public enum ModuleType {
ModuleType(FeatureArea feature, String apiName, @Nullable ModuleType bridge,
Set> capabilities, ChannelGroup... channelGroups) {
- this.bridgeType = bridge;
+ this.bridgeType = Optional.ofNullable(bridge);
this.feature = feature;
this.capabilities = capabilities;
this.apiName = apiName;
@@ -167,21 +169,16 @@ public enum ModuleType {
return !channelGroups.contains(ChannelGroup.SIGNAL);
}
- public boolean isABridge() {
- for (ModuleType mt : ModuleType.values()) {
- if (this.equals(mt.bridgeType)) {
- return true;
- }
- }
- return false;
+ public boolean isABridge() { // I am a bridge if any module references me as being so
+ return AS_SET.stream().anyMatch(mt -> this.equals(mt.getBridge()));
}
public List getExtensions() {
- return channelGroups.stream().map(cg -> cg.extensions).flatMap(Set::stream).collect(Collectors.toList());
+ return channelGroups.stream().map(cg -> cg.extensions).flatMap(Set::stream).toList();
}
- public Set getGroupTypes() {
- return channelGroups.stream().map(cg -> cg.groupTypes).flatMap(Set::stream).collect(Collectors.toSet());
+ public List getGroupTypes() {
+ return channelGroups.stream().map(cg -> cg.groupTypes).flatMap(Set::stream).toList();
}
public int[] getSignalLevels() {
@@ -191,29 +188,29 @@ public enum ModuleType {
: WIFI_SIGNAL_LEVELS;
}
throw new IllegalArgumentException(
- "This should not be called for module type : " + name() + ", please file a bug report.");
+ "getSignalLevels should not be called for module type : '%s', please file a bug report."
+ .formatted(name()));
}
public ModuleType getBridge() {
- ModuleType bridge = bridgeType;
- return bridge != null ? bridge : ModuleType.UNKNOWN;
+ return bridgeType.orElse(UNKNOWN);
}
public URI getConfigDescription() {
return URI.create(BINDING_ID + ":"
+ (equals(ACCOUNT) ? "api_bridge"
: equals(HOME) ? "home"
- : (isLogical() ? "virtual"
- : ModuleType.UNKNOWN.equals(getBridge()) ? "configurable" : "device")));
+ : (isLogical() ? "virtual" : UNKNOWN.equals(getBridge()) ? "configurable" : "device")));
}
public int getDepth() {
- ModuleType parent = bridgeType;
- return parent == null ? 1 : 1 + parent.getDepth();
+ ModuleType parent = getBridge();
+ return parent == UNKNOWN ? 1 : parent.getDepth() + 1;
}
public static ModuleType from(ThingTypeUID thingTypeUID) {
- return ModuleType.AS_SET.stream().filter(mt -> mt.thingTypeUID.equals(thingTypeUID)).findFirst()
- .orElseThrow(() -> new IllegalArgumentException());
+ return AS_SET.stream().filter(mt -> mt.thingTypeUID.equals(thingTypeUID)).findFirst()
+ .orElseThrow(() -> new IllegalArgumentException(
+ "No known ModuleType matched '%s'".formatted(thingTypeUID.toString())));
}
}
diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/data/NetatmoConstants.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/data/NetatmoConstants.java
index c80518022..e2d692a3f 100644
--- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/data/NetatmoConstants.java
+++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/api/data/NetatmoConstants.java
@@ -197,10 +197,13 @@ public class NetatmoConstants {
WRITE_DOORBELL,
@SerializedName("access_doorbell")
ACCESS_DOORBELL,
+ @SerializedName("read_carbonmonoxidedetector")
+ READ_CARBONMONOXIDEDETECTOR,
UNKNOWN;
}
private static final Scope[] SMOKE_SCOPES = { Scope.READ_SMOKEDETECTOR };
+ private static final Scope[] CARBON_MONOXIDE_SCOPES = { Scope.READ_CARBONMONOXIDEDETECTOR };
private static final Scope[] AIR_CARE_SCOPES = { Scope.READ_HOMECOACH };
private static final Scope[] WEATHER_SCOPES = { Scope.READ_STATION };
private static final Scope[] THERMOSTAT_SCOPES = { Scope.READ_THERMOSTAT, Scope.WRITE_THERMOSTAT };
@@ -212,7 +215,7 @@ public class NetatmoConstants {
AIR_CARE(AIR_CARE_SCOPES),
WEATHER(WEATHER_SCOPES),
ENERGY(THERMOSTAT_SCOPES),
- SECURITY(WELCOME_SCOPES, PRESENCE_SCOPES, SMOKE_SCOPES, DOORBELL_SCOPES),
+ SECURITY(WELCOME_SCOPES, PRESENCE_SCOPES, SMOKE_SCOPES, DOORBELL_SCOPES, CARBON_MONOXIDE_SCOPES),
NONE();
public static String ALL_SCOPES = EnumSet.allOf(FeatureArea.class).stream().map(fa -> fa.scopes)
diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/ApiBridgeHandler.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/ApiBridgeHandler.java
index 52018abb7..d210d91de 100644
--- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/ApiBridgeHandler.java
+++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/ApiBridgeHandler.java
@@ -43,6 +43,7 @@ import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.util.InputStreamContentProvider;
+import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpStatus;
@@ -150,6 +151,7 @@ public class ApiBridgeHandler extends BaseBridgeHandler {
String refreshToken = connectApi.authorize(configuration, code, redirectUri);
if (configuration.refreshToken.isBlank()) {
+ logger.trace("Adding refresh token to configuration : {}", refreshToken);
Configuration thingConfig = editConfiguration();
thingConfig.put(ApiHandlerConfiguration.REFRESH_TOKEN, refreshToken);
updateConfiguration(thingConfig);
@@ -254,6 +256,7 @@ public class ApiBridgeHandler extends BaseBridgeHandler {
try (InputStreamContentProvider inputStreamContentProvider = new InputStreamContentProvider(stream)) {
request.content(inputStreamContentProvider, contentType);
}
+ logger.trace(" -with payload : {} ", payload);
}
if (isLinked(requestCountChannelUID)) {
@@ -265,22 +268,25 @@ public class ApiBridgeHandler extends BaseBridgeHandler {
}
updateState(requestCountChannelUID, new DecimalType(requestsTimestamps.size()));
}
+ logger.trace(" -with headers : {} ",
+ String.join(", ", request.getHeaders().stream().map(HttpField::toString).toList()));
ContentResponse response = request.send();
Code statusCode = HttpStatus.getCode(response.getStatus());
String responseBody = new String(response.getContent(), StandardCharsets.UTF_8);
- logger.trace("executeUri returned : code {} body {}", statusCode, responseBody);
+ logger.trace(" -returned : code {} body {}", statusCode, responseBody);
- if (statusCode != Code.OK) {
- try {
- ApiError error = deserializer.deserialize(ApiError.class, responseBody);
- throw new NetatmoException(error);
- } catch (NetatmoException e) {
- logger.debug("Error deserializing payload from error response", e);
- throw new NetatmoException(statusCode.getMessage());
- }
+ if (statusCode == Code.OK) {
+ return deserializer.deserialize(clazz, responseBody);
}
- return deserializer.deserialize(clazz, responseBody);
+
+ NetatmoException exception;
+ try {
+ exception = new NetatmoException(deserializer.deserialize(ApiError.class, responseBody));
+ } catch (NetatmoException e) {
+ exception = new NetatmoException("Error deserializing error : %s".formatted(statusCode.getMessage()));
+ }
+ throw exception;
} catch (NetatmoException e) {
if (e.getStatusCode() == ServiceError.MAXIMUM_USAGE_REACHED) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/SmokeCapability.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/AlarmEventCapability.java
similarity index 62%
rename from bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/SmokeCapability.java
rename to bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/AlarmEventCapability.java
index 904a1cf67..44005b0db 100644
--- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/SmokeCapability.java
+++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/AlarmEventCapability.java
@@ -12,39 +12,31 @@
*/
package org.openhab.binding.netatmo.internal.handler.capability;
-import java.util.ArrayList;
import java.util.List;
import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.openhab.binding.netatmo.internal.api.dto.HomeEvent;
import org.openhab.binding.netatmo.internal.api.dto.NAObject;
import org.openhab.binding.netatmo.internal.handler.CommonInterface;
import org.openhab.binding.netatmo.internal.handler.channelhelper.ChannelHelper;
import org.openhab.binding.netatmo.internal.providers.NetatmoDescriptionProvider;
/**
- * {@link SmokeCapability} gives the ability to handle Smoke detector specifics
+ * {@link AlarmEventCapability} gives the ability to handle Alarm modules events
*
* @author Gaël L'hopital - Initial contribution
*
*/
@NonNullByDefault
-public class SmokeCapability extends HomeSecurityThingCapability {
+public class AlarmEventCapability extends HomeSecurityThingCapability {
- public SmokeCapability(CommonInterface handler, NetatmoDescriptionProvider descriptionProvider,
+ public AlarmEventCapability(CommonInterface handler, NetatmoDescriptionProvider descriptionProvider,
List channelHelpers) {
super(handler, descriptionProvider, channelHelpers);
}
@Override
public List updateReadings() {
- List result = new ArrayList<>();
- securityCapability.ifPresent(cap -> {
- HomeEvent event = cap.getLastDeviceEvent(handler.getId(), moduleType.apiName);
- if (event != null) {
- result.add(event);
- }
- });
- return result;
+ return securityCapability.map(cap -> cap.getDeviceLastEvent(handler.getId(), moduleType.apiName))
+ .map(event -> List.of((NAObject) event)).orElse(List.of());
}
}
diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/CameraCapability.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/CameraCapability.java
index 8e9da2246..35b25ad63 100644
--- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/CameraCapability.java
+++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/CameraCapability.java
@@ -100,7 +100,7 @@ public class CameraCapability extends HomeSecurityThingCapability {
public List updateReadings() {
List result = new ArrayList<>();
securityCapability.ifPresent(cap -> {
- HomeEvent event = cap.getLastDeviceEvent(handler.getId(), moduleType.apiName);
+ HomeEvent event = cap.getDeviceLastEvent(handler.getId(), moduleType.apiName);
if (event != null) {
result.add(event);
result.addAll(event.getSubevents());
diff --git a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/SecurityCapability.java b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/SecurityCapability.java
index 96b6174fa..1bc19c8e9 100644
--- a/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/SecurityCapability.java
+++ b/bundles/org.openhab.binding.netatmo/src/main/java/org/openhab/binding/netatmo/internal/handler/capability/SecurityCapability.java
@@ -162,24 +162,24 @@ class SecurityCapability extends RestCapability {
return event;
}
- public @Nullable HomeEvent getLastDeviceEvent(String cameraId, String deviceType) {
- HomeEvent event = eventBuffer.get(cameraId);
+ public @Nullable HomeEvent getDeviceLastEvent(String moduleId, String deviceType) {
+ HomeEvent event = eventBuffer.get(moduleId);
if (event == null) {
- Collection events = requestDeviceEvents(cameraId, deviceType);
+ Collection events = requestDeviceEvents(moduleId, deviceType);
if (!events.isEmpty()) {
event = events.iterator().next();
- eventBuffer.put(cameraId, event);
+ eventBuffer.put(moduleId, event);
}
}
return event;
}
- private Collection requestDeviceEvents(String cameraId, String deviceType) {
+ private Collection requestDeviceEvents(String moduleId, String deviceType) {
return getApi().map(api -> {
try {
- return api.getDeviceEvents(handler.getId(), cameraId, deviceType);
+ return api.getDeviceEvents(handler.getId(), moduleId, deviceType);
} catch (NetatmoException e) {
- logger.warn("Error retrieving last events of camera '{}' : {}", cameraId, e.getMessage());
+ logger.warn("Error retrieving last events of camera '{}' : {}", moduleId, e.getMessage());
return null;
}
}).orElse(List.of());
diff --git a/bundles/org.openhab.binding.netatmo/src/main/resources/OH-INF/i18n/netatmo.properties b/bundles/org.openhab.binding.netatmo/src/main/resources/OH-INF/i18n/netatmo.properties
index 4f2f8042c..aa6e85628 100644
--- a/bundles/org.openhab.binding.netatmo/src/main/resources/OH-INF/i18n/netatmo.properties
+++ b/bundles/org.openhab.binding.netatmo/src/main/resources/OH-INF/i18n/netatmo.properties
@@ -18,6 +18,9 @@ channel-group-type.netatmo.energy.label = Home Energy
channel-group-type.netatmo.energy.channel.end.label = Mode End
channel-group-type.netatmo.energy.channel.end.description = End time of the currently applied setpoint.
channel-group-type.netatmo.humidity.label = Humidity
+channel-group-type.netatmo.last-event-alarm.label = Last Event
+channel-group-type.netatmo.last-event-alarm.channel.time.label = Event Timestamp
+channel-group-type.netatmo.last-event-alarm.channel.time.description = Moment when event occurred.
channel-group-type.netatmo.last-event-doorbell.label = Last Event
channel-group-type.netatmo.last-event-doorbell.channel.local-video-url.label = Video Local URL
channel-group-type.netatmo.last-event-doorbell.channel.local-video-url.description = Local URL of the event recording.
@@ -31,9 +34,6 @@ channel-group-type.netatmo.last-event-person.channel.snapshot.description = Pict
channel-group-type.netatmo.last-event-person.channel.snapshot-url.description = URL for the picture of the last event for this person.
channel-group-type.netatmo.last-event-person.channel.time.label = Person Timestamp
channel-group-type.netatmo.last-event-person.channel.time.description = Moment of the last event for this person.
-channel-group-type.netatmo.last-event-smoke.label = Last Event
-channel-group-type.netatmo.last-event-smoke.channel.time.label = Event Timestamp
-channel-group-type.netatmo.last-event-smoke.channel.time.description = Moment when event occurred.
channel-group-type.netatmo.last-event.label = Last Event
channel-group-type.netatmo.last-event.channel.local-video-url.label = Video Local URL
channel-group-type.netatmo.last-event.channel.local-video-url.description = Local URL of the event recording.
@@ -174,8 +174,8 @@ channel-type.netatmo.event-subtype.state.option.MOVEMENT_VEHICLE = Car seen
channel-type.netatmo.event-subtype.state.option.MOVEMENT_ANIMAL = Animal seen
channel-type.netatmo.event-subtype.state.option.SOUND_TEST_OK = Alarm test successful
channel-type.netatmo.event-subtype.state.option.SOUND_TEST_ERROR = Alarm test failed
-channel-type.netatmo.event-subtype.state.option.DETECTOR_READY = Smoke detector installed
-channel-type.netatmo.event-subtype.state.option.DETECTOR_TAMPERED = Smoke detector tampered
+channel-type.netatmo.event-subtype.state.option.DETECTOR_READY = Detector installed
+channel-type.netatmo.event-subtype.state.option.DETECTOR_TAMPERED = Detector tampered
channel-type.netatmo.event-subtype.state.option.DETECTION_CHAMBER_CLEAN = Detection chamber clean
channel-type.netatmo.event-subtype.state.option.DETECTION_CHAMBER_DIRTY = Detection chamber dusty
channel-type.netatmo.event-subtype.state.option.BATTERY_LOW = Battery low
@@ -184,6 +184,9 @@ channel-type.netatmo.event-subtype.state.option.SMOKE_CLEARED = Smoke cleared
channel-type.netatmo.event-subtype.state.option.SMOKE_DETECTED = Smoke detected
channel-type.netatmo.event-subtype.state.option.WIFI_STATUS_OK = Wi-Fi status ok
channel-type.netatmo.event-subtype.state.option.WIFI_STATUS_ERROR = Wi-Fi status error
+channel-type.netatmo.event-subtype.state.option.CO_OK = Carbon Monoxide OK
+channel-type.netatmo.event-subtype.state.option.CO_PRE_ALARM = Carbon Monoxide Pre-alarm
+channel-type.netatmo.event-subtype.state.option.CO_ALARM = Carbon Monoxide alarrm
channel-type.netatmo.event-type.label = Event Type
channel-type.netatmo.event-type.description = Description of the event.
channel-type.netatmo.event-type.state.option.PERSON = Face detected
@@ -212,12 +215,13 @@ channel-type.netatmo.event-type.state.option.RTC = Button pressed
channel-type.netatmo.event-type.state.option.MISSED_CALL = Call has not been answered by anyone
channel-type.netatmo.event-type.state.option.HUSH = Smoke detector status
channel-type.netatmo.event-type.state.option.SMOKE = Smoke detection
-channel-type.netatmo.event-type.state.option.TAMPERED = Smoke Detector tamper
+channel-type.netatmo.event-type.state.option.TAMPERED = Detector tamper
channel-type.netatmo.event-type.state.option.WIFI_STATUS = Wifi status
channel-type.netatmo.event-type.state.option.BATTERY_STATUS = Battery status
channel-type.netatmo.event-type.state.option.DETECTION_CHAMBER_STATUS = Detection chamber status
channel-type.netatmo.event-type.state.option.SOUND_TEST = Sound test
channel-type.netatmo.event-type.state.option.NEW_DEVICE = A device has been added
+channel-type.netatmo.event-type.state.option.CO_DETECTED = Carbon Monoxide detection
channel-type.netatmo.floodlight-mode.label = Floodlight
channel-type.netatmo.floodlight-mode.description = State of the floodlight (On/Off/Auto)
channel-type.netatmo.floodlight-mode.state.option.ON = On
@@ -378,6 +382,8 @@ extensible-channel-type.timestamp.pattern = %1$tA, %1$td.%1$tm. %1$tH:%1$tM
thing-type.netatmo.account.label = Netatmo Account
thing-type.netatmo.account.description = This bridge represents an account, gateway to Netatmo API.
+thing-type.netatmo.co-detector.label = Carbon Monoxide Alarm
+thing-type.netatmo.co-detector.description = The Netatmo Smart Carbon Monoxide Alarm device.
thing-type.netatmo.doorbell.label = Smart Video Doorbell
thing-type.netatmo.doorbell.description = The Netatmo Smart Video Doorbell device.
thing-type.netatmo.tag.label = Smart Door Sensor
diff --git a/bundles/org.openhab.binding.netatmo/src/main/resources/OH-INF/thing/channels.xml b/bundles/org.openhab.binding.netatmo/src/main/resources/OH-INF/thing/channels.xml
index 97efdce82..2a0f6a5bb 100644
--- a/bundles/org.openhab.binding.netatmo/src/main/resources/OH-INF/thing/channels.xml
+++ b/bundles/org.openhab.binding.netatmo/src/main/resources/OH-INF/thing/channels.xml
@@ -375,12 +375,13 @@
-
+
+
@@ -408,8 +409,8 @@
-
-
+
+
@@ -418,6 +419,9 @@
+
+
+
diff --git a/bundles/org.openhab.binding.netatmo/src/main/resources/OH-INF/thing/security.xml b/bundles/org.openhab.binding.netatmo/src/main/resources/OH-INF/thing/security.xml
index cf410d75a..fa817e532 100644
--- a/bundles/org.openhab.binding.netatmo/src/main/resources/OH-INF/thing/security.xml
+++ b/bundles/org.openhab.binding.netatmo/src/main/resources/OH-INF/thing/security.xml
@@ -140,7 +140,7 @@
-
+