[netatmo] Add siren device (#12805)

* Starting Siren addition

Signed-off-by: clinique <gael@lhopital.org>
This commit is contained in:
Gaël L'hopital 2022-05-28 11:26:30 +02:00 committed by GitHub
parent 5291d7ddc9
commit 1aec6c9b30
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 289 additions and 120 deletions

View File

@ -5,7 +5,9 @@ The Netatmo binding integrates the following Netatmo products:
- *Personal Weather Station*. Reports temperature, humidity, air pressure, carbon dioxide concentration in the air, as well as the ambient noise level. - *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. - *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. - *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. - *Outdoor Camera / Presence*. Reports last event, consult picture and video from event/camera.
- *Doorbell*
See https://www.netatmo.com/ for details on their product. See https://www.netatmo.com/ for details on their product.
@ -505,7 +507,7 @@ Warnings:
(*) This channel is configurable : low, poor, high. (*) 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 | | Channel Group | Channel ID | Item Type | Read/Write | Description |
|---------------|-------------------|--------------|------------|---------------------------------------------------------------------------------------------------------------------------------------------| |---------------|-------------------|--------------|------------|---------------------------------------------------------------------------------------------------------------------------------------------|
@ -532,6 +534,18 @@ Warnings:
Note: live feeds either locally or via VPN are not available in Netatmo API. 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 ### Welcome Person

View File

@ -53,6 +53,7 @@ public class NetatmoBindingConstants {
public static final String GROUP_CAM_STATUS = "status"; public static final String GROUP_CAM_STATUS = "status";
public static final String GROUP_CAM_LIVE = "live"; public static final String GROUP_CAM_LIVE = "live";
public static final String GROUP_PRESENCE = "presence"; 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_PERSON = "person";
public static final String GROUP_PROPERTIES = "properties"; public static final String GROUP_PROPERTIES = "properties";
public static final String GROUP_SETPOINT = "setpoint"; public static final String GROUP_SETPOINT = "setpoint";
@ -105,6 +106,7 @@ public class NetatmoBindingConstants {
public static final String CHANNEL_SUM_RAIN1 = "sum-1"; public static final String CHANNEL_SUM_RAIN1 = "sum-1";
public static final String CHANNEL_SUM_RAIN24 = "sum-24"; public static final String CHANNEL_SUM_RAIN24 = "sum-24";
public static final String CHANNEL_WIND_ANGLE = "angle"; 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_WIND_STRENGTH = "strength";
public static final String CHANNEL_MAX_WIND_STRENGTH = "max-strength"; public static final String CHANNEL_MAX_WIND_STRENGTH = "max-strength";
public static final String CHANNEL_DATE_MAX_WIND_STRENGTH = "max-strength-date"; public static final String CHANNEL_DATE_MAX_WIND_STRENGTH = "max-strength-date";

View File

@ -43,7 +43,6 @@ import org.slf4j.LoggerFactory;
*/ */
@NonNullByDefault @NonNullByDefault
public class AuthenticationApi extends RestManager { 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 OAUTH_BUILDER = getApiBaseBuilder().path(PATH_OAUTH);
private static final UriBuilder AUTH_BUILDER = OAUTH_BUILDER.clone().path(SUB_PATH_AUTHORIZE); 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(); private static final URI TOKEN_URI = OAUTH_BUILDER.clone().path(SUB_PATH_TOKEN).build();
@ -62,7 +61,7 @@ public class AuthenticationApi extends RestManager {
public String authorize(ApiHandlerConfiguration credentials, @Nullable String code, @Nullable String redirectUri) public String authorize(ApiHandlerConfiguration credentials, @Nullable String code, @Nullable String redirectUri)
throws NetatmoException { throws NetatmoException {
if (!(credentials.clientId.isBlank() || credentials.clientSecret.isBlank())) { 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; String refreshToken = credentials.refreshToken;
if (!refreshToken.isBlank()) { if (!refreshToken.isBlank()) {
params.put(REFRESH_TOKEN, refreshToken); params.put(REFRESH_TOKEN, refreshToken);
@ -118,7 +117,7 @@ public class AuthenticationApi extends RestManager {
} }
public static UriBuilder getAuthorizationBuilder(String clientId) { public static UriBuilder getAuthorizationBuilder(String clientId) {
return AUTH_BUILDER.clone().queryParam(CLIENT_ID, clientId).queryParam(SCOPE, ALL_SCOPES).queryParam(STATE, return AUTH_BUILDER.clone().queryParam(CLIENT_ID, clientId).queryParam(SCOPE, FeatureArea.ALL_SCOPES)
clientId); .queryParam(STATE, clientId);
} }
} }

View File

@ -59,6 +59,7 @@ import org.openhab.binding.netatmo.internal.handler.channelhelper.RainChannelHel
import org.openhab.binding.netatmo.internal.handler.channelhelper.RoomChannelHelper; 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.SetpointChannelHelper;
import org.openhab.binding.netatmo.internal.handler.channelhelper.SignalChannelHelper; 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.TemperatureChannelHelper;
import org.openhab.binding.netatmo.internal.handler.channelhelper.TemperatureExtChannelHelper; 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.TemperatureOutChannelHelper;
@ -68,8 +69,6 @@ import org.openhab.binding.netatmo.internal.handler.channelhelper.TimestampExtCh
import org.openhab.binding.netatmo.internal.handler.channelhelper.WindChannelHelper; import org.openhab.binding.netatmo.internal.handler.channelhelper.WindChannelHelper;
import org.openhab.core.thing.ThingTypeUID; import org.openhab.core.thing.ThingTypeUID;
import com.google.gson.annotations.SerializedName;
/** /**
* This enum all handled Netatmo modules and devices along with their capabilities * This enum all handled Netatmo modules and devices along with their capabilities
* *
@ -79,75 +78,76 @@ import com.google.gson.annotations.SerializedName;
public enum ModuleType { public enum ModuleType {
UNKNOWN(FeatureArea.NONE, "", null, List.of(), List.of()), UNKNOWN(FeatureArea.NONE, "", null, List.of(), List.of()),
ACCOUNT(FeatureArea.NONE, "", null, List.of(), List.of()), ACCOUNT(FeatureArea.NONE, "", null, List.of(), List.of()),
@SerializedName("NAHome")
HOME(FeatureArea.NONE, "NAHome", ACCOUNT, HOME(FeatureArea.NONE, "NAHome", ACCOUNT,
List.of(DeviceCapability.class, EventCapability.class, HomeCapability.class, ChannelHelperCapability.class), List.of(DeviceCapability.class, EventCapability.class, HomeCapability.class, ChannelHelperCapability.class),
List.of(HomeSecurityChannelHelper.class, HomeEnergyChannelHelper.class)), List.of(HomeSecurityChannelHelper.class, HomeEnergyChannelHelper.class)),
@SerializedName("NAPerson")
PERSON(FeatureArea.SECURITY, "NAPerson", HOME, PERSON(FeatureArea.SECURITY, "NAPerson", HOME,
List.of(EventCapability.class, PersonCapability.class, ChannelHelperCapability.class), List.of(EventCapability.class, PersonCapability.class, ChannelHelperCapability.class),
List.of(PersonChannelHelper.class, EventPersonChannelHelper.class)), List.of(PersonChannelHelper.class, EventPersonChannelHelper.class)),
@SerializedName("NACamera")
WELCOME(FeatureArea.SECURITY, "NACamera", HOME, WELCOME(FeatureArea.SECURITY, "NACamera", HOME,
List.of(EventCapability.class, CameraCapability.class, ChannelHelperCapability.class), List.of(EventCapability.class, CameraCapability.class, ChannelHelperCapability.class),
List.of(CameraChannelHelper.class, SignalChannelHelper.class, EventChannelHelper.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, PRESENCE(FeatureArea.SECURITY, "NOC", HOME,
List.of(EventCapability.class, PresenceCapability.class, ChannelHelperCapability.class), List.of(EventCapability.class, PresenceCapability.class, ChannelHelperCapability.class),
List.of(PresenceChannelHelper.class, SignalChannelHelper.class, EventChannelHelper.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, DOORBELL(FeatureArea.SECURITY, "NDB", HOME,
List.of(EventCapability.class, CameraCapability.class, ChannelHelperCapability.class), List.of(EventCapability.class, CameraCapability.class, ChannelHelperCapability.class),
List.of(DoorbellChannelHelper.class, SignalChannelHelper.class, EventDoorbellChannelHelper.class)), List.of(DoorbellChannelHelper.class, SignalChannelHelper.class, EventDoorbellChannelHelper.class)),
@SerializedName("NAMain")
WEATHER_STATION(FeatureArea.WEATHER, "NAMain", ACCOUNT, WEATHER_STATION(FeatureArea.WEATHER, "NAMain", ACCOUNT,
List.of(DeviceCapability.class, WeatherCapability.class, MeasureCapability.class, List.of(DeviceCapability.class, WeatherCapability.class, MeasureCapability.class,
ChannelHelperCapability.class), ChannelHelperCapability.class),
List.of(PressureExtChannelHelper.class, NoiseChannelHelper.class, HumidityChannelHelper.class, List.of(PressureExtChannelHelper.class, NoiseChannelHelper.class, HumidityChannelHelper.class,
TemperatureExtChannelHelper.class, AirQualityChannelHelper.class, LocationChannelHelper.class, TemperatureExtChannelHelper.class, AirQualityChannelHelper.class, LocationChannelHelper.class,
TimestampExtChannelHelper.class, MeasuresChannelHelper.class, SignalChannelHelper.class)), TimestampExtChannelHelper.class, MeasuresChannelHelper.class, SignalChannelHelper.class)),
@SerializedName("NAModule1")
OUTDOOR(FeatureArea.WEATHER, "NAModule1", WEATHER_STATION, OUTDOOR(FeatureArea.WEATHER, "NAModule1", WEATHER_STATION,
List.of(MeasureCapability.class, ChannelHelperCapability.class), List.of(MeasureCapability.class, ChannelHelperCapability.class),
List.of(HumidityChannelHelper.class, TemperatureOutChannelHelper.class, BatteryChannelHelper.class, List.of(HumidityChannelHelper.class, TemperatureOutChannelHelper.class, BatteryChannelHelper.class,
MeasuresChannelHelper.class, TimestampExtChannelHelper.class, SignalChannelHelper.class)), MeasuresChannelHelper.class, TimestampExtChannelHelper.class, SignalChannelHelper.class)),
@SerializedName("NAModule2")
WIND(FeatureArea.WEATHER, "NAModule2", WEATHER_STATION, List.of(ChannelHelperCapability.class), WIND(FeatureArea.WEATHER, "NAModule2", WEATHER_STATION, List.of(ChannelHelperCapability.class),
List.of(WindChannelHelper.class, BatteryChannelHelper.class, TimestampExtChannelHelper.class, List.of(WindChannelHelper.class, BatteryChannelHelper.class, TimestampExtChannelHelper.class,
SignalChannelHelper.class)), SignalChannelHelper.class)),
@SerializedName("NAModule3")
RAIN(FeatureArea.WEATHER, "NAModule3", WEATHER_STATION, RAIN(FeatureArea.WEATHER, "NAModule3", WEATHER_STATION,
List.of(MeasureCapability.class, ChannelHelperCapability.class), List.of(MeasureCapability.class, ChannelHelperCapability.class),
List.of(RainChannelHelper.class, BatteryChannelHelper.class, MeasuresChannelHelper.class, List.of(RainChannelHelper.class, BatteryChannelHelper.class, MeasuresChannelHelper.class,
TimestampExtChannelHelper.class, SignalChannelHelper.class)), TimestampExtChannelHelper.class, SignalChannelHelper.class)),
@SerializedName("NAModule4")
INDOOR(FeatureArea.WEATHER, "NAModule4", WEATHER_STATION, INDOOR(FeatureArea.WEATHER, "NAModule4", WEATHER_STATION,
List.of(MeasureCapability.class, ChannelHelperCapability.class), List.of(MeasureCapability.class, ChannelHelperCapability.class),
List.of(HumidityChannelHelper.class, TemperatureExtChannelHelper.class, AirQualityChannelHelper.class, List.of(HumidityChannelHelper.class, TemperatureExtChannelHelper.class, AirQualityChannelHelper.class,
BatteryChannelHelper.class, MeasuresChannelHelper.class, TimestampExtChannelHelper.class, BatteryChannelHelper.class, MeasuresChannelHelper.class, TimestampExtChannelHelper.class,
SignalChannelHelper.class)), SignalChannelHelper.class)),
@SerializedName("NHC")
HOME_COACH(FeatureArea.AIR_CARE, "NHC", ACCOUNT, HOME_COACH(FeatureArea.AIR_CARE, "NHC", ACCOUNT,
List.of(DeviceCapability.class, AirCareCapability.class, MeasureCapability.class, List.of(DeviceCapability.class, AirCareCapability.class, MeasureCapability.class,
ChannelHelperCapability.class), ChannelHelperCapability.class),
List.of(NoiseChannelHelper.class, HumidityChannelHelper.class, AirQualityExtChannelHelper.class, List.of(NoiseChannelHelper.class, HumidityChannelHelper.class, AirQualityExtChannelHelper.class,
TemperatureChannelHelper.class, PressureChannelHelper.class, TimestampExtChannelHelper.class, TemperatureChannelHelper.class, PressureChannelHelper.class, TimestampExtChannelHelper.class,
SignalChannelHelper.class, MeasuresChannelHelper.class, LocationChannelHelper.class)), SignalChannelHelper.class, MeasuresChannelHelper.class, LocationChannelHelper.class)),
@SerializedName("NAPlug")
PLUG(FeatureArea.ENERGY, "NAPlug", HOME, List.of(ChannelHelperCapability.class), PLUG(FeatureArea.ENERGY, "NAPlug", HOME, List.of(ChannelHelperCapability.class),
List.of(SignalChannelHelper.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)), List.of(Therm1ChannelHelper.class, BatteryExtChannelHelper.class, SignalChannelHelper.class)),
@SerializedName("NARoom")
ROOM(FeatureArea.ENERGY, "NARoom", HOME, List.of(RoomCapability.class, ChannelHelperCapability.class), ROOM(FeatureArea.ENERGY, "NARoom", HOME, List.of(RoomCapability.class, ChannelHelperCapability.class),
List.of(RoomChannelHelper.class, SetpointChannelHelper.class)), List.of(RoomChannelHelper.class, SetpointChannelHelper.class));
@SerializedName("NRV")
VALVE(FeatureArea.ENERGY, "NRV", HOME, List.of(ChannelHelperCapability.class),
List.of(BatteryExtChannelHelper.class, SignalChannelHelper.class));
public static final EnumSet<ModuleType> AS_SET = EnumSet.allOf(ModuleType.class); public static final EnumSet<ModuleType> AS_SET = EnumSet.allOf(ModuleType.class);

View File

@ -17,6 +17,7 @@ import static org.openhab.core.library.CoreItemFactory.*;
import static org.openhab.core.library.unit.MetricPrefix.*; import static org.openhab.core.library.unit.MetricPrefix.*;
import java.net.URI; import java.net.URI;
import java.util.Arrays;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -190,36 +191,35 @@ public class NetatmoConstants {
UNKNOWN; UNKNOWN;
} }
private static final Set<Scope> SMOKE = Set.of(Scope.READ_SMOKEDETECTOR); private static final Scope[] SMOKE_SCOPES = { Scope.READ_SMOKEDETECTOR };
private static final Set<Scope> WELCOME = Set.of(Scope.READ_CAMERA, Scope.WRITE_CAMERA, Scope.ACCESS_CAMERA); private static final Scope[] AIR_CARE_SCOPES = { Scope.READ_HOMECOACH };
private static final Set<Scope> DOORBELL = Set.of(Scope.READ_DOORBELL, Scope.WRITE_DOORBELL, Scope.ACCESS_DOORBELL); private static final Scope[] WEATHER_SCOPES = { Scope.READ_STATION };
private static final Set<Scope> PRESENCE = Set.of(Scope.READ_PRESENCE, Scope.WRITE_PRESENCE, Scope.ACCESS_PRESENCE); 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(AIR_CARE_SCOPES),
WEATHER(WEATHER_SCOPES),
ENERGY(THERMOSTAT_SCOPES),
SECURITY(WELCOME_SCOPES, PRESENCE_SCOPES, SMOKE_SCOPES, DOORBELL_SCOPES),
NONE();
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[]... scopeArrays) {
this.scopes = Stream.of(scopeArrays).flatMap(Arrays::stream).collect(Collectors.toSet());
}
}
// Radio signal quality thresholds // Radio signal quality thresholds
static final int[] WIFI_SIGNAL_LEVELS = new int[] { 99, 84, 69, 54 }; // Resp : bad, average, good, full 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 static final int[] RADIO_SIGNAL_LEVELS = new int[] { 90, 80, 70, 60 }; // Resp : low, medium, high, full
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)),
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 final Set<Scope> scopes;
FeatureArea(Scope... scopes) {
this.scopes = Set.of(scopes);
}
}
// Thermostat definitions // Thermostat definitions
public static enum SetpointMode { public static enum SetpointMode {
@SerializedName("program") @SerializedName("program")

View File

@ -43,4 +43,9 @@ public class HomeDataModule extends NAThing implements NAModule {
public List<String> getModuleBridged() { public List<String> getModuleBridged() {
return moduleBridged; return moduleBridged;
} }
@Override
public boolean isIgnoredForThingUpdate() {
return true;
}
} }

View File

@ -0,0 +1,39 @@
/**
* 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);
}
}

View File

@ -18,6 +18,7 @@ import java.time.ZonedDateTime;
import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.netatmo.internal.api.NetatmoException; 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.i18n.TimeZoneProvider;
import org.openhab.core.library.types.OnOffType; import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.OpenClosedType; import org.openhab.core.library.types.OpenClosedType;
@ -47,6 +48,7 @@ public class NADeserializer {
.registerTypeAdapterFactory(new StrictEnumTypeAdapterFactory()) .registerTypeAdapterFactory(new StrictEnumTypeAdapterFactory())
.registerTypeAdapter(NAObjectMap.class, new NAObjectMapDeserializer()) .registerTypeAdapter(NAObjectMap.class, new NAObjectMapDeserializer())
.registerTypeAdapter(NAPushType.class, new NAPushTypeDeserializer()) .registerTypeAdapter(NAPushType.class, new NAPushTypeDeserializer())
.registerTypeAdapter(ModuleType.class, new ModuleTypeDeserializer())
.registerTypeAdapter(ZonedDateTime.class, .registerTypeAdapter(ZonedDateTime.class,
(JsonDeserializer<ZonedDateTime>) (json, type, jsonDeserializationContext) -> { (JsonDeserializer<ZonedDateTime>) (json, type, jsonDeserializationContext) -> {
long netatmoTS = json.getAsJsonPrimitive().getAsLong(); long netatmoTS = json.getAsJsonPrimitive().getAsLong();

View File

@ -12,6 +12,8 @@
*/ */
package org.openhab.binding.netatmo.internal.discovery; package org.openhab.binding.netatmo.internal.discovery;
import java.util.HashMap;
import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -47,7 +49,7 @@ import org.slf4j.LoggerFactory;
@NonNullByDefault @NonNullByDefault
public class NetatmoDiscoveryService extends AbstractDiscoveryService implements ThingHandlerService, DiscoveryService { public class NetatmoDiscoveryService extends AbstractDiscoveryService implements ThingHandlerService, DiscoveryService {
private static final Set<ModuleType> SKIPPED_TYPES = Set.of(ModuleType.UNKNOWN, ModuleType.ACCOUNT); 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 final Logger logger = LoggerFactory.getLogger(NetatmoDiscoveryService.class);
private @Nullable ApiBridgeHandler handler; private @Nullable ApiBridgeHandler handler;
@ -60,13 +62,13 @@ public class NetatmoDiscoveryService extends AbstractDiscoveryService implements
public void startScan() { public void startScan() {
ApiBridgeHandler localHandler = handler; ApiBridgeHandler localHandler = handler;
if (localHandler != null) { if (localHandler != null) {
ThingUID apiBridgeUID = localHandler.getThing().getUID(); ThingUID accountUID = localHandler.getThing().getUID();
try { try {
AircareApi airCareApi = localHandler.getRestManager(AircareApi.class); AircareApi airCareApi = localHandler.getRestManager(AircareApi.class);
if (airCareApi != null) { // Search Healthy Home Coaches if (airCareApi != null) { // Search Healthy Home Coaches
ListBodyResponse<NAMain> body = airCareApi.getHomeCoachData(null).getBody(); ListBodyResponse<NAMain> body = airCareApi.getHomeCoachData(null).getBody();
if (body != null) { if (body != null) {
body.getElements().stream().forEach(homeCoach -> createThing(homeCoach, apiBridgeUID)); body.getElements().stream().forEach(homeCoach -> createThing(homeCoach, accountUID));
} }
} }
if (localHandler.getReadFriends()) { if (localHandler.getReadFriends()) {
@ -74,31 +76,34 @@ public class NetatmoDiscoveryService extends AbstractDiscoveryService implements
if (weatherApi != null) { // Search favorite stations if (weatherApi != null) { // Search favorite stations
weatherApi.getFavoriteAndGuestStationsData().stream().filter(NAMain::isReadOnly) weatherApi.getFavoriteAndGuestStationsData().stream().filter(NAMain::isReadOnly)
.forEach(station -> { .forEach(station -> {
ThingUID bridgeUID = createThing(station, apiBridgeUID); ThingUID bridgeUID = createThing(station, accountUID);
station.getModules().values().stream() station.getModules().values().stream()
.forEach(module -> createThing(module, bridgeUID)); .forEach(module -> createThing(module, bridgeUID));
}); });
} }
} }
HomeApi homeApi = localHandler.getRestManager(HomeApi.class); 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 -> { 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.getKnownPersons().forEach(person -> createThing(person, homeUID));
home.getModules().values().stream().forEach(device -> {
ModuleType deviceType = device.getType(); Map<String, ThingUID> bridgesUids = new HashMap<>();
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);
});
home.getRooms().values().stream().forEach(room -> { home.getRooms().values().stream().forEach(room -> {
room.getModuleIds().stream().map(id -> home.getModules().get(id)) room.getModuleIds().stream().map(id -> home.getModules().get(id))
.map(m -> m != null ? m.getType().feature : FeatureArea.NONE) .map(m -> m != null ? m.getType().feature : FeatureArea.NONE)
.filter(f -> FeatureArea.ENERGY.equals(f)).findAny() .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) { } catch (NetatmoException e) {
@ -107,26 +112,19 @@ public class NetatmoDiscoveryService extends AbstractDiscoveryService implements
} }
} }
private ThingUID findThingUID(ModuleType thingType, String thingId, @Nullable ThingUID brigdeUID) { private ThingUID findThingUID(ModuleType thingType, String thingId, ThingUID bridgeUID) {
for (ThingTypeUID supported : getSupportedThingTypes()) { ThingTypeUID thingTypeUID = thingType.thingTypeUID;
ThingTypeUID thingTypeUID = thingType.thingTypeUID; return getSupportedThingTypes().stream().filter(supported -> supported.equals(thingTypeUID)).findFirst()
if (supported.equals(thingTypeUID)) { .map(supported -> new ThingUID(supported, bridgeUID, thingId.replaceAll("[^a-zA-Z0-9_]", "")))
String id = thingId.replaceAll("[^a-zA-Z0-9_]", ""); .orElseThrow(() -> new IllegalArgumentException("Unsupported device type discovered : " + thingType));
return brigdeUID == null ? new ThingUID(supported, id) : new ThingUID(supported, brigdeUID, id);
}
}
throw 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); ThingUID moduleUID = findThingUID(module.getType(), module.getId(), bridgeUID);
DiscoveryResultBuilder resultBuilder = DiscoveryResultBuilder.create(moduleUID) DiscoveryResultBuilder resultBuilder = DiscoveryResultBuilder.create(moduleUID)
.withProperty(NAThingConfiguration.ID, module.getId()) .withProperty(NAThingConfiguration.ID, module.getId())
.withRepresentationProperty(NAThingConfiguration.ID) .withRepresentationProperty(NAThingConfiguration.ID)
.withLabel(module.getName() != null ? module.getName() : module.getId()); .withLabel(module.getName() != null ? module.getName() : module.getId()).withBridge(bridgeUID);
if (bridgeUID != null) {
resultBuilder.withBridge(bridgeUID);
}
thingDiscovered(resultBuilder.build()); thingDiscovered(resultBuilder.build());
return moduleUID; return moduleUID;
} }

View File

@ -25,6 +25,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.netatmo.internal.api.data.ModuleType; 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.NAObject;
import org.openhab.binding.netatmo.internal.api.dto.NAThing;
import org.openhab.binding.netatmo.internal.config.NAThingConfiguration; 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.Capability;
import org.openhab.binding.netatmo.internal.handler.capability.CapabilityMap; import org.openhab.binding.netatmo.internal.handler.capability.CapabilityMap;
@ -139,6 +140,14 @@ public interface CommonInterface {
} }
default void setNewData(NAObject newData) { 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; String finalReason = null;
for (Capability cap : getCapabilities().values()) { for (Capability cap : getCapabilities().values()) {
String thingStatusReason = cap.setNewData(newData); String thingStatusReason = cap.setNewData(newData);

View File

@ -94,8 +94,9 @@ public class Capability {
properties = new HashMap<>(thing.getProperties()); properties = new HashMap<>(thing.getProperties());
firstLaunch = properties.isEmpty(); firstLaunch = properties.isEmpty();
if (firstLaunch && !moduleType.isLogical()) { 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_VENDOR, VENDOR);
properties.put(PROPERTY_MODEL_ID, moduleType.name());
} }
statusReason = null; statusReason = null;
} }

View File

@ -58,17 +58,21 @@ public class EnergyCapability extends RestCapability<EnergyApi> {
protected void updateHomeData(HomeData homeData) { protected void updateHomeData(HomeData homeData) {
NAObjectMap<HomeDataRoom> rooms = homeData.getRooms(); NAObjectMap<HomeDataRoom> rooms = homeData.getRooms();
NAObjectMap<HomeDataModule> modules = homeData.getModules(); NAObjectMap<HomeDataModule> modules = homeData.getModules();
handler.getActiveChildren().forEach(handler -> { handler.getActiveChildren().forEach(childHandler -> {
HomeDataRoom roomData = rooms.get(handler.getId()); String childId = childHandler.getId();
if (roomData != null) { rooms.getOpt(childId).ifPresentOrElse(roomData -> {
roomData.setIgnoredForThingUpdate(true); roomData.setIgnoredForThingUpdate(true);
handler.setNewData(roomData); childHandler.setNewData(roomData);
} }, () -> {
HomeDataModule moduleData = modules.get(handler.getId()); modules.getOpt(childId).ifPresent(childData -> {
if (moduleData != null) { childData.setIgnoredForThingUpdate(true);
moduleData.setIgnoredForThingUpdate(true); childHandler.setNewData(childData);
handler.setNewData(moduleData); });
} modules.values().stream().filter(module -> childId.equals(module.getBridge()))
.forEach(bridgedModule -> {
childHandler.setNewData(bridgedModule);
});
});
}); });
descriptionProvider.setStateOptions(new ChannelUID(thing.getUID(), GROUP_ENERGY, CHANNEL_PLANNING), descriptionProvider.setStateOptions(new ChannelUID(thing.getUID(), GROUP_ENERGY, CHANNEL_PLANNING),
homeData.getThermSchedules().stream().map(p -> new StateOption(p.getId(), p.getName())) homeData.getThermSchedules().stream().map(p -> new StateOption(p.getId(), p.getName()))
@ -80,15 +84,23 @@ public class EnergyCapability extends RestCapability<EnergyApi> {
protected void updateHomeStatus(HomeStatus homeStatus) { protected void updateHomeStatus(HomeStatus homeStatus) {
NAObjectMap<Room> rooms = homeStatus.getRooms(); NAObjectMap<Room> rooms = homeStatus.getRooms();
NAObjectMap<HomeStatusModule> modules = homeStatus.getModules(); NAObjectMap<HomeStatusModule> modules = homeStatus.getModules();
handler.getActiveChildren().forEach(handler -> { handler.getActiveChildren().forEach(childHandler -> {
Room roomData = rooms.get(handler.getId()); String childId = childHandler.getId();
if (roomData != null) { rooms.getOpt(childId).ifPresentOrElse(roomData -> childHandler.setNewData(roomData), () -> {
handler.setNewData(roomData); modules.getOpt(childId).ifPresentOrElse(childData -> {
} childHandler.setNewData(childData);
HomeStatusModule data = modules.get(handler.getId()); modules.values().stream().filter(module -> childId.equals(module.getBridge()))
if (data != null) { .forEach(bridgedModule -> {
handler.setNewData(data); 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);
});
});
}); });
} }

View File

@ -52,14 +52,18 @@ class SecurityCapability extends RestCapability<SecurityApi> {
NAObjectMap<HomeDataModule> modules = homeData.getModules(); NAObjectMap<HomeDataModule> modules = homeData.getModules();
handler.getActiveChildren().forEach(childHandler -> { handler.getActiveChildren().forEach(childHandler -> {
String childId = childHandler.getId(); String childId = childHandler.getId();
persons.getOpt(childId).ifPresentOrElse(person -> { persons.getOpt(childId).ifPresentOrElse(personData -> {
person.setIgnoredForThingUpdate(true); personData.setIgnoredForThingUpdate(true);
childHandler.setNewData(person); childHandler.setNewData(personData);
}, () -> { }, () -> {
modules.getOpt(childId).ifPresent(module -> { modules.getOpt(childId).ifPresent(childData -> {
module.setIgnoredForThingUpdate(true); childData.setIgnoredForThingUpdate(true);
childHandler.setNewData(module); childHandler.setNewData(childData);
}); });
modules.values().stream().filter(module -> childId.equals(module.getBridge()))
.forEach(bridgedModule -> {
childHandler.setNewData(bridgedModule);
});
}); });
}); });
} }
@ -70,8 +74,15 @@ class SecurityCapability extends RestCapability<SecurityApi> {
NAObjectMap<HomeStatusModule> modules = homeStatus.getModules(); NAObjectMap<HomeStatusModule> modules = homeStatus.getModules();
handler.getActiveChildren().forEach(childHandler -> { handler.getActiveChildren().forEach(childHandler -> {
String childId = childHandler.getId(); String childId = childHandler.getId();
persons.getOpt(childId).ifPresentOrElse(person -> childHandler.setNewData(person), () -> { persons.getOpt(childId).ifPresentOrElse(personData -> childHandler.setNewData(personData), () -> {
modules.getOpt(childId).ifPresentOrElse(module -> childHandler.setNewData(module), () -> { 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 // This module is not present in the homestatus data, so it is considered as unreachable
HomeStatusModule module = new HomeStatusModule(); HomeStatusModule module = new HomeStatusModule();
module.setReachable(false); module.setReachable(false);

View File

@ -0,0 +1,52 @@
/**
* 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;
}
}

View File

@ -55,7 +55,6 @@ channel-group-type.netatmo.noise.label = Noise
channel-group-type.netatmo.person.label = Person 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.label = Last Seen
channel-group-type.netatmo.person.channel.last-seen.description = Moment when this person was 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.presence.label = Presence Camera
channel-group-type.netatmo.pressure-extended.label = Pressure channel-group-type.netatmo.pressure-extended.label = Pressure
channel-group-type.netatmo.pressure-extended.channel.trend.label = Pressure Trend channel-group-type.netatmo.pressure-extended.channel.trend.label = Pressure Trend
@ -74,6 +73,7 @@ channel-group-type.netatmo.setpoint.channel.end.description = End time of the cu
channel-group-type.netatmo.setpoint.channel.start.label = Setpoint Start 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.setpoint.channel.start.description = Start time of the currently applied setpoint.
channel-group-type.netatmo.signal.label = Signal 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-doorbell.label = Camera Status
channel-group-type.netatmo.status.label = Camera Status channel-group-type.netatmo.status.label = Camera Status
channel-group-type.netatmo.sub-event-doorbell.label = Sub Event channel-group-type.netatmo.sub-event-doorbell.label = Sub Event
@ -268,6 +268,12 @@ channel-type.netatmo.setpoint-duration.label = Setpoint Duration
channel-type.netatmo.setpoint-duration.description = Default duration of manual setpoint changes. channel-type.netatmo.setpoint-duration.description = Default duration of manual setpoint changes.
channel-type.netatmo.setpoint.label = Setpoint channel-type.netatmo.setpoint.label = Setpoint
channel-type.netatmo.setpoint.description = Thermostat temperature 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.label = Thermostat Mode
channel-type.netatmo.th-mode.description = Chosen thermostat mode (home, frost guard, manual, max). channel-type.netatmo.th-mode.description = Chosen thermostat mode (home, frost guard, manual, max).
channel-type.netatmo.th-mode.state.option.HOME = Home channel-type.netatmo.th-mode.state.option.HOME = Home

View File

@ -55,7 +55,6 @@ channel-group-type.netatmo.noise.label = Rumore
channel-group-type.netatmo.person.label = Persona 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.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.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.presence.label = Fotocamera Presenza
channel-group-type.netatmo.pressure-extended.label = Pressione channel-group-type.netatmo.pressure-extended.label = Pressione
channel-group-type.netatmo.pressure-extended.channel.trend.label = Grafico Pressione channel-group-type.netatmo.pressure-extended.channel.trend.label = Grafico Pressione

View File

@ -10,6 +10,25 @@
<description>Monitoring state of the camera</description> <description>Monitoring state of the camera</description>
</channel-type> </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"> <channel-type id="window-open">
<item-type>Contact</item-type> <item-type>Contact</item-type>
<label>Window Status</label> <label>Window Status</label>

View File

@ -4,13 +4,6 @@
xmlns:thing="https://openhab.org/schemas/thing-description/v1.0.0" 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"> 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"> <channel-group-type id="temperature-room">
<label>Room Temperature</label> <label>Room Temperature</label>
<channels> <channels>

View File

@ -23,6 +23,14 @@
</channels> </channels>
</channel-group-type> </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"> <channel-group-type id="status-doorbell">
<label>Camera Status</label> <label>Camera Status</label>
<channels> <channels>