- *Personal Weather Station*. Reports temperature, humidity, air pressure, carbon dioxide concentration in the air, as well as the ambient noise level.
- *Thermostat*. Reports ambient temperature, allow to check target temperature, consult and change furnace heating status.
- *Indoor Camera / Welcome*. Reports last event and persons at home, consult picture and video from event/camera.
+- *Siren*
- *Outdoor Camera / Presence*. Reports last event, consult picture and video from event/camera.
+- *Doorbell*
See https://www.netatmo.com/ for details on their product.
(*) This channel is configurable : low, poor, high.
-**Supported channels for the Welcome Doorbell thing:**
+**Supported channels for the Doorbell thing:**
| Channel Group | Channel ID | Item Type | Read/Write | Description |
|---------------|-------------------|--------------|------------|---------------------------------------------------------------------------------------------------------------------------------------------|
Note: live feeds either locally or via VPN are not available in Netatmo API.
+**Supported channels for the Siren thing:**
+
+| Channel Group | Channel ID | Item Type | Read/Write | Description |
+|---------------|-------------------|--------------|------------|------------------------------------------------------|
+| siren | status | String | Read-only | Status of the siren, if silent or emitting an alarm |
+| siren | monitoring | Switch | Read-only | State of the siren device |
+| signal | strength | Number | Read-only | Signal strength (0 for no signal, 1 for weak...) |
+| signal | value | Number:Power | Read-only | Signal strength in dBm |
+| timestamp | last-seen | DateTime | Read-only | Last time the module reported its presence |
+| battery | value | Number | Read-only | Battery level |
+| battery | low-battery | Switch | Read-only | Low battery |
+
### Welcome Person
public static final String GROUP_CAM_STATUS = "status";
public static final String GROUP_CAM_LIVE = "live";
public static final String GROUP_PRESENCE = "presence";
+ public static final String GROUP_SIREN = "siren";
public static final String GROUP_PERSON = "person";
public static final String GROUP_PROPERTIES = "properties";
public static final String GROUP_SETPOINT = "setpoint";
public static final String CHANNEL_SUM_RAIN1 = "sum-1";
public static final String CHANNEL_SUM_RAIN24 = "sum-24";
public static final String CHANNEL_WIND_ANGLE = "angle";
+ public static final String CHANNEL_STATUS = GROUP_CAM_STATUS;
public static final String CHANNEL_WIND_STRENGTH = "strength";
public static final String CHANNEL_MAX_WIND_STRENGTH = "max-strength";
public static final String CHANNEL_DATE_MAX_WIND_STRENGTH = "max-strength-date";
*/
@NonNullByDefault
public class AuthenticationApi extends RestManager {
- private static final String ALL_SCOPES = FeatureArea.toScopeString(FeatureArea.AS_SET);
private static final UriBuilder OAUTH_BUILDER = getApiBaseBuilder().path(PATH_OAUTH);
private static final UriBuilder AUTH_BUILDER = OAUTH_BUILDER.clone().path(SUB_PATH_AUTHORIZE);
private static final URI TOKEN_URI = OAUTH_BUILDER.clone().path(SUB_PATH_TOKEN).build();
public String authorize(ApiHandlerConfiguration credentials, @Nullable String code, @Nullable String redirectUri)
throws NetatmoException {
if (!(credentials.clientId.isBlank() || credentials.clientSecret.isBlank())) {
- Map<String, String> params = new HashMap<>(Map.of(SCOPE, ALL_SCOPES));
+ Map<String, String> params = new HashMap<>(Map.of(SCOPE, FeatureArea.ALL_SCOPES));
String refreshToken = credentials.refreshToken;
if (!refreshToken.isBlank()) {
params.put(REFRESH_TOKEN, refreshToken);
}
public static UriBuilder getAuthorizationBuilder(String clientId) {
- return AUTH_BUILDER.clone().queryParam(CLIENT_ID, clientId).queryParam(SCOPE, ALL_SCOPES).queryParam(STATE,
- clientId);
+ return AUTH_BUILDER.clone().queryParam(CLIENT_ID, clientId).queryParam(SCOPE, FeatureArea.ALL_SCOPES)
+ .queryParam(STATE, clientId);
}
}
import org.openhab.binding.netatmo.internal.handler.channelhelper.RoomChannelHelper;
import org.openhab.binding.netatmo.internal.handler.channelhelper.SetpointChannelHelper;
import org.openhab.binding.netatmo.internal.handler.channelhelper.SignalChannelHelper;
+import org.openhab.binding.netatmo.internal.handler.channelhelper.SirenChannelHelper;
import org.openhab.binding.netatmo.internal.handler.channelhelper.TemperatureChannelHelper;
import org.openhab.binding.netatmo.internal.handler.channelhelper.TemperatureExtChannelHelper;
import org.openhab.binding.netatmo.internal.handler.channelhelper.TemperatureOutChannelHelper;
import org.openhab.binding.netatmo.internal.handler.channelhelper.WindChannelHelper;
import org.openhab.core.thing.ThingTypeUID;
-import com.google.gson.annotations.SerializedName;
-
/**
* This enum all handled Netatmo modules and devices along with their capabilities
*
public enum ModuleType {
UNKNOWN(FeatureArea.NONE, "", null, List.of(), List.of()),
ACCOUNT(FeatureArea.NONE, "", null, List.of(), List.of()),
- @SerializedName("NAHome")
+
HOME(FeatureArea.NONE, "NAHome", ACCOUNT,
List.of(DeviceCapability.class, EventCapability.class, HomeCapability.class, ChannelHelperCapability.class),
List.of(HomeSecurityChannelHelper.class, HomeEnergyChannelHelper.class)),
- @SerializedName("NAPerson")
+
PERSON(FeatureArea.SECURITY, "NAPerson", HOME,
List.of(EventCapability.class, PersonCapability.class, ChannelHelperCapability.class),
List.of(PersonChannelHelper.class, EventPersonChannelHelper.class)),
- @SerializedName("NACamera")
+
WELCOME(FeatureArea.SECURITY, "NACamera", HOME,
List.of(EventCapability.class, CameraCapability.class, ChannelHelperCapability.class),
List.of(CameraChannelHelper.class, SignalChannelHelper.class, EventChannelHelper.class)),
- @SerializedName("NOC")
+
+ SIREN(FeatureArea.SECURITY, "NIS", WELCOME, List.of(ChannelHelperCapability.class),
+ List.of(SirenChannelHelper.class, BatteryChannelHelper.class, TimestampChannelHelper.class,
+ SignalChannelHelper.class)),
+
PRESENCE(FeatureArea.SECURITY, "NOC", HOME,
List.of(EventCapability.class, PresenceCapability.class, ChannelHelperCapability.class),
List.of(PresenceChannelHelper.class, SignalChannelHelper.class, EventChannelHelper.class)),
- @SerializedName("NIS")
- SIREN(FeatureArea.SECURITY, "NIS", HOME, List.of(ChannelHelperCapability.class),
- List.of(BatteryChannelHelper.class, TimestampChannelHelper.class, SignalChannelHelper.class)),
- @SerializedName("NDB")
+
DOORBELL(FeatureArea.SECURITY, "NDB", HOME,
List.of(EventCapability.class, CameraCapability.class, ChannelHelperCapability.class),
List.of(DoorbellChannelHelper.class, SignalChannelHelper.class, EventDoorbellChannelHelper.class)),
- @SerializedName("NAMain")
+
WEATHER_STATION(FeatureArea.WEATHER, "NAMain", ACCOUNT,
List.of(DeviceCapability.class, WeatherCapability.class, MeasureCapability.class,
ChannelHelperCapability.class),
List.of(PressureExtChannelHelper.class, NoiseChannelHelper.class, HumidityChannelHelper.class,
TemperatureExtChannelHelper.class, AirQualityChannelHelper.class, LocationChannelHelper.class,
TimestampExtChannelHelper.class, MeasuresChannelHelper.class, SignalChannelHelper.class)),
- @SerializedName("NAModule1")
+
OUTDOOR(FeatureArea.WEATHER, "NAModule1", WEATHER_STATION,
List.of(MeasureCapability.class, ChannelHelperCapability.class),
List.of(HumidityChannelHelper.class, TemperatureOutChannelHelper.class, BatteryChannelHelper.class,
MeasuresChannelHelper.class, TimestampExtChannelHelper.class, SignalChannelHelper.class)),
- @SerializedName("NAModule2")
+
WIND(FeatureArea.WEATHER, "NAModule2", WEATHER_STATION, List.of(ChannelHelperCapability.class),
List.of(WindChannelHelper.class, BatteryChannelHelper.class, TimestampExtChannelHelper.class,
SignalChannelHelper.class)),
- @SerializedName("NAModule3")
+
RAIN(FeatureArea.WEATHER, "NAModule3", WEATHER_STATION,
List.of(MeasureCapability.class, ChannelHelperCapability.class),
List.of(RainChannelHelper.class, BatteryChannelHelper.class, MeasuresChannelHelper.class,
TimestampExtChannelHelper.class, SignalChannelHelper.class)),
- @SerializedName("NAModule4")
+
INDOOR(FeatureArea.WEATHER, "NAModule4", WEATHER_STATION,
List.of(MeasureCapability.class, ChannelHelperCapability.class),
List.of(HumidityChannelHelper.class, TemperatureExtChannelHelper.class, AirQualityChannelHelper.class,
BatteryChannelHelper.class, MeasuresChannelHelper.class, TimestampExtChannelHelper.class,
SignalChannelHelper.class)),
- @SerializedName("NHC")
+
HOME_COACH(FeatureArea.AIR_CARE, "NHC", ACCOUNT,
List.of(DeviceCapability.class, AirCareCapability.class, MeasureCapability.class,
ChannelHelperCapability.class),
List.of(NoiseChannelHelper.class, HumidityChannelHelper.class, AirQualityExtChannelHelper.class,
TemperatureChannelHelper.class, PressureChannelHelper.class, TimestampExtChannelHelper.class,
SignalChannelHelper.class, MeasuresChannelHelper.class, LocationChannelHelper.class)),
- @SerializedName("NAPlug")
+
PLUG(FeatureArea.ENERGY, "NAPlug", HOME, List.of(ChannelHelperCapability.class),
List.of(SignalChannelHelper.class)),
- @SerializedName("NATherm1")
- THERMOSTAT(FeatureArea.ENERGY, "NATherm1", HOME, List.of(ChannelHelperCapability.class),
+
+ VALVE(FeatureArea.ENERGY, "NRV", PLUG, List.of(ChannelHelperCapability.class),
+ List.of(BatteryExtChannelHelper.class, SignalChannelHelper.class)),
+
+ THERMOSTAT(FeatureArea.ENERGY, "NATherm1", PLUG, List.of(ChannelHelperCapability.class),
List.of(Therm1ChannelHelper.class, BatteryExtChannelHelper.class, SignalChannelHelper.class)),
- @SerializedName("NARoom")
+
ROOM(FeatureArea.ENERGY, "NARoom", HOME, List.of(RoomCapability.class, ChannelHelperCapability.class),
- List.of(RoomChannelHelper.class, SetpointChannelHelper.class)),
- @SerializedName("NRV")
- VALVE(FeatureArea.ENERGY, "NRV", HOME, List.of(ChannelHelperCapability.class),
- List.of(BatteryExtChannelHelper.class, SignalChannelHelper.class));
+ List.of(RoomChannelHelper.class, SetpointChannelHelper.class));
public static final EnumSet<ModuleType> AS_SET = EnumSet.allOf(ModuleType.class);
import static org.openhab.core.library.unit.MetricPrefix.*;
import java.net.URI;
+import java.util.Arrays;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
UNKNOWN;
}
- private static final Set<Scope> SMOKE = Set.of(Scope.READ_SMOKEDETECTOR);
- private static final Set<Scope> WELCOME = Set.of(Scope.READ_CAMERA, Scope.WRITE_CAMERA, Scope.ACCESS_CAMERA);
- private static final Set<Scope> DOORBELL = Set.of(Scope.READ_DOORBELL, Scope.WRITE_DOORBELL, Scope.ACCESS_DOORBELL);
- private static final Set<Scope> PRESENCE = Set.of(Scope.READ_PRESENCE, Scope.WRITE_PRESENCE, Scope.ACCESS_PRESENCE);
-
- // Radio signal quality thresholds
- static final int[] WIFI_SIGNAL_LEVELS = new int[] { 99, 84, 69, 54 }; // Resp : bad, average, good, full
- static final int[] RADIO_SIGNAL_LEVELS = new int[] { 90, 80, 70, 60 }; // Resp : low, medium, high, full
+ private static final Scope[] SMOKE_SCOPES = { Scope.READ_SMOKEDETECTOR };
+ 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 };
+ private static final Scope[] WELCOME_SCOPES = { Scope.READ_CAMERA, Scope.WRITE_CAMERA, Scope.ACCESS_CAMERA };
+ private static final Scope[] DOORBELL_SCOPES = { Scope.READ_DOORBELL, Scope.WRITE_DOORBELL, Scope.ACCESS_DOORBELL };
+ private static final Scope[] PRESENCE_SCOPES = { Scope.READ_PRESENCE, Scope.WRITE_PRESENCE, Scope.ACCESS_PRESENCE };
public static enum FeatureArea {
- AIR_CARE(Scope.READ_HOMECOACH),
- WEATHER(Scope.READ_STATION),
- ENERGY(Scope.READ_THERMOSTAT, Scope.WRITE_THERMOSTAT),
- SECURITY(Stream.of(WELCOME, PRESENCE, SMOKE, DOORBELL).flatMap(Set::stream).toArray(Scope[]::new)),
+ AIR_CARE(AIR_CARE_SCOPES),
+ WEATHER(WEATHER_SCOPES),
+ ENERGY(THERMOSTAT_SCOPES),
+ SECURITY(WELCOME_SCOPES, PRESENCE_SCOPES, SMOKE_SCOPES, DOORBELL_SCOPES),
NONE();
- public static final Set<FeatureArea> AS_SET = EnumSet.allOf(FeatureArea.class);
-
- public static String toScopeString(Set<FeatureArea> featureSet) {
- return featureSet.stream().map(fa -> fa.scopes).flatMap(Set::stream).map(s -> s.name().toLowerCase())
- .collect(Collectors.joining(" "));
- }
+ public static String ALL_SCOPES = EnumSet.allOf(FeatureArea.class).stream().map(fa -> fa.scopes)
+ .flatMap(Set::stream).map(s -> s.name().toLowerCase()).collect(Collectors.joining(" "));
public final Set<Scope> scopes;
- FeatureArea(Scope... scopes) {
- this.scopes = Set.of(scopes);
+ FeatureArea(Scope[]... scopeArrays) {
+ this.scopes = Stream.of(scopeArrays).flatMap(Arrays::stream).collect(Collectors.toSet());
}
}
+ // Radio signal quality thresholds
+ static final int[] WIFI_SIGNAL_LEVELS = new int[] { 99, 84, 69, 54 }; // Resp : bad, average, good, full
+ static final int[] RADIO_SIGNAL_LEVELS = new int[] { 90, 80, 70, 60 }; // Resp : low, medium, high, full
+
// Thermostat definitions
public static enum SetpointMode {
@SerializedName("program")
public List<String> getModuleBridged() {
return moduleBridged;
}
+
+ @Override
+ public boolean isIgnoredForThingUpdate() {
+ return true;
+ }
}
--- /dev/null
+/**
+ * Copyright (c) 2010-2022 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.deserialization;
+
+import java.lang.reflect.Type;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.netatmo.internal.api.data.ModuleType;
+
+import com.google.gson.JsonDeserializationContext;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonElement;
+
+/**
+ * Specialized deserializer for ModuleType class
+ *
+ * @author Gaël L'hopital - Initial contribution
+ */
+@NonNullByDefault
+public class ModuleTypeDeserializer implements JsonDeserializer<ModuleType> {
+
+ @Override
+ public @Nullable ModuleType deserialize(JsonElement json, Type clazz, JsonDeserializationContext context) {
+ String string = json.getAsString();
+ return ModuleType.AS_SET.stream().filter(mt -> mt.apiName.equalsIgnoreCase(string)).findFirst()
+ .orElse(ModuleType.UNKNOWN);
+ }
+}
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.netatmo.internal.api.NetatmoException;
+import org.openhab.binding.netatmo.internal.api.data.ModuleType;
import org.openhab.core.i18n.TimeZoneProvider;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.OpenClosedType;
.registerTypeAdapterFactory(new StrictEnumTypeAdapterFactory())
.registerTypeAdapter(NAObjectMap.class, new NAObjectMapDeserializer())
.registerTypeAdapter(NAPushType.class, new NAPushTypeDeserializer())
+ .registerTypeAdapter(ModuleType.class, new ModuleTypeDeserializer())
.registerTypeAdapter(ZonedDateTime.class,
(JsonDeserializer<ZonedDateTime>) (json, type, jsonDeserializationContext) -> {
long netatmoTS = json.getAsJsonPrimitive().getAsLong();
*/
package org.openhab.binding.netatmo.internal.discovery;
+import java.util.HashMap;
+import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
@NonNullByDefault
public class NetatmoDiscoveryService extends AbstractDiscoveryService implements ThingHandlerService, DiscoveryService {
private static final Set<ModuleType> SKIPPED_TYPES = Set.of(ModuleType.UNKNOWN, ModuleType.ACCOUNT);
- private static final int DISCOVER_TIMEOUT_SECONDS = 5;
+ private static final int DISCOVER_TIMEOUT_SECONDS = 3;
private final Logger logger = LoggerFactory.getLogger(NetatmoDiscoveryService.class);
private @Nullable ApiBridgeHandler handler;
public void startScan() {
ApiBridgeHandler localHandler = handler;
if (localHandler != null) {
- ThingUID apiBridgeUID = localHandler.getThing().getUID();
+ ThingUID accountUID = localHandler.getThing().getUID();
try {
AircareApi airCareApi = localHandler.getRestManager(AircareApi.class);
if (airCareApi != null) { // Search Healthy Home Coaches
ListBodyResponse<NAMain> body = airCareApi.getHomeCoachData(null).getBody();
if (body != null) {
- body.getElements().stream().forEach(homeCoach -> createThing(homeCoach, apiBridgeUID));
+ body.getElements().stream().forEach(homeCoach -> createThing(homeCoach, accountUID));
}
}
if (localHandler.getReadFriends()) {
if (weatherApi != null) { // Search favorite stations
weatherApi.getFavoriteAndGuestStationsData().stream().filter(NAMain::isReadOnly)
.forEach(station -> {
- ThingUID bridgeUID = createThing(station, apiBridgeUID);
+ ThingUID bridgeUID = createThing(station, accountUID);
station.getModules().values().stream()
.forEach(module -> createThing(module, bridgeUID));
});
}
}
HomeApi homeApi = localHandler.getRestManager(HomeApi.class);
- if (homeApi != null) { // Search all the rest
+ if (homeApi != null) { // Search those who depend from a home
homeApi.getHomesData(null, null).stream().filter(h -> !h.getFeatures().isEmpty()).forEach(home -> {
- ThingUID homeUID = createThing(home, apiBridgeUID);
+ ThingUID homeUID = createThing(home, accountUID);
+
home.getKnownPersons().forEach(person -> createThing(person, homeUID));
- home.getModules().values().stream().forEach(device -> {
- ModuleType deviceType = device.getType();
- String deviceBridge = device.getBridge();
- ThingUID bridgeUID = deviceBridge != null && deviceType.getBridge() != ModuleType.HOME
- ? findThingUID(deviceType.getBridge(), deviceBridge, apiBridgeUID)
- : deviceType.getBridge() == ModuleType.HOME ? homeUID : apiBridgeUID;
- createThing(device, bridgeUID);
- });
+
+ Map<String, ThingUID> bridgesUids = new HashMap<>();
+
home.getRooms().values().stream().forEach(room -> {
room.getModuleIds().stream().map(id -> home.getModules().get(id))
.map(m -> m != null ? m.getType().feature : FeatureArea.NONE)
.filter(f -> FeatureArea.ENERGY.equals(f)).findAny()
- .ifPresent(f -> createThing(room, homeUID));
+ .ifPresent(f -> bridgesUids.put(room.getId(), createThing(room, homeUID)));
});
+
+ // Creating modules that have no bridge first
+ home.getModules().values().stream().filter(module -> module.getBridge() == null)
+ .forEach(device -> bridgesUids.put(device.getId(), createThing(device, homeUID)));
+ // Then the others
+ home.getModules().values().stream().filter(module -> module.getBridge() != null).forEach(
+ device -> createThing(device, bridgesUids.getOrDefault(device.getBridge(), homeUID)));
});
}
} catch (NetatmoException e) {
}
}
- private ThingUID findThingUID(ModuleType thingType, String thingId, @Nullable ThingUID brigdeUID) {
- for (ThingTypeUID supported : getSupportedThingTypes()) {
- ThingTypeUID thingTypeUID = thingType.thingTypeUID;
- if (supported.equals(thingTypeUID)) {
- String id = thingId.replaceAll("[^a-zA-Z0-9_]", "");
- return brigdeUID == null ? new ThingUID(supported, id) : new ThingUID(supported, brigdeUID, id);
- }
- }
- throw new IllegalArgumentException("Unsupported device type discovered : " + thingType);
+ private ThingUID findThingUID(ModuleType thingType, String thingId, ThingUID bridgeUID) {
+ ThingTypeUID thingTypeUID = thingType.thingTypeUID;
+ return getSupportedThingTypes().stream().filter(supported -> supported.equals(thingTypeUID)).findFirst()
+ .map(supported -> new ThingUID(supported, bridgeUID, thingId.replaceAll("[^a-zA-Z0-9_]", "")))
+ .orElseThrow(() -> new IllegalArgumentException("Unsupported device type discovered : " + thingType));
}
- private ThingUID createThing(NAModule module, @Nullable ThingUID bridgeUID) {
+ private ThingUID createThing(NAModule module, ThingUID bridgeUID) {
ThingUID moduleUID = findThingUID(module.getType(), module.getId(), bridgeUID);
DiscoveryResultBuilder resultBuilder = DiscoveryResultBuilder.create(moduleUID)
.withProperty(NAThingConfiguration.ID, module.getId())
.withRepresentationProperty(NAThingConfiguration.ID)
- .withLabel(module.getName() != null ? module.getName() : module.getId());
- if (bridgeUID != null) {
- resultBuilder.withBridge(bridgeUID);
- }
+ .withLabel(module.getName() != null ? module.getName() : module.getId()).withBridge(bridgeUID);
thingDiscovered(resultBuilder.build());
return moduleUID;
}
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.netatmo.internal.api.data.ModuleType;
import org.openhab.binding.netatmo.internal.api.dto.NAObject;
+import org.openhab.binding.netatmo.internal.api.dto.NAThing;
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;
}
default void setNewData(NAObject newData) {
+ if (newData instanceof NAThing) {
+ NAThing thingData = (NAThing) newData;
+ if (getId().equals(thingData.getBridge())) {
+ getActiveChildren().stream().filter(child -> child.getId().equals(thingData.getId())).findFirst()
+ .ifPresent(child -> child.setNewData(thingData));
+ return;
+ }
+ }
String finalReason = null;
for (Capability cap : getCapabilities().values()) {
String thingStatusReason = cap.setNewData(newData);
properties = new HashMap<>(thing.getProperties());
firstLaunch = properties.isEmpty();
if (firstLaunch && !moduleType.isLogical()) {
+ String name = moduleType.apiName.isBlank() ? moduleType.name() : moduleType.apiName;
+ properties.put(PROPERTY_MODEL_ID, name);
properties.put(PROPERTY_VENDOR, VENDOR);
- properties.put(PROPERTY_MODEL_ID, moduleType.name());
}
statusReason = null;
}
protected void updateHomeData(HomeData homeData) {
NAObjectMap<HomeDataRoom> rooms = homeData.getRooms();
NAObjectMap<HomeDataModule> modules = homeData.getModules();
- handler.getActiveChildren().forEach(handler -> {
- HomeDataRoom roomData = rooms.get(handler.getId());
- if (roomData != null) {
+ handler.getActiveChildren().forEach(childHandler -> {
+ String childId = childHandler.getId();
+ rooms.getOpt(childId).ifPresentOrElse(roomData -> {
roomData.setIgnoredForThingUpdate(true);
- handler.setNewData(roomData);
- }
- HomeDataModule moduleData = modules.get(handler.getId());
- if (moduleData != null) {
- moduleData.setIgnoredForThingUpdate(true);
- handler.setNewData(moduleData);
- }
+ childHandler.setNewData(roomData);
+ }, () -> {
+ modules.getOpt(childId).ifPresent(childData -> {
+ childData.setIgnoredForThingUpdate(true);
+ childHandler.setNewData(childData);
+ });
+ modules.values().stream().filter(module -> childId.equals(module.getBridge()))
+ .forEach(bridgedModule -> {
+ childHandler.setNewData(bridgedModule);
+ });
+ });
});
descriptionProvider.setStateOptions(new ChannelUID(thing.getUID(), GROUP_ENERGY, CHANNEL_PLANNING),
homeData.getThermSchedules().stream().map(p -> new StateOption(p.getId(), p.getName()))
protected void updateHomeStatus(HomeStatus homeStatus) {
NAObjectMap<Room> rooms = homeStatus.getRooms();
NAObjectMap<HomeStatusModule> modules = homeStatus.getModules();
- handler.getActiveChildren().forEach(handler -> {
- Room roomData = rooms.get(handler.getId());
- if (roomData != null) {
- handler.setNewData(roomData);
- }
- HomeStatusModule data = modules.get(handler.getId());
- if (data != null) {
- handler.setNewData(data);
- }
+ handler.getActiveChildren().forEach(childHandler -> {
+ String childId = childHandler.getId();
+ rooms.getOpt(childId).ifPresentOrElse(roomData -> childHandler.setNewData(roomData), () -> {
+ modules.getOpt(childId).ifPresentOrElse(childData -> {
+ childHandler.setNewData(childData);
+ modules.values().stream().filter(module -> childId.equals(module.getBridge()))
+ .forEach(bridgedModule -> {
+ childHandler.setNewData(bridgedModule);
+ });
+
+ }, () -> {
+ // This module is not present in the homestatus data, so it is considered as unreachable
+ HomeStatusModule module = new HomeStatusModule();
+ module.setReachable(false);
+ childHandler.setNewData(module);
+ });
+ });
});
}
NAObjectMap<HomeDataModule> modules = homeData.getModules();
handler.getActiveChildren().forEach(childHandler -> {
String childId = childHandler.getId();
- persons.getOpt(childId).ifPresentOrElse(person -> {
- person.setIgnoredForThingUpdate(true);
- childHandler.setNewData(person);
+ persons.getOpt(childId).ifPresentOrElse(personData -> {
+ personData.setIgnoredForThingUpdate(true);
+ childHandler.setNewData(personData);
}, () -> {
- modules.getOpt(childId).ifPresent(module -> {
- module.setIgnoredForThingUpdate(true);
- childHandler.setNewData(module);
+ modules.getOpt(childId).ifPresent(childData -> {
+ childData.setIgnoredForThingUpdate(true);
+ childHandler.setNewData(childData);
});
+ modules.values().stream().filter(module -> childId.equals(module.getBridge()))
+ .forEach(bridgedModule -> {
+ childHandler.setNewData(bridgedModule);
+ });
});
});
}
NAObjectMap<HomeStatusModule> modules = homeStatus.getModules();
handler.getActiveChildren().forEach(childHandler -> {
String childId = childHandler.getId();
- persons.getOpt(childId).ifPresentOrElse(person -> childHandler.setNewData(person), () -> {
- modules.getOpt(childId).ifPresentOrElse(module -> childHandler.setNewData(module), () -> {
+ persons.getOpt(childId).ifPresentOrElse(personData -> childHandler.setNewData(personData), () -> {
+ modules.getOpt(childId).ifPresentOrElse(childData -> {
+ childHandler.setNewData(childData);
+ modules.values().stream().filter(module -> childId.equals(module.getBridge()))
+ .forEach(bridgedModule -> {
+ childHandler.setNewData(bridgedModule);
+ });
+
+ }, () -> {
// This module is not present in the homestatus data, so it is considered as unreachable
HomeStatusModule module = new HomeStatusModule();
module.setReachable(false);
--- /dev/null
+/**
+ * Copyright (c) 2010-2022 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.channelhelper;
+
+import static org.openhab.binding.netatmo.internal.NetatmoBindingConstants.*;
+import static org.openhab.binding.netatmo.internal.utils.ChannelTypeUtils.toStringType;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.netatmo.internal.api.dto.HomeStatusModule;
+import org.openhab.binding.netatmo.internal.api.dto.NAThing;
+import org.openhab.core.config.core.Configuration;
+import org.openhab.core.types.State;
+import org.openhab.core.types.UnDefType;
+
+/**
+ * The {@link SirenChannelHelper} handles specific behavior of the siren module
+ *
+ * @author Gaël L'hopital - Initial contribution
+ *
+ */
+@NonNullByDefault
+public class SirenChannelHelper extends ChannelHelper {
+
+ public SirenChannelHelper() {
+ super(GROUP_SIREN);
+ }
+
+ @Override
+ protected @Nullable State internalGetProperty(String channelId, NAThing naThing, Configuration config) {
+ if (naThing instanceof HomeStatusModule) {
+ HomeStatusModule homeStatus = (HomeStatusModule) naThing;
+ switch (channelId) {
+ case CHANNEL_MONITORING:
+ return homeStatus.getMonitoring();
+ case CHANNEL_STATUS:
+ return homeStatus.getStatus().map(status -> toStringType(status)).orElse(UnDefType.UNDEF);
+ }
+ }
+ return null;
+ }
+}
channel-group-type.netatmo.person.label = Person
channel-group-type.netatmo.person.channel.last-seen.label = Last Seen
channel-group-type.netatmo.person.channel.last-seen.description = Moment when this person was last seen.
-channel-group-type.netatmo.plug.label = Thermostat Plug
channel-group-type.netatmo.presence.label = Presence Camera
channel-group-type.netatmo.pressure-extended.label = Pressure
channel-group-type.netatmo.pressure-extended.channel.trend.label = Pressure Trend
channel-group-type.netatmo.setpoint.channel.start.label = Setpoint Start
channel-group-type.netatmo.setpoint.channel.start.description = Start time of the currently applied setpoint.
channel-group-type.netatmo.signal.label = Signal
+channel-group-type.netatmo.siren.label = Siren Status
channel-group-type.netatmo.status-doorbell.label = Camera Status
channel-group-type.netatmo.status.label = Camera Status
channel-group-type.netatmo.sub-event-doorbell.label = Sub Event
channel-type.netatmo.setpoint-duration.description = Default duration of manual setpoint changes.
channel-type.netatmo.setpoint.label = Setpoint
channel-type.netatmo.setpoint.description = Thermostat temperature setpoint.
+channel-type.netatmo.siren-monitoring.label = Monitoring
+channel-type.netatmo.siren-monitoring.description = Monitoring state of the equipment
+channel-type.netatmo.siren-status.label = Status
+channel-type.netatmo.siren-status.description = Status of the siren
+channel-type.netatmo.siren-status.state.option.no_sound = Silent
+channel-type.netatmo.siren-status.state.option.sound = Alarm
channel-type.netatmo.th-mode.label = Thermostat Mode
channel-type.netatmo.th-mode.description = Chosen thermostat mode (home, frost guard, manual, max).
channel-type.netatmo.th-mode.state.option.HOME = Home
channel-group-type.netatmo.person.label = Persona
channel-group-type.netatmo.person.channel.last-seen.label = Visto l'ultima volta
channel-group-type.netatmo.person.channel.last-seen.description = Momento quando questa persona è stata vista per l'ultima volta.
-channel-group-type.netatmo.plug.label = Spina Termostato
channel-group-type.netatmo.presence.label = Fotocamera Presenza
channel-group-type.netatmo.pressure-extended.label = Pressione
channel-group-type.netatmo.pressure-extended.channel.trend.label = Grafico Pressione
<description>Monitoring state of the camera</description>
</channel-type>
+ <channel-type id="siren-monitoring">
+ <item-type>Switch</item-type>
+ <label>Monitoring</label>
+ <description>Monitoring state of the equipment</description>
+ <state readOnly="true"/>
+ </channel-type>
+
+ <channel-type id="siren-status">
+ <item-type>String</item-type>
+ <label>Status</label>
+ <description>Status of the siren</description>
+ <state pattern="%s" readOnly="true">
+ <options>
+ <option value="no_sound">Silent</option>
+ <option value="sound">Alarm</option>
+ </options>
+ </state>
+ </channel-type>
+
<channel-type id="window-open">
<item-type>Contact</item-type>
<label>Window Status</label>
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0"
xsi:schemaLocation="https://openhab.org/schemas/thing-description/v1.0.0 https://openhab.org/schemas/thing-description-1.0.0.xsd">
- <channel-group-type id="plug">
- <label>Thermostat Plug</label>
- <channels>
- <channel id="boiler-status" typeId="boiler-status"/>
- </channels>
- </channel-group-type>
-
<channel-group-type id="temperature-room">
<label>Room Temperature</label>
<channels>
</channels>
</channel-group-type>
+ <channel-group-type id="siren">
+ <label>Siren Status</label>
+ <channels>
+ <channel id="status" typeId="siren-status"/>
+ <channel id="monitoring" typeId="siren-monitoring"/>
+ </channels>
+ </channel-group-type>
+
<channel-group-type id="status-doorbell">
<label>Camera Status</label>
<channels>