[Netatmo] Adding Carbon Monoxide sensor (#14543)
* Added Carbon Monoxide detector --------- Signed-off-by: clinique <gael@lhopital.org>
This commit is contained in:
parent
50cdd02447
commit
90b2279a29
|
@ -10,6 +10,7 @@ The Netatmo binding integrates the following Netatmo products:
|
|||
- _Doorbell_
|
||||
- _Smoke Detector_
|
||||
- _Smart Door Sensor_
|
||||
- _Carbon Monoxide Detector_
|
||||
|
||||
See <https://www.netatmo.com/> 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
|
||||
|
|
|
@ -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<String> 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;
|
||||
|
|
|
@ -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<ChannelHelper> 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) {
|
||||
|
|
|
@ -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<String> groupTypes;
|
||||
public final Set<String> extensions;
|
||||
|
@ -86,13 +84,13 @@ public class ChannelGroup {
|
|||
this.extensions = extensions;
|
||||
}
|
||||
|
||||
public Optional<ChannelHelper> 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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
|
||||
|
|
|
@ -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<EventType> AS_SET = EnumSet.allOf(EventType.class);
|
||||
|
||||
|
|
|
@ -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<ModuleType> AS_SET = EnumSet.allOf(ModuleType.class);
|
||||
|
||||
private final @Nullable ModuleType bridgeType;
|
||||
private final Optional<ModuleType> bridgeType;
|
||||
public final Set<ChannelGroup> channelGroups;
|
||||
public final Set<Class<? extends Capability>> capabilities;
|
||||
public final ThingTypeUID thingTypeUID;
|
||||
|
@ -155,7 +157,7 @@ public enum ModuleType {
|
|||
|
||||
ModuleType(FeatureArea feature, String apiName, @Nullable ModuleType bridge,
|
||||
Set<Class<? extends Capability>> 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<String> 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<String> getGroupTypes() {
|
||||
return channelGroups.stream().map(cg -> cg.groupTypes).flatMap(Set::stream).collect(Collectors.toSet());
|
||||
public List<String> 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())));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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<ChannelHelper> channelHelpers) {
|
||||
super(handler, descriptionProvider, channelHelpers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<NAObject> updateReadings() {
|
||||
List<NAObject> 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());
|
||||
}
|
||||
}
|
|
@ -100,7 +100,7 @@ public class CameraCapability extends HomeSecurityThingCapability {
|
|||
public List<NAObject> updateReadings() {
|
||||
List<NAObject> 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());
|
||||
|
|
|
@ -162,24 +162,24 @@ class SecurityCapability extends RestCapability<SecurityApi> {
|
|||
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<HomeEvent> events = requestDeviceEvents(cameraId, deviceType);
|
||||
Collection<HomeEvent> events = requestDeviceEvents(moduleId, deviceType);
|
||||
if (!events.isEmpty()) {
|
||||
event = events.iterator().next();
|
||||
eventBuffer.put(cameraId, event);
|
||||
eventBuffer.put(moduleId, event);
|
||||
}
|
||||
}
|
||||
return event;
|
||||
}
|
||||
|
||||
private Collection<HomeEvent> requestDeviceEvents(String cameraId, String deviceType) {
|
||||
private Collection<HomeEvent> 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());
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -375,12 +375,13 @@
|
|||
<option value="MISSED_CALL">Call has not been answered by anyone</option>
|
||||
<option value="HUSH">Smoke detector status</option>
|
||||
<option value="SMOKE">Smoke detection</option>
|
||||
<option value="TAMPERED">Smoke Detector tamper</option>
|
||||
<option value="TAMPERED">Detector tamper</option>
|
||||
<option value="WIFI_STATUS">Wifi status</option>
|
||||
<option value="BATTERY_STATUS">Battery status</option>
|
||||
<option value="DETECTION_CHAMBER_STATUS">Detection chamber status</option>
|
||||
<option value="SOUND_TEST">Sound test</option>
|
||||
<option value="NEW_DEVICE">A device has been added</option>
|
||||
<option value="CO_DETECTED">Carbon Monoxide detection</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
@ -408,8 +409,8 @@
|
|||
<option value="MOVEMENT_ANIMAL">Animal seen</option>
|
||||
<option value="SOUND_TEST_OK">Alarm test successful</option>
|
||||
<option value="SOUND_TEST_ERROR">Alarm test failed</option>
|
||||
<option value="DETECTOR_READY">Smoke detector installed</option>
|
||||
<option value="DETECTOR_TAMPERED">Smoke detector tampered</option>
|
||||
<option value="DETECTOR_READY">Detector installed</option>
|
||||
<option value="DETECTOR_TAMPERED">Detector tampered</option>
|
||||
<option value="DETECTION_CHAMBER_CLEAN">Detection chamber clean</option>
|
||||
<option value="DETECTION_CHAMBER_DIRTY">Detection chamber dusty</option>
|
||||
<option value="BATTERY_LOW">Battery low</option>
|
||||
|
@ -418,6 +419,9 @@
|
|||
<option value="SMOKE_DETECTED">Smoke detected</option>
|
||||
<option value="WIFI_STATUS_OK">Wi-Fi status ok</option>
|
||||
<option value="WIFI_STATUS_ERROR">Wi-Fi status error</option>
|
||||
<option value="CO_OK">Carbon Monoxide OK</option>
|
||||
<option value="CO_PRE_ALARM">Carbon Monoxide Pre-alarm</option>
|
||||
<option value="CO_ALARM">Carbon Monoxide alarrm</option>
|
||||
</options>
|
||||
</state>
|
||||
</channel-type>
|
||||
|
|
|
@ -140,7 +140,7 @@
|
|||
</channels>
|
||||
</channel-group-type>
|
||||
|
||||
<channel-group-type id="last-event-smoke">
|
||||
<channel-group-type id="last-event-alarm">
|
||||
<label>Last Event</label>
|
||||
<channels>
|
||||
<channel id="type" typeId="event-type"/>
|
||||
|
|
Loading…
Reference in New Issue