[netatmo] Add siren device (#12805)
* Starting Siren addition Signed-off-by: clinique <gael@lhopital.org>
This commit is contained in:
parent
5291d7ddc9
commit
1aec6c9b30
|
@ -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.
|
||||
- *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.
|
||||
|
||||
|
@ -505,7 +507,7 @@ Warnings:
|
|||
|
||||
(*) 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 |
|
||||
|---------------|-------------------|--------------|------------|---------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
|
@ -532,6 +534,18 @@ Warnings:
|
|||
|
||||
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
|
||||
|
||||
|
|
|
@ -53,6 +53,7 @@ public class NetatmoBindingConstants {
|
|||
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";
|
||||
|
@ -105,6 +106,7 @@ public class NetatmoBindingConstants {
|
|||
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";
|
||||
|
|
|
@ -43,7 +43,6 @@ import org.slf4j.LoggerFactory;
|
|||
*/
|
||||
@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();
|
||||
|
@ -62,7 +61,7 @@ public class AuthenticationApi extends RestManager {
|
|||
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);
|
||||
|
@ -118,7 +117,7 @@ public class AuthenticationApi extends RestManager {
|
|||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.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;
|
||||
|
@ -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.core.thing.ThingTypeUID;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
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);
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ import static org.openhab.core.library.CoreItemFactory.*;
|
|||
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;
|
||||
|
@ -190,36 +191,35 @@ public class NetatmoConstants {
|
|||
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);
|
||||
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(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
|
||||
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
|
||||
|
||||
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
|
||||
public static enum SetpointMode {
|
||||
@SerializedName("program")
|
||||
|
|
|
@ -43,4 +43,9 @@ public class HomeDataModule extends NAThing implements NAModule {
|
|||
public List<String> getModuleBridged() {
|
||||
return moduleBridged;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isIgnoredForThingUpdate() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -18,6 +18,7 @@ import java.time.ZonedDateTime;
|
|||
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;
|
||||
|
@ -47,6 +48,7 @@ public class NADeserializer {
|
|||
.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();
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
*/
|
||||
package org.openhab.binding.netatmo.internal.discovery;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
@ -47,7 +49,7 @@ import org.slf4j.LoggerFactory;
|
|||
@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;
|
||||
|
||||
|
@ -60,13 +62,13 @@ public class NetatmoDiscoveryService extends AbstractDiscoveryService implements
|
|||
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()) {
|
||||
|
@ -74,31 +76,34 @@ public class NetatmoDiscoveryService extends AbstractDiscoveryService implements
|
|||
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) {
|
||||
|
@ -107,26 +112,19 @@ public class NetatmoDiscoveryService extends AbstractDiscoveryService implements
|
|||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
|
|||
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;
|
||||
|
@ -139,6 +140,14 @@ public interface CommonInterface {
|
|||
}
|
||||
|
||||
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);
|
||||
|
|
|
@ -94,8 +94,9 @@ public class Capability {
|
|||
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;
|
||||
}
|
||||
|
|
|
@ -58,17 +58,21 @@ public class EnergyCapability extends RestCapability<EnergyApi> {
|
|||
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()))
|
||||
|
@ -80,15 +84,23 @@ public class EnergyCapability extends RestCapability<EnergyApi> {
|
|||
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);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -52,14 +52,18 @@ class SecurityCapability extends RestCapability<SecurityApi> {
|
|||
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);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -70,8 +74,15 @@ class SecurityCapability extends RestCapability<SecurityApi> {
|
|||
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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -55,7 +55,6 @@ channel-group-type.netatmo.noise.label = Noise
|
|||
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
|
||||
|
@ -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.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
|
||||
|
@ -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.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
|
||||
|
|
|
@ -55,7 +55,6 @@ channel-group-type.netatmo.noise.label = Rumore
|
|||
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
|
||||
|
|
|
@ -10,6 +10,25 @@
|
|||
<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>
|
||||
|
|
|
@ -4,13 +4,6 @@
|
|||
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>
|
||||
|
|
|
@ -23,6 +23,14 @@
|
|||
</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>
|
||||
|
|
Loading…
Reference in New Issue