From b00db4dd01a5dca866fde722d600a71181daa41d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20L=27hopital?= Date: Tue, 20 Aug 2024 18:24:37 +0200 Subject: [PATCH] [freeboxos] Complete Alarm system handling (#17233) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Initiating the addition of the PIR sensor * Finalized integration of the alarm system * Corrected bug in initialization of basic-shutter Signed-off-by: Gaël L'hopital --- .../internal/FreeboxOsBindingConstants.java | 18 +++-- .../internal/FreeboxOsHandlerFactory.java | 26 ++++--- .../freeboxos/internal/api/ApiHandler.java | 23 ++++++- .../internal/api/rest/HomeManager.java | 34 ++++++--- .../config/NodeConfigurationBuilder.java | 2 +- .../internal/handler/AlarmHandler.java | 8 ++- .../internal/handler/BasicShutterHandler.java | 3 +- .../internal/handler/HomeNodeHandler.java | 60 ++++++++-------- .../internal/handler/KeyfobHandler.java | 18 ++++- .../internal/handler/PirHandler.java | 69 +++++++++++++++++++ .../internal/handler/ShutterHandler.java | 3 +- .../OH-INF/i18n/freeboxos.properties | 22 ++++++ .../resources/OH-INF/thing/channel-types.xml | 40 +++++++++++ .../OH-INF/thing/home-thing-type.xml | 33 +++++++++ 14 files changed, 299 insertions(+), 60 deletions(-) create mode 100644 bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/handler/PirHandler.java diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/FreeboxOsBindingConstants.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/FreeboxOsBindingConstants.java index 9c588c33438..e09a228f075 100644 --- a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/FreeboxOsBindingConstants.java +++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/FreeboxOsBindingConstants.java @@ -71,9 +71,9 @@ public class FreeboxOsBindingConstants { public static final Set THINGS_TYPES_UIDS = Set.of(THING_TYPE_FXS, THING_TYPE_DECT, THING_TYPE_CALL, THING_TYPE_HOST, THING_TYPE_VM, THING_TYPE_PLAYER, THING_TYPE_ACTIVE_PLAYER, THING_TYPE_DELTA, THING_TYPE_REVOLUTION, THING_TYPE_REPEATER, THING_TYPE_WIFI_HOST, THING_TYPE_FREEPLUG); - public static final Set HOME_TYPES_UIDS = Set.of(Category.BASIC_SHUTTER.getThingTypeUID(), - Category.SHUTTER.getThingTypeUID(), Category.KFB.getThingTypeUID(), Category.CAMERA.getThingTypeUID(), - Category.ALARM.getThingTypeUID()); + public static final Set HOME_TYPES_UIDS = Set.of(Category.BASIC_SHUTTER.thingTypeUID, + Category.SHUTTER.thingTypeUID, Category.KFB.thingTypeUID, Category.CAMERA.thingTypeUID, + Category.ALARM.thingTypeUID, Category.PIR.thingTypeUID); protected static final Set SUPPORTED_THING_TYPES_UIDS = Stream .of(BRIDGE_TYPE_UIDS, THINGS_TYPES_UIDS, HOME_TYPES_UIDS).flatMap(Set::stream).collect(Collectors.toSet()); @@ -175,20 +175,30 @@ public class FreeboxOsBindingConstants { public static final String XDSL_UPTIME = "uptime"; // Home channels + public static final String TIMESTAMP_POSTFIX = "-timestamp"; + public static final String KEYFOB_ENABLE = "enable"; + public static final String KEYFOB_PUSHED = "pushed"; + public static final String KEYFOB_PUSHED_UPDATE = KEYFOB_PUSHED + TIMESTAMP_POSTFIX; + public static final String NODE_BATTERY = "battery"; public static final String SHUTTER_POSITION = "position-set"; public static final String SHUTTER_STOP = "stop"; public static final String BASIC_SHUTTER_STATE = "state"; public static final String BASIC_SHUTTER_UP = "up"; public static final String BASIC_SHUTTER_DOWN = "down"; - // public static final String BASIC_SHUTTER_CMD = "basic-shutter"; public static final String ALARM_PIN = "pin"; public static final String ALARM_SOUND = "sound"; public static final String ALARM_VOLUME = "volume"; public static final String ALARM_TIMEOUT1 = "timeout1"; public static final String ALARM_TIMEOUT2 = "timeout2"; public static final String ALARM_TIMEOUT3 = "timeout3"; + public static final String ALARM_STATE = "state"; + + public static final String PIR_TAMPER = "tamper"; + public static final String PIR_TRIGGER = "trigger"; + public static final String PIR_TAMPER_UPDATE = PIR_TAMPER + TIMESTAMP_POSTFIX; + public static final String PIR_TRIGGER_UPDATE = PIR_TRIGGER + TIMESTAMP_POSTFIX; public static final Set TRUE_COMMANDS = Set.of(OnOffType.ON, UpDownType.UP, OpenClosedType.OPEN); public static final Set> ON_OFF_CLASSES = Set.of(OnOffType.class, UpDownType.class, OpenClosedType.class); diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/FreeboxOsHandlerFactory.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/FreeboxOsHandlerFactory.java index 850f6e01cb3..9f3a0927c28 100644 --- a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/FreeboxOsHandlerFactory.java +++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/FreeboxOsHandlerFactory.java @@ -20,7 +20,6 @@ import java.util.concurrent.TimeUnit; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; -import org.eclipse.jetty.client.HttpClient; import org.openhab.binding.freeboxos.internal.api.ApiHandler; import org.openhab.binding.freeboxos.internal.api.rest.FreeboxOsSession; import org.openhab.binding.freeboxos.internal.api.rest.HomeManager.Category; @@ -35,6 +34,7 @@ import org.openhab.binding.freeboxos.internal.handler.FreeplugHandler; import org.openhab.binding.freeboxos.internal.handler.FxsHandler; import org.openhab.binding.freeboxos.internal.handler.HostHandler; import org.openhab.binding.freeboxos.internal.handler.KeyfobHandler; +import org.openhab.binding.freeboxos.internal.handler.PirHandler; import org.openhab.binding.freeboxos.internal.handler.PlayerHandler; import org.openhab.binding.freeboxos.internal.handler.RepeaterHandler; import org.openhab.binding.freeboxos.internal.handler.RevolutionHandler; @@ -56,6 +56,7 @@ import org.openhab.core.thing.binding.ThingHandlerFactory; import org.osgi.service.component.ComponentContext; import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Deactivate; import org.osgi.service.component.annotations.Modified; import org.osgi.service.component.annotations.Reference; import org.slf4j.Logger; @@ -76,7 +77,6 @@ public class FreeboxOsHandlerFactory extends BaseThingHandlerFactory { private final NetworkAddressService networkAddressService; private final AudioHTTPServer audioHTTPServer; - private final HttpClient httpClient; private final ApiHandler apiHandler; private String callbackURL = ""; @@ -88,13 +88,19 @@ public class FreeboxOsHandlerFactory extends BaseThingHandlerFactory { super.activate(componentContext); this.audioHTTPServer = audioHTTPServer; - this.httpClient = httpClientFactory.getCommonHttpClient(); this.networkAddressService = networkAddressService; - this.apiHandler = new ApiHandler(httpClient, timeZoneProvider); + this.apiHandler = new ApiHandler(httpClientFactory, timeZoneProvider); configChanged(config); } + @Override + @Deactivate + public void deactivate(ComponentContext componentContext) { + super.deactivate(componentContext); + apiHandler.dispose(); + } + @Modified public void configChanged(Map config) { String timeout = (String) config.getOrDefault(TIMEOUT, "8"); @@ -149,16 +155,18 @@ public class FreeboxOsHandlerFactory extends BaseThingHandlerFactory { return new ActivePlayerHandler(thing); } else if (THING_TYPE_PLAYER.equals(thingTypeUID)) { return new PlayerHandler(thing); - } else if (Category.BASIC_SHUTTER.getThingTypeUID().equals(thingTypeUID)) { + } else if (Category.BASIC_SHUTTER.thingTypeUID.equals(thingTypeUID)) { return new BasicShutterHandler(thing); - } else if (Category.SHUTTER.getThingTypeUID().equals(thingTypeUID)) { + } else if (Category.SHUTTER.thingTypeUID.equals(thingTypeUID)) { return new ShutterHandler(thing); - } else if (Category.ALARM.getThingTypeUID().equals(thingTypeUID)) { + } else if (Category.ALARM.thingTypeUID.equals(thingTypeUID)) { return new AlarmHandler(thing); - } else if (Category.KFB.getThingTypeUID().equals(thingTypeUID)) { + } else if (Category.KFB.thingTypeUID.equals(thingTypeUID)) { return new KeyfobHandler(thing); - } else if (Category.CAMERA.getThingTypeUID().equals(thingTypeUID)) { + } else if (Category.CAMERA.thingTypeUID.equals(thingTypeUID)) { return new CameraHandler(thing); + } else if (Category.PIR.thingTypeUID.equals(thingTypeUID)) { + return new PirHandler(thing); } return null; diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/ApiHandler.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/ApiHandler.java index 6f760e52cbb..9fcb2ad5c88 100644 --- a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/ApiHandler.java +++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/ApiHandler.java @@ -32,11 +32,13 @@ import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.HttpStatus.Code; +import org.openhab.binding.freeboxos.internal.FreeboxOsBindingConstants; import org.openhab.binding.freeboxos.internal.api.deserialization.ForegroundAppDeserializer; import org.openhab.binding.freeboxos.internal.api.deserialization.ListDeserializer; import org.openhab.binding.freeboxos.internal.api.deserialization.StrictEnumTypeAdapterFactory; import org.openhab.binding.freeboxos.internal.api.rest.PlayerManager.ForegroundApp; import org.openhab.core.i18n.TimeZoneProvider; +import org.openhab.core.io.net.http.HttpClientFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -58,9 +60,10 @@ import inet.ipaddr.mac.MACAddress; */ @NonNullByDefault public class ApiHandler { - public static final String AUTH_HEADER = "X-Fbx-App-Auth"; private static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8; private static final String CONTENT_TYPE = "application/json; charset=" + DEFAULT_CHARSET.name(); + private static final int RESPONSE_BUFFER_SIZE = 65536; + public static final String AUTH_HEADER = "X-Fbx-App-Auth"; private final Logger logger = LoggerFactory.getLogger(ApiHandler.class); private final HttpClient httpClient; @@ -68,8 +71,7 @@ public class ApiHandler { private long timeoutInMs = TimeUnit.SECONDS.toMillis(8); - public ApiHandler(HttpClient httpClient, TimeZoneProvider timeZoneProvider) { - this.httpClient = httpClient; + public ApiHandler(HttpClientFactory httpClientFactory, TimeZoneProvider timeZoneProvider) { this.gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) .registerTypeAdapter(ZonedDateTime.class, (JsonDeserializer) (json, type, jsonDeserializationContext) -> { @@ -86,6 +88,21 @@ public class ApiHandler { .registerTypeAdapter(ForegroundApp.class, new ForegroundAppDeserializer()) .registerTypeAdapter(List.class, new ListDeserializer()).serializeNulls() .registerTypeAdapterFactory(new StrictEnumTypeAdapterFactory()).create(); + httpClient = httpClientFactory.createHttpClient(FreeboxOsBindingConstants.BINDING_ID); + httpClient.setResponseBufferSize(RESPONSE_BUFFER_SIZE); + try { + httpClient.start(); + } catch (Exception e) { + logger.warn("Unable to start httpClient: {}", e.getMessage()); + } + } + + public void dispose() { + try { + httpClient.stop(); + } catch (Exception e) { + logger.warn("Unable to stop httpClient: {}", e.getMessage()); + } } public synchronized T executeUri(URI uri, HttpMethod method, Class clazz, @Nullable String sessionToken, diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/rest/HomeManager.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/rest/HomeManager.java index 2170a2fbdc5..4bdf8617ff0 100644 --- a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/rest/HomeManager.java +++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/api/rest/HomeManager.java @@ -14,8 +14,10 @@ package org.openhab.binding.freeboxos.internal.api.rest; import static org.openhab.binding.freeboxos.internal.FreeboxOsBindingConstants.BINDING_ID; +import java.util.Comparator; import java.util.List; import java.util.Map; +import java.util.Optional; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.NonNullByDefault; @@ -102,17 +104,25 @@ public class HomeManager extends RestManager { } } - private static record LogEntry(long timestamp, int value) { + public static record LogEntry(Long timestamp, int value) { } public static record Endpoint(int id, String name, String label, EpType epType, Visibility visibility, int refresh, - ValueType valueType, EndpointUi ui, @Nullable String category, Object value, List history) { + ValueType valueType, EndpointUi ui, @Nullable String category, Object value, + @Nullable List history) { + + private static final Comparator HISTORY_COMPARATOR = Comparator.comparing(LogEntry::timestamp); + private enum Visibility { INTERNAL, NORMAL, DASHBOARD, UNKNOWN } + + public Optional getLastChange() { + return history != null ? history.stream().max(HISTORY_COMPARATOR) : Optional.empty(); + } } private enum Status { @@ -129,16 +139,13 @@ public class HomeManager extends RestManager { ALARM, KFB, CAMERA, + PIR, UNKNOWN; - private final ThingTypeUID thingTypeUID; + public final ThingTypeUID thingTypeUID; Category() { - thingTypeUID = new ThingTypeUID(BINDING_ID, name().toLowerCase()); - } - - public ThingTypeUID getThingTypeUID() { - return thingTypeUID; + thingTypeUID = new ThingTypeUID(BINDING_ID, name().toLowerCase().replace('_', '-')); } } @@ -148,6 +155,17 @@ public class HomeManager extends RestManager { public static record HomeNode(int id, @Nullable String name, @Nullable String label, Category category, Status status, List showEndpoints, Map props, NodeType type) { + + private static final Comparator ENPOINT_COMPARATOR = Comparator.comparing(Endpoint::refresh); + + public Optional getMinRefresh() { + return showEndpoints.stream().filter(ep -> EpType.SIGNAL.equals(ep.epType) && ep.refresh() != 0) + .min(ENPOINT_COMPARATOR); + } + + public Optional getEndpoint(int slotId) { + return showEndpoints.stream().filter(ep -> ep.id == slotId).findAny(); + } } public HomeManager(FreeboxOsSession session) throws FreeboxException { diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/config/NodeConfigurationBuilder.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/config/NodeConfigurationBuilder.java index 18e0c882f57..dda8d651ea9 100644 --- a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/config/NodeConfigurationBuilder.java +++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/config/NodeConfigurationBuilder.java @@ -41,7 +41,7 @@ public class NodeConfigurationBuilder { if (node.category() == Category.UNKNOWN) { return Optional.empty(); } - ThingUID thingUID = new ThingUID(node.category().getThingTypeUID(), bridgeUID, Integer.toString(node.id())); + ThingUID thingUID = new ThingUID(node.category().thingTypeUID, bridgeUID, Integer.toString(node.id())); DiscoveryResultBuilder discoveryResultBuilder = DiscoveryResultBuilder.create(thingUID); discoveryResultBuilder.withProperty(ClientConfiguration.ID, node.id()).withLabel(node.label()) .withRepresentationProperty(ClientConfiguration.ID).withBridge(bridgeUID); diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/handler/AlarmHandler.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/handler/AlarmHandler.java index ca62189f0bb..74cae9b0bfc 100644 --- a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/handler/AlarmHandler.java +++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/handler/AlarmHandler.java @@ -14,8 +14,10 @@ package org.openhab.binding.freeboxos.internal.handler; import static org.openhab.binding.freeboxos.internal.FreeboxOsBindingConstants.*; +import java.util.Optional; + import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.binding.freeboxos.internal.api.rest.HomeManager; +import org.openhab.binding.freeboxos.internal.api.rest.HomeManager.Endpoint; import org.openhab.binding.freeboxos.internal.api.rest.HomeManager.EndpointState; import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.QuantityType; @@ -38,7 +40,7 @@ public class AlarmHandler extends HomeNodeHandler { } @Override - protected State getChannelState(HomeManager homeManager, String channelId, EndpointState state) { + protected State getChannelState(String channelId, EndpointState state, Optional endPoint) { String value = state.value(); if (value == null) { @@ -47,7 +49,7 @@ public class AlarmHandler extends HomeNodeHandler { return switch (channelId) { case NODE_BATTERY -> DecimalType.valueOf(value); - case ALARM_PIN -> StringType.valueOf(value); + case ALARM_STATE, ALARM_PIN -> StringType.valueOf(value); case ALARM_SOUND, ALARM_VOLUME -> QuantityType.valueOf(value + " %"); case ALARM_TIMEOUT1, ALARM_TIMEOUT2, ALARM_TIMEOUT3 -> QuantityType.valueOf(value + " s"); default -> UnDefType.NULL; diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/handler/BasicShutterHandler.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/handler/BasicShutterHandler.java index ee0492ddff9..46487e1041f 100644 --- a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/handler/BasicShutterHandler.java +++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/handler/BasicShutterHandler.java @@ -15,6 +15,7 @@ package org.openhab.binding.freeboxos.internal.handler; import static org.openhab.binding.freeboxos.internal.FreeboxOsBindingConstants.*; import java.util.List; +import java.util.Optional; import java.util.Set; import org.eclipse.jdt.annotation.NonNullByDefault; @@ -50,7 +51,7 @@ public class BasicShutterHandler extends HomeNodeHandler { } @Override - protected State getChannelState(HomeManager homeManager, String channelId, EndpointState state) { + protected State getChannelState(String channelId, EndpointState state, Optional endPoint) { String value = state.value(); return value != null && channelId.equals(BASIC_SHUTTER_STATE) ? state.asBoolean() ? OpenClosedType.CLOSED : OpenClosedType.OPEN diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/handler/HomeNodeHandler.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/handler/HomeNodeHandler.java index df79ddd21af..269958e6da7 100644 --- a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/handler/HomeNodeHandler.java +++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/handler/HomeNodeHandler.java @@ -12,10 +12,12 @@ */ package org.openhab.binding.freeboxos.internal.handler; +import static org.openhab.binding.freeboxos.internal.FreeboxOsBindingConstants.TIMESTAMP_POSTFIX; + import java.math.BigDecimal; -import java.util.Comparator; import java.util.List; import java.util.Map; +import java.util.Optional; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; @@ -53,48 +55,52 @@ public abstract class HomeNodeHandler extends ApiConsumerHandler { HomeNode node = getManager(HomeManager.class).getHomeNode(getClientId()); // Gets the lowest refresh time or else, we'll keep configuration default - node.showEndpoints().stream().filter(ep -> ep.epType() == EpType.SIGNAL).filter(ep -> ep.refresh() != 0) - .min(Comparator.comparing(Endpoint::refresh)).map(Endpoint::refresh).ifPresent(rate -> { - Configuration thingConfig = editConfiguration(); - thingConfig.put(ApiConsumerConfiguration.REFRESH_INTERVAL, Integer.toString(rate / 1000)); - updateConfiguration(thingConfig); - }); + node.getMinRefresh().map(Endpoint::refresh).ifPresent(rate -> { + Configuration thingConfig = editConfiguration(); + thingConfig.put(ApiConsumerConfiguration.REFRESH_INTERVAL, Integer.toString(rate / 1000)); + updateConfiguration(thingConfig); + }); properties.putAll(node.props()); getThing().getChannels().forEach(channel -> { Configuration conf = channel.getConfiguration(); - node.type().endpoints().stream().filter(ep -> ep.name().equals(channel.getUID().getIdWithoutGroup())) + String channelId = channel.getUID().getIdWithoutGroup(); + node.type().endpoints().stream().filter(ep -> ep.name().equals(channelId)) .forEach(endPoint -> conf.put(endPoint.epType().asConfId(), endPoint.id())); - internalConfigureChannel(channel.getUID().getIdWithoutGroup(), conf, node.type().endpoints()); + internalConfigureChannel(channelId, conf, node.type().endpoints()); }); } protected void internalConfigureChannel(String channelId, Configuration conf, List endpoints) { + if (channelId.endsWith(TIMESTAMP_POSTFIX)) { + String baseEndpoint = channelId.replace(TIMESTAMP_POSTFIX, ""); + endpoints.stream().filter(ep -> ep.name().equals(baseEndpoint)).forEach(ep -> { + conf.put(ep.name(), ep.id()); + conf.put("signal", ep.id()); + }); + } } @Override protected void internalPoll() throws FreeboxException { HomeManager homeManager = getManager(HomeManager.class); - getThing().getChannels().stream().filter(channel -> isLinked(channel.getUID())).forEach(channel -> { - State result = UnDefType.UNDEF; + HomeNode node = homeManager.getHomeNode(getClientId()); + List linkedChannels = getThing().getChannels().stream().filter(channel -> isLinked(channel.getUID())) + .toList(); + + for (Channel channel : linkedChannels) { + State result = null; Integer slotId = getSlotId(channel.getConfiguration(), EpType.SIGNAL.asConfId()); if (slotId instanceof Integer) { - try { - EndpointState state = homeManager.getEndpointsState(getClientId(), slotId); - if (state != null) { - result = getChannelState(homeManager, channel.getUID().getIdWithoutGroup(), state); - } else { - result = getChannelState(homeManager, channel.getUID().getIdWithoutGroup()); - } - } catch (FreeboxException e) { - logger.warn("Error updating channel: {}", e.getMessage()); + EndpointState state = homeManager.getEndpointsState(getClientId(), slotId); + Optional endPoint = node.getEndpoint(slotId); + if (state != null) { + result = getChannelState(channel.getUID().getIdWithoutGroup(), state, endPoint); } - } else { - result = getChannelState(homeManager, channel.getUID().getIdWithoutGroup()); } - updateState(channel.getUID(), result); - }); + updateState(channel.getUID(), result != null ? result : UnDefType.UNDEF); + } } @Override @@ -126,9 +132,5 @@ public abstract class HomeNodeHandler extends ApiConsumerHandler { return false; } - protected State getChannelState(HomeManager homeManager, String channelWG) { - return UnDefType.UNDEF; - } - - protected abstract State getChannelState(HomeManager homeManager, String channelId, EndpointState state); + protected abstract State getChannelState(String channelId, EndpointState state, Optional endPoint); } diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/handler/KeyfobHandler.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/handler/KeyfobHandler.java index 35caf5ea791..524330a22da 100644 --- a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/handler/KeyfobHandler.java +++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/handler/KeyfobHandler.java @@ -14,11 +14,19 @@ package org.openhab.binding.freeboxos.internal.handler; import static org.openhab.binding.freeboxos.internal.FreeboxOsBindingConstants.*; +import java.time.Instant; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.util.Objects; +import java.util.Optional; + import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.binding.freeboxos.internal.api.FreeboxException; import org.openhab.binding.freeboxos.internal.api.rest.HomeManager; +import org.openhab.binding.freeboxos.internal.api.rest.HomeManager.Endpoint; import org.openhab.binding.freeboxos.internal.api.rest.HomeManager.EndpointState; import org.openhab.core.config.core.Configuration; +import org.openhab.core.library.types.DateTimeType; import org.openhab.core.library.types.DecimalType; import org.openhab.core.library.types.OnOffType; import org.openhab.core.thing.Thing; @@ -40,7 +48,15 @@ public class KeyfobHandler extends HomeNodeHandler { } @Override - protected State getChannelState(HomeManager homeManager, String channelId, EndpointState state) { + protected State getChannelState(String channelId, EndpointState state, Optional endPoint) { + if (channelId.startsWith(KEYFOB_PUSHED)) { + return Objects.requireNonNull(endPoint.map(ep -> ep + .getLastChange().map( + change -> (State) (KEYFOB_PUSHED.equals(channelId) ? new DecimalType(change.value()) + : new DateTimeType(ZonedDateTime + .ofInstant(Instant.ofEpochSecond(change.timestamp()), ZoneOffset.UTC)))) + .orElse(UnDefType.UNDEF)).orElse(UnDefType.UNDEF)); + } String value = state.value(); if (value != null) { switch (channelId) { diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/handler/PirHandler.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/handler/PirHandler.java new file mode 100644 index 00000000000..191189e4f94 --- /dev/null +++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/handler/PirHandler.java @@ -0,0 +1,69 @@ +/** + * Copyright (c) 2010-2024 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.freeboxos.internal.handler; + +import static org.openhab.binding.freeboxos.internal.FreeboxOsBindingConstants.*; + +import java.time.Instant; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.util.Objects; +import java.util.Optional; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.freeboxos.internal.api.rest.HomeManager.Endpoint; +import org.openhab.binding.freeboxos.internal.api.rest.HomeManager.EndpointState; +import org.openhab.core.library.types.DateTimeType; +import org.openhab.core.library.types.DecimalType; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.library.types.OpenClosedType; +import org.openhab.core.thing.Thing; +import org.openhab.core.types.State; +import org.openhab.core.types.UnDefType; + +/** + * The {@link PirHandler} is responsible for handling everything associated to + * any Freebox Home PIR motion detection thing type. + * + * @author Gaël L'hopital - Initial contribution + */ +@NonNullByDefault +public class PirHandler extends HomeNodeHandler { + + public PirHandler(Thing thing) { + super(thing); + } + + @Override + protected State getChannelState(String channelId, EndpointState state, Optional endPoint) { + if (PIR_TAMPER_UPDATE.equals(channelId) || PIR_TRIGGER_UPDATE.equals(channelId)) { + return Objects.requireNonNull(endPoint.map(ep -> ep.getLastChange() + .map(change -> (State) new DateTimeType( + ZonedDateTime.ofInstant(Instant.ofEpochSecond(change.timestamp()), ZoneOffset.UTC))) + .orElse(UnDefType.UNDEF)).orElse(UnDefType.UNDEF)); + } + + String value = state.value(); + + if (value == null) { + return UnDefType.NULL; + } + + return switch (channelId) { + case NODE_BATTERY -> DecimalType.valueOf(value); + case PIR_TAMPER -> state.asBoolean() ? OpenClosedType.OPEN : OpenClosedType.CLOSED; + case PIR_TRIGGER -> OnOffType.from(value); + default -> UnDefType.NULL; + }; + } +} diff --git a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/handler/ShutterHandler.java b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/handler/ShutterHandler.java index 20ff18d1c77..04dc1415556 100644 --- a/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/handler/ShutterHandler.java +++ b/bundles/org.openhab.binding.freeboxos/src/main/java/org/openhab/binding/freeboxos/internal/handler/ShutterHandler.java @@ -15,6 +15,7 @@ package org.openhab.binding.freeboxos.internal.handler; import static org.openhab.binding.freeboxos.internal.FreeboxOsBindingConstants.*; import java.util.List; +import java.util.Optional; import org.eclipse.jdt.annotation.NonNullByDefault; import org.openhab.binding.freeboxos.internal.api.FreeboxException; @@ -49,7 +50,7 @@ public class ShutterHandler extends HomeNodeHandler { } @Override - protected State getChannelState(HomeManager homeManager, String channelId, EndpointState state) { + protected State getChannelState(String channelId, EndpointState state, Optional endPoint) { String value = state.value(); return value != null && channelId.equals(SHUTTER_POSITION) ? QuantityType.valueOf(value + " %") : UnDefType.NULL; diff --git a/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/i18n/freeboxos.properties b/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/i18n/freeboxos.properties index 663b4b942b0..995690b6d8c 100644 --- a/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/i18n/freeboxos.properties +++ b/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/i18n/freeboxos.properties @@ -46,6 +46,14 @@ thing-type.freeboxos.host.label = Network Device thing-type.freeboxos.host.description = Provides network device reachability thing-type.freeboxos.kfb.label = Freebox Keyfob thing-type.freeboxos.kfb.description = A keyfob configured in your Freebox Server +thing-type.freeboxos.kfb.channel.pushed-timestamp.label = Timestamp +thing-type.freeboxos.kfb.channel.pushed-timestamp.description = Timestamp of the last action on the keyfob +thing-type.freeboxos.pir.label = Freebox Home PIR +thing-type.freeboxos.pir.description = A motion Sensor +thing-type.freeboxos.pir.channel.tamper-timestamp.label = Tamper Timestamp +thing-type.freeboxos.pir.channel.tamper-timestamp.description = Timestamp of the last cover tampered state change +thing-type.freeboxos.pir.channel.trigger-timestamp.label = Trigger Timestamp +thing-type.freeboxos.pir.channel.trigger-timestamp.description = Timestamp of the last state change thing-type.freeboxos.player.label = Freebox Player thing-type.freeboxos.player.description = The player is the device connected to your TV thing-type.freeboxos.repeater.label = Wifi Repeater @@ -209,6 +217,13 @@ channel-type.freeboxos.afp-file-status.description = Status of Mac OS File Shari channel-type.freeboxos.airmedia-status.label = Air Media Enabled channel-type.freeboxos.airmedia-status.description = Indicates whether Air Media is enabled channel-type.freeboxos.alarm-pin.label = PIN Code +channel-type.freeboxos.alarm-state.label = Alarm State +channel-type.freeboxos.alarm-state.description = Current state of the alarm system +channel-type.freeboxos.alarm-state.state.option.idle = Idle +channel-type.freeboxos.alarm-state.state.option.alarm1_arming = Arming (Absent Mode) +channel-type.freeboxos.alarm-state.state.option.alarm1_armed = Armed (Absent Mode) +channel-type.freeboxos.alarm-state.state.option.alarm2_arming = Arming (Night Mode) +channel-type.freeboxos.alarm-state.state.option.alarm2_armed = Armed (Night Mode) channel-type.freeboxos.alarm-timeout.label = Alarm Duration channel-type.freeboxos.alarm-volume.label = Alarm Volume channel-type.freeboxos.alternate-ring.label = Alternating Ring @@ -282,6 +297,11 @@ channel-type.freeboxos.key-code.state.option.ok = OK channel-type.freeboxos.key-code.state.option.home = Home channel-type.freeboxos.keyfob-enable.label = Keyfob Enabled channel-type.freeboxos.keyfob-enable.description = Activates / deactivates the keyfob +channel-type.freeboxos.kfb-pushed.label = Key Code Pushed +channel-type.freeboxos.kfb-pushed.description = Last key pushed on the remote +channel-type.freeboxos.kfb-pushed.state.option.1 = Arm Absent Mode +channel-type.freeboxos.kfb-pushed.state.option.2 = Disarm +channel-type.freeboxos.kfb-pushed.state.option.3 = Arm Night Mode channel-type.freeboxos.lcd-brightness.label = Screen Brightness channel-type.freeboxos.lcd-brightness.description = Brightness level of the screen in percent channel-type.freeboxos.lcd-forced.label = Forced Orientation @@ -323,6 +343,8 @@ channel-type.freeboxos.package.description = Name of the package currently activ channel-type.freeboxos.phone-event.label = Phone Event channel-type.freeboxos.phone-event.description = Triggers when an event related to the phone has been detected channel-type.freeboxos.phone-number.label = Phone Number +channel-type.freeboxos.pir-tamper.label = Cover Tamper +channel-type.freeboxos.pir-trigger.label = Détection channel-type.freeboxos.player-status.label = Player Status channel-type.freeboxos.player-status.description = Status of the Freebox TV player channel-type.freeboxos.reachable.label = Reachable diff --git a/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/thing/channel-types.xml b/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/thing/channel-types.xml index 4dee0b5198b..7cfb9b4502c 100644 --- a/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/thing/channel-types.xml +++ b/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/thing/channel-types.xml @@ -496,6 +496,46 @@ + + Switch + + oh:freeboxos:mouvement + + + + Contact + + oh:freeboxos:warning + + + + Number + + Last key pushed on the remote + + + + + + + + + + + String + + Current state of the alarm system + + + + + + + + + + + Switch diff --git a/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/thing/home-thing-type.xml b/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/thing/home-thing-type.xml index fb70314e1ce..c8255065ce2 100644 --- a/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/thing/home-thing-type.xml +++ b/bundles/org.openhab.binding.freeboxos/src/main/resources/OH-INF/thing/home-thing-type.xml @@ -21,6 +21,33 @@ + + + + + + + A motion Sensor + + + + + + Timestamp of the last state change + + + + + Timestamp of the last cover tampered state change + + + + + id + + + + @@ -30,6 +57,7 @@ The Alarm system configured in your Freebox Server + @@ -64,6 +92,11 @@ A keyfob configured in your Freebox Server + + + + Timestamp of the last action on the keyfob +